USB通用驱动源码分析
+ -

USBCCGP 关联接口功能设备枚举

2021-09-15 249 0

从上节可知,所有的功能设备都存储在FDO_DEVICE_EXTENSION中的FunctionDescriptor中的,而FunctionDescriptorCount成员记录着子功能数量。

typedef struct _USBC_FUNCTION_DESCRIPTOR{
    // The 0-based index of the function described
    UCHAR FunctionNumber;

    // The number of interfaces contained in this function
    UCHAR NumberOfInterfaces;

    // A callee allocated array of pointers into the config desc buffer passed as an input
    PUSB_INTERFACE_DESCRIPTOR *InterfaceDescriptorList;

    // Callee allocated PNP IDs for this PDO
    UNICODE_STRING HardwareId;
    UNICODE_STRING CompatibleId;
    UNICODE_STRING FunctionDescription;

    // Custom Flags
    ULONG FunctionFlags;
    PVOID Reserved;
} USBC_FUNCTION_DESCRIPTOR, *PUSBC_FUNCTION_DESCRIPTOR;

关联接口功能设备的枚举是通过USBCCGP_EnumWithAssociationDescriptor实现的。
function.c


NTSTATUS
USBCCGP_EnumWithAssociationDescriptor(
    IN PDEVICE_OBJECT DeviceObject)
{
    ULONG DescriptorCount, Index;
    PFDO_DEVICE_EXTENSION FDODeviceExtension;
    NTSTATUS Status = STATUS_SUCCESS;

    //
    // get device extension
    //
    FDODeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
    ASSERT(FDODeviceExtension->Common.IsFDO);

    //
    // count association descriptors
    //
    DescriptorCount = USBCCGP_CountAssociationDescriptors(FDODeviceExtension->ConfigurationDescriptor);
    if (!DescriptorCount)
    {
        //
        // no descriptors found
        //
        return STATUS_NOT_SUPPORTED;
    }

    //
    // allocate function descriptor array
    //
    FDODeviceExtension->FunctionDescriptor = AllocateItem(NonPagedPool, sizeof(USBC_FUNCTION_DESCRIPTOR) * DescriptorCount);
    if (!FDODeviceExtension->FunctionDescriptor)
    {
        //
        // no memory
        //
        DPRINT1("USBCCGP_EnumWithAssociationDescriptor failed to allocate function descriptor count %x\n", DescriptorCount);
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    for (Index = 0; Index < DescriptorCount; Index++)
    {
        //
        // init function descriptors
        //
        Status = USBCCGP_InitFunctionDescriptor(FDODeviceExtension, Index, &FDODeviceExtension->FunctionDescriptor[Index]);
        if (!NT_SUCCESS(Status))
        {
            //
            // failed
            //
            return Status;
        }
    }

    //
    // store function descriptor count
    //
    FDODeviceExtension->FunctionDescriptorCount = DescriptorCount;

    //
    // done
    //
    return Status;
}

1.首选获取关联描述符的数量

有几个关联描述符就应该有几个功能设备,这个是通过接口描述符类型来判断的。

ULONG
USBCCGP_CountAssociationDescriptors(
    IN PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor)
{
    PUSB_INTERFACE_ASSOCIATION_DESCRIPTOR Descriptor;
    PUCHAR Offset, End;
    ULONG Count = 0;

    //
    // init offsets
    //
    Offset = (PUCHAR)ConfigurationDescriptor + ConfigurationDescriptor->bLength;
    End = (PUCHAR)ConfigurationDescriptor + ConfigurationDescriptor->wTotalLength;

    while (Offset < End)
    {
        //
        // get association descriptor
        //
        Descriptor = (PUSB_INTERFACE_ASSOCIATION_DESCRIPTOR)Offset;

        if (Descriptor->bLength == sizeof(USB_INTERFACE_ASSOCIATION_DESCRIPTOR) && Descriptor->bDescriptorType == USB_INTERFACE_ASSOCIATION_DESCRIPTOR_TYPE)
        {
            //
            // found descriptor
            //
            Count++;
        }

        //
        // move to next descriptor
        //
        Offset += Descriptor->bLength;
    }

    //
    // done
    //
    return Count;
}

2.根据关联调备的描述符数量分配USBC_FUNCTION_DESCRIPTOR内存空间

3.使用函数USBCCGP_InitFunctionDescriptor找到各个对应的关联描述符,并分析相关信息,实现各个子功能设备USBC_FUNCTION_DESCRIPTOR结构体数据信息的填充。


NTSTATUS
USBCCGP_InitFunctionDescriptor(
    IN PFDO_DEVICE_EXTENSION FDODeviceExtension,
    IN ULONG FunctionNumber,
    OUT PUSBC_FUNCTION_DESCRIPTOR FunctionDescriptor)
{
    PUSB_INTERFACE_ASSOCIATION_DESCRIPTOR Descriptor;
    NTSTATUS Status;
    LPWSTR DescriptionBuffer;
    WCHAR Buffer[100];
    ULONG Index;

    // init function number
    FunctionDescriptor->FunctionNumber = (UCHAR)FunctionNumber;

    // get association descriptor
    Descriptor = USBCCGP_GetAssociationDescriptorAtIndex(FDODeviceExtension->ConfigurationDescriptor, FunctionNumber);
    ASSERT(Descriptor);

    // store number interfaces
    FunctionDescriptor->NumberOfInterfaces = Descriptor->bInterfaceCount;

    // allocate array for interface count
    FunctionDescriptor->InterfaceDescriptorList = AllocateItem(NonPagedPool, sizeof(PUSB_INTERFACE_DESCRIPTOR) * Descriptor->bInterfaceCount);
    if (!FunctionDescriptor->InterfaceDescriptorList)
    {
        //
        // no memory
        //
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    // init interface list
    Status = USBCCGP_InitInterfaceListOfFunctionDescriptor(FDODeviceExtension->ConfigurationDescriptor, Descriptor, FunctionDescriptor);
    if (!NT_SUCCESS(Status))
    {
        //
        // failed
        //
        return Status;
    }

    //
    // now init interface description
    //
    if (Descriptor->iFunction)
    {
        //
        // get interface description
        //
         Status = USBCCGP_GetStringDescriptor(FDODeviceExtension->NextDeviceObject,
                                              100 * sizeof(WCHAR),
                                              Descriptor->iFunction,
                                              0x0409, //FIXME
                                              (PVOID*)&DescriptionBuffer);
        if (!NT_SUCCESS(Status))
        {
            //
            // no description
            //
            RtlInitUnicodeString(&FunctionDescriptor->FunctionDescription, L"");
        }
        else
        {
            //
            // init description
            //
            RtlInitUnicodeString(&FunctionDescriptor->FunctionDescription, DescriptionBuffer);
        }
        DPRINT1("FunctionDescription %wZ\n", &FunctionDescriptor->FunctionDescription);
    }

    //
    // now init hardware id
    //
    Index = swprintf(Buffer, L"USB\\VID_%04x&PID_%04x&Rev_%04x&MI_%02x", FDODeviceExtension->DeviceDescriptor->idVendor,
                                                                         FDODeviceExtension->DeviceDescriptor->idProduct,
                                                                         FDODeviceExtension->DeviceDescriptor->bcdDevice,
                                                                         Descriptor->bFirstInterface) + 1;
    Index += swprintf(&Buffer[Index], L"USB\\VID_%04x&PID_%04x&MI_%02x", FDODeviceExtension->DeviceDescriptor->idVendor,
                                                                         FDODeviceExtension->DeviceDescriptor->idProduct,
                                                                         Descriptor->bFirstInterface) + 1;

    // allocate result buffer
    DescriptionBuffer = AllocateItem(NonPagedPool, (Index + 1) * sizeof(WCHAR));
    if (!DescriptionBuffer)
    {
        //
        // failed to allocate memory
        //
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    // copy description
    RtlCopyMemory(DescriptionBuffer, Buffer, (Index + 1) * sizeof(WCHAR));
    FunctionDescriptor->HardwareId.Buffer = DescriptionBuffer;
    FunctionDescriptor->HardwareId.Length = Index * sizeof(WCHAR);
    FunctionDescriptor->HardwareId.MaximumLength = (Index + 1) * sizeof(WCHAR);

    //
    // now init the compatible id
    //
    Index = swprintf(Buffer, L"USB\\Class_%02x&SubClass_%02x&Prot_%02x", Descriptor->bFunctionClass, Descriptor->bFunctionSubClass, Descriptor->bFunctionProtocol) + 1;
    Index += swprintf(&Buffer[Index], L"USB\\Class_%02x&SubClass_%02x",  Descriptor->bFunctionClass, Descriptor->bFunctionSubClass) + 1;
    Index += swprintf(&Buffer[Index], L"USB\\Class_%02x", Descriptor->bFunctionClass) + 1;

    // allocate result buffer
    DescriptionBuffer = AllocateItem(NonPagedPool, (Index + 1) * sizeof(WCHAR));
    if (!DescriptionBuffer)
    {
        //
        // failed to allocate memory
        //
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    // copy description
    RtlCopyMemory(DescriptionBuffer, Buffer, (Index + 1) * sizeof(WCHAR));
    FunctionDescriptor->CompatibleId.Buffer = DescriptionBuffer;
    FunctionDescriptor->CompatibleId.Length = Index * sizeof(WCHAR);
    FunctionDescriptor->CompatibleId.MaximumLength = (Index + 1) * sizeof(WCHAR);

    //
    // done
    //
    return STATUS_SUCCESS;
}

3.1.在各个关联调备中可能有多个接口描述符,如UVC中就会有VC和VC,所以对于各个关联接口描述符中,需要初始化FUNCTION_DESCRIPTOR中的 PUSB_INTERFACE_DESCRIPTOR *InterfaceDescriptorList;成员。并使用函数USBCCGP_InitInterfaceListOfFunctionDescriptor来初始化。


NTSTATUS
USBCCGP_InitInterfaceListOfFunctionDescriptor(
    IN PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor,
    IN PUSB_INTERFACE_ASSOCIATION_DESCRIPTOR AssociationDescriptor,
    OUT PUSBC_FUNCTION_DESCRIPTOR FunctionDescriptor)
{
    PUSB_INTERFACE_DESCRIPTOR Descriptor;
    PUCHAR Offset, End;
    ULONG Count = 0;

    //
    // init offsets
    //
    Offset = (PUCHAR)AssociationDescriptor + AssociationDescriptor->bLength;
    End = (PUCHAR)ConfigurationDescriptor + ConfigurationDescriptor->wTotalLength;

    while (Offset < End)
    {
        //
        // get association descriptor
        //
        Descriptor = (PUSB_INTERFACE_DESCRIPTOR)Offset;

        if (Descriptor->bLength == sizeof(USB_INTERFACE_DESCRIPTOR) && Descriptor->bDescriptorType == USB_INTERFACE_DESCRIPTOR_TYPE)
        {
            //
            // store interface descriptor
            //
            FunctionDescriptor->InterfaceDescriptorList[Count] = Descriptor;
            Count++;

            if (Count == AssociationDescriptor->bInterfaceCount)
            {
                //
                // got all interfaces
                //
                return STATUS_SUCCESS;
            }
        }

        if (Descriptor->bLength == sizeof(USB_INTERFACE_ASSOCIATION_DESCRIPTOR) && Descriptor->bDescriptorType == USB_INTERFACE_ASSOCIATION_DESCRIPTOR_TYPE)
        {
            //
            // WTF? a association descriptor which overlaps the next association descriptor
            //
            DPRINT1("Invalid association descriptor\n");
            ASSERT(FALSE);
            return STATUS_UNSUCCESSFUL;
        }

        //
        // move to next descriptor
        //
        Offset += Descriptor->bLength;
    }

    //
    // invalid association descriptor
    //
    DPRINT1("Invalid association descriptor\n");
    return STATUS_UNSUCCESSFUL;
}

在上面的USBCCGP_GetAssociationDescriptorAtIndex函数中,获取的接口描述符并非是关联描述符的指针,而是其下一个接口描述符的指针。

3.2最后,如果功能调备有设置字符描述符,通过函数USBCCGP_GetStringDescriptor获取它。


NTSTATUS
NTAPI
USBCCGP_GetStringDescriptor(
    IN PDEVICE_OBJECT DeviceObject,
    IN ULONG DescriptorLength,
    IN UCHAR DescriptorIndex,
    IN LANGID LanguageId,
    OUT PVOID *OutDescriptor)
{
    NTSTATUS Status;
    PUSB_STRING_DESCRIPTOR StringDescriptor;
    ULONG Size;
    PVOID Buffer;

    // retrieve descriptor
    Status = USBCCGP_GetDescriptor(DeviceObject, USB_STRING_DESCRIPTOR_TYPE, DescriptorLength, DescriptorIndex, LanguageId, OutDescriptor);
    if (!NT_SUCCESS(Status))
    {
        // failed
        return Status;
    }

    // get descriptor structure
    StringDescriptor = (PUSB_STRING_DESCRIPTOR)*OutDescriptor;

    // sanity check
    ASSERT(StringDescriptor->bLength < DescriptorLength - 2);

    if (StringDescriptor->bLength == 2)
    {
        // invalid descriptor
        FreeItem(StringDescriptor);
        return STATUS_DEVICE_DATA_ERROR;
    }

    // calculate size
    Size = StringDescriptor->bLength + sizeof(WCHAR);

    // allocate buffer
    Buffer = AllocateItem(NonPagedPool, Size);
    if (!Buffer)
    {
        // no memory
        FreeItem(StringDescriptor);
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    // copy result
    RtlCopyMemory(Buffer, StringDescriptor->bString, Size - FIELD_OFFSET(USB_STRING_DESCRIPTOR, bString));

    // free buffer
    FreeItem(StringDescriptor);

    // store result
    *OutDescriptor = (PVOID)Buffer;
    return STATUS_SUCCESS;
}
HID人机交互QQ群:564808376    UAC音频QQ群:218581009    UVC相机QQ群:331552032    BOT&UASP大容量存储QQ群:258159197    STC-USB单片机QQ群:315457461    USB技术交流QQ群2:580684376    USB技术交流QQ群:952873936   

0 篇笔记 写笔记

UVC 接口关联描述符IAD
设备必须使用接口关联描述符来描述需要视频控制接口和一个或多个视频流接口的每个设备功能的视频接口集合。标准的VIC接口关联描述符与接口关联描述符ECN中定义的标准接口关联描述符相同,只是有些字段现在具有专用值。说明:如果视频控制接口是视频接口集合的一部分,则接口关联描述符IAD中的iFuncti......
UVC UVC驱动接口关联描述符IAD失踪之迷
先说一下,本人所使用的操作系统是WIN10 x64操作系统Microsoft Windows [版本 10.0.14393](c) 2016 Microsoft Corporation。保留所有权利。这是win10一个相对比较老的版本。新旧的混淆在微软件主的官方文档 USB Interfac......
UAC 接口关联描述符
和UVC设备的接口关联描述符的功能一样,UAC的接口关联描述也用于组织UAC的音频控制接接口和UAC的音频流接口描述符及其子描述符。接口关联描述符的结构定义typedef struct _USB_INTERFACE_ASSOCIATION_DESCRIPTOR { UCHAR bLengt......
USB复合设备-UVC摄像头HID设备共存的设计实现
通常做USB设备的开发,我们做的都是单一的功能设备。这种单一的功能设备只实现某种特定的功能,如只实现一个HID鼠标或键盘,只实现一个USB存储功能,或再复杂一点只实现一个UVC音频麦克风和扬声器功能或一个UVC摄像头功能。但我们在一般的市场上看到的设备通常不只实现一种功能,如UVC摄像头功能还提供......
USBCCGP 关联接口功能设备枚举
从上节可知,所有的功能设备都存储在FDO_DEVICE_EXTENSION中的FunctionDescriptor中的,而FunctionDescriptorCount成员记录着子功能数量。typedef struct _USBC_FUNCTION_DESCRIPTOR{ // The 0......
USBCCGP 关联接口功能设备枚举
从上节可知,所有的功能设备都存储在FDO_DEVICE_EXTENSION中的FunctionDescriptor中的,而FunctionDescriptorCount成员记录着子功能数量。typedef struct _USBC_FUNCTION_DESCRIPTOR{ // The 0......
USB 接口关联描述符
对于复合USB设备的接口描述符,可以在每个类(Class)要合并的接口描述符之前加一个接口关联描述符(Interface Association Descriptor,IAD),其作用就是把多个接口定义成一个类设备,即多个接口作用于一个设备。接口关联描述符的定义如下:typedef struct......
从UVC摄像头配置描述符的长度区别来理解USB接口关联描述符IAD
对于如下的UVC摄像头,其在设备管理器中如下图所示:其硬件ID分别为:USBVID_33F1&PID_1035&REV_0409USBVID_33F1&PID_1035&REV_0409&MI_00可以看到,其根设备是使用的USBCCGP.sy......
关注公众号
取消
感谢您的支持,我会继续努力的!
扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

您的支持,是我们前进的动力!