USBD_CreateConfigurationRequestEx

USBD_CreateConfigurationRequestEx()是用来格式化URB的,URB发送到USB总线驱动程序完成真正的配置操作。
UsbBuildSelectConfigurationRequest()通常是将最后一个参数设置为NULL来卸载USB的当前配置的。

对于Multiple Interfaces的USB设备,用UsbBuildSelectInterfaceRequest()来选择接口: 实际上是格式化URB,以待发送给USB总线驱动程序。

通常的流程:先取得目标配置的配置描述符,然后调用USBD_ParseConfigurationDescriptor()来辅助得到PUSBD_INTERFACE_INFORMATION List, 设置带宽等参数后,接着调用USBD_CreateConfigurationRequestEx()生成URB,然后发送到USB总线驱动程序完成配置。接下来就可以调用UsbBuildSelectConfigurationRequest()来选择接口。

PURB NTAPI
USBD_CreateConfigurationRequestEx(
    PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor,
    PUSBD_INTERFACE_LIST_ENTRY InterfaceList
    )
{
    PURB Urb;
    ULONG UrbSize = 0;
    ULONG InterfaceCount = 0, PipeCount = 0;
    ULONG InterfaceNumber, EndPointNumber;
    PUSBD_INTERFACE_INFORMATION InterfaceInfo;

    while(InterfaceList[InterfaceCount].InterfaceDescriptor)
    {
        // pipe count
        PipeCount += InterfaceList[InterfaceCount].InterfaceDescriptor->bNumEndpoints;

        // interface count
        InterfaceCount++;
    }

    // size of urb
    UrbSize = GET_SELECT_CONFIGURATION_REQUEST_SIZE(InterfaceCount, PipeCount);

    // allocate urb
    Urb = ExAllocatePool(NonPagedPool, UrbSize);
    if (!Urb)
    {
        // no memory
        return NULL;
    }

    // zero urb
    RtlZeroMemory(Urb, UrbSize);

    // init urb header
    Urb->UrbSelectConfiguration.Hdr.Function =  URB_FUNCTION_SELECT_CONFIGURATION;
    Urb->UrbSelectConfiguration.Hdr.Length = UrbSize;
    Urb->UrbSelectConfiguration.ConfigurationDescriptor = ConfigurationDescriptor;

    // init interface information
    InterfaceInfo = &Urb->UrbSelectConfiguration.Interface;
    for (InterfaceNumber = 0; InterfaceNumber < InterfaceCount; InterfaceNumber++)
    {
        // init interface info
        InterfaceList[InterfaceNumber].Interface = InterfaceInfo;
        InterfaceInfo->InterfaceNumber = InterfaceList[InterfaceNumber].InterfaceDescriptor->bInterfaceNumber;
        InterfaceInfo->AlternateSetting = InterfaceList[InterfaceNumber].InterfaceDescriptor->bAlternateSetting;
        InterfaceInfo->NumberOfPipes = InterfaceList[InterfaceNumber].InterfaceDescriptor->bNumEndpoints;

        // store length
        InterfaceInfo->Length = GET_USBD_INTERFACE_SIZE(InterfaceList[InterfaceNumber].InterfaceDescriptor->bNumEndpoints);

        // sanity check
        //C_ASSERT(FIELD_OFFSET(USBD_INTERFACE_INFORMATION, Pipes) == 16);

        for (EndPointNumber = 0; EndPointNumber < InterfaceInfo->NumberOfPipes; EndPointNumber++)
        {
            // init max transfer size
            InterfaceInfo->Pipes[EndPointNumber].MaximumTransferSize = PAGE_SIZE;
        }

        // next interface info
        InterfaceInfo = (PUSBD_INTERFACE_INFORMATION) ((ULONG_PTR)InterfaceInfo + InterfaceInfo->Length);
    }

    return Urb;
}

从代码来看,先获取所有接口的端点数量,然后再使用GET_SELECT_CONFIGURATION_REQUEST_SIZE计算分配的URB空间总大小。


#define GET_SELECT_CONFIGURATION_REQUEST_SIZE(totalInterfaces, totalPipes) \
            ((sizeof(struct _URB_SELECT_CONFIGURATION) - sizeof(USBD_INTERFACE_INFORMATION)) + \
            ((totalInterfaces)*(sizeof(USBD_INTERFACE_INFORMATION) - sizeof(USBD_PIPE_INFORMATION))) + \
            ((totalPipes)*sizeof(USBD_PIPE_INFORMATION)))

URB_SELECT_CONFIGURATION等等小减去一个USBD_INTERFACE_INFORMATION的大小,减去的刚好是URB_SELECT_CONFIGURATION的最后一个成员变量Interface的大小。
最后加下totalInterfaces个接口和totalPipes的大小。

所以最终的内存布局为:

  • URB
    • USBD_INTERFACE_INFORMATION
      • USBD_PIPE_INFORMATION
      • USBD_PIPE_INFORMATION
      • USBD_PIPE_INFORMATION
    • USBD_INTERFACE_INFORMATION
      • USBD_PIPE_INFORMATION
      • USBD_PIPE_INFORMATION
      • USBD_PIPE_INFORMATION