usb: introduce new control request API
Change-Id: I6545d8985ab683c026f28f6a7c0e23b40d0a6506
This commit is contained in:
parent
71cc1e78fd
commit
ec164c389c
|
@ -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.
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)];
|
||||
|
|
Loading…
Reference in New Issue