Hello everyone, today we will learn about USB device drivers.
Kernel version: 4.4.94
1. Linux USB Subsystem
Before introducing the device-side driver, let’s take a look at the Linux USB subsystem. The term “subsystem” here refers to the entire Linux kernel rather than a single device. It generally encompasses the communication framework between USB hosts and devices.
The Linux kernel has long integrated a relatively complete USB protocol stack. Due to its large scale and the inclusion of multiple categories of device drivers, the USB protocol stack in Linux is also referred to as the USB subsystem.

1.1 Host Side
The host side can be simplified into three layers:
- Various types of device drivers: mass storage, CDC, HID, etc.
- USB device driver: core processing of USB.
- Host controller driver: different USB host controllers (OHCI/EHCI/UHCI) abstracted as HDC.
1.2 Device Side
The device side can also be abstracted into three layers:
- Device function driver: mass storage, CDC, HID, etc., corresponding to the class device drivers on the host side.
- Gadget device driver: the middle layer that directly communicates with the UDC to establish a link; it provides a general interface upwards, shielding USB requests and transmission details.
- Device controller driver: UDC driver, which directly handles the USB device controller.
2. USB Device Driver
2.1 Gadget Driver Framework Breakdown 1
Let’s break down the USB device-side driver; its driver framework is as follows:

As mentioned above, the Gadget device layer plays a crucial role. It provides a general driver framework for the upper layer and establishes a connection with the lower layer UDC through the Gadget Interface.
The Composite Framework provides a general usb_gadget_driver template, including various methods for the upper layer Function driver to use. (driver/usb/gadget/composite.c)
From the above diagram, we can see that for USB device-side driver development, more attention is focused on the Function driver layer. The kernel provides an intermediate layer that shields us from the USB control-related processes.
2.2 Gadget Driver Framework Breakdown 2
Kernel version: Linux Kernel 4.4.94, we will analyze based on this version.
The 4.x kernel has a more refined breakdown of the gadget driver compared to the 3.x kernel, with a clear directory structure, distinct layers, and reasonable division of labor, making it easier to understand.
Compared to the 3.x version, the 4.4.94 kernel has split the original driver/usb/gadget directory. The general interface remains unchanged, such as composite.c and functions.c. The USB function driver has been subdivided into legacy and functions.
With this background, let’s look at the 4.4.94 kernel’s gadget driver framework, as illustrated below (the diagram is drawn based on my understanding):

- Legacy: The entry point for the entire Gadget device driver. Located in driver/usb/gadget/legacy, it provides sample drivers for commonly used USB class devices. Its role is to configure USB device descriptor information and provide a usb_composite_driver, which is then registered to the composite layer.
- Functions: Various USB subclass device function drivers. Located in driver/usb/gadget/functions, it also provides corresponding samples. Its role is to configure the interface descriptors of USB subclass protocols and other subclass protocols, such as UVC protocol, HID, etc.
- Note: For a composite device, there can be one or more functions, which correspond to multiple function drivers.
From this diagram, have you noticed that device-side driver development seems to be getting easier? Indeed, we only need to add the corresponding USB device descriptor information and a few other configurations based on the legacy source code.
In other words, we only need to focus on the legacy part, while the functions layer will be slightly adjusted according to business needs, but the overall changes are minimal.
The complexity of the USB driver framework arises not only from the need to study various complex protocols but also from the integration of various drivers, making it somewhat difficult for beginners to understand. In fact, even the legacy part contains other drivers, such as the well-known v4l2 driver framework in webcams.
Therefore, when I study the USB driver framework, I must focus on the main points and ignore the details. By breaking down a complex driver step by step, we find that it is not so daunting after all.
2.3 USB Composite Device Construction
To facilitate understanding, let’s briefly look at the construction process of a USB composite device:
Assuming we are building a USB composite device that needs to support UAC, UAC, and HID functionalities, its driver framework is as follows:

