FYI: forcing a USB host port to full-speed (on Linux)

Werner Almesberger werner at almesberger.net
Wed Aug 10 11:10:51 EDT 2011


Background: on M1, some problems were observed with the USB-JTAG
dongle that may point to USB signal integrity issues. A cable change
apparently only brought temporary relief.

High-Speed USB is quite finicky. It is considerably easier to meet
signal integrity requirements of Full-Speed USB. I would therefore
suggest to try operating a USB device that exhibits problems at
High-Speed, at Full-Speed instead.

On Linux, it is possible to force some High-Speed capable USB host
ports to use Full-Speed mode.

To explain how this works, here's some more background: Low-Speed
and Full-Speed USB is typically implemented by an UHCI or OHCI
controller. When High-Speed was introduced, a dedicated High-Speed
controller, the EHCI, was added to the UHCI/OHCI. When a High-Speed
device is connected to the port, the EHCI takes care of it. If the
device is Full-Speed or Low-Speed, the EHCI is disconneced from the
port and the UHCI/OHCI is connected instead.

More recently, Full-Speed and Low-Speed functionality is being moved
into the EHCI, which then no longer needs a companion UHCI/OHCI. But
many (most ?) USB hosts in use today still use the old arrangement.

If the EHCI of a Linux system uses a companion UHCI/OHCI, the EHCI
can be disabled on a per port basis as follows:

- pick a USB port that's connected to a root hub without
  intermediate hubs, internal or external. Normally, all USB ports
  on a PC mainboard connect to a root hub.

- plug in the device and let it enumerate

- dmesg  should show - towards the end - something like

  usb 2-1: new high speed USB device [...]

  The first number (2) is the bus/controller. The second number is
  the port. It is important that the device enumerates as High-Speed
  (if it already enumerates as Full-Speed or Low-Speed, you
  shouldn't be reading this anyway ;-)

- echo PORT >/sys/bus/usb/drivers/usb/usbBUS/../companion

  where PORT and BUS are the numbers from above, e.g.,

  echo 1 >/sys/bus/usb/drivers/usb/us2/../companion

  Please pay attention to putting a space between the port number
  and the > sign.

- check dmesg. It should say something like

  usb 2-1: USB disconnect, address 21
  ...
  usb 6-1: new full speed USB device [...]

  Note that the bus number changes, because each controller (EHCI
  and UHCI/OHCI) has its own bus.

The device will now operate as Full-Speed device. Also, if you
remove the device and plug something else into the port, it will
work as Full-Speed. If you want to return the port to High-Speed,
do this:

- echo -PORT >/sys/bus/usb/drivers/usb/usbBUS/../companion

  using the EHCI (!) PORT and BUS numbers from above, e.g.,

  echo -1 >/sys/bus/usb/drivers/usb/us2/../companion

- check dmesg. It should say something like

  usb 6-1: USB disconnect, address 21
  ...
  usb 2-1: new high speed USB device [...]

  The bus number changes back to the EHCI's bus.

If your EHCI implements Full-Speed and Low-Speed without a companion
UHCI/OHCI controller, this approach won't work. I don't know if
there is currently a generally applicable way to still force
Full-Speed operation in such cases.

As an alternative, you can try connecting the device via an old USB
1.1 (Low-Speed and Full-Speed only) hub. This will also make sure
that High-Speed is not selected, but adding a hub may cause other
complications.

Some more background on USB error behaviour: USB protects its
packets with a CRC. If a packet is corrupted and fails the CRC
check, it is retransmitted. If there are too many retransmissions,
the USB subsystem will eventually decide that the device doesn't
work and give up. But it tolerates quite high error rates.

Unfortunately, if there are enough errors, every once in a while, an
incorrect packet will have a correct CRC, and slip through the
check. Upper layers may then just happily accept the packet as valid
without further checking, and let it do some damage (e.g., derail
a protocol, corrupt file data being transferred, etc.)

I've seen this happen in real life when I had excessive capacitative
loading on the D+/D- signals of a Full-Speed device. I then added
some more integrity checks to my protocol. And indeed, every 1 hours
or so, my program would encounter a packet that had passes USB's CRC
check but failed my additional integrity check.

- Werner




More information about the discussion mailing list


interactive