USB in Linux

Introduction




USB device


A USB device can:
  • provide a piece of hardware for USB communication
  • implement the USB protocol
  • implement some useful protocol
  • additional hardware/software for providing desired functionality

A device can be self powered, bus powered or both.

Whenever a USB device is attached to the bus:
  • it will be enumerated by the USB subsystem
  • An unique device number (1-127) is assigned
  • the device descriptor is read
  • The desciptor is a data structure which contains information about the device and its properties.

USB endpoint


We can think the USB endpoint as the networking port, different endpoint is used for different service.
Device may have up to 31 endpoints (including ep0):
  • Each of them gets a unique Endpoint address
  • Endpoint 0 may transfer data in both directions
  • All other endpoints may transfer data in one direction
    • IN
    • Transfer data from device to host
    • OUT
    • Transfer data from host to device
There are several Endpoint types:
  • Control
    • Bi-directional endpoint
    • Used for enumeration
    • Can be used for application
  • Interrupt
    • Transfers a small amount of low-latency data
    • Reserves bandwidth on the bus
    • Used for time-sensitive data (HID)
  • Bulk
    • Used for large data transfers
    • Used for large, time-insensitive data(Network packets, Mass Storage, etc).
    • Does not reserve bandwidth on bus, uses whatever time is left over
  • Isochronous
    • Transfers a large amount of time-sensitive data
    • Delivery is not guaranteed (no ACKs are sent)
    • Used for Audio and Video streams
    • Late data is as good as no data
    • Better to drop a frame than to delay and force a re-transmission

USB host Controllers


USB host controllers are compatible with either the Open Host Controller Interface (OHCI, by Compaq) or the Universal Host Controller Interface (UHCI, by Intel) standard.

Both types have the same capabilities and USB devices work with both host controller types.
Basically the hardware of UHCI is simpler and therefore it is cheaper, but needs a more complex device driver, which could cause slightly more CPU load.

USB Complete Fifth Edition, Chapter4 Enumeration: How the Host Learns about Devices

One of a hub’s duties is to detect attachment and removal of devices on its downstream-facing ports.
Each hub has an interrupt IN endpoint for reporting these events to the host.
On system boot-up, hubs inform the host if any devices are attached including additional downstream hubs and any devices attached to those hubs.
After boot-up, a host continues to poll periodically (USB 2.0) or receives ERDY TPs (Enhanced SuperSpeed) that request communications to learn of any newly attached or removed devices.

On learning of a new device,

  1. the host sends requests to the device’s hub to cause the hub to establish a communications path between the host and device.
  2. the host then attempts to enumerate the device by issuing control transfers containing standard USB requests to the device.
  3. All USB devices must support control transfers, standard requests, and endpoint zero.
For a successful enumeration, the device must respond to requests by returning requested information and taking other requested actions.

Getting to the Configured state

Device removal

Tips for successful enumeration

USB Debugging


# lsusb -t
/:  Bus 04.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/4p, 10000M
/:  Bus 03.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/12p, 480M
    |__ Port 1: Dev 2, If 0, Class=Hub, Driver=hub/4p, 480M
    |__ Port 5: Dev 3, If 0, Class=Human Interface Device, Driver=usbhid, 1.5M
    |__ Port 5: Dev 3, If 1, Class=Human Interface Device, Driver=usbhid, 1.5M
    |__ Port 6: Dev 4, If 0, Class=Human Interface Device, Driver=usbhid, 1.5M
    |__ Port 10: Dev 5, If 0, Class=Wireless, Driver=btusb, 12M
    |__ Port 10: Dev 5, If 1, Class=Wireless, Driver=btusb, 12M
/:  Bus 02.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/4p, 10000M
/:  Bus 01.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/1p, 480M
# lsusb
Bus 004 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 003 Device 004: ID 046d:c05a Logitech, Inc. M90/M100 Optical Mouse
Bus 003 Device 003: ID 046d:c31c Logitech, Inc. Keyboard K120
Bus 003 Device 005: ID 8087:0025 Intel Corp. 
Bus 003 Device 002: ID 1a40:0101 Terminus Technology Inc. Hub
Bus 003 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub

