0

我有一个 Variscite DART-MX8M 评估板,我正在尝试将其用作 USB 小工具,因此我编写了一个与 gadgetfs 对话的用户模式小工具驱动程序(安装在 gadgetfs 上/dev/gadget)。它总是向主机返回设备描述符,但有时在响应对字符串描述符的请求时会失败。症状是当我将字符串写回端点 0 时,我得到EBUSY; 重试无济于事,因为随后的尝试得到ESRCH.

它不会在第一次请求字符串描述符时发生,正如您在下面看到主机获取制造商和供应商的位置(我认为是通过 udev 规则)但lsusb无法检索它们。但是当它确实失败时,它总是在执行的同一点。

我已经在内核中进行了一些打印跟踪(5.4.85,Variscite 的“Dunfell”版本),并找到EBUSY来源

    /* we share one TRB for ep0/1 */
    if (!list_empty(&dep->pending_list)) {
      ret = -EBUSY;
      goto out;
    }

但我不明白为什么我们会碰到这个。

我制作了最简单的驱动程序来重现它 - 它只有一个配置,没有端点(不包括 ep0):

#include <linux/usb/ch9.h>
#include <linux/usb/gadgetfs.h>

#include <fcntl.h>
#include <unistd.h>

#include <errno.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define USB_DEV "/dev/gadget/dwc3-gadget"

enum string_id {
    STRINGID_MANUFACTURER = 1,
    STRINGID_PRODUCT,
    STRINGID_SERIAL,
};

static const char *get_string_descriptor(enum string_id sid)
{
    static const char mfr[] = { 10, USB_DT_STRING, 84, 0, 111, 0, 98, 0, 121, 0};
    static const char prod[] = { 16, USB_DT_STRING, 119, 0, 105, 0, 100, 0, 103, 0, 101, 0, 116, 0};
    static const char ser[] = { 8, USB_DT_STRING, 118, 0, 49, 0};
    switch (sid) {
    case STRINGID_MANUFACTURER:
        return mfr;
    case STRINGID_PRODUCT:
        return prod;
    case STRINGID_SERIAL:
        return ser;
    }

    static const char fallback[] = { 20, USB_DT_STRING, 40, 0, 109, 0, 105, 0,
        115, 0, 115, 0, 105, 0, 110, 0, 103, 0, 41, 0 };
    return fallback;
}

static int write_string_descriptor(int fd, enum string_id sid) {
    const char *buf = get_string_descriptor(sid);
    return write(fd, buf, buf[0]);
}

static void handle_setup_request(int fd, struct usb_ctrlrequest* setup)
{
    int status;
    uint8_t buffer[512];
    pthread_t thread;

    printf("Setup request %d\n", setup->bRequest);

    switch (setup->bRequest) {
    case USB_REQ_GET_DESCRIPTOR:
        if (setup->bRequestType != USB_DIR_IN)
            goto stall;
        switch (setup->wValue >> 8) {
        case USB_DT_STRING:
            printf("Get string id #%d(max length %d)\n", setup->wValue & 0xff,
                   setup->wLength);
            status = write_string_descriptor(fd, setup->wValue & 0xff);
            // Error
            if (status < 0) {
                fprintf(stderr, "Failed to write string descriptor %d\n", setup->wValue & 0xff);
            }
            return;
        default:
            fprintf(stderr, "Cannot return descriptor %d\n", (setup->wValue >> 8));
        }
        break;
    case USB_REQ_SET_CONFIGURATION:
        printf("Ignoring configuration value %d\n", setup->wValue);
        // Just ACK
        status = read(fd, &status, 0);
        return;
    case USB_REQ_GET_INTERFACE:
        printf("GET_INTERFACE\n");
        buffer[0] = 0;
        write(fd, buffer, 1);
        return;
    case USB_REQ_SET_INTERFACE:
        printf("SET_INTERFACE\n");
        // ACK
        status = read(fd, &status, 0);
        return;
    }

 stall:
    fprintf(stderr, "Stalled\n");
    // Error
    if (setup->bRequestType & USB_DIR_IN)
        read (fd, &status, 0);
    else
        write (fd, &status, 0);
}

static void handle_ep0(int fd)
{
    while (1) {
        struct usb_gadgetfs_event events[5];

        int readsize = read(fd, &events, sizeof events);
        if (readsize < 0) {
            fprintf(stderr, "Read error %d(%m)\n", readsize);
            return;
        }

        const int nevents = readsize / sizeof events[0];
        int surplus = readsize % sizeof events[0];
        printf("%d event(s) and %d extra\n", nevents, surplus);

        for (int i = 0;  i < nevents;  ++i) {
            switch (events[i].type) {
            case GADGETFS_CONNECT:
                printf("EP0 CONNECT\n");
                break;
            case GADGETFS_DISCONNECT:
                printf("EP0 DISCONNECT\n");
                break;
            case GADGETFS_SETUP:
                printf("EP0 SETUP\n");
                handle_setup_request(fd, &events[i].u.setup);
                break;
            case GADGETFS_NOP:
            case GADGETFS_SUSPEND:
                break;
            }
        }
    }
}

