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; |
