]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
HID: input: Add HID_BATTERY_QUIRK_DYNAMIC for Elan touchscreens
authorHans de Goede <johannes.goede@oss.qualcomm.com>
Sat, 28 Feb 2026 14:52:58 +0000 (15:52 +0100)
committerJiri Kosina <jkosina@suse.com>
Wed, 11 Mar 2026 14:26:47 +0000 (15:26 +0100)
Elan touchscreens have a HID-battery device for the stylus which is always
there even if there is no stylus.

This is causing upower to report an empty battery for the stylus and some
desktop-environments will show a notification about this, which is quite
annoying.

Because of this the HID-battery is being ignored on all Elan I2c and USB
touchscreens, but this causes there to be no battery reporting for
the stylus at all.

This adds a new HID_BATTERY_QUIRK_DYNAMIC and uses these for the Elan
touchscreens.

This new quirks causes the present value of the battery to start at 0,
which will make userspace ignore it and only sets present to 1 after
receiving a battery input report which only happens when the stylus
gets in range.

Reported-by: ggrundik@gmail.com
Closes: https://bugzilla.kernel.org/show_bug.cgi?id=221118
Signed-off-by: Hans de Goede <johannes.goede@oss.qualcomm.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Signed-off-by: Jiri Kosina <jkosina@suse.com>
drivers/hid/hid-input.c
include/linux/hid.h

index 67ca1e88ce13f9f053c74a0ee0435b973fc618f0..8fc20df99b9768982ecb1b2dcf3df6345717df27 100644 (file)
@@ -354,6 +354,7 @@ static enum power_supply_property hidinput_battery_props[] = {
 #define HID_BATTERY_QUIRK_FEATURE      (1 << 1) /* ask for feature report */
 #define HID_BATTERY_QUIRK_IGNORE       (1 << 2) /* completely ignore the battery */
 #define HID_BATTERY_QUIRK_AVOID_QUERY  (1 << 3) /* do not query the battery */
+#define HID_BATTERY_QUIRK_DYNAMIC      (1 << 4) /* report present only after life signs */
 
 static const struct hid_device_id hid_battery_quirks[] = {
        { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE,
@@ -398,8 +399,8 @@ static const struct hid_device_id hid_battery_quirks[] = {
         * Elan HID touchscreens seem to all report a non present battery,
         * set HID_BATTERY_QUIRK_IGNORE for all Elan I2C and USB HID devices.
         */
-       { HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, HID_ANY_ID), HID_BATTERY_QUIRK_IGNORE },
-       { HID_USB_DEVICE(USB_VENDOR_ID_ELAN, HID_ANY_ID), HID_BATTERY_QUIRK_IGNORE },
+       { HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, HID_ANY_ID), HID_BATTERY_QUIRK_DYNAMIC },
+       { HID_USB_DEVICE(USB_VENDOR_ID_ELAN, HID_ANY_ID), HID_BATTERY_QUIRK_DYNAMIC },
        {}
 };
 
@@ -456,11 +457,14 @@ static int hidinput_get_battery_property(struct power_supply *psy,
        int ret = 0;
 
        switch (prop) {
-       case POWER_SUPPLY_PROP_PRESENT:
        case POWER_SUPPLY_PROP_ONLINE:
                val->intval = 1;
                break;
 
+       case POWER_SUPPLY_PROP_PRESENT:
+               val->intval = dev->battery_present;
+               break;
+
        case POWER_SUPPLY_PROP_CAPACITY:
                if (dev->battery_status != HID_BATTERY_REPORTED &&
                    !dev->battery_avoid_query) {
@@ -573,6 +577,8 @@ static int hidinput_setup_battery(struct hid_device *dev, unsigned report_type,
        if (quirks & HID_BATTERY_QUIRK_AVOID_QUERY)
                dev->battery_avoid_query = true;
 
+       dev->battery_present = (quirks & HID_BATTERY_QUIRK_DYNAMIC) ? false : true;
+
        dev->battery = power_supply_register(&dev->dev, psy_desc, &psy_cfg);
        if (IS_ERR(dev->battery)) {
                error = PTR_ERR(dev->battery);
@@ -628,6 +634,7 @@ static void hidinput_update_battery(struct hid_device *dev, unsigned int usage,
                return;
 
        if (hidinput_update_battery_charge_status(dev, usage, value)) {
+               dev->battery_present = true;
                power_supply_changed(dev->battery);
                return;
        }
@@ -643,6 +650,7 @@ static void hidinput_update_battery(struct hid_device *dev, unsigned int usage,
        if (dev->battery_status != HID_BATTERY_REPORTED ||
            capacity != dev->battery_capacity ||
            ktime_after(ktime_get_coarse(), dev->battery_ratelimit_time)) {
+               dev->battery_present = true;
                dev->battery_capacity = capacity;
                dev->battery_status = HID_BATTERY_REPORTED;
                dev->battery_ratelimit_time =
index 2990b9f94cb57d10e2eede1b7c81c09a51200dc3..31324609af4df18d2512249249c41b0b34a8a02d 100644 (file)
@@ -682,6 +682,7 @@ struct hid_device {
        __s32 battery_charge_status;
        enum hid_battery_status battery_status;
        bool battery_avoid_query;
+       bool battery_present;
        ktime_t battery_ratelimit_time;
 #endif