- First, we need a driver entry point (legacy) to configure device descriptor information and supported protocols.
- Then, we add a configuration that supports multiple interfaces; here we support UVC, UAC, and HID, with each interface corresponding to a function driver.
- Finally, we register it to the composite layer.
- For function drivers, there is a USB function driver list, which will automatically be added to a linked list when the kernel registers the function driver. functions.c is used to manage all function drivers.
3. USB Gadget Driver Analysis
3.1 Related Data Structures
Before sorting out the entire framework, let’s first outline a few important data structures, introduced from bottom to top:
usb_udc:
UDC usage, embedded usb_gadget_driver and usb_gadget.
struct usb_udc { struct usb_gadget_driver *driver; struct usb_gadget *gadget; struct device dev; struct list_head list; bool vbus; };
usb gadget:
USB low-level operations, including UDC, endpoint requests, etc.
struct usb_gadget { struct work_struct work; /* Work queue */ struct usb_udc *udc; /* UDC */ /* readonly to gadget driver */ const struct usb_gadget_ops *ops; /* Gadget device operation function set */ struct usb_ep *ep0; /* Control endpoint, responds only to setup packets */ struct list_head ep_list; /* Links all endpoints of the device into a list, ep0 is not included */ enum usb_device_speed speed; /* High-speed, full-speed, and low-speed */ enum usb_device_speed max_speed; /* Maximum speed */ enum usb_device_state state; const char *name; struct device dev; unsigned out_epnum; /* Out endpoint number */ unsigned in_epnum; /* In endpoint number */ struct usb_otg_caps *otg_caps; unsigned sg_supported:1; unsigned is_otg:1; unsigned is_a_peripheral:1; unsigned b_hnp_enable:1; unsigned a_hnp_support:1; unsigned a_alt_hnp_support:1; unsigned quirk_ep_out_aligned_size:1; unsigned quirk_altset_not_supp:1; unsigned quirk_stall_not_supp:1; unsigned quirk_zlp_not_supp:1; unsigned is_selfpowered:1; unsigned deactivated:1; unsigned connected:1; };
usb_gadget_driver:
usb_gadget_driver – driver for USB ‘slave’ devices. General structure for USB slave device drivers.
-
Function: Provides a general usb gadget driver template, registers down to UDC, and provides bind callbacks to function drivers upwards.
-
Focus: bind callback, function driver name, setup request handling.
struct usb_gadget_driver { char *function; /* String describing the gadget's function */ enum usb_device_speed max_speed; /* Highest speed the driver handles */ int (*bind)(struct usb_gadget *gadget, /* the driver's bind callback */ struct usb_gadget_driver *driver); void (*unbind)(struct usb_gadget *); int (*setup)(struct usb_gadget *, /* Handle ep0 request */ const struct usb_ctrlrequest *); void (*disconnect)(struct usb_gadget *); void (*suspend)(struct usb_gadget *); void (*resume)(struct usb_gadget *); void (*reset)(struct usb_gadget *); /* FIXME support safe rmmod */ struct device_driver driver; };
usb_composite_driver:
usb_composite_driver, the entry point for device drivers, used to manage device configuration information and store device descriptors.
Key point: Focus on the bind method.
struct usb_composite_driver { const char *name; /* Driver name */ const struct usb_device_descriptor *dev; /* Device descriptor */ struct usb_gadget_strings **strings; enum usb_device_speed max_speed; unsigned needs_serial:1; int (*bind)(struct usb_composite_dev *cdev); /* Bind method */ int (*unbind)(struct usb_composite_dev *); void (*disconnect)(struct usb_composite_dev *); /* Global suspend hooks */ void (*suspend)(struct usb_composite_dev *); void (*resume)(struct usb_composite_dev *); struct usb_gadget_driver gadget_driver; /* USB gadget driver */ };
usb_composite_dev:
Contains gadget objects and some configurations and requests for USB devices, mainly used for initialization.
struct usb_composite_dev { struct usb_gadget *gadget; struct usb_request *req; struct usb_request *os_desc_req; struct usb_configuration *config; /* USB configuration information */ /* OS String is a custom (yet popular) extension to the USB standard. */ u8 qw_sign[OS_STRING_QW_SIGN_LEN]; u8 b_vendor_code; struct usb_configuration *os_desc_config; unsigned int use_os_string:1; /* Private: */ /* Internals */ unsigned int suspended:1; struct usb_device_descriptor desc; /* Device descriptor */ struct list_head configs; struct list_head gstrings; struct usb_composite_driver *driver; /* Composite driver */ u8 next_string_id; char *def_manufacturer; /* The gadget driver won't enable the data pullup while the deactivation count is nonzero. */ unsigned deactivations; /* The composite driver won't complete the control transfer's data/status stages till delayed_status is zero. */ int delayed_status; /* Protects deactivations and delayed_status counts */ spinlock_t lock; unsigned setup_pending:1; unsigned os_desc_pending:1; };
3.2 Driver Analysis
The diagram shows a general USB gadget driver analysis, where only two functions are listed; if there are multiple functions, they can be added. Regarding the UDC controller part, it is not further illustrated; note that we always maintain a principle: focus on the main points and ignore the details, grasping the important framework is sufficient.
Regarding USB device-side drivers, the content in the complex image is quite extensive; if it is unclear, reply in the background with [gadget driver] to obtain a high-definition image.

