summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAidan MacDonald <amachronic@protonmail.com>2022-12-22 21:52:31 +0000
committerAidan MacDonald <amachronic@protonmail.com>2023-01-18 14:22:26 -0500
commitbd7b54a3c4f12793f855a7e2885b318edba7b06e (patch)
treee4c6414a1880d803241e30f0d24f14da9a4b8f8a
parent969e1ef6cda10aaf1affb07a953ec1b597de24e0 (diff)
downloadrockbox-bd7b54a3c4f12793f855a7e2885b318edba7b06e.tar.gz
rockbox-bd7b54a3c4f12793f855a7e2885b318edba7b06e.zip
usb: Attempt to handle overlapped control requests in legacy drivers
It seems connecting an iPod Video to a Mac triggers the null request check, resulting in a panic. Ignoring the error with a bare return "fixes" it and allows the iPod to connect. This isn't ideal though, because it could silently introduce bugs on other targets. The likely cause of this is the host sending control requests too fast, or a driver problem (the Video uses the ARC driver, which is still on the legacy interface), with multiple requests getting queued at once. Since the USB core expects to deal with only one request at a time, the second response trips the check. Try to handle this situation a bit more gracefully by detecting overlapped requests and returning a STALL to the host when it occurs. At this point the USB stack is able to safely handle a new request. Link: https://forums.rockbox.org/index.php/topic,54414.0.html Change-Id: I9a2b7e35620ff540ebdb39f81671377062a4917d
-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;