Re: [PATCH v2] HID: input: read battery capacity from its actual report offset
Jiri Kosina <[email protected]> Mon, 3 Aug 2026 20:38:39 +0200 (CEST)
| Newsgroups | org.kernel.vger.linux-input,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <[email protected]> |
On Tue, 28 Jul 2026, Jose Villase=F1or Montfort wrote:
> hidinput_query_battery_capacity() assumes the state-of-charge value is
> the first byte following the report ID (buf[1]) and ignores where the
> battery field actually sits within the report.
>=20
> An Apple Magic Trackpad 2 precedes the AbsoluteStateOfCharge byte with a
> byte of status flags in its battery reports, so this query returns the
> flags byte instead of the charge level.
>=20
> The device happens to make that easy to observe, because it exposes the
> same cell twice: its report descriptor declares AbsoluteStateOfCharge in
> two reports (0x90 and 0x9b), so hidinput_setup_battery() registers two
> power supplies. Only the first one is refreshed by hid-magicmouse -- it
> uses hid_get_battery(), which returns the first battery of the list --
> and that refresh goes through the report event path, which parses the
> field correctly. Nothing ever reports the second one, so every read of
> its capacity takes the query path above. On a USB-C Magic Trackpad over
> USB, on an unpatched 7.1.5:
>=20
> hid-<serial>-battery-144 =3D 100% (Charging) <- report event path
> hid-<serial>-battery-155 =3D 3% (Discharging) <- query path
>=20
> Both are the same physical battery. A raw HIDIOCGINPUT of the two
> reports at that same moment:
>=20
> report 0x90 -> [90 03 64]
> report 0x9b -> [9b 03 64 64 00 00 10 00 00 00 00 00 00 00]
> ^flags ^SoC =3D 0x64 =3D 100%
>=20
> The device answers correctly in both cases; only the offset the kernel
> reads the capacity from is wrong. 0x03 is the flags byte (present,
> charging), reported as "3%".
>=20
> Bluetooth takes the same query path for its capacity, where the trackpad
> reported a bogus near-constant ~4% -- 0b100, the FullyCharged flag --
> regardless of the real charge.
>=20
> Store the battery field's offset within the report at setup time and use
> it when querying, so the capacity is read from its real position. The
> report event path already parses the field correctly through the HID
> core; only the explicit GET_REPORT query was wrong.
>=20
> Devices whose capacity field is the first field in the report have a
> report_offset of 0 and are unaffected (buf[1 + 0] =3D=3D buf[1]).
>=20
> Fixes: 581c4484769e ("HID: input: map digitizer battery usage")
> Cc: [email protected]
> Signed-off-by: Jose Villase=F1or Montfort <[email protected]>
> ---
> No code changes since v1, only the commit message and tags.
>=20
> Changes in v2:
> - Added Fixes: 581c4484769e, which introduced
> hidinput_query_battery_capacity() with the hardcoded buf[1], and a
> stable tag.
> - Dropped the claim in v1 that USB is unaffected. It is not: the
> trackpad's descriptor declares AbsoluteStateOfCharge in two reports,
> hidinput_setup_battery() registers a power supply for each, and
> hid-magicmouse only ever refreshes the first one (hid_get_battery()
> returns the head of the list). The second power supply therefore
> serves every read from the broken query path, and shows 3% -- the
> flags byte -- permanently, on USB, next to the first one showing the
> correct 100%. That replaces the v1 example, since it puts the working
> and the broken path on the same cell at the same instant.
> - v1: https://lore.kernel.org/linux-input/20260702192139.114809-1-pepemon=
[email protected]/
Applied, thanks.
--=20
Jiri Kosina
SUSE Labs