Why USB Devices Must Enumerate First
USB is often treated as “plug in and communicate”. Keyboards, flash drives, USB serial adapters, cameras, and debuggers all use USB and appear to share one connector.
To understand USB, first notice how different it is from UART, I2C, and SPI: USB is usually not peer-to-peer. A Host manages Devices. After a device is plugged in, it cannot just start sending arbitrary data. It must first be discovered, identified, and configured by the Host.
That process is enumeration.
Device attached
-> Host reset
-> read descriptors
-> assign address
-> choose configuration
-> load class/vendor driver
-> use endpoints for data transfer
The first USB model is not “how the wires connect”, but “how the Host knows what this device is, what interfaces it has, and which endpoints can be used”.
Host and Device Are Not Equal Roles
USB usually has clear roles:
- Host: manages the bus, initiates transfers, supplies or negotiates power, loads drivers
- Device: responds to Host requests and provides descriptors, interfaces, endpoints
Keyboards, mice, flash drives, USB serial adapters are Devices. PCs, embedded Linux hosts, some gateways, and phones in certain modes are Hosts.
This boundary matters: a USB Device cannot behave like UART where both sides freely send bytes. Most transfers are scheduled by the Host. The Device responds or provides data when the Host gives it a chance.
If two USB Devices are directly connected, they usually cannot communicate because no Host manages the bus.
Descriptors Tell the Host What the Device Is
After attachment, the Host reads descriptors. Descriptors are the device’s self-description.
Common hierarchy:
Device Descriptor
-> Configuration Descriptor
-> Interface Descriptor
-> Endpoint Descriptor
Device Descriptor gives vendor ID, product ID, USB version, device class, and basic information.
Configuration Descriptor describes configurations, power, and attributes.
Interface Descriptor describes a function such as HID, CDC, or MSC.
Endpoint Descriptor describes endpoint direction, transfer type, and maximum packet size.
The Host uses these descriptors to decide which driver to load, what device nodes to expose, and how to exchange data.
Configuration, Interface, and Endpoint Are Different
These concepts are often mixed up.
Configuration is one overall working combination. Many simple devices have one configuration.
Interface is one function inside a configuration. A composite device can expose multiple interfaces, for example:
- CDC serial
- HID keyboard
- vendor-specific interface
Endpoint is a concrete data channel. Besides default control endpoint 0, endpoints carry bulk, interrupt, or isochronous data.
Roughly:
Configuration: which overall mode the device uses
Interface: which functions exist in that mode
Endpoint: which data channels each function uses
If you only check whether a device is plugged in, you may miss interface and endpoint errors.
Control Endpoint 0 Is the Enumeration Entry
Every USB device has endpoint 0. It uses control transfers and is the entry point for enumeration and standard requests.
The Host uses endpoint 0 to:
- get descriptors
- set address
- set configuration
- read status
- send standard, class, or vendor requests
Even before full configuration, the device must answer required requests on endpoint 0. Otherwise the Host cannot identify it.
Many “USB device does nothing” failures are not application-data failures. They fail earlier at endpoint 0, descriptor response, address assignment, or configuration selection.
Endpoint Transfer Type Defines Communication Shape
Common USB transfer types:
- Control: enumeration, configuration, control requests
- Bulk: large reliable data, such as storage, USB network, serial data
- Interrupt: low-latency small data, such as keyboard, mouse, HID
- Isochronous: time-sensitive audio/video where some loss may be acceptable
USB interrupt transfer is not an MCU interrupt. It means the Host polls the endpoint at a defined interval.
Choosing endpoint type means choosing latency, bandwidth, reliability, and scheduling behavior.
Device Classes Enable Generic Drivers
Much of USB’s usefulness comes from device classes.
Common classes:
- HID: keyboard, mouse, simple control devices
- CDC ACM: virtual serial port
- MSC: mass storage
- UVC: camera
- Audio: audio device
- Vendor Specific: custom protocol
If a device declares a standard class, the Host may use a generic driver. CDC may appear as /dev/ttyACM* on Linux, HID may appear through hidraw or input subsystem, and MSC may become a block device.
If a device uses Vendor Specific, it needs a vendor driver, libusb application, or custom kernel driver.
So a key design choice is whether a standard class fits, or whether a private protocol is necessary.
Power and OTG Are Separate Layers
USB also involves power, Type-C, PD, OTG, and role switching. These are important, but not the same layer as enumeration and data interfaces.
Minimal separation:
USB enumeration: Host identifies descriptors, configurations, interfaces, endpoints
USB power/Type-C/PD: power direction, current, voltage negotiation, role detection
USB OTG: some devices can switch between Host and Device roles
Power being present does not mean enumeration succeeds. Enumeration succeeding does not mean power margin, cable quality, or high-speed signal integrity is perfect.
Common Embedded Linux Symptoms
On embedded Linux, common entry points are:
dmesg
lsusb
lsusb -v
/sys/bus/usb/devices
/dev/ttyACM*
/dev/ttyUSB*
/dev/hidraw*
Separate symptoms:
- repeated reset in
dmesg: power, cable, signal quality, or unstable descriptor response - visible in
lsusbbut no device node: class driver not bound or interface mismatch /dev/ttyUSB*: often USB serial chip driver/dev/ttyACM*: usually CDC ACMhidraw: HID device or custom HID protocol
Do not reduce every USB issue to “driver not installed”. First check whether enumeration completed, what class the interface declares, whether a driver bound, and whether endpoints match expectations.
Debugging Order
For USB device connection failures:
- Check physical connection and power: cable, port, current, repeated reset.
- Check whether Host detects attach and reset.
- Check whether Device Descriptor is read successfully.
- Check whether address and configuration are set.
- Check Interface class/subclass/protocol.
- Check endpoint direction, type, and max packet size.
- Check whether the right class or vendor driver binds.
- Check whether the application opens the correct device node or libusb interface.
- Check transfer type: control, bulk, interrupt, or isochronous.
Three Final Judgments
First, USB is a Host-managed bus. Devices must enumerate before normal communication. Do not treat it like peer-to-peer UART.
Second, descriptors, configurations, interfaces, and endpoints are the core structures by which the Host recognizes and drives a USB device.
Third, USB is large, but entry-level debugging should start with the enumeration chain: identified or not, which class, which endpoints, driver binding, and application interface.
Further Reading
- UART: useful when comparing USB CDC or USB serial adapters with real UART
- Embedded Linux Boot Process: USB driver binding and device nodes appear along the broader Linux system startup path