如何在usb-vhci

时间:2016-11-07 09:55:05

标签: linux usb libusb

编辑:重新提出这个问题,因为我已经设法让基础工作正常,但仍然遇到问题。

我正在尝试使用usb-vhci模拟USB设备(条形码扫描仪)进行测试,我遇到了一些问题。

给出一些上下文:设备是CDC抽象调制解调器,客户端 - 一个java程序 - 使用AT命令通过串行线与它通信。

基本上,我已经启动并运行了设备,它可以正确注册,我可以从客户端接收命令并对其进行响应。

主要问题似乎是,一旦设备启动或从主机接收批量传输,它就会触发正在进行的批量传输和中断IN传输(大量数据,我的usbmon日志会增加到100 MB)秒)。

首先在启动时,它一直喷出(主要是)批量IN传输,直到我收到SET_CONTROL_LINE_STATE请求然后它们停止。然后,当客户端发送命令(通过串行设备的AT命令)时,它会再次启动。

我猜这是因为我对某些转移没有正确回应,但我无法弄清楚它是什么。

我一直在比较我的设备的usbmon输出和真实设备的输出,但到目前为止我还没有发现任何差异可以解释为什么我的模拟设备表现得像这样,真正的设备没有'吨。

我基本上从libusb_vhci / examples / virtual_device2.c中的示例代码开始,并将其调整为模仿实际设备。首先关闭设备描述符:

const uint8_t dev_desc[] = {
/* Device Descriptor */
0x12,       //bLength           18
0x01,       //bDescriptorType       1
0x00, 0x02, //bcdUSB            2.00
0x02,       //bDeviceClass          2 Communications
0x00,       //bDeviceSubClass       0
0x00,       //bDeviceProtocol       0
0x40,       //bMaxPacketSize0       64
0x5a, 0x06, //idVendor          065a
0x02, 0xa0, //idProduct         a002
0x00, 0x01, //bcdDevice         1.00
0x00,       //iManufacturer         0
0x01,       //iProduct          1
0x00,       //iSerial           0
0x01        //bNumConfigurations        1
};

const uint8_t conf_desc[] = {
/* Configuration Descriptor */
0x09,       //bLength           9
0x02,       //bDescriptorType       2
0x43, 0x00, //wTotalLength          67 ??
0x02,       //bNumInterfaces        2
0x01,       //bConfigurationValue       1
0x00,       //iConfiguration        0
0x80,       //bmAttributes (Bus Powered)    0x80
250,        //MaxPower          500mA

/* Interface Descriptor 0 */
0x09,       //bLength           9
0x04,       //bDescriptorType       4
0x00,       //bInterfaceNumber      0
0x00,       //bAlternateSetting     0
0x01,       //bNumEndpoints         1
0x02,       //bInterfaceClass       2 Communications
0x02,       //bInterfaceSubClass        2 Abstract (modem)
0x00,       //bInterfaceProtocol        0 None
0x00,       //iInterface            0
/* CDC Header */
0x05,       //bLength           7
0x24,       //bDescriptorType       5
0x00,       //bEndpointAddress      0x01 EP 1 OUT
0x10,       //bcdCDC            1.10
0x01,       //"
/* CDC Call Management */
0x05,       //bLength           3
0x24,       //CDC_CS_INTERFACE
0x01,       //CDC_CALL_MANAGEMENT
0x01,       //bmCapabilities        0x01
0x00,       //bDataInterface        0
    /* CDC ACM */
0x04,       //bLength           2
0x24,       //CDC_CS_INTERFACE
0x02,       //CDC_ABSTRACT_CONTROL_MANAGEMENT
0x02,       //bmCapabilities        0x02
/* CDC Union */
0x05,       //bLength           3
0x24,       //CDC_CS_INTERFACE
0x06,       //CDC_UNION
0x00,       //bMasterInterface      0
0x01,       //bSlaveInterface       1
/* Endpoint Descriptor */
0x07,       //bLength           7
0x05,       //bDescriptorType       5
0x83,       //bEndpointAddress      0x83 EP 3 IN
0x03,       //bmAttributes          3
0x40, 0x00, //wMaxPacketSize        0x0040 1x 64 bytes
0x0a,       //bInterval         10      
/* Interface Descriptor 1 */
0x09,       //bLength           9
0x04,       //bDescriptorType       4
0x01,       //bInterfaceNumber      1
0x00,       //bAlternateSetting     0
0x02,       //bNumEndpoints         2
0x0a,       //bInterfaceClass       10 CDC Data
0x00,       //bInterfaceSubClass        0
0x00,       //bInterfaceProtocol        0
0x00,       //iInterface            0
/* Endpoint Descriptor */
0x07,       //bLength           7
0x05,       //bDescriptorType       5
0x01,       //bEndpointAddress      0x01 EP 1 OUT
0x02,       //bmAttributes          2
0x40, 0x00, //wMaxPacketSize        0x0040 1x 64 bytes
0x00,       //bInterval         0
/* Endpoint Descriptor */
0x07,       //bLength           7
0x05,       //bDescriptorType       5
0x82,       //bEndpointAddress      0x82 EP 2 IN
0x02,       //bmAttributes          2
0x40,0x00,  //wMaxPacketSize        0x0040 1x 64 bytes
0x00        //bInterval         0
};

