aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/hid
diff options
context:
space:
mode:
authorSimon Wood <simon@mungewell.org>2012-04-21 08:41:15 -0400
committerJiri Kosina <jkosina@suse.cz>2012-04-23 14:52:15 -0400
commit22bcefdc8838186f2253edbfd4a0c22cb85d030e (patch)
tree44bdf59337c3a4e6a2d91948b4f9fa6fc75889da /drivers/hid
parent3b6b17b7330c0fab7d8442b6d225bee905df3745 (diff)
HID: hid-lg4ff: Add support for G27 LEDs
This patch adds supports for controlling the LED 'tachometer' on the G27 wheel, via the LED subsystem. The 5 LEDs are arranged from right (1=grn, 2=grn, 3=yel, 4=yel, 5=red) and 'mirrored' to the left (10 LEDs in total). Signed-off-by: Simon Wood <simon@mungewell.org> Signed-off-by: Jiri Kosina <jkosina@suse.cz>
Diffstat (limited to 'drivers/hid')
-rw-r--r--drivers/hid/hid-lg4ff.c158
1 files changed, 157 insertions, 1 deletions
diff --git a/drivers/hid/hid-lg4ff.c b/drivers/hid/hid-lg4ff.c
index 32c173fcb7f8..1e942a014038 100644
--- a/drivers/hid/hid-lg4ff.c
+++ b/drivers/hid/hid-lg4ff.c
@@ -55,7 +55,10 @@ struct lg4ff_device_entry {
55 __u16 range; 55 __u16 range;
56 __u16 min_range; 56 __u16 min_range;
57 __u16 max_range; 57 __u16 max_range;
58 __u8 leds; 58#ifdef CONFIG_LEDS_CLASS
59 __u8 led_state;
60 struct led_classdev *led[5];
61#endif
59 struct list_head list; 62 struct list_head list;
60 void (*set_range)(struct hid_device *hid, u16 range); 63 void (*set_range)(struct hid_device *hid, u16 range);
61}; 64};
@@ -335,6 +338,88 @@ static ssize_t lg4ff_range_store(struct device *dev, struct device_attribute *at
335 return count; 338 return count;
336} 339}
337 340
341#ifdef CONFIG_LEDS_CLASS
342static void lg4ff_set_leds(struct hid_device *hid, __u8 leds)
343{
344 struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
345 struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
346
347 report->field[0]->value[0] = 0xf8;
348 report->field[0]->value[1] = 0x12;
349 report->field[0]->value[2] = leds;
350 report->field[0]->value[3] = 0x00;
351 report->field[0]->value[4] = 0x00;
352 report->field[0]->value[5] = 0x00;
353 report->field[0]->value[6] = 0x00;
354 usbhid_submit_report(hid, report, USB_DIR_OUT);
355}
356
357static void lg4ff_led_set_brightness(struct led_classdev *led_cdev,
358 enum led_brightness value)
359{
360 struct device *dev = led_cdev->dev->parent;
361 struct hid_device *hid = container_of(dev, struct hid_device, dev);
362 struct lg_drv_data *drv_data = (struct lg_drv_data *)hid_get_drvdata(hid);
363 struct lg4ff_device_entry *entry;
364 int i, state = 0;
365
366 if (!drv_data) {
367 hid_err(hid, "Device data not found.");
368 return;
369 }
370
371 entry = (struct lg4ff_device_entry *)drv_data->device_props;
372
373 if (!entry) {
374 hid_err(hid, "Device properties not found.");
375 return;
376 }
377
378 for (i = 0; i < 5; i++) {
379 if (led_cdev != entry->led[i])
380 continue;
381 state = (entry->led_state >> i) & 1;
382 if (value == LED_OFF && state) {
383 entry->led_state &= ~(1 << i);
384 lg4ff_set_leds(hid, entry->led_state);
385 } else if (value != LED_OFF && !state) {
386 entry->led_state |= 1 << i;
387 lg4ff_set_leds(hid, entry->led_state);
388 }
389 break;
390 }
391}
392
393static enum led_brightness lg4ff_led_get_brightness(struct led_classdev *led_cdev)
394{
395 struct device *dev = led_cdev->dev->parent;
396 struct hid_device *hid = container_of(dev, struct hid_device, dev);
397 struct lg_drv_data *drv_data = (struct lg_drv_data *)hid_get_drvdata(hid);
398 struct lg4ff_device_entry *entry;
399 int i, value = 0;
400
401 if (!drv_data) {
402 hid_err(hid, "Device data not found.");
403 return LED_OFF;
404 }
405
406 entry = (struct lg4ff_device_entry *)drv_data->device_props;
407
408 if (!entry) {
409 hid_err(hid, "Device properties not found.");
410 return LED_OFF;
411 }
412
413 for (i = 0; i < 5; i++)
414 if (led_cdev == entry->led[i]) {
415 value = (entry->led_state >> i) & 1;
416 break;
417 }
418
419 return value ? LED_FULL : LED_OFF;
420}
421#endif
422
338int lg4ff_init(struct hid_device *hid) 423int lg4ff_init(struct hid_device *hid)
339{ 424{
340 struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list); 425 struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list);
@@ -453,6 +538,58 @@ int lg4ff_init(struct hid_device *hid)
453 if (entry->set_range != NULL) 538 if (entry->set_range != NULL)
454 entry->set_range(hid, entry->range); 539 entry->set_range(hid, entry->range);
455 540
541#ifdef CONFIG_LEDS_CLASS
542 /* register led subsystem - G27 only */
543 entry->led_state = 0;
544 for (j = 0; j < 5; j++)
545 entry->led[j] = NULL;
546
547 if (lg4ff_devices[i].product_id == USB_DEVICE_ID_LOGITECH_G27_WHEEL) {
548 struct led_classdev *led;
549 size_t name_sz;
550 char *name;
551
552 lg4ff_set_leds(hid, 0);
553
554 name_sz = strlen(dev_name(&hid->dev)) + 8;
555
556 for (j = 0; j < 5; j++) {
557 led = kzalloc(sizeof(struct led_classdev)+name_sz, GFP_KERNEL);
558 if (!led) {
559 hid_err(hid, "can't allocate memory for LED %d\n", j);
560 goto err;
561 }
562
563 name = (void *)(&led[1]);
564 snprintf(name, name_sz, "%s::RPM%d", dev_name(&hid->dev), j+1);
565 led->name = name;
566 led->brightness = 0;
567 led->max_brightness = 1;
568 led->brightness_get = lg4ff_led_get_brightness;
569 led->brightness_set = lg4ff_led_set_brightness;
570
571 entry->led[j] = led;
572 error = led_classdev_register(&hid->dev, led);
573
574 if (error) {
575 hid_err(hid, "failed to register LED %d. Aborting.\n", j);
576err:
577 /* Deregister LEDs (if any) */
578 for (j = 0; j < 5; j++) {
579 led = entry->led[j];
580 entry->led[j] = NULL;
581 if (!led)
582 continue;
583 led_classdev_unregister(led);
584 kfree(led);
585 }
586 goto out; /* Let the driver continue without LEDs */
587 }
588 }
589 }
590#endif
591
592out:
456 hid_info(hid, "Force feedback for Logitech Speed Force Wireless by Simon Wood <simon@mungewell.org>\n"); 593 hid_info(hid, "Force feedback for Logitech Speed Force Wireless by Simon Wood <simon@mungewell.org>\n");
457 return 0; 594 return 0;
458} 595}
@@ -474,6 +611,25 @@ int lg4ff_deinit(struct hid_device *hid)
474 hid_err(hid, "Error while deinitializing device, no device properties data.\n"); 611 hid_err(hid, "Error while deinitializing device, no device properties data.\n");
475 return -1; 612 return -1;
476 } 613 }
614
615#ifdef CONFIG_LEDS_CLASS
616 {
617 int j;
618 struct led_classdev *led;
619
620 /* Deregister LEDs (if any) */
621 for (j = 0; j < 5; j++) {
622
623 led = entry->led[j];
624 entry->led[j] = NULL;
625 if (!led)
626 continue;
627 led_classdev_unregister(led);
628 kfree(led);
629 }
630 }
631#endif
632
477 /* Deallocate memory */ 633 /* Deallocate memory */
478 kfree(entry); 634 kfree(entry);
479 635