summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAidan MacDonald <amachronic@protonmail.com>2021-10-18 21:19:19 +0100
committerAidan MacDonald <amachronic@protonmail.com>2021-11-27 09:29:44 -0500
commit9eaa14dc029587bf35ef1c8c9624192cf20ecbef (patch)
tree004dad448936acc2b5ae0c62140b131a80b82f3e
parentfd50baa23ff5e17fad7d549ac226e8cf8dc67b86 (diff)
downloadrockbox-9eaa14dc02.tar.gz
rockbox-9eaa14dc02.zip
usb-designware: fix setup packet error handling
This handles the case where the host sends a new setup packet in the middle of an ongoing control transfer. Change-Id: I1d2e02467e9b813c9827cc0699cb87a500515e31
-rw-r--r--firmware/drivers/usb-designware.c34
1 files changed, 26 insertions, 8 deletions
diff --git a/firmware/drivers/usb-designware.c b/firmware/drivers/usb-designware.c
index 37e96aa557..beecb5ea7a 100644
--- a/firmware/drivers/usb-designware.c
+++ b/firmware/drivers/usb-designware.c
@@ -102,12 +102,14 @@ enum usb_dw_ep0_state
/* 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. */
+ * 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_FIRST_CNAK_STATE = EP0_DATA_IN,
+ EP0_DATA_IN,
EP0_DATA_OUT,
/* Waiting for the status phase */
@@ -856,11 +858,15 @@ static void usb_dw_control_received(struct usb_ctrlrequest* req)
logf(" bRequestType=%02x bRequest=%02x", req->bRequestType, req->bRequest);
logf(" wValue=%04x wIndex=%u wLength=%u", req->wValue, req->wIndex, req->wLength);
- /* FIXME: This will implode if we receive a setup packet while waiting
- * for a response from the USB stack to a previous packet.
- */
-
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:
@@ -894,6 +900,7 @@ static void usb_dw_control_received(struct usb_ctrlrequest* req)
}
}
+/* note: must be called with IRQs disabled */
static void usb_dw_control_response(enum usb_control_response resp,
void* data, int length)
{
@@ -931,6 +938,15 @@ static void usb_dw_control_response(enum usb_control_response resp,
}
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]);
}
@@ -1063,10 +1079,12 @@ static void usb_dw_handle_setup_received(void)
#if defined(NO_UNCACHED_ADDR) && defined(POST_DMA_FLUSH)
DISCARD_DCACHE_RANGE(ep0_buffer, 64);
#endif
- memcpy(&ep0.pending_req, ep0_buffer, sizeof(struct usb_ctrlrequest));
+ struct usb_ctrlrequest req;
+ memcpy(&req, ep0_buffer, sizeof(struct usb_ctrlrequest));
+
usb_dw_ep0_recv();
- usb_dw_control_received(&ep0.pending_req);
+ usb_dw_control_received(&req);
}
#ifdef USB_DW_SHARED_FIFO