Re: [PATCH v5] hid-logitech-hidpp: read battery voltage from newer devices

2019-09-13 Thread Pedro Vanzella

On 9/9/19 8:00 AM, Filipe Laíns wrote:

On Sat, 2019-08-31 at 13:56 -0400, Pedro Vanzella wrote:

+static int hidpp20_battery_map_status_voltage(u8 data[3], int
*voltage)
+{
+   int status;
+
+   switch (data[2]) {
+   case 0x00: /* discharging */
+   status = POWER_SUPPLY_STATUS_DISCHARGING;
+   break;
+   case 0x10: /* wireless charging */
+   case 0x80: /* charging */
+   status = POWER_SUPPLY_STATUS_CHARGING;
+   break;
+   case 0x81: /* fully charged */
+   status = POWER_SUPPLY_STATUS_FULL;
+   break;
+   default:
+   status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+   }
+
+   *voltage = get_unaligned_be16(data);
+
+   return status;
+}


Here's the missing specification:

++-+
|  byte  |2 
   |
++--+++--+--+--+
|   bit  | 0..2 |  3 |  4 | 5| 6| 7 
   |
++--+++--+--+--+
| buffer | chargeStatus | fastCharge | slowCharge | critical | (unused) | 
extPower |
++--+++--+--+--+
Table 1 - battery voltage (0x1001), getBatteryInfo() (ASE 0), 3rd byte

+---+--+
| value |meaning   |
+---+--+
|   0   | Charging |
+---+--+
|   1   | End of charge (100% charged) |
+---+--+
|   2   | Charge stopped (any "normal" reason) |
+---+--+
|   7   | Hardware error   |
+---+--+
Table 2 - chargeStatus value

I know this is already on the 5th revision but could you please change
hidpp20_battery_map_status_voltage() to properly handle the 3rd byte?
Also, if you could make sure those values are properly exported via
sysfs it would be great! We will need them for proper upower support.


Sure thing. I'll have a new patch in a few days.

Thanks for figuring that stuff out, by the way.



Sorry for not saying this in my first review. Since then I have done
some testing and I discovered that if we want to get accurate upower
support we will need the values exposed by the 3rd byte to be
exported properly.

I am not sure about the endianness of chargeStatus but you can find
that easily.

Thank you!
Filipe Laíns



[PATCH v5] hid-logitech-hidpp: read battery voltage from newer devices

2019-08-31 Thread Pedro Vanzella
Newer Logitech mice report their battery voltage through feature 0x1001
instead of the battery levels through feature 0x1000.

When the device is brought up and we try to query the battery, figure
out if it supports the old or the new feature. If it supports the new
feature, record the feature index and read the battery voltage and
its status.

If everything went correctly, record the fact that we're capable
of querying battery voltage.

Note that the protocol only gives us the current voltage in millivolts.

Like we do for other ways of interacting with the battery for other
devices, refresh the battery status and notify the power supply
subsystem of the changes in voltage and status.

Since there are no known devices which implement both the old and the
new battery feature, we make sure to try the newer feature first and
only fall back to the old one if that fails.

Signed-off-by: Pedro Vanzella 
---
 drivers/hid/hid-logitech-hidpp.c | 141 ++-
 1 file changed, 137 insertions(+), 4 deletions(-)

diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c
index 0179f7ed77e5..2f215e5be001 100644
--- a/drivers/hid/hid-logitech-hidpp.c
+++ b/drivers/hid/hid-logitech-hidpp.c
@@ -87,6 +87,7 @@ MODULE_PARM_DESC(disable_tap_to_click,
 #define HIDPP_CAPABILITY_HIDPP20_BATTERY   BIT(1)
 #define HIDPP_CAPABILITY_BATTERY_MILEAGE   BIT(2)
 #define HIDPP_CAPABILITY_BATTERY_LEVEL_STATUS  BIT(3)
+#define HIDPP_CAPABILITY_BATTERY_VOLTAGE   BIT(4)
 
 /*
  * There are two hidpp protocols in use, the first version hidpp10 is known
@@ -135,12 +136,14 @@ struct hidpp_report {
 struct hidpp_battery {
u8 feature_index;
u8 solar_feature_index;
+   u8 voltage_feature_index;
struct power_supply_desc desc;
struct power_supply *ps;
char name[64];
int status;
int capacity;
int level;
+   int voltage; /* in millivolts */
bool online;
 };
 
@@ -1219,6 +1222,118 @@ static int hidpp20_battery_event(struct hidpp_device 
*hidpp,
return 0;
 }
 
