On Thu, 2020-11-26 at 22:50 +0100, Bastien Nocera wrote: > On Thu, 2020-11-26 at 19:10 +0000, Filipe Laíns wrote: > > <snip> > > I did not have it at the time, Logitech has since made it public. > > > > I went looking for the link :) > > https://drive.google.com/file/d/1F_fuqL0-TbZ77u0suXRcj3YcDidCcN1M/view?usp=sharing > > Looks small enough to put in the kernel to be fair. The hid++ source > is > already 4k long, what's 100 more lines ;) > Patch that's not even compile-tested attached. We probably need more device IDs, although it might be possible to always do that conversion when the battery voltage is known but not the battery level? I'd need comments to finish this off if I'm on the right path, although I'd be happy to leave it to someone with the hardware to finish up. Cheers
From 103e759f90a8f67bcd61d0fed4f098d841520810 Mon Sep 17 00:00:00 2001 From: Bastien Nocera <hadess@xxxxxxxxxx> Date: Fri, 27 Nov 2020 11:35:41 +0100 Subject: [PATCH] WIP: HID: logitech-hidpp: Map voltage to capacity For devices in the G Pro Wireless range that usually sport a 240 mAh battery, convert the battery voltage to a capacity. --- drivers/hid/hid-logitech-hidpp.c | 161 ++++++++++++++++++++++++++++--- 1 file changed, 149 insertions(+), 12 deletions(-) diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c index b8b53dc95e86..222c8d70b3ca 100644 --- a/drivers/hid/hid-logitech-hidpp.c +++ b/drivers/hid/hid-logitech-hidpp.c @@ -75,6 +75,7 @@ MODULE_PARM_DESC(disable_tap_to_click, #define HIDPP_QUIRK_HIDPP_WHEELS BIT(29) #define HIDPP_QUIRK_HIDPP_EXTRA_MOUSE_BTNS BIT(30) #define HIDPP_QUIRK_HIDPP_CONSUMER_VENDOR_KEYS BIT(31) +#define HIDPP_QUIRK_BATTERY_LEVEL_FROM_VOLTAGE BIT(32) /* These are just aliases for now */ #define HIDPP_QUIRK_KBD_SCROLL_WHEEL HIDPP_QUIRK_HIDPP_WHEELS @@ -1251,8 +1252,116 @@ static int hidpp20_battery_event(struct hidpp_device *hidpp, #define EVENT_BATTERY_VOLTAGE_STATUS_BROADCAST 0x00 +static struct { + int mv_voltage; + int capacity; +} battery_to_capacity[] = { + { 4186, 100 }, + { 4156, 99 }, + { 4143, 98 }, + { 4133, 97 }, + { 4122, 96 }, + { 4113, 95 }, + { 4103, 94 }, + { 4094, 93 }, + { 4086, 92 }, + { 4076, 91 }, + { 4067, 90 }, + { 4060, 89 }, + { 4051, 88 }, + { 4043, 87 }, + { 4036, 86 }, + { 4027, 85 }, + { 4019, 84 }, + { 4012, 83 }, + { 4004, 82 }, + { 3997, 81 }, + { 3989, 80 }, + { 3983, 79 }, + { 3976, 78 }, + { 3969, 77 }, + { 3961, 76 }, + { 3955, 75 }, + { 3949, 74 }, + { 3942, 73 }, + { 3935, 72 }, + { 3929, 71 }, + { 3922, 70 }, + { 3916, 69 }, + { 3909, 68 }, + { 3902, 67 }, + { 3896, 66 }, + { 3890, 65 }, + { 3883, 64 }, + { 3877, 63 }, + { 3870, 62 }, + { 3865, 61 }, + { 3859, 60 }, + { 3853, 59 }, + { 3848, 58 }, + { 3842, 57 }, + { 3837, 56 }, + { 3833, 55 }, + { 3828, 54 }, + { 3824, 53 }, + { 3819, 52 }, + { 3815, 51 }, + { 3811, 50 }, + { 3808, 49 }, + { 3804, 48 }, + { 3800, 47 }, + { 3797, 46 }, + { 3793, 45 }, + { 3790, 44 }, + { 3787, 43 }, + { 3784, 42 }, + { 3781, 41 }, + { 3778, 40 }, + { 3775, 39 }, + { 3772, 38 }, + { 3770, 37 }, + { 3767, 36 }, + { 3764, 35 }, + { 3762, 34 }, + { 3759, 33 }, + { 3757, 32 }, + { 3754, 31 }, + { 3751, 30 }, + { 3748, 29 }, + { 3744, 28 }, + { 3741, 27 }, + { 3737, 26 }, + { 3734, 25 }, + { 3730, 24 }, + { 3726, 23 }, + { 3724, 22 }, + { 3720, 21 }, + { 3717, 20 }, + { 3714, 19 }, + { 3710, 18 }, + { 3706, 17 }, + { 3702, 16 }, + { 3697, 15 }, + { 3693, 14 }, + { 3688, 13 }, + { 3683, 12 }, + { 3677, 11 }, + { 3671, 10 }, + { 3666, 9 }, + { 3662, 8 }, + { 3658, 7 }, + { 3654, 6 }, + { 3646, 5 }, + { 3633, 4 }, + { 3612, 3 }, + { 3579, 2 }, + { 3537, 1 }, + { 3500, 0 } +}; + static int hidpp20_battery_map_status_voltage(u8 data[3], int *voltage, - int *level, int *charge_type) + int *level, int *charge_type, + int *capacity) { int status; @@ -1290,13 +1399,28 @@ static int hidpp20_battery_map_status_voltage(u8 data[3], int *voltage, *voltage = get_unaligned_be16(data); + if (hidd->quirks & HIDPP_QUIRK_BATTERY_LEVEL_FROM_VOLTAGE) { + int i; + for (i = 0; i < ARRAY_SIZE(battery_to_capacity); i++) { + if (*voltage < battery_to_capacity[i].mv_voltage) + continue; + if (*voltage == battery_to_capacity[i].mv_voltage || + i == 0) + *capacity = battery_to_capacity[i].capacity; + else + *capacity = battery_to_capacity[i - 1].capacity; + break; + } + } + return status; } static int hidpp20_battery_get_battery_voltage(struct hidpp_device *hidpp, u8 feature_index, int *status, int *voltage, - int *level, int *charge_type) + int *level, int *charge_type, + int *capacity) { struct hidpp_report response; int ret; @@ -1317,7 +1441,8 @@ static int hidpp20_battery_get_battery_voltage(struct hidpp_device *hidpp, hidpp->capabilities |= HIDPP_CAPABILITY_BATTERY_VOLTAGE; *status = hidpp20_battery_map_status_voltage(params, voltage, - level, charge_type); + level, charge_type, + capacity); return 0; } @@ -1326,7 +1451,7 @@ static int hidpp20_query_battery_voltage_info(struct hidpp_device *hidpp) { u8 feature_type; int ret; - int status, voltage, level, charge_type; + int status, voltage, level, charge_type, capacity; if (hidpp->battery.voltage_feature_index == 0xff) { ret = hidpp_root_get_feature(hidpp, HIDPP_PAGE_BATTERY_VOLTAGE, @@ -1338,14 +1463,18 @@ static int hidpp20_query_battery_voltage_info(struct hidpp_device *hidpp) ret = hidpp20_battery_get_battery_voltage(hidpp, hidpp->battery.voltage_feature_index, - &status, &voltage, &level, &charge_type); + &status, &voltage, &level, &charge_type, + &capacity); if (ret) return ret; hidpp->battery.status = status; hidpp->battery.voltage = voltage; - hidpp->battery.level = level; + if (hidd->quirks & HIDPP_QUIRK_BATTERY_LEVEL_FROM_VOLTAGE) + hidpp->batter.capacity = capacity; + else + hidpp->battery.level = level; hidpp->battery.charge_type = charge_type; hidpp->battery.online = status != POWER_SUPPLY_STATUS_NOT_CHARGING; @@ -1356,21 +1485,24 @@ static int hidpp20_battery_voltage_event(struct hidpp_device *hidpp, u8 *data, int size) { struct hidpp_report *report = (struct hidpp_report *)data; - int status, voltage, level, charge_type; + int status, voltage, level, charge_type, capacity; if (report->fap.feature_index != hidpp->battery.voltage_feature_index || report->fap.funcindex_clientid != EVENT_BATTERY_VOLTAGE_STATUS_BROADCAST) return 0; status = hidpp20_battery_map_status_voltage(report->fap.params, &voltage, - &level, &charge_type); + &level, &charge_type, &capacity); hidpp->battery.online = status != POWER_SUPPLY_STATUS_NOT_CHARGING; if (voltage != hidpp->battery.voltage || status != hidpp->battery.status) { hidpp->battery.voltage = voltage; hidpp->battery.status = status; - hidpp->battery.level = level; + if (hidd->quirks & HIDPP_QUIRK_BATTERY_LEVEL_FROM_VOLTAGE) + hidpp->battery.capacity = capacity; + else + hidpp->battery.level = level; hidpp->battery.charge_type = charge_type; if (hidpp->battery.ps) power_supply_changed(hidpp->battery.ps); @@ -3448,7 +3580,8 @@ static int hidpp_initialize_battery(struct hidpp_device *hidpp) num_battery_props = ARRAY_SIZE(hidpp_battery_props) - 3; - if (hidpp->capabilities & HIDPP_CAPABILITY_BATTERY_MILEAGE) + if (hidpp->capabilities & HIDPP_CAPABILITY_BATTERY_MILEAGE || + hidpp->quirks & HIDPP_QUIRK_BATTERY_LEVEL_FROM_VOLTAGE) battery_props[num_battery_props++] = POWER_SUPPLY_PROP_CAPACITY; @@ -3955,6 +4088,8 @@ static const struct hid_device_id hidpp_devices[] = { { /* Mouse Logitech MX Master */ LDJ_DEVICE(0x4041), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 }, { LDJ_DEVICE(0x4060), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 }, + { /* Logitech G703 */ + LDJ_DEVICE(0x4070), .driver_data = HIDPP_QUIRK_BATTERY_LEVEL_FROM_VOLTAGE }, { LDJ_DEVICE(0x4071), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 }, { /* Mouse Logitech MX Master 2S */ LDJ_DEVICE(0x4069), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 }, @@ -3997,7 +4132,8 @@ static const struct hid_device_id hidpp_devices[] = { { /* Logitech G703 Gaming Mouse over USB */ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC087) }, { /* Logitech G703 Hero Gaming Mouse over USB */ - HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC090) }, + HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC090), + .driver_data = HIDPP_QUIRK_BATTERY_LEVEL_FROM_VOLTAGE}, { /* Logitech G900 Gaming Mouse over USB */ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC081) }, { /* Logitech G903 Gaming Mouse over USB */ @@ -4008,7 +4144,8 @@ static const struct hid_device_id hidpp_devices[] = { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G920_WHEEL), .driver_data = HIDPP_QUIRK_CLASS_G920 | HIDPP_QUIRK_FORCE_OUTPUT_REPORTS}, { /* Logitech G Pro Gaming Mouse over USB */ - HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC088) }, + HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC088), + .driver_data = HIDPP_QUIRK_BATTERY_LEVEL_FROM_VOLTAGE}, { /* MX5000 keyboard over Bluetooth */ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb305), -- 2.28.0