URB URB_FUNCTION_SELECT_INTERFACE分析详解
2023-10-24
本文链接为:http://www.usbzh.com/article/detail-1290.html ,欢迎转载,转载请附上本文链接。
URB_FUNCTION_SELECT_INTERFACE对应的USB标准请求是SetInterface。
很奇怪,一个是Select,一个是Set.是微软故意的么。
在SetInterface中,其wIndex对应的是InterfaceId,wValue对应的是AlterInterfaeId,如下所示:
| bmRequestType(1) | bRequest(1) | wValue(2) | wIndex(2) | wLength(2) |
|---|---|---|---|---|
| 01 | 0B | 可替换接口值 | 接口 | 0 |
- bmRequestType:x01表示从主机到设备,请求标准命令,接收者为接口。
- bRequest:0x0B,表示设置接口。
- wValue:可替换设置值
- wIndex:接口ID
所以如在USBIP中,当为URB_FUNCTION_SELECT_INTERFACE时,其8字节的SETUP包会设置如下:
csp->wLength = 0;
csp->wValue.W = urb_si->Interface.AlternateSetting;
csp->wIndex.W = urb_si->Interface.InterfaceNumber;
但其实际的结果处理是通过函数vpdo_select_interface实现的,并且其对应的URB结构体为:URB_SELECT_INTERFACE
static NTSTATUS
post_select_interface(pvpdo_dev_t vpdo, PURB urb)
{
struct _URB_SELECT_INTERFACE *urb_seli = &urb->UrbSelectInterface;
return vpdo_select_interface(vpdo, &urb_seli->Interface);
}
URB_SELECT_INTERFACE对应的结构体为:
struct _URB_SELECT_INTERFACE {
struct _URB_HEADER Hdr;
USBD_CONFIGURATION_HANDLE ConfigurationHandle; //配置句柄,当SelectConfigure时保存
USBD_INTERFACE_INFORMATION Interface;
};
而USBD_INTERFACE_INFORMATION内容如下:
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;
可见,其重点是USBD_PIPE_INFORMATION数组的设置,其个数由NumberOfPipes句柄。
USBD_PIPE_INFORMATION中需要总线驱动手动填充相应的数据结构,其比较重要的是PipeHandle,当数据通讯时以此为依据。
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;
在USBIP中,其内容设置过程如下:
PAGEABLE NTSTATUS
vpdo_select_interface(pvpdo_dev_t vpdo, PUSBD_INTERFACE_INFORMATION info_intf)
{
NTSTATUS status;
if (vpdo->dsc_conf == NULL) {
DBGW(DBG_WRITE, "failed to select interface: empty configuration descriptor\n");
return STATUS_INVALID_DEVICE_REQUEST;
}
status = setup_intf(info_intf, vpdo->dsc_conf, vpdo->speed);
if (NT_SUCCESS(status))
{
//USBIP 保存当前的接口ID和备用接口ID
vpdo->current_intf_num = info_intf->InterfaceNumber;
vpdo->current_intf_alt = info_intf->AlternateSetting;
}
return status;
}
setup_intf内容如下:
NTSTATUS
setup_intf(USBD_INTERFACE_INFORMATION *intf, PUSB_CONFIGURATION_DESCRIPTOR dsc_conf, UCHAR speed)
{
PUSB_INTERFACE_DESCRIPTOR dsc_intf;
if (sizeof(USBD_INTERFACE_INFORMATION) - sizeof(USBD_PIPE_INFORMATION) > intf->Length)
{
DBGE(DBG_URB, "insufficient interface information size?\n");
///TODO: need to check
return STATUS_SUCCESS;
}
dsc_intf = dsc_find_intf(dsc_conf, intf->InterfaceNumber, intf->AlternateSetting);
if (dsc_intf == NULL)
{
DBGW(DBG_IOCTL, "no interface desc\n");
return STATUS_INVALID_DEVICE_REQUEST;
}
intf->Class = dsc_intf->bInterfaceClass;
intf->SubClass = dsc_intf->bInterfaceSubClass;
intf->Protocol = dsc_intf->bInterfaceProtocol;
intf->InterfaceHandle = TO_INTF_HANDLE(intf->InterfaceNumber, intf->AlternateSetting);
intf->NumberOfPipes = dsc_intf->bNumEndpoints;
if (!setup_endpoints(intf, dsc_conf, dsc_intf, speed))
return STATUS_INVALID_DEVICE_REQUEST;
return STATUS_SUCCESS;
}
接口句柄的生成规则:
#define TO_INTF_HANDLE(intf_num, altsetting) ((USBD_INTERFACE_HANDLE)((intf_num << 8) + altsetting))
其首选通过dsc_find_intf函数找到对应的接口,然后设备接口相关的信息。
PUSB_INTERFACE_DESCRIPTOR
dsc_find_intf(PUSB_CONFIGURATION_DESCRIPTOR dsc_conf, UCHAR intf_num, USHORT alt_setting)
{
return USBD_ParseConfigurationDescriptorEx(dsc_conf, dsc_conf, intf_num, alt_setting, -1, -1, -1);
}
然后填充管道结构体USBD_PIPE_INFORMATION信息。
static NTSTATUS
setup_endpoints(USBD_INTERFACE_INFORMATION *intf, PUSB_CONFIGURATION_DESCRIPTOR dsc_conf, PUSB_INTERFACE_DESCRIPTOR dsc_intf, UCHAR speed)
{
PVOID start = dsc_intf;
ULONG n_pipes_setup;
unsigned int i;
n_pipes_setup = (intf->Length - sizeof(USBD_INTERFACE_INFORMATION)) / sizeof(USBD_PIPE_INFORMATION) + 1;
if (n_pipes_setup < intf->NumberOfPipes)
{
DBGW(DBG_URB, "insufficient interface information size: %u < %u\n", n_pipes_setup, intf->NumberOfPipes);
}
else
{
n_pipes_setup = intf->NumberOfPipes;
}
for (i = 0; i < n_pipes_setup; i++)
{
PUSB_ENDPOINT_DESCRIPTOR dsc_ep;
dsc_ep = dsc_next_ep(dsc_conf, start);
if (dsc_ep == NULL)
{
DBGW(DBG_IOCTL, "no ep desc\n");
return FALSE;
}
set_pipe(&intf->Pipes[i], dsc_ep, speed);
DBGI(DBG_IOCTL, "ep setup[%u]: %s\n", i, dbg_pipe(&intf->Pipes[i]));
start = dsc_ep;
}
return TRUE;
}
PUSB_ENDPOINT_DESCRIPTOR
dsc_next_ep(PUSB_CONFIGURATION_DESCRIPTOR dsc_conf, PVOID start)
{
PUSB_COMMON_DESCRIPTOR dsc = (PUSB_COMMON_DESCRIPTOR)start;
if (dsc->bDescriptorType == USB_ENDPOINT_DESCRIPTOR_TYPE)
dsc = NEXT_DESC(dsc);
return (PUSB_ENDPOINT_DESCRIPTOR)USBD_ParseDescriptors(dsc_conf, dsc_conf->wTotalLength, dsc, USB_ENDPOINT_DESCRIPTOR_TYPE);
}
填充的过程内容如下:
static void
set_pipe(PUSBD_PIPE_INFORMATION pipe, PUSB_ENDPOINT_DESCRIPTOR ep_desc, unsigned char speed)
{
pipe->MaximumPacketSize = ep_desc->wMaxPacketSize;
pipe->EndpointAddress = ep_desc->bEndpointAddress;
pipe->Interval = ep_desc->bInterval;
pipe->PipeType = ep_desc->bmAttributes & USB_ENDPOINT_TYPE_MASK;
/* From usb_submit_urb in linux */
if (pipe->PipeType == USB_ENDPOINT_TYPE_ISOCHRONOUS && speed == USB_SPEED_HIGH)
{
USHORT mult = 1 + ((pipe->MaximumPacketSize >> 11) & 0x03);
pipe->MaximumPacketSize &= 0x7ff;
pipe->MaximumPacketSize *= mult;
}
pipe->PipeHandle = MAKE_PIPE(ep_desc->bEndpointAddress, pipe->PipeType, ep_desc->bInterval);
}
这里注意其PipeHandle的生成:
#define MAKE_PIPE(ep, type, interval) ((USBD_PIPE_HANDLE)((ep) | ((interval) << 8) | ((type) << 16)))
本文链接为:http://www.usbzh.com/article/detail-1290.html ,欢迎转载,转载请附上本文链接。
HID人机交互QQ群:564808376
UAC音频QQ群:218581009
UVC相机QQ群:331552032
BOT&UASP大容量存储QQ群:258159197
STC-USB单片机QQ群:315457461
USB技术交流QQ群2:580684376
USB技术交流QQ群:952873936 USB技术交流3:1031974172
Windows下USB驱动基础知识