const uint8_t str0_desc[] = {
0x04,       //bLength           4
0x03,       //bDescriptorType       3
0x09, 0x04  //bLanguage             0409 US
};

const uint8_t *str1_desc = 
(uint8_t *)"\x36\x03O\0p\0t\0i\0c\0o\0n\0 \0U\0S\0B\00\0B\0a\0r\0c\0o\0d\0e\0 \0R\0e\0a\0d\0e\0r";

主要功能与示例中的相同,但process_urb()函数主要是已更改的功能。控制部分基本上完好无损,但我已经为一些额外的设置数据包添加了处理:

uint8_t rt = urb->bmRequestType;
uint8_t r = urb->bRequest;
if(rt == 0x00 && r == URB_RQ_SET_CONFIGURATION)
{
    devlog("URB_RQ_SET_CONFIGURATION\n");
    urb->status = USB_VHCI_STATUS_SUCCESS;
}
else if(rt == 0x00 && r == URB_RQ_SET_INTERFACE)
{
    devlog("URB_RQ_SET_INTERFACE\n");
    urb->status = USB_VHCI_STATUS_SUCCESS;
}
else if (rt == 0x21 && r == 0x20)
{
    devlog("URB_CDC_SET_LINE_CODING\n");
    urb->status = USB_VHCI_STATUS_SUCCESS;
}
else if (rt == 0x21 && r == 0x22)
{
    devlog("URB_CDC_SET_CONTROL_LINE_STATE\n");
    urb->status = USB_VHCI_STATUS_SUCCESS;
}
else if(rt == 0x80 && r == URB_RQ_GET_DESCRIPTOR)
{
    int l = urb->wLength;
    uint8_t *buffer = urb->buffer;
    devlog("GET_DESCRIPTOR ");
    switch(urb->wValue >> 8)
    {
    case 0:
        puts("WTF_DESCRIPTOR");
        urb->status = USB_VHCI_STATUS_SUCCESS;
        break;
    case 1:
        puts("DEV_DESC");
        if(dev_desc[0] < l) l = dev_desc[0];
        memcpy(buffer, dev_desc, l);
        urb->buffer_actual = l;
        urb->status = USB_VHCI_STATUS_SUCCESS;
        break;
    case 2:
        puts("CONF_DESC");
        if(conf_desc[2] < l) l = conf_desc[2];
        memcpy(buffer, conf_desc, l);
        urb->buffer_actual = l;
        urb->status = USB_VHCI_STATUS_SUCCESS;
        break;
    case 3:
        devlog(" Reading string %d\n", urb->wValue & 0xff);
        switch(urb->wValue & 0xff)
        {
        case 0:
            if(str0_desc[0] < l) l = str0_desc[0];
            memcpy(buffer, str0_desc, l);
            urb->buffer_actual = l;
            urb->status = USB_VHCI_STATUS_SUCCESS;
            break;
        case 1:
            if(str1_desc[0] < l) l = str1_desc[0];
            memcpy(buffer, str1_desc, l);
            urb->buffer_actual = l;
            urb->status = USB_VHCI_STATUS_SUCCESS;
            break;
        default:
            devlog(" Trying to read unknown string: %d\n",urb->wValue & 0xff);
            urb->status = USB_VHCI_STATUS_STALL;
            break;
        }
        break;
    default:
        devlog(" UNKNOWN: wValue=%d (%d)\n",urb->wValue, urb->wValue >> 8);
        urb->status = USB_VHCI_STATUS_STALL;
        break;
    }
}
else
{
    devlog("OTHER bmRequestType %x bRequest %x\n", rt, r);
    urb->status = USB_VHCI_STATUS_STALL;
}

主要问题是处理非控制转移。这是我目前的实施:

