USB通用驱动源码分析
+ -

USBCCGP 获取USB设备描述符

2021-09-15 596 0

USB设备的描述符是通过函数USBCCGP_GetDescriptors来获取的。
descriptor.c

    /* Get descriptors */
    Status = USBCCGP_GetDescriptors(DeviceObject);
    if (!NT_SUCCESS(Status))
    {
        /* Failed to start lower device */
        DPRINT1("FDO_StartDevice failed to get descriptors with %x\n", Status);
        return Status;
    }

USB描述符主要是获取设备描述符配置描述符
两种设备描述符分别存储于FDO扩展结构体的DeviceDescriptor和ConfigurationDescriptor指向的内存中。

typedef struct
{
...
    PUSB_DEVICE_DESCRIPTOR DeviceDescriptor;                 // usb device descriptor
    PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor;   // usb configuration descriptor
    ...
    }FDO_DEVICE_EXTENSION, *PFDO_DEVICE_EXTENSION;

获取设备描述符

设备描述符是标准的数据结构,且数据大小已知,可一次性获取。数据结构定义如下:

//
// USB 1.1: 9.6.1 Device, Table 9-7. Standard Device Descriptor
// USB 2.0: 9.6.1 Device, Table 9-8. Standard Device Descriptor
// USB 3.0: 9.6.1 Device, Table 9-8. Standard Device Descriptor
//
typedef struct _USB_DEVICE_DESCRIPTOR {
    UCHAR   bLength;
    UCHAR   bDescriptorType;
    USHORT  bcdUSB;
    UCHAR   bDeviceClass;
    UCHAR   bDeviceSubClass;
    UCHAR   bDeviceProtocol;
    UCHAR   bMaxPacketSize0;
    USHORT  idVendor;
    USHORT  idProduct;
    USHORT  bcdDevice;
    UCHAR   iManufacturer;
    UCHAR   iProduct;
    UCHAR   iSerialNumber;
    UCHAR   bNumConfigurations;
} USB_DEVICE_DESCRIPTOR, *PUSB_DEVICE_DESCRIPTOR;

描述符的获取通过一个通用函数USBCCGP_GetDescriptors来实现,而获取设备描述符的代码如下:

     Status = USBCCGP_GetDescriptor(DeviceExtension->NextDeviceObject, USB_DEVICE_DESCRIPTOR_TYPE, sizeof(USB_DEVICE_DESCRIPTOR), 0, 0, (PVOID*)&DeviceExtension->DeviceDescriptor);
     if (!NT_SUCCESS(Status))
     {
         //
         // failed to get device descriptor
         //
         DeviceExtension->DeviceDescriptor = NULL;
         return Status;
     }

可以看到,获取设备描述符需要提定数据类型:

#define USB_DEVICE_DESCRIPTOR_TYPE                          0x01

获取配置描述符

配置描述符里面不光有配置描述符,还包括可能多个接口描述符端点描述符,在UVC设备中,甚至还包括接口关联描述符等其它接口描述符
配置描述符的总大小因硬件设备的不同而不同,但在配置描述符的头有一个成员指定了配置描述符的总大小。所在在获取配配描述符就需要通过二次来获取,第一次只获取配置描述符的基本大小,从基本数据结构中解析出总大小后,再次获取全部的描述符。

  //
    // 2.0 now get basic configuration descriptor
    //
    Status = BulkUsbGetDescriptor(DeviceExtension->TopOfStackDeviceObject, USB_CONFIGURATION_DESCRIPTOR_TYPE, sizeof(USB_CONFIGURATION_DESCRIPTOR), 0, 0, (PVOID*)&DeviceExtension->ConfigurationDescriptor);
    if (!NT_SUCCESS(Status))
    {
        ExFreePool(DeviceExtension->DeviceDescriptor);
        DeviceExtension->DeviceDescriptor = NULL;
        return Status;
    }

    DescriptorLength = DeviceExtension->ConfigurationDescriptor->wTotalLength;

    //
    // release basic descriptor
    //
    ExFreePool(DeviceExtension->ConfigurationDescriptor);
    DeviceExtension->ConfigurationDescriptor = NULL;

    //
    // 2.1 allocate full descriptor
    //
    Status = BulkUsbGetDescriptor(DeviceExtension->TopOfStackDeviceObject, USB_CONFIGURATION_DESCRIPTOR_TYPE, DescriptorLength, 0, 0, (PVOID*)&DeviceExtension->ConfigurationDescriptor);
    if (!NT_SUCCESS(Status))
    {
        ExFreePool(DeviceExtension->DeviceDescriptor);
        DeviceExtension->DeviceDescriptor = NULL;
        return Status;
    }

BulkUsbGetDescriptor的实现