int main()
{
    const int fd = open(USB_DEV, O_RDWR|O_SYNC|O_EXCL);
    if (fd < 0) {
        fprintf(stderr, "Unable to open %s(%m)\n", USB_DEV);
        return EXIT_FAILURE;
    }

    const struct usb_device_descriptor device_descriptor =
        { .bLength = USB_DT_DEVICE_SIZE,
          .bDescriptorType = USB_DT_DEVICE,
          .bDeviceClass = USB_CLASS_COMM,
          .bDeviceSubClass = 0,
          .bDeviceProtocol = 0,
          .idVendor = 0xAA00, // Fake vendor ID
          .idProduct = 0xBB, // Fake product ID
          .bcdDevice = 0x0200, // Version
          .iManufacturer = STRINGID_MANUFACTURER,
          .iProduct = STRINGID_PRODUCT,
          .iSerialNumber = STRINGID_SERIAL,
          .bNumConfigurations = 1
        };
    const struct usb_interface_descriptor if_descriptor = {
        .bLength = sizeof if_descriptor,
        .bDescriptorType = USB_DT_INTERFACE,
        .bInterfaceNumber = 0,
        .bAlternateSetting = 0,
        .bNumEndpoints = 2,
        .bInterfaceClass = USB_CLASS_COMM,
        .bInterfaceSubClass = 0,
        .bInterfaceProtocol = 0,
        .iInterface = 0
    };
    const struct usb_config_descriptor config = {
        .bLength = sizeof config,
        .bDescriptorType = USB_DT_CONFIG,
        .wTotalLength = sizeof config + sizeof if_descriptor,
        .bNumInterfaces = 1,
        .bConfigurationValue = 1,
        .iConfiguration = 0,
        .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER,
        .bMaxPower = 1
    };

    uint8_t init_config[2048];
    uint8_t* p = init_config;

    printf("Start init\n");
    *p++ = 0;
    *p++ = 0;
    *p++ = 0;
    *p++ = 0;

#define FETCH(desc)                             \
    memcpy(p, &desc, desc.bLength);             \
    p += desc.bLength;

    FETCH(config);
    FETCH(if_descriptor);
    FETCH(config);              /* again for a high-speed i/f */
    FETCH(if_descriptor);

    FETCH(device_descriptor);

    // Configure ep0
    int send_size = p - init_config;
    int sent = write(fd, init_config, send_size);

    if (sent != send_size) {
        fprintf(stderr, "Write error %d/%d (%m)\n", sent/send_size);
        return EXIT_FAILURE;
    }

    printf("ep0 configured\n");

    handle_ep0(fd);
}

在不更改代码的情况下,我出人意料地得到了两个不同的结果,正如从使用lsusb -v -d aa00:00bb. 当它起作用时,我得到:

Device Descriptor:
  bLength                18
  bDescriptorType         1
  bcdUSB               2.00
  bDeviceClass            2 Communications
  bDeviceSubClass         0
  bDeviceProtocol         0
  bMaxPacketSize0        64
  idVendor           0xaa00
  idProduct          0x00bb
  bcdDevice            2.00
  iManufacturer           1 Toby
  iProduct                2 widget
  iSerial                 3 v1
  bNumConfigurations      1
Device Qualifier (for other device speed):
  bLength                10
  bDescriptorType         6
  bcdUSB               2.00
  bDeviceClass            2 Communications
  bDeviceSubClass         0
  bDeviceProtocol         0
  bMaxPacketSize0        64
  bNumConfigurations      1
Device Status:     0x0000
  (Bus Powered)

当它失败时:

Bus 001 Device 027: ID aa00:00bb Toby widget
Device Descriptor:
  bLength                18
  bDescriptorType         1
  bcdUSB               2.00
  bDeviceClass            2 Communications
  bDeviceSubClass         0
  bDeviceProtocol         0
  bMaxPacketSize0        64
  idVendor           0xaa00
  idProduct          0x00bb
  bcdDevice            2.00
  iManufacturer           1 (error)
  iProduct                2 (error)
  iSerial                 3 (error)
  bNumConfigurations      1

dwc3在我卸载驱动程序并重新加载它之前,它不会再次工作。

这是失败案例的相关内核日志消息(我启用CONFIG_USB_GADGET_VERBOSE了这些消息):

