mirror of
https://github.com/Rockbox/rockbox.git
synced 2026-07-10 13:29:52 -04:00
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
This commit is contained in:
parent
64b1f09e06
commit
2291aa24f2
23 changed files with 441 additions and 825 deletions
146
docs/usb-api.md
146
docs/usb-api.md
|
|
@ -3,38 +3,21 @@ 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:
|
||||
|
||||
enum usb_control_response {
|
||||
USB_CONTROL_ACK,
|
||||
USB_CONTROL_STALL,
|
||||
USB_CONTROL_RECEIVE,
|
||||
};
|
||||
# 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);
|
||||
|
||||
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);
|
||||
Also control response goes through the inverse steps, and the following
|
||||
functions are used:
|
||||
|
||||
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.
|
||||
# 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
|
||||
------------------------
|
||||
|
|
@ -45,100 +28,17 @@ 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`.
|
||||
`usb_core_setup_received(req)`, passing the setup packet in `req`.
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
### Non-data transfers
|
||||
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.
|
||||
|
||||
- `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.
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -343,7 +343,7 @@ static void usb_handle_setup_rx(void)
|
|||
if (len == 8)
|
||||
{
|
||||
ISP1583_DFLOW_CTRLFUN |= DFLOW_CTRLFUN_STATUS; /* Acknowledge packet */
|
||||
usb_core_legacy_control_request((struct usb_ctrlrequest*)setup_pkt_buf);
|
||||
usb_core_setup_received((struct usb_ctrlrequest*)setup_pkt_buf);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
|||
|
|
@ -210,7 +210,7 @@ static void control_received(void) {
|
|||
/* acknowledge packet recieved (clear valid) */
|
||||
M66591_INTSTAT_MAIN &= ~(1<<3);
|
||||
|
||||
usb_core_legacy_control_request(&temp);
|
||||
usb_core_setup_received(&temp);
|
||||
}
|
||||
|
||||
/* This is a helper function, it is used to notife the stack that a transfer is
|
||||
|
|
|
|||
|
|
@ -41,7 +41,6 @@
|
|||
/*#define LOGF_ENABLE*/
|
||||
#include "logf.h"
|
||||
|
||||
|
||||
/* The ARM940T uses a subset of the ARMv4 functions, not
|
||||
* supporting clean/invalidate cache entries using MVA.
|
||||
*/
|
||||
|
|
@ -109,30 +108,6 @@ enum usb_dw_epdir
|
|||
USB_DW_EPDIR_OUT = 1,
|
||||
};
|
||||
|
||||
enum usb_dw_ep0_state
|
||||
{
|
||||
/* Waiting for a setup packet to arrive. This is the default state. */
|
||||
EP0_SETUP,
|
||||
|
||||
/* Request wait states -- after submitting a request, we enter EP0_REQ
|
||||
* (or EP0_REQ_CTRLWRITE for control writes). EP0_REQ is also used for
|
||||
* the 2nd phase of a control write. EP0_REQ_CANCELLED is entered if we
|
||||
* receive a setup packet before getting a response from the USB stack. */
|
||||
EP0_REQ,
|
||||
EP0_REQ_CTRLWRITE,
|
||||
EP0_REQ_CANCELLED,
|
||||
|
||||
/* Waiting for a data phase to complete. */
|
||||
EP0_DATA_IN,
|
||||
EP0_DATA_OUT,
|
||||
|
||||
/* Waiting for the status phase */
|
||||
EP0_STATUS_IN,
|
||||
EP0_STATUS_OUT,
|
||||
|
||||
EP0_NUM_STATES
|
||||
};
|
||||
|
||||
/* Internal EP state/info */
|
||||
struct usb_dw_ep
|
||||
{
|
||||
|
|
@ -147,42 +122,14 @@ struct usb_dw_ep
|
|||
uint8_t busy;
|
||||
};
|
||||
|
||||
/* Additional state for EP0 */
|
||||
struct usb_dw_ep0
|
||||
{
|
||||
enum usb_dw_ep0_state state;
|
||||
struct usb_ctrlrequest active_req;
|
||||
struct usb_ctrlrequest pending_req;
|
||||
};
|
||||
|
||||
static const char* const dw_dir_str[USB_DW_NUM_DIRS] =
|
||||
{
|
||||
[USB_DW_EPDIR_IN] = "IN",
|
||||
[USB_DW_EPDIR_OUT] = "OUT",
|
||||
};
|
||||
|
||||
static const char* const dw_state_str[EP0_NUM_STATES] =
|
||||
{
|
||||
[EP0_SETUP] = "setup",
|
||||
[EP0_REQ] = "req",
|
||||
[EP0_REQ_CTRLWRITE] = "req_cw",
|
||||
[EP0_DATA_IN] = "dat_in",
|
||||
[EP0_DATA_OUT] = "dat_out",
|
||||
[EP0_STATUS_IN] = "sts_in",
|
||||
[EP0_STATUS_OUT] = "sts_out",
|
||||
};
|
||||
|
||||
#if 0
|
||||
static const char* const dw_resp_str[3] =
|
||||
{
|
||||
[USB_CONTROL_ACK] = "ACK",
|
||||
[USB_CONTROL_RECEIVE] = "RECV",
|
||||
[USB_CONTROL_STALL] = "STALL",
|
||||
};
|
||||
#endif
|
||||
|
||||
static struct usb_dw_ep usb_dw_ep_list[USB_NUM_ENDPOINTS][USB_DW_NUM_DIRS];
|
||||
static struct usb_dw_ep0 ep0;
|
||||
|
||||
static uint8_t _ep0_buffer[64] USB_DEVBSS_ATTR __attribute__((aligned(32)));
|
||||
static uint8_t* ep0_buffer; /* Uncached, unless NO_UNCACHED_ADDR is defined */
|
||||
|
||||
|
|
@ -750,26 +697,6 @@ static void usb_dw_epstart(int epnum, enum usb_dw_epdir epdir,
|
|||
dw_ep->status = -1;
|
||||
dw_ep->busy = true;
|
||||
|
||||
if (epnum == 0 && epdir == USB_DW_EPDIR_OUT)
|
||||
{
|
||||
/* FIXME: there's an extremely rare race condition here.
|
||||
*
|
||||
* 1. Host sends a control write.
|
||||
* 2. We process the request.
|
||||
* 3. (time passes)
|
||||
* 4. This function is called via USB_CONTROL_RECEIVE response.
|
||||
* 5. Right before we set CNAK, host sends another control write.
|
||||
*
|
||||
* So we may unintentionally receive data from the second request.
|
||||
* It's possible to detect this when we see a setup packet because
|
||||
* EP0 OUT will be busy. In principle it should even be possible to
|
||||
* handle the 2nd request correctly. Currently we don't attempt to
|
||||
* detect or recover from this error.
|
||||
*/
|
||||
DWC_DOEPCTL(0) |= CNAK;
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t maxpktsize = usb_dw_maxpktsize(epnum, epdir);
|
||||
uint32_t packets = usb_dw_calc_packets(xfersize, maxpktsize);
|
||||
uint32_t eptsiz = PKTCNT(packets) | xfersize;
|
||||
|
|
@ -847,139 +774,6 @@ static void usb_dw_abort_endpoint(int epnum, enum usb_dw_epdir epdir)
|
|||
}
|
||||
}
|
||||
|
||||
static void usb_dw_control_received(struct usb_ctrlrequest* req)
|
||||
{
|
||||
logf("%s(%p) state=%s", __func__, req, dw_state_str[ep0.state]);
|
||||
logf(" bRequestType=%02x bRequest=%02x", req->bRequestType, req->bRequest);
|
||||
logf(" wValue=%04x wIndex=%u wLength=%u", req->wValue, req->wIndex, req->wLength);
|
||||
|
||||
switch(ep0.state) {
|
||||
case EP0_REQ:
|
||||
case EP0_REQ_CTRLWRITE:
|
||||
case EP0_REQ_CANCELLED:
|
||||
/* Save the request for later */
|
||||
memcpy(&ep0.pending_req, req, sizeof(*req));
|
||||
ep0.state = EP0_REQ_CANCELLED;
|
||||
break;
|
||||
|
||||
case EP0_DATA_IN:
|
||||
case EP0_STATUS_IN:
|
||||
case EP0_DATA_OUT:
|
||||
case EP0_STATUS_OUT:
|
||||
usb_core_control_complete(-1);
|
||||
/* fallthrough */
|
||||
|
||||
case EP0_SETUP:
|
||||
/* Save the request */
|
||||
memcpy(&ep0.active_req, req, sizeof(*req));
|
||||
req = &ep0.active_req;
|
||||
|
||||
/* Check for a SET ADDRESS request, which we must handle here */
|
||||
if ((req->bRequestType & USB_RECIP_MASK) == USB_RECIP_DEVICE &&
|
||||
(req->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD &&
|
||||
(req->bRequest == USB_REQ_SET_ADDRESS))
|
||||
usb_dw_set_address(req->wValue);
|
||||
|
||||
/* Check for control writes */
|
||||
if (req->wLength > 0 && !(req->bRequestType & USB_DIR_IN))
|
||||
ep0.state = EP0_REQ_CTRLWRITE;
|
||||
else
|
||||
ep0.state = EP0_REQ;
|
||||
|
||||
usb_dw_flush_endpoint(0, USB_DW_EPDIR_IN);
|
||||
usb_core_control_request(req, NULL);
|
||||
break;
|
||||
|
||||
default:
|
||||
panicf("%s: bad state=%s", __func__, ep0.state >= EP0_NUM_STATES ? "unk" : dw_state_str[ep0.state]);
|
||||
}
|
||||
}
|
||||
|
||||
/* note: must be called with IRQs disabled */
|
||||
static void usb_dw_control_response(enum usb_control_response resp,
|
||||
void* data, int length)
|
||||
{
|
||||
struct usb_ctrlrequest* req = &ep0.active_req;
|
||||
|
||||
switch(ep0.state) {
|
||||
case EP0_REQ:
|
||||
case EP0_REQ_CTRLWRITE:
|
||||
switch(resp) {
|
||||
case USB_CONTROL_ACK:
|
||||
if(req->wLength > 0 && (req->bRequestType & USB_DIR_IN))
|
||||
ep0.state = EP0_DATA_IN; /* control read */
|
||||
else
|
||||
ep0.state = EP0_STATUS_IN; /* non-data or write */
|
||||
|
||||
usb_dw_transfer(0, USB_DW_EPDIR_IN, data, length);
|
||||
break;
|
||||
|
||||
case USB_CONTROL_RECEIVE:
|
||||
if(ep0.state != EP0_REQ_CTRLWRITE)
|
||||
panicf("%s: bad response", __func__);
|
||||
|
||||
ep0.state = EP0_DATA_OUT;
|
||||
usb_dw_transfer(0, USB_DW_EPDIR_OUT, data, length);
|
||||
break;
|
||||
|
||||
case USB_CONTROL_STALL:
|
||||
if(ep0.state == EP0_REQ_CTRLWRITE)
|
||||
usb_dw_set_stall(0, USB_DW_EPDIR_OUT, 1);
|
||||
else
|
||||
usb_dw_set_stall(0, USB_DW_EPDIR_IN, 1);
|
||||
|
||||
ep0.state = EP0_SETUP;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case EP0_REQ_CANCELLED:
|
||||
/* Terminate the old request */
|
||||
usb_core_control_complete(-3);
|
||||
|
||||
/* Submit the pending request */
|
||||
ep0.state = EP0_SETUP;
|
||||
usb_dw_control_received(&ep0.pending_req);
|
||||
break;
|
||||
|
||||
default:
|
||||
panicf("%s: bad state=%s", __func__, dw_state_str[ep0.state]);
|
||||
}
|
||||
}
|
||||
|
||||
static void usb_dw_ep0_xfer_complete(enum usb_dw_epdir epdir,
|
||||
int status, int transferred)
|
||||
{
|
||||
struct usb_dw_ep* dw_ep = usb_dw_get_ep(0, epdir);
|
||||
|
||||
switch((ep0.state << 1) | epdir)
|
||||
{
|
||||
case (EP0_DATA_IN << 1) | USB_DW_EPDIR_IN:
|
||||
ep0.state = EP0_STATUS_OUT;
|
||||
usb_dw_transfer(0, USB_DW_EPDIR_OUT, NULL, 0);
|
||||
break;
|
||||
|
||||
case (EP0_DATA_OUT << 1) | USB_DW_EPDIR_OUT:
|
||||
ep0.state = EP0_REQ;
|
||||
usb_core_control_request(&ep0.active_req, dw_ep->req_addr);
|
||||
break;
|
||||
|
||||
case (EP0_STATUS_IN << 1) | USB_DW_EPDIR_IN:
|
||||
case (EP0_STATUS_OUT << 1) | USB_DW_EPDIR_OUT:
|
||||
if(status != 0 || transferred != 0)
|
||||
usb_core_control_complete(-2);
|
||||
else
|
||||
usb_core_control_complete(0);
|
||||
|
||||
ep0.state = EP0_SETUP;
|
||||
break;
|
||||
|
||||
default:
|
||||
panicf("%s: state=%s dir=%s", __func__,
|
||||
dw_state_str[ep0.state], dw_dir_str[epdir]);
|
||||
}
|
||||
}
|
||||
|
||||
static void usb_dw_handle_xfer_complete(int epnum, enum usb_dw_epdir epdir)
|
||||
{
|
||||
struct usb_dw_ep* dw_ep = usb_dw_get_ep(epnum, epdir);
|
||||
|
|
@ -993,7 +787,7 @@ static void usb_dw_handle_xfer_complete(int epnum, enum usb_dw_epdir epdir)
|
|||
}
|
||||
|
||||
uint32_t bytes_left = DWC_EPTSIZ(epnum, epdir) & 0x7ffff;
|
||||
uint32_t transferred = (is_ep0out ? 64 : dw_ep->size) - bytes_left;
|
||||
uint32_t transferred = dw_ep->size - bytes_left;
|
||||
|
||||
if(transferred > dw_ep->sizeleft)
|
||||
{
|
||||
|
|
@ -1008,7 +802,9 @@ static void usb_dw_handle_xfer_complete(int epnum, enum usb_dw_epdir epdir)
|
|||
#if !defined(USB_DW_ARCH_SLAVE) && defined(NO_UNCACHED_ADDR) && defined(POST_DMA_FLUSH)
|
||||
DISCARD_DCACHE_RANGE(ep0_buffer, 64);
|
||||
#endif
|
||||
#ifdef USB_DW_ARCH_SLAVE
|
||||
memcpy(dw_ep->addr, ep0_buffer, transferred);
|
||||
#endif
|
||||
usb_dw_ep0_recv();
|
||||
}
|
||||
|
||||
|
|
@ -1058,15 +854,8 @@ static void usb_dw_handle_xfer_complete(int epnum, enum usb_dw_epdir epdir)
|
|||
semaphore_release(&dw_ep->complete);
|
||||
|
||||
int total_bytes = dw_ep->req_size - dw_ep->sizeleft;
|
||||
if (epnum == 0)
|
||||
{
|
||||
usb_dw_ep0_xfer_complete(epdir, dw_ep->status, total_bytes);
|
||||
}
|
||||
else
|
||||
{
|
||||
usb_core_transfer_complete(epnum, (epdir == USB_DW_EPDIR_OUT) ?
|
||||
USB_DIR_OUT : USB_DIR_IN, dw_ep->status, total_bytes);
|
||||
}
|
||||
usb_core_transfer_complete(epnum, (epdir == USB_DW_EPDIR_OUT) ?
|
||||
USB_DIR_OUT : USB_DIR_IN, dw_ep->status, total_bytes);
|
||||
}
|
||||
|
||||
static void usb_dw_handle_setup_received(void)
|
||||
|
|
@ -1076,10 +865,15 @@ static void usb_dw_handle_setup_received(void)
|
|||
#endif
|
||||
struct usb_ctrlrequest req;
|
||||
memcpy(&req, ep0_buffer, sizeof(struct usb_ctrlrequest));
|
||||
|
||||
usb_dw_flush_endpoint(0, USB_DW_EPDIR_IN);
|
||||
usb_dw_ep0_recv();
|
||||
|
||||
usb_dw_control_received(&req);
|
||||
if ((req.bRequestType & USB_RECIP_MASK) == USB_RECIP_DEVICE &&
|
||||
(req.bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD &&
|
||||
(req.bRequest == USB_REQ_SET_ADDRESS))
|
||||
usb_dw_set_address(req.wValue);
|
||||
|
||||
usb_core_setup_received(&req);
|
||||
}
|
||||
|
||||
#ifdef USB_DW_SHARED_FIFO
|
||||
|
|
@ -1290,7 +1084,6 @@ static void usb_dw_irq(void)
|
|||
if (gintsts & ENUMDNE)
|
||||
{
|
||||
DWC_GINTSTS = ENUMDNE;
|
||||
ep0.state = EP0_SETUP;
|
||||
usb_dw_ep0_recv();
|
||||
}
|
||||
}
|
||||
|
|
@ -1715,14 +1508,6 @@ int usb_drv_send(int endpoint, void *ptr, int length)
|
|||
return dw_ep->status;
|
||||
}
|
||||
|
||||
void usb_drv_control_response(enum usb_control_response resp,
|
||||
void* data, int length)
|
||||
{
|
||||
usb_dw_target_disable_irq();
|
||||
usb_dw_control_response(resp, data, length);
|
||||
usb_dw_target_enable_irq();
|
||||
}
|
||||
|
||||
int usb_drv_get_frame_number()
|
||||
{
|
||||
// SOFFN is 14 bits, the least significant 3 appear to be some sort of microframe count.
|
||||
|
|
|
|||
|
|
@ -1335,7 +1335,6 @@ Lyre prototype 1 */
|
|||
/* Define the implemented USB transport classes */
|
||||
#if CONFIG_USBOTG == USBOTG_ISP1583
|
||||
#define USB_HAS_BULK
|
||||
#define USB_LEGACY_CONTROL_API
|
||||
#elif (CONFIG_USBOTG == USBOTG_DESIGNWARE)
|
||||
#define USB_HAS_BULK
|
||||
#define USB_HAS_INTERRUPT
|
||||
|
|
@ -1352,13 +1351,10 @@ Lyre prototype 1 */
|
|||
#if (CONFIG_USBOTG == USBOTG_ARC)
|
||||
#define USB_HAS_ISOCHRONOUS
|
||||
#endif
|
||||
#define USB_LEGACY_CONTROL_API
|
||||
#elif defined(CPU_TCC780X)
|
||||
#define USB_HAS_BULK
|
||||
#define USB_LEGACY_CONTROL_API
|
||||
#elif CONFIG_USBOTG == USBOTG_S3C6400X
|
||||
#define USB_HAS_BULK
|
||||
#define USB_LEGACY_CONTROL_API
|
||||
//#define USB_HAS_INTERRUPT -- seems to be broken
|
||||
#endif /* CONFIG_USBOTG */
|
||||
|
||||
|
|
|
|||
|
|
@ -179,14 +179,7 @@ enum {
|
|||
USB_NUM_DRIVERS
|
||||
};
|
||||
|
||||
struct usb_transfer_completion_event_data
|
||||
{
|
||||
unsigned char endpoint;
|
||||
int dir;
|
||||
int status;
|
||||
int length;
|
||||
void* data[2];
|
||||
};
|
||||
struct usb_transfer_completion_event_data;
|
||||
#endif /* HAVE_USBSTACK */
|
||||
|
||||
/* initialise the usb code and thread */
|
||||
|
|
|
|||
|
|
@ -39,6 +39,11 @@
|
|||
|
||||
extern int usb_max_pkt_size;
|
||||
|
||||
enum usb_control_response {
|
||||
USB_CONTROL_ACK,
|
||||
USB_CONTROL_STALL,
|
||||
};
|
||||
|
||||
enum {
|
||||
USB_STRING_INDEX_LANGUAGE,
|
||||
USB_STRING_INDEX_MANUFACTURER,
|
||||
|
|
@ -51,9 +56,8 @@ struct usb_class_driver;
|
|||
|
||||
void usb_core_init(void);
|
||||
void usb_core_exit(void);
|
||||
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_setup_received(struct usb_ctrlrequest* req);
|
||||
void usb_core_control_response(enum usb_control_response response, const void* data, size_t size);
|
||||
void usb_core_transfer_complete(int endpoint,int dir,int status,int length);
|
||||
void usb_core_bus_reset(void);
|
||||
void usb_core_enable_driver(int driver,bool enabled);
|
||||
|
|
|
|||
|
|
@ -56,12 +56,6 @@
|
|||
* -> usb_drv_int_enable(false) [ditto]
|
||||
* -> soc specific controller/clock deinit */
|
||||
|
||||
enum usb_control_response {
|
||||
USB_CONTROL_ACK,
|
||||
USB_CONTROL_STALL,
|
||||
USB_CONTROL_RECEIVE,
|
||||
};
|
||||
|
||||
/* endpoint allocation:
|
||||
* there are two ways to implement endpoint allocation.
|
||||
* 1. define usb_drv_ep_specs and usb_drv_ep_specs_flags in the driver.
|
||||
|
|
@ -112,8 +106,6 @@ 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_blocking(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);
|
||||
|
|
|
|||
|
|
@ -629,7 +629,7 @@ static void handle_out_ep(int ep)
|
|||
req->wIndex,
|
||||
req->wLength);
|
||||
|
||||
usb_core_legacy_control_request(&req_copy);
|
||||
usb_core_setup_received(&req_copy);
|
||||
setup_desc_init(setup_desc);
|
||||
|
||||
ep_sts &= ~USB_EP_STAT_SETUP_RCVD;
|
||||
|
|
@ -734,7 +734,7 @@ void INT_USB_FUNC(void)
|
|||
got_set_configuration = 1;
|
||||
|
||||
set_config.wValue = USB_DEV_STS & USB_DEV_STS_MASK_CFG;
|
||||
usb_core_legacy_control_request(&set_config);
|
||||
usb_core_setup_received(&set_config);
|
||||
intr &= ~USB_DEV_INTR_SET_CONFIG;
|
||||
}
|
||||
if (intr & USB_DEV_INTR_EARLY_SUSPEND) {/* idle >3ms detected */
|
||||
|
|
|
|||
|
|
@ -119,7 +119,7 @@ static void setup_received(void)
|
|||
setup_data[1] = SETUP2;
|
||||
|
||||
/* pass setup data to the upper layer */
|
||||
usb_core_legacy_control_request((struct usb_ctrlrequest*)setup_data);
|
||||
usb_core_setup_received((struct usb_ctrlrequest*)setup_data);
|
||||
}
|
||||
|
||||
static int max_pkt_size(struct endpoint_t *endp)
|
||||
|
|
|
|||
|
|
@ -1182,7 +1182,7 @@ void VLYNQ(void)
|
|||
}
|
||||
|
||||
/* Process control packet */
|
||||
usb_core_legacy_control_request(&setup);
|
||||
usb_core_setup_received(&setup);
|
||||
}
|
||||
|
||||
if (sysIntrStatus.f.ep0_in_ack)
|
||||
|
|
|
|||
|
|
@ -1081,7 +1081,7 @@ static void control_received(void)
|
|||
}
|
||||
}
|
||||
|
||||
usb_core_legacy_control_request((struct usb_ctrlrequest*)tmp);
|
||||
usb_core_setup_received((struct usb_ctrlrequest*)tmp);
|
||||
}
|
||||
|
||||
static void transfer_completed(void)
|
||||
|
|
|
|||
|
|
@ -530,7 +530,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_legacy_control_request(ep0_setup_pkt);
|
||||
usb_core_setup_received(ep0_setup_pkt);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -229,7 +229,7 @@ void handle_control(void)
|
|||
DEBUG(2, "req: %02x %02d", req->bRequestType, req->bRequest);
|
||||
}
|
||||
|
||||
usb_core_legacy_control_request(req);
|
||||
usb_core_setup_received(req);
|
||||
}
|
||||
|
||||
static
|
||||
|
|
|
|||
|
|
@ -246,7 +246,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_legacy_control_request((struct usb_ctrlrequest*)ep_recv->buf);
|
||||
usb_core_setup_received((struct usb_ctrlrequest*)ep_recv->buf);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -320,7 +320,7 @@ static void EP0_handler(void)
|
|||
ep0_data_supplied = true;
|
||||
}
|
||||
REG_USB_CSR0 = csr0;
|
||||
usb_core_legacy_control_request(&ep0_rx.request);
|
||||
usb_core_setup_received(&ep0_rx.request);
|
||||
ep_transfer_completed(ep_recv);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -307,9 +307,6 @@ static struct usb_class_driver_ep_allocation ep_allocs[2] = {
|
|||
#define EP_ISO_OUT (ep_allocs[0].ep)
|
||||
#define EP_ISO_FEEDBACK_IN (ep_allocs[1].ep)
|
||||
|
||||
/* small buffer used for control transfers */
|
||||
static unsigned char usb_buffer[128] USB_DEVBSS_ATTR;
|
||||
|
||||
/* number of buffers: 2 is double-buffering (one for usb, one for playback),
|
||||
* 3 is triple-buffering (one for usb, one for playback, one for queuing), ... */
|
||||
|
||||
|
|
@ -833,8 +830,10 @@ int32_t usb_audio_get_samples_rx_perframe(void)
|
|||
return samples_received_report;
|
||||
}
|
||||
|
||||
static bool usb_audio_as_ctrldata_endpoint_request(struct usb_ctrlrequest* req, void *reqdata)
|
||||
static bool usb_audio_as_ctrldata_endpoint_request(struct usb_ctrlrequest* req, uint8_t* reqdata, size_t reqdata_size)
|
||||
{
|
||||
(void)reqdata_size; /* should check this? */
|
||||
|
||||
/* only support sampling frequency */
|
||||
if(req->wValue != (USB_AS_EP_CS_SAMPLING_FREQ_CTL << 8))
|
||||
{
|
||||
|
|
@ -848,40 +847,26 @@ static bool usb_audio_as_ctrldata_endpoint_request(struct usb_ctrlrequest* req,
|
|||
if(req->wLength != 3)
|
||||
{
|
||||
logf("usbaudio: bad length for SET_CUR");
|
||||
usb_drv_control_response(USB_CONTROL_STALL, NULL, 0);
|
||||
usb_core_control_response(USB_CONTROL_STALL, NULL, 0);
|
||||
return true;
|
||||
}
|
||||
logf("usbaudio: SET_CUR sampling freq");
|
||||
|
||||
if (reqdata) { /* control write, second pass */
|
||||
set_playback_sampling_frequency(decode3(reqdata));
|
||||
usb_drv_control_response(USB_CONTROL_ACK, NULL, 0);
|
||||
return true;
|
||||
} else { /* control write, first pass */
|
||||
bool error = false;
|
||||
set_playback_sampling_frequency(decode3(reqdata));
|
||||
usb_core_control_response(USB_CONTROL_ACK, NULL, 0);
|
||||
|
||||
if (req->wLength != 3)
|
||||
error = true;
|
||||
/* ... other validation? */
|
||||
|
||||
if (error)
|
||||
usb_drv_control_response(USB_CONTROL_STALL, NULL, 0);
|
||||
else
|
||||
usb_drv_control_response(USB_CONTROL_RECEIVE, usb_buffer, 3);
|
||||
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
|
||||
case USB_AC_GET_CUR:
|
||||
if(req->wLength != 3)
|
||||
{
|
||||
logf("usbaudio: bad length for GET_CUR");
|
||||
usb_drv_control_response(USB_CONTROL_STALL, NULL, 0);
|
||||
usb_core_control_response(USB_CONTROL_STALL, NULL, 0);
|
||||
return true;
|
||||
}
|
||||
logf("usbaudio: GET_CUR sampling freq");
|
||||
encode3(usb_buffer, usb_audio_get_playback_sampling_frequency());
|
||||
usb_drv_control_response(USB_CONTROL_ACK, usb_buffer, req->wLength);
|
||||
encode3(reqdata, usb_audio_get_playback_sampling_frequency());
|
||||
usb_core_control_response(USB_CONTROL_ACK, reqdata, 3);
|
||||
|
||||
return true;
|
||||
|
||||
|
|
@ -892,12 +877,12 @@ static bool usb_audio_as_ctrldata_endpoint_request(struct usb_ctrlrequest* req,
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool usb_audio_endpoint_request(struct usb_ctrlrequest* req, void *reqdata)
|
||||
static bool usb_audio_endpoint_request(struct usb_ctrlrequest* req, uint8_t* reqdata, size_t reqdata_size)
|
||||
{
|
||||
int ep = req->wIndex & 0xff;
|
||||
|
||||
if(ep == EP_ISO_OUT)
|
||||
return usb_audio_as_ctrldata_endpoint_request(req, reqdata);
|
||||
return usb_audio_as_ctrldata_endpoint_request(req, reqdata, reqdata_size);
|
||||
else
|
||||
{
|
||||
logf("usbaudio: unhandled ep req (ep=%d)", ep);
|
||||
|
|
@ -1029,8 +1014,10 @@ int usb_audio_get_cur_volume(void)
|
|||
return usb_audio_volume_to_db(vol, sound_numdecimals(SOUND_VOLUME));
|
||||
}
|
||||
|
||||
static bool usb_audio_set_get_feature_unit(struct usb_ctrlrequest* req, void *reqdata)
|
||||
static bool usb_audio_set_get_feature_unit(struct usb_ctrlrequest* req, uint8_t* reqdata, size_t reqdata_size)
|
||||
{
|
||||
(void)reqdata_size; /* should check this? */
|
||||
|
||||
int channel = req->wValue & 0xff;
|
||||
int selector = req->wValue >> 8;
|
||||
uint8_t cmd = (req->bRequest & ~USB_AC_GET_REQ);
|
||||
|
|
@ -1066,7 +1053,7 @@ static bool usb_audio_set_get_feature_unit(struct usb_ctrlrequest* req, void *re
|
|||
if(!handled)
|
||||
{
|
||||
logf("usbaudio: unhandled get control 0x%x selector 0x%x of feature unit", cmd, selector);
|
||||
usb_drv_control_response(USB_CONTROL_STALL, NULL, 0);
|
||||
usb_core_control_response(USB_CONTROL_STALL, NULL, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -1077,9 +1064,9 @@ static bool usb_audio_set_get_feature_unit(struct usb_ctrlrequest* req, void *re
|
|||
}
|
||||
|
||||
for(i = 0; i < req->wLength; i++)
|
||||
usb_buffer[i] = (value >> (8 * i)) & 0xff;
|
||||
reqdata[i] = (value >> (8 * i)) & 0xff;
|
||||
|
||||
usb_drv_control_response(USB_CONTROL_ACK, usb_buffer, req->wLength);
|
||||
usb_core_control_response(USB_CONTROL_ACK, reqdata, req->wLength);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
|
|
@ -1091,68 +1078,47 @@ static bool usb_audio_set_get_feature_unit(struct usb_ctrlrequest* req, void *re
|
|||
return false;
|
||||
}
|
||||
|
||||
if (reqdata) {
|
||||
for(i = 0; i < req->wLength; i++)
|
||||
value = value | (reqdata[i] << (i * 8));
|
||||
|
||||
for(i = 0; i < req->wLength; i++)
|
||||
value = value | (usb_buffer[i] << (i * 8));
|
||||
switch(selector)
|
||||
{
|
||||
case USB_AC_FU_MUTE:
|
||||
handled = (req->wLength == 1) && feature_unit_set_mute(value, cmd);
|
||||
break;
|
||||
case USB_AC_VOLUME_CONTROL:
|
||||
handled = (req->wLength == 2) && feature_unit_set_volume(value, cmd);
|
||||
break;
|
||||
default:
|
||||
handled = false;
|
||||
logf("usbaudio: unhandled control selector of feature unit (0x%x)", selector);
|
||||
break;
|
||||
}
|
||||
|
||||
switch(selector)
|
||||
{
|
||||
case USB_AC_FU_MUTE:
|
||||
handled = (req->wLength == 1) && feature_unit_set_mute(value, cmd);
|
||||
break;
|
||||
case USB_AC_VOLUME_CONTROL:
|
||||
handled = (req->wLength == 2) && feature_unit_set_volume(value, cmd);
|
||||
break;
|
||||
default:
|
||||
handled = false;
|
||||
logf("usbaudio: unhandled control selector of feature unit (0x%x)", selector);
|
||||
break;
|
||||
}
|
||||
|
||||
if(!handled)
|
||||
{
|
||||
logf("usbaudio: unhandled set control 0x%x selector 0x%x of feature unit", cmd, selector);
|
||||
usb_drv_control_response(USB_CONTROL_STALL, NULL, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
usb_drv_control_response(USB_CONTROL_ACK, NULL, 0);
|
||||
return true;
|
||||
if(!handled) {
|
||||
logf("usbaudio: unhandled set control 0x%x selector 0x%x of feature unit", cmd, selector);
|
||||
usb_core_control_response(USB_CONTROL_STALL, NULL, 0);
|
||||
} else {
|
||||
/*
|
||||
* should handle the following (req->wValue >> 8):
|
||||
* USB_AC_FU_MUTE
|
||||
* USB_AC_VOLUME_CONTROL
|
||||
*/
|
||||
|
||||
bool error = false;
|
||||
|
||||
if (error)
|
||||
usb_drv_control_response(USB_CONTROL_STALL, NULL, 0);
|
||||
else
|
||||
usb_drv_control_response(USB_CONTROL_RECEIVE, usb_buffer, 3);
|
||||
|
||||
return true;
|
||||
usb_core_control_response(USB_CONTROL_ACK, NULL, 0);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static bool usb_audio_ac_set_get_request(struct usb_ctrlrequest* req, void *reqdata)
|
||||
static bool usb_audio_ac_set_get_request(struct usb_ctrlrequest* req, uint8_t* reqdata, size_t reqdata_size)
|
||||
{
|
||||
switch(req->wIndex >> 8)
|
||||
{
|
||||
case AC_PLAYBACK_FEATURE_ID:
|
||||
return usb_audio_set_get_feature_unit(req, reqdata);
|
||||
return usb_audio_set_get_feature_unit(req, reqdata, reqdata_size);
|
||||
default:
|
||||
logf("usbaudio: unhandled set/get on entity %d", req->wIndex >> 8);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool usb_audio_interface_request(struct usb_ctrlrequest* req, void *reqdata)
|
||||
static bool usb_audio_interface_request(struct usb_ctrlrequest* req, uint8_t* reqdata, size_t reqdata_size)
|
||||
{
|
||||
int intf = req->wIndex & 0xff;
|
||||
|
||||
|
|
@ -1163,7 +1129,7 @@ static bool usb_audio_interface_request(struct usb_ctrlrequest* req, void *reqda
|
|||
case USB_AC_SET_CUR: case USB_AC_SET_MIN: case USB_AC_SET_MAX: case USB_AC_SET_RES:
|
||||
case USB_AC_SET_MEM: case USB_AC_GET_CUR: case USB_AC_GET_MIN: case USB_AC_GET_MAX:
|
||||
case USB_AC_GET_RES: case USB_AC_GET_MEM:
|
||||
return usb_audio_ac_set_get_request(req, reqdata);
|
||||
return usb_audio_ac_set_get_request(req, reqdata, reqdata_size);
|
||||
default:
|
||||
logf("usbaudio: unhandled ac intf req 0x%x", req->bRequest);
|
||||
return false;
|
||||
|
|
@ -1182,17 +1148,14 @@ static bool usb_audio_interface_request(struct usb_ctrlrequest* req, void *reqda
|
|||
*
|
||||
* Return true if this driver handles the request, false otherwise.
|
||||
*/
|
||||
static bool usb_audio_control_request(struct usb_ctrlrequest* req, void *reqdata, unsigned char* dest)
|
||||
static bool usb_audio_control_request(struct usb_ctrlrequest* req, uint8_t* reqdata, size_t reqdata_size)
|
||||
{
|
||||
(void) reqdata;
|
||||
(void) dest;
|
||||
|
||||
switch(req->bRequestType & USB_RECIP_MASK)
|
||||
{
|
||||
case USB_RECIP_ENDPOINT:
|
||||
return usb_audio_endpoint_request(req, reqdata);
|
||||
return usb_audio_endpoint_request(req, reqdata, reqdata_size);
|
||||
case USB_RECIP_INTERFACE:
|
||||
return usb_audio_interface_request(req, reqdata);
|
||||
return usb_audio_interface_request(req, reqdata, reqdata_size);
|
||||
default:
|
||||
logf("usbaudio: unhandled req type 0x%x", req->bRequestType);
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -112,7 +112,7 @@ struct usb_class_driver {
|
|||
able to handle it, it should ack the request, and return true. Otherwise
|
||||
it should return false.
|
||||
Optional function */
|
||||
bool (*control_request)(struct usb_ctrlrequest* req, void* reqdata, unsigned char *dest);
|
||||
bool (*control_request)(struct usb_ctrlrequest* req, uint8_t* reqdata, size_t reqdata_size);
|
||||
|
||||
#ifdef HAVE_HOTSWAP
|
||||
/* Tells the driver that a hotswappable disk/card was inserted or
|
||||
|
|
|
|||
|
|
@ -184,10 +184,71 @@ static int usb_no_host_callback(struct timeout *tmo)
|
|||
}
|
||||
#endif
|
||||
|
||||
static struct usb_ctrlrequest handling_request;
|
||||
static struct usb_ctrlrequest pending_request;
|
||||
static volatile bool have_pending_request;
|
||||
|
||||
/* control endpoint typical state flow
|
||||
* READY -setup_received(IN, wLength==0)->
|
||||
* HANDLING_TX_CONTROL -control_response(size==0)->
|
||||
* EXPECT_TX_STATUS_COMP -transfer_complete(OUT)->
|
||||
* READY
|
||||
* -setup_received(IN, wLength>0)->
|
||||
* HANDLING_TX_CONTROL -control_response(size>0)->
|
||||
* EXPECT_TX_DATA_STATUS_COMP
|
||||
* -transfer_complete(OUT)->
|
||||
* EXPECT_TX_DATA_COMP -transfer_complete(IN)->
|
||||
* READY
|
||||
* -transfer_complete(IN)->
|
||||
* EXPECT_TX_STATUS_COMP -transfer_complete(OUT)->
|
||||
* READY
|
||||
* -setup_received(OUT, wLength==0)->
|
||||
* HANDLING_RX_CONTROL -control_response(size==0)->
|
||||
* EXPECT_RX_STATUS_COMP -transfer_complete(IN)->
|
||||
* READY
|
||||
* -setup_received(OUT, wLength>0)->
|
||||
* HANDLING_RX_CONTROL ->
|
||||
* EXPECT_RX_DATA_COMP -transfer_complete(OUT)->
|
||||
* HANDLING_TX_CONTROL -control_response(size==0)->
|
||||
* EXPECT_RX_STATUS_COMP -transfer_complete()->
|
||||
* READY
|
||||
* */
|
||||
enum {
|
||||
EP0_READY, /* waiting for new setup packet */
|
||||
/* IN request */
|
||||
EP0_HANDLING_TX_CONTROL, /* control in received, usb thread is processing it */
|
||||
EP0_EXPECT_TX_DATA_STATUS_COMP, /* sending control in data phase */
|
||||
EP0_EXPECT_TX_DATA_COMP, /* status packet received earlier than tx completion */
|
||||
EP0_EXPECT_TX_STATUS_COMP, /* receiving status */
|
||||
/* OUT request */
|
||||
EP0_HANDLING_RX_CONTROL, /* control out received, usb thread is processing it */
|
||||
EP0_EXPECT_RX_DATA_COMP, /* receiving control out data phase */
|
||||
EP0_EXPECT_RX_STATUS_COMP, /* sending status */
|
||||
};
|
||||
static volatile int ep0_state;
|
||||
|
||||
#define TRACE_EP0_STATE 0
|
||||
#if TRACE_EP0_STATE == 1
|
||||
#define set_ep0_state(new) \
|
||||
{ \
|
||||
logf("usb_core:%d ep0_state %d -> %d", __LINE__, ep0_state, new); \
|
||||
ep0_state = new; \
|
||||
}
|
||||
#else
|
||||
#define set_ep0_state(new) {ep0_state = new;}
|
||||
#endif
|
||||
|
||||
struct usb_transfer_completion_event_data
|
||||
{
|
||||
struct usb_ctrlrequest* req;
|
||||
unsigned char ep;
|
||||
int status;
|
||||
int length;
|
||||
};
|
||||
|
||||
typedef void (*completion_handler_t)(int ep, int dir, int status, int length);
|
||||
typedef bool (*fast_completion_handler_t)(int ep, int dir, int status, int length);
|
||||
typedef bool (*control_handler_t)(struct usb_ctrlrequest* req, void* reqdata,
|
||||
unsigned char* dest);
|
||||
typedef bool (*control_handler_t)(struct usb_ctrlrequest* req, uint8_t* reqdata, size_t reqdata_size);
|
||||
|
||||
static struct
|
||||
{
|
||||
|
|
@ -232,18 +293,8 @@ static struct usb_class_driver* drivers[USB_NUM_DRIVERS] =
|
|||
#endif
|
||||
};
|
||||
|
||||
#ifdef USB_LEGACY_CONTROL_API
|
||||
static struct usb_ctrlrequest buffered_request;
|
||||
static struct usb_ctrlrequest* volatile active_request = NULL;
|
||||
static volatile unsigned int num_active_requests = 0;
|
||||
static void* volatile control_write_data = NULL;
|
||||
static volatile bool control_write_data_done = false;
|
||||
#endif
|
||||
|
||||
static int usb_core_do_set_config(uint8_t new_config);
|
||||
static void usb_core_control_request_handler(struct usb_ctrlrequest* req, void* reqdata);
|
||||
|
||||
static unsigned char response_data[256] USB_DEVBSS_ATTR;
|
||||
static void usb_core_control_request_handler(struct usb_ctrlrequest* req, uint8_t* reqdata, size_t reqdata_size);
|
||||
|
||||
#define is_active(driver) ((driver)->enabled && !(driver)->error && (driver)->config == usb_config)
|
||||
#define has_if(driver, interface) ((interface) >= (driver)->first_interface && (interface) < (driver)->last_interface)
|
||||
|
|
@ -441,27 +492,24 @@ void usb_core_exit(void)
|
|||
logf("usb_core_exit() finished");
|
||||
}
|
||||
|
||||
/* for sending/receiving control data */
|
||||
static uint8_t usb_control_data[256] USB_DEVBSS_ATTR;
|
||||
|
||||
void usb_core_handle_transfer_completion(
|
||||
struct usb_transfer_completion_event_data* event)
|
||||
{
|
||||
completion_handler_t handler;
|
||||
int ep = event->endpoint;
|
||||
int num = EP_NUM(event->ep);
|
||||
int dir = EP_DIR(event->ep);
|
||||
|
||||
switch(ep) {
|
||||
case EP_CONTROL:
|
||||
logf("ctrl handled %ld req=0x%x",
|
||||
current_tick,
|
||||
((struct usb_ctrlrequest*)event->data[0])->bRequest);
|
||||
|
||||
usb_core_control_request_handler(
|
||||
(struct usb_ctrlrequest*)event->data[0], event->data[1]);
|
||||
break;
|
||||
default:
|
||||
handler = ep_data[ep].completion_handler[EP_DIR(event->dir)];
|
||||
if(handler != NULL)
|
||||
handler(ep, event->dir, event->status, event->length);
|
||||
break;
|
||||
if(num == EP_CONTROL) {
|
||||
logf("ctrl handled %ld req=0x%x", current_tick,event->req->bRequest);
|
||||
usb_core_control_request_handler(event->req, usb_control_data, sizeof(usb_control_data));
|
||||
return;
|
||||
}
|
||||
|
||||
completion_handler_t handler = ep_data[num].completion_handler[dir];
|
||||
if(handler != NULL)
|
||||
handler(num, dir == DIR_IN ? USB_DIR_IN : USB_DIR_OUT, event->status, event->length);
|
||||
}
|
||||
|
||||
void usb_core_enable_driver(int driver, bool enabled)
|
||||
|
|
@ -676,8 +724,7 @@ retry:
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
static void control_request_handler_drivers(struct usb_ctrlrequest* req, void* reqdata)
|
||||
static void control_request_handler_drivers(struct usb_ctrlrequest* req, uint8_t* reqdata, size_t reqdata_size)
|
||||
{
|
||||
int i, interface = req->wIndex & 0xff;
|
||||
bool handled = false;
|
||||
|
|
@ -694,7 +741,7 @@ static void control_request_handler_drivers(struct usb_ctrlrequest* req, void* r
|
|||
if(req->bRequest == USB_REQ_SET_INTERFACE) {
|
||||
logf("usb_core: SET INTERFACE 0x%x 0x%x", req->wValue, req->wIndex);
|
||||
if(driver->set_interface && driver->set_interface(req->wIndex, req->wValue) >= 0) {
|
||||
usb_drv_control_response(USB_CONTROL_ACK, NULL, 0);
|
||||
usb_core_control_response(USB_CONTROL_ACK, NULL, 0);
|
||||
handled = true;
|
||||
}
|
||||
break;
|
||||
|
|
@ -706,26 +753,26 @@ static void control_request_handler_drivers(struct usb_ctrlrequest* req, void* r
|
|||
alt = driver->get_interface(req->wIndex);
|
||||
|
||||
if(alt >= 0 && alt < 255) {
|
||||
response_data[0] = alt;
|
||||
usb_drv_control_response(USB_CONTROL_ACK, response_data, 1);
|
||||
reqdata[0] = alt;
|
||||
usb_core_control_response(USB_CONTROL_ACK, reqdata, 1);
|
||||
handled = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
handled = driver->control_request(req, reqdata, response_data);
|
||||
handled = driver->control_request(req, reqdata, reqdata_size);
|
||||
break; /* no other driver can handle it because it's interface specific */
|
||||
}
|
||||
if(!handled) {
|
||||
/* nope. flag error */
|
||||
logf("bad req 0x%x:0x%x:0x%x:0x%x:0x%x", req->bRequestType,req->bRequest,
|
||||
req->wValue, req->wIndex, req->wLength);
|
||||
usb_drv_control_response(USB_CONTROL_STALL, NULL, 0);
|
||||
usb_core_control_response(USB_CONTROL_STALL, NULL, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void request_handler_device_get_descriptor(struct usb_ctrlrequest* req, void* reqdata)
|
||||
static void request_handler_device_get_descriptor(struct usb_ctrlrequest* req, uint8_t* reqdata, size_t reqdata_size)
|
||||
{
|
||||
int size;
|
||||
const void* ptr = NULL;
|
||||
|
|
@ -769,16 +816,16 @@ static void request_handler_device_get_descriptor(struct usb_ctrlrequest* req, v
|
|||
|
||||
for(i = 0; i < USB_NUM_DRIVERS; i++) {
|
||||
if(drivers[i]->enabled && drivers[i]->config == index + 1 && drivers[i]->get_config_descriptor) {
|
||||
size += drivers[i]->get_config_descriptor(&response_data[size], max_packet_size);
|
||||
size += drivers[i]->get_config_descriptor(reqdata + size, max_packet_size);
|
||||
}
|
||||
}
|
||||
|
||||
config_descriptor.bNumInterfaces = config_states[index].num_interfaces;
|
||||
config_descriptor.bConfigurationValue = index + 1;
|
||||
config_descriptor.wTotalLength = (uint16_t)size;
|
||||
memcpy(&response_data[0], &config_descriptor, sizeof(struct usb_config_descriptor));
|
||||
memcpy(reqdata, &config_descriptor, sizeof(struct usb_config_descriptor));
|
||||
|
||||
ptr = response_data;
|
||||
ptr = reqdata;
|
||||
} break;
|
||||
case USB_DT_STRING:
|
||||
logf("STRING %d", index);
|
||||
|
|
@ -807,7 +854,7 @@ static void request_handler_device_get_descriptor(struct usb_ctrlrequest* req, v
|
|||
|
||||
default:
|
||||
logf("ctrl desc.");
|
||||
control_request_handler_drivers(req, reqdata);
|
||||
control_request_handler_drivers(req, reqdata, reqdata_size);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -815,12 +862,12 @@ static void request_handler_device_get_descriptor(struct usb_ctrlrequest* req, v
|
|||
logf("data %d (%d)", size, length);
|
||||
length = MIN(size, length);
|
||||
|
||||
if (ptr != response_data)
|
||||
memcpy(response_data, ptr, length);
|
||||
if (ptr != reqdata)
|
||||
memcpy(reqdata, ptr, length);
|
||||
|
||||
usb_drv_control_response(USB_CONTROL_ACK, response_data, length);
|
||||
usb_core_control_response(USB_CONTROL_ACK, reqdata, length);
|
||||
} else {
|
||||
usb_drv_control_response(USB_CONTROL_STALL, NULL, 0);
|
||||
usb_core_control_response(USB_CONTROL_STALL, NULL, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -916,22 +963,22 @@ static void usb_core_do_clear_feature(int recip, int recip_nr, int feature)
|
|||
}
|
||||
}
|
||||
|
||||
static void request_handler_device(struct usb_ctrlrequest* req, void* reqdata)
|
||||
static void request_handler_device(struct usb_ctrlrequest* req, uint8_t* reqdata, size_t reqdata_size)
|
||||
{
|
||||
unsigned address;
|
||||
|
||||
switch(req->bRequest) {
|
||||
case USB_REQ_GET_CONFIGURATION:
|
||||
logf("usb_core: GET_CONFIG");
|
||||
response_data[0] = usb_config;
|
||||
usb_drv_control_response(USB_CONTROL_ACK, response_data, 1);
|
||||
reqdata[0] = usb_config;
|
||||
usb_core_control_response(USB_CONTROL_ACK, reqdata, 1);
|
||||
break;
|
||||
case USB_REQ_SET_CONFIGURATION:
|
||||
usb_drv_cancel_all_transfers();
|
||||
if(usb_core_do_set_config(req->wValue) == 0) {
|
||||
usb_drv_control_response(USB_CONTROL_ACK, NULL, 0);
|
||||
usb_core_control_response(USB_CONTROL_ACK, NULL, 0);
|
||||
} else {
|
||||
usb_drv_control_response(USB_CONTROL_STALL, NULL, 0);
|
||||
usb_core_control_response(USB_CONTROL_STALL, NULL, 0);
|
||||
}
|
||||
break;
|
||||
case USB_REQ_SET_ADDRESS:
|
||||
|
|
@ -939,85 +986,85 @@ static void request_handler_device(struct usb_ctrlrequest* req, void* reqdata)
|
|||
* should just handle it themselves. We don't care beyond
|
||||
* knowing if we've been assigned an address yet, or not. */
|
||||
address = req->wValue;
|
||||
usb_drv_control_response(USB_CONTROL_ACK, NULL, 0);
|
||||
usb_drv_cancel_all_transfers();
|
||||
usb_core_control_response(USB_CONTROL_ACK, NULL, 0);
|
||||
usb_drv_set_address(address);
|
||||
usb_core_do_set_addr(address);
|
||||
break;
|
||||
case USB_REQ_GET_DESCRIPTOR:
|
||||
logf("usb_core: GET_DESC %d", req->wValue >> 8);
|
||||
request_handler_device_get_descriptor(req, reqdata);
|
||||
request_handler_device_get_descriptor(req, reqdata, reqdata_size);
|
||||
break;
|
||||
case USB_REQ_SET_FEATURE:
|
||||
if(req->wValue==USB_DEVICE_TEST_MODE) {
|
||||
int mode = req->wIndex >> 8;
|
||||
usb_drv_control_response(USB_CONTROL_ACK, NULL, 0);
|
||||
usb_core_control_response(USB_CONTROL_ACK, NULL, 0);
|
||||
usb_drv_set_test_mode(mode);
|
||||
} else {
|
||||
usb_drv_control_response(USB_CONTROL_STALL, NULL, 0);
|
||||
usb_core_control_response(USB_CONTROL_STALL, NULL, 0);
|
||||
}
|
||||
break;
|
||||
case USB_REQ_GET_STATUS:
|
||||
response_data[0] = 0;
|
||||
response_data[1] = 0;
|
||||
usb_drv_control_response(USB_CONTROL_ACK, response_data, 2);
|
||||
reqdata[0] = 0;
|
||||
reqdata[1] = 0;
|
||||
usb_core_control_response(USB_CONTROL_ACK, reqdata, 2);
|
||||
break;
|
||||
#ifdef USB_ENABLE_IAP
|
||||
case USB_REQ_APPLE_SET_AVAIL_CURRENT:
|
||||
usb_drv_control_response(USB_CONTROL_ACK, NULL, 0);
|
||||
usb_core_control_response(USB_CONTROL_ACK, NULL, 0);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
logf("bad req:desc %d:%d", req->bRequest, req->wValue);
|
||||
usb_drv_control_response(USB_CONTROL_STALL, NULL, 0);
|
||||
usb_core_control_response(USB_CONTROL_STALL, NULL, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void request_handler_interface_standard(struct usb_ctrlrequest* req, void* reqdata)
|
||||
static void request_handler_interface_standard(struct usb_ctrlrequest* req, uint8_t* reqdata, size_t reqdata_size)
|
||||
{
|
||||
switch (req->bRequest)
|
||||
{
|
||||
case USB_REQ_SET_INTERFACE:
|
||||
logf("usb_core: SET_INTERFACE");
|
||||
case USB_REQ_GET_INTERFACE:
|
||||
control_request_handler_drivers(req, reqdata);
|
||||
control_request_handler_drivers(req, reqdata, reqdata_size);
|
||||
break;
|
||||
case USB_REQ_GET_STATUS:
|
||||
response_data[0] = 0;
|
||||
response_data[1] = 0;
|
||||
usb_drv_control_response(USB_CONTROL_ACK, response_data, 2);
|
||||
reqdata[0] = 0;
|
||||
reqdata[1] = 0;
|
||||
usb_core_control_response(USB_CONTROL_ACK, reqdata, 2);
|
||||
break;
|
||||
case USB_REQ_CLEAR_FEATURE:
|
||||
case USB_REQ_SET_FEATURE:
|
||||
/* TODO: These used to be ignored (erroneously).
|
||||
* Should they be passed to the drivers instead? */
|
||||
usb_drv_control_response(USB_CONTROL_STALL, NULL, 0);
|
||||
usb_core_control_response(USB_CONTROL_STALL, NULL, 0);
|
||||
break;
|
||||
default:
|
||||
control_request_handler_drivers(req, reqdata);
|
||||
control_request_handler_drivers(req, reqdata, reqdata_size);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void request_handler_interface(struct usb_ctrlrequest* req, void* reqdata)
|
||||
static void request_handler_interface(struct usb_ctrlrequest* req, uint8_t* reqdata, size_t reqdata_size)
|
||||
{
|
||||
switch(req->bRequestType & USB_TYPE_MASK) {
|
||||
case USB_TYPE_STANDARD:
|
||||
request_handler_interface_standard(req, reqdata);
|
||||
request_handler_interface_standard(req, reqdata, reqdata_size);
|
||||
break;
|
||||
case USB_TYPE_CLASS:
|
||||
control_request_handler_drivers(req, reqdata);
|
||||
control_request_handler_drivers(req, reqdata, reqdata_size);
|
||||
break;
|
||||
case USB_TYPE_VENDOR:
|
||||
default:
|
||||
logf("bad req:desc %d", req->bRequest);
|
||||
usb_drv_control_response(USB_CONTROL_STALL, NULL, 0);
|
||||
usb_core_control_response(USB_CONTROL_STALL, NULL, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void request_handler_endpoint_drivers(struct usb_ctrlrequest* req, void* reqdata)
|
||||
static void request_handler_endpoint_drivers(struct usb_ctrlrequest* req, uint8_t* reqdata, size_t reqdata_size)
|
||||
{
|
||||
bool handled = false;
|
||||
control_handler_t control_handler = NULL;
|
||||
|
|
@ -1027,67 +1074,67 @@ static void request_handler_endpoint_drivers(struct usb_ctrlrequest* req, void*
|
|||
ep_data[EP_NUM(req->wIndex)].control_handler[EP_DIR(req->wIndex)];
|
||||
|
||||
if(control_handler)
|
||||
handled = control_handler(req, reqdata, response_data);
|
||||
handled = control_handler(req, reqdata, reqdata_size);
|
||||
|
||||
if(!handled) {
|
||||
/* nope. flag error */
|
||||
logf("bad req 0x%x:0x%x:0x%x:0x%x:0x%x", req->bRequestType,req->bRequest,
|
||||
req->wValue, req->wIndex, req->wLength);
|
||||
usb_drv_control_response(USB_CONTROL_STALL, NULL, 0);
|
||||
usb_core_control_response(USB_CONTROL_STALL, NULL, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void request_handler_endpoint_standard(struct usb_ctrlrequest* req, void* reqdata)
|
||||
static void request_handler_endpoint_standard(struct usb_ctrlrequest* req, uint8_t* reqdata, size_t reqdata_size)
|
||||
{
|
||||
switch (req->bRequest) {
|
||||
case USB_REQ_CLEAR_FEATURE:
|
||||
usb_core_do_clear_feature(USB_RECIP_ENDPOINT,
|
||||
req->wIndex,
|
||||
req->wValue);
|
||||
usb_drv_control_response(USB_CONTROL_ACK, NULL, 0);
|
||||
usb_core_control_response(USB_CONTROL_ACK, NULL, 0);
|
||||
break;
|
||||
case USB_REQ_SET_FEATURE:
|
||||
logf("usb_core: SET FEATURE (%d)", req->wValue);
|
||||
if(req->wValue == USB_ENDPOINT_HALT)
|
||||
usb_drv_stall(EP_NUM(req->wIndex), true, EP_DIR(req->wIndex));
|
||||
|
||||
usb_drv_control_response(USB_CONTROL_ACK, NULL, 0);
|
||||
usb_core_control_response(USB_CONTROL_ACK, NULL, 0);
|
||||
break;
|
||||
case USB_REQ_GET_STATUS:
|
||||
response_data[0] = 0;
|
||||
response_data[1] = 0;
|
||||
reqdata[0] = 0;
|
||||
reqdata[1] = 0;
|
||||
logf("usb_core: GET_STATUS");
|
||||
if(req->wIndex > 0)
|
||||
response_data[0] = usb_drv_stalled(EP_NUM(req->wIndex),
|
||||
EP_DIR(req->wIndex));
|
||||
reqdata[0] = usb_drv_stalled(EP_NUM(req->wIndex),
|
||||
EP_DIR(req->wIndex));
|
||||
|
||||
usb_drv_control_response(USB_CONTROL_ACK, response_data, 2);
|
||||
usb_core_control_response(USB_CONTROL_ACK, reqdata, 2);
|
||||
break;
|
||||
default:
|
||||
request_handler_endpoint_drivers(req, reqdata);
|
||||
request_handler_endpoint_drivers(req, reqdata, reqdata_size);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void request_handler_endpoint(struct usb_ctrlrequest* req, void* reqdata)
|
||||
static void request_handler_endpoint(struct usb_ctrlrequest* req, uint8_t* reqdata, size_t reqdata_size)
|
||||
{
|
||||
switch(req->bRequestType & USB_TYPE_MASK) {
|
||||
case USB_TYPE_STANDARD:
|
||||
request_handler_endpoint_standard(req, reqdata);
|
||||
request_handler_endpoint_standard(req, reqdata, reqdata_size);
|
||||
break;
|
||||
case USB_TYPE_CLASS:
|
||||
request_handler_endpoint_drivers(req, reqdata);
|
||||
request_handler_endpoint_drivers(req, reqdata, reqdata_size);
|
||||
break;
|
||||
case USB_TYPE_VENDOR:
|
||||
default:
|
||||
logf("bad req:desc %d", req->bRequest);
|
||||
usb_drv_control_response(USB_CONTROL_STALL, NULL, 0);
|
||||
usb_core_control_response(USB_CONTROL_STALL, NULL, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Handling USB requests starts here */
|
||||
static void usb_core_control_request_handler(struct usb_ctrlrequest* req, void* reqdata)
|
||||
static void usb_core_control_request_handler(struct usb_ctrlrequest* req, uint8_t* reqdata, size_t reqdata_size)
|
||||
{
|
||||
#ifdef HAVE_USB_CHARGING_ENABLE
|
||||
timeout_cancel(&usb_no_host_timeout);
|
||||
|
|
@ -1105,17 +1152,17 @@ static void usb_core_control_request_handler(struct usb_ctrlrequest* req, void*
|
|||
|
||||
switch(req->bRequestType & USB_RECIP_MASK) {
|
||||
case USB_RECIP_DEVICE:
|
||||
request_handler_device(req, reqdata);
|
||||
request_handler_device(req, reqdata, reqdata_size);
|
||||
break;
|
||||
case USB_RECIP_INTERFACE:
|
||||
request_handler_interface(req, reqdata);
|
||||
request_handler_interface(req, reqdata, reqdata_size);
|
||||
break;
|
||||
case USB_RECIP_ENDPOINT:
|
||||
request_handler_endpoint(req, reqdata);
|
||||
request_handler_endpoint(req, reqdata, reqdata_size);
|
||||
break;
|
||||
default:
|
||||
logf("unsupported recipient");
|
||||
usb_drv_control_response(USB_CONTROL_STALL, NULL, 0);
|
||||
usb_core_control_response(USB_CONTROL_STALL, NULL, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -1123,10 +1170,9 @@ static void usb_core_control_request_handler(struct usb_ctrlrequest* req, void*
|
|||
static void do_bus_reset(void) {
|
||||
usb_address = 0;
|
||||
usb_state = DEFAULT;
|
||||
#ifdef USB_LEGACY_CONTROL_API
|
||||
num_active_requests = 0;
|
||||
#endif
|
||||
bus_reset_pending = false;
|
||||
set_ep0_state(EP0_READY);
|
||||
have_pending_request = false;
|
||||
}
|
||||
|
||||
/* called by usb_drv_int() */
|
||||
|
|
@ -1145,9 +1191,51 @@ void usb_core_bus_reset(void)
|
|||
}
|
||||
}
|
||||
|
||||
static void signal_xfer_complete(int ep, struct usb_ctrlrequest* req, int status, int length) {
|
||||
struct usb_transfer_completion_event_data* completion_event =
|
||||
&ep_data[EP_NUM(ep)].completion_event[EP_DIR(ep)];
|
||||
|
||||
completion_event->req = req;
|
||||
completion_event->ep = ep;
|
||||
completion_event->status = status;
|
||||
completion_event->length = length;
|
||||
usb_signal_transfer_completion(completion_event);
|
||||
}
|
||||
|
||||
static void process_setup_request(struct usb_ctrlrequest* req) {
|
||||
set_ep0_state(req->bRequestType & USB_DIR_IN ? EP0_HANDLING_TX_CONTROL : EP0_HANDLING_RX_CONTROL);
|
||||
|
||||
if(ep0_state == EP0_HANDLING_TX_CONTROL || req->wLength == 0) {
|
||||
signal_xfer_complete(EP_CONTROL | USB_DIR_IN, req, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
/* start control out data phase without usb thread interaction */
|
||||
if(req->wLength > sizeof(usb_control_data)) {
|
||||
logf("usb_core: control write too large %u > %u", req->wLength, sizeof(usb_control_data));
|
||||
usb_drv_stall(EP_CONTROL, true, false);
|
||||
return;
|
||||
}
|
||||
set_ep0_state(EP0_EXPECT_RX_DATA_COMP);
|
||||
usb_drv_recv_nonblocking(EP_CONTROL, usb_control_data, req->wLength);
|
||||
return;
|
||||
}
|
||||
|
||||
static bool check_for_new_setup(void) {
|
||||
int oldlevel = disable_irq_save();
|
||||
if(!have_pending_request) {
|
||||
restore_irq(oldlevel);
|
||||
return false;
|
||||
}
|
||||
have_pending_request = false;
|
||||
handling_request = pending_request;
|
||||
process_setup_request(&handling_request);
|
||||
restore_irq(oldlevel);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* called by usb_drv_transfer_completed() */
|
||||
void usb_core_transfer_complete(int endpoint, int dir, int status, int length)
|
||||
{
|
||||
void usb_core_transfer_complete(int endpoint, int dir, int status, int length) {
|
||||
#ifdef USB_BATCH_NON_NATIVE
|
||||
/* batch api */
|
||||
if(batch_ep != 0 && (endpoint | dir) == batch_ep) {
|
||||
|
|
@ -1158,37 +1246,52 @@ void usb_core_transfer_complete(int endpoint, int dir, int status, int length)
|
|||
|
||||
/* Fast notification */
|
||||
fast_completion_handler_t handler = ep_data[endpoint].fast_completion_handler[EP_DIR(dir)];
|
||||
if(handler != NULL && handler(endpoint, dir, status, length))
|
||||
if(handler != NULL && handler(endpoint, dir, status, length)) {
|
||||
return; /* do not dispatch to the queue if handled */
|
||||
|
||||
void* data0 = NULL;
|
||||
void* data1 = NULL;
|
||||
|
||||
#ifdef USB_LEGACY_CONTROL_API
|
||||
if(endpoint == EP_CONTROL) {
|
||||
bool cwdd = control_write_data_done;
|
||||
struct usb_ctrlrequest* req = active_request;
|
||||
|
||||
if(dir == USB_DIR_OUT && req && cwdd) {
|
||||
data0 = req;
|
||||
data1 = control_write_data;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
struct usb_transfer_completion_event_data* completion_event =
|
||||
&ep_data[endpoint].completion_event[EP_DIR(dir)];
|
||||
/* Non-control packet handling */
|
||||
if(endpoint != EP_CONTROL) {
|
||||
signal_xfer_complete(endpoint | dir, NULL, status, length);
|
||||
return;
|
||||
}
|
||||
|
||||
completion_event->endpoint = endpoint;
|
||||
completion_event->dir = dir;
|
||||
completion_event->data[0] = data0;
|
||||
completion_event->data[1] = data1;
|
||||
completion_event->status = status;
|
||||
completion_event->length = length;
|
||||
/* Control packet handling */
|
||||
switch(dir | ep0_state) {
|
||||
/* EXPECT_TX_DATA_STATUS_COMP -(status comp)-> EXPECT_TX_DATA_COMP -(data comp)-> READY
|
||||
* -(data comp)-> EXPECT_TX_STATUS_COMP -(status comp)-> READY */
|
||||
case USB_DIR_OUT | EP0_EXPECT_TX_DATA_STATUS_COMP:
|
||||
logf("usb_core: control-in done success=%d", status == 0 && length == 0);
|
||||
set_ep0_state(EP0_EXPECT_TX_DATA_COMP);
|
||||
break;
|
||||
case USB_DIR_IN | EP0_EXPECT_TX_DATA_STATUS_COMP:
|
||||
set_ep0_state(EP0_EXPECT_TX_STATUS_COMP);
|
||||
break;
|
||||
case USB_DIR_OUT | EP0_EXPECT_TX_STATUS_COMP:
|
||||
logf("usb_core: control-in done success=%d", status == 0 && length == 0);
|
||||
set_ep0_state(EP0_READY);
|
||||
break;
|
||||
case USB_DIR_IN | EP0_EXPECT_TX_DATA_COMP:
|
||||
set_ep0_state(EP0_READY);
|
||||
break;
|
||||
/* EXPECT_RX_DATA_COMP -(data comp)-> HANDLING_RX_CONTROL [-(send status)-> EXPECT_RX_STATUS_COMP] -(status comp)-> READY
|
||||
* ^ done in control_response() */
|
||||
case USB_DIR_OUT | EP0_EXPECT_RX_DATA_COMP:
|
||||
set_ep0_state(EP0_HANDLING_RX_CONTROL);
|
||||
signal_xfer_complete(EP_CONTROL | USB_DIR_OUT, &handling_request, status, length);
|
||||
break;
|
||||
case USB_DIR_IN | EP0_EXPECT_RX_STATUS_COMP:
|
||||
logf("usb_core: control-out done success=%d", status == 0 && length == 0);
|
||||
set_ep0_state(EP0_READY);
|
||||
break;
|
||||
default:
|
||||
panicf("unhandled endpoint xfer completion ep0_state=%d dir=%d", ep0_state, dir);
|
||||
break;
|
||||
}
|
||||
|
||||
usb_signal_transfer_completion(completion_event);
|
||||
if(ep0_state == EP0_READY) {
|
||||
check_for_new_setup();
|
||||
}
|
||||
}
|
||||
|
||||
void usb_core_handle_notify(long id, intptr_t data)
|
||||
|
|
@ -1227,153 +1330,68 @@ void usb_core_handle_notify(long id, intptr_t data)
|
|||
}
|
||||
}
|
||||
|
||||
void usb_core_control_request(struct usb_ctrlrequest* req, void* reqdata)
|
||||
{
|
||||
struct usb_transfer_completion_event_data* completion_event =
|
||||
&ep_data[EP_CONTROL].completion_event[EP_DIR(USB_DIR_IN)];
|
||||
|
||||
completion_event->endpoint = EP_CONTROL;
|
||||
completion_event->dir = 0;
|
||||
completion_event->data[0] = (void*)req;
|
||||
completion_event->data[1] = reqdata;
|
||||
completion_event->status = 0;
|
||||
completion_event->length = 0;
|
||||
logf("ctrl received %ld, req=0x%x", current_tick, req->bRequest);
|
||||
usb_signal_transfer_completion(completion_event);
|
||||
}
|
||||
|
||||
void usb_core_control_complete(int status)
|
||||
{
|
||||
/* We currently don't use this, it's here to make the API look good ;)
|
||||
* It makes sense to #define it away on normal builds.
|
||||
*/
|
||||
(void)status;
|
||||
logf("ctrl complete %ld, %d", current_tick, status);
|
||||
}
|
||||
|
||||
#ifdef USB_LEGACY_CONTROL_API
|
||||
/* Only needed if the driver does not support the new API yet */
|
||||
void usb_core_legacy_control_request(struct usb_ctrlrequest* req)
|
||||
{
|
||||
/* Only submit non-overlapping requests */
|
||||
if (num_active_requests++ == 0)
|
||||
{
|
||||
buffered_request = *req;
|
||||
active_request = &buffered_request;
|
||||
control_write_data = NULL;
|
||||
control_write_data_done = false;
|
||||
|
||||
usb_core_control_request(req, NULL);
|
||||
void usb_core_setup_received(struct usb_ctrlrequest* req) {
|
||||
if(bus_reset_pending) {
|
||||
logf("usb_core: bus resetting tick=%lu", current_tick);
|
||||
return;
|
||||
}
|
||||
if(ep0_state != EP0_READY) {
|
||||
logf("usb_core: control pending tick=%lu", current_tick);
|
||||
pending_request = *req;
|
||||
have_pending_request = true;
|
||||
return;
|
||||
}
|
||||
handling_request = *req;
|
||||
process_setup_request(&handling_request);
|
||||
}
|
||||
|
||||
void usb_drv_control_response(enum usb_control_response resp,
|
||||
void* data, int length)
|
||||
{
|
||||
struct usb_ctrlrequest* req = active_request;
|
||||
unsigned int num_active = num_active_requests--;
|
||||
void usb_core_control_response(enum usb_control_response response, const void* data, size_t size) {
|
||||
logf("usb_core: response ack=%d size=%u ep0_state=%d tick=%lu", response, size, ep0_state, current_tick);
|
||||
|
||||
/*
|
||||
* There should have been a prior request submission, at least.
|
||||
* FIXME: It seems the iPod video can get here and ignoring it
|
||||
* allows the connection to succeed??
|
||||
*/
|
||||
if (num_active == 0)
|
||||
{
|
||||
//panicf("null ctrl req");
|
||||
if((ep0_state == EP0_HANDLING_TX_CONTROL || ep0_state == EP0_HANDLING_RX_CONTROL) && check_for_new_setup()) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* This can happen because an active request was already pending when
|
||||
* the driver submitted a new one in usb_core_legacy_control_request().
|
||||
* This could mean two things: (a) a driver bug; or (b) the host sent
|
||||
* another request because we were too slow in handling an earlier one.
|
||||
*
|
||||
* The USB spec requires we respond to the latest request and drop any
|
||||
* earlier ones, but that's not easy to do with the current design of
|
||||
* the USB stack. Thus, the host will be expecting a response for the
|
||||
* latest request, but this response is for the _earliest_ request.
|
||||
*
|
||||
* Play it safe and return a STALL. At this point we've recovered from
|
||||
* the error on our end and will be ready to handle the next request.
|
||||
*/
|
||||
if (num_active > 1)
|
||||
{
|
||||
active_request = NULL;
|
||||
num_active_requests = 0;
|
||||
if(response == USB_CONTROL_STALL) {
|
||||
set_ep0_state(EP0_READY);
|
||||
usb_drv_stall(EP_CONTROL, true, true);
|
||||
return;
|
||||
}
|
||||
|
||||
if(req->wLength == 0)
|
||||
{
|
||||
active_request = NULL;
|
||||
|
||||
/* No-data request */
|
||||
if(resp == USB_CONTROL_ACK)
|
||||
usb_drv_send(EP_CONTROL, data, length);
|
||||
else if(resp == USB_CONTROL_STALL)
|
||||
usb_drv_stall(EP_CONTROL, true, true);
|
||||
else
|
||||
panicf("RECEIVE on non-data req");
|
||||
}
|
||||
else if(req->bRequestType & USB_DIR_IN)
|
||||
{
|
||||
/* Control read request */
|
||||
if(resp == USB_CONTROL_ACK)
|
||||
{
|
||||
active_request = NULL;
|
||||
switch(ep0_state) {
|
||||
case EP0_HANDLING_TX_CONTROL:
|
||||
if(size == 0) {
|
||||
/* non-data control-in */
|
||||
set_ep0_state(EP0_EXPECT_TX_STATUS_COMP);
|
||||
usb_drv_recv_nonblocking(EP_CONTROL, NULL, 0);
|
||||
usb_drv_send(EP_CONTROL, data, length);
|
||||
} else {
|
||||
/* control-in data phase */
|
||||
set_ep0_state(EP0_EXPECT_TX_DATA_STATUS_COMP);
|
||||
/* prepare for a status packet before sending data.
|
||||
* note that it is driver and host-dependent that which completion
|
||||
* of the following two commands is notified first. */
|
||||
usb_drv_recv_nonblocking(EP_CONTROL, NULL, 0);
|
||||
/* send data packets */
|
||||
usb_drv_send_nonblocking(EP_CONTROL, (void*)data, size);
|
||||
}
|
||||
else if(resp == USB_CONTROL_STALL)
|
||||
{
|
||||
active_request = NULL;
|
||||
usb_drv_stall(EP_CONTROL, true, true);
|
||||
break;
|
||||
case EP0_HANDLING_RX_CONTROL:
|
||||
if(size == 0) {
|
||||
/* non-data control-out */
|
||||
set_ep0_state(EP0_EXPECT_RX_STATUS_COMP);
|
||||
/* send status packet*/
|
||||
usb_drv_send_nonblocking(EP_CONTROL, NULL, 0);
|
||||
} else {
|
||||
/* control-out data phase */
|
||||
/* should not happen, we've received it internally */
|
||||
logf("usb_core: receiving control data by class drivers is not allowed");
|
||||
}
|
||||
else
|
||||
{
|
||||
panicf("RECEIVE on ctrl read req");
|
||||
}
|
||||
}
|
||||
else if(!control_write_data_done)
|
||||
{
|
||||
/* Control write request, data phase */
|
||||
if(resp == USB_CONTROL_RECEIVE)
|
||||
{
|
||||
control_write_data = data;
|
||||
control_write_data_done = true;
|
||||
usb_drv_recv_nonblocking(EP_CONTROL, data, length);
|
||||
}
|
||||
else if(resp == USB_CONTROL_STALL)
|
||||
{
|
||||
/* We should stall the OUT endpoint here, but the old code did
|
||||
* not do so and some drivers may not handle it correctly. */
|
||||
active_request = NULL;
|
||||
usb_drv_stall(EP_CONTROL, true, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
panicf("ACK on ctrl write data");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
active_request = NULL;
|
||||
control_write_data = NULL;
|
||||
control_write_data_done = false;
|
||||
|
||||
/* Control write request, status phase */
|
||||
if(resp == USB_CONTROL_ACK)
|
||||
usb_drv_send(EP_CONTROL, NULL, 0);
|
||||
else if(resp == USB_CONTROL_STALL)
|
||||
usb_drv_stall(EP_CONTROL, true, true);
|
||||
else
|
||||
panicf("RECEIVE on ctrl write status");
|
||||
break;
|
||||
default:
|
||||
panicf("usb_core: invalid control response ep_state=%d", ep0_state);
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void usb_core_notify_set_address(uint8_t addr)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -594,9 +594,11 @@ static void usb_hid_transfer_complete(int ep, int dir, int status, int length)
|
|||
* In order to allow sending info to the DAP, the Set Report mechanism can be
|
||||
* used by defining vendor specific output reports and send them from the host
|
||||
* to the DAP using the host's custom driver */
|
||||
static int usb_hid_set_report(struct usb_ctrlrequest *req, void *reqdata)
|
||||
static int usb_hid_set_report(struct usb_ctrlrequest *req, uint8_t* reqdata, size_t reqdata_size)
|
||||
{
|
||||
static unsigned char buf[64] USB_DEVBSS_ATTR __attribute__((aligned(32)));
|
||||
(void)reqdata;
|
||||
(void)reqdata_size;
|
||||
|
||||
int length;
|
||||
|
||||
if ((req->wValue >> 8) != USB_HID_REPORT_TYPE_OUTPUT)
|
||||
|
|
@ -621,28 +623,22 @@ static int usb_hid_set_report(struct usb_ctrlrequest *req, void *reqdata)
|
|||
return 4;
|
||||
}
|
||||
|
||||
if(!reqdata) {
|
||||
memset(buf, 0, length);
|
||||
usb_drv_control_response(USB_CONTROL_RECEIVE, buf, length);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef LOGF_ENABLE
|
||||
if (buf[1] & 0x01)
|
||||
if (reqdata[1] & 0x01)
|
||||
logf("Num Lock enabled");
|
||||
if (buf[1] & 0x02)
|
||||
if (reqdata[1] & 0x02)
|
||||
logf("Caps Lock enabled");
|
||||
if (buf[1] & 0x04)
|
||||
if (reqdata[1] & 0x04)
|
||||
logf("Scroll Lock enabled");
|
||||
if (buf[1] & 0x08)
|
||||
if (reqdata[1] & 0x08)
|
||||
logf("Compose enabled");
|
||||
if (buf[1] & 0x10)
|
||||
if (reqdata[1] & 0x10)
|
||||
logf("Kana enabled");
|
||||
#endif
|
||||
|
||||
/* Defining other LEDs and setting them from the USB host (OS) can be used
|
||||
* to send messages to the DAP */
|
||||
usb_drv_control_response(USB_CONTROL_ACK, NULL, 0);
|
||||
usb_core_control_response(USB_CONTROL_ACK, NULL, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -674,15 +670,14 @@ static int usb_hid_get_report(struct usb_ctrlrequest *req, unsigned char* dest)
|
|||
|
||||
dest[0] = 0;
|
||||
dest[1] = battery_level();
|
||||
usb_drv_control_response(USB_CONTROL_ACK, dest, 2);
|
||||
usb_core_control_response(USB_CONTROL_ACK, dest, 2);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* called by usb_core_control_request() */
|
||||
static bool usb_hid_control_request(struct usb_ctrlrequest *req, void *reqdata, unsigned char *dest)
|
||||
static bool usb_hid_control_request(struct usb_ctrlrequest *req, uint8_t* reqdata, size_t reqdata_size)
|
||||
{
|
||||
(void)reqdata;
|
||||
|
||||
unsigned char* dest = reqdata;
|
||||
unsigned char *orig_dest = dest;
|
||||
switch (req->bRequestType & USB_TYPE_MASK)
|
||||
{
|
||||
|
|
@ -706,7 +701,7 @@ static bool usb_hid_control_request(struct usb_ctrlrequest *req, void *reqdata,
|
|||
|
||||
if (dest != orig_dest)
|
||||
{
|
||||
usb_drv_control_response(USB_CONTROL_ACK, orig_dest, dest - orig_dest);
|
||||
usb_core_control_response(USB_CONTROL_ACK, orig_dest, dest - orig_dest);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
|
@ -723,13 +718,13 @@ static bool usb_hid_control_request(struct usb_ctrlrequest *req, void *reqdata,
|
|||
switch (req->bRequest)
|
||||
{
|
||||
case USB_HID_SET_REPORT:
|
||||
rc = usb_hid_set_report(req, reqdata);
|
||||
rc = usb_hid_set_report(req, reqdata, reqdata_size);
|
||||
break;
|
||||
case USB_HID_GET_REPORT:
|
||||
rc = usb_hid_get_report(req, dest);
|
||||
break;
|
||||
case USB_HID_SET_IDLE:
|
||||
usb_drv_control_response(USB_CONTROL_ACK, NULL, 0);
|
||||
usb_core_control_response(USB_CONTROL_ACK, NULL, 0);
|
||||
return true;
|
||||
default:
|
||||
/* all other requests are errors */
|
||||
|
|
|
|||
|
|
@ -481,39 +481,19 @@ static bool usb_iap_fast_transfer_complete(int ep, int dir, int status, int leng
|
|||
return (ep | dir) == AS_EP_IN;
|
||||
}
|
||||
|
||||
static unsigned char ctrl_buf[256] USB_DEVBSS_ATTR;
|
||||
|
||||
static void respond_zero(struct usb_ctrlrequest* req) {
|
||||
if(req->wLength > sizeof(ctrl_buf)) {
|
||||
ERROR("required data too long %u > %u", req->wLength, sizeof(ctrl_buf));
|
||||
usb_drv_control_response(USB_CONTROL_STALL, NULL, 0);
|
||||
static void respond_zero(struct usb_ctrlrequest* req, uint8_t* reqdata, size_t reqdata_size) {
|
||||
if(req->wLength > reqdata_size) {
|
||||
ERROR("required data too long %u > %u", req->wLength, reqdata_size);
|
||||
usb_core_control_response(USB_CONTROL_STALL, NULL, 0);
|
||||
} else {
|
||||
memset(ctrl_buf, 0, req->wLength);
|
||||
usb_drv_control_response(USB_CONTROL_ACK, ctrl_buf, req->wLength);
|
||||
memset(reqdata, 0, req->wLength);
|
||||
usb_core_control_response(USB_CONTROL_ACK, reqdata, req->wLength);
|
||||
}
|
||||
}
|
||||
|
||||
/* returns true when ctrl_buf has received data */
|
||||
static bool receive_data(struct usb_ctrlrequest* req, void* reqdata) {
|
||||
if(reqdata == NULL) {
|
||||
/* setup */
|
||||
if(req->wLength > sizeof(ctrl_buf)) {
|
||||
ERROR("parameter too long");
|
||||
usb_drv_control_response(USB_CONTROL_STALL, NULL, 0);
|
||||
} else {
|
||||
usb_drv_control_response(USB_CONTROL_RECEIVE, ctrl_buf, req->wLength);
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
/* data */
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static bool control_request_if_std(struct usb_ctrlrequest* req, void* reqdata, unsigned char* dest) {
|
||||
(void)reqdata;
|
||||
|
||||
unsigned char* const orig_dest = dest;
|
||||
static bool control_request_if_std(struct usb_ctrlrequest* req, uint8_t* reqdata, size_t reqdata_size) {
|
||||
const void* src = NULL;
|
||||
size_t size;
|
||||
switch(req->bRequest) {
|
||||
case USB_REQ_GET_DESCRIPTOR: {
|
||||
const uint8_t desc_type = req->wValue >> 8;
|
||||
|
|
@ -522,18 +502,28 @@ static bool control_request_if_std(struct usb_ctrlrequest* req, void* reqdata, u
|
|||
(void)desc_index;
|
||||
switch(desc_type) {
|
||||
case USB_DT_HID:
|
||||
PACK_DATA(&dest, ipod_hid_hid_desc);
|
||||
src = &ipod_hid_hid_desc;
|
||||
size = sizeof(ipod_hid_hid_desc);
|
||||
break;
|
||||
case USB_DT_REPORT:
|
||||
if(usb_drv_port_speed()) {
|
||||
PACK_DATA(&dest, ipod_hid_report_hs);
|
||||
src = &ipod_hid_report_hs;
|
||||
size = sizeof(ipod_hid_report_hs);
|
||||
} else {
|
||||
PACK_DATA(&dest, ipod_hid_report_fs);
|
||||
src = &ipod_hid_report_fs;
|
||||
size = sizeof(ipod_hid_report_fs);
|
||||
}
|
||||
break;
|
||||
}
|
||||
if(dest != orig_dest) {
|
||||
usb_drv_control_response(USB_CONTROL_ACK, orig_dest, MIN(dest - orig_dest, req->wLength));
|
||||
if(src != NULL) {
|
||||
if(size > reqdata_size) {
|
||||
LOG("descriptor size too large");
|
||||
usb_core_control_response(USB_CONTROL_STALL, NULL, 0);
|
||||
} else {
|
||||
size = MIN(size, req->wLength);
|
||||
memcpy(reqdata, src, size);
|
||||
usb_core_control_response(USB_CONTROL_ACK, reqdata, size);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} break;
|
||||
|
|
@ -541,19 +531,14 @@ static bool control_request_if_std(struct usb_ctrlrequest* req, void* reqdata, u
|
|||
return false;
|
||||
}
|
||||
|
||||
static bool control_request_if_class(struct usb_ctrlrequest* req, void* reqdata, unsigned char* dest) {
|
||||
(void)dest;
|
||||
|
||||
static bool control_request_if_class(struct usb_ctrlrequest* req, uint8_t* reqdata, size_t reqdata_size) {
|
||||
const uint8_t recip_interface = req->wIndex & 0xff;
|
||||
if(recip_interface == hid.interface) {
|
||||
switch(req->bRequest) {
|
||||
case USB_HID_GET_REPORT:
|
||||
respond_zero(req);
|
||||
respond_zero(req, reqdata, reqdata_size);
|
||||
return true;
|
||||
case USB_HID_SET_REPORT: {
|
||||
if(!receive_data(req, reqdata)) {
|
||||
return true;
|
||||
}
|
||||
#if DEBUG_DUMP_RX == 1
|
||||
logf("==== acc: %u bytes ====", req->wLength);
|
||||
iap_platform_dump_hex(reqdata, req->wLength);
|
||||
|
|
@ -564,20 +549,18 @@ static bool control_request_if_class(struct usb_ctrlrequest* req, void* reqdata,
|
|||
_iap_release_ctx();
|
||||
|
||||
check_act(ret, return false);
|
||||
usb_drv_control_response(USB_CONTROL_ACK, NULL, 0);
|
||||
usb_core_control_response(USB_CONTROL_ACK, NULL, 0);
|
||||
return true;
|
||||
}
|
||||
case USB_HID_SET_IDLE:
|
||||
usb_drv_control_response(USB_CONTROL_ACK, NULL, 0);
|
||||
usb_core_control_response(USB_CONTROL_ACK, NULL, 0);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool control_request_if_endpoint(struct usb_ctrlrequest* req, void* reqdata, unsigned char* dest) {
|
||||
(void)dest;
|
||||
|
||||
static bool control_request_if_endpoint(struct usb_ctrlrequest* req, uint8_t* reqdata, size_t reqdata_size) {
|
||||
LOG("ctrl to endpoint %x (stream=%x, hid=%x)", req->wIndex, AS_EP_IN, HID_EP_IN);
|
||||
if(req->wIndex == AS_EP_IN) {
|
||||
const uint8_t recip_entity = req->wIndex >> 8;
|
||||
|
|
@ -585,45 +568,42 @@ static bool control_request_if_endpoint(struct usb_ctrlrequest* req, void* reqda
|
|||
(void)recip_entity;
|
||||
switch(req->bRequest) {
|
||||
case USB_AC_SET_CUR:
|
||||
if(!receive_data(req, reqdata)) {
|
||||
return true;
|
||||
}
|
||||
LOG("audio ctrl to stream endpoint entity=0x%02X request=0x%02X length=%u", recip_entity, req->bRequest, req->wLength);
|
||||
switch(control_selector) {
|
||||
case USB_AS_EP_CS_SAMPLING_FREQ_CTL:
|
||||
check_act(req->wLength == 3, goto stall);
|
||||
stream.sample_rate = ctrl_buf[0] | (ctrl_buf[1] << 8) | (ctrl_buf[2] << 16);
|
||||
stream.sample_rate = reqdata[0] | (reqdata[1] << 8) | (reqdata[2] << 16);
|
||||
LOG("audio stream sampling rate %lu", stream.sample_rate);
|
||||
check_act(iap_audio_set_sampr(stream.sample_rate), goto stall);
|
||||
break;
|
||||
}
|
||||
usb_drv_control_response(USB_CONTROL_ACK, NULL, 0);
|
||||
usb_core_control_response(USB_CONTROL_ACK, NULL, 0);
|
||||
return true;
|
||||
case USB_AC_GET_CUR:
|
||||
switch(control_selector) {
|
||||
case USB_AS_EP_CS_SAMPLING_FREQ_CTL:
|
||||
check_act(req->wLength == 3, goto stall);
|
||||
ctrl_buf[2] = (stream.sample_rate >> 16) & 0xff;
|
||||
ctrl_buf[1] = (stream.sample_rate >> 8) & 0xff;
|
||||
ctrl_buf[0] = (stream.sample_rate & 0xff);
|
||||
usb_drv_control_response(USB_CONTROL_ACK, ctrl_buf, req->wLength);
|
||||
reqdata[2] = (stream.sample_rate >> 16) & 0xff;
|
||||
reqdata[1] = (stream.sample_rate >> 8) & 0xff;
|
||||
reqdata[0] = (stream.sample_rate & 0xff);
|
||||
usb_core_control_response(USB_CONTROL_ACK, reqdata, 3);
|
||||
return true;
|
||||
}
|
||||
/* fallthrough */
|
||||
case USB_AC_GET_MIN:
|
||||
case USB_AC_GET_MAX:
|
||||
case USB_AC_GET_RES:
|
||||
respond_zero(req);
|
||||
respond_zero(req, reqdata, reqdata_size);
|
||||
return true;
|
||||
stall:
|
||||
usb_drv_control_response(USB_CONTROL_STALL, NULL, 0);
|
||||
usb_core_control_response(USB_CONTROL_STALL, NULL, 0);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool usb_iap_control_request(struct usb_ctrlrequest* req, void* reqdata, unsigned char* dest) {
|
||||
static bool usb_iap_control_request(struct usb_ctrlrequest* req, uint8_t* reqdata, size_t reqdata_size) {
|
||||
const uint8_t req_recipient = req->bRequestType & USB_RECIP_MASK;
|
||||
const uint8_t req_type = req->bRequestType & USB_TYPE_MASK;
|
||||
#if 0
|
||||
|
|
@ -631,11 +611,11 @@ static bool usb_iap_control_request(struct usb_ctrlrequest* req, void* reqdata,
|
|||
LOG("recip=%x type=%x", req_recipient, req_type);
|
||||
#endif
|
||||
if(req_recipient == USB_RECIP_INTERFACE && req_type == USB_TYPE_STANDARD) {
|
||||
return control_request_if_std(req, reqdata, dest);
|
||||
return control_request_if_std(req, reqdata, reqdata_size);
|
||||
} else if(req_recipient == USB_RECIP_INTERFACE && req_type == USB_TYPE_CLASS) {
|
||||
return control_request_if_class(req, reqdata, dest);
|
||||
return control_request_if_class(req, reqdata, reqdata_size);
|
||||
} else if(req_recipient == USB_RECIP_ENDPOINT && req_type == USB_TYPE_CLASS) {
|
||||
return control_request_if_endpoint(req, reqdata, dest);
|
||||
return control_request_if_endpoint(req, reqdata, reqdata_size);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -180,7 +180,7 @@ union line_coding_buffer
|
|||
unsigned char raw[64];
|
||||
};
|
||||
|
||||
static union line_coding_buffer line_coding USB_DEVBSS_ATTR;
|
||||
static union line_coding_buffer line_coding;
|
||||
|
||||
/* send_buffer: local ring buffer.
|
||||
* transit_buffer: used to store aligned data that will be sent by the USB
|
||||
|
|
@ -270,12 +270,11 @@ static int usb_serial_get_config_descriptor(unsigned char *dest, int max_packet_
|
|||
}
|
||||
|
||||
/* called by usb_core_control_request() */
|
||||
static bool usb_serial_control_request(struct usb_ctrlrequest* req, void* reqdata, unsigned char* dest)
|
||||
static bool usb_serial_control_request(struct usb_ctrlrequest* req, uint8_t* reqdata, size_t reqdata_size)
|
||||
{
|
||||
bool handled = false;
|
||||
(void)reqdata_size; /* should check this? */
|
||||
|
||||
(void)dest;
|
||||
(void)reqdata;
|
||||
bool handled = false;
|
||||
|
||||
if (req->wIndex != control_interface)
|
||||
{
|
||||
|
|
@ -289,16 +288,7 @@ static bool usb_serial_control_request(struct usb_ctrlrequest* req, void* reqdat
|
|||
if (req->wLength == sizeof(struct cdc_line_coding))
|
||||
{
|
||||
/* Receive line coding into local copy */
|
||||
if (!reqdata)
|
||||
{
|
||||
usb_drv_control_response(USB_CONTROL_RECEIVE, line_coding.raw,
|
||||
sizeof(struct cdc_line_coding));
|
||||
}
|
||||
else
|
||||
{
|
||||
usb_drv_control_response(USB_CONTROL_ACK, NULL, 0);
|
||||
}
|
||||
|
||||
memcpy(line_coding.raw, reqdata, sizeof(struct cdc_line_coding));
|
||||
handled = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -307,7 +297,7 @@ static bool usb_serial_control_request(struct usb_ctrlrequest* req, void* reqdat
|
|||
if (req->wLength == 0)
|
||||
{
|
||||
/* wValue holds Control Signal Bitmap that is simply ignored here */
|
||||
usb_drv_control_response(USB_CONTROL_ACK, NULL, 0);
|
||||
usb_core_control_response(USB_CONTROL_ACK, NULL, 0);
|
||||
handled = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -319,7 +309,7 @@ static bool usb_serial_control_request(struct usb_ctrlrequest* req, void* reqdat
|
|||
if (req->wLength == sizeof(struct cdc_line_coding))
|
||||
{
|
||||
/* Send back line coding so host is happy */
|
||||
usb_drv_control_response(USB_CONTROL_ACK, line_coding.raw,
|
||||
usb_core_control_response(USB_CONTROL_ACK, line_coding.raw,
|
||||
sizeof(struct cdc_line_coding));
|
||||
handled = true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -695,12 +695,12 @@ static void usb_storage_send_smart(uint8_t cmd)
|
|||
#endif /* STORAGE_ATA */
|
||||
|
||||
/* called by usb_core_control_request() */
|
||||
static bool usb_storage_control_request(struct usb_ctrlrequest* req, void* reqdata, unsigned char* dest)
|
||||
static bool usb_storage_control_request(struct usb_ctrlrequest* req, uint8_t* reqdata, size_t reqdata_size)
|
||||
{
|
||||
bool handled = false;
|
||||
|
||||
(void)dest;
|
||||
(void)reqdata;
|
||||
(void)reqdata_size;
|
||||
|
||||
bool handled = false;
|
||||
|
||||
switch (req->bRequest) {
|
||||
case USB_BULK_GET_MAX_LUN: {
|
||||
|
|
@ -709,7 +709,7 @@ static bool usb_storage_control_request(struct usb_ctrlrequest* req, void* reqda
|
|||
if(skip_first) (*tb.max_lun) --;
|
||||
#endif
|
||||
logf("ums: getmaxlun");
|
||||
usb_drv_control_response(USB_CONTROL_ACK, tb.max_lun, 1);
|
||||
usb_core_control_response(USB_CONTROL_ACK, tb.max_lun, 1);
|
||||
handled = true;
|
||||
break;
|
||||
}
|
||||
|
|
@ -724,7 +724,7 @@ static bool usb_storage_control_request(struct usb_ctrlrequest* req, void* reqda
|
|||
usb_drv_reset_endpoint(EP_IN, false);
|
||||
usb_drv_reset_endpoint(EP_OUT, true);
|
||||
#endif
|
||||
usb_drv_control_response(USB_CONTROL_ACK, NULL, 0);
|
||||
usb_core_control_response(USB_CONTROL_ACK, NULL, 0);
|
||||
handled = true;
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue