驱动程序中USB设备的配置过程

驱动程序中USB设备的配置过程(参考Windows XP DDK)
2009-08-22 21:59

DDK 驱动程序写得很规范, USB 初始化、数据传输的过程写的很清楚。通过阅读 DDK 驱动程序,我对原来 USB 驱动程序中许多不理解的地方有了更清楚的理解. 下面就参照 DDK 提供的 iso_usb 例子对 USB 设备的配置过程进行总结。

1. 驱动程序加载后首先执行 DriverEntry 入口函数。该函数设定了对各个 IRP 进行处理的派遣函数。

2.DriverEntry 函数执行完成后,开始执行 AddDevice 函数。这个函数创建设备对象把设备对象连接到设备堆栈上,清除 DO_DEVICE_INITIALIZING 标志。然后配置管理器向驱动程序发送一个即插即用请求 IRP_MN_START_DEVICE ,而调用下面的 HandleStartDevice 函数。

3. HandleStartDevice 函数中完成了 USB 设备的配置过程:首先为设备选择一个配置(大多数设备仅有一种配置)。选定了某种配置后,接着应该选择配置中的一个或多个接口。然后向总线驱动程序发送配置选择 URB ,总线驱动程序接收到该 URB 后向设备发出命令使用选定的配置和接口。

1 )为设备选择配置的过程其实就是获取设备的配置描述符的过程。 Iso_usb 中使用了两个 URB 来读取配置描述符。

// 首先获取固定大小的配置描述符,这时,此描述符不包含接口描述符和端点描述符。

siz = sizeof(USB_CONFIGURATION_DESCRIPTOR);

configurationDescriptor = ExAllocatePool(NonPagedPool, siz);

if(configurationDescriptor) {

              //UsbBuildGetDescriptorRequest 函数构造指定类型的 urb

       UsbBuildGetDescriptorRequest(

              urb,

              (USHORT) sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST),

              USB_CONFIGURATION_DESCRIPTOR_TYPE,

              0,

              0,

              configurationDescriptor,

              NULL,

              sizeof(USB_CONFIGURATION_DESCRIPTOR),

              NULL);

              //CallUSBD 函数负责把 urb 转发到底层总线驱动程序

        ntStatus = CallUSBD(DeviceObject, urb);

        ……

}

……

// 然后获取全部的配置描述符,包括接口描述符和端点描述符

siz = configurationDescriptor->wTotalLength;

ExFreePool(configurationDescriptor);

configurationDescriptor = ExAllocatePool(NonPagedPool, siz);

if(configurationDescriptor) {

        UsbBuildGetDescriptorRequest(

              urb,

              (USHORT)sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST),

              USB_CONFIGURATION_DESCRIPTOR_TYPE,

              0,

              0,

              configurationDescriptor,

              NULL,

              siz,

              NULL);

        ntStatus = CallUSBD(DeviceObject, urb);

        ……

}

2 )从配置描述符中提取感兴趣的接口描述符,总线驱动程序提供了函数 USBD_ParseConfigurationDescriptorEx 以简化这个过程。

interfaceDescriptor =USBD_ParseConfigurationDescriptorEx(

                                             ConfigurationDescriptor,

                                    ConfigurationDescriptor,

                                     interfaceindex,

                                    0,

                                                               -1, -1, -1);

该函数各个参数的含义是:第一个参数是上一步获取的完整的配置描述符;第二个参数是描述符内部开始搜索的地址,如果从头开始搜索,需要设置和第一个参数相同;剩下的五个参数是和感兴趣的接口相关搜索关键字,分别是 InterfaceNumber, AlternateSetting, InterfaceClass, InterfaceSubClass, InterfaceProtoco 。但相关的关键字不需要的时候,可以设置成 -1

由于配置描述符中可能包含多个接口,所以驱动程序需要将上述函数返回的接口描述符保存在 USBD_INTERFACE_LIST_ENTRY 类型的数组中。 iso_usb 程序首先使用 ExAllocatePool 函数为接口描述符分配足够的内存。

interfaceList =ExAllocatePool(

               NonPagedPool,

               sizeof(USBD_INTERFACE_LIST_ENTRY) * (numberOfInterfaces + 1));

然后通过循环使用 USBD_ParseConfigurationDescriptorEx 函数获取的接口描述符对数组进行初始化。初始化时,应该把接口描述符地址赋给 USBD_INTERFACE_LIST_ENTRY 结构的 InterfaceDescriptor 成员,并把 Interface 成员置 NULL 。最后需要将数组的最后一个元素的两个成员全部置为 NULL

3 )初始化接口。首先调用 USBD_CreateConfigurationRequestEx 函数创建一个 urb 。然后需要对接口中的管道进行相应的初始化,最后将这个 urb 传递给底层驱动程序,由底层总线驱动程序完成接口的初始化。

urb = USBD_CreateConfigurationRequestEx(ConfigurationDescriptor, tmp);

Interface = &urb->UrbSelectConfiguration.Interface;

// 需要初始化管道的 MaximumTransferSize 成员。它代表单一 URB 能携带的最大数据量

for(i=0; i<Interface->NumberOfPipes; i++) {

            Interface->Pipes[i].MaximumTransferSize = <constant>

        }

ntStatus = CallUSBD(DeviceObject, urb);

4 )但 USB 设备配置完成之后,应该将一些句柄保存到设备扩展中供以后使用。

Ø         URB 成员 UrbSelectConfiguration.ConfigurationHandle 返回配置句柄;

Ø         USBD_INTERFACE_INFORMATION 结构中 InterfaceHandle 返回接口句柄;

Ø         每个 USBD_PIPE_INFORMATION 结构中都含有与端点对应的管道句柄 PipeHandle

5 )关闭设备。当驱动程序接到一个 IRP_MN_STOP_DEVICE 请求时,应该把设备置成为配置状态,创建并传递一个含有 NULL 配置指针的配置选择 URB 可以达到这个目的。

siz = sizeof(struct _URB_SELECT_CONFIGURATION);

urb = ExAllocatePool(NonPagedPool, siz);

UsbBuildSelectConfigurationRequest(urb, (USHORT)siz, NULL);

ntStatus = CallUSBD(DeviceObject, urb);

你可能感兴趣的:(struct,配置管理,null,interface,Descriptor,DDK)