# lsusb -v -s 3:1
Bus 003 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Device Descriptor:
  bLength                18
  bDescriptorType         1
  bcdUSB               2.00
  bDeviceClass            9 Hub
  bDeviceSubClass         0 
  bDeviceProtocol         1 Single TT
  bMaxPacketSize0        64
  idVendor           0x1d6b Linux Foundation
  idProduct          0x0002 2.0 root hub
  bcdDevice            5.13
  iManufacturer           3 Linux 5.13.0-1008-intel xhci-hcd
  iProduct                2 xHCI Host Controller
  iSerial                 1 0000:00:14.0
  bNumConfigurations      1
  Configuration Descriptor:
    bLength                 9
    bDescriptorType         2
    wTotalLength       0x0019
    bNumInterfaces          1
    bConfigurationValue     1
    iConfiguration          0 
    bmAttributes         0xe0
      Self Powered
      Remote Wakeup
    MaxPower                0mA
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        0
      bAlternateSetting       0
      bNumEndpoints           1
      bInterfaceClass         9 Hub
      bInterfaceSubClass      0 
      bInterfaceProtocol      0 Full speed (or root) hub
      iInterface              0 
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x81  EP 1 IN
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0004  1x 4 bytes
        bInterval              12
Hub Descriptor:
  bLength              11
  bDescriptorType      41
  nNbrPorts            12
  wHubCharacteristic 0x000a
    No power switching (usb 1.0)
    Per-port overcurrent protection
    TT think time 8 FS bits
  bPwrOn2PwrGood       10 * 2 milli seconds
  bHubContrCurrent      0 milli Ampere
  DeviceRemovable    0x00 0x00
  PortPwrCtrlMask    0xff 0xff
 Hub Port Status:
   Port 1: 0000.0303 lowspeed power enable connect
   Port 2: 0000.0100 power
   Port 3: 0000.0100 power
   Port 4: 0000.0100 power
   Port 5: 0000.0303 lowspeed power enable connect
   Port 6: 0000.0507 highspeed power suspend enable connect
   Port 7: 0000.0100 power
   Port 8: 0000.0100 power
   Port 9: 0000.0100 power
   Port 10: 0000.0107 power suspend enable connect
   Port 11: 0000.0100 power
   Port 12: 0000.0100 power
can't get device qualifier: Resource temporarily unavailable
can't get debug descriptor: Resource temporarily unavailable
Device Status:     0x0001
  Self Powered

# lspci | grep USB
00:0d.0 USB controller: Intel Corporation Device 9a13 (rev 01)
00:0d.2 USB controller: Intel Corporation Device 9a1b (rev 01)
00:14.0 USB controller: Intel Corporation Device a0ed (rev 20)
00:14.1 USB controller: Intel Corporation Device a0ee (rev 20)

All USB information can be found under /sys/bus/usb/devices/ :

$ ls  /sys/bus/usb/devices/
1-0:1.0  1-1.1      1-1.2      1-1.2:1.1  1-1.2:1.3  1-10:1.0  1-1:1.0  1-2:1.0  2-0:1.0  4-0:1.0  usb2  usb4
1-1      1-1.1:1.0  1-1.2:1.0  1-1.2:1.2  1-10       1-10:1.1  1-2      1-2:1.1  3-0:1.0  usb1     usb3
  • usbX
    • X
    • ID of host controller on your machine.
  • X-A.B.C
  • Device path.
    • X
    • HCD ID.
    • A.B.C
    • Physical path to port where your USB device is connected.
  • X-A.B.C:Y.Z
    • X-A.B.C
    • Device path
    • Y
    • Active configuration
    • Z
    • bInterfaceNumber

Basic Information

To get a list of currently attached USB devices (including hubs);

$ sudo lsusb -v

/sys/bus/usb

The USB is a polled bus, the host polls each device, devices cannot initiate any communication.
  • /sys/bus/usb/drivers/.../new_id
  • Writing a device ID to this file will attempt to dynamically add a new device ID to a USB device driver. This may allow the driver to support more hardware than was included in the driver's static device ID support table at compile time. The format for the device ID is: idVendor idProduct bInterfaceClass RefIdVendor RefIdProduct The vendor ID and device ID fields are required, the rest is optional. The `Ref*` tuple can be used to tell the driver to use the same driver_data for the new device as it is used for the reference device.

