summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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] =
264}; 264};
265 265
266#ifdef USB_LEGACY_CONTROL_API 266#ifdef USB_LEGACY_CONTROL_API
267static struct usb_ctrlrequest buffered_request;
267static struct usb_ctrlrequest* volatile active_request = NULL; 268static struct usb_ctrlrequest* volatile active_request = NULL;
269static volatile unsigned int num_active_requests = 0;
268static void* volatile control_write_data = NULL; 270static void* volatile control_write_data = NULL;
269static volatile bool control_write_data_done = false; 271static volatile bool control_write_data_done = false;
270#endif 272#endif
@@ -1019,21 +1021,52 @@ void usb_core_control_complete(int status)
1019/* Only needed if the driver does not support the new API yet */ 1021/* Only needed if the driver does not support the new API yet */
1020void usb_core_legacy_control_request(struct usb_ctrlrequest* req) 1022void usb_core_legacy_control_request(struct usb_ctrlrequest* req)
1021{ 1023{
1022 active_request = req; 1024 /* Only submit non-overlapping requests */
1023 control_write_data = NULL; 1025 if (num_active_requests++ == 0)
1024 control_write_data_done = false; 1026 {
1027 buffered_request = *req;
1028 active_request = &buffered_request;
1029 control_write_data = NULL;
1030 control_write_data_done = false;
1025 1031
1026 usb_core_control_request(req, NULL); 1032 usb_core_control_request(req, NULL);
1033 }
1027} 1034}
1028 1035
1029void usb_drv_control_response(enum usb_control_response resp, 1036void usb_drv_control_response(enum usb_control_response resp,
1030 void* data, int length) 1037 void* data, int length)
1031{ 1038{
1032 struct usb_ctrlrequest* req = active_request; 1039 struct usb_ctrlrequest* req = active_request;
1040 unsigned int num_active = num_active_requests--;
1033 1041
1034 if(!req) 1042 /*
1043 * There must have been a prior request submission, at least.
1044 */
1045 if (num_active == 0)
1035 panicf("null ctrl req"); 1046 panicf("null ctrl req");
1036 1047
1048 /*
1049 * This can happen because an active request was already pending when
1050 * the driver submitted a new one in usb_core_legacy_control_request().
1051 * This could mean two things: (a) a driver bug; or (b) the host sent
1052 * another request because we were too slow in handling an earlier one.
1053 *
1054 * The USB spec requires we respond to the latest request and drop any
1055 * earlier ones, but that's not easy to do with the current design of
1056 * the USB stack. Thus, the host will be expecting a response for the
1057 * latest request, but this response is for the _earliest_ request.
1058 *
1059 * Play it safe and return a STALL. At this point we've recovered from
1060 * the error on our end and will be ready to handle the next request.
1061 */
1062 if (num_active > 1)
1063 {
1064 active_request = NULL;
1065 num_active_requests = 0;
1066 usb_drv_stall(EP_CONTROL, true, true);
1067 return;
1068 }
1069
1037 if(req->wLength == 0) 1070 if(req->wLength == 0)
1038 { 1071 {
1039 active_request = NULL; 1072 active_request = NULL;