Skip to content

Commit 2e210bb

Browse files
dtorJiri Kosina
authored andcommitted
HID: input: fix battery level reporting on BT mice
The commit 581c448 ("HID: input: map digitizer battery usage") assumed that devices having input (qas opposed to feature) report for battery strength would report the data on their own, without the need to be polled by the kernel; unfortunately it is not so. Many wireless mice do not send unsolicited reports with battery strength data and have to be polled explicitly. As a complication, stylus devices on digitizers are not normally connected to the base and thus can not be polled - the base can only determine battery strength in the stylus when it is in proximity. To solve this issue, we add a special flag that tells the kernel to avoid polling the device (and expect unsolicited reports) and set it when report field with physical usage of digitizer stylus (HID_DG_STYLUS). Unless this flag is set, and we have not seen the unsolicited reports, the kernel will attempt to poll the device when userspace attempts to read "capacity" and "state" attributes of power_supply object corresponding to the devices battery. Fixes: 581c448 ("HID: input: map digitizer battery usage") Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=198095 Cc: [email protected] Reported-and-tested-by: Martin van Es <[email protected]> Signed-off-by: Dmitry Torokhov <[email protected]> Signed-off-by: Jiri Kosina <[email protected]>
1 parent e8403b4 commit 2e210bb

File tree

2 files changed

+25
-8
lines changed

2 files changed

+25
-8
lines changed

drivers/hid/hid-input.c

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -387,7 +387,8 @@ static int hidinput_get_battery_property(struct power_supply *psy,
387387
break;
388388

389389
case POWER_SUPPLY_PROP_CAPACITY:
390-
if (dev->battery_report_type == HID_FEATURE_REPORT) {
390+
if (dev->battery_status != HID_BATTERY_REPORTED &&
391+
!dev->battery_avoid_query) {
391392
value = hidinput_query_battery_capacity(dev);
392393
if (value < 0)
393394
return value;
@@ -403,17 +404,17 @@ static int hidinput_get_battery_property(struct power_supply *psy,
403404
break;
404405

405406
case POWER_SUPPLY_PROP_STATUS:
406-
if (!dev->battery_reported &&
407-
dev->battery_report_type == HID_FEATURE_REPORT) {
407+
if (dev->battery_status != HID_BATTERY_REPORTED &&
408+
!dev->battery_avoid_query) {
408409
value = hidinput_query_battery_capacity(dev);
409410
if (value < 0)
410411
return value;
411412

412413
dev->battery_capacity = value;
413-
dev->battery_reported = true;
414+
dev->battery_status = HID_BATTERY_QUERIED;
414415
}
415416

416-
if (!dev->battery_reported)
417+
if (dev->battery_status == HID_BATTERY_UNKNOWN)
417418
val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
418419
else if (dev->battery_capacity == 100)
419420
val->intval = POWER_SUPPLY_STATUS_FULL;
@@ -486,6 +487,14 @@ static int hidinput_setup_battery(struct hid_device *dev, unsigned report_type,
486487
dev->battery_report_type = report_type;
487488
dev->battery_report_id = field->report->id;
488489

490+
/*
491+
* Stylus is normally not connected to the device and thus we
492+
* can't query the device and get meaningful battery strength.
493+
* We have to wait for the device to report it on its own.
494+
*/
495+
dev->battery_avoid_query = report_type == HID_INPUT_REPORT &&
496+
field->physical == HID_DG_STYLUS;
497+
489498
dev->battery = power_supply_register(&dev->dev, psy_desc, &psy_cfg);
490499
if (IS_ERR(dev->battery)) {
491500
error = PTR_ERR(dev->battery);
@@ -530,9 +539,10 @@ static void hidinput_update_battery(struct hid_device *dev, int value)
530539

531540
capacity = hidinput_scale_battery_capacity(dev, value);
532541

533-
if (!dev->battery_reported || capacity != dev->battery_capacity) {
542+
if (dev->battery_status != HID_BATTERY_REPORTED ||
543+
capacity != dev->battery_capacity) {
534544
dev->battery_capacity = capacity;
535-
dev->battery_reported = true;
545+
dev->battery_status = HID_BATTERY_REPORTED;
536546
power_supply_changed(dev->battery);
537547
}
538548
}

include/linux/hid.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -516,6 +516,12 @@ enum hid_type {
516516
HID_TYPE_USBNONE
517517
};
518518

519+
enum hid_battery_status {
520+
HID_BATTERY_UNKNOWN = 0,
521+
HID_BATTERY_QUERIED, /* Kernel explicitly queried battery strength */
522+
HID_BATTERY_REPORTED, /* Device sent unsolicited battery strength report */
523+
};
524+
519525
struct hid_driver;
520526
struct hid_ll_driver;
521527

@@ -558,7 +564,8 @@ struct hid_device { /* device report descriptor */
558564
__s32 battery_max;
559565
__s32 battery_report_type;
560566
__s32 battery_report_id;
561-
bool battery_reported;
567+
enum hid_battery_status battery_status;
568+
bool battery_avoid_query;
562569
#endif
563570

564571
unsigned int status; /* see STAT flags above */

0 commit comments

Comments
 (0)