Fixing USB Autosuspend

The Linux kernel automatically suspends USB devices when there is driver support and the devices are not in use.This saves quite a bit of power.

However, some USB devices are not compatible with USB autosuspend and will misbehave at some point. Affected devices are most commonly USB mice and keyboards.

In essence, this is not really a USB hardware problem, but perhaps more a Linux problem.
The actual fault lies with a misinterpretation of the eXtensible Host Controller Interface (xHCI) specification.
This issue previously did not exist with the older Enhanced Host Controller Interface (EHCI) specification.

Identify the device

To identify the affected USB device by its vendor and product ID:

$ lsusb -v
...
Bus 001 Device 002: ID 046d:c31c Logitech, Inc. Keyboard K120
Couldn't open device, some information will be missing
Device Descriptor:
  bLength                18
  bDescriptorType         1
  bcdUSB               1.10
  bDeviceClass            0 
  bDeviceSubClass         0 
  bDeviceProtocol         0 
  bMaxPacketSize0         8
    idVendor           0x046d Logitech, Inc.
    idProduct          0xc31c Keyboard K120
  bcdDevice           64.00
  iManufacturer           1 
  iProduct                2 
  iSerial                 0 
  bNumConfigurations      1
...
Wite down the numbers:
  • Bus
  • Device
  • idVendor
  • idProduct

Test

A simple test suffices to know whether the suspect USB device is really affected by autosuspend.

$ grep 046d /sys/bus/usb/devices/usb*/*/idVendor
/sys/bus/usb/devices/usb1/1-7/idVendor:046d
$ grep c31c /sys/bus/usb/devices/usb*/*/idProduct
/sys/bus/usb/devices/usb1/1-7/idProduct:c31c
If the device driver supports it, the USB power/control attribute will default to auto. The auto state is the normal state in which the kernel is allowed to autosuspend and autoresume the device.

$ cat /sys/bus/usb/devices/usb1/1-7/power/control
on
The on state means device autosuspend is not allowed, system suspends are still allowed.
If your device now works in the on state correctly, USB autosuspend is the problem at hand.

Solution

A permanent solution can only be achieved by creating a device-specific rules file for udev.
/etc/udev/rules.d/usb-power.rules:

# Logic Inc. Keyboard
ACTION=="add", SUBSYSTEM=="usb", ATTR{idVendor}=="046d", ATTR{idProduct}=="c31c", TEST=="power/control", ATTR{power/control}="auto"

Power Management for USB

What is Power Management?

Power Management (PM) is the practice of saving energy by suspending parts of a computer system when they aren't being used.
A suspended component can be "resumed" (returned to a functional full-power state) when the kernel needs to use it.

What is Remote Wakeup?

When a device has been suspended, it generally doesn't resume until the computer tells it to.
When a device is enabled for remote wakeup and it is suspended, it may resume itself (or send a request to be resumed) in response to some external event.
Examples include a suspended keyboard resuming when a key is pressed, or a suspended USB hub resuming when a device is plugged in.

When is a USB device idle?

A device is idle whenever the kernel thinks it's not busy doing anything important and thus is a candidate for being suspended.
Drivers are allowed to declare that a device isn't idle even when there's no actual communication taking place.

If a USB device has no driver, its usbfs file isn't open, and it isn't being accessed through sysfs, then it definitely is idle.

autosuspend

Dynamic suspends occur when the kernel decides to suspend an idle device.
This is called "autosuspend" for short.
In general, a device won't be autosuspended unless it has been idle for some minimum period of time, the so-called idle-delay time.

If a device has been autosuspended and a program tries to use it, the kernel will automatically resume the device (autoresume). It is worth mentioning that many USB drivers don't support autosuspend.
If a non-supporting driver is bound to a device, the device won't be autosuspended. In effect, the kernel pretends the device is never idle.

The user interface for autosuspend

The user interface for controlling dynamic PM is located in the power/ subdirectory of each USB device's sysfs directory, that is,

