The Linux USB driver is the core subsystem for all USB devices on Linux. It detects, communicates with, and manages USB devices on the system's hardware and kernel spaces.
The USB protocol allows for a variety of device types and bandwidth usage, so drivers are written to support a wide range of devices. This article focuses on devices that are common to most users, such as mice and keyboards.
In order to support USB devices, the Linux USB driver must be able to detect them. This can be done either with a simple device-specific detection mechanism or by combining information from the protocol and the udev database.
Device detection works by examining a USB interface’s configurations and interfaces. Each valid device contains one or more configurations, and these can be enabled or disabled at different points in time.
These configurations are bundled up into interfaces, which are associated with each functionality that the device provides. For example, a multi-function printer would likely have multiple interfaces for each independent functionality.
Each interface has a udev_probe callback function that is called when a probe is received for it. The udev_probe function then sets up the struct usb_probe_device structure and fills it in with information that the USB core can use to recognize the device.
The USB core then uses this information to identify the device and communicate with the driver through a minor number and a set of file_operations functions.
Buffers are used to allow drivers to execute asynchronous operations. They also help to limit the amount of time that a driver needs to spend waiting for another function to complete before allowing it to unload from memory and return to the caller.
A buffer is allocated by the driver when it receives a request from the device. For example, if a USB device is connected to the computer and the driver detects that it has captured a picture, the driver must allocate a buffer for the capture stream data.
The driver can also use transfer buffers to ensure that data is transferred properly when it transfers large amounts of data in a single urb. In this case, the driver must set a value to transfer_buffers that is greater than the maximum size of the endpoint’s packets.
If the total size of the endpoint’s packets is larger than transfer_buffers, the USB core breaks the data into smaller chunks so that it can be transferred faster. The core will then send these buffers back to the driver as a separate urb, rather than in consecutive frames.
A Linux USB driver communicates with all the USB devices on a system using a structure called an urb. This urb is submitted by the driver to the USB core, and then processed by the specific USB host controller driver for the specified device.
The urb contains a transfer buffer that the USB core uses when sending data to the endpoint, or receiving data from the device. In most cases, this is done through a DMA transfer.
However, some devices do not support DMA transfers and the USB core can only do these using a special function. This is the case with bulk endpoints, which are used to send a lot of data in one go.
A bulk endpoint is usually much larger than an interrupt endpoint and can hold a large amount of data at once. If there is not enough space on the bus for a bulk packet, it is split up into multiple transfer requests.
The Linux USB driver interacts with the different kernel subsystems and the USB hardware controllers on your system. It can also provide user-space interactions for drivers that do not have a matching subsystem, such as MP3 players or scanners.
Control endpoints are used for configuring the device, retrieving information about it, sending commands to it, or requesting status reports about it. They are often small in size and have higher priority than bulk endpoints.
There are three types of control endpoints: isochronous, bulk, and interrupt. The isochronous endpoints are reserved for transferring data that require guaranteed bandwidth and can only be processed as soon as the available bandwidth permits.
Bulk endpoints can transfer more data but are not subject to guaranteed time constraints. The interrupt endpoints are used for transferring a small amount of data at a time.
Each endpoint has a unique USB address that the USB core uses to identify it. It can have a bitmask to specify the direction of the data and an 8-bit value that indicates whether it is an isochronous or bulk endpoint.