usb: introduce new control request API

Change-Id: I6545d8985ab683c026f28f6a7c0e23b40d0a6506
This commit is contained in:
Aidan MacDonald 2021-09-19 11:44:38 +01:00
parent 71cc1e78fd
commit ec164c389c
15 changed files with 168 additions and 14 deletions

144
docs/usb-api.md Normal file
View File

@ -0,0 +1,144 @@
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.

View File

@ -341,7 +341,7 @@ static void usb_handle_setup_rx(void)
if (len == 8)
{
ISP1583_DFLOW_CTRLFUN |= DFLOW_CTRLFUN_STATUS; /* Acknowledge packet */
usb_core_control_request((struct usb_ctrlrequest*)setup_pkt_buf);
usb_core_legacy_control_request((struct usb_ctrlrequest*)setup_pkt_buf);
}
else
{

View File

@ -208,7 +208,7 @@ static void control_received(void) {
/* acknowledge packet recieved (clear valid) */
M66591_INTSTAT_MAIN &= ~(1<<3);
usb_core_control_request(&temp);
usb_core_legacy_control_request(&temp);
}
/* This is a helper function, it is used to notife the stack that a transfer is

View File

@ -741,7 +741,7 @@ static void usb_dw_handle_setup_received(void)
&& (usb_ctrlsetup.bRequest == USB_REQ_SET_ADDRESS))
usb_dw_set_address(usb_ctrlsetup.wValue);
usb_core_control_request(&usb_ctrlsetup);
usb_core_legacy_control_request(&usb_ctrlsetup);
}
static void usb_dw_abort_endpoint(int epnum, enum usb_dw_epdir epdir)

View File

@ -51,7 +51,9 @@ struct usb_class_driver;
void usb_core_init(void);
void usb_core_exit(void);
void usb_core_control_request(struct usb_ctrlrequest* req);
void usb_core_control_request(struct usb_ctrlrequest* req, void* data);
void usb_core_control_complete(int status);
void usb_core_legacy_control_request(struct usb_ctrlrequest* req);
void usb_core_transfer_complete(int endpoint,int dir,int status,int length);
void usb_core_bus_reset(void);
bool usb_core_any_exclusive_storage(void);

View File

@ -56,6 +56,12 @@
* -> usb_drv_int_enable(false) [ditto]
* -> soc specific controller/clock deinit */
enum usb_control_response {
USB_CONTROL_ACK,
USB_CONTROL_STALL,
USB_CONTROL_RECEIVE,
};
/* one-time initialisation of the USB driver */
void usb_drv_startup(void);
void usb_drv_int_enable(bool enable); /* Target implemented */
@ -69,6 +75,8 @@ bool usb_drv_stalled(int endpoint,bool in);
int usb_drv_send(int endpoint, void* ptr, int length);
int usb_drv_send_nonblocking(int endpoint, void* ptr, int length);
int usb_drv_recv_nonblocking(int endpoint, void* ptr, int length);
void usb_drv_control_response(enum usb_control_response resp,
void* data, int length);
void usb_drv_set_address(int address);
void usb_drv_reset_endpoint(int endpoint, bool send);
bool usb_drv_powered(void);

View File

@ -655,7 +655,7 @@ static void handle_out_ep(int ep)
req->wIndex,
req->wLength);
usb_core_control_request(&req_copy);
usb_core_legacy_control_request(&req_copy);
setup_desc_init(setup_desc);
ep_sts &= ~USB_EP_STAT_SETUP_RCVD;
@ -760,7 +760,7 @@ void INT_USB_FUNC(void)
got_set_configuration = 1;
set_config.wValue = USB_DEV_STS & USB_DEV_STS_MASK_CFG;
usb_core_control_request(&set_config);
usb_core_legacy_control_request(&set_config);
intr &= ~USB_DEV_INTR_SET_CONFIG;
}
if (intr & USB_DEV_INTR_EARLY_SUSPEND) {/* idle >3ms detected */

View File

@ -117,7 +117,7 @@ static void setup_received(void)
setup_data[1] = SETUP2;
/* pass setup data to the upper layer */
usb_core_control_request((struct usb_ctrlrequest*)setup_data);
usb_core_legacy_control_request((struct usb_ctrlrequest*)setup_data);
}
static int max_pkt_size(struct endpoint_t *endp)

View File

@ -1181,7 +1181,7 @@ void VLYNQ(void)
}
/* Process control packet */
usb_core_control_request(&setup);
usb_core_legacy_control_request(&setup);
}
if (sysIntrStatus.f.ep0_in_ack)

View File

@ -877,7 +877,7 @@ static void control_received(void)
}
}
usb_core_control_request((struct usb_ctrlrequest*)tmp);
usb_core_legacy_control_request((struct usb_ctrlrequest*)tmp);
}
static void transfer_completed(void)

View File

@ -522,7 +522,7 @@ static void handle_ep_int(int ep, bool out)
ep0_setup_pkt->bRequest == USB_REQ_SET_ADDRESS)
DCFG = (DCFG & ~bitm(DCFG, devadr)) | (ep0_setup_pkt->wValue << DCFG_devadr_bitp);
usb_core_control_request(ep0_setup_pkt);
usb_core_legacy_control_request(ep0_setup_pkt);
}
}

View File

@ -251,7 +251,7 @@ void handle_control(void)
DEBUG(2, "req: %02x %02d", req->bRequestType, req->bRequest);
}
usb_core_control_request(req);
usb_core_legacy_control_request(req);
}
static

View File

@ -239,7 +239,7 @@ static void EP0_handler(void)
{
readFIFO(ep_recv, REG_USB_REG_COUNT0);
REG_USB_REG_CSR0 = csr0 | USB_CSR0_SVDOUTPKTRDY; /* clear OUTPKTRDY bit */
usb_core_control_request((struct usb_ctrlrequest*)ep_recv->buf);
usb_core_legacy_control_request((struct usb_ctrlrequest*)ep_recv->buf);
}
}

View File

@ -335,7 +335,7 @@ static void EP0_handler(void)
ep0_data_requested = true;
else ep0_data_supplied = true;
REG_USB_CSR0 = csr0;
usb_core_control_request(&ep0_rx.request);
usb_core_legacy_control_request(&ep0_rx.request);
ep_transfer_completed(ep_recv);
}
}

View File

@ -977,7 +977,7 @@ void usb_core_handle_notify(long id, intptr_t data)
}
/* called by usb_drv_int() */
void usb_core_control_request(struct usb_ctrlrequest* req)
void usb_core_legacy_control_request(struct usb_ctrlrequest* req)
{
struct usb_transfer_completion_event_data* completion_event =
&ep_data[EP_CONTROL].completion_event[EP_DIR(USB_DIR_IN)];