从BulkUsbGetDescriptor的实现可以看出,其还是按照USB的规范,创建URB,然后再下发到PDO中,由PDO实现数据的组织返回。

NTSTATUS BulkUsbGetDescriptor(
    IN PDEVICE_OBJECT DeviceObject,
    IN UCHAR DescriptorType,
    IN ULONG DescriptorLength,
    IN UCHAR DescriptorIndex,
    IN LANGID LanguageId,
    OUT PVOID* OutDescriptor)
{
    PURB Urb;
    NTSTATUS Status;
    PVOID Descriptor;

    ASSERT(DeviceObject);
    ASSERT(OutDescriptor);
    ASSERT(DescriptorLength);

    Descriptor = ExAllocatePool(NonPagedPool, DescriptorLength);
    if (!Descriptor)
    {
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    Urb = (PURB)ExAllocatePool(NonPagedPool, sizeof(URB));
    if (!Urb)
    {
        ExFreePool(Descriptor);
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    UsbBuildGetDescriptorRequest(Urb,
        sizeof(Urb->UrbControlDescriptorRequest),
        DescriptorType,
        DescriptorIndex,
        LanguageId,
        Descriptor,
        NULL,
        DescriptorLength,
        NULL);

   // Status = CallUSBD(DeviceObject, Urb);
    Status = USBCCGP_SyncUrbRequest(DeviceObject, Urb);
    ExFreePool(Urb);
    if (NT_SUCCESS(Status))
    {
        *OutDescriptor = Descriptor;
    }

    return Status;
}

IPR和URB的关系

IRP是WINDOWS定义的设备之间通讯的请求包,而IO_STACK_LOCATION是各层设备之间的数据栈,而URB一般用于上下层设备之间的传输,故在WINDOWS中,其作为IO_STACK_LOCATION的一个指针存在。
这点在USBCCGP_SyncUrbRequest可以得到很明显的体现。


NTSTATUS
USBCCGP_SyncUrbRequest(
    IN PDEVICE_OBJECT DeviceObject,
    OUT PURB UrbRequest)
{
    PIRP Irp;
    PIO_STACK_LOCATION IoStack;
    KEVENT Event;
    NTSTATUS Status;

    /* Allocate irp */
    Irp = IoAllocateIrp(DeviceObject->StackSize, FALSE);
    if (!Irp)
    {
        /* No memory */
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    /* Initialize event */
    KeInitializeEvent(&Event, NotificationEvent, FALSE);

    /* Get next stack location */
    IoStack = IoGetNextIrpStackLocation(Irp);

    /* Initialize stack location */
    IoStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
    IoStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_SUBMIT_URB;
    IoStack->Parameters.Others.Argument1 = (PVOID)UrbRequest;
    IoStack->Parameters.DeviceIoControl.InputBufferLength = UrbRequest->UrbHeader.Length;
    Irp->IoStatus.Status = STATUS_SUCCESS;

    /* Setup completion routine */
    IoSetCompletionRoutine(Irp,
                           USBSTOR_SyncForwardIrpCompletionRoutine,
                           &Event,
                           TRUE,
                           TRUE,
                           TRUE);

    /* Call driver */
    Status = IoCallDriver(DeviceObject, Irp);

    /* Check if request is pending */
    if (Status == STATUS_PENDING)
    {
        /* Wait for completion */
        KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);

        /* Update status */
        Status = Irp->IoStatus.Status;
    }

    /* Free irp */
    IoFreeIrp(Irp);

    /* Done */
    return Status;
}

而函数数据返回的成功,也是通过完成函数来实现的,这点和上一节中USBCCGP_SyncForwardIrp的实现大同小异,请大家注意类比,只是这里多了一个创建URB,并设备URB指针的过程。

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

USB音箱 UAC设备描述符分析
这里看到,USB规范采用的是USB1.1版本,不过字符串中又显示的是”USB2.0 Device”,所以这里就有点迷~关于设备描述符各字段的详细解释,详见USB规范中设备描述符一节。从bDeviceClass,bDeviceSubClass和bDeviceSubClass都被置为0来看,这是一个典......
Windows10设备管理器USB设备描述符请求失败(未知的usb设备)
在电脑中插入USB设备后,有时会报USB设备描述符请求失败,导致USB设备在设备管理器中枚举失败。一种专业的解决办法见:http://www.usbzh.com/article/detail-1059.htmlUSB设备描述符请求失败现象USB设备描述符请求失败在设备管理器中发现通用串行总线......
USB设备描述符布局
通用串行总线USB以其强大的功能,方便的连接广受用户使用,这些都得益于USB灵活多样的描述符。USB设备通过配置描述符,接口描述符,端点描述符等描述符实现设备功能。USB设备灵活多变的角色转换是通过USB的配置描述符来实现的,但一个USB设备有多个配置描述符时,就表明了这个USB设备支持多角的转换......
USB超高速设备描述符的bMaxPacketSize0为0x09
USB3的设备中,其USB设备描述符和2.0的结构一致,不过在含义上有的字段稍有区别。比如说在USB3.2的版本中,其设备描述符中的bMaxPacketSize0和USB2.0有就所不同。在USB2.0中bMaxPacketSize0代表的是USB设备默认端点0的最大包的大小,一般为0x40即64字......
华为UAC耳机 设备描述符
从UAC的设备描述符可知,音频是属于接口级别的,在UAC1.0版本,是需要将相关的配置参数bDeviceClass,bDeviceSubClass,bDeviceProtocoll置为0。对于别的参数,可参考USB设备描述符进行相关配置。本设备只有一个配置描述符,且端点0的bMaxPacketSiz......
HID 设备描述符关系
HID设备连接到USB主机后,主机通过发送Get_Descriptor请求读取HID设备的描述符,了解描述符对了解USB设备是至关重要的。HID设备的描述符HID设备除了支持USB设备的5种标准描述符之外,还支持HID设备特有的3种描述符。这些描述符是:USB标准描述符:设备描述符、配置描述符、......
USB设备描述符、配置描述符、接口描述符和端点描述符的拓扑关系
在USB设备的逻辑组织中,包含设备、配置、接口和端点4个层次。每个USB设备都提供了不同级别的配置信息,可以包含一个或多个配置,不同的配置使设备表现出不同的功能组合(在探测/连接期间需从其中选定一个),配置由多个接口组成。一次只能有一个配置是活动的。大多数的设备只有一个配置和一个接口,当一个设备......
USB 设备描述符
USB设备描述符是USB设备在进行插拔和初始化过程中,最先被主机读取的一部分信息,它包含了设备的一些基本属性信息,如USB规范版本、设备类、设备子类、设备协议、最大数据包长度等。USB设备描述符通常是在设备插入时被主机读取,在获取到设备描述符后,主机可以自动加载相应的驱动程序,完成设备的初始化和配......
USB-UAC麦克风 设备描述符
UAC麦克风采用的设备描述符结构和USB设备描述符的结构一致,只是有些字段的值有所要求。偏移地址字段长度值描述0bLength10x12设备描述符的总和长度1bDescriptorType10x01设备描述符类型2bcdUSB 20x01001.00使用的USB版本号4bDeviceClass10x......
USB 设备限定描述符
设备限定描述符描述符用在当一个设备能够工作在不同的速度下时,会获取设备限定描述符。同时支持全速与高速的设备,必须有一个Device Qualifier Descriptor。当设备转换速度的时候,设备描述符中的某些字段可能改变。Device Qualifier Descriptor描述符储存当前不......
USB2.0设备枚举-获取设备描述符事务
USB设备与主机进行数据传输或进行设备配置时,有一些常见的术语如事务,令牌,包等。USB设备在枚举过程中有2次获取设备描述符的过程,这里分别标识为首次获取设备描述符和分配地址后的获取描述符。两次获取描述符的过程基本类似,惟一的区别是首次首次获取描述符由于设备尚未分配地址,故使用地址0与主机进行通......
UAC设备描述符
由于音频设备在USB规范中只能定义在接口级别。所在USB规范中没有定义特定的音频设备描述符,所以想要找到音频设备的信息,是在这些设备(如复合设备)的接口描述符信息中找到。因此设备描述符中的bDeviceClass, bDeviceSubClass 和bDeviceProtocol 中的应为0xEF,......
HID报表描述符与设备描述符、配置描述符、字符串描述符的通讯格式对比
报表描述符是USB HID规范中一个很重要的概念。USB HID设备报表描述符内容表述了该HID设备的功能及数据传输的格式。USB HID报表描述符在概念上和设备描述符,配置描述符,接口描述符,端点描述符和其它USB设备描述符类似,但是还是有一些细微的区别。我里我们做一个简要的区别:设备描述符、......
Windows下的USB设备描述符分析工具-UsbTreeView
UsbTreeView是Windows下的一款关于USB描述符分析的工具,该工具俱有以下特点:实时显示USB设备的连接状态以树状的形式显示连接到PC机中的所有USB设备,集线器和控制器可完美的分析连接到PC机中USB设备的各类USB描述符信息。支持USB1.0/USB2.0/USB3.x规范协议......
UAC麦克风设备描述符
所有的USB设备第一个要设计的描述符基本都是设备描述符。这里的UAC音频设备描述符数据配置如下:结构体定义如下:/* USB_DT_DEVICE: Device descriptor */struct usb_device_descriptor { __u8 bLength; ......
关注公众号
取消
感谢您的支持,我会继续努力的!
扫码支持
扫码打赏,你说多少就多少

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

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