diff options
| author | Frank Praznik <frank.praznik@oh.rr.com> | 2014-01-27 10:17:36 -0500 |
|---|---|---|
| committer | Jiri Kosina <jkosina@suse.cz> | 2014-01-28 14:37:45 -0500 |
| commit | d902f4724ccd0c6b8f2b0a7d22af428c637b6dda (patch) | |
| tree | 6c10a4adbefaf51b07265713de5b772bf32c01fa | |
| parent | 4988abf1749241bc80600a6b3283d03898d2717c (diff) | |
HID: sony: add battery status reporting for the Sixaxis and Dualshock 4
Add battery status reporting for the Sixaxis and Dualshock 4 controllers.
Signed-off-by: Frank Praznik <frank.praznik@oh.rr.com>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
| -rw-r--r-- | drivers/hid/hid-sony.c | 186 |
1 files changed, 185 insertions, 1 deletions
diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c index 12354055d474..04fd611d3099 100644 --- a/drivers/hid/hid-sony.c +++ b/drivers/hid/hid-sony.c | |||
| @@ -30,6 +30,8 @@ | |||
| 30 | #include <linux/slab.h> | 30 | #include <linux/slab.h> |
| 31 | #include <linux/usb.h> | 31 | #include <linux/usb.h> |
| 32 | #include <linux/leds.h> | 32 | #include <linux/leds.h> |
| 33 | #include <linux/power_supply.h> | ||
| 34 | #include <linux/spinlock.h> | ||
| 33 | 35 | ||
| 34 | #include "hid-ids.h" | 36 | #include "hid-ids.h" |
| 35 | 37 | ||
| @@ -42,6 +44,7 @@ | |||
| 42 | #define DUALSHOCK4_CONTROLLER_BT BIT(6) | 44 | #define DUALSHOCK4_CONTROLLER_BT BIT(6) |
| 43 | 45 | ||
| 44 | #define SONY_LED_SUPPORT (SIXAXIS_CONTROLLER_USB | BUZZ_CONTROLLER | DUALSHOCK4_CONTROLLER_USB) | 46 | #define SONY_LED_SUPPORT (SIXAXIS_CONTROLLER_USB | BUZZ_CONTROLLER | DUALSHOCK4_CONTROLLER_USB) |
| 47 | #define SONY_BATTERY_SUPPORT (SIXAXIS_CONTROLLER_USB | SIXAXIS_CONTROLLER_BT | DUALSHOCK4_CONTROLLER_USB) | ||
| 45 | 48 | ||
| 46 | #define MAX_LEDS 4 | 49 | #define MAX_LEDS 4 |
| 47 | 50 | ||
| @@ -487,18 +490,30 @@ static const unsigned int buzz_keymap[] = { | |||
| 487 | [20] = BTN_TRIGGER_HAPPY20, | 490 | [20] = BTN_TRIGGER_HAPPY20, |
| 488 | }; | 491 | }; |
| 489 | 492 | ||
| 493 | static enum power_supply_property sony_battery_props[] = { | ||
| 494 | POWER_SUPPLY_PROP_PRESENT, | ||
| 495 | POWER_SUPPLY_PROP_CAPACITY, | ||
| 496 | POWER_SUPPLY_PROP_SCOPE, | ||
| 497 | POWER_SUPPLY_PROP_STATUS, | ||
| 498 | }; | ||
| 499 | |||
| 490 | struct sony_sc { | 500 | struct sony_sc { |
| 501 | spinlock_t lock; | ||
| 491 | struct hid_device *hdev; | 502 | struct hid_device *hdev; |
| 492 | struct led_classdev *leds[MAX_LEDS]; | 503 | struct led_classdev *leds[MAX_LEDS]; |
| 493 | struct hid_report *output_report; | 504 | struct hid_report *output_report; |
| 494 | unsigned long quirks; | 505 | unsigned long quirks; |
| 495 | struct work_struct state_worker; | 506 | struct work_struct state_worker; |
| 507 | struct power_supply battery; | ||
| 496 | 508 | ||
| 497 | #ifdef CONFIG_SONY_FF | 509 | #ifdef CONFIG_SONY_FF |
| 498 | __u8 left; | 510 | __u8 left; |
| 499 | __u8 right; | 511 | __u8 right; |
| 500 | #endif | 512 | #endif |
| 501 | 513 | ||
| 514 | __u8 cable_state; | ||
| 515 | __u8 battery_charging; | ||
| 516 | __u8 battery_capacity; | ||
| 502 | __u8 led_state[MAX_LEDS]; | 517 | __u8 led_state[MAX_LEDS]; |
| 503 | __u8 led_count; | 518 | __u8 led_count; |
| 504 | }; | 519 | }; |
| @@ -599,6 +614,63 @@ static __u8 *sony_report_fixup(struct hid_device *hdev, __u8 *rdesc, | |||
| 599 | return rdesc; | 614 | return rdesc; |
| 600 | } | 615 | } |
| 601 | 616 | ||
| 617 | static void sixaxis_parse_report(struct sony_sc *sc, __u8 *rd, int size) | ||
| 618 | { | ||
| 619 | static const __u8 sixaxis_battery_capacity[] = { 0, 1, 25, 50, 75, 100 }; | ||
| 620 | unsigned long flags; | ||
| 621 | __u8 cable_state, battery_capacity, battery_charging; | ||
| 622 | |||
| 623 | /* The sixaxis is charging if the battery value is 0xee | ||
| 624 | * and it is fully charged if the value is 0xef. | ||
| 625 | * It does not report the actual level while charging so it | ||
| 626 | * is set to 100% while charging is in progress. | ||
| 627 | */ | ||
| 628 | if (rd[30] >= 0xee) { | ||
| 629 | battery_capacity = 100; | ||
| 630 | battery_charging = rd[30] & 0x01; | ||
| 631 | } else { | ||
| 632 | battery_capacity = sixaxis_battery_capacity[rd[30]]; | ||
| 633 | battery_charging = 0; | ||
| 634 | } | ||
| 635 | cable_state = (rd[31] >> 4) & 0x01; | ||
| 636 | |||
| 637 | spin_lock_irqsave(&sc->lock, flags); | ||
| 638 | sc->cable_state = cable_state; | ||
| 639 | sc->battery_capacity = battery_capacity; | ||
| 640 | sc->battery_charging = battery_charging; | ||
| 641 | spin_unlock_irqrestore(&sc->lock, flags); | ||
| 642 | } | ||
| 643 | |||
| 644 | static void dualshock4_parse_report(struct sony_sc *sc, __u8 *rd, int size) | ||
| 645 | { | ||
| 646 | unsigned long flags; | ||
| 647 | __u8 cable_state, battery_capacity, battery_charging; | ||
| 648 | |||
| 649 | /* The lower 4 bits of byte 30 contain the battery level | ||
| 650 | * and the 5th bit contains the USB cable state. | ||
| 651 | */ | ||
| 652 | cable_state = (rd[30] >> 4) & 0x01; | ||
| 653 | battery_capacity = rd[30] & 0x0F; | ||
| 654 | |||
| 655 | /* On USB the Dualshock 4 battery level goes from 0 to 11. | ||
| 656 | * A battery level of 11 means fully charged. | ||
| 657 | */ | ||
| 658 | if (cable_state && battery_capacity == 11) | ||
| 659 | battery_charging = 0; | ||
| 660 | else | ||
| 661 | battery_charging = 1; | ||
| 662 | |||
| 663 | if (battery_capacity > 10) | ||
| 664 | battery_capacity--; | ||
| 665 | battery_capacity *= 10; | ||
| 666 | |||
| 667 | spin_lock_irqsave(&sc->lock, flags); | ||
| 668 | sc->cable_state = cable_state; | ||
| 669 | sc->battery_capacity = battery_capacity; | ||
| 670 | sc->battery_charging = battery_charging; | ||
| 671 | spin_unlock_irqrestore(&sc->lock, flags); | ||
| 672 | } | ||
| 673 | |||
| 602 | static int sony_raw_event(struct hid_device *hdev, struct hid_report *report, | 674 | static int sony_raw_event(struct hid_device *hdev, struct hid_report *report, |
| 603 | __u8 *rd, int size) | 675 | __u8 *rd, int size) |
| 604 | { | 676 | { |
| @@ -613,6 +685,11 @@ static int sony_raw_event(struct hid_device *hdev, struct hid_report *report, | |||
| 613 | swap(rd[43], rd[44]); | 685 | swap(rd[43], rd[44]); |
| 614 | swap(rd[45], rd[46]); | 686 | swap(rd[45], rd[46]); |
| 615 | swap(rd[47], rd[48]); | 687 | swap(rd[47], rd[48]); |
| 688 | |||
| 689 | sixaxis_parse_report(sc, rd, size); | ||
| 690 | } else if ((sc->quirks & DUALSHOCK4_CONTROLLER_USB) && rd[0] == 0x01 && | ||
| 691 | size == 64) { | ||
| 692 | dualshock4_parse_report(sc, rd, size); | ||
| 616 | } | 693 | } |
| 617 | 694 | ||
| 618 | return 0; | 695 | return 0; |
| @@ -1011,6 +1088,91 @@ static void sony_destroy_ff(struct hid_device *hdev) | |||
| 1011 | } | 1088 | } |
| 1012 | #endif | 1089 | #endif |
| 1013 | 1090 | ||
| 1091 | static int sony_battery_get_property(struct power_supply *psy, | ||
| 1092 | enum power_supply_property psp, | ||
| 1093 | union power_supply_propval *val) | ||
| 1094 | { | ||
| 1095 | struct sony_sc *sc = container_of(psy, struct sony_sc, battery); | ||
| 1096 | unsigned long flags; | ||
| 1097 | int ret = 0; | ||
| 1098 | u8 battery_charging, battery_capacity, cable_state; | ||
| 1099 | |||
| 1100 | spin_lock_irqsave(&sc->lock, flags); | ||
| 1101 | battery_charging = sc->battery_charging; | ||
| 1102 | battery_capacity = sc->battery_capacity; | ||
| 1103 | cable_state = sc->cable_state; | ||
| 1104 | spin_unlock_irqrestore(&sc->lock, flags); | ||
| 1105 | |||
| 1106 | switch (psp) { | ||
| 1107 | case POWER_SUPPLY_PROP_PRESENT: | ||
| 1108 | val->intval = 1; | ||
| 1109 | break; | ||
| 1110 | case POWER_SUPPLY_PROP_SCOPE: | ||
| 1111 | val->intval = POWER_SUPPLY_SCOPE_DEVICE; | ||
| 1112 | break; | ||
| 1113 | case POWER_SUPPLY_PROP_CAPACITY: | ||
| 1114 | val->intval = battery_capacity; | ||
| 1115 | break; | ||
| 1116 | case POWER_SUPPLY_PROP_STATUS: | ||
| 1117 | if (battery_charging) | ||
| 1118 | val->intval = POWER_SUPPLY_STATUS_CHARGING; | ||
| 1119 | else | ||
| 1120 | if (battery_capacity == 100 && cable_state) | ||
| 1121 | val->intval = POWER_SUPPLY_STATUS_FULL; | ||
| 1122 | else | ||
| 1123 | val->intval = POWER_SUPPLY_STATUS_DISCHARGING; | ||
| 1124 | break; | ||
| 1125 | default: | ||
| 1126 | ret = -EINVAL; | ||
| 1127 | break; | ||
| 1128 | } | ||
| 1129 | return ret; | ||
| 1130 | } | ||
| 1131 | |||
| 1132 | static int sony_battery_probe(struct sony_sc *sc) | ||
| 1133 | { | ||
| 1134 | static atomic_t power_id_seq = ATOMIC_INIT(0); | ||
| 1135 | unsigned long power_id; | ||
| 1136 | struct hid_device *hdev = sc->hdev; | ||
| 1137 | int ret; | ||
| 1138 | |||
| 1139 | power_id = (unsigned long)atomic_inc_return(&power_id_seq); | ||
| 1140 | |||
| 1141 | sc->battery.properties = sony_battery_props; | ||
| 1142 | sc->battery.num_properties = ARRAY_SIZE(sony_battery_props); | ||
| 1143 | sc->battery.get_pro | ||
