diff options
Diffstat (limited to 'drivers/hid/usbhid/hid-core.c')
-rw-r--r-- | drivers/hid/usbhid/hid-core.c | 31 |
1 files changed, 23 insertions, 8 deletions
diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index 76013eb5cb7f..045b5da9b992 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -680,18 +680,21 @@ static int usbhid_open(struct hid_device *hid) struct usbhid_device *usbhid = hid->driver_data; int res; + set_bit(HID_OPENED, &usbhid->iofl); + if (hid->quirks & HID_QUIRK_ALWAYS_POLL) return 0; res = usb_autopm_get_interface(usbhid->intf); /* the device must be awake to reliably request remote wakeup */ - if (res < 0) + if (res < 0) { + clear_bit(HID_OPENED, &usbhid->iofl); return -EIO; + } usbhid->intf->needs_remote_wakeup = 1; set_bit(HID_RESUME_RUNNING, &usbhid->iofl); - set_bit(HID_OPENED, &usbhid->iofl); set_bit(HID_IN_POLLING, &usbhid->iofl); res = hid_start_in(hid); @@ -727,19 +730,20 @@ static void usbhid_close(struct hid_device *hid) { struct usbhid_device *usbhid = hid->driver_data; - if (hid->quirks & HID_QUIRK_ALWAYS_POLL) - return; - /* * Make sure we don't restart data acquisition due to * a resumption we no longer care about by avoiding racing * with hid_start_in(). */ spin_lock_irq(&usbhid->lock); - clear_bit(HID_IN_POLLING, &usbhid->iofl); clear_bit(HID_OPENED, &usbhid->iofl); + if (!(hid->quirks & HID_QUIRK_ALWAYS_POLL)) + clear_bit(HID_IN_POLLING, &usbhid->iofl); spin_unlock_irq(&usbhid->lock); + if (hid->quirks & HID_QUIRK_ALWAYS_POLL) + return; + hid_cancel_delayed_stuff(usbhid); usb_kill_urb(usbhid->urbin); usbhid->intf->needs_remote_wakeup = 0; @@ -971,6 +975,8 @@ static int usbhid_parse(struct hid_device *hid) unsigned int rsize = 0; char *rdesc; int ret, n; + int num_descriptors; + size_t offset = offsetof(struct hid_descriptor, desc); quirks = usbhid_lookup_quirk(le16_to_cpu(dev->descriptor.idVendor), le16_to_cpu(dev->descriptor.idProduct)); @@ -993,10 +999,18 @@ static int usbhid_parse(struct hid_device *hid) return -ENODEV; } + if (hdesc->bLength < sizeof(struct hid_descriptor)) { + dbg_hid("hid descriptor is too short\n"); + return -EINVAL; + } + hid->version = le16_to_cpu(hdesc->bcdHID); hid->country = hdesc->bCountryCode; - for (n = 0; n < hdesc->bNumDescriptors; n++) + num_descriptors = min_t(int, hdesc->bNumDescriptors, + (hdesc->bLength - offset) / sizeof(struct hid_class_descriptor)); + + for (n = 0; n < num_descriptors; n++) if (hdesc->desc[n].bDescriptorType == HID_DT_REPORT) rsize = le16_to_cpu(hdesc->desc[n].wDescriptorLength); @@ -1261,7 +1275,7 @@ static int usbhid_idle(struct hid_device *hid, int report, int idle, return hid_set_idle(dev, ifnum, report, idle); } -static struct hid_ll_driver usb_hid_driver = { +struct hid_ll_driver usb_hid_driver = { .parse = usbhid_parse, .start = usbhid_start, .stop = usbhid_stop, @@ -1274,6 +1288,7 @@ static struct hid_ll_driver usb_hid_driver = { .output_report = usbhid_output_report, .idle = usbhid_idle, }; +EXPORT_SYMBOL_GPL(usb_hid_driver); static int usbhid_probe(struct usb_interface *intf, const struct usb_device_id *id) { |