Change-Id: I6545d8985ab683c026f28f6a7c0e23b40d0a6506
6.3 KiB
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 validdata
buffer withlength
not exceeding thewLength
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 validdata
buffer withlength
greater than or equal to thewLength
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 orUSB_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 thelength
is also zero. If a buffer is not required, the core must passdata = NULL
andlength = 0
. Otherwise, driver behavior is undefined. There are two responses which require a buffer:USB_CONTROL_ACK
to a control readUSB_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.