diff options
| -rw-r--r-- | drivers/hid/hid-logitech-hidpp.c | 238 |
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 | ||
| 115 | struct 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 | |||
| 113 | struct hidpp_device { | 124 | struct 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 | |||
| 632 | static 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 | |||
| 681 | static 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 | |||
| 708 | static 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 | |||
| 735 | static 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 | |||
| 762 | static enum power_supply_property hidpp_battery_props[] = { | ||
| 763 | POWER_SUPPLY_PROP_STATUS, | ||
| 764 | POWER_SUPPLY_PROP_CAPACITY, | ||
| 765 | }; | ||
| 766 | |||
| 767 | static 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 | |||
| 789 | static 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 | |||
| 824 | static 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; |