/* handle non-control sequences */
if(!usb_vhci_is_control(urb->type)) {
    /* if we have a BULK OUT transfer */
    if (usb_vhci_is_bulk(urb->type) && usb_vhci_is_out(urb->epadr)) {
        /* we have a bulk out transfer, i.e. a command from client */
        int cmd = get_at_command(urb->buffer, urb->buffer_actual);
        if (cmd == COMMAND_Z1) {
            /* we have request for version, need to wait for the BULK IN transfer */
            last_command = cmd;
        }
        urb->status = USB_VHCI_STATUS_SUCCESS;
        return;
    }

    /* if we have a BULK IN transfer */
    if (usb_vhci_is_bulk(urb->type) && usb_vhci_is_in(urb->epadr)) {
        /* we have a BULK IN transfer, use it to respond to any buffered commands */
        if (last_command) {
            /* send version */
            memcpy(urb->buffer, VERSION_STR, strlen(VERSION_STR));
            urb->buffer_actual = strlen(VERSION_STR);
            last_command = 0;
            urb->status = USB_VHCI_STATUS_SUCCESS;
            return;
        }
    }

    urb->status = USB_VHCI_STATUS_SUCCESS;
    return;
}

这是我的设备启动时获得的usbmon日志的片段:

ffff880510727900 266671312 S Bi:5:002:2 -115 128 <
ffff880510727f00 266671315 C Bi:5:002:2 0 0
ffff880510727f00 266671316 S Bi:5:002:2 -115 128 <
ffff880510727cc0 266671319 C Ii:5:002:3 0:8 0
ffff880510727cc0 266671321 S Ii:5:002:3 -115:8 64 <
ffff880514d80900 266671323 S Co:5:002:0 s 21 22 0000 0000 0000 0
ffff880510727780 266671324 C Bi:5:002:2 0 0
ffff880510727780 266671325 S Bi:5:002:2 -115 128 <
ffff8805101096c0 266671329 C Bi:5:002:2 0 0
ffff8805101096c0 266671333 S Bi:5:002:2 -115 128 <
ffff8805107273c0 266671339 C Bi:5:002:2 0 0
ffff8805107273c0 266671344 S Bi:5:002:2 -115 128 <
ffff880510109b40 266671348 C Bi:5:002:2 0 0
ffff880510109b40 266671350 S Bi:5:002:2 -115 128 <
ffff880510109000 266671354 C Bi:5:002:2 0 0
ffff880510109000 266671357 S Bi:5:002:2 -115 128 <
ffff880510727d80 266671360 C Bi:5:002:2 0 0
ffff880510727d80 266671361 S Bi:5:002:2 -115 128 <
ffff880510109a80 266671363 C Bi:5:002:2 0 0
ffff880510109c00 266671370 C Bi:5:002:2 0 0
...

所以,这基本上就是我被困住的地方。我有一个功能正常的设备,但是大量的传输基本上扼杀了我的系统,使它无用。任何帮助或信息将不胜感激!

1 个答案:

答案 0 :(得分:1)

现在看来我已经能够解决我的大多数问题了,问题确实是我没有正确回应事件。

在对真实设备的usbmon输出做了一些更详细的分析之后,我注意到它用-ENOENT响应多余的中断传输,而我用0响应(即成功)。更多挖掘usb-vhci代码显示此错误代码对应于USB_VHCI_STATUS_CANCELED,一旦我开始响应此操作,我在设备中的行为与真实设备相同。基本上我把它添加到process_urb的非控制部分:

/* if we have a INTERRUPT transfer */
if (usb_vhci_is_int(urb->type)) {
    urb->status = USB_VHCI_STATUS_CANCELED;
    return;
}
但是,我并没有完全走出困境。我注意到同样的事情似乎适用于批量IN转移;我在启动过程中获得了大量的信息(一旦设置完成就会停止) - 再次 - 对于真实设备来说似乎不是这样,真实设备 - 再次 - 响应这些(多余的) )使用-ENOENT进行传输。我试过这样做,看起来效果很好。额外的传输确实停止,它的行为与真实设备一样,但不幸的是,它也导致我的设备无法将数据发送回客户端。我修改了我的批量IN处理代码,如下所示:

/* if we have a BULK IN transfer */
if (usb_vhci_is_bulk(urb->type) && usb_vhci_is_in(urb->epadr)) {
    if (last_command) {
        // send version
        memcpy(urb->buffer, VERSION_STR, strlen(VERSION_STR));
        urb->buffer_actual = strlen(VERSION_STR);
        last_command = 0;
        urb->status = USB_VHCI_STATUS_SUCCESS;
    } else {
        urb->status = USB_VHCI_STATUS_CANCELED;
    }
    return;
}

我认为这个应该工作,即如果我在之前的批量OUT转移中收到命令,我应该能够使用IN转移来响应(因为我一直在做)如果没有命令,我只需用-ENOENT回复。出于某种原因,这不起作用,我不知道为什么。

我注意到有关来自真实设备的跟踪的另一件事:虽然它确实使用-ENOENT响应这些批量传输,但是在收到请求后它们会发送响应超过10秒(!)!不知道那是什么,但如果有人有想法,我会非常感激。