Commit a8c52b66 authored by Oliver Neukum's avatar Oliver Neukum Committed by Jiri Kosina

HID: usbhid: fix error handling of not enough bandwidth

In case IO cannot be started because there is a lack of bandwidth
on the bus, it makes no sense to reset the device. If IO is requested
because the device is opened, user space should be notified with
an error right away. If the lack of bandwidth arises later, for
example after resume, there's no other choice but to retry in the
hope that bandwidth will be freed.
Signed-off-by: default avatarOliver Neukum <oneukum@suse.de>
Signed-off-by: default avatarJiri Kosina <jkosina@suse.cz>
parent d464c92b
...@@ -86,8 +86,13 @@ static int hid_start_in(struct hid_device *hid) ...@@ -86,8 +86,13 @@ static int hid_start_in(struct hid_device *hid)
!test_bit(HID_REPORTED_IDLE, &usbhid->iofl) && !test_bit(HID_REPORTED_IDLE, &usbhid->iofl) &&
!test_and_set_bit(HID_IN_RUNNING, &usbhid->iofl)) { !test_and_set_bit(HID_IN_RUNNING, &usbhid->iofl)) {
rc = usb_submit_urb(usbhid->urbin, GFP_ATOMIC); rc = usb_submit_urb(usbhid->urbin, GFP_ATOMIC);
if (rc != 0) if (rc != 0) {
clear_bit(HID_IN_RUNNING, &usbhid->iofl); clear_bit(HID_IN_RUNNING, &usbhid->iofl);
if (rc == -ENOSPC)
set_bit(HID_NO_BANDWIDTH, &usbhid->iofl);
} else {
clear_bit(HID_NO_BANDWIDTH, &usbhid->iofl);
}
} }
spin_unlock_irqrestore(&usbhid->lock, flags); spin_unlock_irqrestore(&usbhid->lock, flags);
return rc; return rc;
...@@ -173,8 +178,10 @@ static void hid_io_error(struct hid_device *hid) ...@@ -173,8 +178,10 @@ static void hid_io_error(struct hid_device *hid)
if (time_after(jiffies, usbhid->stop_retry)) { if (time_after(jiffies, usbhid->stop_retry)) {
/* Retries failed, so do a port reset */ /* Retries failed, so do a port reset unless we lack bandwidth*/
if (!test_and_set_bit(HID_RESET_PENDING, &usbhid->iofl)) { if (test_bit(HID_NO_BANDWIDTH, &usbhid->iofl)
&& !test_and_set_bit(HID_RESET_PENDING, &usbhid->iofl)) {
schedule_work(&usbhid->reset_work); schedule_work(&usbhid->reset_work);
goto done; goto done;
} }
...@@ -700,7 +707,7 @@ static int hid_get_class_descriptor(struct usb_device *dev, int ifnum, ...@@ -700,7 +707,7 @@ static int hid_get_class_descriptor(struct usb_device *dev, int ifnum,
int usbhid_open(struct hid_device *hid) int usbhid_open(struct hid_device *hid)
{ {
struct usbhid_device *usbhid = hid->driver_data; struct usbhid_device *usbhid = hid->driver_data;
int res; int res = 0;
mutex_lock(&hid_open_mut); mutex_lock(&hid_open_mut);
if (!hid->open++) { if (!hid->open++) {
...@@ -708,17 +715,27 @@ int usbhid_open(struct hid_device *hid) ...@@ -708,17 +715,27 @@ int usbhid_open(struct hid_device *hid)
/* the device must be awake to reliably request remote wakeup */ /* the device must be awake to reliably request remote wakeup */
if (res < 0) { if (res < 0) {
hid->open--; hid->open--;
mutex_unlock(&hid_open_mut); res = -EIO;
return -EIO; goto done;
} }
usbhid->intf->needs_remote_wakeup = 1; usbhid->intf->needs_remote_wakeup = 1;
if (hid_start_in(hid)) res = hid_start_in(hid);
if (res) {
if (res != -ENOSPC) {
hid_io_error(hid); hid_io_error(hid);
res = 0;
} else {
/* no use opening if resources are insufficient */
hid->open--;
res = -EBUSY;
usbhid->intf->needs_remote_wakeup = 0;
}
}
usb_autopm_put_interface(usbhid->intf); usb_autopm_put_interface(usbhid->intf);
} }
done:
mutex_unlock(&hid_open_mut); mutex_unlock(&hid_open_mut);
return 0; return res;
} }
void usbhid_close(struct hid_device *hid) void usbhid_close(struct hid_device *hid)
......
...@@ -55,6 +55,7 @@ struct usb_interface *usbhid_find_interface(int minor); ...@@ -55,6 +55,7 @@ struct usb_interface *usbhid_find_interface(int minor);
#define HID_STARTED 8 #define HID_STARTED 8
#define HID_REPORTED_IDLE 9 #define HID_REPORTED_IDLE 9
#define HID_KEYS_PRESSED 10 #define HID_KEYS_PRESSED 10
#define HID_NO_BANDWIDTH 11
/* /*
* USB-specific HID struct, to be pointed to * USB-specific HID struct, to be pointed to
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment