From f1a02a8e1e570ec209443c5ce1dff417d7c7d80b Mon Sep 17 00:00:00 2001 From: "Ithamar R. Adema" Date: Sun, 15 Mar 2015 17:34:47 +0000 Subject: [PATCH] usb_hid: fix STALL handling It is not allowed to do synchronous calls to the usb bus manager while in a usb callback routine. This causes the usb stack to get very confused. This code now uses the async interface to clear the STALL, and will try to continue normal operations after that request has been handled. Fix sponsered by http://www.izcorp.com --- .../drivers/input/usb_hid/HIDDevice.cpp | 22 ++++++++++++++++--- .../kernel/drivers/input/usb_hid/HIDDevice.h | 4 ++++ 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/src/add-ons/kernel/drivers/input/usb_hid/HIDDevice.cpp b/src/add-ons/kernel/drivers/input/usb_hid/HIDDevice.cpp index 9348370891..7ed6017933 100644 --- a/src/add-ons/kernel/drivers/input/usb_hid/HIDDevice.cpp +++ b/src/add-ons/kernel/drivers/input/usb_hid/HIDDevice.cpp @@ -170,6 +170,7 @@ HIDDevice::HIDDevice(usb_device device, const usb_configuration_info *config, if ((descriptor->endpoint_address & USB_ENDPOINT_ADDR_DIR_IN) && (descriptor->attributes & USB_ENDPOINT_ATTR_MASK) == USB_ENDPOINT_ATTR_INTERRUPT) { + fEndpointAddress = descriptor->endpoint_address; fInterruptPipe = interface->endpoint[i].handle; break; } @@ -309,6 +310,20 @@ HIDDevice::ProtocolHandlerAt(uint32 index) const } +void +HIDDevice::_UnstallCallback(void *cookie, status_t status, void *data, + size_t actualLength) +{ + HIDDevice *device = (HIDDevice *)cookie; + if (status != B_OK) { + TRACE_ALWAYS("Unable to unstall device: %s\n", strerror(status)); + } + + // Now report the original failure, since we're ready to retry + _TransferCallback(cookie, B_ERROR, device->fTransferBuffer, 0); +} + + void HIDDevice::_TransferCallback(void *cookie, status_t status, void *data, size_t actualLength) @@ -316,10 +331,11 @@ HIDDevice::_TransferCallback(void *cookie, status_t status, void *data, HIDDevice *device = (HIDDevice *)cookie; if (status == B_DEV_STALLED && !device->fRemoved) { // try clearing stalls right away, the report listeners will resubmit - gUSBModule->clear_feature(device->fInterruptPipe, - USB_FEATURE_ENDPOINT_HALT); + gUSBModule->queue_request(device->fDevice, + USB_REQTYPE_STANDARD | USB_REQTYPE_ENDPOINT_OUT, USB_REQUEST_CLEAR_FEATURE, + USB_FEATURE_ENDPOINT_HALT, device->fEndpointAddress, 0, NULL, _UnstallCallback, device); + return; } - atomic_set(&device->fTransferScheduled, 0); device->fParser.SetReport(status, device->fTransferBuffer, actualLength); } diff --git a/src/add-ons/kernel/drivers/input/usb_hid/HIDDevice.h b/src/add-ons/kernel/drivers/input/usb_hid/HIDDevice.h index 744b9ba0fd..8cd8b957f9 100644 --- a/src/add-ons/kernel/drivers/input/usb_hid/HIDDevice.h +++ b/src/add-ons/kernel/drivers/input/usb_hid/HIDDevice.h @@ -48,12 +48,16 @@ private: static void _TransferCallback(void *cookie, status_t status, void *data, size_t actualLength); + static void _UnstallCallback(void *cookie, + status_t status, void *data, + size_t actualLength); private: status_t fStatus; usb_device fDevice; usb_pipe fInterruptPipe; size_t fInterfaceIndex; + uint8 fEndpointAddress; int32 fTransferScheduled; size_t fTransferBufferSize;