USB物理设备通过USBCCGP拆分成逻辑设备
USB规范中定义一个接口代表一个功能,故一般情况下一个接口描述符及其下的描述符实现一个特定的功能。
但在USB规范中,也定义一些特定的类设备,如UVC和UAC设备,其一般是需要2个接口描述符实现一个设备功能。如UVC、UAC设备下,其一般会有一个控制接口描述符和N{0~N}个音视频流接口描述符。对于这种特别的情况,USB规范为了使用更加友好的呈现给用户,其使用了接口关联描述符IAD来实现关联。
所以,对于一个配置描述符,其一般可以抽象成1个或者多个逻辑设备,其逻辑设备的大小一般小于等于接口描述符的数量。
逻辑设备的拆分
一个USB设备选择配置SET_CONFIGURATION之后,其配置描述符就确定了。
这样对于Windows系统,可以通过USBD_ParseConfigurationDescriptorEx函数来拆分设备各个接口描述符的地址。
PUSB_INTERFACE_DESCRIPTOR
  USBD_ParseConfigurationDescriptorEx(
    IN PUSB_CONFIGURATION_DESCRIPTOR  ConfigurationDescriptor,
    IN PVOID  StartPosition,
    IN LONG  InterfaceNumber,
    IN LONG  AlternateSetting,
    IN LONG  InterfaceClass,
    IN LONG  InterfaceSubClass,
    IN LONG  InterfaceProtocol
    );
对于大部分的配置描述符,其第一个接口描述符的InterfaceNumber为0,并且后续连续。但其实很多设备根据就不遵守这个规则,他们惟一遵守的可能就是对于多个接口描述符复合的设备,其下一个接口描述符只与上一个保持联系,其它根据不做任何保证。比如一个有UVC,UAC、HID的设备,其接口描述符的索引如下就很正常:
- 配置描述符声明有5个接口
- UVC设备- 控制接口描述符索引为2
- 视频流接口描述符索引为3=2+1
 
- UAC设备- 控制接口描述符索引为6
- 音频流接口描述符索引为7=6+1
 
- HID设备- 接口描述符索引为9
 
所以对于使用USBD_ParseConfigurationDescriptorEx,更合适的代码也许如此:
PUSB_CONFIGURATION_DESCRIPTOR configDesc = ?
PCOMM_DESCRIPTOR pComm = configDesc; 
for(int i=0;i<configDesc->bNumInterfaces;i++)
{
    interfaceDesc = USBD_ParseConfigurationDescriptorEx(
        configDesc,
        pComm,
        -1,
        0,//不取备用接口
        -1,
        -1,
        -1);
    pComm= (PCOMM_DESCRIPTOR)((PUCHAR)pComm+pComm->length);
}
拆分出的接口描述符组,个人更倾向于通过其接口描述符的bInterfaceClass来进行逻辑设备分组。所以有的设备有一个接口描述符,有的有多个接口描述符。对于功能设备,其至少存如下信息:
- ULONG numInterfaces
- PUSB_INTERFACE_DESCRIPTOR InterfaceDescriptorStart //实际由USBD_INTERFACE_LIST_ENTRY functionInterfaceList代替
另外一点是需要对设备的设备描述符修正(friendlyName):
DeviceDesc.iProduct = functionInterfaceList[0].InterfaceDescriptor->iInterface;
逻辑设备配置描述符的生成
/*
 *  BuildFunctionConfigurationDescriptor
 *
 *
 *      Note:  this function cannot be pageable because internal
 *             ioctls may be sent at IRQL==DISPATCH_LEVEL.
 */
