diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index b403fcef0b869..b78b3fb41335f 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -239,14 +239,29 @@ static void hid_irq_in(struct urb *urb) struct hid_device *hid = urb->context; struct usbhid_device *usbhid = hid->driver_data; int status; + int process_input_report = 1; switch (urb->status) { case 0: /* success */ usbhid_mark_busy(usbhid); usbhid->retry_delay = 0; - hid_input_report(urb->context, HID_INPUT_REPORT, - urb->transfer_buffer, - urb->actual_length, 1); + if (hid->quirks & HID_QUIRK_USBHID_SET_IDLE) { + /* when set idle is enabled, device may send duplicate reports. + * so, do not process report if the same as previous */ + if (memcmp(urb->transfer_buffer, usbhid->idle_inbuf, urb->actual_length) == 0) { + process_input_report = 0; + } + else { + memcpy(usbhid->idle_inbuf, urb->transfer_buffer, urb->actual_length); + } + } + + if (process_input_report) { + hid_input_report(urb->context, HID_INPUT_REPORT, + urb->transfer_buffer, + urb->actual_length, 1); + } + /* * autosuspend refused while keys are pressed * because most keyboards don't wake up when @@ -633,6 +648,21 @@ static int hid_set_idle(struct usb_device *dev, int ifnum, int report, int idle) ifnum, NULL, 0, USB_CTRL_SET_TIMEOUT); } +static int hid_set_idle_with_quirk(struct usb_device *dev, int ifnum, int report, int idle, u32 quirks) +{ + if (quirks & HID_QUIRK_USBHID_SET_IDLE) { + printk(KERN_INFO "usbhid: HID set idle quirk for %04hx:%04hx\n", + le16_to_cpu(dev->descriptor.idVendor), le16_to_cpu(dev->descriptor.idProduct)); + if ((idle <= 0) || (idle > 10)) + { + /* Raspberry PI's USB might miss packets, so set to (10 * 4) = 40ms USB HID idle rate to workaround sticky key issue */ + idle = 10; + } + } + + return hid_set_idle(dev, ifnum, report, idle); +} + static int hid_get_class_descriptor(struct usb_device *dev, int ifnum, unsigned char type, void *buf, int size) { @@ -796,6 +826,16 @@ static int hid_alloc_buffers(struct usb_device *dev, struct hid_device *hid) !usbhid->ctrlbuf) return -1; + if (hid->quirks & HID_QUIRK_USBHID_SET_IDLE) + { + usbhid->idle_inbuf = (hid->quirks & HID_QUIRK_USBHID_SET_IDLE) ? kmalloc(usbhid->bufsize, GFP_KERNEL) : NULL; + if (usbhid->idle_inbuf) { + memset(usbhid->idle_inbuf, 0, usbhid->bufsize); + } + else + return -1; + } + return 0; } @@ -900,6 +940,11 @@ static void hid_free_buffers(struct usb_device *dev, struct hid_device *hid) usb_free_coherent(dev, usbhid->bufsize, usbhid->outbuf, usbhid->outbuf_dma); kfree(usbhid->cr); usb_free_coherent(dev, usbhid->bufsize, usbhid->ctrlbuf, usbhid->ctrlbuf_dma); + + if (usbhid->idle_inbuf) { + kfree(usbhid->idle_inbuf); + usbhid->idle_inbuf = NULL; + } } static int usbhid_parse(struct hid_device *hid) @@ -951,7 +996,7 @@ static int usbhid_parse(struct hid_device *hid) return -ENOMEM; } - hid_set_idle(dev, interface->desc.bInterfaceNumber, 0, 0); + hid_set_idle_with_quirk(dev, interface->desc.bInterfaceNumber, 0, 0, quirks); ret = hid_get_class_descriptor(dev, interface->desc.bInterfaceNumber, HID_DT_REPORT, rdesc, rsize); @@ -1301,7 +1346,7 @@ static int hid_post_reset(struct usb_interface *intf) spin_lock_irq(&usbhid->lock); clear_bit(HID_RESET_PENDING, &usbhid->iofl); spin_unlock_irq(&usbhid->lock); - hid_set_idle(dev, intf->cur_altsetting->desc.bInterfaceNumber, 0, 0); + hid_set_idle_with_quirk(dev, intf->cur_altsetting->desc.bInterfaceNumber, 0, 0, hid->quirks); status = hid_start_in(hid); if (status < 0) hid_io_error(hid); diff --git a/drivers/hid/usbhid/usbhid.h b/drivers/hid/usbhid/usbhid.h index 1673cac93d77b..1dbfdfa187b3a 100644 --- a/drivers/hid/usbhid/usbhid.h +++ b/drivers/hid/usbhid/usbhid.h @@ -97,6 +97,8 @@ struct usbhid_device { struct work_struct reset_work; /* Task context for resets */ wait_queue_head_t wait; /* For sleeping */ int ledcount; /* counting the number of active leds */ + + char *idle_inbuf; /* buffer for duplicate input comparison */ }; #define hid_to_usb_dev(hid_dev) \ diff --git a/include/linux/hid.h b/include/linux/hid.h index c235e4e8767c5..6fad8646ab546 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -315,6 +315,7 @@ struct hid_item { #define HID_QUIRK_HIDINPUT_FORCE 0x00000080 #define HID_QUIRK_MULTITOUCH 0x00000100 #define HID_QUIRK_SKIP_OUTPUT_REPORTS 0x00010000 +#define HID_QUIRK_USBHID_SET_IDLE 0x08000000 #define HID_QUIRK_FULLSPEED_INTERVAL 0x10000000 #define HID_QUIRK_NO_INIT_REPORTS 0x20000000 #define HID_QUIRK_NO_IGNORE 0x40000000