aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Kurtz <djkurtz@chromium.org>2011-11-17 06:23:50 -0500
committerJiri Kosina <jkosina@suse.cz>2011-12-21 05:18:35 -0500
commit4371ea8202e98c8ef77ca887de3b19affbb3498f (patch)
treefc69c8be52b10dd549237ac117870e1fb872dfe8
parentf0befcd64bc57e6a0b7a96c37c55f79e6b999af7 (diff)
HID: usbhid: defer LED setting to a workqueue
Defer LED setting action to a workqueue. This is more likely to send all LED change events in a single URB. Signed-off-by: Daniel Kurtz <djkurtz@chromium.org> Acked-by: Oliver Neukum <oneukum@suse.de> Signed-off-by: Jiri Kosina <jkosina@suse.cz>
-rw-r--r--drivers/hid/hid-input.c42
-rw-r--r--drivers/hid/usbhid/hid-core.c47
-rw-r--r--drivers/hid/usbhid/usbhid.h2
-rw-r--r--include/linux/hid.h2
4 files changed, 82 insertions, 11 deletions
diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c
index b9b8c75a6f9a..c6ee632bfd68 100644
--- a/drivers/hid/hid-input.c
+++ b/drivers/hid/hid-input.c
@@ -976,6 +976,48 @@ int hidinput_find_field(struct hid_device *hid, unsigned int type, unsigned int
976} 976}
977EXPORT_SYMBOL_GPL(hidinput_find_field); 977EXPORT_SYMBOL_GPL(hidinput_find_field);
978 978
979struct hid_field *hidinput_get_led_field(struct hid_device *hid)
980{
981 struct hid_report *report;
982 struct hid_field *field;
983 int i, j;
984
985 list_for_each_entry(report,
986 &hid->report_enum[HID_OUTPUT_REPORT].report_list,
987 list) {
988 for (i = 0; i < report->maxfield; i++) {
989 field = report->field[i];
990 for (j = 0; j < field->maxusage; j++)
991 if (field->usage[j].type == EV_LED)
992 return field;
993 }
994 }
995 return NULL;
996}
997EXPORT_SYMBOL_GPL(hidinput_get_led_field);
998
999unsigned int hidinput_count_leds(struct hid_device *hid)
1000{
1001 struct hid_report *report;
1002 struct hid_field *field;
1003 int i, j;
1004 unsigned int count = 0;
1005
1006 list_for_each_entry(report,
1007 &hid->report_enum[HID_OUTPUT_REPORT].report_list,
1008 list) {
1009 for (i = 0; i < report->maxfield; i++) {
1010 field = report->field[i];
1011 for (j = 0; j < field->maxusage; j++)
1012 if (field->usage[j].type == EV_LED &&
1013 field->value[j])
1014 count += 1;
1015 }
1016 }
1017 return count;
1018}
1019EXPORT_SYMBOL_GPL(hidinput_count_leds);
1020
979static int hidinput_open(struct input_dev *dev) 1021static int hidinput_open(struct input_dev *dev)
980{ 1022{
981 struct hid_device *hid = input_get_drvdata(dev); 1023 struct hid_device *hid = input_get_drvdata(dev);
diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c
index 719f6b02fab1..5bf91dbad59d 100644
--- a/drivers/hid/usbhid/hid-core.c
+++ b/drivers/hid/usbhid/hid-core.c
@@ -602,6 +602,30 @@ void usbhid_submit_report(struct hid_device *hid, struct hid_report *report, uns
602} 602}
603EXPORT_SYMBOL_GPL(usbhid_submit_report); 603EXPORT_SYMBOL_GPL(usbhid_submit_report);
604 604
605/* Workqueue routine to send requests to change LEDs */
606static void hid_led(struct work_struct *work)
607{
608 struct usbhid_device *usbhid =
609 container_of(work, struct usbhid_device, led_work);
610 struct hid_device *hid = usbhid->hid;
611 struct hid_field *field;
612 unsigned long flags;
613
614 field = hidinput_get_led_field(hid);
615 if (!field) {
616 hid_warn(hid, "LED event field not found\n");
617 return;
618 }
619
620 spin_lock_irqsave(&usbhid->lock, flags);
621 if (!test_bit(HID_DISCONNECTED, &usbhid->iofl)) {
622 usbhid->ledcount = hidinput_count_leds(hid);
623 hid_dbg(usbhid->hid, "New ledcount = %u\n", usbhid->ledcount);
624 __usbhid_submit_report(hid, field->report, USB_DIR_OUT);
625 }
626 spin_unlock_irqrestore(&usbhid->lock, flags);
627}
628
605static int usb_hidinput_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) 629static int usb_hidinput_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
606{ 630{
607 struct hid_device *hid = input_get_drvdata(dev); 631 struct hid_device *hid = input_get_drvdata(dev);
@@ -621,17 +645,15 @@ static int usb_hidinput_input_event(struct input_dev *dev, unsigned int type, un
621 return -1; 645 return -1;
622 } 646 }
623 647
648 spin_lock_irqsave(&usbhid->lock, flags);
624 hid_set_field(field, offset, value); 649 hid_set_field(field, offset, value);
625 if (value) { 650 spin_unlock_irqrestore(&usbhid->lock, flags);
626 spin_lock_irqsave(&usbhid->lock, flags); 651
627 usbhid->ledcount++; 652 /*
628 spin_unlock_irqrestore(&usbhid->lock, flags); 653 * Defer performing requested LED action.
629 } else { 654 * This is more likely gather all LED changes into a single URB.
630 spin_lock_irqsave(&usbhid->lock, flags); 655 */
631 usbhid->ledcount--; 656 schedule_work(&usbhid->led_work);
632 spin_unlock_irqrestore(&usbhid->lock, flags);
633 }
634 usbhid_submit_report(hid, field->report, USB_DIR_OUT);
635 657
636 return 0; 658 return 0;
637} 659}
@@ -1126,7 +1148,7 @@ static void usbhid_stop(struct hid_device *hid)
1126 return; 1148 return;
1127 1149
1128 clear_bit(HID_STARTED, &usbhid->iofl); 1150 clear_bit(HID_STARTED, &usbhid->iofl);
1129 spin_lock_irq(&usbhid->lock); /* Sync with error handler */ 1151 spin_lock_irq(&usbhid->lock); /* Sync with error and led handlers */
1130 set_bit(HID_DISCONNECTED, &usbhid->iofl); 1152 set_bit(HID_DISCONNECTED, &usbhid->iofl);
1131 spin_unlock_irq(&usbhid->lock); 1153 spin_unlock_irq(&usbhid->lock);
1132 usb_kill_urb(usbhid->urbin); 1154 usb_kill_urb(usbhid->urbin);
@@ -1260,6 +1282,8 @@ static int usbhid_probe(struct usb_interface *intf, const struct usb_device_id *
1260 setup_timer(&usbhid->io_retry, hid_retry_timeout, (unsigned long) hid); 1282 setup_timer(&usbhid->io_retry, hid_retry_timeout, (unsigned long) hid);
1261 spin_lock_init(&usbhid->lock); 1283 spin_lock_init(&usbhid->lock);
1262 1284
1285 INIT_WORK(&usbhid->led_work, hid_led);
1286
1263 ret = hid_add_device(hid); 1287 ret = hid_add_device(hid);
1264 if (ret) { 1288 if (ret) {
1265 if (ret != -ENODEV) 1289 if (ret != -ENODEV)
@@ -1292,6 +1316,7 @@ static void hid_cancel_delayed_stuff(struct usbhid_device *usbhid)
1292{ 1316{
1293 del_timer_sync(&usbhid->io_retry); 1317 del_timer_sync(&usbhid->io_retry);
1294 cancel_work_sync(&usbhid->reset_work); 1318 cancel_work_sync(&usbhid->reset_work);
1319 cancel_work_sync(&usbhid->led_work);
1295} 1320}
1296 1321
1297static void hid_cease_io(struct usbhid_device *usbhid) 1322static void hid_cease_io(struct usbhid_device *usbhid)
diff --git a/drivers/hid/usbhid/usbhid.h b/drivers/hid/usbhid/usbhid.h
index 2d8957c11d2d..cb8f703efde5 100644
--- a/drivers/hid/usbhid/usbhid.h
+++ b/drivers/hid/usbhid/usbhid.h
@@ -96,6 +96,8 @@ struct usbhid_device {
96 struct work_struct reset_work; /* Task context for resets */ 96 struct work_struct reset_work; /* Task context for resets */
97 wait_queue_head_t wait; /* For sleeping */ 97 wait_queue_head_t wait; /* For sleeping */
98 int ledcount; /* counting the number of active leds */ 98 int ledcount; /* counting the number of active leds */
99
100 struct work_struct led_work; /* Task context for setting LEDs */
99}; 101};
100 102
101#define hid_to_usb_dev(hid_dev) \ 103#define hid_to_usb_dev(hid_dev) \
diff --git a/include/linux/hid.h b/include/linux/hid.h
index 7f344c3da767..999a54c72b20 100644
--- a/include/linux/hid.h
+++ b/include/linux/hid.h
@@ -727,6 +727,8 @@ extern void hidinput_disconnect(struct hid_device *);
727int hid_set_field(struct hid_field *, unsigned, __s32); 727int hid_set_field(struct hid_field *, unsigned, __s32);
728int hid_input_report(struct hid_device *, int type, u8 *, int, int); 728int hid_input_report(struct hid_device *, int type, u8 *, int, int);
729int hidinput_find_field(struct hid_device *hid, unsigned int type, unsigned int code, struct hid_field **field); 729int hidinput_find_field(struct hid_device *hid, unsigned int type, unsigned int code, struct hid_field **field);
730struct hid_field *hidinput_get_led_field(struct hid_device *hid);
731unsigned int hidinput_count_leds(struct hid_device *hid);
730void hid_output_report(struct hid_report *report, __u8 *data); 732void hid_output_report(struct hid_report *report, __u8 *data);
731struct hid_device *hid_allocate_device(void); 733struct hid_device *hid_allocate_device(void);
732struct hid_report *hid_register_report(struct hid_device *device, unsigned type, unsigned id); 734struct hid_report *hid_register_report(struct hid_device *device, unsigned type, unsigned id);