[342748.061482] gadgetfs: bound to dwc3-gadget driver
[342748.066303] gadgetfs: bound to gadget device
[342748.071482] gadgetfs: ep0_read wait
[342748.344072] gadgetfs: connected
[342748.347308] gadgetfs: event[0] = 1
[342748.347929] gadgetfs: ep0_read wait
[342748.352265] gadgetfs: disconnected
[342748.355755] gadgetfs: event[0] = 2
[342748.356315] gadgetfs: ep0_read wait
[342748.504041] gadgetfs: connected
[342748.507269] gadgetfs: event[0] = 1
[342748.507842] gadgetfs: ep0_read wait
[342748.510596] gadgetfs: delegate req80.06 v0300 i0000 l255
[342748.510600] gadgetfs: event[0] = 3
[342748.511208] gadgetfs: usb_ep_queue returned 0
[342748.511213] gadgetfs: ep0_read wait
[342748.511557] gadgetfs: delegate req80.06 v0302 i0028 l255
[342748.511563] gadgetfs: event[0] = 3
[342748.511630] gadgetfs: usb_ep_queue returned 0
[342748.511635] gadgetfs: ep0_read wait
[342748.511934] gadgetfs: delegate req80.06 v0301 i0028 l255
[342748.511940] gadgetfs: event[0] = 3
[342748.511989] gadgetfs: usb_ep_queue returned 0
[342748.511995] gadgetfs: ep0_read wait
[342748.512181] gadgetfs: delegate req80.06 v0303 i0028 l255
[342748.512187] gadgetfs: event[0] = 3
[342748.512232] gadgetfs: usb_ep_queue returned 0
[342748.512238] gadgetfs: ep0_read wait
[342748.512711] gadgetfs: configuration #1
[342748.516551] gadgetfs: event[0] = 3
[342748.517139] gadgetfs: ep0_read wait
[342748.953968] gadgetfs: delegate req80.06 v0300 i0000 l4
[342748.953974] gadgetfs: event[0] = 3
[342748.954544] dwc3 38100000.usb: ep0out: pending_list empty
[342748.960062] gadgetfs: usb_ep_queue returned -16
[342748.960108] gadgetfs: ep0_read wait

(来自的行dwc是我添加到上ret = -EBUSY图所示的行)。

Wireshark 跟踪可能将手指指向SET_CONFIGURATION,因为它一直有效到该点:

No. Time           delta          Source    Length Info                                      Descriptor no
  1 0.000000       0.000000       host      64     GET DESCRIPTOR Request DEVICE             0x00
  2 0.004048       0.004048       1.113.0   82     GET DESCRIPTOR Response DEVICE            
  3 0.004074       0.000026       host      64     GET DESCRIPTOR Request CONFIGURATION      0x00
  4 0.004669       0.000595       1.113.0   73     GET DESCRIPTOR Response CONFIGURATION     
  5 0.004682       0.000013       host      64     GET DESCRIPTOR Request CONFIGURATION      0x00
  6 0.005918       0.001236       1.113.0   110    GET DESCRIPTOR Response CONFIGURATION     
  7 0.005948       0.000030       host      64     GET DESCRIPTOR Request STRING             0x00
  8 0.570791       0.564843       1.113.0   68     GET DESCRIPTOR Response STRING            
  9 0.570822       0.000031       host      64     GET DESCRIPTOR Request STRING             0x02
 10 0.571913       0.001091       1.113.0   86     GET DESCRIPTOR Response STRING            
 11 0.571933       0.000020       host      64     GET DESCRIPTOR Request STRING             0x01
 12 0.572662       0.000729       1.113.0   84     GET DESCRIPTOR Response STRING            
 13 0.572680       0.000018       host      64     GET DESCRIPTOR Request STRING             0x03
 14 0.573539       0.000859       1.113.0   98     GET DESCRIPTOR Response STRING            
 15 0.573806       0.000267       host      64     SET CONFIGURATION Request                 
 16 0.578297       0.004491       1.113.0   64     SET CONFIGURATION Response                
 17 0.578325       0.000028       host      64     GET DESCRIPTOR Request STRING             0x04
 18 5.703230       5.124905       1.113.0   64     GET DESCRIPTOR Response                   
 19 5.703244       0.000014       host      64     GET DESCRIPTOR Request STRING             0x04
 20 10.823171      5.119927       1.113.0   64     GET DESCRIPTOR Response                   
       

什么可能导致此故障,我需要做些什么来避免它?

4

1 回答 1

0

我发现了一些似乎可以解决这个问题的东西。我不太喜欢它,应该有一些不涉及任意睡眠的东西!

我将设置配置处理程序更改为在回复前等待 10 毫秒,并测试写入 ACK 的结果。单凭其中任何一项都是不够的。

    case USB_REQ_SET_CONFIGURATION:
        printf("Ignoring configuration value %d\n", setup->wValue);
        usleep(10'000);
        // Just ACK
        status = read(fd, &status, 0);
        if (status < 0) {
            goto stall;
        }
        return;
于 2021-03-13T13:07:51.843 回答