/sys/bus/usb/devices/.../power/
The relevant attribute files are:
  • power/wakeup
  • This file is empty if the device does not support remote wakeup.
    Otherwise the file contains either the word "enabled" or the word "disabled"
  • power/control
  • This file contains one of two words:
    • on
    • means that the device should be resumed and autosuspend is not allowed. (Of course, system suspends are still allowed.)
    • auto
    • is the normal state in which the kernel is allowed to autosuspend and autoresume the device.
  • power/autosuspend_delay_ms
  • This file contains the number of milliseconds the device should remain idle before the kernel will autosuspend it (the idle-delay time).
    • The default is 2000
    • 0 means to autosuspend as soon as the device becomes idle,
    • negative values mean never to autosuspend

the default idle-delay time

The default autosuspend idle-delay time (in seconds) is controlled by a module parameter in usbcore.

You can specify the value when usbcore is loaded. For example, to set it to 5 seconds instead of 2 you would do:


$ sudo modprobe usbcore autosuspend=5
If usbcore is compiled into the kernel rather than built as a loadable module, you can add this to the kernel's boot command line.

	usbcore.autosuspend=5
Setting the initial default idle-delay to -1 will prevent any autosuspend of any USB device. This has the benefit of allowing you then to enable autosuspend for selected devices.

Warnings

Many devices do not support power management very well.
You can suspend them, but when you try to resume them they disconnect themselves from the USB bus or they stop working entirely.

For this reason, by default the kernel disables autosuspend (the power/control attribute is initialized to "on") for all devices other than hubs.
Hubs, at least, appear to be reasonably well-behaved in this regard.
This means that non-hub devices won't be autosuspended unless the user or a program explicitly enables it.

USB Network Protocol

Past, present and future of USB Network Protocol

Most of 3G/LTE modems communicate by USB bus.

Kind of USB network drivers

USB network device driver can be classified by various protocols offered from communication module manufacturer or OS developer.
USB communications device class (or USB CDC class) is a composite USB device class.
The communications device class provides an interface for transmitting Ethernet or ATM frames onto some physical media.
CDC(Communications Device Class) includes all protocols for modem and Ethernet devices as device class according to USB-IF for communication device.

USB-IF defines CDC ECM (Ethernet Networking Control Model) and CDC EEM (Ethernet Emulation Devices) for USB network device and CDC NCM (Network Control Model) to improve packet handling mode for large size of data.

  • CDC ACM
  • Early USB modem offered CDC ACM (Abstract Control Model) .
    Most OS following CDC ACM, the USB-IF standard, include CDC ACM support driver, so USB modem manufacturers didn’t need to develop special USB device driver.
    ACM driver thinks UDB devices as a virtual modem or COM port.
    Device drivers Tx/Rx data and AT commands via AM driver (different channels)
  • CDC ECM
  • ECM protocol is used to Tx/Rx frames between host and device.
    ECM devices are usually LAN/WAN adaptors which can be assigned with MAC and IP.
  • CDC NCM
  • NCM protocol is used to Tx/Rx frames between host and device to Tx/Rx high data in high throughput.
    NCM devices are usually 3.5G/4G celluar adaptor.
  • RNDIS(Remote NDIS)
  • Microsoft produced RNDIS that is a variant of CDC ECM.
    Different kernel config is used to enable RNDIS for host and device:
    • host
    • To build rndis_host.ko:
      
      Device Drivers   --->
      	Network Device Support --->
      		Usb Network Adapters --->
      			Multi-purpose USB Networking Framework --->
      				CDC Ethernet Support
      				Host For RDNIS and ActiveSync Devices        
              
    • device
    • To build g_ether.ko:
      
      Device Drivers   --->
      	USB support --->
      		USB Gadget Support --->
      			USB Gadget Driver --->
      				Ethernet Gadget (with CDC Ethernet Support)
      				RNDIS support        
              
Qualcomm produced and used QMI/RmNet for communication and control on their chips.
Every CDC USB sub class is composed of a control and data interface.
No matter CDC-ECM or RNDIS, the USB networking model looks like:

The Linux USB Input Subsystem

Input subsystem is a collection of drivers that is designed to support all input devices under Linux.
Most of the drivers reside in drivers/input, although quite a few live in drivers/hid and drivers/platform.

