aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDmitry Torokhov <dmitry.torokhov@gmail.com>2018-04-03 13:52:20 -0400
committerJiri Kosina <jkosina@suse.cz>2018-04-09 03:26:12 -0400
commit2e210bbb7429cdcf1a1a3ad00c1bf98bd9bf2452 (patch)
tree36ea9b6ca35b836bf7d78f945a7f46d56ca97823
parente8403b493fd5180e351ca67eb66406d95dadcd0b (diff)
HID: input: fix battery level reporting on BT mice
The commit 581c4484769e ("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: 581c4484769e ("HID: input: map digitizer battery usage") Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=198095 Cc: stable@vger.kernel.org Reported-and-tested-by: Martin van Es <martin@mrvanes.com> Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com> Signed-off-by: Jiri Kosina <jkosina@suse.cz>
-rw-r--r--drivers/hid/hid-input.c24
-rw-r--r--include/linux/hid.h9
2 files changed, 25 insertions, 8 deletions
diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c
index 6836a856c243..930652c25120 100644
--- a/drivers/hid/hid-input.c
+++ b/drivers/hid/hid-input.c
@@ -387,7 +387,8 @@ static int hidinput_get_battery_property(struct power_supply *psy,
387 break; 387 break;
388 388
389 case POWER_SUPPLY_PROP_CAPACITY: 389 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) {
391 value = hidinput_query_battery_capacity(dev); 392 value = hidinput_query_battery_capacity(dev);
392 if (value < 0) 393 if (value < 0)
393 return value; 394 return value;
@@ -403,17 +404,17 @@ static int hidinput_get_battery_property(struct power_supply *psy,
403 break; 404 break;
404 405
405 case POWER_SUPPLY_PROP_STATUS: 406 case POWER_SUPPLY_PROP_STATUS:
406 if (!dev->battery_reported && 407 if (dev->battery_status != HID_BATTERY_REPORTED &&
407 dev->battery_report_type == HID_FEATURE_REPORT) { 408 !dev->battery_avoid_query) {
408 value = hidinput_query_battery_capacity(dev); 409 value = hidinput_query_battery_capacity(dev);
409 if (value < 0) 410 if (value < 0)
410 return value; 411 return value;
411 412
412 dev->battery_capacity = value; 413 dev->battery_capacity = value;
413 dev->battery_reported = true; 414 dev->battery_status = HID_BATTERY_QUERIED;
414 } 415 }
415 416
416 if (!dev->battery_reported) 417 if (dev->battery_status == HID_BATTERY_UNKNOWN)
417 val->intval = POWER_SUPPLY_STATUS_UNKNOWN; 418 val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
418 else if (dev->battery_capacity == 100) 419 else if (dev->battery_capacity == 100)
419 val->intval = POWER_SUPPLY_STATUS_FULL; 420 val->intval = POWER_SUPPLY_STATUS_FULL;
@@ -486,6 +487,14 @@ static int hidinput_setup_battery(struct hid_device *dev, unsigned report_type,
486 dev->battery_report_type = report_type; 487 dev->battery_report_type = report_type;
487 dev->battery_report_id = field->report->id; 488 dev->battery_report_id = field->report->id;
488 489
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
489 dev->battery = power_supply_register(&dev->dev, psy_desc, &psy_cfg); 498 dev->battery = power_supply_register(&dev->dev, psy_desc, &psy_cfg);
490 if (IS_ERR(dev->battery)) { 499 if (IS_ERR(dev->battery)) {
491 error = PTR_ERR(dev->battery); 500 error = PTR_ERR(dev->battery);
@@ -530,9 +539,10 @@ static void hidinput_update_battery(struct hid_device *dev, int value)
530 539
531 capacity = hidinput_scale_battery_capacity(dev, value); 540 capacity = hidinput_scale_battery_capacity(dev, value);
532 541
533 if (!dev->battery_reported || capacity != dev->battery_capacity) { 542 if (dev->battery_status != HID_BATTERY_REPORTED ||
543 capacity != dev->battery_capacity) {
534 dev->battery_capacity = capacity; 544 dev->battery_capacity = capacity;
535 dev->battery_reported = true; 545 dev->battery_status = HID_BATTERY_REPORTED;
536 power_supply_changed(dev->battery); 546 power_supply_changed(dev->battery);
537 } 547 }
538} 548}
diff --git a/include/linux/hid.h b/include/linux/hid.h
index 8da3e1f48195..26240a22978a 100644
--- a/include/linux/hid.h
+++ b/include/linux/hid.h
@@ -516,6 +516,12 @@ enum hid_type {
516 HID_TYPE_USBNONE 516 HID_TYPE_USBNONE
517}; 517};
518 518
519enum 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
519struct hid_driver; 525struct hid_driver;
520struct hid_ll_driver; 526struct hid_ll_driver;
521 527
@@ -558,7 +564,8 @@ struct hid_device { /* device report descriptor */
558 __s32 battery_max; 564 __s32 battery_max;
559 __s32 battery_report_type; 565 __s32 battery_report_type;
560 __s32 battery_report_id; 566 __s32 battery_report_id;
561 bool battery_reported; 567 enum hid_battery_status battery_status;
568 bool battery_avoid_query;
562#endif 569#endif
563 570
564 unsigned int status; /* see STAT flags above */ 571 unsigned int status; /* see STAT flags above */