NetBSD-Bugs archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

kern/48358: Repeated low-speed USB control transfers returning short data fail on EHCI

>Number:         48358
>Category:       kern
>Synopsis:       Repeated low-speed USB control transfers returning short data 
>fail on EHCI
>Confidential:   no
>Severity:       serious
>Priority:       high
>Responsible:    kern-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Fri Nov 01 12:20:00 +0000 2013
>Originator:     Andreas Gustafsson
>Release:        NetBSD 6.0
System: NetBSD
Architecture: x86_64
Machine: amd64

Trying to track down the cause of PR 46696 as well as some other USB
communication problems, I constructed the attached test case.  It
reads the device descriptor of a USB device repeatedly in a tight
loop, asking for 64 bytes, which will result in a "short read" since
the actual descriptor size is smaller.  The short read is to mimic
the behavior of kernel versions afflicted by 46696.

When I run the test against a low-speed USB device attached to an EHCI
controller, it fails.  I have tried three different EHCI controllers:
the on-board Intel one, a VIA-based PCI card, and an ALI-based PCI
card, and it fails with all of them (in the case of the PCI cards, an
external high-speed hub must be used to reproduce the failure, because
otherwise the device attaches to the OHCI/UHCI companion controller).

If the device is attached to an OHCI or UHCI rather than an EHCI,
the test passes.

If I use a full-speed device rather than a low-speed one, the test

If I reduce TRANSFER_SIZE from 64 bytes to the actual size of the
descriptor or smaller, the test passes.

If I introduce a 10 ms delay between the calls to usb_control_msg(),
the test passes.

This may or may not actually be the cause of PR 46696, and may the
cause of problems other than 46696, so I'm making this a separate
PR rather than adding to 46696.

Other PRs applying to the same machine are 38970, 46596, 46696, 47153,
and 48211.


How to run the test:

0. Extract the Makefile and test.c included below.

1. Find a low-speed USB device.  I used a USB-to-PS/2 adapter dongle
(with no PS/2 keyboard or mouse connected), but a keyboard or mouse
should work, too.

2. Run "usbdevs -v" to find its vendor and product IDs.  Mine says:

  port 4 addr 4: low speed, power 400 mA, config 1, PS2toUSB Adapter(0x0020), 
GASIA(0x0e8f), rev 2.80

Note that the product ID is listed first and the vendor ID second,
which is the opposite order from the kernel configuration file and
test program below.

3. Create a kernel configuration file which will attach the device
using the generic USB driver "ugen" instead of the uhid driver.
Assuming the above product and vendor IDs, the kernel configuration
file should look like

  include "arch/amd64/conf/GENERIC"
  ugen* at uhub? vendor 0x0e8f product 0x0020 flags 1

4. Build, install, and boot the new kernel.

5. Install the devel/libusb package if you don't already have it.

6. Edit the VENDOR and PRODUCT definitions in test.c to
match your device.

7. Build the test program by running "make".

8. Attach the device and inspect the console messages to verify that
it attached as ugen rather than uhid, and that it attached to an 
EHCI controller (via a hub).

9. Run the test program as root:

  sudo ./test

The output is self-explanatory.

Contents of the Makefile:

test: test.c
        cc -I/usr/pkg/include -L/usr/pkg/lib test.c -R/usr/pkg/lib -lusb -o test

Contents of test.c:

 * Test repeated short transfers on USB endpoint 0.
 * Rembember to update the VENDOR and PRODUCT
 * definitions to match your device.

#include <memory.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>

#include <usb.h>

#define VENDOR 0x0e8f
#define PRODUCT 0x0020

#define TRANSFER_SIZE 64

/* Timeout in milliseconds */
#define TIMEOUT 5000

static void
fatal(const char *format, ...) {
    va_list args;
    va_start(args, format);
    vfprintf(stderr, format, args);
    fprintf(stderr, "\n");

main(int argc, char *argv[]) {
    struct usb_bus *bus;
    struct usb_device *dev;
    struct usb_dev_handle *handle;
    int r, n;
    unsigned char buffer[TRANSFER_SIZE];


    for (bus = usb_busses; bus != NULL; bus = bus->next) {
        for (dev = bus->devices; dev != NULL; dev = dev->next) {
            if (dev->descriptor.idVendor == VENDOR &&
                dev->descriptor.idProduct == PRODUCT)
                goto found;
    fatal("device not found");

    handle = usb_open(dev);

    for (n = 0; n < 1000; n++) {
        memset(buffer, 0, sizeof buffer);
        r = usb_control_msg(handle,
                            /* bmRequestType */
                            USB_TYPE_STANDARD | USB_RECIP_DEVICE | 
                            /* bRequest */
                            /* wValue: top byte is 1 = device descriptor */
                            1 << 8,
                            /* wIndex */
                            buffer, TRANSFER_SIZE, TIMEOUT);
        if (r < 0)
            fatal("FAILED: usb_control_msg() failed on iteration %d, return 
value = %d", n, r);
    usb_release_interface(handle, 0);
    return 0;


Home | Main Index | Thread Index | Old Index