The core of the input subsystem is the input module, which must be loaded before any other of the input modules - it serves as a way of communication between two groups of modules:

  • Device drivers
  • These modules talk to the hardware (for example via USB), and provide events (keystrokes, mouse movements) to the input module.
  • Event handlers
  • Event handlers get events from input core and distribute the events to userspace and in-kernel consumers, as needed.
    
    $ ls -l /dev/input
    total 0
    drwxr-xr-x 2 root root     140 Nov 19 01:13 by-id
    drwxr-xr-x 2 root root     140 Nov 19 01:13 by-path
    crw-rw---- 1 root input 13, 64 Nov 19 01:13 event0
    crw-rw---- 1 root input 13, 65 Nov 19 01:13 event1
    crw-rw---- 1 root input 13, 74 Nov 19 01:13 event10
    crw-rw---- 1 root input 13, 75 Nov 19 01:13 event11
    crw-rw---- 1 root input 13, 76 Nov 19 01:13 event12
    crw-rw---- 1 root input 13, 77 Nov 19 01:13 event13
    crw-rw---- 1 root input 13, 66 Nov 19 01:13 event2
    crw-rw---- 1 root input 13, 67 Nov 19 01:13 event3
    crw-rw---- 1 root input 13, 68 Nov 19 01:13 event4
    crw-rw---- 1 root input 13, 69 Nov 19 01:13 event5
    crw-rw---- 1 root input 13, 70 Nov 19 01:13 event6
    crw-rw---- 1 root input 13, 71 Nov 19 01:13 event7
    crw-rw---- 1 root input 13, 72 Nov 19 01:13 event8
    crw-rw---- 1 root input 13, 73 Nov 19 01:13 event9
    crw-rw---- 1 root input 13, 63 Nov 19 01:13 mice
    crw-rw---- 1 root input 13, 32 Nov 19 01:13 mouse0  
      
    For ex., the USB mouse will be available as a character device on major 13, minor 63.
    XFree can use it :
    
    Section "Pointer"
        Protocol    "ImPS/2"
        Device      "/dev/input/mice"
        ZAxisMapping 4 5
    EndSection    
        
You can use blocking and nonblocking reads on the /dev/input/eventX devices to get a number of input events.
The data structure of the input event:

struct input_event {
        struct timeval time;
        unsigned short type;
        unsigned short code;
        unsigned int value;
};
For ex., read the mouse input

$ sudo cat /dev/input/mouse0
characters should appear when you move the mouse.

  • Initially, the USB mouse used /sys/class/input/input3
  • 
    $ cat /sys/class/input/input3/name
    Logitech USB Optical Mouse
    		
  • After re-plug in the same USB mouse, /sys/class/input/input3 was gone, a new input instance /sys/class/input/input14 was used
  • 
    $ cat /sys/class/input/input14/name
    Logitech USB Optical Mouse  
    		

Part I

The three elements of the input subsystem are :
  • the input core
  • drivers
  • event handlers
The interaction between various elements is through events, which are implemented as structures:

struct input_dev {
    void *private;
    char *name;
    char *phys;
    char *uniq;
    struct input_id id;

    unsigned long evbit[NBITS(EV_MAX)];
    unsigned long keybit[NBITS(KEY_MAX)];
    unsigned long relbit[NBITS(REL_MAX)];
    unsigned long absbit[NBITS(ABS_MAX)];
    unsigned long mscbit[NBITS(MSC_MAX)];
    unsigned long ledbit[NBITS(LED_MAX)];
    unsigned long sndbit[NBITS(SND_MAX)];
    unsigned long ffbit[NBITS(FF_MAX)];
    int ff_effects_max;
    
    unsigned int keycodemax;
    unsigned int keycodesize;
    void *keycode;
    
    unsigned int repeat_key;
    struct timer_list timer;
    
    struct pm_dev *pm_dev;
    int state;
    
    int sync;
    
    int abs[ABS_MAX + 1];
    int rep[REP_MAX + 1];

    unsigned long key[NBITS(KEY_MAX)];
    unsigned long led[NBITS(LED_MAX)];
    unsigned long snd[NBITS(SND_MAX)];
    
    int absmax[ABS_MAX + 1];
    int absmin[ABS_MAX + 1];
    int absfuzz[ABS_MAX + 1];
    int absflat[ABS_MAX + 1];
    