Layering and Partitioning
The idea of separating layers vertically and functions horizontally.
- Device function driver
- Legacy driver entry
- Functions driver implementation
- Gadget device layer: the most important is the composite_bind method, which serves as a bridge.
- UDC device controller layer. The actual processing of the USB protocol.
Driver Flow
- Downward: usb_composite_driver -> usb_gadget_driver -> usb_udc
- Upward callback: udc_bind_to_driver -> composite_bind -> webcam_bind. The two main structures are usb_gadget_driver and usb_composite_dev. The former registers down to the UDC list, establishing a binding relationship with the UDC controller; the latter provides an interface upwards for configuring various functions and other configuration information of the USB device.
Code Analysis
- Register usb_composite_driver
module_usb_composite_driver(webcam_driver) module_driver(webcam_driver, usb_composite_probe, usb_composite_unregister)
- usb_composite_probe
usb_composite_probe(webcam_driver); driver->gadget_driver = composite_driver_template; gadget_driver = &driver->gadget_driver; ... usb_gadget_probe_driver(composite_driver_template); udc_bind_to_driver(udc, driver); composite_driver_template->bind(udc->gadget, composite_driver_template); usb_gadget_udc_start(udc);
- composite_bind
composite_bind(udc->gadget,composite_driver_template); cdev->gadget = gadget; composite_dev_prepare(webcam_driver,cdev); cdev->req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL); /* Request endpoint 0 */ cdev->req->complete = composite_setup_complete; cdev->driver = webcam_driver; usb_ep_autoconfig_reset(gadget); webcam_driver->bind(cdev);
- webcam_bind
webcam_bind(cdev); usb_get_function_instance("uvc"); try_get_usb_function_instance("uvc"); uvc_alloc_inst(); usb_add_config(); webcam_config_bind(); usb_get_function(); usb_add_function(); others_config_bind();
Others
We have not detailed the function driver here; this diagram is just a general USB device driver framework diagram, and we have not conducted a specific analysis of the actual USB function driver.
For example, with f_uvc, the detailed process can be found in the kernel source code.
DECLARE_USB_FUNCTION_INIT(uvc, uvc_alloc_inst, uvc_alloc);
DECLARE_USB_FUNCTION_INIT(uvc, uvc_alloc_inst, uvc_alloc); usb_function_register(&uvcusb_func); list_for_each_entry(fd, &func_list, list) list_add_tail();
DECLARE_USB_FUNCTION_INIT
A general driver template used to register usb_function_driver and add it to the func_list.
#define DECLARE_USB_FUNCTION(_name, _inst_alloc, _func_alloc)
static struct usb_function_driver _name ## usb_func = {
.name = __stringify(_name),
.mod = THIS_MODULE,
.alloc_inst = _inst_alloc,
.alloc_func = _func_alloc,
};
MODULE_ALIAS("usbfunc:"__stringify(_name));
#define DECLARE_USB_FUNCTION_INIT(_name, _inst_alloc, _func_alloc)
DECLARE_USB_FUNCTION(_name, _inst_alloc, _func_alloc)
static int __init _name ## mod_init(void)
{
return usb_function_register(&_name ## usb_func);
}
static void __exit _name ## mod_exit(void)
{
usb_function_unregister(&_name ## usb_func);
}
module_init(_name ## mod_init);
module_exit(_name ## mod_exit);
4. Conclusion
This article gradually dissects the USB device-side driver framework, leading everyone to re-understand USB device-side drivers, while providing a general driver framework model for composite devices and analyzing the entire driver flow from the source code level.
Regarding USB or other similar advanced drivers, I have a suggestion: when starting out, it is crucial to grasp the main points and ignore the details.
For example, a composite USB device may include UVC, UAC, HID, etc. Videos have UVC function drivers and V4L2 drivers, and UAC also has corresponding drivers, which can become very complex.
Therefore, once we master the device-side driver framework and flow, we can later study the protocols or drivers of other USB function drivers as needed.
Reply in the background with [gadget driver] to obtain a high-definition image.
If you find this article helpful, feel free to like, share, and bookmark it. Thank you!