From: Lucas Zampieri Date: Sat, 14 Mar 2026 01:05:30 +0000 (+0000) Subject: HID: input: Add support for multiple batteries per device X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=4a58ae85c3f9b142ffba023d0f976978ade57d1b;p=thirdparty%2Fkernel%2Fstable.git HID: input: Add support for multiple batteries per device 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 Signed-off-by: Benjamin Tissoires --- diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 833df14ef68f..deb6239e375c 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -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; diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index b5d34658b68d..8fff185fe0e6 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -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; } diff --git a/include/linux/hid.h b/include/linux/hid.h index e4e2a5643bda..fb1a3f3ad9fa 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -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