    int (*open)(struct input_dev *dev);
    void (*close)(struct input_dev *dev);
    int (*accept)(struct input_dev *dev,
		  struct file *file);
    int (*flush)(struct input_dev *dev,
		 struct file *file);
    int (*event)(struct input_dev *dev,
		 unsigned int type,
		 unsigned int code,
		 int value);
    int (*upload_effect)(struct input_dev *dev,
			 struct ff_effect *effect);
    int (*erase_effect)(struct input_dev *dev,
			int effect_id);
    
    struct list_head        h_list;
    struct list_head        node;
};
Event handlers provide the interface to user space, converting the standard event format into the format required by a particular API.
Handlers usually take care of the device nodes (/dev entries) too.
The most common handler is the keyboard handler, which is the “standard input” that most programmers (especially C programmers) are familiar with.

Drivers usually interface with low-level hardware, such as USB, PCI memory or I/O regions, or serial port I/O regions.
They convert the low-level hardware version of the user input into the standard event format before sending it to the input core.

The input core uses a standard kernel plugin design, with input_register_device() used to add each device and input_unregister_device() used to remove it.

In addition to managing drivers and handlers, the input core also exports a useful /proc filesystem interface, which can be used to see what devices and handlers are currently active.

      
$ cat /proc/bus/input/devices
...     
I: Bus=0003 Vendor=046d Product=c31c Version=0110
N: Name="Logitech USB Keyboard"
P: Phys=usb-0000:00:15.0-2.2/input0
S: Sysfs=/devices/pci0000:00/0000:00:15.0/usb1/1-2/1-2.2/1-2.2:1.0/0003:046D:C31C.0002/input/input6
U: Uniq=
H: Handlers=sysrq kbd event3 leds 
B: PROP=0
B: EV=120013
B: KEY=1000000000007 ff9f207ac14057ff febeffdfffefffff fffffffffffffffe
B: MSC=10
B: LED=1f
...      
  • I:
  • The identity information - showing bus type 3 (which is USB) and the vendor, product and version information from the USB descriptors in the keyboard.
  • N:
  • The name - showing a string provided by the USB descriptors.
  • P:
  • The physical device information - a structure information comprised of the PCI address for the USB controller, the USB tree and the input interface.
    The input0 part indicates this is the first logical input device for the physical device.
    The logical input device is an abstraction of one or more physical devices that delivers logical input values to an application.
    Some devices, such as multimedia keyboards, can map part of the physical device to one logical input device and map another part to a second logical input device.
  • H:
  • The handler drivers associated with this device
  • B:
  • The various B: lines show the bitfields that identify the devices' capabilities

Resetting the USB Subsystem

There are four basic USB standards. Each standard has a specific Interface type as follows:
  • 1.x – Open Host Controller Interface (OHCI)
  • 1.x – Universal Host Controller Interface (UHCI)
  • 2.0 – Enhanced Host Controller Interface (EHCI)
  • 3.0 – eXtensible Host Controller Interface (xHCI)
To dump the physical USB device hierarchy as a tree,

$ lsusb -t
/:  Bus 04.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/2p, 10000M
/:  Bus 03.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/2p, 480M
/:  Bus 02.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/6p, 10000M
/:  Bus 01.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/12p, 480M
    |__ Port 1: Dev 2, If 0, Class=Hub, Driver=hub/4p, 480M
        |__ Port 1: Dev 4, If 0, Class=Human Interface Device, Driver=usbhid, 1.5M
        |__ Port 2: Dev 6, If 3, Class=Human Interface Device, Driver=usbhid, 12M
        |__ Port 2: Dev 6, If 1, Class=Audio, Driver=snd-usb-audio, 12M
        |__ Port 2: Dev 6, If 2, Class=Audio, Driver=snd-usb-audio, 12M
        |__ Port 2: Dev 6, If 0, Class=Audio, Driver=snd-usb-audio, 12M
    |__ Port 2: Dev 3, If 0, Class=Human Interface Device, Driver=usbhid, 1.5M
    |__ Port 2: Dev 3, If 1, Class=Human Interface Device, Driver=usbhid, 1.5M
    |__ Port 10: Dev 5, If 0, Class=Wireless, Driver=btusb, 12M
    |__ Port 10: Dev 5, If 1, Class=Wireless, Driver=btusb, 12M
  • There are four USB buses.
  • These are labeled Bus 1 to Bus 4.
  • Each bus has a root hub
  • All of these main devices are listed as “root_hubs” on Port 1.
    Root hubs are always Port 1 and devices attached are listed on the following ports, starting at 2, as they are attached.
    The driver specifies the Linux driver and designates the USB Standard (OHCI, UHCI, EHCI or xHCI), the number following the “/” designates how many ports are located on the root hub device.
    Be aware that the number of ports may not be external ports.
    An internal USB port can be used to add external ports or add an internal device.
    Some internal devices, which may be part of the ports:
    • Audio
    • Video
    • Network
    • Chipsets
    • Card readers
    • Optical devices
    • Internally mounted Mass Storage Device
    • Bluetooth
