From: Daniel Kurtz <djkurtz@chromium.org>
To: jkosina@suse.cz, oneukum@suse.de, bleung@chromium.org
Cc: stern@rowland.harvard.edu, olofj@chromium.org,
linux-input@vger.kernel.org, linux-kernel@vger.kernel.org,
linux-usb@vger.kernel.org, Daniel Kurtz <djkurtz@chromium.org>
Subject: [PATCH 3/3] HID: usbhid: defer LED setting to a workqueue
Date: Thu, 17 Nov 2011 19:23:50 +0800 [thread overview]
Message-ID: <1321529030-7845-4-git-send-email-djkurtz@chromium.org> (raw)
In-Reply-To: <1321529030-7845-1-git-send-email-djkurtz@chromium.org>
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>
---
drivers/hid/hid-input.c | 42 ++++++++++++++++++++++++++++++++++++
drivers/hid/usbhid/hid-core.c | 47 +++++++++++++++++++++++++++++++---------
drivers/hid/usbhid/usbhid.h | 2 +
include/linux/hid.h | 2 +
4 files changed, 82 insertions(+), 11 deletions(-)
diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c
index 6e32526..9509684 100644
--- a/drivers/hid/hid-input.c
+++ b/drivers/hid/hid-input.c
@@ -867,6 +867,48 @@ int hidinput_find_field(struct hid_device *hid, unsigned int type, unsigned int
}
EXPORT_SYMBOL_GPL(hidinput_find_field);
+struct hid_field *hidinput_get_led_field(struct hid_device *hid)
+{
+ struct hid_report *report;
+ struct hid_field *field;
+ int i, j;
+
+ list_for_each_entry(report,
+ &hid->report_enum[HID_OUTPUT_REPORT].report_list,
+ list) {
+ for (i = 0; i < report->maxfield; i++) {
+ field = report->field[i];
+ for (j = 0; j < field->maxusage; j++)
+ if (field->usage[j].type == EV_LED)
+ return field;
+ }
+ }
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(hidinput_get_led_field);
+
+unsigned int hidinput_count_leds(struct hid_device *hid)
+{
+ struct hid_report *report;
+ struct hid_field *field;
+ int i, j;
+ unsigned int count = 0;
+
+ list_for_each_entry(report,
+ &hid->report_enum[HID_OUTPUT_REPORT].report_list,
+ list) {
+ for (i = 0; i < report->maxfield; i++) {
+ field = report->field[i];
+ for (j = 0; j < field->maxusage; j++)
+ if (field->usage[j].type == EV_LED &&
+ field->value[j])
+ count += 1;
+ }
+ }
+ return count;
+}
+EXPORT_SYMBOL_GPL(hidinput_count_leds);
+
static int hidinput_open(struct input_dev *dev)
{
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 719f6b0..5bf91db 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
}
EXPORT_SYMBOL_GPL(usbhid_submit_report);
+/* Workqueue routine to send requests to change LEDs */
+static void hid_led(struct work_struct *work)
+{
+ struct usbhid_device *usbhid =
+ container_of(work, struct usbhid_device, led_work);
+ struct hid_device *hid = usbhid->hid;
+ struct hid_field *field;
+ unsigned long flags;
+
+ field = hidinput_get_led_field(hid);
+ if (!field) {
+ hid_warn(hid, "LED event field not found\n");
+ return;
+ }
+
+ spin_lock_irqsave(&usbhid->lock, flags);
+ if (!test_bit(HID_DISCONNECTED, &usbhid->iofl)) {
+ usbhid->ledcount = hidinput_count_leds(hid);
+ hid_dbg(usbhid->hid, "New ledcount = %u\n", usbhid->ledcount);
+ __usbhid_submit_report(hid, field->report, USB_DIR_OUT);
+ }
+ spin_unlock_irqrestore(&usbhid->lock, flags);
+}
+
static int usb_hidinput_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
{
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
return -1;
}
+ spin_lock_irqsave(&usbhid->lock, flags);
hid_set_field(field, offset, value);
- if (value) {
- spin_lock_irqsave(&usbhid->lock, flags);
- usbhid->ledcount++;
- spin_unlock_irqrestore(&usbhid->lock, flags);
- } else {
- spin_lock_irqsave(&usbhid->lock, flags);
- usbhid->ledcount--;
- spin_unlock_irqrestore(&usbhid->lock, flags);
- }
- usbhid_submit_report(hid, field->report, USB_DIR_OUT);
+ spin_unlock_irqrestore(&usbhid->lock, flags);
+
+ /*
+ * Defer performing requested LED action.
+ * This is more likely gather all LED changes into a single URB.
+ */
+ schedule_work(&usbhid->led_work);
return 0;
}
@@ -1126,7 +1148,7 @@ static void usbhid_stop(struct hid_device *hid)
return;
clear_bit(HID_STARTED, &usbhid->iofl);
- spin_lock_irq(&usbhid->lock); /* Sync with error handler */
+ spin_lock_irq(&usbhid->lock); /* Sync with error and led handlers */
set_bit(HID_DISCONNECTED, &usbhid->iofl);
spin_unlock_irq(&usbhid->lock);
usb_kill_urb(usbhid->urbin);
@@ -1260,6 +1282,8 @@ static int usbhid_probe(struct usb_interface *intf, const struct usb_device_id *
setup_timer(&usbhid->io_retry, hid_retry_timeout, (unsigned long) hid);
spin_lock_init(&usbhid->lock);
+ INIT_WORK(&usbhid->led_work, hid_led);
+
ret = hid_add_device(hid);
if (ret) {
if (ret != -ENODEV)
@@ -1292,6 +1316,7 @@ static void hid_cancel_delayed_stuff(struct usbhid_device *usbhid)
{
del_timer_sync(&usbhid->io_retry);
cancel_work_sync(&usbhid->reset_work);
+ cancel_work_sync(&usbhid->led_work);
}
static void hid_cease_io(struct usbhid_device *usbhid)
diff --git a/drivers/hid/usbhid/usbhid.h b/drivers/hid/usbhid/usbhid.h
index 2d8957c..cb8f703 100644
--- a/drivers/hid/usbhid/usbhid.h
+++ b/drivers/hid/usbhid/usbhid.h
@@ -96,6 +96,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 */
+
+ struct work_struct led_work; /* Task context for setting LEDs */
};
#define hid_to_usb_dev(hid_dev) \
diff --git a/include/linux/hid.h b/include/linux/hid.h
index deed5f9..0df7d62 100644
--- a/include/linux/hid.h
+++ b/include/linux/hid.h
@@ -711,6 +711,8 @@ extern void hidinput_disconnect(struct hid_device *);
int hid_set_field(struct hid_field *, unsigned, __s32);
int hid_input_report(struct hid_device *, int type, u8 *, int, int);
int hidinput_find_field(struct hid_device *hid, unsigned int type, unsigned int code, struct hid_field **field);
+struct hid_field *hidinput_get_led_field(struct hid_device *hid);
+unsigned int hidinput_count_leds(struct hid_device *hid);
void hid_output_report(struct hid_report *report, __u8 *data);
struct hid_device *hid_allocate_device(void);
struct hid_report *hid_register_report(struct hid_device *device, unsigned type, unsigned id);
--
1.7.3.1
next prev parent reply other threads:[~2011-11-17 11:23 UTC|newest]
Thread overview: 23+ messages / expand[flat|nested] mbox.gz Atom feed top
2011-11-17 11:23 [PATCH 0/3 v2] usb/hid-core: drain URB queue when going to suspend Daniel Kurtz
2011-11-17 11:23 ` [PATCH 1/3] HID: usbhid: remove LED_ON Daniel Kurtz
2011-11-17 11:23 ` [PATCH 2/3] HID: usbhid: hid-core: submit queued urbs before suspend Daniel Kurtz
2011-12-14 7:55 ` Oliver Neukum
2011-12-14 8:00 ` Daniel Kurtz
[not found] ` <CAGS+omDMipRTVOGV9fG0ciWpuwMRBL-eTY8uDwaiSOW==CkQ-w-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2011-12-14 9:14 ` Oliver Neukum
2011-12-14 9:41 ` Daniel Kurtz
2011-12-14 11:36 ` Oliver Neukum
2011-11-17 11:23 ` Daniel Kurtz [this message]
2011-12-14 8:01 ` [PATCH 3/3] HID: usbhid: defer LED setting to a workqueue Oliver Neukum
2011-12-14 8:19 ` Daniel Kurtz
2011-12-14 9:25 ` Oliver Neukum
[not found] ` <201112140901.35203.oneukum-l3A5Bk7waGM@public.gmane.org>
2011-12-14 10:33 ` Daniel Kurtz
2011-12-14 11:22 ` Oliver Neukum
2011-12-15 6:43 ` Daniel Kurtz
2011-12-15 9:01 ` Oliver Neukum
[not found] ` <201112151001.52761.oneukum-l3A5Bk7waGM@public.gmane.org>
2011-12-20 10:12 ` Daniel Kurtz
2011-12-20 10:18 ` Oliver Neukum
2011-12-21 10:19 ` Jiri Kosina
[not found] ` <1321529030-7845-1-git-send-email-djkurtz-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org>
2011-12-07 2:42 ` [PATCH 0/3 v2] usb/hid-core: drain URB queue when going to suspend Daniel Kurtz
2011-12-07 9:27 ` Jiri Kosina
-- strict thread matches above, loose matches on Subject: below --
2011-11-01 9:25 [PATCH 0/3] " Daniel Kurtz
2011-11-01 9:25 ` [PATCH 3/3] HID: usbhid: defer LED setting to a workqueue Daniel Kurtz
2011-11-07 14:48 ` Oliver Neukum
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1321529030-7845-4-git-send-email-djkurtz@chromium.org \
--to=djkurtz@chromium.org \
--cc=bleung@chromium.org \
--cc=jkosina@suse.cz \
--cc=linux-input@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-usb@vger.kernel.org \
--cc=olofj@chromium.org \
--cc=oneukum@suse.de \
--cc=stern@rowland.harvard.edu \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).