+/* -- 
*/
+/* 0x1001: Battery voltage
*/
+/* -- 
*/
+
+#define HIDPP_PAGE_BATTERY_VOLTAGE 0x1001
+
+#define CMD_BATTERY_VOLTAGE_GET_BATTERY_VOLTAGE 0x00
+
+#define EVENT_BATTERY_VOLTAGE_STATUS_BROADCAST 0x00
+
+static int hidpp20_battery_map_status_voltage(u8 data[3], int *voltage)
+{
+   int status;
+
+   switch (data[2]) {
+   case 0x00: /* discharging */
+   status = POWER_SUPPLY_STATUS_DISCHARGING;
+   break;
+   case 0x10: /* wireless charging */
+   case 0x80: /* charging */
+   status = POWER_SUPPLY_STATUS_CHARGING;
+   break;
+   case 0x81: /* fully charged */
+   status = POWER_SUPPLY_STATUS_FULL;
+   break;
+   default:
+   status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+   }
+
+   *voltage = get_unaligned_be16(data);
+
+   return status;
+}
+
+static int hidpp20_battery_get_battery_voltage(struct hidpp_device *hidpp,
+  u8 feature_index,
+  int *status, int *voltage)
+{
+   struct hidpp_report response;
+   int ret;
+   u8 *params = (u8 *)response.fap.params;
+
+   ret = hidpp_send_fap_command_sync(hidpp, feature_index,
+ 
CMD_BATTERY_VOLTAGE_GET_BATTERY_VOLTAGE,
+ NULL, 0, &response);
+
+   if (ret > 0) {
+   hid_err(hidpp->hid_dev, "%s: received protocol error 0x%02x\n",
+   __func__, ret);
+   return -EPROTO;
+   }
+   if (ret)
+   return ret;
+
+   hidpp->capabilities |= HIDPP_CAPABILITY_BATTERY_VOLTAGE;
+
+   *status = hidpp20_battery_map_status_voltage(params, voltage);
+
+   return 0;
+}
+
+static int hidpp20_query_battery_voltage_info(struct hidpp_device *hidpp)
+{
+   u8 feature_type;
+   int ret;
+   int status, voltage;
+
+   if (hidpp->battery.voltage_feature_index == 0xff) {
+   ret = hidpp_root_get_feature(hidpp, HIDPP_PAGE_BATTERY_VOLTAGE,
+
&hidpp->battery.voltage_feature_index,
+&feature_type);
+   if (ret)
+   return ret;
+   }
+
+   ret = hidpp20_battery_get_battery_voltage(hidpp,
+ 
hidpp->battery.voltage_feature_index,
+ &status, &voltage);
+
+   if (ret)
+   return ret;
+
+   hidpp->battery.status = status;
+   hidpp-&

[PATCH v4] hid-logitech-hidpp: read battery voltage from newer devices

2019-08-23 Thread Pedro Vanzella
Newer Logitech mice report their battery voltage through feature 0x1001
instead of the battery levels through feature 0x1000.

When the device is brought up and we try to query the battery, figure
out if it supports the old or the new feature. If it supports the new
feature, record the feature index and read the battery voltage and
its status.

If everything went correctly, record the fact that we're capable
of querying battery voltage.

Note that the protocol only gives us the current voltage in millivolts.

Like we do for other ways of interacting with the battery for other
devices, refresh the battery status and notify the power supply
subsystem of the changes in voltage and status.

Signed-off-by: Pedro Vanzella 
---
 drivers/hid/hid-logitech-hidpp.c | 140 ++-
 1 file changed, 138 insertions(+), 2 deletions(-)

diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c
index 0179f7ed77e5..5c9c3133d2ae 100644
--- a/drivers/hid/hid-logitech-hidpp.c
+++ b/drivers/hid/hid-logitech-hidpp.c
@@ -87,6 +87,7 @@ MODULE_PARM_DESC(disable_tap_to_click,
 #define HIDPP_CAPABILITY_HIDPP20_BATTERY   BIT(1)
 #define HIDPP_CAPABILITY_BATTERY_MILEAGE   BIT(2)
 #define HIDPP_CAPABILITY_BATTERY_LEVEL_STATUS  BIT(3)
+#define HIDPP_CAPABILITY_BATTERY_VOLTAGE   BIT(4)
 
 /*
  * There are two hidpp protocols in use, the first version hidpp10 is known
@@ -135,12 +136,14 @@ struct hidpp_report {
 struct hidpp_battery {
u8 feature_index;
u8 solar_feature_index;
+   u8 voltage_feature_index;
struct power_supply_desc desc;
struct power_supply *ps;
char name[64];
int status;
int capacity;
int level;
+   int voltage; /* in millivolts */
bool online;
 };
 
@@ -1219,6 +1222,122 @@ static int hidpp20_battery_event(struct hidpp_device 
*hidpp,
return 0;
 }
 
+/* -- 
*/
+/* 0x1001: Battery voltage
*/
+/* -- 
*/
+
+#define HIDPP_PAGE_BATTERY_VOLTAGE 0x1001
+
+#define CMD_BATTERY_VOLTAGE_GET_BATTERY_VOLTAGE 0x00
+
+static int hidpp20_battery_map_status_voltage(u8 data[3], int *voltage)
+{
+   int status;
+
+   switch (data[2]) {
+   case 0x00: /* discharging */
+   status = POWER_SUPPLY_STATUS_DISCHARGING;
+   break;
+   case 0x10: /* wireless charging */
+   case 0x80: /* charging */
+   status = POWER_SUPPLY_STATUS_CHARGING;
+   break;
+   case 0x81: /* fully charged */
+   status = POWER_SUPPLY_STATUS_FULL;
+   break;
+   default:
+   status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+   }
+
+   *voltage = (data[0] << 8) + data[1];
+
+   return status;
+}
+
+static int hidpp20_battery_get_battery_voltage(struct hidpp_device *hidpp,
+  u8 feature_index,
+  int *status, int *voltage)
+{
+   struct hidpp_report response;
+   int ret;
+   u8 *params = (u8 *)response.fap.params;
+
+   ret = hidpp_send_fap_command_sync(hidpp, feature_index,
+ 
CMD_BATTERY_VOLTAGE_GET_BATTERY_VOLTAGE,
+ NULL, 0, &response);
+
+   if (ret > 0) {
+   hid_err(hidpp->hid_dev, "%s: received protocol error 0x%02x\n",
+   __func__, ret);
+   return -EPROTO;
+   }
+   if (ret)
+   return ret;
+
+   hidpp->capabilities |= HIDPP_CAPABILITY_BATTERY_VOLTAGE;
+
+   *status = hidpp20_battery_map_status_voltage(params, voltage);
+
+   return 0;
+}
+
+static int hidpp20_query_battery_voltage_info(struct hidpp_device *hidpp)
+{
+   u8 feature_type;
+   int ret;
+   int status, voltage;
+
+   if (hidpp->battery.voltage_feature_index == 0xff) {
+   ret = hidpp_root_get_feature(hidpp, HIDPP_PAGE_BATTERY_VOLTAGE,
+
&hidpp->battery.voltage_feature_index,
+&feature_type);
+   if (ret)
+   return ret;
+   }
+
+   ret = hidpp20_battery_get_battery_voltage(hidpp,
+ 
hidpp->battery.voltage_feature_index,
+ &status, &voltage);
+
+   if (ret)
+   return ret;
+
+   hidpp->battery.status = status;
+   hidpp->battery.voltage = voltage;
+   hidpp->battery.online = status != POWER_SUPPLY_STATUS_NOT_CHARGING;
+
+   return 0;
+}
+
+static int hidpp20_battery_voltage_event(struct hidpp_device *hidpp,
+  

Re: [Resubmit] Read battery voltage from Logitech Gaming mice

2019-08-23 Thread Pedro Vanzella




On 8/23/19 10:32 AM, Benjamin Tissoires wrote:

On Fri, Aug 23, 2019 at 4:22 PM Pedro Vanzella  wrote:


Hi Benjamin,

On 8/23/19 4:25 AM, Benjamin Tissoires wrote:

Hi Pedro,

On Thu, Aug 22, 2019 at 10:19 PM Pedro Vanzella  wrote:


Resumitting this after having rebased it against the latest changes.


thanks for resubmitting. Sorry I wasn't able to provide feedback on
the last revision



No worries, I know how these things are.



The gaming line of Logitech devices doesn't use the old hidpp20
feature for battery level reporting. Instead, they report the
current voltage of the battery, in millivolts.

This patch set handles this case by adding a quirk to the
devices we know to have this new feature, in both wired
and wireless mode.


So the quirk is in the end a bad idea after all. I had some chats with
Filipe that made me realize this.


I actually resubmitted by Filipe's request, since the patches weren't
applying cleanly anymore. The idea was to apply these patches and in the
future refactor the code to use the feature discovery routines.


Re-reading our previous exchanges also made me understood why I wasn't
happy with the initial submission: for every request the code was
checking both features 0x1000 and 0x1001 when we can remember this
once and for all during hidpp_initialize_battery().


Yeah I wasn't too happy about this either, but since there was nothing
prohibiting some device to have both features enabled, I thought this
wasn't too horrible.


I honestly don't think we will find one device that has both features
enabled. It doesn't make sense as the "new" feature allows for fine
tuning in the software, which would be moot if you also enable the
percentage.





So I think we should remove the useless quirk in the end (bad idea
from me, I concede), and instead during hidpp_initialize_battery() set
the correct HIDPP_CAPABILITY_*.
Not entirely sure if we should try to call 0x1000, or 0x1001 or if we
should rely on the 0x0001 feature to know which feature is available,
but this should be implementation detail.


I like the idea of calling 0x0001 once on device boot much better. I
think we could easily just fetch the location for the features we know
about and want to deal with once only. This also makes sure we support
every single device that supports this feature, so that is a huge plus.

In fact, I think we should _not_ call 0x0001 on battery init, but only
call battery init _after_ we called 0x0001 and discovered either 0x1000
or 0x1001 (or the solar battery feature, or any other one that might
crop up in the feature).


ack for that







This version of the patch set is better split, as well as adding the
quirk to make sure we don't needlessly probe every device connected.


It is for sure easy to review, but doesn't make much sense in the end.
I think we should squash all the patches together as you are just
adding one feature in the driver, and it is a little bit disturbing to
first add the quirk that has no use, then set up the structs when they
are not used, and so on, so forth.


You're right. My first instinct was to send a single patch. As much as I
tested this, I always feel like breaking the patch up post-facto will
break a git bisect in the future and everyone will hate me :P


as long as the patches are compiling and are not breaking, git bisect
will not be a problem. However, we might end up with the last one,
which is not very explicit in what it does as it just enables the
features implemented previously.



So we (you, me and Filipe) should probably come up with an action plan
here. The way I see it there are two issues here: one is adding this
feature, and the other is refactoring to use feature discovery for all
features. There are advantages and disadvantages to doing one or another
first and we might want to discuss that.

By merging this first (probably after I resubmit it as a single squashed
patch) we get to test it a bit better and have a usable feature sooner.
Plenty of people have been requesting this and there is plenty of stuff
that can be built on top of it, but only once this is actually merged I
think.

On the other hand, by first refactoring the rest of the code to use
0x0001 we avoid some rework on this patch. It should be minor, as most
functions here do all the heavy lifting after the initial feature
discovery, and are thus mostly independent from how that is done.

I'm happy either way, so just let me know what you guys decide.


I think we should merge your v3 squashed series with a slight
autodetection during battery init, like the method you used in the v1.
This would remove the quirk, but keep the straightforward commands
when addressing battery data.


Alright, I rebased against for-5.4/logitech to make sure, squashed 
everything and restored the detection code from v1, removing the quirk. 
Tested and it works on both wired and wireless on my G900.




Relying on 0x

Re: [Resubmit] Read battery voltage from Logitech Gaming mice

2019-08-23 Thread Pedro Vanzella

Hi Benjamin,

On 8/23/19 4:25 AM, Benjamin Tissoires wrote:

Hi Pedro,

On Thu, Aug 22, 2019 at 10:19 PM Pedro Vanzella  wrote:


Resumitting this after having rebased it against the latest changes.


thanks for resubmitting. Sorry I wasn't able to provide feedback on
the last revision



No worries, I know how these things are.



The gaming line of Logitech devices doesn't use the old hidpp20
feature for battery level reporting. Instead, they report the
current voltage of the battery, in millivolts.

This patch set handles this case by adding a quirk to the
devices we know to have this new feature, in both wired
and wireless mode.


So the quirk is in the end a bad idea after all. I had some chats with
Filipe that made me realize this.


I actually resubmitted by Filipe's request, since the patches weren't 
applying cleanly anymore. The idea was to apply these patches and in the 
future refactor the code to use the feature discovery routines.



Re-reading our previous exchanges also made me understood why I wasn't
happy with the initial submission: for every request the code was
checking both features 0x1000 and 0x1001 when we can remember this
once and for all during hidpp_initialize_battery().


Yeah I wasn't too happy about this either, but since there was nothing 
prohibiting some device to have both features enabled, I thought this 
wasn't too horrible.




So I think we should remove the useless quirk in the end (bad idea
from me, I concede), and instead during hidpp_initialize_battery() set
the correct HIDPP_CAPABILITY_*.
Not entirely sure if we should try to call 0x1000, or 0x1001 or if we
should rely on the 0x0001 feature to know which feature is available,
but this should be implementation detail.


I like the idea of calling 0x0001 once on device boot much better. I 
think we could easily just fetch the location for the features we know 
about and want to deal with once only. This also makes sure we support 
every single device that supports this feature, so that is a huge plus.


In fact, I think we should _not_ call 0x0001 on battery init, but only 
call battery init _after_ we called 0x0001 and discovered either 0x1000 
or 0x1001 (or the solar battery feature, or any other one that might 
crop up in the feature).






This version of the patch set is better split, as well as adding the
quirk to make sure we don't needlessly probe every device connected.


It is for sure easy to review, but doesn't make much sense in the end.
I think we should squash all the patches together as you are just
adding one feature in the driver, and it is a little bit disturbing to
first add the quirk that has no use, then set up the structs when they
are not used, and so on, so forth.


You're right. My first instinct was to send a single patch. As much as I 
tested this, I always feel like breaking the patch up post-facto will 
break a git bisect in the future and everyone will hate me :P


So we (you, me and Filipe) should probably come up with an action plan 
here. The way I see it there are two issues here: one is adding this 
feature, and the other is refactoring to use feature discovery for all 
features. There are advantages and disadvantages to doing one or another 
first and we might want to discuss that.


By merging this first (probably after I resubmit it as a single squashed 
patch) we get to test it a bit better and have a usable feature sooner. 
Plenty of people have been requesting this and there is plenty of stuff 
that can be built on top of it, but only once this is actually merged I 
think.


On the other hand, by first refactoring the rest of the code to use 
0x0001 we avoid some rework on this patch. It should be minor, as most 
functions here do all the heavy lifting after the initial feature 
discovery, and are thus mostly independent from how that is done.


I'm happy either way, so just let me know what you guys decide.

If you guys (or anyone else reading this on the public list, really) has 
any input - naming things being notoriosly hard, I'm actually happy with 
nitpicking - I'd appreciate it. On that note, come to think of it, I'm 
not 100% sure reporting the voltage in milivolts is the standard way. I 
looked through the docs, but found no solid guideline. It was either 
that or a float, so I think I made the right call here, but still.


- Pedro



Cheers,
Benjamin






[PATCH v3 3/4] hid-logitech-hidpp: report battery voltage to the power supply

2019-08-22 Thread Pedro Vanzella
If we know the device supports reading its voltage, report that.

Note that the protocol only gives us the current voltage in millivolts.

Signed-off-by: Pedro Vanzella 
---
 drivers/hid/hid-logitech-hidpp.c | 10 +-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c
index e1ddc9cdcc8b..06bee97d33b4 100644
--- a/drivers/hid/hid-logitech-hidpp.c
+++ b/drivers/hid/hid-logitech-hidpp.c
@@ -1319,6 +1319,7 @@ static enum power_supply_property hidpp_battery_props[] = 
{
POWER_SUPPLY_PROP_SERIAL_NUMBER,
0, /* placeholder for POWER_SUPPLY_PROP_CAPACITY, */
0, /* placeholder for POWER_SUPPLY_PROP_CAPACITY_LEVEL, */
+   0, /* placeholder for POWER_SUPPLY_PROP_VOLTAGE_NOW, */
 };
 
 static int hidpp_battery_get_property(struct power_supply *psy,
@@ -1356,6 +1357,9 @@ static int hidpp_battery_get_property(struct power_supply 
*psy,
case POWER_SUPPLY_PROP_SERIAL_NUMBER:
val->strval = hidpp->hid_dev->uniq;
break;
+   case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+   val->intval = hidpp->battery.voltage;
+   break;
default:
ret = -EINVAL;
break;
@@ -3328,7 +3332,7 @@ static int hidpp_initialize_battery(struct hidpp_device 
*hidpp)
if (!battery_props)
return -ENOMEM;
 
-   num_battery_props = ARRAY_SIZE(hidpp_battery_props) - 2;
+   num_battery_props = ARRAY_SIZE(hidpp_battery_props) - 3;
 
if (hidpp->capabilities & HIDPP_CAPABILITY_BATTERY_MILEAGE)
battery_props[num_battery_props++] =
@@ -3338,6 +3342,10 @@ static int hidpp_initialize_battery(struct hidpp_device 
*hidpp)
battery_props[num_battery_props++] =
POWER_SUPPLY_PROP_CAPACITY_LEVEL;
 
+   if (hidpp->capabilities & HIDPP_CAPABILITY_BATTERY_VOLTAGE)
+   battery_props[num_battery_props++] =
+   POWER_SUPPLY_PROP_VOLTAGE_NOW;
+
battery = &hidpp->battery;
 
n = atomic_inc_return(&battery_no) - 1;
-- 
2.23.0



[PATCH v3 4/4] hid-logitech-hidpp: subscribe to battery voltage events

2019-08-22 Thread Pedro Vanzella
Like we do for other ways of interacting with the battery for other
devices, refresh the battery status and notify the power supply
subsystem of the changes in voltage and status.

Signed-off-by: Pedro Vanzella 
---
 drivers/hid/hid-logitech-hidpp.c | 32 
 1 file changed, 32 insertions(+)

diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c
index 06bee97d33b4..9f09ed6abbad 100644
--- a/drivers/hid/hid-logitech-hidpp.c
+++ b/drivers/hid/hid-logitech-hidpp.c
@@ -1310,6 +1310,35 @@ static int hidpp20_query_battery_voltage_info(struct 
hidpp_device *hidpp)
return 0;
 }
 
+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;
+   bool changed;
+
+   if (report->fap.feature_index != hidpp->battery.voltage_feature_index ||
+   report->fap.funcindex_clientid !=
+   EVENT_BATTERY_LEVEL_STATUS_BROADCAST)
+   return 0;
+
+   status = hidpp20_battery_map_status_voltage(report->fap.params,
+   &voltage);
+
+   hidpp->battery.online = status != POWER_SUPPLY_STATUS_NOT_CHARGING;
+
+   changed = voltage != hidpp->battery.voltage ||
+ status != hidpp->battery.status;
+
+   if (changed) {
+   hidpp->battery.voltage = voltage;
+   hidpp->battery.status = status;
+   if (hidpp->battery.ps)
+   power_supply_changed(hidpp->battery.ps);
+   }
+   return 0;
+}
+
 static enum power_supply_property hidpp_battery_props[] = {
POWER_SUPPLY_PROP_ONLINE,
POWER_SUPPLY_PROP_STATUS,
@@ -3178,6 +3207,9 @@ static int hidpp_raw_hidpp_event(struct hidpp_device 
*hidpp, u8 *data,
ret = hidpp_solar_battery_event(hidpp, data, size);
if (ret != 0)
return ret;
+   ret = hidpp20_battery_voltage_event(hidpp, data, size);
+   if (ret != 0)
+   return ret;
}
 
if (hidpp->capabilities & HIDPP_CAPABILITY_HIDPP10_BATTERY) {
-- 
2.23.0



[PATCH v3 1/4] hid-logitech-hidpp: add quirk to handle battery voltage

2019-08-22 Thread Pedro Vanzella
This quirk allows us to pick which devices support the 0x1001 hidpp
feature to read the battery voltage.

Signed-off-by: Pedro Vanzella 
---
 drivers/hid/hid-logitech-hidpp.c | 16 +---
 1 file changed, 13 insertions(+), 3 deletions(-)

diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c
index 0179f7ed77e5..402ddba93adc 100644
--- a/drivers/hid/hid-logitech-hidpp.c
+++ b/drivers/hid/hid-logitech-hidpp.c
@@ -59,7 +59,8 @@ MODULE_PARM_DESC(disable_tap_to_click,
 #define HIDPP_QUIRK_CLASS_G920 BIT(3)
 #define HIDPP_QUIRK_CLASS_K750 BIT(4)
 
-/* bits 2..20 are reserved for classes */
+/* bits 2..1f are reserved for classes */
+#define HIDPP_QUIRK_BATTERY_VOLTAGE_X1001  BIT(20)
 /* #define HIDPP_QUIRK_CONNECT_EVENTS  BIT(21) disabled */
 #define HIDPP_QUIRK_WTP_PHYSICAL_BUTTONS   BIT(22)
 #define HIDPP_QUIRK_NO_HIDINPUTBIT(23)
@@ -3732,6 +3733,13 @@ static const struct hid_device_id hidpp_devices[] = {
  LDJ_DEVICE(0xb30b),
  .driver_data = HIDPP_QUIRK_HIDPP_CONSUMER_VENDOR_KEYS },
 
+   { /* Logitech G403 Gaming Mouse over Lightspeed */
+ LDJ_DEVICE(0x405d),
+ .driver_data = HIDPP_QUIRK_BATTERY_VOLTAGE_X1001 },
+   { /* Logitech G900 Gaming Mouse over Lightspeed */
+ LDJ_DEVICE(0x4053),
+ .driver_data = HIDPP_QUIRK_BATTERY_VOLTAGE_X1001 },
+
{ LDJ_DEVICE(HID_ANY_ID) },
 
{ /* Keyboard LX501 (Y-RR53) */
@@ -3750,13 +3758,15 @@ static const struct hid_device_id hidpp_devices[] = {
{ L27MHZ_DEVICE(HID_ANY_ID) },
 
{ /* Logitech G403 Wireless Gaming Mouse over USB */
- HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC082) },
+ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC082),
+ .driver_data = HIDPP_QUIRK_BATTERY_VOLTAGE_X1001 },
{ /* 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) },
{ /* Logitech G900 Gaming Mouse over USB */
- HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC081) },
+ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC081),
+ .driver_data = HIDPP_QUIRK_BATTERY_VOLTAGE_X1001 },
{ /* Logitech G903 Gaming Mouse over USB */
  HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC086) },
{ /* Logitech G903 Hero Gaming Mouse over USB */
-- 
2.23.0



[Resubmit] Read battery voltage from Logitech Gaming mice

2019-08-22 Thread Pedro Vanzella
Resumitting this after having rebased it against the latest changes.

The gaming line of Logitech devices doesn't use the old hidpp20
feature for battery level reporting. Instead, they report the 
current voltage of the battery, in millivolts.

This patch set handles this case by adding a quirk to the 
devices we know to have this new feature, in both wired 
and wireless mode.

This version of the patch set is better split, as well as adding the
quirk to make sure we don't needlessly probe every device connected.




[PATCH v3 2/4] hid-logitech-hidpp: add function to query battery voltage

2019-08-22 Thread Pedro Vanzella
When the device is brought up, if it's one of the devices we know
supports battery voltage checking, figure out the feature index
and get the battery voltage and status.

If everything went correctly, record the fact that we're capable
of querying battery voltage.

Signed-off-by: Pedro Vanzella 
---
 drivers/hid/hid-logitech-hidpp.c | 95 
 1 file changed, 95 insertions(+)

diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c
index 402ddba93adc..e1ddc9cdcc8b 100644
--- a/drivers/hid/hid-logitech-hidpp.c
+++ b/drivers/hid/hid-logitech-hidpp.c
@@ -88,6 +88,7 @@ MODULE_PARM_DESC(disable_tap_to_click,
 #define HIDPP_CAPABILITY_HIDPP20_BATTERY   BIT(1)
 #define HIDPP_CAPABILITY_BATTERY_MILEAGE   BIT(2)
 #define HIDPP_CAPABILITY_BATTERY_LEVEL_STATUS  BIT(3)
+#define HIDPP_CAPABILITY_BATTERY_VOLTAGE   BIT(4)
 
 /*
  * There are two hidpp protocols in use, the first version hidpp10 is known
@@ -136,12 +137,14 @@ struct hidpp_report {
 struct hidpp_battery {
u8 feature_index;
u8 solar_feature_index;
+   u8 voltage_feature_index;
struct power_supply_desc desc;
struct power_supply *ps;
char name[64];
int status;
int capacity;
int level;
+   int voltage; /* in millivolts */
bool online;
 };
 
@@ -1220,6 +1223,93 @@ static int hidpp20_battery_event(struct hidpp_device 
*hidpp,
return 0;
 }
 
+/* -- 
*/
+/* 0x1001: Battery voltage
*/
+/* -- 
*/
+
+#define HIDPP_PAGE_BATTERY_VOLTAGE 0x1001
+
+#define CMD_BATTERY_VOLTAGE_GET_BATTERY_VOLTAGE 0x00
+
+static int hidpp20_battery_map_status_voltage(u8 data[3], int *voltage)
+{
+   int status;
+
+   switch (data[2]) {
+   case 0x00: /* discharging */
+   status = POWER_SUPPLY_STATUS_DISCHARGING;
+   break;
+   case 0x10: /* wireless charging */
+   case 0x80: /* charging */
+   status = POWER_SUPPLY_STATUS_CHARGING;
+   break;
+   case 0x81: /* fully charged */
+   status = POWER_SUPPLY_STATUS_FULL;
+   break;
+   default:
+   status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+   }
+
+   *voltage = (data[0] << 8) + data[1];
+
+   return status;
+}
+
+static int hidpp20_battery_get_battery_voltage(struct hidpp_device *hidpp,
+  u8 feature_index,
+  int *status, int *voltage)
+{
+   struct hidpp_report response;
+   int ret;
+   u8 *params = (u8 *)response.fap.params;
+
+   ret = hidpp_send_fap_command_sync(hidpp, feature_index,
+ 
CMD_BATTERY_VOLTAGE_GET_BATTERY_VOLTAGE,
+ NULL, 0, &response);
+
+   if (ret > 0) {
+   hid_err(hidpp->hid_dev, "%s: received protocol error 0x%02x\n",
+   __func__, ret);
+   return -EPROTO;
+   }
+   if (ret)
+   return ret;
+
+   hidpp->capabilities |= HIDPP_CAPABILITY_BATTERY_VOLTAGE;
+
+   *status = hidpp20_battery_map_status_voltage(params, voltage);
+
+   return 0;
+}
+
+static int hidpp20_query_battery_voltage_info(struct hidpp_device *hidpp)
+{
+   u8 feature_type;
+   int ret;
+   int status, voltage;
+
+   if (hidpp->battery.voltage_feature_index == 0xff) {
+   ret = hidpp_root_get_feature(hidpp, HIDPP_PAGE_BATTERY_VOLTAGE,
+
&hidpp->battery.voltage_feature_index,
+&feature_type);
+   if (ret)
+   return ret;
+   }
+
+   ret = hidpp20_battery_get_battery_voltage(hidpp,
+ 
hidpp->battery.voltage_feature_index,
+ &status, &voltage);
+
+   if (ret)
+   return ret;
+
+   hidpp->battery.status = status;
+   hidpp->battery.voltage = voltage;
+   hidpp->battery.online = status != POWER_SUPPLY_STATUS_NOT_CHARGING;
+
+   return 0;
+}
+
 static enum power_supply_property hidpp_battery_props[] = {
POWER_SUPPLY_PROP_ONLINE,
POWER_SUPPLY_PROP_STATUS,
@@ -3205,10 +3295,13 @@ static int hidpp_initialize_battery(struct hidpp_device 
*hidpp)
 
hidpp->battery.feature_index = 0xff;
hidpp->battery.solar_feature_index = 0xff;
+   hidpp->battery.voltage_feature_index = 0xff;
 
if (hidpp->protocol_major >= 2) {
if (hidpp->quirks & HIDPP_QUIRK_CLASS_K750)
ret = hidpp_solar_request_b

Re: [PATCH v2 0/4] Read battery voltage from G403 and G900 mice

2019-06-05 Thread Pedro Vanzella


> On Jun 5, 2019, at 6:24 PM, Filipe Laíns  wrote:
> 
>> On Wed, 2019-06-05 at 15:45 -0400, Pedro Vanzella wrote:
>> The gaming line of Logitech devices doesn't use the old hidpp20
>> feature
>> for battery level reporting. Instead, they report the current voltage
>> of the battery, in millivolts.
>> 
>> This patch set handles this case by adding a quirk to the devices we
>> know
>> to have this new feature, in both wired and wireless mode.
>> 
>> This version of the patch set is better split, as well as adding the
>> quirk to make sure we don't needlessly probe every device connected.
>> 
>> Pedro Vanzella (4):
>>  HID: hid-logitech-hidpp: add quirk to handle battery voltage
>>  HID: hid-logitech-hidpp: add function to query battery voltage
>>  HID: hid-logitech-hidpp: report battery voltage to the power supply
>>  HID: hid-logitech-hidpp: subscribe to battery voltage events
>> 
>> drivers/hid/hid-logitech-hidpp.c | 150
>> ++-
>> 1 file changed, 147 insertions(+), 3 deletions(-)
>> 
> 
> Hello,
> 
> Why using quirks? 0x1001 is a feature, it should be discoverable in
> IFeatureSet (0x0001). I don't understand the need to hardcode the
> supported devices, HID++ exists specifically to prevent that.
> 
> Wasn't this what you started in your previous patch? Why move away from
> it?

I was asked to change to conform to the way the other features were handled. 
I’ll let the maintainers decide, but I agree with you that the other way was 
better.

In fact, since the kernel only needs to support about half a dozen features, we 
could refactor the probe function to, well, probe the device for those features 
and set the capability flags. It looks to me like that would be cleaner and 
easier to extend (and would make it easier to support future devices).

> Thank you,
> Filipe Laíns
> 3DCE 51D6 0930 EBA4 7858 BA41 46F6 33CB B0EB 4BF2



[PATCH v2 2/4] HID: hid-logitech-hidpp: add function to query battery voltage

2019-06-05 Thread Pedro Vanzella
When the device is brought up, if it's one of the devices we know
supports battery voltage checking, figure out the feature index
and get the battery voltage and status.

If everything went correctly, record the fact that we're capable
of querying battery voltage.

Signed-off-by: Pedro Vanzella 
---
 drivers/hid/hid-logitech-hidpp.c | 94 
 1 file changed, 94 insertions(+)

diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c
index 8b38c14725b8..31e99363ab65 100644
--- a/drivers/hid/hid-logitech-hidpp.c
+++ b/drivers/hid/hid-logitech-hidpp.c
@@ -92,6 +92,7 @@ MODULE_PARM_DESC(disable_tap_to_click,
 #define HIDPP_CAPABILITY_HIDPP20_BATTERY   BIT(1)
 #define HIDPP_CAPABILITY_BATTERY_MILEAGE   BIT(2)
 #define HIDPP_CAPABILITY_BATTERY_LEVEL_STATUS  BIT(3)
+#define HIDPP_CAPABILITY_BATTERY_VOLTAGE   BIT(4)
 
 /*
  * There are two hidpp protocols in use, the first version hidpp10 is known
@@ -140,12 +141,14 @@ struct hidpp_report {
 struct hidpp_battery {
u8 feature_index;
u8 solar_feature_index;
+   u8 voltage_feature_index;
struct power_supply_desc desc;
struct power_supply *ps;
char name[64];
int status;
int capacity;
int level;
+   int voltage; /* in millivolts */
bool online;
 };
 
@@ -1224,6 +1227,92 @@ static int hidpp20_battery_event(struct hidpp_device 
*hidpp,
return 0;
 }
 
+/* -- 
*/
+/* 0x1001: Battery voltage
*/
+/* -- 
*/
+
+#define HIDPP_PAGE_BATTERY_VOLTAGE 0x1001
+
+#define CMD_BATTERY_VOLTAGE_GET_BATTERY_VOLTAGE 0x00
+
+static int hidpp20_battery_map_status_voltage(u8 data[3], int *voltage)
+{
+   int status;
+
+   switch (data[2]) {
+   case 0x00: /* discharging */
+   status = POWER_SUPPLY_STATUS_DISCHARGING;
+   break;
+   case 0x10: /* wireless charging */
+   case 0x80: /* charging */
+   status = POWER_SUPPLY_STATUS_CHARGING;
+   break;
+   case 0x81: /* fully charged */
+   status = POWER_SUPPLY_STATUS_FULL;
+   break;
+   default:
+   status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+   }
+
+   *voltage = (data[0] << 8) + data[1];
+
+   return status;
+}
+
+static int hidpp20_battery_get_battery_voltage(struct hidpp_device *hidpp,
+  u8 feature_index,
+  int *status, int *voltage)
+{
+   struct hidpp_report response;
+   int ret;
+   u8 *params = (u8 *)response.fap.params;
+
+   ret = hidpp_send_fap_command_sync(hidpp, feature_index,
+ 
CMD_BATTERY_VOLTAGE_GET_BATTERY_VOLTAGE,
+ NULL, 0, &response);
+
+   if (ret > 0) {
+   hid_err(hidpp->hid_dev, "%s: received protocol error 0x%02x\n",
+   __func__, ret);
+   return -EPROTO;
+   }
+   if (ret)
+   return ret;
+
+   hidpp->capabilities |= HIDPP_CAPABILITY_BATTERY_VOLTAGE;
+
+   *status = hidpp20_battery_map_status_voltage(params, voltage);
+
+   return 0;
+}
+
+static int hidpp20_query_battery_voltage_info(struct hidpp_device *hidpp)
+{
+   u8 feature_type;
+   int ret;
+   int status, voltage;
+
+   if (hidpp->battery.voltage_feature_index == 0xff) {
+   ret = hidpp_root_get_feature(hidpp, HIDPP_PAGE_BATTERY_VOLTAGE,
+
&hidpp->battery.voltage_feature_index,
+&feature_type);
+   if (ret)
+   return ret;
+   }
+
+   ret = hidpp20_battery_get_battery_voltage(hidpp,
+ 
hidpp->battery.voltage_feature_index,
+ &status, &voltage);
+
+   if (ret)
+   return ret;
+
+   hidpp->battery.status = status;
+   hidpp->battery.voltage = voltage;
+   hidpp->battery.online = status != POWER_SUPPLY_STATUS_NOT_CHARGING;
+
+   return 0;
+}
 static enum power_supply_property hidpp_battery_props[] = {
POWER_SUPPLY_PROP_ONLINE,
POWER_SUPPLY_PROP_STATUS,
@@ -3209,10 +3298,13 @@ static int hidpp_initialize_battery(struct hidpp_device 
*hidpp)
 
hidpp->battery.feature_index = 0xff;
hidpp->battery.solar_feature_index = 0xff;
+   hidpp->battery.voltage_feature_index = 0xff;
 
if (hidpp->protocol_major >= 2) {
if (hidpp->quirks & HIDPP_QUIRK_CLASS_K750)
ret = hidpp_solar_request_b

[PATCH v2 3/4] HID: hid-logitech-hidpp: report battery voltage to the power supply

2019-06-05 Thread Pedro Vanzella
If we know the device supports reading its voltage, report that.

Note that the protocol only gives us the current voltage in millivolts.

Signed-off-by: Pedro Vanzella 
---
 drivers/hid/hid-logitech-hidpp.c | 10 +-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c
index 31e99363ab65..d6c59b11b9d2 100644
--- a/drivers/hid/hid-logitech-hidpp.c
+++ b/drivers/hid/hid-logitech-hidpp.c
@@ -1322,6 +1322,7 @@ static enum power_supply_property hidpp_battery_props[] = 
{
POWER_SUPPLY_PROP_SERIAL_NUMBER,
0, /* placeholder for POWER_SUPPLY_PROP_CAPACITY, */
0, /* placeholder for POWER_SUPPLY_PROP_CAPACITY_LEVEL, */
+   0, /* placeholder for POWER_SUPPLY_PROP_VOLTAGE_NOW, */
 };
 
 static int hidpp_battery_get_property(struct power_supply *psy,
@@ -1359,6 +1360,9 @@ static int hidpp_battery_get_property(struct power_supply 
*psy,
case POWER_SUPPLY_PROP_SERIAL_NUMBER:
val->strval = hidpp->hid_dev->uniq;
break;
+   case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+   val->intval = hidpp->battery.voltage;
+   break;
default:
ret = -EINVAL;
break;
@@ -3331,7 +3335,7 @@ static int hidpp_initialize_battery(struct hidpp_device 
*hidpp)
if (!battery_props)
return -ENOMEM;
 
-   num_battery_props = ARRAY_SIZE(hidpp_battery_props) - 2;
+   num_battery_props = ARRAY_SIZE(hidpp_battery_props) - 3;
 
if (hidpp->capabilities & HIDPP_CAPABILITY_BATTERY_MILEAGE)
battery_props[num_battery_props++] =
@@ -3341,6 +3345,10 @@ static int hidpp_initialize_battery(struct hidpp_device 
*hidpp)
battery_props[num_battery_props++] =
POWER_SUPPLY_PROP_CAPACITY_LEVEL;
 
+   if (hidpp->capabilities & HIDPP_CAPABILITY_BATTERY_VOLTAGE)
+   battery_props[num_battery_props++] =
+   POWER_SUPPLY_PROP_VOLTAGE_NOW;
+
battery = &hidpp->battery;
 
n = atomic_inc_return(&battery_no) - 1;
-- 
2.21.0



[PATCH v2 4/4] HID: hid-logitech-hidpp: subscribe to battery voltage events

2019-06-05 Thread Pedro Vanzella
Like we do for other ways of interacting with the battery for other
devices, refresh the battery status and notify the power supply
subsystem of the changes in voltage and status.

Signed-off-by: Pedro Vanzella 
---
 drivers/hid/hid-logitech-hidpp.c | 33 
 1 file changed, 33 insertions(+)

diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c
index d6c59b11b9d2..a37bd0834335 100644
--- a/drivers/hid/hid-logitech-hidpp.c
+++ b/drivers/hid/hid-logitech-hidpp.c
@@ -1313,6 +1313,36 @@ static int hidpp20_query_battery_voltage_info(struct 
hidpp_device *hidpp)
 
return 0;
 }
+
+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;
+   bool changed;
+
+   if (report->fap.feature_index != hidpp->battery.voltage_feature_index ||
+   report->fap.funcindex_clientid !=
+   EVENT_BATTERY_LEVEL_STATUS_BROADCAST)
+   return 0;
+
+   status = hidpp20_battery_map_status_voltage(report->fap.params,
+   &voltage);
+
+   hidpp->battery.online = status != POWER_SUPPLY_STATUS_NOT_CHARGING;
+
+   changed = voltage != hidpp->battery.voltage ||
+ status != hidpp->battery.status;
+
+   if (changed) {
+   hidpp->battery.voltage = voltage;
+   hidpp->battery.status = status;
+   if (hidpp->battery.ps)
+   power_supply_changed(hidpp->battery.ps);
+   }
+   return 0;
+}
+
 static enum power_supply_property hidpp_battery_props[] = {
POWER_SUPPLY_PROP_ONLINE,
POWER_SUPPLY_PROP_STATUS,
@@ -3181,6 +3211,9 @@ static int hidpp_raw_hidpp_event(struct hidpp_device 
*hidpp, u8 *data,
ret = hidpp_solar_battery_event(hidpp, data, size);
if (ret != 0)
return ret;
+   ret = hidpp20_battery_voltage_event(hidpp, data, size);
+   if (ret != 0)
+   return ret;
}
 
if (hidpp->capabilities & HIDPP_CAPABILITY_HIDPP10_BATTERY) {
-- 
2.21.0



[PATCH v2 1/4] HID: hid-logitech-hidpp: add quirk to handle battery voltage

2019-06-05 Thread Pedro Vanzella
By adding this quirk we're able to handle battery voltage for devices
in both wired and wireless modes.

Signed-off-by: Pedro Vanzella 
---
 drivers/hid/hid-logitech-hidpp.c | 13 +++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c
index 72fc9c0566db..8b38c14725b8 100644
--- a/drivers/hid/hid-logitech-hidpp.c
+++ b/drivers/hid/hid-logitech-hidpp.c
@@ -63,7 +63,8 @@ MODULE_PARM_DESC(disable_tap_to_click,
 #define HIDPP_QUIRK_CLASS_G920 BIT(3)
 #define HIDPP_QUIRK_CLASS_K750 BIT(4)
 
-/* bits 2..20 are reserved for classes */
+/* bits 2..1f are reserved for classes */
+#define HIDPP_QUIRK_BATTERY_VOLTAGE_X1001  BIT(20)
 /* #define HIDPP_QUIRK_CONNECT_EVENTS  BIT(21) disabled */
 #define HIDPP_QUIRK_WTP_PHYSICAL_BUTTONS   BIT(22)
 #define HIDPP_QUIRK_NO_HIDINPUTBIT(23)
@@ -3733,6 +3734,13 @@ static const struct hid_device_id hidpp_devices[] = {
  LDJ_DEVICE(0xb305),
  .driver_data = HIDPP_QUIRK_HIDPP_CONSUMER_VENDOR_KEYS },
 
+   { /* Logitech G403 Gaming Mouse over Lightspeed */
+ LDJ_DEVICE(0x405d),
+ .driver_data = HIDPP_QUIRK_BATTERY_VOLTAGE_X1001 },
+   { /* Logitech G900 Gaming Mouse over Lightspeed */
+ LDJ_DEVICE(0x4053),
+ .driver_data = HIDPP_QUIRK_BATTERY_VOLTAGE_X1001 },
+
{ LDJ_DEVICE(HID_ANY_ID) },
 
{ /* Keyboard LX501 (Y-RR53) */
@@ -3752,7 +3760,8 @@ static const struct hid_device_id hidpp_devices[] = {
{ /* Logitech G700 Gaming Mouse over USB */
  HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC06B) },
{ /* Logitech G900 Gaming Mouse over USB */
- HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC081) },
+ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC081),
+ .driver_data = HIDPP_QUIRK_BATTERY_VOLTAGE_X1001 },
{ /* Logitech G920 Wheel over USB */
  HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 
USB_DEVICE_ID_LOGITECH_G920_WHEEL),
.driver_data = HIDPP_QUIRK_CLASS_G920 | 
HIDPP_QUIRK_FORCE_OUTPUT_REPORTS},
-- 
2.21.0



[PATCH v2 0/4] Read battery voltage from G403 and G900 mice

2019-06-05 Thread Pedro Vanzella
The gaming line of Logitech devices doesn't use the old hidpp20 feature
for battery level reporting. Instead, they report the current voltage
of the battery, in millivolts.

This patch set handles this case by adding a quirk to the devices we know
to have this new feature, in both wired and wireless mode.

This version of the patch set is better split, as well as adding the
quirk to make sure we don't needlessly probe every device connected.

Pedro Vanzella (4):
  HID: hid-logitech-hidpp: add quirk to handle battery voltage
  HID: hid-logitech-hidpp: add function to query battery voltage
  HID: hid-logitech-hidpp: report battery voltage to the power supply
  HID: hid-logitech-hidpp: subscribe to battery voltage events

 drivers/hid/hid-logitech-hidpp.c | 150 ++-
 1 file changed, 147 insertions(+), 3 deletions(-)

-- 
2.21.0



Re: [PATCH 2/2] HID: hid-logitech-hidpp: subscribe to battery voltage change events

2019-06-05 Thread Pedro Vanzella
Sorry for littering the list, but please ignore this patch set. I'll
have one that uses a quirk to detect the right devices in a little
while.

On 06/04, Pedro Vanzella wrote:
> Same as with the other ways of reporting battery status,
> fetch the battery voltage on raw hidpp events.
> 
> Signed-off-by: Pedro Vanzella 
> ---
>  drivers/hid/hid-logitech-hidpp.c | 32 
>  1 file changed, 32 insertions(+)
> 
> diff --git a/drivers/hid/hid-logitech-hidpp.c 
> b/drivers/hid/hid-logitech-hidpp.c
> index e68ea44b0d24..1eee206a0aed 100644
> --- a/drivers/hid/hid-logitech-hidpp.c
> +++ b/drivers/hid/hid-logitech-hidpp.c
> @@ -1313,6 +1313,35 @@ static int hidpp20_query_battery_voltage_info(struct 
> hidpp_device *hidpp)
>   return 0;
>  }
>  
> +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;
> + bool changed;
> +
> + if (report->fap.feature_index != hidpp->battery.voltage_feature_index ||
> + report->fap.funcindex_clientid !=
> + EVENT_BATTERY_LEVEL_STATUS_BROADCAST)
> + return 0;
> +
> + status = hidpp20_battery_map_status_voltage(report->fap.params,
> + &voltage);
> +
> + hidpp->battery.online = status != POWER_SUPPLY_STATUS_NOT_CHARGING;
> +
> + changed = voltage != hidpp->battery.voltage ||
> +   status != hidpp->battery.status;
> +
> + if (changed) {
> + hidpp->battery.voltage = voltage;
> + hidpp->battery.status = status;
> + if (hidpp->battery.ps)
> + power_supply_changed(hidpp->battery.ps);
> + }
> + return 0;
> +}
> +
>  static enum power_supply_property hidpp_battery_props[] = {
>   POWER_SUPPLY_PROP_ONLINE,
>   POWER_SUPPLY_PROP_STATUS,
> @@ -3181,6 +3210,9 @@ static int hidpp_raw_hidpp_event(struct hidpp_device 
> *hidpp, u8 *data,
>   ret = hidpp_solar_battery_event(hidpp, data, size);
>   if (ret != 0)
>   return ret;
> + ret = hidpp20_battery_voltage_event(hidpp, data, size);
> + if (ret != 0)
> + return ret;
>   }
>  
>   if (hidpp->capabilities & HIDPP_CAPABILITY_HIDPP10_BATTERY) {
> -- 
> 2.21.0
> 

-- 
Pedro Vanzella
pedrovanzella.com
#include 
Don't Panic


signature.asc
Description: PGP signature


[PATCH 2/2] HID: hid-logitech-hidpp: subscribe to battery voltage change events

2019-06-04 Thread Pedro Vanzella
Same as with the other ways of reporting battery status,
fetch the battery voltage on raw hidpp events.

Signed-off-by: Pedro Vanzella 
---
 drivers/hid/hid-logitech-hidpp.c | 32 
 1 file changed, 32 insertions(+)

diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c
index e68ea44b0d24..1eee206a0aed 100644
--- a/drivers/hid/hid-logitech-hidpp.c
+++ b/drivers/hid/hid-logitech-hidpp.c
@@ -1313,6 +1313,35 @@ static int hidpp20_query_battery_voltage_info(struct 
hidpp_device *hidpp)
return 0;
 }
 
+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;
+   bool changed;
+
+   if (report->fap.feature_index != hidpp->battery.voltage_feature_index ||
+   report->fap.funcindex_clientid !=
+   EVENT_BATTERY_LEVEL_STATUS_BROADCAST)
+   return 0;
+
+   status = hidpp20_battery_map_status_voltage(report->fap.params,
+   &voltage);
+
+   hidpp->battery.online = status != POWER_SUPPLY_STATUS_NOT_CHARGING;
+
+   changed = voltage != hidpp->battery.voltage ||
+ status != hidpp->battery.status;
+
+   if (changed) {
+   hidpp->battery.voltage = voltage;
+   hidpp->battery.status = status;
+   if (hidpp->battery.ps)
+   power_supply_changed(hidpp->battery.ps);
+   }
+   return 0;
+}
+
 static enum power_supply_property hidpp_battery_props[] = {
POWER_SUPPLY_PROP_ONLINE,
POWER_SUPPLY_PROP_STATUS,
@@ -3181,6 +3210,9 @@ static int hidpp_raw_hidpp_event(struct hidpp_device 
*hidpp, u8 *data,
ret = hidpp_solar_battery_event(hidpp, data, size);
if (ret != 0)
return ret;
+   ret = hidpp20_battery_voltage_event(hidpp, data, size);
+   if (ret != 0)
+   return ret;
}
 
if (hidpp->capabilities & HIDPP_CAPABILITY_HIDPP10_BATTERY) {
-- 
2.21.0



[PATCH 1/2] HID: hid-logitech-hidpp: Report battery voltage

2019-06-04 Thread Pedro Vanzella
Newer devices have a feature with id 0x1001 that reports the battery
voltage instead of the old feature with id 0x1000 that reports a
percentage.

This patch fetches and decodes this data for all devices that
support it.

Signed-off-by: Pedro Vanzella 
---
 drivers/hid/hid-logitech-hidpp.c | 108 ++-
 1 file changed, 106 insertions(+), 2 deletions(-)

diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c
index 72fc9c0566db..e68ea44b0d24 100644
--- a/drivers/hid/hid-logitech-hidpp.c
+++ b/drivers/hid/hid-logitech-hidpp.c
@@ -91,6 +91,7 @@ MODULE_PARM_DESC(disable_tap_to_click,
 #define HIDPP_CAPABILITY_HIDPP20_BATTERY   BIT(1)
 #define HIDPP_CAPABILITY_BATTERY_MILEAGE   BIT(2)
 #define HIDPP_CAPABILITY_BATTERY_LEVEL_STATUS  BIT(3)
+#define HIDPP_CAPABILITY_BATTERY_VOLTAGE   BIT(4)
 
 /*
  * There are two hidpp protocols in use, the first version hidpp10 is known
@@ -139,12 +140,14 @@ struct hidpp_report {
 struct hidpp_battery {
u8 feature_index;
u8 solar_feature_index;
+   u8 voltage_feature_index;
struct power_supply_desc desc;
struct power_supply *ps;
char name[64];
int status;
int capacity;
int level;
+   int voltage; /* in millivolts */
bool online;
 };
 
@@ -1223,6 +1226,93 @@ static int hidpp20_battery_event(struct hidpp_device 
*hidpp,
return 0;
 }
 
+/* -- 
*/
+/* 0x1001: Battery voltage
*/
+/* -- 
*/
+
+#define HIDPP_PAGE_BATTERY_VOLTAGE 0x1001
+
+#define CMD_BATTERY_VOLTAGE_GET_BATTERY_VOLTAGE 0x00
+
+static int hidpp20_battery_map_status_voltage(u8 data[3], int *voltage)
+{
+   int status;
+
+   switch (data[2]) {
+   case 0x00: /* discharging */
+   status = POWER_SUPPLY_STATUS_DISCHARGING;
+   break;
+   case 0x10: /* wireless charging */
+   case 0x80: /* charging */
+   status = POWER_SUPPLY_STATUS_CHARGING;
+   break;
+   case 0x81: /* fully charged */
+   status = POWER_SUPPLY_STATUS_FULL;
+   break;
+   default:
+   status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+   }
+
+   *voltage = (data[0] << 8) + data[1];
+
+   return status;
+}
+
+static int hidpp20_battery_get_battery_voltage(struct hidpp_device *hidpp,
+  u8 feature_index,
+  int *status, int *voltage)
+{
+   struct hidpp_report response;
+   int ret;
+   u8 *params = (u8 *)response.fap.params;
+
+   ret = hidpp_send_fap_command_sync(hidpp, feature_index,
+ 
CMD_BATTERY_VOLTAGE_GET_BATTERY_VOLTAGE,
+ NULL, 0, &response);
+
+   if (ret > 0) {
+   hid_err(hidpp->hid_dev, "%s: received protocol error 0x%02x\n",
+   __func__, ret);
+   return -EPROTO;
+   }
+   if (ret)
+   return ret;
+
+   hidpp->capabilities |= HIDPP_CAPABILITY_BATTERY_VOLTAGE;
+
+   *status = hidpp20_battery_map_status_voltage(params, voltage);
+
+   return 0;
+}
+
+static int hidpp20_query_battery_voltage_info(struct hidpp_device *hidpp)
+{
+   u8 feature_type;
+   int ret;
+   int status, voltage;
+
+   if (hidpp->battery.voltage_feature_index == 0xff) {
+   ret = hidpp_root_get_feature(hidpp, HIDPP_PAGE_BATTERY_VOLTAGE,
+
&hidpp->battery.voltage_feature_index,
+&feature_type);
+   if (ret)
+   return ret;
+   }
+
+   ret = hidpp20_battery_get_battery_voltage(hidpp,
+ 
hidpp->battery.voltage_feature_index,
+ &status, &voltage);
+
+   if (ret)
+   return ret;
+
+   hidpp->battery.status = status;
+   hidpp->battery.voltage = voltage;
+   hidpp->battery.online = status != POWER_SUPPLY_STATUS_NOT_CHARGING;
+
+   return 0;
+}
+
 static enum power_supply_property hidpp_battery_props[] = {
POWER_SUPPLY_PROP_ONLINE,
POWER_SUPPLY_PROP_STATUS,
@@ -1232,6 +1322,7 @@ static enum power_supply_property hidpp_battery_props[] = 
{
POWER_SUPPLY_PROP_SERIAL_NUMBER,
0, /* placeholder for POWER_SUPPLY_PROP_CAPACITY, */
0, /* placeholder for POWER_SUPPLY_PROP_CAPACITY_LEVEL, */
+   0, /* placeholder for POWER_SUPPLY_PROP_VOLTAGE_NOW, */
 };
 
 static int hidpp_battery_get_property(struct power_supply *psy,
@@ -1269,6 +1360,9 @@ static int hidpp_batte

Re: [PATCH] HID: hid-logitech-hidpp: detect wireless lightspeed devices

2019-06-03 Thread Pedro Vanzella
On 05/28, Benjamin Tissoires wrote:
> On Tue, May 28, 2019 at 6:30 PM Pedro Vanzella  
> wrote:
> >
> > Send a low device index when the device is connected via the lightspeed
> > receiver so that the receiver will pass the message along to the device
> > instead of responding. If we don't do that, we end up thinking it's a
> > hidpp10 device and miss out on all new features available to newer devices.
> >
> > This will enable correct detection of the following models:
> > G603, GPro, G305, G613, G900 and G903, and possibly others.
> 
> Thanks for the patch.
Thanks for reviewing it :)

> However, there is already support for this receiver in Linus' tree:
> https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/drivers/hid/hid-logitech-dj.c?id=f5fb57a74e88bd1788f57bf77d587c91d4dc9d57
> 
> With kernel 5.2-rc1, the connected device should already be handled by
> hid-logitech-hidpp :)
Why are the wireless receivers handled by hid-logitech-dj and the wired
mice handled by hid-logitech-hidpp? They are, in the end, all hidpp
devices, and having them all handled by the -hidpp driver with a quirk
class would allow us to check for support for the battery voltage
feature, as it seems to be an either-or scenario here.

- Pedro
> 
> Cheers,
> Benjamin
> 
> >
> > Signed-off-by: Pedro Vanzella 
> > ---
> >  drivers/hid/hid-logitech-hidpp.c | 10 +-
> >  1 file changed, 9 insertions(+), 1 deletion(-)
> >
> > diff --git a/drivers/hid/hid-logitech-hidpp.c 
> > b/drivers/hid/hid-logitech-hidpp.c
> > index 72fc9c0566db..621fce141d9f 100644
> > --- a/drivers/hid/hid-logitech-hidpp.c
> > +++ b/drivers/hid/hid-logitech-hidpp.c
> > @@ -62,6 +62,7 @@ MODULE_PARM_DESC(disable_tap_to_click,
> >  #define HIDPP_QUIRK_CLASS_K400 BIT(2)
> >  #define HIDPP_QUIRK_CLASS_G920 BIT(3)
> >  #define HIDPP_QUIRK_CLASS_K750 BIT(4)
> > +#define HIDPP_QUIRK_CLASS_LIGHTSPEED   BIT(5)
> >
> >  /* bits 2..20 are reserved for classes */
> >  /* #define HIDPP_QUIRK_CONNECT_EVENTS  BIT(21) disabled */
> > @@ -236,7 +237,11 @@ static int __hidpp_send_report(struct hid_device *hdev,
> >  * set the device_index as the receiver, it will be overwritten by
> >  * hid_hw_request if needed
> >  */
> > -   hidpp_report->device_index = 0xff;
> > +   if (hidpp->quirks & HIDPP_QUIRK_CLASS_LIGHTSPEED) {
> > +   hidpp_report->device_index = 0x01;
> > +   } else {
> > +   hidpp_report->device_index = 0xff;
> > +   }
> >
> > if (hidpp->quirks & HIDPP_QUIRK_FORCE_OUTPUT_REPORTS) {
> > ret = hid_hw_output_report(hdev, (u8 *)hidpp_report, 
> > fields_count);
> > @@ -3753,6 +3758,9 @@ static const struct hid_device_id hidpp_devices[] = {
> >   HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC06B) },
> > { /* Logitech G900 Gaming Mouse over USB */
> >   HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC081) },
> > +   { /* Logitech Gaming Mice over Lightspeed Receiver */
> > + HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC539),
> > + .driver_data = HIDPP_QUIRK_CLASS_LIGHTSPEED },
> > { /* Logitech G920 Wheel over USB */
> >   HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 
> > USB_DEVICE_ID_LOGITECH_G920_WHEEL),
> > .driver_data = HIDPP_QUIRK_CLASS_G920 | 
> > HIDPP_QUIRK_FORCE_OUTPUT_REPORTS},
> > --
> > 2.21.0
> >

-- 
Pedro Vanzella
pedrovanzella.com
#include 
Don't Panic


signature.asc
Description: PGP signature


[PATCH] HID: hid-logitech-hidpp: detect wireless lightspeed devices

2019-05-28 Thread Pedro Vanzella
Send a low device index when the device is connected via the lightspeed
receiver so that the receiver will pass the message along to the device
instead of responding. If we don't do that, we end up thinking it's a
hidpp10 device and miss out on all new features available to newer devices.

This will enable correct detection of the following models:
G603, GPro, G305, G613, G900 and G903, and possibly others.

Signed-off-by: Pedro Vanzella 
---
 drivers/hid/hid-logitech-hidpp.c | 10 +-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c
index 72fc9c0566db..621fce141d9f 100644
--- a/drivers/hid/hid-logitech-hidpp.c
+++ b/drivers/hid/hid-logitech-hidpp.c
@@ -62,6 +62,7 @@ MODULE_PARM_DESC(disable_tap_to_click,
 #define HIDPP_QUIRK_CLASS_K400 BIT(2)
 #define HIDPP_QUIRK_CLASS_G920 BIT(3)
 #define HIDPP_QUIRK_CLASS_K750 BIT(4)
+#define HIDPP_QUIRK_CLASS_LIGHTSPEED   BIT(5)
 
 /* bits 2..20 are reserved for classes */
 /* #define HIDPP_QUIRK_CONNECT_EVENTS  BIT(21) disabled */
@@ -236,7 +237,11 @@ static int __hidpp_send_report(struct hid_device *hdev,
 * set the device_index as the receiver, it will be overwritten by
 * hid_hw_request if needed
 */
-   hidpp_report->device_index = 0xff;
+   if (hidpp->quirks & HIDPP_QUIRK_CLASS_LIGHTSPEED) {
+   hidpp_report->device_index = 0x01;
+   } else {
+   hidpp_report->device_index = 0xff;
+   }
 
if (hidpp->quirks & HIDPP_QUIRK_FORCE_OUTPUT_REPORTS) {
ret = hid_hw_output_report(hdev, (u8 *)hidpp_report, 
fields_count);
@@ -3753,6 +3758,9 @@ static const struct hid_device_id hidpp_devices[] = {
  HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC06B) },
{ /* Logitech G900 Gaming Mouse over USB */
  HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC081) },
+   { /* Logitech Gaming Mice over Lightspeed Receiver */
+ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC539),
+ .driver_data = HIDPP_QUIRK_CLASS_LIGHTSPEED },
{ /* Logitech G920 Wheel over USB */
  HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 
USB_DEVICE_ID_LOGITECH_G920_WHEEL),
.driver_data = HIDPP_QUIRK_CLASS_G920 | 
HIDPP_QUIRK_FORCE_OUTPUT_REPORTS},
-- 
2.21.0