Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 282326
b: refs/heads/master
c: 4371ea8
h: refs/heads/master
v: v3
  • Loading branch information
Daniel Kurtz authored and Jiri Kosina committed Dec 21, 2011
1 parent 673c4f7 commit bc4b4a1
Show file tree
Hide file tree
Showing 5 changed files with 83 additions and 12 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: f0befcd64bc57e6a0b7a96c37c55f79e6b999af7
refs/heads/master: 4371ea8202e98c8ef77ca887de3b19affbb3498f
42 changes: 42 additions & 0 deletions trunk/drivers/hid/hid-input.c
Original file line number Diff line number Diff line change
Expand Up @@ -976,6 +976,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);
Expand Down
47 changes: 36 additions & 11 deletions trunk/drivers/hid/usbhid/hid-core.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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;
}
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down
2 changes: 2 additions & 0 deletions trunk/drivers/hid/usbhid/usbhid.h
Original file line number Diff line number Diff line change
Expand Up @@ -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) \
Expand Down
2 changes: 2 additions & 0 deletions trunk/include/linux/hid.h
Original file line number Diff line number Diff line change
Expand Up @@ -727,6 +727,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);
Expand Down

0 comments on commit bc4b4a1

Please sign in to comment.