USB通用驱动源码分析
+ -

USBCCGP 配置设备

2021-09-15 382 0

获取设备的配置描述符通过前面已经知道,其包括的内容比较多,如接口描述符端点描述符等,其中有很多我们进行数据通讯的信息,所以下来我们就要对设备进行配置。
USBCCGP USB复合设备驱动中,由于其将各个独立的接口会枚举成一个单独的设备,所以一般情况下有几个接口描述符就会被枚举成几个设备(UVC设备除外).

USB设备的配置,就是对其配置描述符的解析分隔归类。
1.根据接口描述符的数量逐步使用函数USBD_ParseConfigurationDescriptorEx进行解析,并分别存储在USBD_INTERFACE_LIST_ENTRY中。
2.然后通过调用函数USBD_CreateConfigurationRequestEx,其上面1解析出的各个接口描述符数据传进行,返回一个URB.
3.将这个URB同步调用下层PDO,返回选择后的接口描述符。选择后的接口描述符是被下层总线驱动初始化的,其含有可直接进行数据通讯的信息。
4.将这些选择后的接口描述符存储,以待数据通读或枚举子设行时使用。

配置描述符的入口函数是:USBCCGP_SelectConfiguration
descriptor.c


NTSTATUS
USBCCGP_SelectConfiguration(
    IN PDEVICE_OBJECT DeviceObject,
    IN PFDO_DEVICE_EXTENSION DeviceExtension)
{
    PUSBD_INTERFACE_INFORMATION InterfaceInformation;
    NTSTATUS Status;
    PURB Urb;
    ULONG Index;

    //
    // now scan configuration descriptors
    //
    Status = USBCCGP_ScanConfigurationDescriptor(DeviceExtension, DeviceExtension->ConfigurationDescriptor);
    if (!NT_SUCCESS(Status))
    {
        //
        // failed to scan
        //
        return Status;
    }

    //
    // now allocate the urb
    //
    Urb = USBD_CreateConfigurationRequestEx(DeviceExtension->ConfigurationDescriptor, DeviceExtension->InterfaceList);
    if (!Urb)
    {
        //
        // no memory
        //
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    //
    // submit urb
    //
    Status = USBCCGP_SyncUrbRequest(DeviceExtension->NextDeviceObject, Urb);
    if (!NT_SUCCESS(Status))
    {
        //
        // failed to set configuration
        //
        DPRINT1("USBCCGP_SyncUrbRequest failed to set interface %x\n", Status);
        ExFreePool(Urb);
        return Status;
    }

    //
    // get interface information
    //
    InterfaceInformation = &Urb->UrbSelectConfiguration.Interface;
    for(Index = 0; Index < DeviceExtension->InterfaceListCount; Index++)
    {
        //
        // allocate buffer to store interface information
        //
        DeviceExtension->InterfaceList[Index].Interface = AllocateItem(NonPagedPool, InterfaceInformation->Length);
        if (!DeviceExtension->InterfaceList[Index].Interface)
        {
            //
            // no memory
            //
            return STATUS_INSUFFICIENT_RESOURCES;
        }

        //
        // copy interface information
        //
        RtlCopyMemory(DeviceExtension->InterfaceList[Index].Interface, InterfaceInformation, InterfaceInformation->Length);

        //
        // move to next interface
        //
        InterfaceInformation = (PUSBD_INTERFACE_INFORMATION)((ULONG_PTR)InterfaceInformation + InterfaceInformation->Length);
    }


    //
    // store pipe handle
    //
    DeviceExtension->ConfigurationHandle = Urb->UrbSelectConfiguration.ConfigurationHandle;

    //
    // free interface list & urb
    //
    ExFreePool(Urb);

    //
    // done
    //
    return Status;
}

可以看到,
1.首选是进行接口描述符的解析,使用函数USBCCGP_ScanConfigurationDescriptor来实现。


NTSTATUS
NTAPI
USBCCGP_ScanConfigurationDescriptor(
    IN OUT PFDO_DEVICE_EXTENSION FDODeviceExtension,
    IN PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor)
{
    PUSB_INTERFACE_DESCRIPTOR InterfaceDescriptor;
    ULONG InterfaceIndex = 0;
    ULONG DescriptorCount;

    //
    // sanity checks
    //
    ASSERT(ConfigurationDescriptor);
    ASSERT(ConfigurationDescriptor->bNumInterfaces);

    //
    // count all interface descriptors
    //
    DescriptorCount = ConfigurationDescriptor->bNumInterfaces;
    if (DescriptorCount == 0)
    {
        DPRINT1("[USBCCGP] DescriptorCount is zero\n");
        return STATUS_INVALID_PARAMETER;
    }

    //
    // allocate array holding the interface descriptors
    //
    FDODeviceExtension->InterfaceList = AllocateItem(NonPagedPool, sizeof(USBD_INTERFACE_LIST_ENTRY) * (DescriptorCount + 1));
    if (!FDODeviceExtension->InterfaceList)
    {
        //
        // no memory
        //
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    //
    // reset interface list count
    //
    FDODeviceExtension->InterfaceListCount = 0;

    do
    {
        //
        // parse configuration descriptor
        //
        InterfaceDescriptor = USBD_ParseConfigurationDescriptorEx(ConfigurationDescriptor, ConfigurationDescriptor, InterfaceIndex, -1, -1, -1, -1);
        if (InterfaceDescriptor)
        {
            //
            // store in interface list
            //
            ASSERT(FDODeviceExtension->InterfaceListCount < DescriptorCount);
            FDODeviceExtension->InterfaceList[FDODeviceExtension->InterfaceListCount].InterfaceDescriptor = InterfaceDescriptor;
            FDODeviceExtension->InterfaceListCount++;
        }
        else
        {
            DumpConfigurationDescriptor(ConfigurationDescriptor);
            DumpFullConfigurationDescriptor(FDODeviceExtension, ConfigurationDescriptor);

            //
            // see issue
            // CORE-6574 Test 3 (USB Web Cam)
            //
            if (FDODeviceExtension->DeviceDescriptor && FDODeviceExtension->DeviceDescriptor->idVendor == 0x0458 && FDODeviceExtension->DeviceDescriptor->idProduct == 0x705f)
                ASSERT(FALSE);
        }

        //
        // move to next interface
        //
        InterfaceIndex++;

    }while(InterfaceIndex < DescriptorCount);

    //
    // sanity check
    //
    ASSERT(FDODeviceExtension->InterfaceListCount);

    //
    // done
    //
    return STATUS_SUCCESS;
}

2.然后根据接口描述符数据使用函数USBD_CreateConfigurationRequestEx来创建对应的URB.
3.将URB下传给总线驱动的PDO(USBCCGP_SyncUrbRequest),返回返择后的接口描述符。
4.解析存储后的接口描述符。

说明:

接口描述符数据必须必须比接口描述符多一个,且最后一个的成员变量都得设为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;

其中InterfaceDescriptor为对应的接口描述符,Interface为选择后即由下层总线驱动数据配置后的信息。
结构体定义如下:

typedef struct _USBD_INTERFACE_INFORMATION {
    USHORT Length;
    UCHAR InterfaceNumber;
    UCHAR AlternateSetting;
    UCHAR Class;
    UCHAR SubClass;
    UCHAR Protocol;
    UCHAR Reserved;
    USBD_INTERFACE_HANDLE InterfaceHandle;
    ULONG NumberOfPipes;
    USBD_PIPE_INFORMATION Pipes[1];
} USBD_INTERFACE_INFORMATION, *PUSBD_INTERFACE_INFORMATION;

可以看到,有端点数量,接口ID和端点的相关信息

typedef struct _USBD_PIPE_INFORMATION {
    USHORT MaximumPacketSize;
    UCHAR EndpointAddress;
    UCHAR Interval;
    USBD_PIPE_TYPE PipeType;
    USBD_PIPE_HANDLE PipeHandle;
    ULONG MaximumTransferSize;
    ULONG PipeFlags;
} USBD_PIPE_INFORMATION, *PUSBD_PIPE_INFORMATION;

附:扫描接口描述符函数

NTSTATUS
NTAPI
USBCCGP_ScanConfigurationDescriptor(
    IN OUT PFDO_DEVICE_EXTENSION FDODeviceExtension,
    IN PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor)
{
    PUSB_INTERFACE_DESCRIPTOR InterfaceDescriptor;
    ULONG InterfaceIndex = 0;
    ULONG DescriptorCount;

    //
    // sanity checks
    //
    ASSERT(ConfigurationDescriptor);
    ASSERT(ConfigurationDescriptor->bNumInterfaces);

    //
    // count all interface descriptors
    //
    DescriptorCount = ConfigurationDescriptor->bNumInterfaces;
    if (DescriptorCount == 0)
    {
        DPRINT1("[USBCCGP] DescriptorCount is zero\n");
        return STATUS_INVALID_PARAMETER;
    }

    //
    // allocate array holding the interface descriptors
    //
    FDODeviceExtension->InterfaceList = AllocateItem(NonPagedPool, sizeof(USBD_INTERFACE_LIST_ENTRY) * (DescriptorCount + 1));
    if (!FDODeviceExtension->InterfaceList)
    {
        //
        // no memory
        //
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    //
    // reset interface list count
    //
    FDODeviceExtension->InterfaceListCount = 0;

    do
    {
        //
        // parse configuration descriptor
        //
        InterfaceDescriptor = USBD_ParseConfigurationDescriptorEx(ConfigurationDescriptor, ConfigurationDescriptor, InterfaceIndex, -1, -1, -1, -1);
        if (InterfaceDescriptor)
        {
            //
            // store in interface list
            //
            ASSERT(FDODeviceExtension->InterfaceListCount < DescriptorCount);
            FDODeviceExtension->InterfaceList[FDODeviceExtension->InterfaceListCount].InterfaceDescriptor = InterfaceDescriptor;
            FDODeviceExtension->InterfaceListCount++;
        }
        else
        {
            DumpConfigurationDescriptor(ConfigurationDescriptor);
            DumpFullConfigurationDescriptor(FDODeviceExtension, ConfigurationDescriptor);

            //
            // see issue
            // CORE-6574 Test 3 (USB Web Cam)
            //
            if (FDODeviceExtension->DeviceDescriptor && FDODeviceExtension->DeviceDescriptor->idVendor == 0x0458 && FDODeviceExtension->DeviceDescriptor->idProduct == 0x705f)
                ASSERT(FALSE);
        }

        //
        // move to next interface
        //
        InterfaceIndex++;

    }while(InterfaceIndex < DescriptorCount);

    //
    // sanity check
    //
    ASSERT(FDODeviceExtension->InterfaceListCount);

    //
    // done
    //
    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 篇笔记 写笔记

USBCCGP 配置设备
获取设备的配置描述符通过前面已经知道,其包括的内容比较多,如接口描述符、端点描述符等,其中有很多我们进行数据通讯的信息,所以下来我们就要对设备进行配置。在USBCCGP USB复合设备驱动中,由于其将各个独立的接口会枚举成一个单独的设备,所以一般情况下有几个接口描述符就会被枚举成几个设备(UVC设备......
关注公众号
  • HID人机交互
  • Linux&USB
  • UAC音频
  • TYPE-C
  • USB规范
  • USB大容量存储
  • USB百科
  • USB周边
  • UVC摄像头
  • Windows系统USB
  • 音视频博客
  • 取消
    感谢您的支持,我会继续努力的!
    扫码支持
    扫码打赏,你说多少就多少

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

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