diff options
Diffstat (limited to 'firmware/usbstack')
-rw-r--r-- | firmware/usbstack/usb_core.c | 43 |
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 |
267 | static struct usb_ctrlrequest buffered_request; | ||
267 | static struct usb_ctrlrequest* volatile active_request = NULL; | 268 | static struct usb_ctrlrequest* volatile active_request = NULL; |
269 | static volatile unsigned int num_active_requests = 0; | ||
268 | static void* volatile control_write_data = NULL; | 270 | static void* volatile control_write_data = NULL; |
269 | static volatile bool control_write_data_done = false; | 271 | static 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 */ |
1020 | void usb_core_legacy_control_request(struct usb_ctrlrequest* req) | 1022 | void 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 | ||
1029 | void usb_drv_control_response(enum usb_control_response resp, | 1036 | void 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; |