summaryrefslogtreecommitdiffstats
path: root/firmware/usbstack/usb_core.c
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/usbstack/usb_core.c')
-rw-r--r--firmware/usbstack/usb_core.c43
1 files changed, 38 insertions, 5 deletions
diff --git a/firmware/usbstack/usb_core.c b/firmware/usbstack/usb_core.c
index 63df173033..5e50d21383 100644
--- a/firmware/usbstack/usb_core.c
+++ b/firmware/usbstack/usb_core.c
@@ -264,7 +264,9 @@ static struct usb_class_driver drivers[USB_NUM_DRIVERS] =
};
#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
@@ -1019,21 +1021,52 @@ void usb_core_control_complete(int status)
/* Only needed if the driver does not support the new API yet */
void usb_core_legacy_control_request(struct usb_ctrlrequest* req)
{
- active_request = req;
- control_write_data = NULL;
- control_write_data_done = false;
+ /* 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);
+ usb_core_control_request(req, NULL);
+ }
}
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--;
- if(!req)
+ /*
+ * There must have been a prior request submission, at least.
+ */
+ if (num_active == 0)
panicf("null ctrl req");
+ /*
+ * 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;
+ usb_drv_stall(EP_CONTROL, true, true);
+ return;
+ }
+
if(req->wLength == 0)
{
active_request = NULL;