Get a brief summary about the Linux USB gadget subsystem, and how interactions are made with vendor USB device controller drivers.
Table of Contents
Overview

The Linux USB gadget subsystem allows for a device to operate as a USB peripheral. Within the USB concepts, a device can act as a host, peripheral, or dual role (DRD/OTG). A DRD based device will utilize the same USB gadget software stack as a peripheral only device.
This post focuses mainly on the components that the USB gadget stack is comprised of, and how they interact with each layer.
- How are USB device controllers (UDC) defined and registered?
- What does the USB composite and USB configFS do?
- How do functions get initialized and added to a composite device?
Although most users won’t need to know about the in-depth designs of the USB UDC, having an understanding of the general design can help with debugging issues where the USB device is not enumerating with the host.
USB Device Controllers
The Linux USB gadget stack is built in a way where layers can be easily swapped out depending on the platform, and USB IP used within the SoC. The UDC drivers are the lowest level of the gadget stack. It’s responsibility is to expose a set of APIs that allow for the upper software layers to queue transfers to the USB hardware/controller.
The mechanisms to submit data transfers to the hardware differs from vendor to vendor, so there has to be a generic abstraction software driver, so that vendor specific implementations can be easily interchangeable. This abstraction layer is the udc-core driver.
- udc-core
- File Location: <kernel root>/drivers/usb/gadget/udc/core.c
- Differs Between Vendors? : No
Key Structures:
struct usb_gadget {
...
/* readonly to gadget driver */
const struct usb_gadget_ops *ops;
usb_gadget_ops Callbacks:
Callback | Required | Comments |
---|---|---|
get_frame | Y | Returns the current frame number, ie SOF packet count. |
wakeup | N | USB remote wakeup – device to host bus resume. |
func_wakeup | N | USB3.0 function wake feature. |
set_remote_wakeup | N | Tells UDC if remote wakeup is configured from within the USB configuration descriptor. |
set_selfpowered | N | Tells UDC if the device is configured to be self powered from within the USB configuration descriptor. |
vbus_session | N | Signal when a VBUS change has occurred if there is a mechanism to detect transitions. |
vbus_draw | N | Communicates to the UDC how much current can be drawn from the USB bus. Depending on the USB state, the amount of current allowed is different. |
pullup | N | Triggers the vendor UDC to enable the hardware and attempt enumeration. |
ioctl | N | Used for legacy gadgetfs USB gadgets. |
get_config_params | N | Retrieve USB L1 latency parameters. |
udc_start | Y | Trigger the UDC to initialize resources for operation. |
udc_stop | Y | Release UDC resources readied from UDC start. |
udc_set_speed | N | Notifies the UDC about the maximum speed supported by the USB gadget layer. This is done before UDC start is executed. |
udc_set_ssp_rate | N | When USB gadget supports USB super speed plus, notifies UDC of the max USB SSP rate, ie for x2 configurations. |
udc_async_callbacks | N | Used by USB gadget to block certain notifications from the UDC, such as suspend, resume, etc… which are asynchronous events. |
match_ep | N | Called when function drivers request for an available endpoint. Used for UDC to determine if a specific endpoint needs to be assigned. |
check_config | N | Issued when the USB configuration is created. Allows the UDC to check if there are sufficient resources within the controller to accommodate the composition. |
Adding and Removing USB UDCs
Functions:
- int usb_add_gadget(struct usb_gadget *gadget)
- int usb_add_gadget_udc(struct device parent, struct usb_gadget gadget)
- int usb_add_gadget_udc_release(struct device parent, struct usb_gadget gadget, void (*release)(struct device *dev))
int usb_add_gadget(struct usb_gadget *gadget)
Vendor UDCs call this to register itself to the UDC core. Part of the arguments will have a structure to the gadget operations exposed by the driver. These comprise of a set of function pointers, which can be defined similar to the below example:
static const struct usb_gadget_ops dwc2_hsotg_gadget_ops = {
.get_frame = dwc2_hsotg_gadget_getframe,
.set_selfpowered = dwc2_hsotg_set_selfpowered,
.udc_start = dwc2_hsotg_udc_start,
.udc_stop = dwc2_hsotg_udc_stop,
.pullup = dwc2_hsotg_pullup,
.udc_set_speed = dwc2_gadget_set_speed,
.vbus_session = dwc2_hsotg_vbus_session,
.vbus_draw = dwc2_hsotg_vbus_draw,
};
Similarly, usb_add_gadget_udc() and usb_add_gadget_udc_release() is a variant that will add a parent device relation to the allocated UDC device and is kref reference counted. The latter will allow drivers to specify a release() callback when the kref counter reaches zero.
Functions:
- void usb_del_gadget(struct usb_gadget *gadget)
- void usb_del_gadget_udc(struct usb_gadget *gadget)
void usb_del_gadget(struct usb_gadget *gadget)
This is the opposite of what the usb_add_gadget() API does. It will remove the allocated UDC device from the system. usb_del_gadget_udc() is similar, but also will do the kref count decrementing.
Verifying USB UDCs
Users can check for USB UDCs that have been successfully added by checking the following directory:
# UDC class directory
ls /sys/class/udc
When a UDC is added, it is allocated as part of the UDC class. If the directory is empty, users can start to debug the APIs which execute any of the ADD paths. This will allow them to find out if there are any resource dependencies that haven’t been met (devlinks), kernel configuration issues, etc…
USB Gadget
The USB gadget comprises of two main parts:
- USB function drivers
- USB composite driver
To differentiate the two, functions drivers handle operations that are explicit to a particular USB interface. A USB device can expose itself as a composite device, which would include more than one function. Each of these functions are defined by the USB interface descriptors which are individually defined by the USB function drivers.
The USB composite driver, on the other hand, will be the entity that maintains/manages the USB configuration. This boils down to the list of USB functions that are exposed by the device. In addition, it defines callbacks that vendor UDCs can utilize to handle standard USB chapter 9 control requests.
USB Composite
Some of the major responsibilities of USB composite include:
- Managing USB configuration
- Handling USB control (EP0) chapter 9 requests
As for drivers, there are many different options that are available, such as:
- USB configFS
- Gadget FFS
- Mass Storage Gadget
- etc…
However, all these USB composite drivers will utilize the composite.c, which serves almost as an abstraction layer between the above drivers to the UDC.

Taking USB configFS as an example, it interacts with the main configFS filesystem driver to register callbacks for file and directory changes. This is how it maintains the list of functions available within a USB configuration. This information is passed along to composite.c using:
- int usb_add_function(struct usb_configuration *config, struct usb_function *function)
- void usb_remove_function(struct usb_configuration *c, struct usb_function *f)
By calling the above add and remove function APIs, composite.c is able to respond directly to any USB chapter 9 requests, as it maintains its own references to what is kept in the USB configuration. Creating a USB composition is part of the required sequences to initiate a session with a USB host.

Once the USB composite binds the gadget driver, the UDC driver will attempt to start the USB enumeration as highlighted above.
USB Function Drivers
USB function drivers represents the different interfaces that the device exposes. Some examples of USB function drivers include:
- Mass Storage
- USB ACM
- USB Video Class (UVC)
- USB Audio Class (UAC)
- NCM
- etc…
A device can contain one or more functions within the USB configuration. However, the number of functions should not exceed the maximum number of available endpoints supported by the UDC. Normally, the number of endpoints is 16 IN and 16 OUT (including EP0), which is defined by the USB specification. Some vendor UDCs may support less than this.
Function Driver Registration:
- #define DECLARE_USB_FUNCTION(_name, _inst_alloc, _func_alloc)
- int usb_function_register(struct usb_function_driver *newf)
USB function drivers are made available using either of the APIs above. The USB gadget (ie USB configFS) can then reference the allocated function instance by using:
struct usb_function_instance *usb_get_function_instance(const char *name)
The "name" argument is the string assigned to the function driver during DECLARE_USB_FUNCTION(). For example, in the f_hid function driver the associated name is declared as "hid".
DECLARE_USB_FUNCTION_INIT(hid, hidg_alloc_inst, hidg_alloc);
The USB gadget driver in use, will be able to reference function names based on their design. In case of USB configFS, whenever a user makes the function directory, the name of the mkdir call would attempt to match to the corresponding function:
mkdir /sys/kernel/config/usb_gadget/g1/functions/hid.0
Once the function driver makes a connection to the USB gadget, the USB gadget can then reference the function once the USB configuration is decided. Callbacks for configuration management are registered by the function drivers.
struct usb_function {
...
/* configuration management: bind/unbind */
int (*bind)(struct usb_configuration *,
struct usb_function *);
void (*unbind)(struct usb_configuration *, struct
usb_function *);
void (*free_func)(struct usb_function *f);
bind() – When a function is added to the USB configuration, the function driver needs to reserve certain indexes/references from the USB gadget and UDC. These include, USB endpoint numbers assigned, SS/HS/FS USB descriptors associated to the function, and the USB interface ID.
- Related APIs
- usb_interface_id()
- usb_ep_autoconfig()
- usb_assign_descriptors()
unbind() – Undo what was done when the function was binded. Free the USB descriptors and allocated resources.
- Related APIs
- usb_free_all_descriptors()
USB Gadget and USB UDC Interactions
Once added to the USB configuration, the USB function driver will have a few callbacks registered to the USB UDC and USB gadget.
struct usb_function {
...
/* runtime state management */
int (*set_alt)(struct usb_function *,
unsigned interface, unsigned alt);
int (*get_alt)(struct usb_function *,
unsigned interface);
void (*disable)(struct usb_function *);
int (*setup)(struct usb_function *,
const struct usb_ctrlrequest *);
bool (*req_match)(struct usb_function *,
const struct usb_ctrlrequest *,
bool config0);
void (*suspend)(struct usb_function *);
void (*resume)(struct usb_function *);
set_alt() – Callback registered from when the USB gadget receives a SET_INTERFACE or SET_CONFIGURATION control packet. This determines which data endpoints to enable.
- Related APIs
- config_ep_by_speed()
- usb_ep_enable()
get_alt() – Callback registered from when the USB gadget receives a GET_INTERFACE control packet. The purpose is to fetch information about the currently enabled alternate interface. Not commonly registered if your function does not have alternate interfaces.
disable() – Notification when there has been a USB disconnect or reset event. This is meant to ensure that any pending IO/transfers are properly halted.
setup() – Interface specific USB control packets. If function/class drivers have interactions through the EP0 endpoints.
req_match() – Determines if a SETUP packet should be handled by the function driver.
suspend() – Called when USB gadget suspend() is executed. Normally, this occurs when there is an USB bus suspend event. This allows for function drivers to place the interface into idle.
resume() – Called when USB gadget resume() is executed. This occurs when an USB bus resume or remote wakeup is seen. This allows for the function drivers to start operation again.
Leave a Reply