diff options
Diffstat (limited to 'drivers/hid/usbhid/hid-core.c')
| -rw-r--r-- | drivers/hid/usbhid/hid-core.c | 110 |
1 files changed, 60 insertions, 50 deletions
diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index 06130dc431a0..aac0051a2cf6 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -19,8 +19,9 @@ #include <linux/list.h> #include <linux/mm.h> #include <linux/mutex.h> +#include <linux/property.h> #include <linux/spinlock.h> -#include <asm/unaligned.h> +#include <linux/unaligned.h> #include <asm/byteorder.h> #include <linux/input.h> #include <linux/wait.h> @@ -34,6 +35,7 @@ #include <linux/hid-debug.h> #include <linux/hidraw.h> #include "usbhid.h" +#include "hid-pidff.h" /* * Version Information @@ -104,7 +106,7 @@ static int hid_start_in(struct hid_device *hid) /* I/O retry timer routine */ static void hid_retry_timeout(struct timer_list *t) { - struct usbhid_device *usbhid = from_timer(usbhid, t, io_retry); + struct usbhid_device *usbhid = timer_container_of(usbhid, t, io_retry); struct hid_device *hid = usbhid->hid; dev_dbg(&usbhid->intf->dev, "retrying intr urb\n"); @@ -377,27 +379,23 @@ static int hid_submit_ctrl(struct hid_device *hid) len = hid_report_len(report); if (dir == USB_DIR_OUT) { usbhid->urbctrl->pipe = usb_sndctrlpipe(hid_to_usb_dev(hid), 0); - usbhid->urbctrl->transfer_buffer_length = len; if (raw_report) { memcpy(usbhid->ctrlbuf, raw_report, len); kfree(raw_report); usbhid->ctrl[usbhid->ctrltail].raw_report = NULL; } } else { - int maxpacket, padlen; + int maxpacket; usbhid->urbctrl->pipe = usb_rcvctrlpipe(hid_to_usb_dev(hid), 0); maxpacket = usb_maxpacket(hid_to_usb_dev(hid), - usbhid->urbctrl->pipe, 0); - if (maxpacket > 0) { - padlen = DIV_ROUND_UP(len, maxpacket); - padlen *= maxpacket; - if (padlen > usbhid->bufsize) - padlen = usbhid->bufsize; - } else - padlen = 0; - usbhid->urbctrl->transfer_buffer_length = padlen; + usbhid->urbctrl->pipe); + len += (len == 0); /* Don't allow 0-length reports */ + len = round_up(len, maxpacket); + if (len > usbhid->bufsize) + len = usbhid->bufsize; } + usbhid->urbctrl->transfer_buffer_length = len; usbhid->urbctrl->dev = hid_to_usb_dev(hid); usbhid->cr->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE | dir; @@ -505,7 +503,7 @@ static void hid_ctrl(struct urb *urb) if (unplug) { usbhid->ctrltail = usbhid->ctrlhead; - } else { + } else if (usbhid->ctrlhead != usbhid->ctrltail) { usbhid->ctrltail = (usbhid->ctrltail + 1) & (HID_CONTROL_FIFO_SIZE - 1); if (usbhid->ctrlhead != usbhid->ctrltail && @@ -986,12 +984,11 @@ static int usbhid_parse(struct hid_device *hid) struct usb_host_interface *interface = intf->cur_altsetting; struct usb_device *dev = interface_to_usbdev (intf); struct hid_descriptor *hdesc; + struct hid_class_descriptor *hcdesc; u32 quirks = 0; unsigned int rsize = 0; char *rdesc; - int ret, n; - int num_descriptors; - size_t offset = offsetof(struct hid_descriptor, desc); + int ret; quirks = hid_lookup_quirk(hid); @@ -1013,20 +1010,19 @@ 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"); + if (!hdesc->bNumDescriptors || + hdesc->bLength != sizeof(*hdesc) + + (hdesc->bNumDescriptors - 1) * sizeof(*hcdesc)) { + dbg_hid("hid descriptor invalid, bLen=%hhu bNum=%hhu\n", + hdesc->bLength, hdesc->bNumDescriptors); return -EINVAL; } hid->version = le16_to_cpu(hdesc->bcdHID); hid->country = hdesc->bCountryCode; - 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); + if (hdesc->rpt_desc.bDescriptorType == HID_DT_REPORT) + rsize = le16_to_cpu(hdesc->rpt_desc.wDescriptorLength); if (!rsize || rsize > HID_MAX_DESCRIPTOR_SIZE) { dbg_hid("weird size of report descriptor (%u)\n", rsize); @@ -1054,6 +1050,11 @@ static int usbhid_parse(struct hid_device *hid) goto err; } + if (hdesc->bNumDescriptors > 1) + hid_warn(intf, + "%u unsupported optional hid class descriptors\n", + (int)(hdesc->bNumDescriptors - 1)); + hid->quirks |= quirks; return 0; @@ -1103,7 +1104,7 @@ static int usbhid_start(struct hid_device *hid) interval = endpoint->bInterval; - /* Some vendors give fullspeed interval on highspeed devides */ + /* Some vendors give fullspeed interval on highspeed devices */ if (hid->quirks & HID_QUIRK_FULLSPEED_INTERVAL && dev->speed == USB_SPEED_HIGH) { interval = fls(endpoint->bInterval*8); @@ -1223,9 +1224,20 @@ static void usbhid_stop(struct hid_device *hid) mutex_lock(&usbhid->mutex); clear_bit(HID_STARTED, &usbhid->iofl); + spin_lock_irq(&usbhid->lock); /* Sync with error and led handlers */ set_bit(HID_DISCONNECTED, &usbhid->iofl); + while (usbhid->ctrltail != usbhid->ctrlhead) { + if (usbhid->ctrl[usbhid->ctrltail].dir == USB_DIR_OUT) { + kfree(usbhid->ctrl[usbhid->ctrltail].raw_report); + usbhid->ctrl[usbhid->ctrltail].raw_report = NULL; + } + + usbhid->ctrltail = (usbhid->ctrltail + 1) & + (HID_CONTROL_FIFO_SIZE - 1); + } spin_unlock_irq(&usbhid->lock); + usb_kill_urb(usbhid->urbin); usb_kill_urb(usbhid->urbout); usb_kill_urb(usbhid->urbctrl); @@ -1311,7 +1323,7 @@ static bool usbhid_may_wakeup(struct hid_device *hid) return device_may_wakeup(&dev->dev); } -struct hid_ll_driver usb_hid_driver = { +static const struct hid_ll_driver usb_hid_driver = { .parse = usbhid_parse, .start = usbhid_start, .stop = usbhid_stop, @@ -1325,7 +1337,12 @@ struct hid_ll_driver usb_hid_driver = { .idle = usbhid_idle, .may_wakeup = usbhid_may_wakeup, }; -EXPORT_SYMBOL_GPL(usb_hid_driver); + +bool hid_is_usb(const struct hid_device *hdev) +{ + return hdev->ll_driver == &usb_hid_driver; +} +EXPORT_SYMBOL_GPL(hid_is_usb); static int usbhid_probe(struct usb_interface *intf, const struct usb_device_id *id) { @@ -1362,6 +1379,7 @@ static int usbhid_probe(struct usb_interface *intf, const struct usb_device_id * hid->hiddev_report_event = hiddev_report_event; #endif hid->dev.parent = &intf->dev; + device_set_node(&hid->dev, dev_fwnode(&intf->dev)); hid->bus = BUS_USB; hid->vendor = le16_to_cpu(dev->descriptor.idVendor); hid->product = le16_to_cpu(dev->descriptor.idProduct); @@ -1374,7 +1392,7 @@ static int usbhid_probe(struct usb_interface *intf, const struct usb_device_id * hid->type = HID_TYPE_USBNONE; if (dev->manufacturer) - strlcpy(hid->name, dev->manufacturer, sizeof(hid->name)); + strscpy(hid->name, dev->manufacturer, sizeof(hid->name)); if (dev->product) { if (dev->manufacturer) @@ -1447,13 +1465,13 @@ static void usbhid_disconnect(struct usb_interface *intf) static void hid_cancel_delayed_stuff(struct usbhid_device *usbhid) { - del_timer_sync(&usbhid->io_retry); + timer_delete_sync(&usbhid->io_retry); cancel_work_sync(&usbhid->reset_work); } static void hid_cease_io(struct usbhid_device *usbhid) { - del_timer_sync(&usbhid->io_retry); + timer_delete_sync(&usbhid->io_retry); usb_kill_urb(usbhid->urbin); usb_kill_urb(usbhid->urbctrl); usb_kill_urb(usbhid->urbout); @@ -1550,14 +1568,13 @@ static int hid_post_reset(struct usb_interface *intf) return 0; } -#ifdef CONFIG_PM static int hid_resume_common(struct hid_device *hid, bool driver_suspended) { int status = 0; hid_restart_io(hid); - if (driver_suspended && hid->driver && hid->driver->resume) - status = hid->driver->resume(hid); + if (driver_suspended) + status = hid_driver_resume(hid); return status; } @@ -1581,11 +1598,9 @@ static int hid_suspend(struct usb_interface *intf, pm_message_t message) { set_bit(HID_SUSPENDED, &usbhid->iofl); spin_unlock_irq(&usbhid->lock); - if (hid->driver && hid->driver->suspend) { - status = hid->driver->suspend(hid, message); - if (status < 0) - goto failed; - } + status = hid_driver_suspend(hid, message); + if (status < 0) + goto failed; driver_suspended = true; } else { usbhid_mark_busy(usbhid); @@ -1595,8 +1610,7 @@ static int hid_suspend(struct usb_interface *intf, pm_message_t message) } else { /* TODO: resume() might need to handle suspend failure */ - if (hid->driver && hid->driver->suspend) - status = hid->driver->suspend(hid, message); + status = hid_driver_suspend(hid, message); driver_suspended = true; spin_lock_irq(&usbhid->lock); set_bit(HID_SUSPENDED, &usbhid->iofl); @@ -1637,16 +1651,14 @@ static int hid_reset_resume(struct usb_interface *intf) int status; status = hid_post_reset(intf); - if (status >= 0 && hid->driver && hid->driver->reset_resume) { - int ret = hid->driver->reset_resume(hid); + if (status >= 0) { + int ret = hid_driver_reset_resume(hid); if (ret < 0) status = ret; } return status; } -#endif /* CONFIG_PM */ - static const struct usb_device_id hid_usb_ids[] = { { .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS, .bInterfaceClass = USB_INTERFACE_CLASS_HID }, @@ -1659,11 +1671,9 @@ static struct usb_driver hid_driver = { .name = "usbhid", .probe = usbhid_probe, .disconnect = usbhid_disconnect, -#ifdef CONFIG_PM - .suspend = hid_suspend, - .resume = hid_resume, - .reset_resume = hid_reset_resume, -#endif + .suspend = pm_ptr(hid_suspend), + .resume = pm_ptr(hid_resume), + .reset_resume = pm_ptr(hid_reset_resume), .pre_reset = hid_pre_reset, .post_reset = hid_post_reset, .id_table = hid_usb_ids, |