To investigate which port belongs to which bus:

$ lsusb
Bus 004 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 003 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 001 Device 003: ID 045e:0750 Microsoft Corp. Wired Keyboard 600
Bus 001 Device 005: ID 8087:0026 Intel Corp. 
Bus 001 Device 006: ID 047f:c009 Plantronics, Inc. Wired Keyboard 600
Bus 001 Device 004: ID 046d:c077 Logitech, Inc. M105 Optical Mouse
Bus 001 Device 002: ID 214b:7250  USB2.0 HUB
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
  • Bus 1 has 6 devies attached
    1. Linux Foundation 2.0 root hub
    2. USB2.0 HUB
    3. Microsoft Corp. Wired Keyboard 600
    4. Logitech, Inc. M105 Optical Mouse
    5. Intel Corp. Bluetooth
    6. Plantronics, Inc. Wired Keyboard 600
  • Each USB device has a built-in Device ID which is in two parts separated by a colon
  • The first part is the manufacturer (Vendor ID) and the second part is the device (Product ID). A list of Vendor IDs can be found on the Internet or at https://usb-ids.gowdy.us/read/UD/
To reset the USB controller, we need to know where the device resides ( [domain:]bus:device.function ) in the PCI bus:

Domain:Bus:Slot.Function
  • Domain
  • Bus
  • The "bus" refers to a PCI/PCI-X/PCI express bus from the motherboard's chipset, to which devices are attached.
  • Slot
  • The "slot" refers to a space on the PCI bus to which a single physical device is attached, which is electrically distinct from other "slots" on that bus.
    For instance, two distinct USB controllers occupying two different slots.
  • Function
  • The "function" refers to a subset of a physical device which provides distinct functionality.
lspci is a utility for displaying information about PCI buses in the system and devices connected to them.

$ lspci | grep USB
00:14.0 USB controller: Intel Corporation Comet Lake PCH-LP USB 3.1 xHCI Host Controller
39:00.0 USB controller: Intel Corporation JHL7540 Thunderbolt 3 USB Controller [Titan Ridge 2C 2018] (rev 06)
It is only missing the Domain portion which is: 0000.

We know the PCI address for the USB controller, need to find which PCI address is used for which USB bus.
To reset a USB Root Hub, you need to know its address.
To show only devices with specified device and/or bus numbers (in decimal)


lsusb  -s [[bus]:][devnum]
For example, if I wanted the address for the 1st device(root hub) on Bus 1,

