rockbox/docs/usb-api.md

145 lines
6.3 KiB
Markdown
Raw Normal View History

Handling USB control requests
=============================
API overview
------------
enum usb_control_response {
USB_CONTROL_ACK,
USB_CONTROL_STALL,
USB_CONTROL_RECEIVE,
};
void usb_core_control_request(struct usb_ctrlrequest* req, void* reqdata);
void usb_core_control_complete(int status);
void usb_drv_control_response(enum usb_control_response resp,
void* data, int length);
The two `usb_core` functions are common to all targets with a USB stack and
are implemented in `usb_core.c`. The USB driver calls them to inform the core
when a control request arrives or is completed.
Each USB driver implements `usb_drv_control_response()`. The core calls this
to let the driver know how to respond to each control request.
### Legacy API
void usb_core_legacy_control_request(struct usb_ctrlrequest* req);
The old control request API is available through this function. Drivers which
don't yet implement the new API can use the legacy API instead. To support
legacy drivers, the USB core implements all functions in the new API and
emulates the old control request handling behavior, bugs included.
This is intended as a stopgap measure so that old drivers keep working as-is.
The core can start using the new API right away, and drivers can be ported
one-by-one as time allows. Once all drivers are ported to the new API, all
legacy driver support can be removed.
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_control_request(req, NULL)`, passing the setup packet in `req`.
The second argument, `reqdata`, is not used at this time and is passed
as `NULL`.
The core processes the setup packet and calls `usb_drv_control_response()`
when it's done. The allowed responses depend on the type of control transfer
being processed.
### Non-data transfers
- `USB_CONTROL_ACK`, to indicate the request was processed successfully.
- `USB_CONTROL_STALL`, if the request is unsupported or cannot be processed.
### Control read transfers
- `USB_CONTROL_ACK`, to indicate the request was processed successfully.
The core must provide a valid `data` buffer with `length` not exceeding
the `wLength` field in the setup packet; otherwise, driver behavior is
undefined. The driver will transfer this data to the host during the
data phase of the control transfer, and then acknowledge the host's OUT
packet to complete the transfer successfully.
- `USB_CONTROL_STALL`, if the request is unsupported or cannot be processed.
### Control write transfers
The driver calls `usb_core_control_request()` twice to handle control writes.
The first call allows the core to handle the setup packet, and if the core
decides to accept the data phase, the second call is made when the data has
been received without error.
#### Setup phase
The first call is made at the end of the setup phase, after receiving the
setup packet. The driver passes `reqdata = NULL` to indicate this.
The core can decide whether it wants to receive the data phase:
- `USB_CONTROL_RECEIVE`, if the core wishes to continue to the data phase.
The core must provide a valid `data` buffer with `length` greater than or
equal to the `wLength` specified in the setup packet; otherwise, driver
behavior is undefined. The driver will proceed to the data phase and store
received data into the provided buffer.
- `USB_CONTROL_STALL`, if the request is unsupported or cannot be processed.
If the core accepts the data phase, the driver will re-submit the request
when the data phase is completed correctly. If any error occurs during the
data phase, the driver will not re-submit the request; instead, it will
call `usb_core_control_complete()` with a non-zero status code.
#### Status phase
The second call to `usb_core_control_request()` is made at the end of the data
phase. The `reqdata` passed by the driver is the same one that the core passed
in its `USB_CONTROL_RECEIVE` response.
The core's allowed responses are:
- `USB_CONTROL_ACK`, to indicate the request was processed successfully.
- `USB_CONTROL_STALL`, if the request is unsupported or cannot be processed.
### Request completion
The driver will notify the core when a request has completed by calling
`usb_core_control_complete()`. A status code of zero means the request was
completed successfully; a non-zero code means it failed. Note that failure
can occur even if the request was successful from the core's perspective.
If the core response is `USB_CONTROL_STALL` at any point, the request is
considered complete. In this case, the driver won't deliver a completion
notification because it would be redundant.
The driver may only complete a request after the core has provided a response
to any pending `usb_core_control_request()` call. Specifically, if the core
has not yet responded to a request, the driver needs to defer the completion
notification until it sees the core's response. If the core's response is a
stall, then the notification should be silently dropped.
### Notes
- Driver behavior is undefined if the core makes an inappropriate response
to a request, for example, responding with `USB_CONTROL_ACK` in the setup
phase of a control write or `USB_CONTROL_RECEIVE` to a non-data request.
The only permissible responses are the documented ones.
- If a response requires a buffer, then `data` must be non-NULL unless the
`length` is also zero. If a buffer is not required, the core must pass
`data = NULL` and `length = 0`. Otherwise, driver behavior is undefined.
There are two responses which require a buffer:
+ `USB_CONTROL_ACK` to a control read
+ `USB_CONTROL_RECEIVE` to the setup phase of a control write
- Drivers must be prepared to accept a setup packet at any time, including
in the middle of a control request. In such a case, devices are required
to abort the ongoing request and start handling the new request. (This is
intended as an error recovery mechanism and should not be abused by hosts
in normal operation.) The driver must take care to notify the core of the
current request's failure, and then submit the new request.