Windows下USB驱动基础知识
+ -

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

0 篇笔记 写笔记

USB 设置接口SetInterface
ID请求码说明11SET_INTERFACE用于主机要求设备用某个描述符来描述接口SetInterface请求用于USB主机为设备指定的接口选择一个合适的替换值,该请求没有数据阶段。。bmRequestType(1)bRequest(1)wValue(2)......
Windows下USB驱动SET_INTERFAC失败 bad pipe flags
最近在调试一个UVC摄像头,由于项目的原因,需要在驱动下对UVC协议进行模拟,以实现在驱支层打下固件,从而读取数据的要求。本以为这是一个很简单的事,没想到还是耽搁了一点时间,花了一点小功夫。打开USB摄像头,对UVC协议的模拟主要是是USB特定类请求的模拟,通过我们的基础知识可知道,上层应用或驱动......
USB 设置配置(SetConfiguration)和设置接口(SetInterface)的区别与联系
在USB设备枚举的最后,主机都会对设备发送设置配置置(SetConfiguration)请求和对接口发送设置接口(SetInterface)请求,这两个控制请求在USB设备的工作中,具有重要的作用。 CTL 00 09 01 00 00 00 00 00 ......
USBIP 选择接口URB_FUNCTION_SELECT_INTERFACE
URB_FUNCTION_SELECT_INTERFACE命令发起static NTSTATUSstore_urbr_submit(PIRP irp, struct urb_req *urbr){... case URB_FUNCTION_SELECT_INTERFACE: ......
URB URB_FUNCTION_SELECT_INTERFACE分析详解
URB_FUNCTION_SELECT_INTERFACE对应的USB标准请求是SetInterface。很奇怪,一个是Select,一个是Set.是微软故意的么。在SetInterface中,其wIndex对应的是InterfaceId,wValue对应的是AlterInterfaeId,......
URB URB_FUNCTION_SELECT_CONFIGURATION分析详解
先对比一下URB_FUNCTION_SELECT_INTERFACE和URB_FUNCTION_SELECT_CONFIGURATION他们的结构体。struct _URB_SELECT_INTERFACE { struct _URB_HEADER Hdr; USBD_CONFIG......
关注公众号
  • HID人机交互
  • Linux&USB
  • UAC音频
  • CDC
  • TYPE-C
  • USB规范
  • USB大容量存储
  • USB百科
  • USB周边
  • UVC摄像头
  • USB资源
  • Windows系统USB
  • 音视频博客
  • 取消
    感谢您的支持,我会继续努力的!
    扫码支持
    扫码打赏,你说多少就多少

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

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