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
This commit is contained in:
Aidan MacDonald 2021-10-18 21:19:19 +01:00
parent fd50baa23f
commit 9eaa14dc02

View file

@ -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