]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
HID: input: Add support for multiple batteries per device
authorLucas Zampieri <lcasmz54@gmail.com>
Sat, 14 Mar 2026 01:05:30 +0000 (01:05 +0000)
committerBenjamin Tissoires <bentiss@kernel.org>
Thu, 19 Mar 2026 14:52:45 +0000 (15:52 +0100)
Add support for HID devices that report multiple batteries, each
identified by its report ID.

The hid_device->battery pointer is replaced with a batteries list.
Batteries are named using the pattern hid-{uniq}-battery-{report_id}.
The hid_get_battery() helper returns the first battery in the list for
backwards compatibility with single-battery drivers.

Signed-off-by: Lucas Zampieri <lcasmz54@gmail.com>
Signed-off-by: Benjamin Tissoires <bentiss@kernel.org>
drivers/hid/hid-core.c
drivers/hid/hid-input.c
include/linux/hid.h

index 833df14ef68f158260a62f61928627230d231af7..deb6239e375c87fde1c60b4e3fd76297c1056424 100644 (file)
@@ -2991,6 +2991,10 @@ struct hid_device *hid_allocate_device(void)
        mutex_init(&hdev->ll_open_lock);
        kref_init(&hdev->ref);
 
+#ifdef CONFIG_HID_BATTERY_STRENGTH
+       INIT_LIST_HEAD(&hdev->batteries);
+#endif
+
        ret = hid_bpf_device_init(hdev);
        if (ret)
                goto out_err;
index b5d34658b68df8169a827af14a33072c9cdebc6f..8fff185fe0e6bb7db2e36bd447d09b887680d2d0 100644 (file)
@@ -507,6 +507,18 @@ static int hidinput_get_battery_property(struct power_supply *psy,
        return ret;
 }
 
+static struct hid_battery *hidinput_find_battery(struct hid_device *dev,
+                                                int report_id)
+{
+       struct hid_battery *bat;
+
+       list_for_each_entry(bat, &dev->batteries, list) {
+               if (bat->report_id == report_id)
+                       return bat;
+       }
+       return NULL;
+}
+
 static int hidinput_setup_battery(struct hid_device *dev, unsigned report_type,
                                  struct hid_field *field, bool is_percentage)
 {
@@ -517,13 +529,15 @@ static int hidinput_setup_battery(struct hid_device *dev, unsigned report_type,
        s32 min, max;
        int error;
 
-       if (dev->battery)
-               return 0;       /* already initialized? */
+       /* Check if battery for this report ID already exists */
+       if (hidinput_find_battery(dev, field->report->id))
+               return 0;
 
        quirks = find_battery_quirk(dev);
 
-       hid_dbg(dev, "device %x:%x:%x %d quirks %d\n",
-               dev->bus, dev->vendor, dev->product, dev->version, quirks);
+       hid_dbg(dev, "device %x:%x:%x %d quirks %d report_id %d\n",
+               dev->bus, dev->vendor, dev->product, dev->version, quirks,
+               field->report->id);
 
        if (quirks & HID_BATTERY_QUIRK_IGNORE)
                return 0;
@@ -538,9 +552,11 @@ static int hidinput_setup_battery(struct hid_device *dev, unsigned report_type,
                goto err_free_bat;
        }
 
-       psy_desc->name = devm_kasprintf(&dev->dev, GFP_KERNEL, "hid-%s-battery",
+       psy_desc->name = devm_kasprintf(&dev->dev, GFP_KERNEL,
+                                       "hid-%s-battery-%d",
                                        strlen(dev->uniq) ?
-                                               dev->uniq : dev_name(&dev->dev));
+                                               dev->uniq : dev_name(&dev->dev),
+                                       field->report->id);
        if (!psy_desc->name) {
                error = -ENOMEM;
                goto err_free_desc;
@@ -593,7 +609,7 @@ static int hidinput_setup_battery(struct hid_device *dev, unsigned report_type,
        }
 
        power_supply_powers(bat->ps, &dev->dev);
-       dev->battery = bat;
+       list_add_tail(&bat->list, &dev->batteries);
        return 0;
 
 err_free_name:
@@ -602,7 +618,6 @@ err_free_desc:
        devm_kfree(&dev->dev, psy_desc);
 err_free_bat:
        devm_kfree(&dev->dev, bat);
-       dev->battery = NULL;
        return error;
 }
 
@@ -620,12 +635,13 @@ static bool hidinput_update_battery_charge_status(struct hid_battery *bat,
        return false;
 }
 
-static void hidinput_update_battery(struct hid_device *dev, unsigned int usage,
-                                   int value)
+static void hidinput_update_battery(struct hid_device *dev, int report_id,
+                                   unsigned int usage, int value)
 {
-       struct hid_battery *bat = dev->battery;
+       struct hid_battery *bat;
        int capacity;
 
+       bat = hidinput_find_battery(dev, report_id);
        if (!bat)
                return;
 
@@ -661,8 +677,8 @@ static int hidinput_setup_battery(struct hid_device *dev, unsigned report_type,
        return 0;
 }
 
-static void hidinput_update_battery(struct hid_device *dev, unsigned int usage,
-                                   int value)
+static void hidinput_update_battery(struct hid_device *dev, int report_id,
+                                   unsigned int usage, int value)
 {
 }
 #endif /* CONFIG_HID_BATTERY_STRENGTH */
@@ -1546,7 +1562,7 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct
                return;
 
        if (usage->type == EV_PWR) {
-               hidinput_update_battery(hid, usage->hid, value);
+               hidinput_update_battery(hid, report->id, usage->hid, value);
                return;
        }
 
index e4e2a5643bdaa0e7baeae0e6f7453f8453998e5f..fb1a3f3ad9fa548dd1ee62eb42805ee286cdbf6c 100644 (file)
@@ -648,6 +648,7 @@ enum hid_battery_status {
  * @avoid_query: if true, avoid querying battery (e.g., for stylus)
  * @present: if true, battery is present (may be dynamic)
  * @ratelimit_time: rate limiting for battery reports
+ * @list: list node for linking into hid_device's battery list
  */
 struct hid_battery {
        struct hid_device *dev;
@@ -662,6 +663,7 @@ struct hid_battery {
        bool avoid_query;
        bool present;
        ktime_t ratelimit_time;
+       struct list_head list;
 };
 
 struct hid_driver;
@@ -700,9 +702,10 @@ struct hid_device {
 #ifdef CONFIG_HID_BATTERY_STRENGTH
        /*
         * Power supply information for HID devices which report
-        * battery strength. battery is non-NULL if successfully registered.
+        * battery strength. Each battery is tracked separately in the
+        * batteries list.
         */
-       struct hid_battery *battery;
+       struct list_head batteries;
 #endif
 
        unsigned long status;                                           /* see STAT flags above */
@@ -766,7 +769,9 @@ static inline void hid_set_drvdata(struct hid_device *hdev, void *data)
 #ifdef CONFIG_HID_BATTERY_STRENGTH
 static inline struct hid_battery *hid_get_battery(struct hid_device *hdev)
 {
-       return hdev->battery;
+       if (list_empty(&hdev->batteries))
+               return NULL;
+       return list_first_entry(&hdev->batteries, struct hid_battery, list);
 }
 #endif