aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPeter Hutterer <peter.hutterer@who-t.net>2016-06-29 05:28:01 -0400
committerJiri Kosina <jkosina@suse.cz>2016-07-07 05:25:50 -0400
commit5a2b190cddb9aa69b9037f5b1fd1c2cc8a1d68b9 (patch)
treeb032a0cfcfc1a2de9460213dfbf4612296ce9d33
parent595d9e34eedc4b8d0631260ce93bbeb08e5b3bd7 (diff)
HID: logitech-hidpp: add battery support for HID++ 2.0 devices
If the 0x1000 Unified Battery Level Status feature exists, expose the battery level. The main drawback is that while a device is plugged in its battery level is 0. To avoid exposing that as 0% charge we make up a number based on the charging status. Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net> Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com> Signed-off-by: Jiri Kosina <jkosina@suse.cz>
-rw-r--r--drivers/hid/hid-logitech-hidpp.c238
1 files changed, 237 insertions, 1 deletions
diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c
index 2e2515a4c070..1ead9f697d8c 100644
--- a/drivers/hid/hid-logitech-hidpp.c
+++ b/drivers/hid/hid-logitech-hidpp.c
@@ -62,6 +62,8 @@ MODULE_PARM_DESC(disable_tap_to_click,
62#define HIDPP_QUIRK_WTP_PHYSICAL_BUTTONS BIT(22) 62#define HIDPP_QUIRK_WTP_PHYSICAL_BUTTONS BIT(22)
63#define HIDPP_QUIRK_NO_HIDINPUT BIT(23) 63#define HIDPP_QUIRK_NO_HIDINPUT BIT(23)
64#define HIDPP_QUIRK_FORCE_OUTPUT_REPORTS BIT(24) 64#define HIDPP_QUIRK_FORCE_OUTPUT_REPORTS BIT(24)
65#define HIDPP_QUIRK_HIDPP20_BATTERY BIT(25)
66#define HIDPP_QUIRK_HIDPP10_BATTERY BIT(26)
65 67
66#define HIDPP_QUIRK_DELAYED_INIT (HIDPP_QUIRK_NO_HIDINPUT | \ 68#define HIDPP_QUIRK_DELAYED_INIT (HIDPP_QUIRK_NO_HIDINPUT | \
67 HIDPP_QUIRK_CONNECT_EVENTS) 69 HIDPP_QUIRK_CONNECT_EVENTS)
@@ -110,6 +112,15 @@ struct hidpp_report {
110 }; 112 };
111} __packed; 113} __packed;
112 114
115struct hidpp_battery {
116 u8 feature_index;
117 struct power_supply_desc desc;
118 struct power_supply *ps;
119 char name[64];
120 int status;
121 int level;
122};
123
113struct hidpp_device { 124struct hidpp_device {
114 struct hid_device *hid_dev; 125 struct hid_device *hid_dev;
115 struct mutex send_mutex; 126 struct mutex send_mutex;
@@ -128,8 +139,9 @@ struct hidpp_device {
128 struct input_dev *delayed_input; 139 struct input_dev *delayed_input;
129 140
130 unsigned long quirks; 141 unsigned long quirks;
131};
132 142
143 struct hidpp_battery battery;
144};
133 145
134/* HID++ 1.0 error codes */ 146/* HID++ 1.0 error codes */
135#define HIDPP_ERROR 0x8f 147#define HIDPP_ERROR 0x8f
@@ -607,6 +619,222 @@ static char *hidpp_get_device_name(struct hidpp_device *hidpp)
607} 619}
608 620
609/* -------------------------------------------------------------------------- */ 621/* -------------------------------------------------------------------------- */
622/* 0x1000: Battery level status */
623/* -------------------------------------------------------------------------- */
624
625#define HIDPP_PAGE_BATTERY_LEVEL_STATUS 0x1000
626
627#define CMD_BATTERY_LEVEL_STATUS_GET_BATTERY_LEVEL_STATUS 0x00
628#define CMD_BATTERY_LEVEL_STATUS_GET_BATTERY_CAPABILITY 0x10
629
630#define EVENT_BATTERY_LEVEL_STATUS_BROADCAST 0x00
631
632static int hidpp20_batterylevel_map_status_level(u8 data[3], int *level,
633 int *next_level)
634{
635 int status;
636 int level_override;
637
638 *level = data[0];
639 *next_level = data[1];
640
641 /* When discharging, we can rely on the device reported level.
642 * For all other states the device reports level 0 (unknown). Make up
643 * a number instead
644 */
645 switch (data[2]) {
646 case 0: /* discharging (in use) */
647 status = POWER_SUPPLY_STATUS_DISCHARGING;
648 level_override = 0;
649 break;
650 case 1: /* recharging */
651 status = POWER_SUPPLY_STATUS_CHARGING;
652 level_override = 80;
653 break;
654 case 2: /* charge in final stage */
655 status = POWER_SUPPLY_STATUS_CHARGING;
656 level_override = 90;
657 break;
658 case 3: /* charge complete */
659 status = POWER_SUPPLY_STATUS_FULL;
660 level_override = 100;
661 break;
662 case 4: /* recharging below optimal speed */
663 status = POWER_SUPPLY_STATUS_CHARGING;
664 level_override = 50;
665 break;
666 /* 5 = invalid battery type
667 6 = thermal error
668 7 = other charging error */
669 default:
670 status = POWER_SUPPLY_STATUS_NOT_CHARGING;
671 level_override = 0;
672 break;
673 }
674
675 if (level_override != 0 && *level == 0)
676 *level = level_override;
677
678 return status;
679}
680
681static int hidpp20_batterylevel_get_battery_level(struct hidpp_device *hidpp,
682 u8 feature_index,
683 int *status,
684 int *level,
685 int *next_level)
686{
687 struct hidpp_report response;
688 int ret;
689 u8 *params = (u8 *)response.fap.params;
690
691 ret = hidpp_send_fap_command_sync(hidpp, feature_index,
692 CMD_BATTERY_LEVEL_STATUS_GET_BATTERY_LEVEL_STATUS,
693 NULL, 0, &response);
694 if (ret > 0) {
695 hid_err(hidpp->hid_dev, "%s: received protocol error 0x%02x\n",
696 __func__, ret);
697 return -EPROTO;
698 }
699 if (ret)
700 return ret;
701
702 *status = hidpp20_batterylevel_map_status_level(params, level,
703 next_level);
704
705 return 0;
706}
707
708static int hidpp20_query_battery_info(struct hidpp_device *hidpp)
709{
710 u8 feature_type;
711 int ret;
712 int status, level, next_level;
713
714 if (hidpp->battery.feature_index == 0) {
715 ret = hidpp_root_get_feature(hidpp,
716 HIDPP_PAGE_BATTERY_LEVEL_STATUS,
717 &hidpp->battery.feature_index,
718 &feature_type);
719 if (ret)
720 return ret;
721 }
722
723 ret = hidpp20_batterylevel_get_battery_level(hidpp,
724 hidpp->battery.feature_index,
725 &status, &level, &next_level);
726 if (ret)
727 return ret;
728
729 hidpp->battery.status = status;
730 hidpp->battery.level = level;
731
732 return 0;
733}
734
735static int hidpp20_battery_event(struct hidpp_device *hidpp,
736 u8 *data, int size)
737{
738 struct hidpp_report *report = (struct hidpp_report *)data;
739 int status, level, next_level;
740 bool changed;
741
742 if (report->fap.feature_index != hidpp->battery.feature_index ||
743 report->fap.funcindex_clientid != EVENT_BATTERY_LEVEL_STATUS_BROADCAST)
744 return 0;
745
746 status = hidpp20_batterylevel_map_status_level(report->fap.params,
747 &level, &next_level);
748
749 changed = level != hidpp->battery.level ||
750 status != hidpp->battery.status;
751
752 if (changed) {
753 hidpp->battery.level = level;
754 hidpp->battery.status = status;
755 if (hidpp->battery.ps)
756 power_supply_changed(hidpp->battery.ps);
757 }
758
759 return 0;
760}
761
762static enum power_supply_property hidpp_battery_props[] = {
763 POWER_SUPPLY_PROP_STATUS,
764 POWER_SUPPLY_PROP_CAPACITY,
765};
766
767static int hidpp_battery_get_property(struct power_supply *psy,
768 enum power_supply_property psp,
769 union power_supply_propval *val)
770{
771 struct hidpp_device *hidpp = power_supply_get_drvdata(psy);
772 int ret = 0;
773
774 switch(psp) {
775 case POWER_SUPPLY_PROP_STATUS:
776 val->intval = hidpp->battery.status;
777 break;
778 case POWER_SUPPLY_PROP_CAPACITY:
779 val->intval = hidpp->battery.level;
780 break;
781 default:
782 ret = -EINVAL;
783 break;
784 }
785
786 return ret;
787}
788
789static int hidpp20_initialize_battery(struct hidpp_device *hidpp)
790{
791 static atomic_t battery_no = ATOMIC_INIT(0);
792 struct power_supply_config cfg = { .drv_data = hidpp };
793 struct power_supply_desc *desc = &hidpp->battery.desc;
794 struct hidpp_battery *battery;
795 unsigned long n;
796 int ret;
797
798 ret = hidpp20_query_battery_info(hidpp);
799 if (ret)
800 return ret;
801
802 battery = &hidpp->battery;
803
804 n = atomic_inc_return(&battery_no) - 1;
805 desc->properties = hidpp_battery_props;
806 desc->num_properties = ARRAY_SIZE(hidpp_battery_props);
807 desc->get_property = hidpp_battery_get_property;
808 sprintf(battery->name, "hidpp_battery_%ld", n);
809 desc->name = battery->name;
810 desc->type = POWER_SUPPLY_TYPE_BATTERY;
811 desc->use_for_apm = 0;
812
813 battery->ps = devm_power_supply_register(&hidpp->hid_dev->dev,
814 &battery->desc,
815 &cfg);
816 if (IS_ERR(battery->ps))
817 return PTR_ERR(battery->ps);
818
819 power_supply_powers(battery->ps, &hidpp->hid_dev->dev);
820
821 return 0;
822}
823
824static int hidpp_initialize_battery(struct hidpp_device *hidpp)
825{
826 int ret;
827
828 if (hidpp->protocol_major >= 2) {
829 ret = hidpp20_initialize_battery(hidpp);
830 if (ret == 0)
831 hidpp->quirks |= HIDPP_QUIRK_HIDPP20_BATTERY;
832 }
833
834 return ret;
835}
836
837/* -------------------------------------------------------------------------- */
610/* 0x6010: Touchpad FW items */ 838/* 0x6010: Touchpad FW items */
611/* -------------------------------------------------------------------------- */ 839/* -------------------------------------------------------------------------- */
612 840
@@ -2050,6 +2278,12 @@ static int hidpp_raw_event(struct hid_device *hdev, struct hid_report *report,
2050 if (ret != 0) 2278 if (ret != 0)
2051 return ret; 2279 return ret;
2052 2280
2281 if (hidpp->quirks & HIDPP_QUIRK_HIDPP20_BATTERY) {
2282 ret = hidpp20_battery_event(hidpp, data, size);
2283 if (ret != 0)
2284 return ret;
2285 }
2286
2053 if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP) 2287 if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP)
2054 return wtp_raw_event(hdev, data, size); 2288 return wtp_raw_event(hdev, data, size);
2055 else if (hidpp->quirks & HIDPP_QUIRK_CLASS_M560) 2289 else if (hidpp->quirks & HIDPP_QUIRK_CLASS_M560)
@@ -2158,6 +2392,8 @@ static void hidpp_connect_event(struct hidpp_device *hidpp)
2158 hidpp->protocol_major, hidpp->protocol_minor); 2392 hidpp->protocol_major, hidpp->protocol_minor);
2159 } 2393 }
2160 2394
2395 hidpp_initialize_battery(hidpp);
2396
2161 if (!(hidpp->quirks & HIDPP_QUIRK_NO_HIDINPUT)) 2397 if (!(hidpp->quirks & HIDPP_QUIRK_NO_HIDINPUT))
2162 /* if HID created the input nodes for us, we can stop now */ 2398 /* if HID created the input nodes for us, we can stop now */
2163 return; 2399 return;