diff options
author | Frank Praznik <frank.praznik@oh.rr.com> | 2014-01-11 15:13:15 -0500 |
---|---|---|
committer | Jiri Kosina <jkosina@suse.cz> | 2014-01-16 16:44:35 -0500 |
commit | 60781cf487e3aae5134b5fec85fe60ece31ff627 (patch) | |
tree | 9f89d7489e844cedfbb17539e834ffcfff51c5c1 /drivers/hid | |
parent | 0bd88dd3dd5e73e5d43b8d1e7a96b841978df562 (diff) |
HID: sony: Add LED controls for the Dualshock 4
Add LED lightbar controls for the Dualshock 4.
The Dualshock 4 light bar has 3 separate RGB LEDs that can range in
brightness from 0 to 255 so a full byte is now needed to store each LED's
state
Changed the module to support an arbitrary number of LEDs instead of being
hardcoded to 4.
Signed-off-by: Frank Praznik <frank.praznik@oh.rr.com>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
Diffstat (limited to 'drivers/hid')
-rw-r--r-- | drivers/hid/hid-sony.c | 77 |
1 files changed, 50 insertions, 27 deletions
diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c index 8020d10ce703..79e0d5808a92 100644 --- a/drivers/hid/hid-sony.c +++ b/drivers/hid/hid-sony.c | |||
@@ -40,7 +40,9 @@ | |||
40 | #define PS3REMOTE BIT(4) | 40 | #define PS3REMOTE BIT(4) |
41 | #define DUALSHOCK4_CONTROLLER BIT(5) | 41 | #define DUALSHOCK4_CONTROLLER BIT(5) |
42 | 42 | ||
43 | #define SONY_LED_SUPPORT (SIXAXIS_CONTROLLER_USB | BUZZ_CONTROLLER) | 43 | #define SONY_LED_SUPPORT (SIXAXIS_CONTROLLER_USB | BUZZ_CONTROLLER | DUALSHOCK4_CONTROLLER) |
44 | |||
45 | #define MAX_LEDS 4 | ||
44 | 46 | ||
45 | static const u8 sixaxis_rdesc_fixup[] = { | 47 | static const u8 sixaxis_rdesc_fixup[] = { |
46 | 0x95, 0x13, 0x09, 0x01, 0x81, 0x02, 0x95, 0x0C, | 48 | 0x95, 0x13, 0x09, 0x01, 0x81, 0x02, 0x95, 0x0C, |
@@ -227,7 +229,7 @@ static const unsigned int buzz_keymap[] = { | |||
227 | 229 | ||
228 | struct sony_sc { | 230 | struct sony_sc { |
229 | struct hid_device *hdev; | 231 | struct hid_device *hdev; |
230 | struct led_classdev *leds[4]; | 232 | struct led_classdev *leds[MAX_LEDS]; |
231 | unsigned long quirks; | 233 | unsigned long quirks; |
232 | struct work_struct state_worker; | 234 | struct work_struct state_worker; |
233 | 235 | ||
@@ -236,7 +238,8 @@ struct sony_sc { | |||
236 | __u8 right; | 238 | __u8 right; |
237 | #endif | 239 | #endif |
238 | 240 | ||
239 | __u8 led_state; | 241 | __u8 led_state[MAX_LEDS]; |
242 | __u8 led_count; | ||
240 | }; | 243 | }; |
241 | 244 | ||
242 | static __u8 *ps3remote_fixup(struct hid_device *hdev, __u8 *rdesc, | 245 | static __u8 *ps3remote_fixup(struct hid_device *hdev, __u8 *rdesc, |
@@ -447,7 +450,7 @@ static int sixaxis_set_operational_bt(struct hid_device *hdev) | |||
447 | return hdev->hid_output_raw_report(hdev, buf, sizeof(buf), HID_FEATURE_REPORT); | 450 | return hdev->hid_output_raw_report(hdev, buf, sizeof(buf), HID_FEATURE_REPORT); |
448 | } | 451 | } |
449 | 452 | ||
450 | static void buzz_set_leds(struct hid_device *hdev, int leds) | 453 | static void buzz_set_leds(struct hid_device *hdev, const __u8 *leds) |
451 | { | 454 | { |
452 | struct list_head *report_list = | 455 | struct list_head *report_list = |
453 | &hdev->report_enum[HID_OUTPUT_REPORT].report_list; | 456 | &hdev->report_enum[HID_OUTPUT_REPORT].report_list; |
@@ -456,23 +459,28 @@ static void buzz_set_leds(struct hid_device *hdev, int leds) | |||
456 | __s32 *value = report->field[0]->value; | 459 | __s32 *value = report->field[0]->value; |
457 | 460 | ||
458 | value[0] = 0x00; | 461 | value[0] = 0x00; |
459 | value[1] = (leds & 1) ? 0xff : 0x00; | 462 | value[1] = leds[0] ? 0xff : 0x00; |
460 | value[2] = (leds & 2) ? 0xff : 0x00; | 463 | value[2] = leds[1] ? 0xff : 0x00; |
461 | value[3] = (leds & 4) ? 0xff : 0x00; | 464 | value[3] = leds[2] ? 0xff : 0x00; |
462 | value[4] = (leds & 8) ? 0xff : 0x00; | 465 | value[4] = leds[3] ? 0xff : 0x00; |
463 | value[5] = 0x00; | 466 | value[5] = 0x00; |
464 | value[6] = 0x00; | 467 | value[6] = 0x00; |
465 | hid_hw_request(hdev, report, HID_REQ_SET_REPORT); | 468 | hid_hw_request(hdev, report, HID_REQ_SET_REPORT); |
466 | } | 469 | } |
467 | 470 | ||
468 | static void sony_set_leds(struct hid_device *hdev, __u8 leds) | 471 | static void sony_set_leds(struct hid_device *hdev, const __u8 *leds, int count) |
469 | { | 472 | { |
470 | struct sony_sc *drv_data = hid_get_drvdata(hdev); | 473 | struct sony_sc *drv_data = hid_get_drvdata(hdev); |
474 | int n; | ||
471 | 475 | ||
472 | if (drv_data->quirks & BUZZ_CONTROLLER) { | 476 | BUG_ON(count > MAX_LEDS); |
477 | |||
478 | if (drv_data->quirks & BUZZ_CONTROLLER && count == 4) { | ||
473 | buzz_set_leds(hdev, leds); | 479 | buzz_set_leds(hdev, leds); |
474 | } else if (drv_data->quirks & SIXAXIS_CONTROLLER_USB) { | 480 | } else if ((drv_data->quirks & SIXAXIS_CONTROLLER_USB) || |
475 | drv_data->led_state = leds; | 481 | (drv_data->quirks & DUALSHOCK4_CONTROLLER)) { |
482 | for (n = 0; n < count; n++) | ||
483 | drv_data->led_state[n] = leds[n]; | ||
476 | schedule_work(&drv_data->state_worker); | 484 | schedule_work(&drv_data->state_worker); |
477 | } | 485 | } |
478 | } | 486 | } |
@@ -492,15 +500,11 @@ static void sony_led_set_brightness(struct led_classdev *led, | |||
492 | return; | 500 | return; |
493 | } | 501 | } |
494 | 502 | ||
495 | for (n = 0; n < 4; n++) { | 503 | for (n = 0; n < drv_data->led_count; n++) { |
496 | if (led == drv_data->leds[n]) { | 504 | if (led == drv_data->leds[n]) { |
497 | int on = !!(drv_data->led_state & (1 << n)); | 505 | if (value != drv_data->led_state[n]) { |
498 | if (value == LED_OFF && on) { | 506 | drv_data->led_state[n] = value; |
499 | drv_data->led_state &= ~(1 << n); | 507 | sony_set_leds(hdev, drv_data->led_state, drv_data->led_count); |
500 | sony_set_leds(hdev, drv_data->led_state); | ||
501 | } else if (value != LED_OFF && !on) { | ||
502 | drv_data->led_state |= (1 << n); | ||
503 | sony_set_leds(hdev, drv_data->led_state); | ||
504 | } | 508 | } |
505 | break; | 509 | break; |
506 | } | 510 | } |
@@ -522,9 +526,9 @@ static enum led_brightness sony_led_get_brightness(struct led_classdev *led) | |||
522 | return LED_OFF; | 526 | return LED_OFF; |
523 | } | 527 | } |
524 | 528 | ||
525 | for (n = 0; n < 4; n++) { | 529 | for (n = 0; n < drv_data->led_count; n++) { |
526 | if (led == drv_data->leds[n]) { | 530 | if (led == drv_data->leds[n]) { |
527 | on = !!(drv_data->led_state & (1 << n)); | 531 | on = !!(drv_data->led_state[n]); |
528 | break; | 532 | break; |
529 | } | 533 | } |
530 | } | 534 | } |
@@ -541,7 +545,7 @@ static void sony_leds_remove(struct hid_device *hdev) | |||
541 | drv_data = hid_get_drvdata(hdev); | 545 | drv_data = hid_get_drvdata(hdev); |
542 | BUG_ON(!(drv_data->quirks & SONY_LED_SUPPORT)); | 546 | BUG_ON(!(drv_data->quirks & SONY_LED_SUPPORT)); |
543 | 547 | ||
544 | for (n = 0; n < 4; n++) { | 548 | for (n = 0; n < drv_data->led_count; n++) { |
545 | led = drv_data->leds[n]; | 549 | led = drv_data->leds[n]; |
546 | drv_data->leds[n] = NULL; | 550 | drv_data->leds[n] = NULL; |
547 | if (!led) | 551 | if (!led) |
@@ -549,17 +553,21 @@ static void sony_leds_remove(struct hid_device *hdev) | |||
549 | led_classdev_unregister(led); | 553 | led_classdev_unregister(led); |
550 | kfree(led); | 554 | kfree(led); |
551 | } | 555 | } |
556 | |||
557 | drv_data->led_count = 0; | ||
552 | } | 558 | } |
553 | 559 | ||
554 | static int sony_leds_init(struct hid_device *hdev) | 560 | static int sony_leds_init(struct hid_device *hdev) |
555 | { | 561 | { |
556 | struct sony_sc *drv_data; | 562 | struct sony_sc *drv_data; |
557 | int n, ret = 0; | 563 | int n, ret = 0; |
564 | int max_brightness; | ||
558 | struct led_classdev *led; | 565 | struct led_classdev *led; |
559 | size_t name_sz; | 566 | size_t name_sz; |
560 | char *name; | 567 | char *name; |
561 | size_t name_len; | 568 | size_t name_len; |
562 | const char *name_fmt; | 569 | const char *name_fmt; |
570 | static const __u8 initial_values[MAX_LEDS] = { 0x00, 0x00, 0x00, 0x00 }; | ||
563 | 571 | ||
564 | drv_data = hid_get_drvdata(hdev); | 572 | drv_data = hid_get_drvdata(hdev); |
565 | BUG_ON(!(drv_data->quirks & SONY_LED_SUPPORT)); | 573 | BUG_ON(!(drv_data->quirks & SONY_LED_SUPPORT)); |
@@ -575,14 +583,22 @@ static int sony_leds_init(struct hid_device *hdev) | |||
575 | name_fmt = "%s::sony%d"; | 583 | name_fmt = "%s::sony%d"; |
576 | } | 584 | } |
577 | 585 | ||
586 | if (drv_data->quirks & DUALSHOCK4_CONTROLLER) { | ||
587 | drv_data->led_count = 3; | ||
588 | max_brightness = 255; | ||
589 | } else { | ||
590 | drv_data->led_count = 4; | ||
591 | max_brightness = 1; | ||
592 | } | ||
593 | |||
578 | /* Clear LEDs as we have no way of reading their initial state. This is | 594 | /* Clear LEDs as we have no way of reading their initial state. This is |
579 | * only relevant if the driver is loaded after somebody actively set the | 595 | * only relevant if the driver is loaded after somebody actively set the |
580 | * LEDs to on */ | 596 | * LEDs to on */ |
581 | sony_set_leds(hdev, 0x00); | 597 | sony_set_leds(hdev, initial_values, drv_data->led_count); |
582 | 598 | ||
583 | name_sz = strlen(dev_name(&hdev->dev)) + name_len + 1; | 599 | name_sz = strlen(dev_name(&hdev->dev)) + name_len + 1; |
584 | 600 | ||
585 | for (n = 0; n < 4; n++) { | 601 | for (n = 0; n < drv_data->led_count; n++) { |
586 | led = kzalloc(sizeof(struct led_classdev) + name_sz, GFP_KERNEL); | 602 | led = kzalloc(sizeof(struct led_classdev) + name_sz, GFP_KERNEL); |
587 | if (!led) { | 603 | if (!led) { |
588 | hid_err(hdev, "Couldn't allocate memory for LED %d\n", n); | 604 | hid_err(hdev, "Couldn't allocate memory for LED %d\n", n); |
@@ -594,7 +610,7 @@ static int sony_leds_init(struct hid_device *hdev) | |||
594 | snprintf(name, name_sz, name_fmt, dev_name(&hdev->dev), n + 1); | 610 | snprintf(name, name_sz, name_fmt, dev_name(&hdev->dev), n + 1); |
595 | led->name = name; | 611 | led->name = name; |
596 | led->brightness = 0; | 612 | led->brightness = 0; |
597 | led->max_brightness = 1; | 613 | led->max_brightness = max_brightness; |
598 | led->brightness_get = sony_led_get_brightness; | 614 | led->brightness_get = sony_led_get_brightness; |
599 | led->brightness_set = sony_led_set_brightness; | 615 | led->brightness_set = sony_led_set_brightness; |
600 | 616 | ||
@@ -635,7 +651,10 @@ static void sony_state_worker(struct work_struct *work) | |||
635 | buf[5] = sc->left; | 651 | buf[5] = sc->left; |
636 | #endif | 652 | #endif |
637 | 653 | ||
638 | buf[10] |= (sc->led_state & 0xf) << 1; | 654 | buf[10] |= sc->led_state[0] << 1; |
655 | buf[10] |= sc->led_state[1] << 2; | ||
656 | buf[10] |= sc->led_state[2] << 3; | ||
657 | buf[10] |= sc->led_state[3] << 4; | ||
639 | 658 | ||
640 | sc->hdev->hid_output_raw_report(sc->hdev, buf, sizeof(buf), | 659 | sc->hdev->hid_output_raw_report(sc->hdev, buf, sizeof(buf), |
641 | HID_OUTPUT_REPORT); | 660 | HID_OUTPUT_REPORT); |
@@ -660,6 +679,10 @@ static void dualshock4_state_worker(struct work_struct *work) | |||
660 | buf[5] = sc->left; | 679 | buf[5] = sc->left; |
661 | #endif | 680 | #endif |
662 | 681 | ||
682 | buf[6] = sc->led_state[0]; | ||
683 | buf[7] = sc->led_state[1]; | ||
684 | buf[8] = sc->led_state[2]; | ||
685 | |||
663 | sc->hdev->hid_output_raw_report(sc->hdev, buf, sizeof(buf), | 686 | sc->hdev->hid_output_raw_report(sc->hdev, buf, sizeof(buf), |
664 | HID_OUTPUT_REPORT); | 687 | HID_OUTPUT_REPORT); |
665 | } | 688 | } |