rockbox/docs/usb-api.md
mojyack 2291aa24f2 usb: refactor control request handling
this commit is a combination of the following changes, which
significantly refactors usb core and class drivers.

1. unify usb buffers of each class driver to reduce iram usage
currently, many class drivers allocate their own buffer to receive
control out data, which is a waste of iram.
share one common buffer for that usage to address the issue.

2. simplify control request handling by implicitly receiving write
   request data packets
change 1 above fixed the data destination. therefore, having the core
receive the data allows us to reduce the class driver's work and
simplifies the api.

3. enhance usb core's control request handling and unify the legacy
   driver api
in order to implement change 2, both the legacy and new driver apis
should be supported. so that, using the designware driver as a
reference, the new driver api functionality is move into usb core.
this simplifies the usb device drivers by requiring them to implement
only the functionalities equivalent to the legacy api.

tested with ipodvideo(arc) and erosqnative(designware)

Change-Id: I3627daa90278751f599e2108ec150ec3f8f6c524
2026-05-25 12:42:36 +01:00

2 KiB

Handling USB control requests

API overview

A control request goes through from device driver to class drivers, via usb core. Each step is done by those functions:

# from device driver to usb core
void usb_core_setup_received(struct usb_ctrlrequest* req);
# from usb core to class drivers
bool usb_class_driver::control_request(struct usb_ctrlrequest* req);

Also control response goes through the inverse steps, and the following functions are used:

# from class drivers to usb core
void usb_core_control_response(enum usb_control_response response, const void* data, size_t size);
# from usb core to device drivers
int usb_drv_{send,recv}_nonblocking(int endpoint, void *ptr, int length);

Request handling process

The driver submits control requests to the USB core one at a time. Once a request is submitted, it must be completed before the next request can be submitted. This mirrors normal USB operation.

When the USB driver receives a setup packet from the host, it submits it to the core to begin handling the control transfer. The driver calls usb_core_setup_received(req), passing the setup packet in req.

If the request was a Non-data transfer, the core or recipient class driver will call usb_core_control_response(USB_CONTROL_ACK, NULL, 0) for ACK or usb_core_control_response(USB_CONTROL_STALL, NULL, 0) for NAK.

If the request was a Control read transfer, the core or recipient class driver will call usb_core_control_response(USB_CONTROL_ACK, data, size) to send data packets, or usb_core_control_response(false, USB_CONTROL_STALL, 0) for NAK.

If the request was a Control write transfer, the core firstly receive data packets using usb_drv_recv_nonblocking. Once it's done, the core or recipient class driver will process it, then usb_core_control_response(USB_CONTROL_ACK, NULL, 0) for ACK or usb_core_control_response(USB_CONTROL_STALL, NULL, 0) for NAK.