NTSTATUS BuildFunctionConfigurationDescriptor(
                    PFUNCTION_PDO_EXT functionPdoExt,
                    PUCHAR buffer,
                    ULONG bufferLength,
                    PULONG bytesReturned)
{
    PUSB_CONFIGURATION_DESCRIPTOR functionConfigDesc;
    PUSB_COMMON_DESCRIPTOR commonDesc;
    PUSB_INTERFACE_DESCRIPTOR thisIfaceDesc;
    PUCHAR scratch;
    ULONG totalLength;
    NTSTATUS status;
    ULONG i;
    // BUGBUG - change this to use the USBD ParseConfiguration function
    /*
     *  The function's configuration descriptor will include
     *  a subset of the interface descriptors in the parent's
     *  configuration descriptor.
     */
     //物理设备的配置描述符
    PUSB_CONFIGURATION_DESCRIPTOR parentConfigDesc = functionPdoExt->parentFdoExt->selectedConfigDesc;
    PUCHAR parentConfigDescEnd = (PUCHAR)((PUCHAR)parentConfigDesc + parentConfigDesc->wTotalLength);
    /*
     *  First calculate the total length of what we'll be copying.
     *  It will include a configuration descriptor followed by
     *  some number of interface descriptors.
     *  Each interface descriptor may be followed by a some number
     *  of class-specific descriptors.
     */
    totalLength = sizeof(USB_CONFIGURATION_DESCRIPTOR);
    for (i = 0; i < functionPdoExt->numInterfaces; i++) 
    {
        /*
         *  We will copy the interface descriptor and all following
         *  descriptors until either the next interface
         *  descriptor or the end of the entire
         *  configuration descriptor.
         */
        thisIfaceDesc = functionPdoExt->functionInterfaceList[i].InterfaceDescriptor;
        commonDesc = (PUSB_COMMON_DESCRIPTOR)thisIfaceDesc;
        do {
            totalLength += commonDesc->bLength;
            commonDesc = (PUSB_COMMON_DESCRIPTOR)(((PUCHAR)commonDesc) + commonDesc->bLength);
        }
#if 0
        if((PUCHAR)commonDesc  >= parentConfigDescEnd)
        {
            break;
        }
        if(commonDesc->bDescriptorType == USB_INTERFACE_DESCRIPTOR_TYPE)
        {
            if((PUSB_INTERFACE_DESCRIPTOR)commonDesc)->bInterfaceNumber != thisIfaceDesc->bInterfaceNumber)
            {
                break;
            }
        }
        while(1);
#else
        while (((PUCHAR)commonDesc < parentConfigDescEnd)
            //到下一个接口描述符停止 if(isinterface || interfaceNum == thisinterfacenum)
            &&((commonDesc->bDescriptorType != USB_INTERFACE_DESCRIPTOR_TYPE) || (((PUSB_INTERFACE_DESCRIPTOR)commonDesc)->bInterfaceNumber == thisIfaceDesc->bInterfaceNumber)));
#endif
    }
    scratch = ALLOCPOOL(NonPagedPool, totalLength);
    if (scratch){
        PUCHAR pch;
        pch = scratch;
        RtlCopyMemory(pch, parentConfigDesc, sizeof(USB_CONFIGURATION_DESCRIPTOR));
        pch += sizeof(USB_CONFIGURATION_DESCRIPTOR);
        for (i = 0; i < functionPdoExt->numInterfaces; i++) {
            /*
             *  Copy the interface descriptor and all following
             *  descriptors until either the next interface
             *  descriptor or the end of the entire
             *  configuration descriptor.
             */
            thisIfaceDesc = functionPdoExt->functionInterfaceList[i].InterfaceDescriptor;
            ASSERT(thisIfaceDesc->bDescriptorType == USB_INTERFACE_DESCRIPTOR_TYPE);
            commonDesc = (PUSB_COMMON_DESCRIPTOR)thisIfaceDesc;
            do {
                RtlCopyMemory(pch, commonDesc, commonDesc->bLength);
                pch += commonDesc->bLength;
                commonDesc = (PUSB_COMMON_DESCRIPTOR)(((PUCHAR)commonDesc) + commonDesc->bLength);
            }
            while (((PUCHAR)commonDesc < parentConfigDescEnd) &&
                   ((commonDesc->bDescriptorType != USB_INTERFACE_DESCRIPTOR_TYPE) ||
                    (((PUSB_INTERFACE_DESCRIPTOR)commonDesc)->bInterfaceNumber == thisIfaceDesc->bInterfaceNumber)));
        }
        /*
         *  This 'function' child's config descriptor contains
         *  a subset of the parent's interface descriptors.
         *  Update the child's configuration descriptor's size
         *  to reflect the possibly-reduced number of interface descriptors.
         */
        functionConfigDesc = (PUSB_CONFIGURATION_DESCRIPTOR)scratch;
        functionConfigDesc->bNumInterfaces = (UCHAR)functionPdoExt->numInterfaces;
        functionConfigDesc->wTotalLength = (USHORT)totalLength;
        /*
         *  Copy as much of the config descriptor as will fit in the caller's buffer.
         *  Return success whether or not the caller's buffer was actually big enough (BUGBUG ? - this is what usbhub did).
         */
        *bytesReturned = MIN(bufferLength, totalLength);
        RtlCopyMemory(buffer, scratch, *bytesReturned);
        DBGDUMPBYTES("BuildFunctionConfigurationDescriptor built config desc for function", buffer, *bytesReturned);
        FREEPOOL(scratch);
        status = STATUS_SUCCESS;
    }
    else {
        ASSERT(scratch);
        status = STATUS_INSUFFICIENT_RESOURCES;
        *bytesReturned = 0;
    }
    return status;
}
 USB通用驱动源码分析
			USB通用驱动源码分析
			




