Marcel spearheaded the upstream first policy at Toradex. He introduces us to a lot of concepts of USB and how they are used on embedded boards. This is a good overview talk with many references to other resources at the end of the slides.

sched slides

USB has devices connected to the bus. The configuration is the state of the device, including initialisation, standby, active. It consists of interfaces, which are “logical devices”, i.e. they encapsulate a function like webcam. A driver is needed for each interface. Alternative configurations are possible, it starts in configuration 0 and the host can switch to a different one. Communication is done with endpoints, each interface defines how many endpoints and of which type its need. 4 different endpoint types exist with different properties. The scheduling of the endpoints within and between devices is done by the host. The transfers on the endpoints are organised in URBs (USB Request Blocks), basically packets. Each (hardware) endpoint queues URBs and they have a completion handler.

Most SoCs have at least one USB port, often including a PHY. For USB 2.0 there are just 2 lines, D+ and D-. For SuperSpeed (USB 3.0+) there are 4 lines: SSRX+, SSRX-, SSTX+, SSTX-. There are also supporting signals, usually mapped to GPIOs: ID (for USB-OTG), OverCurrent, VBUS (often not in the SoC even if the PHY is integrated), VBUS enable.

On the board you can have external hub and PHY chips, and built-in USB peripherals e.g. wifi, ethernet, serial. For USB-C there are almost always specialist companion chips that take care of the signalling. They use either USB 2.0 signalling (i.e. ID pin) or a specific I2C interface for which a driver is needed.

i.MX8M supports USB 3.0 on the SoC. It also has a legacy ID pin but that actually doesn’t work, don’t use it.

Most SoCs have a USB boot mode, where the boot ROM communicates with a USB host to load the software. There’s a more or less standard protocol, Serial Download Protocol (SDP) used on NXP devices.

DFU is an official USB device class so really standard. It’s used on TI AM62x. It’s pretty slow because it uses only control EP0. For the host side there is dfu-util.

Once U-Boot is loaded with one of the above, it needs to load a gadget driver to load the rest. It has DFU, Fastboot (cfr. Android, much faster than DFU) and UMS (USB Mass Storage). Support has to be configured in, and then a U-Boot command must be given to enable it (the boot script doesn’t usually have this command). DFU is mostly statically configured (i.e. what you can upload over it), fastboot is much more flexible. UMS just exposes a block device on the board to the host. But this doesn’t allow to expose e.g. the eMMC boot partitions (not said in the talk, but probably also not MTD devices).

U-Boot also has host-side drivers. Enumeration is pretty slow because U-Boot isn’t multi-threaded.

In Linux USB support consists of the core, a host controller driver (standard ones e.g. xHCI, or SoC-specific drivers), (peripheral) device drivers that are loaded when the device is enumerated.

In userspace, USB devices are visible in /proc/bus/usb/devices. usbutils has utilities to access this information more human readable: lsusb, usb-devices, usbhid-dump, usbview (GTK based). libusb is used to create userspace device drivers for USB devices. It works even on macOS and Windows. uhubctl controls the power of ports on a hub.

In embedded devices, there are often ports that can be configured as either device or host. This can be configured statically in device tree with the dr_mode properly. For dynamic configuration, you need the ID and VBUS which are mapped to GPIOs. These are handled by the extcon-usb-gpio driver, with id-gpio and/or vbus-gpio properties to specify which pins to use. This is however obsolete, modern is usb-conn-gpio driver which also supports type-C. It has a lot more properties e.g. vbus-supply.

For gadget functionality, the functions to use must be enabled in the kernel config, and the configuration to actually use must be instantiated in configfs. from userspace. the usb_gadget directory is used. Create a directory in it to define an instance. Set global properties like vendor ID in this directory. Create subdirecties for configurations, and then deeper subdirectories for functions. libusbgx is a C interface to all this.

For debugging, the usbmon interface captures packets, and wireshark can visualize them. You can do that on the embedded device with the following command:

tcpcump -i usbmon2 -U -w -” | flatpak run --filesystem=host --file-forwarding=host --share=network
org.wireshark.Wireshark -k -i -

References (copied from slides)

USB
Specification https://www.usb.org/documents

USB-C
https://www.toradex.com/blog/add-usb-c-to-your-next-carrier-board-design-1
https://www.toradex.com/blog/add-usb-c-to-your-next-carrier-board-design-2
https://www.toradex.com/webinars/add-usb-c-to-your-next-carrier-board-design

imx_loader
https://github.com/boundarydevices/imx_usb_loader

uuu
https://github.com/nxp-imx/mfgtools

dfu-util
https://dfu-util.sourceforge.net

Toradex Easy Installer
https://www.toradex.com/tools-libraries/toradex-easy-installer

usbview
http://www.kroah.com/linux-usb

libusb
https://libusb.info

uhubctl
https://github.com/mvp/uhubctl

Virtual USB Analyzer
https://vusb-analyzer.sourceforge.net

Wireshark
https://wiki.wireshark.org/CaptureSetup/USB

usbmon
https://docs.kernel.org/usb/usbmon.html