Re: Support for Logitech g703 mouse battery levels

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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


[Index of Archives]     [Linux Media Devel]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Linux Wireless Networking]     [Linux Omap]

  Powered by Linux