$ lsusb  -s 1:1
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Both /sys/bus/pci/drivers/*/ and /sys/bus/usb/drivers/usb/ has the folllowing interfaces to enable/disable devices:
  • bind – enable the device
  • unbind – disable device
For the PCI interface:

echo -n "0000:00:13.0" | tee /sys/bus/pci/drivers/ohci_hcd/unbind
echo -n "0000:00:13.0" | tee /sys/bus/pci/drivers/ohci_hcd/bind
For the USB interface:

# echo 2-1.4 > /sys/bus/usb/drivers/usb/unbind
# echo 2-1.4 > /sys/bus/usb/drivers/usb/bind

PCIe Hot Reset on Linux

  • A 'cold reset'
  • is a fundamental reset that takes place after power is applied to a PCIe device.
    
    # tree /sys/bus/pci/slots
    /sys/bus/pci/slots
    |-- 1
    |   |-- adapter
    |   |-- address
    |   |-- cur_bus_speed
    |   |-- max_bus_speed
    |   |-- module -> ../../../../module/pciehp
    |   `-- power
    `-- 8
        |-- adapter
        |-- address
        |-- cur_bus_speed
        |-- max_bus_speed
        |-- module -> ../../../../module/pciehp
        `-- power
    
  • A 'warm reset'
  • is a fundamental reset that is triggered without disconnecting power from the device.
  • A 'hot reset'
  • is a conventional reset that is triggered across a PCI express link.
    Software can initiate a hot reset by setting and then clearing the secondary bus reset bit in the bridge control register in the PCI configuration space of the bridge port upstream of the device.
  • A 'function-level reset' (FLR)
  • is a reset that affects only a single function of a PCI express device.
    It must not reset the entire PCIe device.
    Implementing function-level resets is not required by the PCIe specification.
    A function-level reset is initiated by setting the initiate function-level reset bit in the function's device control register in the PCI express capability structure in the PCI configuration space.
Linux exposes the function-level reset functionality in the form of /sys/bus/pci/devices/$dev/reset.
Writing a 1 to this file will initiate a function-level reset on the corresponding function.

# ls /sys/bus/pci/devices/*/reset
/sys/bus/pci/devices/0000:00:02.0/reset  /sys/bus/pci/devices/0000:00:1c.5/reset  /sys/bus/pci/devices/0000:2d:00.0/reset
/sys/bus/pci/devices/0000:00:06.0/reset  /sys/bus/pci/devices/0000:00:1c.6/reset  /sys/bus/pci/devices/0000:2e:00.0/reset
/sys/bus/pci/devices/0000:00:07.0/reset  /sys/bus/pci/devices/0000:00:1c.7/reset  /sys/bus/pci/devices/0000:2f:00.0/reset
/sys/bus/pci/devices/0000:00:1c.0/reset  /sys/bus/pci/devices/0000:01:00.0/reset  /sys/bus/pci/devices/0000:30:00.0/reset

Note that this only affects that specific function of the device, not the whole device, and devices are not required to implement function-level resets as per the PCIe specification.

The following script will attempt to:

  1. remove the PCIe device
  2. command the upstream switch port to issue a hot reset
  3. rescan the PCIe bus.
Ensure that all attached drivers are unloaded before running this script:

    #!/bin/bash
     
    dev=$1
     
    if [ -z "$dev" ]; then
        echo "Error: no device specified"
        exit 1
    fi
     
    if [ ! -e "/sys/bus/pci/devices/$dev" ]; then
        dev="0000:$dev"
    fi
     
    if [ ! -e "/sys/bus/pci/devices/$dev" ]; then
        echo "Error: device $dev not found"
        exit 1
    fi
     
    port=$(basename $(dirname $(readlink "/sys/bus/pci/devices/$dev")))
     
    if [ ! -e "/sys/bus/pci/devices/$port" ]; then
        echo "Error: device $port not found"
        exit 1
    fi
     
    echo "Removing $dev..."
     
    echo 1 > "/sys/bus/pci/devices/$dev/remove"
     
    echo "Performing hot reset of port $port..."
     
    bc=$(setpci -s $port BRIDGE_CONTROL)
     
    echo "Bridge control:" $bc
     
    setpci -s $port BRIDGE_CONTROL=$(printf "%04x" $(("0x$bc" | 0x40)))
    sleep 0.01
    setpci -s $port BRIDGE_CONTROL=$bc
    sleep 0.5
     
    echo "Rescanning bus..."
     
    echo 1 > "/sys/bus/pci/devices/$port/rescan"

Linux tip: How to reset device connected to USB port

use a SystemD Timer

Create a script file in /home/myuser/bin/myscript.sh :

#!/bin/bash

x=1

while [ $x -le 5 ]

do
  logger "Welcome $x times"
  x=$(( $x + 1 ))
  sleep 60
done 
Create a Systemd service unit file /etc/systemd/system/myscript.service

[Unit]
Description=Hello: just say hi

[Service]
Type=simple
ExecStart=/home/jerry-lee-tpe/bin/myscript.sh

[Install]
WantedBy=default.target
Enable a unit to be started on bootup and Start immediately:

chmod 644 /etc/systemd/system/myscript.service
systemctl enable myscript.service

留言

熱門文章