diff options
author | Benjamin Tissoires <benjamin.tissoires@redhat.com> | 2017-03-27 10:59:36 -0400 |
---|---|---|
committer | Jiri Kosina <jkosina@suse.cz> | 2017-04-06 08:36:39 -0400 |
commit | 5b036ea18e13e006e99cb197e9aceb09d897d20a (patch) | |
tree | 0bc7406942824db5f2c05e45dcdcbc15f393328d | |
parent | 14f437a1d7b49a2e873f63436526f9aed3a781c3 (diff) |
HID: logitech-hidpp: battery: provide CAPACITY_LEVEL
CAPACITY LEVEL allows to forward rough information on the battery mileage.
HID++ 2.0 devices will either report percentage or levels, so better
forwarding this information to the user space.
The M325 supports only 2 levels: 'Full' and 'Critical'. With mileage,
it will report either 90% or 5%, which might confuse users. With this
change the battery will either report "Full" or "Critical".
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
Tested-by: Bastien Nocera <hadess@hadess.net>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
-rw-r--r-- | drivers/hid/hid-logitech-hidpp.c | 104 |
1 files changed, 94 insertions, 10 deletions
diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c index c59f7e5eedfa..d7dc45801226 100644 --- a/drivers/hid/hid-logitech-hidpp.c +++ b/drivers/hid/hid-logitech-hidpp.c | |||
@@ -68,6 +68,8 @@ MODULE_PARM_DESC(disable_tap_to_click, | |||
68 | 68 | ||
69 | #define HIDPP_CAPABILITY_HIDPP10_BATTERY BIT(0) | 69 | #define HIDPP_CAPABILITY_HIDPP10_BATTERY BIT(0) |
70 | #define HIDPP_CAPABILITY_HIDPP20_BATTERY BIT(1) | 70 | #define HIDPP_CAPABILITY_HIDPP20_BATTERY BIT(1) |
71 | #define HIDPP_CAPABILITY_BATTERY_MILEAGE BIT(2) | ||
72 | #define HIDPP_CAPABILITY_BATTERY_LEVEL_STATUS BIT(3) | ||
71 | 73 | ||
72 | /* | 74 | /* |
73 | * There are two hidpp protocols in use, the first version hidpp10 is known | 75 | * There are two hidpp protocols in use, the first version hidpp10 is known |
@@ -120,6 +122,7 @@ struct hidpp_battery { | |||
120 | char name[64]; | 122 | char name[64]; |
121 | int status; | 123 | int status; |
122 | int capacity; | 124 | int capacity; |
125 | int level; | ||
123 | bool online; | 126 | bool online; |
124 | }; | 127 | }; |
125 | 128 | ||
@@ -683,13 +686,30 @@ static char *hidpp_get_device_name(struct hidpp_device *hidpp) | |||
683 | 686 | ||
684 | #define EVENT_BATTERY_LEVEL_STATUS_BROADCAST 0x00 | 687 | #define EVENT_BATTERY_LEVEL_STATUS_BROADCAST 0x00 |
685 | 688 | ||
689 | #define FLAG_BATTERY_LEVEL_DISABLE_OSD BIT(0) | ||
690 | #define FLAG_BATTERY_LEVEL_MILEAGE BIT(1) | ||
691 | #define FLAG_BATTERY_LEVEL_RECHARGEABLE BIT(2) | ||
692 | |||
693 | static int hidpp_map_battery_level(int capacity) | ||
694 | { | ||
695 | if (capacity < 11) | ||
696 | return POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL; | ||
697 | else if (capacity < 31) | ||
698 | return POWER_SUPPLY_CAPACITY_LEVEL_LOW; | ||
699 | else if (capacity < 81) | ||
700 | return POWER_SUPPLY_CAPACITY_LEVEL_NORMAL; | ||
701 | return POWER_SUPPLY_CAPACITY_LEVEL_FULL; | ||
702 | } | ||
703 | |||
686 | static int hidpp20_batterylevel_map_status_capacity(u8 data[3], int *capacity, | 704 | static int hidpp20_batterylevel_map_status_capacity(u8 data[3], int *capacity, |
687 | int *next_capacity) | 705 | int *next_capacity, |
706 | int *level) | ||
688 | { | 707 | { |
689 | int status; | 708 | int status; |
690 | 709 | ||
691 | *capacity = data[0]; | 710 | *capacity = data[0]; |
692 | *next_capacity = data[1]; | 711 | *next_capacity = data[1]; |
712 | *level = POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN; | ||
693 | 713 | ||
694 | /* When discharging, we can rely on the device reported capacity. | 714 | /* When discharging, we can rely on the device reported capacity. |
695 | * For all other states the device reports 0 (unknown). | 715 | * For all other states the device reports 0 (unknown). |
@@ -697,6 +717,7 @@ static int hidpp20_batterylevel_map_status_capacity(u8 data[3], int *capacity, | |||
697 | switch (data[2]) { | 717 | switch (data[2]) { |
698 | case 0: /* discharging (in use) */ | 718 | case 0: /* discharging (in use) */ |
699 | status = POWER_SUPPLY_STATUS_DISCHARGING; | 719 | status = POWER_SUPPLY_STATUS_DISCHARGING; |
720 | *level = hidpp_map_battery_level(*capacity); | ||
700 | break; | 721 | break; |
701 | case 1: /* recharging */ | 722 | case 1: /* recharging */ |
702 | status = POWER_SUPPLY_STATUS_CHARGING; | 723 | status = POWER_SUPPLY_STATUS_CHARGING; |
@@ -706,6 +727,7 @@ static int hidpp20_batterylevel_map_status_capacity(u8 data[3], int *capacity, | |||
706 | break; | 727 | break; |
707 | case 3: /* charge complete */ | 728 | case 3: /* charge complete */ |
708 | status = POWER_SUPPLY_STATUS_FULL; | 729 | status = POWER_SUPPLY_STATUS_FULL; |
730 | *level = POWER_SUPPLY_CAPACITY_LEVEL_FULL; | ||
709 | *capacity = 100; | 731 | *capacity = 100; |
710 | break; | 732 | break; |
711 | case 4: /* recharging below optimal speed */ | 733 | case 4: /* recharging below optimal speed */ |
@@ -726,7 +748,8 @@ static int hidpp20_batterylevel_get_battery_capacity(struct hidpp_device *hidpp, | |||
726 | u8 feature_index, | 748 | u8 feature_index, |
727 | int *status, | 749 | int *status, |
728 | int *capacity, | 750 | int *capacity, |
729 | int *next_capacity) | 751 | int *next_capacity, |
752 | int *level) | ||
730 | { | 753 | { |
731 | struct hidpp_report response; | 754 | struct hidpp_report response; |
732 | int ret; | 755 | int ret; |
@@ -744,7 +767,38 @@ static int hidpp20_batterylevel_get_battery_capacity(struct hidpp_device *hidpp, | |||
744 | return ret; | 767 | return ret; |
745 | 768 | ||
746 | *status = hidpp20_batterylevel_map_status_capacity(params, capacity, | 769 | *status = hidpp20_batterylevel_map_status_capacity(params, capacity, |
747 | next_capacity); | 770 | next_capacity, |
771 | level); | ||
772 | |||
773 | return 0; | ||
774 | } | ||
775 | |||
776 | static int hidpp20_batterylevel_get_battery_info(struct hidpp_device *hidpp, | ||
777 | u8 feature_index) | ||
778 | { | ||
779 | struct hidpp_report response; | ||
780 | int ret; | ||
781 | u8 *params = (u8 *)response.fap.params; | ||
782 | unsigned int level_count, flags; | ||
783 | |||
784 | ret = hidpp_send_fap_command_sync(hidpp, feature_index, | ||
785 | CMD_BATTERY_LEVEL_STATUS_GET_BATTERY_CAPABILITY, | ||
786 | NULL, 0, &response); | ||
787 | if (ret > 0) { | ||
788 | hid_err(hidpp->hid_dev, "%s: received protocol error 0x%02x\n", | ||
789 | __func__, ret); | ||
790 | return -EPROTO; | ||
791 | } | ||
792 | if (ret) | ||
793 | return ret; | ||
794 | |||
795 | level_count = params[0]; | ||
796 | flags = params[1]; | ||
797 | |||
798 | if (level_count < 10 || !(flags & FLAG_BATTERY_LEVEL_MILEAGE)) | ||
799 | hidpp->capabilities |= HIDPP_CAPABILITY_BATTERY_LEVEL_STATUS; | ||
800 | else | ||
801 | hidpp->capabilities |= HIDPP_CAPABILITY_BATTERY_MILEAGE; | ||
748 | 802 | ||
749 | return 0; | 803 | return 0; |
750 | } | 804 | } |
@@ -753,7 +807,7 @@ static int hidpp20_query_battery_info(struct hidpp_device *hidpp) | |||
753 | { | 807 | { |
754 | u8 feature_type; | 808 | u8 feature_type; |
755 | int ret; | 809 | int ret; |
756 | int status, capacity, next_capacity; | 810 | int status, capacity, next_capacity, level; |
757 | 811 | ||
758 | if (hidpp->battery.feature_index == 0) { | 812 | if (hidpp->battery.feature_index == 0) { |
759 | ret = hidpp_root_get_feature(hidpp, | 813 | ret = hidpp_root_get_feature(hidpp, |
@@ -767,12 +821,18 @@ static int hidpp20_query_battery_info(struct hidpp_device *hidpp) | |||
767 | ret = hidpp20_batterylevel_get_battery_capacity(hidpp, | 821 | ret = hidpp20_batterylevel_get_battery_capacity(hidpp, |
768 | hidpp->battery.feature_index, | 822 | hidpp->battery.feature_index, |
769 | &status, &capacity, | 823 | &status, &capacity, |
770 | &next_capacity); | 824 | &next_capacity, &level); |
825 | if (ret) | ||
826 | return ret; | ||
827 | |||
828 | ret = hidpp20_batterylevel_get_battery_info(hidpp, | ||
829 | hidpp->battery.feature_index); | ||
771 | if (ret) | 830 | if (ret) |
772 | return ret; | 831 | return ret; |
773 | 832 | ||
774 | hidpp->battery.status = status; | 833 | hidpp->battery.status = status; |
775 | hidpp->battery.capacity = capacity; | 834 | hidpp->battery.capacity = capacity; |
835 | hidpp->battery.level = level; | ||
776 | /* the capacity is only available when discharging or full */ | 836 | /* the capacity is only available when discharging or full */ |
777 | hidpp->battery.online = status == POWER_SUPPLY_STATUS_DISCHARGING || | 837 | hidpp->battery.online = status == POWER_SUPPLY_STATUS_DISCHARGING || |
778 | status == POWER_SUPPLY_STATUS_FULL; | 838 | status == POWER_SUPPLY_STATUS_FULL; |
@@ -784,7 +844,7 @@ static int hidpp20_battery_event(struct hidpp_device *hidpp, | |||
784 | u8 *data, int size) | 844 | u8 *data, int size) |
785 | { | 845 | { |
786 | struct hidpp_report *report = (struct hidpp_report *)data; | 846 | struct hidpp_report *report = (struct hidpp_report *)data; |
787 | int status, capacity, next_capacity; | 847 | int status, capacity, next_capacity, level; |
788 | bool changed; | 848 | bool changed; |
789 | 849 | ||
790 | if (report->fap.feature_index != hidpp->battery.feature_index || | 850 | if (report->fap.feature_index != hidpp->battery.feature_index || |
@@ -793,16 +853,19 @@ static int hidpp20_battery_event(struct hidpp_device *hidpp, | |||
793 | 853 | ||
794 | status = hidpp20_batterylevel_map_status_capacity(report->fap.params, | 854 | status = hidpp20_batterylevel_map_status_capacity(report->fap.params, |
795 | &capacity, | 855 | &capacity, |
796 | &next_capacity); | 856 | &next_capacity, |
857 | &level); | ||
797 | 858 | ||
798 | /* the capacity is only available when discharging or full */ | 859 | /* the capacity is only available when discharging or full */ |
799 | hidpp->battery.online = status == POWER_SUPPLY_STATUS_DISCHARGING || | 860 | hidpp->battery.online = status == POWER_SUPPLY_STATUS_DISCHARGING || |
800 | status == POWER_SUPPLY_STATUS_FULL; | 861 | status == POWER_SUPPLY_STATUS_FULL; |
801 | 862 | ||
802 | changed = capacity != hidpp->battery.capacity || | 863 | changed = capacity != hidpp->battery.capacity || |
864 | level != hidpp->battery.level || | ||
803 | status != hidpp->battery.status; | 865 | status != hidpp->battery.status; |
804 | 866 | ||
805 | if (changed) { | 867 | if (changed) { |
868 | hidpp->battery.level = level; | ||
806 | hidpp->battery.capacity = capacity; | 869 | hidpp->battery.capacity = capacity; |
807 | hidpp->battery.status = status; | 870 | hidpp->battery.status = status; |
808 | if (hidpp->battery.ps) | 871 | if (hidpp->battery.ps) |
@@ -815,11 +878,12 @@ static int hidpp20_battery_event(struct hidpp_device *hidpp, | |||
815 | static enum power_supply_property hidpp_battery_props[] = { | 878 | static enum power_supply_property hidpp_battery_props[] = { |
816 | POWER_SUPPLY_PROP_ONLINE, | 879 | POWER_SUPPLY_PROP_ONLINE, |
817 | POWER_SUPPLY_PROP_STATUS, | 880 | POWER_SUPPLY_PROP_STATUS, |
818 | POWER_SUPPLY_PROP_CAPACITY, | ||
819 | POWER_SUPPLY_PROP_SCOPE, | 881 | POWER_SUPPLY_PROP_SCOPE, |
820 | POWER_SUPPLY_PROP_MODEL_NAME, | 882 | POWER_SUPPLY_PROP_MODEL_NAME, |
821 | POWER_SUPPLY_PROP_MANUFACTURER, | 883 | POWER_SUPPLY_PROP_MANUFACTURER, |
822 | POWER_SUPPLY_PROP_SERIAL_NUMBER, | 884 | POWER_SUPPLY_PROP_SERIAL_NUMBER, |
885 | 0, /* placeholder for POWER_SUPPLY_PROP_CAPACITY, */ | ||
886 | 0, /* placeholder for POWER_SUPPLY_PROP_CAPACITY_LEVEL, */ | ||
823 | }; | 887 | }; |
824 | 888 | ||
825 | static int hidpp_battery_get_property(struct power_supply *psy, | 889 | static int hidpp_battery_get_property(struct power_supply *psy, |
@@ -836,6 +900,9 @@ static int hidpp_battery_get_property(struct power_supply *psy, | |||
836 | case POWER_SUPPLY_PROP_CAPACITY: | 900 | case POWER_SUPPLY_PROP_CAPACITY: |
837 | val->intval = hidpp->battery.capacity; | 901 | val->intval = hidpp->battery.capacity; |
838 | break; | 902 | break; |
903 | case POWER_SUPPLY_PROP_CAPACITY_LEVEL: | ||
904 | val->intval = hidpp->battery.level; | ||
905 | break; | ||
839 | case POWER_SUPPLY_PROP_SCOPE: | 906 | case POWER_SUPPLY_PROP_SCOPE: |
840 | val->intval = POWER_SUPPLY_SCOPE_DEVICE; | 907 | val->intval = POWER_SUPPLY_SCOPE_DEVICE; |
841 | break; | 908 | break; |
@@ -2316,7 +2383,9 @@ static int hidpp_initialize_battery(struct hidpp_device *hidpp) | |||
2316 | static atomic_t battery_no = ATOMIC_INIT(0); | 2383 | static atomic_t battery_no = ATOMIC_INIT(0); |
2317 | struct power_supply_config cfg = { .drv_data = hidpp }; | 2384 | struct power_supply_config cfg = { .drv_data = hidpp }; |
2318 | struct power_supply_desc *desc = &hidpp->battery.desc; | 2385 | struct power_supply_desc *desc = &hidpp->battery.desc; |
2386 | enum power_supply_property *battery_props; | ||
2319 | struct hidpp_battery *battery; | 2387 | struct hidpp_battery *battery; |
2388 | unsigned int num_battery_props; | ||
2320 | unsigned long n; | 2389 | unsigned long n; |
2321 | int ret; | 2390 | int ret; |
2322 | 2391 | ||
@@ -2332,11 +2401,25 @@ static int hidpp_initialize_battery(struct hidpp_device *hidpp) | |||
2332 | return -ENOENT; | 2401 | return -ENOENT; |
2333 | } | 2402 | } |
2334 | 2403 | ||
2404 | battery_props = devm_kmemdup(&hidpp->hid_dev->dev, | ||
2405 | hidpp_battery_props, | ||
2406 | sizeof(hidpp_battery_props), | ||
2407 | GFP_KERNEL); | ||
2408 | num_battery_props = ARRAY_SIZE(hidpp_battery_props) - 2; | ||
2409 | |||
2410 | if (hidpp->capabilities & HIDPP_CAPABILITY_BATTERY_MILEAGE) | ||
2411 | battery_props[num_battery_props++] = | ||
2412 | POWER_SUPPLY_PROP_CAPACITY; | ||
2413 | |||
2414 | if (hidpp->capabilities & HIDPP_CAPABILITY_BATTERY_LEVEL_STATUS) | ||
2415 | battery_props[num_battery_props++] = | ||
2416 | POWER_SUPPLY_PROP_CAPACITY_LEVEL; | ||
2417 | |||
2335 | battery = &hidpp->battery; | 2418 | battery = &hidpp->battery; |
2336 | 2419 | ||
2337 | n = atomic_inc_return(&battery_no) - 1; | 2420 | n = atomic_inc_return(&battery_no) - 1; |
2338 | desc->properties = hidpp_battery_props; | 2421 | desc->properties = battery_props; |
2339 | desc->num_properties = ARRAY_SIZE(hidpp_battery_props); | 2422 | desc->num_properties = num_battery_props; |
2340 | desc->get_property = hidpp_battery_get_property; | 2423 | desc->get_property = hidpp_battery_get_property; |
2341 | sprintf(battery->name, "hidpp_battery_%ld", n); | 2424 | sprintf(battery->name, "hidpp_battery_%ld", n); |
2342 | desc->name = battery->name; | 2425 | desc->name = battery->name; |
@@ -2424,6 +2507,7 @@ static void hidpp_connect_event(struct hidpp_device *hidpp) | |||
2424 | if (hidpp->battery.ps) { | 2507 | if (hidpp->battery.ps) { |
2425 | hidpp->battery.online = false; | 2508 | hidpp->battery.online = false; |
2426 | hidpp->battery.status = POWER_SUPPLY_STATUS_UNKNOWN; | 2509 | hidpp->battery.status = POWER_SUPPLY_STATUS_UNKNOWN; |
2510 | hidpp->battery.level = POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN; | ||
2427 | power_supply_changed(hidpp->battery.ps); | 2511 | power_supply_changed(hidpp->battery.ps); |
2428 | } | 2512 | } |
2429 | return; | 2513 | return; |