Windows系统UAC音频设备的初始化过程 USB麦克风USB声卡
第一步当然是获取设备描述符了,使用UsbBuildGetDescriptorRequest函数初始化获取设备描述符的Urb,然后再调用SubmitUrbToUsbdSynch函数返回。当然获取的设备描述符必须保存。
第二步是获取配置描述符,方法和获取设备描述符一致,只是需要分2次获取。第一次只获取sizeof(USB_CONFIGURATION_DESCRIPTOR)大小的配置描述符内容,然后解析出配置描述符的总长度之后,再获取全部的配置描述符全部内容。
第三步就是选择配置。根据获取到的配置描述符解析出接口描述符的总数量,然后申请N+1个USBD_INTERFACE_LIST_ENTRY(最后一个必须会为NULL)内存空间。USBD_INTERFACE_LIST_ENTRY结构体的定义如下:
typedef struct _USBD_INTERFACE_LIST_ENTRY {
PUSB_INTERFACE_DESCRIPTOR InterfaceDescriptor;
PUSBD_INTERFACE_INFORMATION Interface;
} USBD_INTERFACE_LIST_ENTRY, *PUSBD_INTERFACE_LIST_ENTRY;
由于每人接口描述符都对应一个USBD_INTERFACE_LIST_ENTRY,故其成员InterfaceDescriptor指向的是获取的配置描述符中对应的接口描述符的指针。PUSBD_INTERFACE_INFORMATION是选择配置之后的接口描述符下的相关接口和USB端点信息。
以上都是USB设备统一的方法,而对于UAC这种复杂的复合设备,需要做一些特别的处理。这种表现在音频控制接口和音频流接口上。
使用USBD_ParseConfigurationDescriptorEx分别解析出音频流接口描述符和音频控制接口描述符的位置。
其对应的AudioClass为USB_DEVICE_CLASS_AUDIO=0x01,而SubClass分别为AUDIO_SUBCLASS_CONTROL=0x01和AUDIO_SUBCLASS_STREAMING=0x02,当然,搞了这么多,竟然只是为了验证配置描述符中是否有相关的信息。这从另一方面来讲,就是对于UAC设备,配置描述符中必须有音频流接口描述符和音频控制接口描述符。
我印象是UVC规范中视频流接口描述符中可以为0,却不知道UAC中是否也这样。不过从Windows来看,好像必须得有,至少UAC设备必须有一个音频流接口描述符。
使用USBD_ParseConfigurationDescriptorEx获取Class=USB_DEVICE_CLASS_AUDIO的接口描述符(主要用于跳过IAD,当然像USBTreeViewer又不显示IAD),然后按循环解析以下的相关描述符。
- 当SubClass=AUDIO_SUBCLASS_MIDISTREAMING,MIDI的,我们不关注。
- 当SubClass=AUDIO_SUBCLASS_STREAMING,音频流的
- 当SubClass=AUDIO_SUBCLASS_CONTROL,音频控制的,需要争析其UAC拓扑结构
当然,以上都得保存其存其相关的接口描述符到USBD_INTERFACE_LIST_ENTRY中。
所以说标准的接口描述符是分界面,保存在USBD_INTERFACE_LIST_ENTRY中,而其各对应类的如AUDIO_SUBCLASS_CONTROL的,就对应解析,形成一个树形关系。
生成了USBD_INTERFACE_LIST_ENTRY表格后,使用SubmitUrbToUsbdSynch选择配置。最后保存其句柄。
pAudioDevice->ConfigurationHandle = pUrb->UrbSelectConfiguration.ConfigurationHandle;
最后,保留留选择配置后的PUSBD_INTERFACE_INFORMATION信息,至此选择配置SET_CONFIGURATION完成。
第三步,使用获取USB总线的接口信息,这由设备的总线驱动提供。生成的IRP默认说不支持,但可以通过MajorFunction= IRP_MJ_PNP和MinorFunction= IRP_MN_QUERY_INTERFACE来IoCallDriver到USB总线。
这玩意又分好多种版本,所以结构体也是随版本在扩展。
#define USB_BUSIF_USBDI_VERSION_0 0x0000
#define USB_BUSIF_USBDI_VERSION_1 0x0001
#define USB_BUSIF_USBDI_VERSION_2 0x0002
#define USB_BUSIF_USBDI_VERSION_3 0x0003
其结构体分别为:
typedef struct _USB_BUS_INTERFACE_USBDI_V0 {
USHORT Size;
USHORT Version;
PVOID BusContext;
PINTERFACE_REFERENCE InterfaceReference;
PINTERFACE_DEREFERENCE InterfaceDereference;
PUSB_BUSIFFN_GETUSBDI_VERSION GetUSBDIVersion;
PUSB_BUSIFFN_QUERY_BUS_TIME QueryBusTime;
PUSB_BUSIFFN_SUBMIT_ISO_OUT_URB SubmitIsoOutUrb;
PUSB_BUSIFFN_QUERY_BUS_INFORMATION QueryBusInformation;
} USB_BUS_INTERFACE_USBDI_V0, *PUSB_BUS_INTERFACE_USBDI_V0;
typedef struct _USB_BUS_INTERFACE_USBDI_V1 {
USHORT Size;
USHORT Version;
PVOID BusContext;
PINTERFACE_REFERENCE InterfaceReference;
PINTERFACE_DEREFERENCE InterfaceDereference;
PUSB_BUSIFFN_GETUSBDI_VERSION GetUSBDIVersion;
PUSB_BUSIFFN_QUERY_BUS_TIME QueryBusTime;
PUSB_BUSIFFN_SUBMIT_ISO_OUT_URB SubmitIsoOutUrb;
PUSB_BUSIFFN_QUERY_BUS_INFORMATION QueryBusInformation;
PUSB_BUSIFFN_IS_DEVICE_HIGH_SPEED IsDeviceHighSpeed;
} USB_BUS_INTERFACE_USBDI_V1, *PUSB_BUS_INTERFACE_USBDI_V1;
typedef struct _USB_BUS_INTERFACE_USBDI_V2 {
USHORT Size;
USHORT Version;
PVOID BusContext;
PINTERFACE_REFERENCE InterfaceReference;
PINTERFACE_DEREFERENCE InterfaceDereference;
PUSB_BUSIFFN_GETUSBDI_VERSION GetUSBDIVersion;
PUSB_BUSIFFN_QUERY_BUS_TIME QueryBusTime;
PUSB_BUSIFFN_SUBMIT_ISO_OUT_URB SubmitIsoOutUrb;
PUSB_BUSIFFN_QUERY_BUS_INFORMATION QueryBusInformation;
PUSB_BUSIFFN_IS_DEVICE_HIGH_SPEED IsDeviceHighSpeed;
PUSB_BUSIFFN_ENUM_LOG_ENTRY EnumLogEntry;
} USB_BUS_INTERFACE_USBDI_V2, *PUSB_BUS_INTERFACE_USBDI_V2;
typedef struct _USB_BUS_INTERFACE_USBDI_V3 {
USHORT Size;
USHORT Version;
PVOID BusContext;
PINTERFACE_REFERENCE InterfaceReference;
PINTERFACE_DEREFERENCE InterfaceDereference;
PUSB_BUSIFFN_GETUSBDI_VERSION GetUSBDIVersion;
PUSB_BUSIFFN_QUERY_BUS_TIME QueryBusTime;
PUSB_BUSIFFN_SUBMIT_ISO_OUT_URB SubmitIsoOutUrb;
PUSB_BUSIFFN_QUERY_BUS_INFORMATION QueryBusInformation; //V0
PUSB_BUSIFFN_IS_DEVICE_HIGH_SPEED IsDeviceHighSpeed; //V1
PUSB_BUSIFFN_ENUM_LOG_ENTRY EnumLogEntry; //V2
PUSB_BUSIFFN_QUERY_BUS_TIME_EX QueryBusTimeEx;
PUSB_BUSIFFN_QUERY_CONTROLLER_TYPE QueryControllerType;
} USB_BUS_INTERFACE_USBDI_V3, *PUSB_BUS_INTERFACE_USBDI_V3;
看结构体,他们就是多了几个成员。这每个成员又都是相关的回调函数