* Re: [PATCH 10/10] arm64: dts: qcom: sdm845-google: Add STM FTS touchscreen support
From: David Heidelberg @ 2026-03-15 15:52 UTC (permalink / raw)
To: Konrad Dybcio, Dmitry Torokhov, Maxime Coquelin, Alexandre Torgue,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Henrik Rydberg,
Bjorn Andersson, Konrad Dybcio
Cc: Petr Hodina, linux-input, linux-stm32, linux-arm-kernel,
linux-kernel, Krzysztof Kozlowski, devicetree, linux-arm-msm,
phone-devel
In-Reply-To: <b747b545-12c7-4e33-95ae-ffa114fa13ec@oss.qualcomm.com>
On 02/03/2026 12:06, Konrad Dybcio wrote:
> On 3/1/26 6:51 PM, David Heidelberg via B4 Relay wrote:
>> From: Petr Hodina <petr.hodina@protonmail.com>
>>
>> Basic touchscreen connected to second i2c bus.
>>
>> Signed-off-by: Petr Hodina <petr.hodina@protonmail.com>
>> Co-developed-by: David Heidelberg <david@ixit.cz>
>> Signed-off-by: David Heidelberg <david@ixit.cz>
>> ---
>> arch/arm64/boot/dts/qcom/sdm845-google-blueline.dts | 21 ++++++++++++++++++++-
>> 1 file changed, 20 insertions(+), 1 deletion(-)
>>
>> diff --git a/arch/arm64/boot/dts/qcom/sdm845-google-blueline.dts b/arch/arm64/boot/dts/qcom/sdm845-google-blueline.dts
>> index fa89be500fb85..2501104b06e1b 100644
>> --- a/arch/arm64/boot/dts/qcom/sdm845-google-blueline.dts
>> +++ b/arch/arm64/boot/dts/qcom/sdm845-google-blueline.dts
>> @@ -26,7 +26,26 @@ &i2c2 {
>>
>> status = "okay";
>>
>> - /* ST,FTS @ 49 */
>> + touchscreen@49 {
>> + compatible = "st,stmfts5";
>> + reg = <0x49>;
>> +
>> + pinctrl-0 = <&touchscreen_pins &touchscreen_reset>;
>> + pinctrl-names = "default";
>> +
>> + interrupt-parent = <&tlmm>;
>> + interrupts = <125 IRQ_TYPE_LEVEL_LOW>;
>> +
>> + irq-gpios = <&tlmm 125 GPIO_ACTIVE_HIGH>;
>
> This is an anti-pattern - you can translate the GPIO handle to an
> IRQ handle, but unless the hardware is spectacularly odd, an interrupt
> reference is usually what you're after
Thanks, dropped in follow-up version.
>
>> + switch-gpios = <&tlmm 136 GPIO_ACTIVE_HIGH>;
>> + reset-gpios = <&tlmm 99 GPIO_ACTIVE_LOW>;
>> +
>> + avdd-supply = <&vreg_l14a_1p8>;
>> + vdd-supply = <&vreg_l19a_3p3>;
>> +
>> + touchscreen-size-x = <1079>;
>> + touchscreen-size-y = <2159>;
>
> Are you sure about these off-by-ones?
These we're extracted from st,maxcoords and yet, it should be 1080, 2160. Fixed
in follow-up version.
Thanks
David
>
> FWIW
>
> input/touchscreen.c:
>
> touchscreen_get_prop_u32(dev, "touchscreen-size-x",
> input_abs_get_max(input,
> axis_x) + 1,
>
> notice ^
>
> Konrad
--
David Heidelberg
^ permalink raw reply
* Re: [PATCH v3] dt-bindings: input: touchscreen: edt-ft5x06: Add FocalTech FT3519
From: Krzysztof Kozlowski @ 2026-03-15 8:44 UTC (permalink / raw)
To: Bhushan Shah
Cc: Dmitry Torokhov, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
linux-input, devicetree, linux-kernel
In-Reply-To: <20260314-edt-ft3519-v3-1-5ee91b408ed6@machinesoul.in>
On Sat, Mar 14, 2026 at 08:27:58PM +0530, Bhushan Shah wrote:
> Document FocalTech FT3519 support by adding the compatible. It's 10
> point touchscreen, which is compatible with FT3518
>
> Signed-off-by: Bhushan Shah <bhushan.shah@machinesoul.in>
> ---
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
Best regards,
Krzysztof
^ permalink raw reply
* Re: [PATCH v7 1/2] rust: core abstractions for HID drivers
From: kernel test robot @ 2026-03-15 4:03 UTC (permalink / raw)
To: Rahul Rameshbabu, linux-input, linux-kernel, rust-for-linux
Cc: oe-kbuild-all, a.hindborg, alex.gaynor, aliceryhl,
benjamin.tissoires, benno.lossin, bjorn3_gh, boqun.feng, dakr,
db48x, gary, jikos, ojeda, peter.hutterer, tmgross, linuxhid,
Rahul Rameshbabu
In-Reply-To: <20260314215527.57575-2-sergeantsagara@protonmail.com>
Hi Rahul,
kernel test robot noticed the following build errors:
[auto build test ERROR on b3d161f22ba9b2dc16bb82aa2b8515d98c99624f]
url: https://github.com/intel-lab-lkp/linux/commits/Rahul-Rameshbabu/rust-core-abstractions-for-HID-drivers/20260315-071004
base: b3d161f22ba9b2dc16bb82aa2b8515d98c99624f
patch link: https://lore.kernel.org/r/20260314215527.57575-2-sergeantsagara%40protonmail.com
patch subject: [PATCH v7 1/2] rust: core abstractions for HID drivers
config: x86_64-rhel-9.4-rust (https://download.01.org/0day-ci/archive/20260315/202603150411.1TqpNCVC-lkp@intel.com/config)
compiler: clang version 20.1.8 (https://github.com/llvm/llvm-project 87f0227cb60147a26a1eeb4fb06e3b505e9c7261)
rustc: rustc 1.88.0 (6b00bc388 2025-06-23)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260315/202603150411.1TqpNCVC-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202603150411.1TqpNCVC-lkp@intel.com/
All errors (new ones prefixed by >>):
PATH=/opt/cross/clang-20/bin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
INFO PATH=/opt/cross/rustc-1.88.0-bindgen-0.72.1/cargo/bin:/opt/cross/clang-20/bin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
/usr/bin/timeout -k 100 12h /usr/bin/make KCFLAGS=\ -fno-crash-diagnostics\ -Wno-error=return-type\ -Wreturn-type\ -funsigned-char\ -Wundef\ -falign-functions=64 W=1 --keep-going LLVM=1 -j32 -C source O=/kbuild/obj/consumer/x86_64-rhel-9.4-rust ARCH=x86_64 SHELL=/bin/bash rustfmtcheck
make: Entering directory '/kbuild/src/consumer'
make[1]: Entering directory '/kbuild/obj/consumer/x86_64-rhel-9.4-rust'
>> Diff in rust/kernel/hid.rs:6:
//!
//! C header: [`include/linux/hid.h`](srctree/include/linux/hid.h)
-use core::{
- marker::PhantomData,
- ptr::{
- addr_of_mut,
- NonNull, //
- } //
-};
use crate::{
device,
device_id::{
Diff in rust/kernel/hid.rs:24:
impl_flags,
prelude::*,
types::Opaque, //
+};
+use core::{
+ marker::PhantomData,
+ ptr::{
+ addr_of_mut,
+ NonNull, //
+ }, //
};
use kernel::fmt;
>> Diff in rust/kernel/hid.rs:6:
//!
//! C header: [`include/linux/hid.h`](srctree/include/linux/hid.h)
-use core::{
- marker::PhantomData,
- ptr::{
- addr_of_mut,
- NonNull, //
- } //
-};
use crate::{
device,
device_id::{
Diff in rust/kernel/hid.rs:24:
impl_flags,
prelude::*,
types::Opaque, //
+};
+use core::{
+ marker::PhantomData,
+ ptr::{
+ addr_of_mut,
+ NonNull, //
+ }, //
};
use kernel::fmt;
>> Diff in rust/kernel/lib.rs:97:
pub mod firmware;
pub mod fmt;
pub mod fs;
+#[cfg(CONFIG_RUST_HID_ABSTRACTIONS)]
+pub mod hid;
#[cfg(CONFIG_I2C = "y")]
pub mod i2c;
pub mod id_pool;
Diff in rust/kernel/lib.rs:103:
#[doc(hidden)]
pub mod impl_flags;
-#[cfg(CONFIG_RUST_HID_ABSTRACTIONS)]
-pub mod hid;
pub mod init;
pub mod io;
pub mod ioctl;
make[2]: *** [Makefile:1912: rustfmt] Error 123
make[2]: Target 'rustfmtcheck' not remade because of errors.
make[1]: Leaving directory '/kbuild/obj/consumer/x86_64-rhel-9.4-rust'
make[1]: *** [Makefile:248: __sub-make] Error 2
make[1]: Target 'rustfmtcheck' not remade because of errors.
make: *** [Makefile:248: __sub-make] Error 2
make: Target 'rustfmtcheck' not remade because of errors.
make: Leaving directory '/kbuild/src/consumer'
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply
* [PATCH v7 2/2] rust: hid: Glorious PC Gaming Race Model O and O- mice reference driver
From: Rahul Rameshbabu @ 2026-03-14 21:56 UTC (permalink / raw)
To: linux-input, linux-kernel, rust-for-linux
Cc: a.hindborg, alex.gaynor, aliceryhl, benjamin.tissoires,
benno.lossin, bjorn3_gh, boqun.feng, dakr, db48x, gary, jikos,
ojeda, peter.hutterer, tmgross, linuxhid, Rahul Rameshbabu
In-Reply-To: <20260314215527.57575-1-sergeantsagara@protonmail.com>
Demonstrate how to perform a report fixup from a Rust HID driver. The mice
specify the const flag incorrectly in the consumer input report descriptor,
which leads to inputs being ignored. Correctly patch the report descriptor
for the Model O and O- mice.
Portions of the HID report post-fixup:
device 0:0
...
0x81, 0x06, // Input (Data,Var,Rel) 84
...
0x81, 0x06, // Input (Data,Var,Rel) 112
...
0x81, 0x06, // Input (Data,Var,Rel) 140
Signed-off-by: Rahul Rameshbabu <sergeantsagara@protonmail.com>
---
Notes:
Changelog:
v6->v7:
* Removed duplicate driver/hid/hid_glorious_rust.rs source
* Made use of HID item bitflags
* Updated report_fixup to use a single lifetime
v5->v6:
* NONE
v4->v5:
* NONE
v3->v4:
* Removed specifying tree in MAINTAINERS file since that is up for
debate
* Minor rebase cleanup
* Moved driver logic under drivers/hid/rust
* Use hex instead of decimal for the report descriptor comparisons
v2->v3:
* Fixed docstring formatting
* Updated MAINTAINERS file based on v1 and v2 discussion
v1->v2:
* Use vendor id and device id from drivers/hid/hid-ids.h bindings
* Make use for dev_err! as appropriate
MAINTAINERS | 6 +++
drivers/hid/hid-glorious.c | 2 +
drivers/hid/rust/Kconfig | 16 +++++++
drivers/hid/rust/Makefile | 6 +++
drivers/hid/rust/hid_glorious_rust.rs | 67 +++++++++++++++++++++++++++
5 files changed, 97 insertions(+)
create mode 100644 drivers/hid/rust/Makefile
create mode 100644 drivers/hid/rust/hid_glorious_rust.rs
diff --git a/MAINTAINERS b/MAINTAINERS
index be3638209bf3..66d977114efa 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -10805,6 +10805,12 @@ L: platform-driver-x86@vger.kernel.org
S: Maintained
F: drivers/platform/x86/gigabyte-wmi.c
+GLORIOUS RUST DRIVER [RUST]
+M: Rahul Rameshbabu <sergeantsagara@protonmail.com>
+L: linux-input@vger.kernel.org
+S: Maintained
+F: drivers/hid/rust/hid_glorious_rust.rs
+
GNSS SUBSYSTEM
M: Johan Hovold <johan@kernel.org>
S: Maintained
diff --git a/drivers/hid/hid-glorious.c b/drivers/hid/hid-glorious.c
index 5bbd81248053..d7362852c20f 100644
--- a/drivers/hid/hid-glorious.c
+++ b/drivers/hid/hid-glorious.c
@@ -76,8 +76,10 @@ static int glorious_probe(struct hid_device *hdev,
}
static const struct hid_device_id glorious_devices[] = {
+#if !IS_ENABLED(CONFIG_HID_GLORIOUS_RUST)
{ HID_USB_DEVICE(USB_VENDOR_ID_SINOWEALTH,
USB_DEVICE_ID_GLORIOUS_MODEL_O) },
+#endif
{ HID_USB_DEVICE(USB_VENDOR_ID_SINOWEALTH,
USB_DEVICE_ID_GLORIOUS_MODEL_D) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LAVIEW,
diff --git a/drivers/hid/rust/Kconfig b/drivers/hid/rust/Kconfig
index d3247651829e..d7a1bf26bed0 100644
--- a/drivers/hid/rust/Kconfig
+++ b/drivers/hid/rust/Kconfig
@@ -9,4 +9,20 @@ config RUST_HID_ABSTRACTIONS
Adds support needed for HID drivers written in Rust. It provides a
wrapper around the C hid core.
+if RUST_HID_ABSTRACTIONS
+
+menu "Special HID drivers"
+
+config HID_GLORIOUS_RUST
+ tristate "Glorious O and O- mice Rust reference driver"
+ depends on USB_HID
+ depends on RUST_HID_ABSTRACTIONS
+ help
+ Support for Glorious PC Gaming Race O and O- mice
+ in Rust.
+
+endmenu # Special HID drivers
+
+endif # RUST_HID_ABSTRACTIONS
+
endmenu # Rust HID support
diff --git a/drivers/hid/rust/Makefile b/drivers/hid/rust/Makefile
new file mode 100644
index 000000000000..6676030a2f87
--- /dev/null
+++ b/drivers/hid/rust/Makefile
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for Rust HID support
+#
+
+obj-$(CONFIG_HID_GLORIOUS_RUST) += hid_glorious_rust.o
diff --git a/drivers/hid/rust/hid_glorious_rust.rs b/drivers/hid/rust/hid_glorious_rust.rs
new file mode 100644
index 000000000000..09951fcdfb0d
--- /dev/null
+++ b/drivers/hid/rust/hid_glorious_rust.rs
@@ -0,0 +1,67 @@
+// SPDX-License-Identifier: GPL-2.0
+
+// Copyright (C) 2026 Rahul Rameshbabu <sergeantsagara@protonmail.com>
+
+//! Rust reference HID driver for Glorious Model O and O- mice.
+
+use kernel::{
+ self,
+ bindings,
+ device,
+ hid,
+ prelude::*, //
+};
+
+struct GloriousRust;
+
+kernel::hid_device_table!(
+ HID_TABLE,
+ MODULE_HID_TABLE,
+ <GloriousRust as hid::Driver>::IdInfo,
+ [(
+ hid::DeviceId::new_usb(
+ hid::Group::Generic,
+ bindings::USB_VENDOR_ID_SINOWEALTH,
+ bindings::USB_DEVICE_ID_GLORIOUS_MODEL_O,
+ ),
+ (),
+ )]
+);
+
+#[vtable]
+impl hid::Driver for GloriousRust {
+ type IdInfo = ();
+ const ID_TABLE: hid::IdTable<Self::IdInfo> = &HID_TABLE;
+
+ /// Fix the Glorious Model O and O- consumer input report descriptor to use
+ /// the variable and relative flag, while clearing the const flag.
+ ///
+ /// Without this fixup, inputs from the mice will be ignored.
+ fn report_fixup<'a>(hdev: &hid::Device<device::Core>, rdesc: &'a mut [u8]) -> &'a [u8] {
+ if rdesc.len() == 213
+ && (rdesc[84] == 0x81 && rdesc[85] == 0x3)
+ && (rdesc[112] == 0x81 && rdesc[113] == 0x3)
+ && (rdesc[140] == 0x81 && rdesc[141] == 0x3)
+ {
+ dev_info!(
+ hdev.as_ref(),
+ "patching Glorious Model O consumer control report descriptor\n"
+ );
+
+ let desc_val = u32::from(hid::Item::Relative | hid::Item::Variable) as u8;
+ rdesc[85] = desc_val;
+ rdesc[113] = desc_val;
+ rdesc[141] = desc_val;
+ }
+
+ rdesc
+ }
+}
+
+kernel::module_hid_driver! {
+ type: GloriousRust,
+ name: "GloriousRust",
+ authors: ["Rahul Rameshbabu <sergeantsagara@protonmail.com>"],
+ description: "Rust reference HID driver for Glorious Model O and O- mice",
+ license: "GPL",
+}
--
2.52.0
^ permalink raw reply related
* [PATCH v7 1/2] rust: core abstractions for HID drivers
From: Rahul Rameshbabu @ 2026-03-14 21:56 UTC (permalink / raw)
To: linux-input, linux-kernel, rust-for-linux
Cc: a.hindborg, alex.gaynor, aliceryhl, benjamin.tissoires,
benno.lossin, bjorn3_gh, boqun.feng, dakr, db48x, gary, jikos,
ojeda, peter.hutterer, tmgross, linuxhid, Rahul Rameshbabu
In-Reply-To: <20260314215527.57575-1-sergeantsagara@protonmail.com>
These abstractions enable the development of HID drivers in Rust by binding
with the HID core C API. They provide Rust types that map to the
equivalents in C. In this initial draft, only hid_device and hid_device_id
are provided direct Rust type equivalents. hid_driver is specially wrapped
with a custom Driver type. The module_hid_driver! macro provides analogous
functionality to its C equivalent. Only the .report_fixup callback is
binded to Rust so far.
Future work for these abstractions would include more bindings for common
HID-related types, such as hid_field, hid_report_enum, and hid_report as
well as more bus callbacks. Providing Rust equivalents to useful core HID
functions will also be necessary for HID driver development in Rust.
Signed-off-by: Rahul Rameshbabu <sergeantsagara@protonmail.com>
---
Notes:
Changelog:
v6->v7:
* Implemented HID items as bitflags
* Used #[inline] for into_u16 and Device::as_raw
* Defined a custom GroupUnknownError type
* Fixed the description of DeviceId::new_usb
* Made report_fixup use a single lifetime value due to references
being covariant
* Simplified error handling in report_fixup_callback
v5->v6:
* Converted From<u16> for Group to TryFrom<u16> to properly handle
error case
* Renamed into method for Group to into_u16 to not conflate with the
From trait
* Refactored new upstream changes to RegistrationOps
* Implemented DriverLayout trait for hid::Adapter<T>
v4->v5:
* Add rust/ to drivers/hid/Makefile
* Implement RawDeviceIdIndex trait
v3->v4:
* Removed specifying tree in MAINTAINERS file since that is up for
debate
* Minor rebase cleanup
* Moved driver logic under drivers/hid/rust
v2->v3:
* Implemented AlwaysRefCounted trait using embedded struct device's
reference counts instead of the separate reference counter in struct
hid_device
* Used &raw mut as appropriate
* Binded include/linux/device.h for get_device and put_device
* Cleaned up various comment related formatting
* Minified dev_err! format string
* Updated Group enum to be repr(u16)
* Implemented From<u16> trait for Group
* Added TODO comment when const_trait_impl stabilizes
* Made group getter functions return a Group variant instead of a raw
number
* Made sure example code builds
v1->v2:
* Binded drivers/hid/hid-ids.h for use in Rust drivers
* Remove pre-emptive referencing of a C HID driver instance before
it is fully initialized in the driver registration path
* Moved static getters to generic Device trait implementation, so
they can be used by all device::DeviceContext
* Use core macros for supporting DeviceContext transitions
* Implemented the AlwaysRefCounted and AsRef traits
* Make use for dev_err! as appropriate
RFC->v1:
* Use Danilo's core infrastructure
* Account for HID device groups
* Remove probe and remove callbacks
* Implement report_fixup support
* Properly comment code including SAFETY comments
MAINTAINERS | 8 +
drivers/hid/Kconfig | 2 +
drivers/hid/Makefile | 2 +
drivers/hid/rust/Kconfig | 12 +
rust/bindings/bindings_helper.h | 3 +
rust/kernel/hid.rs | 509 ++++++++++++++++++++++++++++++++
rust/kernel/lib.rs | 2 +
7 files changed, 538 insertions(+)
create mode 100644 drivers/hid/rust/Kconfig
create mode 100644 rust/kernel/hid.rs
diff --git a/MAINTAINERS b/MAINTAINERS
index 77fdfcb55f06..be3638209bf3 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -11316,6 +11316,14 @@ F: include/uapi/linux/hid*
F: samples/hid/
F: tools/testing/selftests/hid/
+HID CORE LAYER [RUST]
+M: Rahul Rameshbabu <sergeantsagara@protonmail.com>
+R: Benjamin Tissoires <bentiss@kernel.org>
+L: linux-input@vger.kernel.org
+S: Maintained
+F: drivers/hid/rust/*.rs
+F: rust/kernel/hid.rs
+
HID LOGITECH DRIVERS
R: Filipe Laíns <lains@riseup.net>
L: linux-input@vger.kernel.org
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index c1d9f7c6a5f2..750c2d49a806 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -1439,6 +1439,8 @@ endmenu
source "drivers/hid/bpf/Kconfig"
+source "drivers/hid/rust/Kconfig"
+
source "drivers/hid/i2c-hid/Kconfig"
source "drivers/hid/intel-ish-hid/Kconfig"
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index e01838239ae6..b78ab84c47b4 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -8,6 +8,8 @@ hid-$(CONFIG_HID_HAPTIC) += hid-haptic.o
obj-$(CONFIG_HID_BPF) += bpf/
+obj-$(CONFIG_RUST_HID_ABSTRACTIONS) += rust/
+
obj-$(CONFIG_HID) += hid.o
obj-$(CONFIG_UHID) += uhid.o
diff --git a/drivers/hid/rust/Kconfig b/drivers/hid/rust/Kconfig
new file mode 100644
index 000000000000..d3247651829e
--- /dev/null
+++ b/drivers/hid/rust/Kconfig
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: GPL-2.0-only
+menu "Rust HID support"
+
+config RUST_HID_ABSTRACTIONS
+ bool "Rust HID abstractions support"
+ depends on RUST
+ depends on HID=y
+ help
+ Adds support needed for HID drivers written in Rust. It provides a
+ wrapper around the C hid core.
+
+endmenu # Rust HID support
diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
index 083cc44aa952..200e58af27a3 100644
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@ -48,6 +48,7 @@
#include <linux/cpumask.h>
#include <linux/cred.h>
#include <linux/debugfs.h>
+#include <linux/device.h>
#include <linux/device/faux.h>
#include <linux/dma-direction.h>
#include <linux/dma-mapping.h>
@@ -60,6 +61,8 @@
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/io-pgtable.h>
+#include <linux/hid.h>
+#include "../../drivers/hid/hid-ids.h"
#include <linux/ioport.h>
#include <linux/jiffies.h>
#include <linux/jump_label.h>
diff --git a/rust/kernel/hid.rs b/rust/kernel/hid.rs
new file mode 100644
index 000000000000..cae9b1980553
--- /dev/null
+++ b/rust/kernel/hid.rs
@@ -0,0 +1,509 @@
+// SPDX-License-Identifier: GPL-2.0
+
+// Copyright (C) 2026 Rahul Rameshbabu <sergeantsagara@protonmail.com>
+
+//! Abstractions for the HID interface.
+//!
+//! C header: [`include/linux/hid.h`](srctree/include/linux/hid.h)
+
+use core::{
+ marker::PhantomData,
+ ptr::{
+ addr_of_mut,
+ NonNull, //
+ } //
+};
+use crate::{
+ device,
+ device_id::{
+ RawDeviceId,
+ RawDeviceIdIndex, //
+ },
+ driver,
+ error::*,
+ impl_flags,
+ prelude::*,
+ types::Opaque, //
+};
+use kernel::fmt;
+
+impl_flags!(
+ /// Represents a combination of HID items.
+ #[derive(Debug, Clone, Default, Copy, PartialEq, Eq)]
+ pub struct Items(u32);
+
+ /// Represents a single HID item.
+ ///
+ /// Refer to [Device Class Definition for HID 1.11]
+ /// Section 6.2.2.5 Input, Output, and Feature Items.
+ ///
+ /// [Device Class Definition for HID 1.11]: https://www.usb.org/sites/default/files/hid1_11.pdf
+ #[derive(Debug, Clone, Copy, PartialEq, Eq)]
+ pub enum Item {
+ /// Indicates the item is static read-only.
+ Constant = bindings::HID_MAIN_ITEM_CONSTANT,
+
+ /// Indicates the item represents data from a physical control.
+ Variable = bindings::HID_MAIN_ITEM_VARIABLE,
+
+ /// Indicates the item should be treated as a relative change from the
+ /// previous report.
+ Relative = bindings::HID_MAIN_ITEM_RELATIVE,
+
+ /// Indicates the item should wrap around when reaching the extreme high
+ /// or extreme low values.
+ Wrap = bindings::HID_MAIN_ITEM_WRAP,
+
+ /// Indicates the item should wrap around when reaching the extreme high
+ /// or extreme low values.
+ NonLinear = bindings::HID_MAIN_ITEM_NONLINEAR,
+
+ /// Indicates whether the control has a preferred state it will
+ /// physically return to without user intervention.
+ NoPreferred = bindings::HID_MAIN_ITEM_NO_PREFERRED,
+
+ /// Indicates whether the control has a physical state where it will not
+ /// send any reports.
+ NullState = bindings::HID_MAIN_ITEM_NULL_STATE,
+
+ /// Indicates whether the control requires host system logic to change
+ /// state.
+ Volatile = bindings::HID_MAIN_ITEM_VOLATILE,
+
+ /// Indicates whether the item is fixed size or a variable buffer of
+ /// bytes.
+ BufferedBytw = bindings::HID_MAIN_ITEM_BUFFERED_BYTE,
+ }
+);
+
+/// HID device groups are intended to help categories HID devices based on a set
+/// of common quirks and logic that they will require to function correctly.
+#[repr(u16)]
+pub enum Group {
+ /// Used to match a device against any group when probing.
+ Any = bindings::HID_GROUP_ANY as u16,
+
+ /// Indicates a generic device that should need no custom logic from the
+ /// core HID stack.
+ Generic = bindings::HID_GROUP_GENERIC as u16,
+
+ /// Maps multitouch devices to hid-multitouch instead of hid-generic.
+ Multitouch = bindings::HID_GROUP_MULTITOUCH as u16,
+
+ /// Used for autodetecing and mapping of HID sensor hubs to
+ /// hid-sensor-hub.
+ SensorHub = bindings::HID_GROUP_SENSOR_HUB as u16,
+
+ /// Used for autodetecing and mapping Win 8 multitouch devices to set the
+ /// needed quirks.
+ MultitouchWin8 = bindings::HID_GROUP_MULTITOUCH_WIN_8 as u16,
+
+ // Vendor-specific device groups.
+ /// Used to distinguish Synpatics touchscreens from other products. The
+ /// touchscreens will be handled by hid-multitouch instead, while everything
+ /// else will be managed by hid-rmi.
+ RMI = bindings::HID_GROUP_RMI as u16,
+
+ /// Used for hid-core handling to automatically identify Wacom devices and
+ /// have them probed by hid-wacom.
+ Wacom = bindings::HID_GROUP_WACOM as u16,
+
+ /// Used by logitech-djreceiver and logitech-djdevice to autodetect if
+ /// devices paied to the DJ receivers are DJ devices and handle them with
+ /// the device driver.
+ LogitechDJDevice = bindings::HID_GROUP_LOGITECH_DJ_DEVICE as u16,
+
+ /// Since the Valve Steam Controller only has vendor-specific usages,
+ /// prevent hid-generic from parsing its reports since there would be
+ /// nothing hid-generic could do for the device.
+ Steam = bindings::HID_GROUP_STEAM as u16,
+
+ /// Used to differentiate 27 Mhz frequency Logitech DJ devices from other
+ /// Logitech DJ devices.
+ Logitech27MHzDevice = bindings::HID_GROUP_LOGITECH_27MHZ_DEVICE as u16,
+
+ /// Used for autodetecting and mapping Vivaldi devices to hid-vivaldi.
+ Vivaldi = bindings::HID_GROUP_VIVALDI as u16,
+}
+
+// TODO: use `const_trait_impl` once stabilized:
+//
+// ```
+// impl const From<Group> for u16 {
+// /// [`Group`] variants are represented by [`u16`] values.
+// fn from(value: Group) -> Self {
+// value as Self
+// }
+// }
+// ```
+impl Group {
+ /// Internal function used to convert [`Group`] variants into [`u16`].
+ #[inline]
+ const fn into_u16(self) -> u16 {
+ self as u16
+ }
+}
+
+/// Error type used when a HID group value from C cannot be safely represented
+/// with the Rust `Group` abstraction.
+#[derive(Debug, Clone)]
+pub struct GroupUnknownError(u16);
+
+impl From<GroupUnknownError> for u16 {
+ fn from(value: GroupUnknownError) -> Self {
+ value.0
+ }
+}
+
+impl fmt::Display for GroupUnknownError {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "Unknown HID group {:#x} encountered!", self.0)
+ }
+}
+
+impl TryFrom<u16> for Group {
+ type Error = GroupUnknownError;
+
+ /// Valid [`u16`] values can be converted to [`Group`] variants.
+ fn try_from(value: u16) -> Result<Group, Self::Error> {
+ match value.into() {
+ bindings::HID_GROUP_GENERIC => Ok(Group::Generic),
+ bindings::HID_GROUP_MULTITOUCH => Ok(Group::Multitouch),
+ bindings::HID_GROUP_SENSOR_HUB => Ok(Group::SensorHub),
+ bindings::HID_GROUP_MULTITOUCH_WIN_8 => Ok(Group::MultitouchWin8),
+ bindings::HID_GROUP_RMI => Ok(Group::RMI),
+ bindings::HID_GROUP_WACOM => Ok(Group::Wacom),
+ bindings::HID_GROUP_LOGITECH_DJ_DEVICE => Ok(Group::LogitechDJDevice),
+ bindings::HID_GROUP_STEAM => Ok(Group::Steam),
+ bindings::HID_GROUP_LOGITECH_27MHZ_DEVICE => Ok(Group::Logitech27MHzDevice),
+ bindings::HID_GROUP_VIVALDI => Ok(Group::Vivaldi),
+ _ => Err(GroupUnknownError(value)),
+ }
+ }
+}
+
+/// The HID device representation.
+///
+/// This structure represents the Rust abstraction for a C `struct hid_device`.
+/// The implementation abstracts the usage of an already existing C `struct
+/// hid_device` within Rust code that we get passed from the C side.
+///
+/// # Invariants
+///
+/// A [`Device`] instance represents a valid `struct hid_device` created by the
+/// C portion of the kernel.
+#[repr(transparent)]
+pub struct Device<Ctx: device::DeviceContext = device::Normal>(
+ Opaque<bindings::hid_device>,
+ PhantomData<Ctx>,
+);
+
+impl<Ctx: device::DeviceContext> Device<Ctx> {
+ #[inline]
+ fn as_raw(&self) -> *mut bindings::hid_device {
+ self.0.get()
+ }
+
+ /// Returns the HID transport bus ID.
+ pub fn bus(&self) -> u16 {
+ // SAFETY: `self.as_raw` is a valid pointer to a `struct hid_device`
+ unsafe { *self.as_raw() }.bus
+ }
+
+ /// Returns the HID report group.
+ pub fn group(&self) -> Result<Group, GroupUnknownError> {
+ // SAFETY: `self.as_raw` is a valid pointer to a `struct hid_device`
+ unsafe { *self.as_raw() }.group.try_into()
+ }
+
+ /// Returns the HID vendor ID.
+ pub fn vendor(&self) -> u32 {
+ // SAFETY: `self.as_raw` is a valid pointer to a `struct hid_device`
+ unsafe { *self.as_raw() }.vendor
+ }
+
+ /// Returns the HID product ID.
+ pub fn product(&self) -> u32 {
+ // SAFETY: `self.as_raw` is a valid pointer to a `struct hid_device`
+ unsafe { *self.as_raw() }.product
+ }
+}
+
+// SAFETY: `Device` is a transparent wrapper of a type that doesn't depend on `Device`'s generic
+// argument.
+kernel::impl_device_context_deref!(unsafe { Device });
+kernel::impl_device_context_into_aref!(Device);
+
+// SAFETY: Instances of `Device` are always reference-counted.
+unsafe impl crate::types::AlwaysRefCounted for Device {
+ fn inc_ref(&self) {
+ // SAFETY: The existence of a shared reference guarantees that the refcount is non-zero.
+ unsafe { bindings::get_device(&raw mut (*self.as_raw()).dev) };
+ }
+
+ unsafe fn dec_ref(obj: NonNull<Self>) {
+ // SAFETY: The safety requirements guarantee that the refcount is non-zero.
+ unsafe { bindings::put_device(&raw mut (*obj.cast::<bindings::hid_device>().as_ptr()).dev) }
+ }
+}
+
+impl<Ctx: device::DeviceContext> AsRef<device::Device<Ctx>> for Device<Ctx> {
+ fn as_ref(&self) -> &device::Device<Ctx> {
+ // SAFETY: By the type invariant of `Self`, `self.as_raw()` is a pointer to a valid
+ // `struct hid_device`.
+ let dev = unsafe { addr_of_mut!((*self.as_raw()).dev) };
+
+ // SAFETY: `dev` points to a valid `struct device`.
+ unsafe { device::Device::from_raw(dev) }
+ }
+}
+
+/// Abstraction for the HID device ID structure `struct hid_device_id`.
+#[repr(transparent)]
+#[derive(Clone, Copy)]
+pub struct DeviceId(bindings::hid_device_id);
+
+impl DeviceId {
+ /// Create a new `hid::DeviceId` from a group, vendor ID, and device ID
+ /// number.
+ ///
+ /// Equivalent to C's `HID_USB_DEVICE` macro.
+ pub const fn new_usb(group: Group, vendor: u32, product: u32) -> Self {
+ Self(bindings::hid_device_id {
+ bus: 0x3, // BUS_USB
+ group: group.into_u16(),
+ vendor,
+ product,
+ driver_data: 0,
+ })
+ }
+
+ /// Returns the HID transport bus ID.
+ pub fn bus(&self) -> u16 {
+ self.0.bus
+ }
+
+ /// Returns the HID report group.
+ pub fn group(&self) -> Result<Group, GroupUnknownError> {
+ self.0.group.try_into()
+ }
+
+ /// Returns the HID vendor ID.
+ pub fn vendor(&self) -> u32 {
+ self.0.vendor
+ }
+
+ /// Returns the HID product ID.
+ pub fn product(&self) -> u32 {
+ self.0.product
+ }
+}
+
+// SAFETY:
+// * `DeviceId` is a `#[repr(transparent)` wrapper of `hid_device_id` and does not add
+// additional invariants, so it's safe to transmute to `RawType`.
+// * `DRIVER_DATA_OFFSET` is the offset to the `driver_data` field.
+unsafe impl RawDeviceId for DeviceId {
+ type RawType = bindings::hid_device_id;
+}
+
+// SAFETY: `DRIVER_DATA_OFFSET` is the offset to the `driver_data` field.
+unsafe impl RawDeviceIdIndex for DeviceId {
+ const DRIVER_DATA_OFFSET: usize = core::mem::offset_of!(bindings::hid_device_id, driver_data);
+
+ fn index(&self) -> usize {
+ self.0.driver_data
+ }
+}
+
+/// [`IdTable`] type for HID.
+pub type IdTable<T> = &'static dyn kernel::device_id::IdTable<DeviceId, T>;
+
+/// Create a HID [`IdTable`] with its alias for modpost.
+#[macro_export]
+macro_rules! hid_device_table {
+ ($table_name:ident, $module_table_name:ident, $id_info_type: ty, $table_data: expr) => {
+ const $table_name: $crate::device_id::IdArray<
+ $crate::hid::DeviceId,
+ $id_info_type,
+ { $table_data.len() },
+ > = $crate::device_id::IdArray::new($table_data);
+
+ $crate::module_device_table!("hid", $module_table_name, $table_name);
+ };
+}
+
+/// The HID driver trait.
+///
+/// # Examples
+///
+/// ```
+/// use kernel::{bindings, device, hid};
+///
+/// struct MyDriver;
+///
+/// kernel::hid_device_table!(
+/// HID_TABLE,
+/// MODULE_HID_TABLE,
+/// <MyDriver as hid::Driver>::IdInfo,
+/// [(
+/// hid::DeviceId::new_usb(
+/// hid::Group::Steam,
+/// bindings::USB_VENDOR_ID_VALVE,
+/// bindings::USB_DEVICE_ID_STEAM_DECK,
+/// ),
+/// (),
+/// )]
+/// );
+///
+/// #[vtable]
+/// impl hid::Driver for MyDriver {
+/// type IdInfo = ();
+/// const ID_TABLE: hid::IdTable<Self::IdInfo> = &HID_TABLE;
+///
+/// /// This function is optional to implement.
+/// fn report_fixup<'a>(_hdev: &hid::Device<device::Core>, rdesc: &'a mut [u8]) -> &'a [u8] {
+/// // Perform some report descriptor fixup.
+/// rdesc
+/// }
+/// }
+/// ```
+/// Drivers must implement this trait in order to get a HID driver registered.
+/// Please refer to the `Adapter` documentation for an example.
+#[vtable]
+pub trait Driver: Send {
+ /// The type holding information about each device id supported by the driver.
+ // TODO: Use `associated_type_defaults` once stabilized:
+ //
+ // ```
+ // type IdInfo: 'static = ();
+ // ```
+ type IdInfo: 'static;
+
+ /// The table of device ids supported by the driver.
+ const ID_TABLE: IdTable<Self::IdInfo>;
+
+ /// Called before report descriptor parsing. Can be used to mutate the
+ /// report descriptor before the core HID logic processes the descriptor.
+ /// Useful for problematic report descriptors that prevent HID devices from
+ /// functioning correctly.
+ ///
+ /// Optional to implement.
+ fn report_fixup<'a>(_hdev: &Device<device::Core>, _rdesc: &'a mut [u8]) -> &'a [u8] {
+ build_error!(VTABLE_DEFAULT_ERROR)
+ }
+}
+
+/// An adapter for the registration of HID drivers.
+pub struct Adapter<T: Driver>(T);
+
+// SAFETY:
+// - `bindings::hid_driver` is a C type declared as `repr(C)`.
+// - `T` is the type of the driver's device private data.
+// - `struct hid_driver` embeds a `struct device_driver`.
+// - `DEVICE_DRIVER_OFFSET` is the correct byte offset to the embedded `struct device_driver`.
+unsafe impl<T: Driver + 'static> driver::DriverLayout for Adapter<T> {
+ type DriverType = bindings::hid_driver;
+ type DriverData = T;
+ const DEVICE_DRIVER_OFFSET: usize = core::mem::offset_of!(Self::DriverType, driver);
+}
+
+// SAFETY: A call to `unregister` for a given instance of `DriverType` is guaranteed to be valid if
+// a preceding call to `register` has been successful.
+unsafe impl<T: Driver + 'static> driver::RegistrationOps for Adapter<T> {
+ unsafe fn register(
+ hdrv: &Opaque<Self::DriverType>,
+ name: &'static CStr,
+ module: &'static ThisModule,
+ ) -> Result {
+ // SAFETY: It's safe to set the fields of `struct hid_driver` on initialization.
+ unsafe {
+ (*hdrv.get()).name = name.as_char_ptr();
+ (*hdrv.get()).id_table = T::ID_TABLE.as_ptr();
+ (*hdrv.get()).report_fixup = if T::HAS_REPORT_FIXUP {
+ Some(Self::report_fixup_callback)
+ } else {
+ None
+ };
+ }
+
+ // SAFETY: `hdrv` is guaranteed to be a valid `DriverType`
+ to_result(unsafe {
+ bindings::__hid_register_driver(hdrv.get(), module.0, name.as_char_ptr())
+ })
+ }
+
+ unsafe fn unregister(hdrv: &Opaque<Self::DriverType>) {
+ // SAFETY: `hdrv` is guaranteed to be a valid `DriverType`
+ unsafe { bindings::hid_unregister_driver(hdrv.get()) }
+ }
+}
+
+impl<T: Driver + 'static> Adapter<T> {
+ extern "C" fn report_fixup_callback(
+ hdev: *mut bindings::hid_device,
+ buf: *mut u8,
+ size: *mut kernel::ffi::c_uint,
+ ) -> *const u8 {
+ // SAFETY: The HID subsystem only ever calls the report_fixup callback
+ // with a valid pointer to a `struct hid_device`.
+ //
+ // INVARIANT: `hdev` is valid for the duration of
+ // `report_fixup_callback()`.
+ let hdev = unsafe { &*hdev.cast::<Device<device::Core>>() };
+
+ // SAFETY: The HID subsystem only ever calls the report_fixup callback
+ // with a valid pointer to a `kernel::ffi::c_uint`.
+ //
+ // INVARIANT: `size` is valid for the duration of
+ // `report_fixup_callback()`.
+ let buf_len: usize = unsafe { *size } as usize;
+
+ // Build a mutable Rust slice from `buf` and `size`.
+ //
+ // SAFETY: The HID subsystem only ever calls the `report_fixup callback`
+ // with a valid pointer to a `u8` buffer.
+ //
+ // INVARIANT: `buf` is valid for the duration of
+ // `report_fixup_callback()`.
+ let rdesc_slice = unsafe { core::slice::from_raw_parts_mut(buf, buf_len) };
+ let rdesc_slice = T::report_fixup(hdev, rdesc_slice);
+
+ // HID report descriptors are typically not gigabytes in size. A Rust
+ // `panic!` can only be triggered if a report descriptor greater than
+ // 4GB is used.
+ let tmp_size: kernel::ffi::c_uint = rdesc_slice
+ .len()
+ .try_into()
+ .expect("Fixed report descriptor is too large!");
+
+ // SAFETY: The HID subsystem only ever calls the report_fixup callback
+ // with a valid pointer to a `kernel::ffi::c_uint`.
+ //
+ // INVARIANT: `size` is valid for the duration of
+ // `report_fixup_callback()`.
+ unsafe { *size = tmp_size };
+
+ rdesc_slice.as_ptr()
+ }
+}
+
+/// Declares a kernel module that exposes a single HID driver.
+///
+/// # Examples
+///
+/// ```ignore
+/// kernel::module_hid_driver! {
+/// type: MyDriver,
+/// name: "Module name",
+/// authors: ["Author name"],
+/// description: "Description",
+/// license: "GPL",
+/// }
+/// ```
+#[macro_export]
+macro_rules! module_hid_driver {
+ ($($f:tt)*) => {
+ $crate::module_driver!(<T>, $crate::hid::Adapter<T>, { $($f)* });
+ };
+}
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index 3da92f18f4ee..e2dcacd9369e 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -102,6 +102,8 @@
pub mod id_pool;
#[doc(hidden)]
pub mod impl_flags;
+#[cfg(CONFIG_RUST_HID_ABSTRACTIONS)]
+pub mod hid;
pub mod init;
pub mod io;
pub mod ioctl;
--
2.52.0
^ permalink raw reply related
* [PATCH v7 0/2] Initial work for Rust abstraction for HID device driver development
From: Rahul Rameshbabu @ 2026-03-14 21:56 UTC (permalink / raw)
To: linux-input, linux-kernel, rust-for-linux
Cc: a.hindborg, alex.gaynor, aliceryhl, benjamin.tissoires,
benno.lossin, bjorn3_gh, boqun.feng, dakr, db48x, gary, jikos,
ojeda, peter.hutterer, tmgross, linuxhid, Rahul Rameshbabu
Thanks Gary and others for the great review on v6. In parallel to v7, I am
working on a more complex driver for RazerBlade laptops that I am hoping will be
interesting for review.
Link: https://lore.kernel.org/rust-for-linux/20260222215611.79760-1-sergeantsagara@protonmail.com/
Link: https://youtu.be/c8JAZm-QinY
Link: https://lore.kernel.org/rust-for-linux/wjfjzjc626n55zvhksiyldobwubr2imbvfavqej333lvnka2wn@r4zfcjqtanvu/
Link: https://lore.kernel.org/rust-for-linux/175810473311.3076338.14309101339951114135.b4-ty@kernel.org/
Rahul Rameshbabu (2):
rust: core abstractions for HID drivers
rust: hid: Glorious PC Gaming Race Model O and O- mice reference
driver
MAINTAINERS | 14 +
drivers/hid/Kconfig | 2 +
drivers/hid/Makefile | 2 +
drivers/hid/hid-glorious.c | 2 +
drivers/hid/rust/Kconfig | 28 ++
drivers/hid/rust/Makefile | 6 +
drivers/hid/rust/hid_glorious_rust.rs | 67 ++++
rust/bindings/bindings_helper.h | 3 +
rust/kernel/hid.rs | 509 ++++++++++++++++++++++++++
rust/kernel/lib.rs | 2 +
10 files changed, 635 insertions(+)
create mode 100644 drivers/hid/rust/Kconfig
create mode 100644 drivers/hid/rust/Makefile
create mode 100644 drivers/hid/rust/hid_glorious_rust.rs
create mode 100644 rust/kernel/hid.rs
base-commit: b3d161f22ba9b2dc16bb82aa2b8515d98c99624f
--
2.52.0
^ permalink raw reply
* [PATCH][next] HID: mcp2221: Fix spelling mistake "Enfore" -> "Enforce"
From: Colin Ian King @ 2026-03-14 17:02 UTC (permalink / raw)
To: Rishi Gupta, Jiri Kosina, Benjamin Tissoires, linux-i2c,
linux-input, Linus Walleij
Cc: kernel-janitors, linux-kernel
There is a spelling mistake in a module description. Fix it.
Signed-off-by: Colin Ian King <colin.i.king@gmail.com>
---
drivers/hid/hid-mcp2221.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/hid/hid-mcp2221.c b/drivers/hid/hid-mcp2221.c
index ea9f7ddd93a2..be80970ab48e 100644
--- a/drivers/hid/hid-mcp2221.c
+++ b/drivers/hid/hid-mcp2221.c
@@ -26,7 +26,7 @@ static bool gpio_mode_enforce;
module_param(gpio_mode_enforce, bool, 0644);
MODULE_PARM_DESC(gpio_mode_enforce,
- "Enfore GPIO mode for GP0 thru GP3 (default: false, will be used for IIO)");
+ "Enforce GPIO mode for GP0 thru GP3 (default: false, will be used for IIO)");
/* Commands codes in a raw output report */
enum {
--
2.53.0
^ permalink raw reply related
* Re: [PATCH 1/2] iio: hid-sensor-gyro-3d: move iio_device_register() to end of probe()
From: Bhargav Joshi @ 2026-03-14 16:36 UTC (permalink / raw)
To: srinivas pandruvada
Cc: Jonathan Cameron, jikos, dlechner, nuno.sa, andy, linux-iio,
linux-kernel, linux-input
In-Reply-To: <4695861781487359fddc6cb8aab932c70cb8ebba.camel@linux.intel.com>
Hi,
On Mon, Mar 9, 2026 at 9:12 PM srinivas pandruvada
<srinivas.pandruvada@linux.intel.com> wrote:
>
> On Sat, 2026-03-07 at 14:21 +0000, Jonathan Cameron wrote:
> > On Mon, 2 Mar 2026 01:00:06 +0530
> > Bhargav Joshi <rougueprince47@gmail.com> wrote:
> >
> > > On Sun, Mar 1, 2026 at 5:15 PM Jonathan Cameron <jic23@kernel.org>
> > > wrote:
> > > >
> > > > On Sun, 1 Mar 2026 00:43:59 +0530
> > > > Bhargav Joshi <rougueprince47@gmail.com> wrote:
> > > >
> > > > > Currently, calling iio_device_register() before
> > > > > sensor_hub_register_callback() may create a race condition
> > > > > where the
> > > > > device is exposed to userspace before callbacks are wired.
> > > >
> > > > We needs some more here on 'why' this is a problem.
> > > > Whilst this is an unusual arrangement, I couldn't immediately
> > > > find
> > > > anything that was actually broken. It's possible data will turn
> > > > up
> > > > after we tear down the callbacks and before the userspace
> > > > interfaces
> > > > are removed, but I think that just results in dropping data.
> > > > Given it's
> > > > in a race anyway I don't think we care about that. The callbacks
> > > > don't seem to be involved in device configuration which can go on
> > > > whether
> > > > or not the callbacks are registered. Maybe there is something
> > > > that
> > > > won't get acknowledged if they aren't in place in time?
> > > >
> > > > For a fix like this we'd normally want to see a clear flow that
> > > > leads to a bug.
> > > >
> > > > Also a fixes tag so we know how far to backport.
> > > >
> > >
> > > Hi Jonathan,
> > >
> > > I originally submitted the patch for the driver because calling
> > > iio_device_register() before the callbacks are wired up violates
> > > standard IIO LIFO ordering. I assumed this created a dangerous race
> > > condition with userspace. However, as you correctly pointed out,
> > > the
> > > HID sensor hub safely drops those early events without crashing,
> > > even
> > > if it is unusual behaviour still won't have a hard crash.
> > >
> > > My underlying goal was simply a structural cleanup to align the
> > > probe() and remove() sequences with standard IIO architecture.
> > >
> > > Would you like me to send a v2 of this patch with a revised commit
> > > message classifying it purely as a structural cleanup (and without
> > > a
> > > Fixes tag), or is the current driver behavior acceptable enough
> > > that I
> > > should just drop this patch entirely?
> >
> > My gut is leave this one alone. It's a rather unusual driver in lots
> > of ways, so I don't really mind one more ;)
> >
> > Lets see if anyone else has strong views one way or the other.
> >
> > Srinivas?
> >
> I don't see anything wrong with this. If you register callback before,
> then iio_push_to_buffers_with_timestamp() can be called before
> iio_device_register(), although it will not happen because nobody
> powered on sensors in the hub.
>
Thank you for the clarification, given that current code won't cause
any issues, Jonathan I am completely fine with dropping this one.
Best Regards,
Bhargav
> Thanks,
> Srinivs
>
>
>
>
>
>
> > >
> > > Thanks,
> > > Bhargav
> > >
> > > > >
> > > > > Move iio_device_register() to the end of the probe() function
> > > > > to prevent
> > > > > race condition.
> > > > >
> > > > > Consequently, update the error handling path in probe() and in
> > > > > remove()
> > > > > ensuring that iio_device_unregister() is called first to cut
> > > > > off
> > > > > userspace access before the hardware callbacks are removed.
> > > > >
> > > > > Signed-off-by: Bhargav Joshi <rougueprince47@gmail.com>
> > > > > ---
> > > > > drivers/iio/gyro/hid-sensor-gyro-3d.c | 20 ++++++++++---------
> > > > > -
> > > > > 1 file changed, 10 insertions(+), 10 deletions(-)
> > > > >
> > > > > diff --git a/drivers/iio/gyro/hid-sensor-gyro-3d.c
> > > > > b/drivers/iio/gyro/hid-sensor-gyro-3d.c
> > > > > index c43990c518f7..8e3628cd8529 100644
> > > > > --- a/drivers/iio/gyro/hid-sensor-gyro-3d.c
> > > > > +++ b/drivers/iio/gyro/hid-sensor-gyro-3d.c
> > > > > @@ -333,12 +333,6 @@ static int hid_gyro_3d_probe(struct
> > > > > platform_device *pdev)
> > > > > return ret;
> > > > > }
> > > > >
> > > > > - ret = iio_device_register(indio_dev);
> > > > > - if (ret) {
> > > > > - dev_err(&pdev->dev, "device register failed\n");
> > > > > - goto error_remove_trigger;
> > > > > - }
> > > > > -
> > > > > gyro_state->callbacks.send_event = gyro_3d_proc_event;
> > > > > gyro_state->callbacks.capture_sample =
> > > > > gyro_3d_capture_sample;
> > > > > gyro_state->callbacks.pdev = pdev;
> > > > > @@ -346,13 +340,19 @@ static int hid_gyro_3d_probe(struct
> > > > > platform_device *pdev)
> > > > > &gyro_state->callbacks);
> > > > > if (ret < 0) {
> > > > > dev_err(&pdev->dev, "callback reg failed\n");
> > > > > - goto error_iio_unreg;
> > > > > + goto error_remove_trigger;
> > > > > + }
> > > > > +
> > > > > + ret = iio_device_register(indio_dev);
> > > > > + if (ret) {
> > > > > + dev_err(&pdev->dev, "device register failed\n");
> > > > > + goto error_remove_callback;
> > > > > }
> > > > >
> > > > > return ret;
> > > > >
> > > > > -error_iio_unreg:
> > > > > - iio_device_unregister(indio_dev);
> > > > > +error_remove_callback:
> > > > > + sensor_hub_remove_callback(hsdev,
> > > > > HID_USAGE_SENSOR_GYRO_3D);
> > > > > error_remove_trigger:
> > > > > hid_sensor_remove_trigger(indio_dev, &gyro_state-
> > > > > >common_attributes);
> > > > > return ret;
> > > > > @@ -365,8 +365,8 @@ static void hid_gyro_3d_remove(struct
> > > > > platform_device *pdev)
> > > > > struct iio_dev *indio_dev = platform_get_drvdata(pdev);
> > > > > struct gyro_3d_state *gyro_state = iio_priv(indio_dev);
> > > > >
> > > > > - sensor_hub_remove_callback(hsdev,
> > > > > HID_USAGE_SENSOR_GYRO_3D);
> > > > > iio_device_unregister(indio_dev);
> > > > > + sensor_hub_remove_callback(hsdev,
> > > > > HID_USAGE_SENSOR_GYRO_3D);
> > > > > hid_sensor_remove_trigger(indio_dev, &gyro_state-
> > > > > >common_attributes);
> > > > > }
> > > > >
> > > >
^ permalink raw reply
* [PATCH v3] dt-bindings: input: touchscreen: edt-ft5x06: Add FocalTech FT3519
From: Bhushan Shah @ 2026-03-14 14:57 UTC (permalink / raw)
To: Dmitry Torokhov, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: linux-input, devicetree, linux-kernel, Bhushan Shah
Document FocalTech FT3519 support by adding the compatible. It's 10
point touchscreen, which is compatible with FT3518
Signed-off-by: Bhushan Shah <bhushan.shah@machinesoul.in>
---
$ make dt_binding_check DT_SCHEMA_FILES=input/touchscreen/edt-ft5x06.yaml
SCHEMA Documentation/devicetree/bindings/processed-schema.json
CHKDT ./Documentation/devicetree/bindings
LINT ./Documentation/devicetree/bindings
DTEX Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.example.dts
DTC [C] Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.example.dtb
---
Changes in v3:
- Fix syntax
- Link to v2: https://lore.kernel.org/r/20260314-edt-ft3519-v2-1-e28c3f9c6559@machinesoul.in
Changes in v2:
- Remove the driver change and add only compatible in dt-bindings.
- Link to v1: https://lore.kernel.org/r/20260313-edt-ft3519-v1-0-fe5ffc632fd2@machinesoul.in
---
.../bindings/input/touchscreen/edt-ft5x06.yaml | 30 ++++++++++++----------
1 file changed, 17 insertions(+), 13 deletions(-)
diff --git a/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.yaml b/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.yaml
index 6f90522de8c0..68b2f1601654 100644
--- a/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.yaml
+++ b/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.yaml
@@ -33,19 +33,23 @@ allOf:
properties:
compatible:
- enum:
- - edt,edt-ft5206
- - edt,edt-ft5306
- - edt,edt-ft5406
- - edt,edt-ft5506
- - evervision,ev-ft5726
- - focaltech,ft3518
- - focaltech,ft5426
- - focaltech,ft5452
- - focaltech,ft6236
- - focaltech,ft8201
- - focaltech,ft8716
- - focaltech,ft8719
+ oneOf:
+ - enum:
+ - edt,edt-ft5206
+ - edt,edt-ft5306
+ - edt,edt-ft5406
+ - edt,edt-ft5506
+ - evervision,ev-ft5726
+ - focaltech,ft3518
+ - focaltech,ft5426
+ - focaltech,ft5452
+ - focaltech,ft6236
+ - focaltech,ft8201
+ - focaltech,ft8716
+ - focaltech,ft8719
+ - items:
+ - const: focaltech,ft3519
+ - const: focaltech,ft3518
reg:
maxItems: 1
---
base-commit: 0257f64bdac7fdca30fa3cae0df8b9ecbec7733a
change-id: 20260313-edt-ft3519-b3e2a33e88ee
Best regards,
--
Bhushan Shah <bhushan.shah@machinesoul.in>
^ permalink raw reply related
* Re: [PATCH v2] dt-bindings: input: touchscreen: edt-ft5x06: Add FocalTech FT3519
From: Krzysztof Kozlowski @ 2026-03-14 14:07 UTC (permalink / raw)
To: Bhushan Shah, Dmitry Torokhov, Rob Herring, Krzysztof Kozlowski,
Conor Dooley
Cc: linux-input, devicetree, linux-kernel
In-Reply-To: <20260314-edt-ft3519-v2-1-e28c3f9c6559@machinesoul.in>
On 14/03/2026 14:50, Bhushan Shah wrote:
> Document FocalTech FT3519 support by adding the compatible. It's 10
> point touchscreen, which is compatible with FT3518
>
> Signed-off-by: Bhushan Shah <bhushan.shah@machinesoul.in>
> ---
> Document FocalTech FT3519 support by adding the compatible. It's 10
> point touchscreen, which is compatible with FT3518.
> ---
> Changes in v2:
> - Remove the driver change and add only compatible in dt-bindings.
> - Link to v1: https://lore.kernel.org/r/20260313-edt-ft3519-v1-0-fe5ffc632fd2@machinesoul.in
> ---
It does not look like you tested the bindings, at least after quick
look. Please run `make dt_binding_check` (see
Documentation/devicetree/bindings/writing-schema.rst for instructions).
Maybe you need to update your dtschema and yamllint. Don't rely on
distro packages for dtschema and be sure you are using the latest
released dtschema.
Best regards,
Krzysztof
^ permalink raw reply
* Re: [PATCH 2/2] Input: edt-ft5x06 - add support for FocalTech FT3519
From: Bhushan Shah @ 2026-03-14 13:59 UTC (permalink / raw)
To: Krzysztof Kozlowski
Cc: Dmitry Torokhov, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
linux-input, devicetree, linux-kernel
In-Reply-To: <20260314-successful-nano-hyrax-cc3b21@quoll>
On Saturday, 14 March 2026 15:55:13 IST Krzysztof Kozlowski wrote:
> On Fri, Mar 13, 2026 at 12:09:51PM +0530, Bhushan Shah wrote:
> > This driver is compatible with the FocalTech FT3519 touchscreen, which
> > supports up to 10 concurrent touch points. Add a compatible for it.
> >
> > Signed-off-by: Bhushan Shah <bhushan.shah@machinesoul.in>
> > ---
> >
> > drivers/input/touchscreen/edt-ft5x06.c | 6 ++++++
> > 1 file changed, 6 insertions(+)
> >
> > diff --git a/drivers/input/touchscreen/edt-ft5x06.c
> > b/drivers/input/touchscreen/edt-ft5x06.c index d0ab644be006..52188e1aa9bc
> > 100644
> > --- a/drivers/input/touchscreen/edt-ft5x06.c
> > +++ b/drivers/input/touchscreen/edt-ft5x06.c
> > @@ -1479,6 +1479,10 @@ static const struct edt_i2c_chip_data
> > edt_ft3518_data = {>
> > .max_support_points = 10,
> >
> > };
> >
> > +static const struct edt_i2c_chip_data edt_ft3519_data = {
> > + .max_support_points = 10,
> > +};
>
> So same as edt_ft3518_data? Why are you duplicating it then?
>
You are right, it does not need duplicating, I sent v2 which adjusts only dt-
bindings. Although, I must point out that this driver already contains some
duplicates and I simply followed that.
^ permalink raw reply
* [PATCH v2] dt-bindings: input: touchscreen: edt-ft5x06: Add FocalTech FT3519
From: Bhushan Shah @ 2026-03-14 13:50 UTC (permalink / raw)
To: Dmitry Torokhov, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: linux-input, devicetree, linux-kernel, Bhushan Shah
Document FocalTech FT3519 support by adding the compatible. It's 10
point touchscreen, which is compatible with FT3518
Signed-off-by: Bhushan Shah <bhushan.shah@machinesoul.in>
---
Document FocalTech FT3519 support by adding the compatible. It's 10
point touchscreen, which is compatible with FT3518.
---
Changes in v2:
- Remove the driver change and add only compatible in dt-bindings.
- Link to v1: https://lore.kernel.org/r/20260313-edt-ft3519-v1-0-fe5ffc632fd2@machinesoul.in
---
.../bindings/input/touchscreen/edt-ft5x06.yaml | 30 ++++++++++++----------
1 file changed, 17 insertions(+), 13 deletions(-)
diff --git a/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.yaml b/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.yaml
index 6f90522de8c0..cc071acc1c72 100644
--- a/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.yaml
+++ b/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.yaml
@@ -33,19 +33,23 @@ allOf:
properties:
compatible:
- enum:
- - edt,edt-ft5206
- - edt,edt-ft5306
- - edt,edt-ft5406
- - edt,edt-ft5506
- - evervision,ev-ft5726
- - focaltech,ft3518
- - focaltech,ft5426
- - focaltech,ft5452
- - focaltech,ft6236
- - focaltech,ft8201
- - focaltech,ft8716
- - focaltech,ft8719
+ oneOf:
+ - enum:
+ - edt,edt-ft5206
+ - edt,edt-ft5306
+ - edt,edt-ft5406
+ - edt,edt-ft5506
+ - evervision,ev-ft5726
+ - focaltech,ft3518
+ - focaltech,ft5426
+ - focaltech,ft5452
+ - focaltech,ft6236
+ - focaltech,ft8201
+ - focaltech,ft8716
+ - focaltech,ft8719
+ - items:
+ - const: focaltech,ft3519
+ - const: focaltech,ft3518
reg:
maxItems: 1
---
base-commit: 0257f64bdac7fdca30fa3cae0df8b9ecbec7733a
change-id: 20260313-edt-ft3519-b3e2a33e88ee
Best regards,
--
Bhushan Shah <bhushan.shah@machinesoul.in>
^ permalink raw reply related
* Re: [PATCH v2 1/5] iio: orientation: hid-sensor-rotation: add timestamp hack to not break userspace
From: Jonathan Cameron @ 2026-03-14 12:18 UTC (permalink / raw)
To: David Lechner
Cc: Jiri Kosina, Srinivas Pandruvada, Nuno Sá, Andy Shevchenko,
Lars Möllendorf, Lars-Peter Clausen, Greg Kroah-Hartman,
Jonathan Cameron, Lixu Zhang, Francesco Lavra, linux-input,
linux-iio, linux-kernel
In-Reply-To: <20260307-iio-fix-timestamp-alignment-v2-1-d1d48fbadbbf@baylibre.com>
On Sat, 07 Mar 2026 19:44:09 -0600
David Lechner <dlechner@baylibre.com> wrote:
> Add a hack to push two timestamps in the hid-sensor-rotation scan data
> to avoid breaking userspace applications that depend on the timestamp
> being at the incorrect location in the scan data due to unintentional
> misalignment in older kernels.
>
> When this driver was written, the timestamp was in the correct location
> because of the way iio_compute_scan_bytes() was implemented at the time.
> (Samples were 24 bytes each.) Then commit 883f61653069 ("iio: buffer:
> align the size of scan bytes to size of the largest element") changed
> the computed scan_bytes to be a different size (32 bytes), which caused
> iio_push_to_buffers_with_timestamp() to place the timestamp at an
> incorrect offset.
>
> There have been long periods of time (6 years each) where the timestamp
> was in either location, so to not break either case, we open-code the
> timestamps to be pushed to both locations in the scan data.
>
> Reported-by: Jonathan Cameron <jic23@kernel.org>
> Closes: https://lore.kernel.org/linux-iio/20260215162351.79f40b32@jic23-huawei/
> Fixes: 883f61653069 ("iio: buffer: align the size of scan bytes to size of the largest element")
> Signed-off-by: David Lechner <dlechner@baylibre.com>
As suggested, I've applied this one to fixes-togreg and the rest will have
to wait for now. Interestingly this cycle has more fixes coming through
than any I remember! Hence I've sent a 2nd fixes pull and this will
be queued up for the 3rd in a week or so. It's possible the rest
might have to wait for next cycle though given round trip times etc.
Let's see.
Thanks for sorting this out and for the test scripts etc!
Jonathan
> ---
>
> v2 changes:
> - Rebased on fix that also touches the scan struct.
> - Improved comments.
>
> I found that I could emulate this thanks to /dev/uhid. And thanks to AI
> code generators, I was able to reasonably quickly make a script that
> worked for emulating "HID-SENSOR-20008a". See v1 message for test script
> source code.
>
> [v1]: https://lore.kernel.org/linux-iio/20260301-iio-fix-timestamp-alignment-v1-1-1a54980bfb90@baylibre.com/
>
> I set up the buffer like this:
>
> cd /sys/bus/iio/devices/iio:device1/buffer0
> echo 1 > in_rot_quaternion_en
> echo 1 > in_timestamp_en
> echo 1 > enable
>
> Before this series is applied, we can see that the timestamp (group of 8
> ending in "98 18") is at offset of 24 in the 32-byte data.
>
> hd /dev/iio\:device1
>
> 00000000 6a 18 00 00 ac f3 ff ff 83 2d 00 00 02 d3 ff ff |j........-......|
> 00000010 00 00 00 00 00 00 00 00 5a 17 a0 2a 73 cb 98 18 |........Z..*s...|
>
> 00000020 ad 17 00 00 6a f4 ff ff 35 2b 00 00 ca d0 ff ff |....j...5+......|
> 00000030 00 00 00 00 00 00 00 00 2a a6 bb 30 73 cb 98 18 |........*..0s...|
>
> 00000040 92 1e 00 00 50 ec ff ff ea c1 ff ff 78 f0 ff ff |....P.......x...|
> 00000050 00 00 00 00 00 00 00 00 8f 3b a7 39 77 cb 98 18 |.........;.9w...|
>
> After the first patch, we can see that the timestamp is now repeated at
> both the correct and previous incorrect offsets (24 and 32). (Normally,
> the last 8 bytes would be all 00 for padding.)
>
> 00000000 dd e0 ff ff 0e e0 ff ff 75 07 00 00 90 3f 00 00 |........u....?..|
> 00000010 f4 38 82 d0 3a cc 98 18 f4 38 82 d0 3a cc 98 18 |.8..:....8..:...|
>
> 00000020 a0 e0 ff ff 1d e0 ff ff a0 0a 00 00 1c 3f 00 00 |.............?..|
> 00000030 3a 29 9f d6 3a cc 98 18 3a 29 9f d6 3a cc 98 18 |:)..:...:)..:...|
>
> 00000040 a9 e1 ff ff 1e 14 00 00 6c c1 ff ff 98 f2 ff ff |........l.......|
> 00000050 39 21 77 11 55 cc 98 18 39 21 77 11 55 cc 98 18 |9!w.U...9!w.U...|
> ---
> drivers/iio/orientation/hid-sensor-rotation.c | 22 +++++++++++++++++++---
> 1 file changed, 19 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/iio/orientation/hid-sensor-rotation.c b/drivers/iio/orientation/hid-sensor-rotation.c
> index 6806481873be..5a5e6e4fbe34 100644
> --- a/drivers/iio/orientation/hid-sensor-rotation.c
> +++ b/drivers/iio/orientation/hid-sensor-rotation.c
> @@ -20,7 +20,12 @@ struct dev_rot_state {
> struct hid_sensor_hub_attribute_info quaternion;
> struct {
> IIO_DECLARE_QUATERNION(s32, sampled_vals);
> - aligned_s64 timestamp;
> + /*
> + * ABI regression avoidance: There are two copies of the same
> + * timestamp in case of userspace depending on broken alignment
> + * from older kernels.
> + */
> + aligned_s64 timestamp[2];
> } scan;
> int scale_pre_decml;
> int scale_post_decml;
> @@ -154,8 +159,19 @@ static int dev_rot_proc_event(struct hid_sensor_hub_device *hsdev,
> if (!rot_state->timestamp)
> rot_state->timestamp = iio_get_time_ns(indio_dev);
>
> - iio_push_to_buffers_with_timestamp(indio_dev, &rot_state->scan,
> - rot_state->timestamp);
> + /*
> + * ABI regression avoidance: IIO previously had an incorrect
> + * implementation of iio_push_to_buffers_with_timestamp() that
> + * put the timestamp in the last 8 bytes of the buffer, which
> + * was incorrect according to the IIO ABI. To avoid breaking
> + * userspace that may be depending on this broken behavior, we
> + * put the timestamp in both the correct place [0] and the old
> + * incorrect place [1].
> + */
> + rot_state->scan.timestamp[0] = rot_state->timestamp;
> + rot_state->scan.timestamp[1] = rot_state->timestamp;
> +
> + iio_push_to_buffers(indio_dev, &rot_state->scan);
>
> rot_state->timestamp = 0;
> }
>
^ permalink raw reply
* [PATCH v1] Input: atlas - convert ACPI driver to a platform one
From: Rafael J. Wysocki @ 2026-03-14 11:54 UTC (permalink / raw)
To: Dmitry Torokhov; +Cc: LKML, Linux ACPI, linux-input
From: "Rafael J. Wysocki" <rafael.j.wysocki@intel.com>
In all cases in which a struct acpi_driver is used for binding a driver
to an ACPI device object, a corresponding platform device is created by
the ACPI core and that device is regarded as a proper representation of
underlying hardware. Accordingly, a struct platform_driver should be
used by driver code to bind to that device. There are multiple reasons
why drivers should not bind directly to ACPI device objects [1].
Overall, it is better to bind drivers to platform devices than to their
ACPI companions, so convert the ACPI Atlas button driver to a platform
one.
While this is not expected to alter functionality, it changes sysfs
layout and so it will be visible to user space.
Link: https://lore.kernel.org/all/2396510.ElGaqSPkdT@rafael.j.wysocki/ [1]
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
---
drivers/input/misc/atlas_btns.c | 22 ++++++++++++----------
1 file changed, 12 insertions(+), 10 deletions(-)
diff --git a/drivers/input/misc/atlas_btns.c b/drivers/input/misc/atlas_btns.c
index 5b9be2957746..47b31725e850 100644
--- a/drivers/input/misc/atlas_btns.c
+++ b/drivers/input/misc/atlas_btns.c
@@ -14,6 +14,7 @@
#include <linux/input.h>
#include <linux/types.h>
#include <linux/acpi.h>
+#include <linux/platform_device.h>
#include <linux/uaccess.h>
#define ACPI_ATLAS_NAME "Atlas ACPI"
@@ -57,8 +58,9 @@ static acpi_status acpi_atlas_button_handler(u32 function,
return status;
}
-static int atlas_acpi_button_add(struct acpi_device *device)
+static int atlas_acpi_button_probe(struct platform_device *pdev)
{
+ struct acpi_device *device = ACPI_COMPANION(&pdev->dev);
acpi_status status;
int i;
int err;
@@ -106,8 +108,9 @@ static int atlas_acpi_button_add(struct acpi_device *device)
return err;
}
-static void atlas_acpi_button_remove(struct acpi_device *device)
+static void atlas_acpi_button_remove(struct platform_device *pdev)
{
+ struct acpi_device *device = ACPI_COMPANION(&pdev->dev);
acpi_status status;
status = acpi_remove_address_space_handler(device->handle,
@@ -124,16 +127,15 @@ static const struct acpi_device_id atlas_device_ids[] = {
};
MODULE_DEVICE_TABLE(acpi, atlas_device_ids);
-static struct acpi_driver atlas_acpi_driver = {
- .name = ACPI_ATLAS_NAME,
- .class = ACPI_ATLAS_CLASS,
- .ids = atlas_device_ids,
- .ops = {
- .add = atlas_acpi_button_add,
- .remove = atlas_acpi_button_remove,
+static struct platform_driver atlas_acpi_driver = {
+ .probe = atlas_acpi_button_probe,
+ .remove = atlas_acpi_button_remove,
+ .driver = {
+ .name = ACPI_ATLAS_NAME,
+ .acpi_match_table = atlas_device_ids,
},
};
-module_acpi_driver(atlas_acpi_driver);
+module_platform_driver(atlas_acpi_driver);
MODULE_AUTHOR("Jaya Kumar");
MODULE_LICENSE("GPL");
--
2.51.0
^ permalink raw reply related
* Re: [PATCH 1/2] dt-bindings: input: touchscreen: edt-ft5x06: Add FocalTech FT3519
From: Krzysztof Kozlowski @ 2026-03-14 10:29 UTC (permalink / raw)
To: Bhushan Shah
Cc: Dmitry Torokhov, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
linux-input, devicetree, linux-kernel
In-Reply-To: <20260313-edt-ft3519-v1-1-fe5ffc632fd2@machinesoul.in>
On Fri, Mar 13, 2026 at 12:09:50PM +0530, Bhushan Shah wrote:
> Document FocalTech FT3519 support by adding the compatible. It's 10
> point touchscreen, which works with same driver.
>
> Signed-off-by: Bhushan Shah <bhushan.shah@machinesoul.in>
> ---
> Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.yaml | 1 +
> 1 file changed, 1 insertion(+)
>
> diff --git a/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.yaml b/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.yaml
> index 6f90522de8c0..34161af90156 100644
> --- a/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.yaml
> +++ b/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.yaml
> @@ -40,6 +40,7 @@ properties:
> - edt,edt-ft5506
> - evervision,ev-ft5726
> - focaltech,ft3518
> + - focaltech,ft3519
Driver clearly indicates it is compatible with 3518 so express it with
fallback (see writing bindings, writing schema, example schema, DTS101
presentation slides).
Best regards,
Krzysztof
^ permalink raw reply
* Re: [PATCH 2/2] Input: edt-ft5x06 - add support for FocalTech FT3519
From: Krzysztof Kozlowski @ 2026-03-14 10:25 UTC (permalink / raw)
To: Bhushan Shah
Cc: Dmitry Torokhov, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
linux-input, devicetree, linux-kernel
In-Reply-To: <20260313-edt-ft3519-v1-2-fe5ffc632fd2@machinesoul.in>
On Fri, Mar 13, 2026 at 12:09:51PM +0530, Bhushan Shah wrote:
> This driver is compatible with the FocalTech FT3519 touchscreen, which
> supports up to 10 concurrent touch points. Add a compatible for it.
>
> Signed-off-by: Bhushan Shah <bhushan.shah@machinesoul.in>
> ---
> drivers/input/touchscreen/edt-ft5x06.c | 6 ++++++
> 1 file changed, 6 insertions(+)
>
> diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c
> index d0ab644be006..52188e1aa9bc 100644
> --- a/drivers/input/touchscreen/edt-ft5x06.c
> +++ b/drivers/input/touchscreen/edt-ft5x06.c
> @@ -1479,6 +1479,10 @@ static const struct edt_i2c_chip_data edt_ft3518_data = {
> .max_support_points = 10,
> };
>
> +static const struct edt_i2c_chip_data edt_ft3519_data = {
> + .max_support_points = 10,
> +};
So same as edt_ft3518_data? Why are you duplicating it then?
Best regards,
Krzysztof
^ permalink raw reply
* [PATCH] xpad: Overhaul device data for wireless devices
From: Sanjay Govind @ 2026-03-14 7:50 UTC (permalink / raw)
To: Dmitry Torokhov, Vicki Pfau, Nilton Perim Neto, Mario Limonciello,
Sanjay Govind, Pierre-Loup A. Griffais
Cc: Antheas Kapenekakis, linux-input, linux-kernel
Xbox 360 wireless controllers expose information in the link and
capabilities reports.
Extract and use the vendor id for wireless controllers, and use
the subtype to build a nicer device name and product id.
Some xbox 360 controllers put a vid and pid into the stick capability
data, so check if this was done, and pull the vid, pid and revision from
there.
Signed-off-by: Sanjay Govind <sanjay.govind9@gmail.com>
---
drivers/input/joystick/xpad.c | 138 +++++++++++++++++++++++++++++++++-
1 file changed, 135 insertions(+), 3 deletions(-)
diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c
index bf4accf3f581..2490eb21a534 100644
--- a/drivers/input/joystick/xpad.c
+++ b/drivers/input/joystick/xpad.c
@@ -94,6 +94,22 @@
#define XTYPE_XBOXONE 3
#define XTYPE_UNKNOWN 4
+#define FLAG_FORCE_FEEDBACK 0x01
+
+#define SUBTYPE_GAMEPAD 0x01
+#define SUBTYPE_WHEEL 0x02
+#define SUBTYPE_ARCADE_STICK 0x03
+#define SUBTYPE_FLIGHT_SICK 0x04
+#define SUBTYPE_DANCE_PAD 0x05
+#define SUBTYPE_GUITAR 0x06
+#define SUBTYPE_GUITAR_ALTERNATE 0x07
+#define SUBTYPE_DRUM_KIT 0x08
+#define SUBTYPE_GUITAR_BASS 0x0B
+#define SUBTYPE_RB_KEYBOARD 0x0F
+#define SUBTYPE_ARCADE_PAD 0x13
+#define SUBTYPE_TURNTABLE 0x17
+#define SUBTYPE_PRO_GUITAR 0x19
+
/* Send power-off packet to xpad360w after holding the mode button for this many
* seconds
*/
@@ -795,6 +811,9 @@ struct usb_xpad {
int xtype; /* type of xbox device */
int packet_type; /* type of the extended packet */
int pad_nr; /* the order x360 pads were attached */
+ u8 sub_type;
+ u16 flags;
+ u16 wireless_vid;
const char *name; /* name of the device */
struct work_struct work; /* init/remove device from callback */
time64_t mode_btn_down_ts;
@@ -807,6 +826,8 @@ static void xpad_deinit_input(struct usb_xpad *xpad);
static int xpad_start_input(struct usb_xpad *xpad);
static void xpadone_ack_mode_report(struct usb_xpad *xpad, u8 seq_num);
static void xpad360w_poweroff_controller(struct usb_xpad *xpad);
+static int xpad_inquiry_pad_capabilities(struct usb_xpad *xpad);
+
/*
* xpad_process_packet
@@ -1026,12 +1047,46 @@ static void xpad360w_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned cha
if (data[0] & 0x08) {
present = (data[1] & 0x80) != 0;
- if (xpad->pad_present != present) {
+ /* delay prescence until after we get the link report */
+ if (!present && xpad->pad_present) {
xpad->pad_present = present;
schedule_work(&xpad->work);
}
}
+ /* Link report */
+ if (data[0] == 0x00 && data[1] == 0x0F) {
+ xpad->sub_type = data[25] & 0x7f;
+
+ /* Decode vendor id from link report */
+ xpad->wireless_vid = ((data[0x16] & 0xf) | data[0x18] << 4) << 8 | data[0x17];
+
+ if ((data[25] & 0x80) != 0)
+ xpad->flags |= FLAG_FORCE_FEEDBACK;
+
+ if (!xpad->pad_present) {
+ xpad->pad_present = true;
+ schedule_work(&xpad->work);
+ }
+ xpad_inquiry_pad_capabilities(xpad);
+ }
+
+ /* Capabilities report */
+ if (data[0] == 0x00 && data[1] == 0x05 && data[5] == 0x12) {
+ xpad->flags |= data[20];
+ /*
+ * A bunch of vendors started putting vids and pids
+ * into capabilities data because they can't be
+ * retrieved by xinput easliy.
+ * Not all of them do though, so check the vids match
+ * before extracting that info.
+ */
+ if (((data[11] << 8) | data[10]) == xpad->wireless_vid) {
+ xpad->dev->id.product = (data[13] << 8) | data[12];
+ xpad->dev->id.version = (data[15] << 8) | data[14];
+ }
+ }
+
/* Valid pad data */
if (data[1] != 0x1)
return;
@@ -1495,6 +1550,31 @@ static int xpad_inquiry_pad_presence(struct usb_xpad *xpad)
return xpad_try_sending_next_out_packet(xpad);
}
+static int xpad_inquiry_pad_capabilities(struct usb_xpad *xpad)
+{
+ struct xpad_output_packet *packet =
+ &xpad->out_packets[XPAD_OUT_CMD_IDX];
+
+ guard(spinlock_irqsave)(&xpad->odata_lock);
+
+ packet->data[0] = 0x00;
+ packet->data[1] = 0x00;
+ packet->data[2] = 0x02;
+ packet->data[3] = 0x80;
+ packet->data[4] = 0x00;
+ packet->data[5] = 0x00;
+ packet->data[6] = 0x00;
+ packet->data[7] = 0x00;
+ packet->data[8] = 0x00;
+ packet->data[9] = 0x00;
+ packet->data[10] = 0x00;
+ packet->data[11] = 0x00;
+ packet->len = 12;
+ packet->pending = true;
+
+ return xpad_try_sending_next_out_packet(xpad);
+}
+
static int xpad_start_xbox_one(struct usb_xpad *xpad)
{
int error;
@@ -1965,8 +2045,60 @@ static int xpad_init_input(struct usb_xpad *xpad)
usb_to_input_id(xpad->udev, &input_dev->id);
if (xpad->xtype == XTYPE_XBOX360W) {
- /* x360w controllers and the receiver have different ids */
- input_dev->id.product = 0x02a1;
+ /*
+ * x360w controllers on windows put the subtype into the product
+ * for wheels and gamepads, but it makes sense to do it for all
+ * subtypes
+ */
+ input_dev->id.product = 0x02a0 + xpad->sub_type;
+ /* If the Link report has provided a vid, it won't be set to 1 */
+ if (xpad->wireless_vid != 1)
+ input_dev->id.vendor = xpad->wireless_vid;
+ switch (xpad->sub_type) {
+ case SUBTYPE_GAMEPAD:
+ input_dev->name = "Xbox 360 Wireless Controller";
+ break;
+ case SUBTYPE_WHEEL:
+ input_dev->name = "Xbox 360 Wireless Wheel";
+ break;
+ case SUBTYPE_ARCADE_STICK:
+ input_dev->name = "Xbox 360 Wireless Arcade Stick";
+ break;
+ case SUBTYPE_FLIGHT_SICK:
+ input_dev->name = "Xbox 360 Wireless Flight Stick";
+ break;
+ case SUBTYPE_DANCE_PAD:
+ input_dev->name = "Xbox 360 Wireless Dance Pad";
+ break;
+ case SUBTYPE_GUITAR:
+ input_dev->name = "Xbox 360 Wireless Guitar";
+ break;
+ case SUBTYPE_GUITAR_ALTERNATE:
+ input_dev->name = "Xbox 360 Wireless Alternate Guitar";
+ break;
+ case SUBTYPE_GUITAR_BASS:
+ input_dev->name = "Xbox 360 Wireless Bass Guitar";
+ break;
+ case SUBTYPE_DRUM_KIT:
+ /* Vendors used force feedback flag to differentiate these */
+ if (xpad->flags & FLAG_FORCE_FEEDBACK)
+ input_dev->name = "Xbox 360 Wireless Guitar Hero Drum Kit";
+ else
+ input_dev->name = "Xbox 360 Wireless Rock Band Drum Kit";
+ break;
+ case SUBTYPE_RB_KEYBOARD:
+ input_dev->name = "Xbox 360 Wireless Rock Band Keyboard";
+ break;
+ case SUBTYPE_ARCADE_PAD:
+ input_dev->name = "Xbox 360 Wireless Arcade Pad";
+ break;
+ case SUBTYPE_TURNTABLE:
+ input_dev->name = "Xbox 360 Wireless DJ Hero Turntable";
+ break;
+ case SUBTYPE_PRO_GUITAR:
+ input_dev->name = "Xbox 360 Wireless Rock Band Pro Guitar";
+ break;
+ }
}
input_dev->dev.parent = &xpad->intf->dev;
--
2.53.0
^ permalink raw reply related
* [PATCH v7 3/3] HID: input: Add support for multiple batteries per device
From: Lucas Zampieri @ 2026-03-14 1:05 UTC (permalink / raw)
To: linux-input
Cc: Lucas Zampieri, linux-kernel, Jiri Kosina, Benjamin Tissoires,
Sebastian Reichel, Bastien Nocera, linux-pm
In-Reply-To: <20260314010533.110278-1-lcasmz54@gmail.com>
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>
---
drivers/hid/hid-core.c | 4 ++++
drivers/hid/hid-input.c | 44 ++++++++++++++++++++++++++++-------------
include/linux/hid.h | 11 ++++++++---
3 files changed, 42 insertions(+), 17 deletions(-)
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index da57cbf0a..58a31ee07 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -2995,6 +2995,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 b5d34658b..8fff185fe 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 @@ static int hidinput_setup_battery(struct hid_device *dev, unsigned report_type,
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 71beff003..442a80d79 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 */
@@ -767,7 +770,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
--
2.53.0
^ permalink raw reply related
* [PATCH v7 2/3] HID: input: Introduce struct hid_battery and refactor battery code
From: Lucas Zampieri @ 2026-03-14 1:05 UTC (permalink / raw)
To: linux-input
Cc: Lucas Zampieri, linux-kernel, Jiri Kosina, Benjamin Tissoires,
Sebastian Reichel, Bastien Nocera, linux-pm
In-Reply-To: <20260314010533.110278-1-lcasmz54@gmail.com>
Introduce struct hid_battery to encapsulate individual battery state,
preparing for future multi-battery support.
The new structure contains all battery-related fields previously stored
directly in hid_device (capacity, min, max, report_type, report_id,
charge_status, etc.). The hid_device->battery pointer type changes from
struct power_supply* to struct hid_battery*, and all battery functions
are refactored accordingly.
A hid_get_battery() helper is added for external drivers, with
hid-apple.c and hid-magicmouse.c updated to use the new API. The
hid-input-test.c KUnit tests are also updated for the new structure.
No functional changes for single-battery devices.
Signed-off-by: Lucas Zampieri <lcasmz54@gmail.com>
---
drivers/hid/hid-apple.c | 10 +--
drivers/hid/hid-input-test.c | 39 ++++++-----
drivers/hid/hid-input.c | 131 +++++++++++++++++++----------------
drivers/hid/hid-magicmouse.c | 10 +--
include/linux/hid.h | 52 ++++++++++----
5 files changed, 146 insertions(+), 96 deletions(-)
diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c
index fc5897a6b..8b8b05c8a 100644
--- a/drivers/hid/hid-apple.c
+++ b/drivers/hid/hid-apple.c
@@ -623,17 +623,19 @@ static int apple_fetch_battery(struct hid_device *hdev)
struct apple_sc *asc = hid_get_drvdata(hdev);
struct hid_report_enum *report_enum;
struct hid_report *report;
+ struct hid_battery *bat;
- if (!(asc->quirks & APPLE_RDESC_BATTERY) || !hdev->battery)
+ bat = hid_get_battery(hdev);
+ if (!(asc->quirks & APPLE_RDESC_BATTERY) || !bat)
return -1;
- report_enum = &hdev->report_enum[hdev->battery_report_type];
- report = report_enum->report_id_hash[hdev->battery_report_id];
+ report_enum = &hdev->report_enum[bat->report_type];
+ report = report_enum->report_id_hash[bat->report_id];
if (!report || report->maxfield < 1)
return -1;
- if (hdev->battery_capacity == hdev->battery_max)
+ if (bat->capacity == bat->max)
return -1;
hid_hw_request(hdev, report, HID_REQ_GET_REPORT);
diff --git a/drivers/hid/hid-input-test.c b/drivers/hid/hid-input-test.c
index 6f5c71660..c92008daf 100644
--- a/drivers/hid/hid-input-test.c
+++ b/drivers/hid/hid-input-test.c
@@ -9,54 +9,59 @@
static void hid_test_input_update_battery_charge_status(struct kunit *test)
{
- struct hid_device *dev;
+ struct hid_battery *bat;
bool handled;
- dev = kunit_kzalloc(test, sizeof(*dev), GFP_KERNEL);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev);
+ bat = kunit_kzalloc(test, sizeof(*bat), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, bat);
- handled = hidinput_update_battery_charge_status(dev, HID_DG_HEIGHT, 0);
+ handled = hidinput_update_battery_charge_status(bat, HID_DG_HEIGHT, 0);
KUNIT_EXPECT_FALSE(test, handled);
- KUNIT_EXPECT_EQ(test, dev->battery_charge_status, POWER_SUPPLY_STATUS_UNKNOWN);
+ KUNIT_EXPECT_EQ(test, bat->charge_status, POWER_SUPPLY_STATUS_UNKNOWN);
- handled = hidinput_update_battery_charge_status(dev, HID_BAT_CHARGING, 0);
+ handled = hidinput_update_battery_charge_status(bat, HID_BAT_CHARGING, 0);
KUNIT_EXPECT_TRUE(test, handled);
- KUNIT_EXPECT_EQ(test, dev->battery_charge_status, POWER_SUPPLY_STATUS_DISCHARGING);
+ KUNIT_EXPECT_EQ(test, bat->charge_status, POWER_SUPPLY_STATUS_DISCHARGING);
- handled = hidinput_update_battery_charge_status(dev, HID_BAT_CHARGING, 1);
+ handled = hidinput_update_battery_charge_status(bat, HID_BAT_CHARGING, 1);
KUNIT_EXPECT_TRUE(test, handled);
- KUNIT_EXPECT_EQ(test, dev->battery_charge_status, POWER_SUPPLY_STATUS_CHARGING);
+ KUNIT_EXPECT_EQ(test, bat->charge_status, POWER_SUPPLY_STATUS_CHARGING);
}
static void hid_test_input_get_battery_property(struct kunit *test)
{
struct power_supply *psy;
+ struct hid_battery *bat;
struct hid_device *dev;
union power_supply_propval val;
int ret;
dev = kunit_kzalloc(test, sizeof(*dev), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev);
- dev->battery_avoid_query = true;
+
+ bat = kunit_kzalloc(test, sizeof(*bat), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, bat);
+ bat->dev = dev;
+ bat->avoid_query = true;
psy = kunit_kzalloc(test, sizeof(*psy), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, psy);
- psy->drv_data = dev;
+ psy->drv_data = bat;
- dev->battery_status = HID_BATTERY_UNKNOWN;
- dev->battery_charge_status = POWER_SUPPLY_STATUS_CHARGING;
+ bat->status = HID_BATTERY_UNKNOWN;
+ bat->charge_status = POWER_SUPPLY_STATUS_CHARGING;
ret = hidinput_get_battery_property(psy, POWER_SUPPLY_PROP_STATUS, &val);
KUNIT_EXPECT_EQ(test, ret, 0);
KUNIT_EXPECT_EQ(test, val.intval, POWER_SUPPLY_STATUS_UNKNOWN);
- dev->battery_status = HID_BATTERY_REPORTED;
- dev->battery_charge_status = POWER_SUPPLY_STATUS_CHARGING;
+ bat->status = HID_BATTERY_REPORTED;
+ bat->charge_status = POWER_SUPPLY_STATUS_CHARGING;
ret = hidinput_get_battery_property(psy, POWER_SUPPLY_PROP_STATUS, &val);
KUNIT_EXPECT_EQ(test, ret, 0);
KUNIT_EXPECT_EQ(test, val.intval, POWER_SUPPLY_STATUS_CHARGING);
- dev->battery_status = HID_BATTERY_REPORTED;
- dev->battery_charge_status = POWER_SUPPLY_STATUS_DISCHARGING;
+ bat->status = HID_BATTERY_REPORTED;
+ bat->charge_status = POWER_SUPPLY_STATUS_DISCHARGING;
ret = hidinput_get_battery_property(psy, POWER_SUPPLY_PROP_STATUS, &val);
KUNIT_EXPECT_EQ(test, ret, 0);
KUNIT_EXPECT_EQ(test, val.intval, POWER_SUPPLY_STATUS_DISCHARGING);
diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c
index 7e0f971ef..b5d34658b 100644
--- a/drivers/hid/hid-input.c
+++ b/drivers/hid/hid-input.c
@@ -416,18 +416,18 @@ static unsigned find_battery_quirk(struct hid_device *hdev)
return quirks;
}
-static int hidinput_scale_battery_capacity(struct hid_device *dev,
+static int hidinput_scale_battery_capacity(struct hid_battery *bat,
int value)
{
- if (dev->battery_min < dev->battery_max &&
- value >= dev->battery_min && value <= dev->battery_max)
- value = ((value - dev->battery_min) * 100) /
- (dev->battery_max - dev->battery_min);
+ if (bat->min < bat->max &&
+ value >= bat->min && value <= bat->max)
+ value = ((value - bat->min) * 100) /
+ (bat->max - bat->min);
return value;
}
-static int hidinput_query_battery_capacity(struct hid_device *dev)
+static int hidinput_query_battery_capacity(struct hid_battery *bat)
{
int ret;
@@ -435,19 +435,20 @@ static int hidinput_query_battery_capacity(struct hid_device *dev)
if (!buf)
return -ENOMEM;
- ret = hid_hw_raw_request(dev, dev->battery_report_id, buf, 4,
- dev->battery_report_type, HID_REQ_GET_REPORT);
+ ret = hid_hw_raw_request(bat->dev, bat->report_id, buf, 4,
+ bat->report_type, HID_REQ_GET_REPORT);
if (ret < 2)
return -ENODATA;
- return hidinput_scale_battery_capacity(dev, buf[1]);
+ return hidinput_scale_battery_capacity(bat, buf[1]);
}
static int hidinput_get_battery_property(struct power_supply *psy,
enum power_supply_property prop,
union power_supply_propval *val)
{
- struct hid_device *dev = power_supply_get_drvdata(psy);
+ struct hid_battery *bat = power_supply_get_drvdata(psy);
+ struct hid_device *dev = bat->dev;
int value;
int ret = 0;
@@ -457,17 +458,17 @@ static int hidinput_get_battery_property(struct power_supply *psy,
break;
case POWER_SUPPLY_PROP_PRESENT:
- val->intval = dev->battery_present;
+ val->intval = bat->present;
break;
case POWER_SUPPLY_PROP_CAPACITY:
- if (dev->battery_status != HID_BATTERY_REPORTED &&
- !dev->battery_avoid_query) {
- value = hidinput_query_battery_capacity(dev);
+ if (bat->status != HID_BATTERY_REPORTED &&
+ !bat->avoid_query) {
+ value = hidinput_query_battery_capacity(bat);
if (value < 0)
return value;
} else {
- value = dev->battery_capacity;
+ value = bat->capacity;
}
val->intval = value;
@@ -478,20 +479,20 @@ static int hidinput_get_battery_property(struct power_supply *psy,
break;
case POWER_SUPPLY_PROP_STATUS:
- if (dev->battery_status != HID_BATTERY_REPORTED &&
- !dev->battery_avoid_query) {
- value = hidinput_query_battery_capacity(dev);
+ if (bat->status != HID_BATTERY_REPORTED &&
+ !bat->avoid_query) {
+ value = hidinput_query_battery_capacity(bat);
if (value < 0)
return value;
- dev->battery_capacity = value;
- dev->battery_status = HID_BATTERY_QUERIED;
+ bat->capacity = value;
+ bat->status = HID_BATTERY_QUERIED;
}
- if (dev->battery_status == HID_BATTERY_UNKNOWN)
+ if (bat->status == HID_BATTERY_UNKNOWN)
val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
else
- val->intval = dev->battery_charge_status;
+ val->intval = bat->charge_status;
break;
case POWER_SUPPLY_PROP_SCOPE:
@@ -509,8 +510,9 @@ static int hidinput_get_battery_property(struct power_supply *psy,
static int hidinput_setup_battery(struct hid_device *dev, unsigned report_type,
struct hid_field *field, bool is_percentage)
{
+ struct hid_battery *bat;
struct power_supply_desc *psy_desc;
- struct power_supply_config psy_cfg = { .drv_data = dev, };
+ struct power_supply_config psy_cfg = { 0 };
unsigned quirks;
s32 min, max;
int error;
@@ -526,16 +528,22 @@ static int hidinput_setup_battery(struct hid_device *dev, unsigned report_type,
if (quirks & HID_BATTERY_QUIRK_IGNORE)
return 0;
- psy_desc = devm_kzalloc(&dev->dev, sizeof(*psy_desc), GFP_KERNEL);
- if (!psy_desc)
+ bat = devm_kzalloc(&dev->dev, sizeof(*bat), GFP_KERNEL);
+ if (!bat)
return -ENOMEM;
+ psy_desc = devm_kzalloc(&dev->dev, sizeof(*psy_desc), GFP_KERNEL);
+ if (!psy_desc) {
+ error = -ENOMEM;
+ goto err_free_bat;
+ }
+
psy_desc->name = devm_kasprintf(&dev->dev, GFP_KERNEL, "hid-%s-battery",
strlen(dev->uniq) ?
dev->uniq : dev_name(&dev->dev));
if (!psy_desc->name) {
error = -ENOMEM;
- goto err_free_mem;
+ goto err_free_desc;
}
psy_desc->type = POWER_SUPPLY_TYPE_BATTERY;
@@ -555,51 +563,57 @@ static int hidinput_setup_battery(struct hid_device *dev, unsigned report_type,
if (quirks & HID_BATTERY_QUIRK_FEATURE)
report_type = HID_FEATURE_REPORT;
- dev->battery_min = min;
- dev->battery_max = max;
- dev->battery_report_type = report_type;
- dev->battery_report_id = field->report->id;
- dev->battery_charge_status = POWER_SUPPLY_STATUS_DISCHARGING;
+ bat->dev = dev;
+ bat->min = min;
+ bat->max = max;
+ bat->report_type = report_type;
+ bat->report_id = field->report->id;
+ bat->charge_status = POWER_SUPPLY_STATUS_DISCHARGING;
+ bat->status = HID_BATTERY_UNKNOWN;
/*
* Stylus is normally not connected to the device and thus we
* can't query the device and get meaningful battery strength.
* We have to wait for the device to report it on its own.
*/
- dev->battery_avoid_query = report_type == HID_INPUT_REPORT &&
- field->physical == HID_DG_STYLUS;
+ bat->avoid_query = report_type == HID_INPUT_REPORT &&
+ field->physical == HID_DG_STYLUS;
if (quirks & HID_BATTERY_QUIRK_AVOID_QUERY)
- dev->battery_avoid_query = true;
+ bat->avoid_query = true;
- dev->battery_present = (quirks & HID_BATTERY_QUIRK_DYNAMIC) ? false : true;
+ bat->present = (quirks & HID_BATTERY_QUIRK_DYNAMIC) ? false : true;
- dev->battery = devm_power_supply_register(&dev->dev, psy_desc, &psy_cfg);
- if (IS_ERR(dev->battery)) {
- error = PTR_ERR(dev->battery);
+ psy_cfg.drv_data = bat;
+ bat->ps = devm_power_supply_register(&dev->dev, psy_desc, &psy_cfg);
+ if (IS_ERR(bat->ps)) {
+ error = PTR_ERR(bat->ps);
hid_warn(dev, "can't register power supply: %d\n", error);
goto err_free_name;
}
- power_supply_powers(dev->battery, &dev->dev);
+ power_supply_powers(bat->ps, &dev->dev);
+ dev->battery = bat;
return 0;
err_free_name:
devm_kfree(&dev->dev, psy_desc->name);
-err_free_mem:
+err_free_desc:
devm_kfree(&dev->dev, psy_desc);
+err_free_bat:
+ devm_kfree(&dev->dev, bat);
dev->battery = NULL;
return error;
}
-static bool hidinput_update_battery_charge_status(struct hid_device *dev,
+static bool hidinput_update_battery_charge_status(struct hid_battery *bat,
unsigned int usage, int value)
{
switch (usage) {
case HID_BAT_CHARGING:
- dev->battery_charge_status = value ?
- POWER_SUPPLY_STATUS_CHARGING :
- POWER_SUPPLY_STATUS_DISCHARGING;
+ bat->charge_status = value ?
+ POWER_SUPPLY_STATUS_CHARGING :
+ POWER_SUPPLY_STATUS_DISCHARGING;
return true;
}
@@ -609,34 +623,35 @@ static bool hidinput_update_battery_charge_status(struct hid_device *dev,
static void hidinput_update_battery(struct hid_device *dev, unsigned int usage,
int value)
{
+ struct hid_battery *bat = dev->battery;
int capacity;
- if (!dev->battery)
+ if (!bat)
return;
- if (hidinput_update_battery_charge_status(dev, usage, value)) {
- dev->battery_present = true;
- power_supply_changed(dev->battery);
+ if (hidinput_update_battery_charge_status(bat, usage, value)) {
+ bat->present = true;
+ power_supply_changed(bat->ps);
return;
}
if ((usage & HID_USAGE_PAGE) == HID_UP_DIGITIZER && value == 0)
return;
- if (value < dev->battery_min || value > dev->battery_max)
+ if (value < bat->min || value > bat->max)
return;
- capacity = hidinput_scale_battery_capacity(dev, value);
+ capacity = hidinput_scale_battery_capacity(bat, value);
- 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 =
+ if (bat->status != HID_BATTERY_REPORTED ||
+ capacity != bat->capacity ||
+ ktime_after(ktime_get_coarse(), bat->ratelimit_time)) {
+ bat->present = true;
+ bat->capacity = capacity;
+ bat->status = HID_BATTERY_REPORTED;
+ bat->ratelimit_time =
ktime_add_ms(ktime_get_coarse(), 30 * 1000);
- power_supply_changed(dev->battery);
+ power_supply_changed(bat->ps);
}
}
#else /* !CONFIG_HID_BATTERY_STRENGTH */
diff --git a/drivers/hid/hid-magicmouse.c b/drivers/hid/hid-magicmouse.c
index 9eadf3252..e70bd3dc0 100644
--- a/drivers/hid/hid-magicmouse.c
+++ b/drivers/hid/hid-magicmouse.c
@@ -817,19 +817,21 @@ static int magicmouse_fetch_battery(struct hid_device *hdev)
#ifdef CONFIG_HID_BATTERY_STRENGTH
struct hid_report_enum *report_enum;
struct hid_report *report;
+ struct hid_battery *bat;
- if (!hdev->battery ||
+ bat = hid_get_battery(hdev);
+ if (!bat ||
(!is_usb_magicmouse2(hdev->vendor, hdev->product) &&
!is_usb_magictrackpad2(hdev->vendor, hdev->product)))
return -1;
- report_enum = &hdev->report_enum[hdev->battery_report_type];
- report = report_enum->report_id_hash[hdev->battery_report_id];
+ report_enum = &hdev->report_enum[bat->report_type];
+ report = report_enum->report_id_hash[bat->report_id];
if (!report || report->maxfield < 1)
return -1;
- if (hdev->battery_capacity == hdev->battery_max)
+ if (bat->capacity == bat->max)
return -1;
hid_hw_request(hdev, report, HID_REQ_GET_REPORT);
diff --git a/include/linux/hid.h b/include/linux/hid.h
index 44357295b..71beff003 100644
--- a/include/linux/hid.h
+++ b/include/linux/hid.h
@@ -634,6 +634,36 @@ enum hid_battery_status {
HID_BATTERY_REPORTED, /* Device sent unsolicited battery strength report */
};
+/**
+ * struct hid_battery - represents a single battery power supply
+ * @dev: pointer to the parent hid_device
+ * @ps: the power supply instance
+ * @min: minimum battery value from HID descriptor
+ * @max: maximum battery value from HID descriptor
+ * @report_type: HID report type (input/feature)
+ * @report_id: HID report ID for this battery
+ * @charge_status: current charging status
+ * @status: battery reporting status
+ * @capacity: current battery capacity (0-100)
+ * @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
+ */
+struct hid_battery {
+ struct hid_device *dev;
+ struct power_supply *ps;
+ __s32 min;
+ __s32 max;
+ __s32 report_type;
+ __s32 report_id;
+ __s32 charge_status;
+ enum hid_battery_status status;
+ __s32 capacity;
+ bool avoid_query;
+ bool present;
+ ktime_t ratelimit_time;
+};
+
struct hid_driver;
struct hid_ll_driver;
@@ -670,20 +700,9 @@ struct hid_device {
#ifdef CONFIG_HID_BATTERY_STRENGTH
/*
* Power supply information for HID devices which report
- * battery strength. power_supply was successfully registered if
- * battery is non-NULL.
+ * battery strength. battery is non-NULL if successfully registered.
*/
- struct power_supply *battery;
- __s32 battery_capacity;
- __s32 battery_min;
- __s32 battery_max;
- __s32 battery_report_type;
- __s32 battery_report_id;
- __s32 battery_charge_status;
- enum hid_battery_status battery_status;
- bool battery_avoid_query;
- bool battery_present;
- ktime_t battery_ratelimit_time;
+ struct hid_battery *battery;
#endif
unsigned long status; /* see STAT flags above */
@@ -745,6 +764,13 @@ static inline void hid_set_drvdata(struct hid_device *hdev, void *data)
dev_set_drvdata(&hdev->dev, data);
}
+#ifdef CONFIG_HID_BATTERY_STRENGTH
+static inline struct hid_battery *hid_get_battery(struct hid_device *hdev)
+{
+ return hdev->battery;
+}
+#endif
+
#define HID_GLOBAL_STACK_SIZE 4
#define HID_COLLECTION_STACK_SIZE 4
--
2.53.0
^ permalink raw reply related
* [PATCH v7 1/3] HID: input: Convert battery code to devm_*
From: Lucas Zampieri @ 2026-03-14 1:05 UTC (permalink / raw)
To: linux-input
Cc: Lucas Zampieri, linux-kernel, Jiri Kosina, Benjamin Tissoires,
Sebastian Reichel, Bastien Nocera, linux-pm
In-Reply-To: <20260314010533.110278-1-lcasmz54@gmail.com>
Convert the HID battery code to use devm_* managed resource APIs for
the power_supply_desc allocation, battery name string, and power supply
registration.
The error path uses devm_kfree() to clean up allocated memory if
devm_power_supply_register() fails, preventing memory waste on repeated
setup attempts. The hidinput_cleanup_battery() function is removed as
devm handles cleanup automatically.
Signed-off-by: Lucas Zampieri <lcasmz54@gmail.com>
---
drivers/hid/hid-input.c | 34 +++++++---------------------------
1 file changed, 7 insertions(+), 27 deletions(-)
diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c
index 395138372..7e0f971ef 100644
--- a/drivers/hid/hid-input.c
+++ b/drivers/hid/hid-input.c
@@ -526,13 +526,13 @@ static int hidinput_setup_battery(struct hid_device *dev, unsigned report_type,
if (quirks & HID_BATTERY_QUIRK_IGNORE)
return 0;
- psy_desc = kzalloc_obj(*psy_desc);
+ psy_desc = devm_kzalloc(&dev->dev, sizeof(*psy_desc), GFP_KERNEL);
if (!psy_desc)
return -ENOMEM;
- psy_desc->name = kasprintf(GFP_KERNEL, "hid-%s-battery",
- strlen(dev->uniq) ?
- dev->uniq : dev_name(&dev->dev));
+ psy_desc->name = devm_kasprintf(&dev->dev, GFP_KERNEL, "hid-%s-battery",
+ strlen(dev->uniq) ?
+ dev->uniq : dev_name(&dev->dev));
if (!psy_desc->name) {
error = -ENOMEM;
goto err_free_mem;
@@ -574,7 +574,7 @@ static int hidinput_setup_battery(struct hid_device *dev, unsigned report_type,
dev->battery_present = (quirks & HID_BATTERY_QUIRK_DYNAMIC) ? false : true;
- dev->battery = power_supply_register(&dev->dev, psy_desc, &psy_cfg);
+ dev->battery = devm_power_supply_register(&dev->dev, psy_desc, &psy_cfg);
if (IS_ERR(dev->battery)) {
error = PTR_ERR(dev->battery);
hid_warn(dev, "can't register power supply: %d\n", error);
@@ -585,27 +585,13 @@ static int hidinput_setup_battery(struct hid_device *dev, unsigned report_type,
return 0;
err_free_name:
- kfree(psy_desc->name);
+ devm_kfree(&dev->dev, psy_desc->name);
err_free_mem:
- kfree(psy_desc);
+ devm_kfree(&dev->dev, psy_desc);
dev->battery = NULL;
return error;
}
-static void hidinput_cleanup_battery(struct hid_device *dev)
-{
- const struct power_supply_desc *psy_desc;
-
- if (!dev->battery)
- return;
-
- psy_desc = dev->battery->desc;
- power_supply_unregister(dev->battery);
- kfree(psy_desc->name);
- kfree(psy_desc);
- dev->battery = NULL;
-}
-
static bool hidinput_update_battery_charge_status(struct hid_device *dev,
unsigned int usage, int value)
{
@@ -660,10 +646,6 @@ static int hidinput_setup_battery(struct hid_device *dev, unsigned report_type,
return 0;
}
-static void hidinput_cleanup_battery(struct hid_device *dev)
-{
-}
-
static void hidinput_update_battery(struct hid_device *dev, unsigned int usage,
int value)
{
@@ -2393,8 +2375,6 @@ void hidinput_disconnect(struct hid_device *hid)
{
struct hid_input *hidinput, *next;
- hidinput_cleanup_battery(hid);
-
list_for_each_entry_safe(hidinput, next, &hid->inputs, list) {
list_del(&hidinput->list);
if (hidinput->registered)
--
2.53.0
^ permalink raw reply related
* [PATCH v7 0/3] HID: Add support for multiple batteries per device
From: Lucas Zampieri @ 2026-03-14 1:05 UTC (permalink / raw)
To: linux-input
Cc: Lucas Zampieri, linux-kernel, Jiri Kosina, Benjamin Tissoires,
Sebastian Reichel, Bastien Nocera, linux-pm
This series adds support for HID devices with multiple batteries.
Currently, the HID battery reporting subsystem only supports one battery per
device. There are several devices with multiple batteries that would benefit
from this support:
- Gaming headsets with batteries in both the headset and charging dock
- Wireless earbuds with per-earbud batteries plus charging case
- Split keyboards with per-side batteries
## Proposed Solution
This series introduces struct hid_battery to encapsulate individual battery
state, replaces the old battery fields with a list-based approach, and adds
support for multiple batteries tracked within struct hid_device. Batteries
are identified by report ID and named as hid-{uniq}-battery-{id}. The
implementation is fully backwards compatible with single-battery devices
through a helper function. The series first converts the battery code to
devm_* as preparatory cleanup, which simplifies the subsequent refactoring
and reduces risk of memory management bugs.
## Testing
Tested with split keyboard hardware (Dactyl 5x6) using custom ZMK firmware
that implements per-side HID battery reporting. Each battery (left and right
keyboard halves) reports independently through the power supply interface with
distinct report IDs (0x05 and 0x06).
Test firmware available on my personal fork at:
https://github.com/zampierilucas/zmk/tree/feat/individual-hid-battery-reporting
If this series gets merged, these changes will be proposed to upstream ZMK.
HID descriptor and recording captured with hid-recorder:
D: 0
R: 162 05 01 09 06 a1 01 85 01 05 07 19 e0 29 e7 15 00 25 01 75 01 95 08 81 02 05 07 75 08 95 01 81 03 05 07 15 00 25 01 19 00 29 67 75 01 95 68 81 02 c0 05 0c 09 01 a1 01 85 02 05 0c 15 00 26 ff 0f 19 00 2a ff 0f 75 10 95 06 81 00 c0 05 84 09 05 a1 01 05 85 85 05 09 44 15 00 25 01 35 00 45 01 75 08 95 01 81 02 09 65 15 00 25 64 35 00 45 64 75 08 95 01 81 02 c0 05 84 09 05 a1 01 05 85 85 06 09 44 15 00 25 01 35 00 45 01 75 08 95 01 81 02 09 65 15 00 25 64 35 00 45 64 75 08 95 01 81 02 c0
N: ZMK Project Dactyl 5x6
P: usb-0000:2d:00.3-4.2/input2
I: 3 1d50 615e
D: 0
E: 0.000000 3 05 00 56
E: 0.000977 3 05 00 56
E: 1.490974 3 06 00 52
E: 1.491958 3 06 00 52
E: 6.492979 3 06 00 53
E: 6.493962 3 06 00 53
The recording shows both batteries reporting with different charge levels
(Report ID 05: 86%, Report ID 06: 82%-83%), demonstrating the multi-battery
functionality. This can be used to verify UPower compatibility.
## Future Work: Userspace Integration
As suggested by Bastien, semantic battery differentiation (e.g., "left
earbud" vs "right earbud") requires userspace coordination, as HID
reports typically lack role metadata.
This will require:
1. systemd/hwdb entries for device-specific battery role mappings
2. UPower updates to enumerate and group multi-battery devices
3. Desktop environment changes to display batteries with meaningful labels
This kernel infrastructure is a prerequisite for that userspace work.
Lucas Zampieri (3):
HID: input: Convert battery code to devm_*
HID: input: Introduce struct hid_battery and refactor battery code
HID: input: Add support for multiple batteries per device
Signed-off-by: Lucas Zampieri <lcasmz54@gmail.com>
Changes in v7:
- Rebased on top of hid.git for-next branch as requested by Jiri
- Adapted to new battery_present field and HID_BATTERY_QUIRK_DYNAMIC
quirk introduced upstream
- Adapted to kzalloc_obj() -> devm_kzalloc() and __free(kfree) cleanup
guard changes in upstream
Changes in v6:
- Split v5 patch 2/2 into two separate patches as suggested by Benjamin:
- Patch 2/3: Introduce struct hid_battery and convert existing code
(no functional change for single-battery devices)
- Patch 3/3: Add multi-battery list support
- Renamed hid_get_first_battery() to hid_get_battery() as suggested by
Benjamin
- Added devm_kfree() calls in error path of hidinput_setup_battery() for
proper cleanup if devm_power_supply_register() fails
Changes in v5:
- Split the monolithic v4 patch into two logical patches as suggested by
Benjamin, devm_* conversion, then struct refactor and multi-battery support
combined
Changes in v4:
- Added missing hidinput_update_battery() stub in #else block for
CONFIG_HID_BATTERY_STRENGTH=n builds
- Reported-by: kernel test robot <lkp@intel.com>
- Closes: https://lore.kernel.org/oe-kbuild-all/202511201624.yUv4VtBv-lkp@intel.com/
Changes in v3:
- Squashed the three v2 patches into a single patch as suggested by
Benjamin
- Removed all legacy dev->battery_* fields, using list-based storage only
- Changed battery naming to include report ID: hid-{uniq}-battery-{report_id}
- Converted battery memory management to devm_* for automatic cleanup
- Updated hidinput_update_battery() to take struct hid_battery directly
- Added hid_get_first_battery() helper for external driver compatibility
- Updated hid-apple.c and hid-magicmouse.c to use new battery API
- Simplified cover letter based on feedback
Changes in v2:
- Split the monolithic v1 patch into three logical patches for easier review:
1. Introduce struct hid_battery (pure structure addition)
2. Refactor existing code to use the new structure (internal changes)
3. Add multi-battery support (new functionality)
- Added detailed testing section with hardware specifics
- Added hid-recorder output (dactyl-hid-recording.txt) demonstrating two-battery
HID descriptor for UPower validation
- Added "Future Work: Userspace Integration" section addressing Bastien's feedback
about semantic battery differentiation
- Added hardware examples with product links to commit messages (per Bastien's
suggestion)
- No functional changes from v1, only improved patch organization and documentation
drivers/hid/hid-apple.c | 10 +-
drivers/hid/hid-core.c | 4 +
drivers/hid/hid-input-test.c | 39 ++++---
drivers/hid/hid-input.c | 197 ++++++++++++++++++-----------------
drivers/hid/hid-magicmouse.c | 10 +-
include/linux/hid.h | 57 +++++++---
6 files changed, 186 insertions(+), 131 deletions(-)
base-commit: 73965d0aefc8ef6504552e5805f645d58a8727b0
--
2.53.0
^ permalink raw reply
* Re: [PATCH 01/15] tracepoint: Add trace_invoke_##name() API
From: Keith Busch @ 2026-03-14 0:24 UTC (permalink / raw)
To: Vineeth Remanan Pillai
Cc: Peter Zijlstra, Steven Rostedt, Dmitry Ilvokhin, Masami Hiramatsu,
Mathieu Desnoyers, Ingo Molnar, Jens Axboe, io-uring,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Alexei Starovoitov, Daniel Borkmann, Marcelo Ricardo Leitner,
Xin Long, Jon Maloy, Aaron Conole, Eelco Chaudron, Ilya Maximets,
netdev, bpf, linux-sctp, tipc-discussion, dev, Oded Gabbay,
Koby Elbaz, dri-devel, Rafael J. Wysocki, Viresh Kumar,
Gautham R. Shenoy, Huang Rui, Mario Limonciello, Len Brown,
Srinivas Pandruvada, linux-pm, MyungJoo Ham, Kyungmin Park,
Chanwoo Choi, Christian König, Sumit Semwal, linaro-mm-sig,
Eddie James, Andrew Jeffery, Joel Stanley, linux-fsi,
David Airlie, Simona Vetter, Alex Deucher, Danilo Krummrich,
Matthew Brost, Philipp Stanner, Harry Wentland, Leo Li, amd-gfx,
Jiri Kosina, Benjamin Tissoires, linux-input, Wolfram Sang,
linux-i2c, Mark Brown, Michael Hennerich, Nuno Sá, linux-spi,
James E.J. Bottomley, Martin K. Petersen, linux-scsi, Chris Mason,
David Sterba, linux-btrfs, linux-trace-kernel, linux-kernel
In-Reply-To: <CAO7JXPiu8-LE_gG001_GQLoGVYakPdzmH2SXLqfzJjEUxbn1Rw@mail.gmail.com>
On Thu, Mar 12, 2026 at 12:05:37PM -0400, Vineeth Remanan Pillai wrote:
> On Thu, Mar 12, 2026 at 11:53 AM Peter Zijlstra <peterz@infradead.org> wrote:
> >
> > That seems like an unreasonable waste of energy. You could've had claude
> > write a Coccinelle script for you and saved a ton of tokens.
>
> Yeah true, Steve also mentioned this to me offline. Haven't used
> Coccinelle before, but now I know :-)
[+ Chris Mason]
At the risk of creating a distraction...
This discussion got me thinking the right skill loaded should have the
AI implicitly use coccinelle to generate the patchset rather than do it
by hand. You could prompt with simple language for a pattern
substitution rather than explicitly request coccinelle, and it should
generate a patch set using a script rather than spending tokens on doing
it "by hand".
I sent such a "skill" to Chris' kernel "review-prompts":
https://github.com/masoncl/review-prompts/pull/35
I used patch one from this series as the starting point and let the AI
figure the rest out. The result actually found additional patterns that
could take advantage of the optimisation that this series did not
include. The resulting kernel tree that the above github pull request
references cost 2.8k tokens to create with the skill.
^ permalink raw reply
* Re: [PATCH 1/2] dt-bindings: input: touchscreen: ti,tsc2005: Add wakeup-source
From: Rob Herring @ 2026-03-13 23:23 UTC (permalink / raw)
To: phucduc.bui
Cc: Dmitry Torokhov, Krzysztof Kozlowski, Conor Dooley, Ingo Molnar,
Thomas Gleixner, Marek Vasut, Michael Welling, linux-input,
devicetree, linux-kernel
In-Reply-To: <20260309110045.108209-2-phucduc.bui@gmail.com>
On Mon, Mar 09, 2026 at 06:00:43PM +0700, phucduc.bui@gmail.com wrote:
> From: bui duc phuc <phucduc.bui@gmail.com>
>
> The tsc200x driver uses the "wakeup-source" device tree property to
> determine whether the device should be configured as a system wakeup
> source.
>
> In the driver, this property is read with:
>
> device_init_wakeup(dev,
> device_property_read_bool(dev, "wakeup-source"));
Write your commit messages independent of the Linux driver.
>
> Document this property in the binding to make it visible to DT schema
> validation tools and to clarify its usage in device tree descriptions.
>
> Signed-off-by: bui duc phuc <phucduc.bui@gmail.com>
> ---
> .../devicetree/bindings/input/touchscreen/ti,tsc2005.yaml | 7 +++++++
> 1 file changed, 7 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/input/touchscreen/ti,tsc2005.yaml b/Documentation/devicetree/bindings/input/touchscreen/ti,tsc2005.yaml
> index 7187c390b2f5..c0aae044d7d4 100644
> --- a/Documentation/devicetree/bindings/input/touchscreen/ti,tsc2005.yaml
> +++ b/Documentation/devicetree/bindings/input/touchscreen/ti,tsc2005.yaml
> @@ -55,6 +55,9 @@ properties:
> touchscreen-size-x: true
> touchscreen-size-y: true
>
> + wakeup-source:
> + type: boolean
> +
> allOf:
> - $ref: touchscreen.yaml#
> - if:
> @@ -97,6 +100,8 @@ examples:
>
> ti,x-plate-ohms = <280>;
> ti,esd-recovery-timeout-ms = <8000>;
> +
> + wakeup-source;
> };
> };
> - |
> @@ -124,5 +129,7 @@ examples:
>
> ti,x-plate-ohms = <280>;
> ti,esd-recovery-timeout-ms = <8000>;
> +
> + wakeup-source;
> };
> };
> --
> 2.43.0
>
^ permalink raw reply
* Re: [PATCH v6 4/4] Input: Add TouchNetix aXiom I2C Touchscreen support
From: Marco Felsch @ 2026-03-13 19:50 UTC (permalink / raw)
To: Andrew Thomas
Cc: Luis Chamberlain, Russ Weight, Greg Kroah-Hartman,
Rafael J. Wysocki, Andrew Morton, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Dmitry Torokhov, Kamel Bouhara,
Marco Felsch, Henrik Rydberg, Danilo Krummrich, linux-kernel,
devicetree, linux-input
In-Reply-To: <rnbwxsdiwjojk7354c6k4us6xxl3qpbyt2lrbhgqz77avrdwga@tqb6voucuysi>
Hi Andrew,
thanks for your feedback! Please see below.
On 26-03-13, Andrew Thomas wrote:
> On Tue, Mar 03, 2026 at 11:41:22PM +0100, Marco Felsch wrote:
> >This adds the initial support for the TouchNetix AX54A touchcontroller
> >which is part of TouchNetix's aXiom touchscreen controller family.
> >
> >The TouchNetix aXiom family provides two physical interfaces: SPI and
> >I2C. This patch covers only the I2C interface.
> >
> >Apart the input event handling the driver supports firmware updates too.
> >One firmware interface handles the touchcontroller firmware (AXFW)
> >update the other handles the touchcontroller configuration (TH2CFGBIN)
> >update.
> >
> >Signed-off-by: Marco Felsch <m.felsch@pengutronix.de>
> >---
...
> >+static int axiom_u02_enter_bootloader(struct axiom_data *ts)
> >+{
> >+ struct axiom_u02_rev1_system_manager_msg msg = { };
> >+ struct device *dev = ts->dev;
> >+ unsigned int val;
> >+ int error;
> >+
> >+ if (!axiom_driver_supports_usage(ts, AXIOM_U02))
> >+ return -EINVAL;
> >+
> >+ /*
> >+ * Enter the bootloader mode requires 3 consecutive messages so we can't
> >+ * check for the response.
> >+ * TODO: Check if it's required to add a delay between the consecutive
> >+ * CMD_ENTERBOOTLOADER cmds.
> >+ */
> >+ msg.command = cpu_to_le16(AXIOM_U02_REV1_CMD_ENTERBOOTLOADER);
> >+ msg.parameters[0] = cpu_to_le16(AXIOM_U02_REV1_PARAM0_ENTERBOOLOADER_KEY1);
> >+ error = axiom_u02_send_msg(ts, &msg, false);
>
> As mentioned before the delay between commands is too short and the
> next command is sent before u02 is ready, which means the driver fails
> to put axiom into the bootloader.
Please see my comment [1].
> Have you tested with an i2c speed of 400KHz?
Yes, my target platform is based on a i.MX8MP.
> All you need is to put true in above to wait for the bootloader command.
> error = axiom_u02_send_msg(ts, &msg, true);
Please see my comment [1].
> Just dont do it for the last command.
Please see my comment [1].
> I am not too sure why you are having issues with this, this is how we
> do it for all our devices.
Please see my comment [1].
On what platform do you perform the tests?
> >+ if (error) {
> >+ dev_err(dev, "Failed to send bootloader-key1: %d\n", error);
> >+ return error;
> >+ }
> >+
> >+ msg.parameters[0] = cpu_to_le16(AXIOM_U02_REV1_PARAM0_ENTERBOOLOADER_KEY2);
> >+ error = axiom_u02_send_msg(ts, &msg, false);
>
> Here also.
Please see my comment [1].
> >+ if (error) {
> >+ dev_err(dev, "Failed to send bootloader-key2: %d\n", error);
> >+ return error;
> >+ }
> >+
> >+ msg.parameters[0] = cpu_to_le16(AXIOM_U02_REV1_PARAM0_ENTERBOOLOADER_KEY3);
> >+ error = axiom_u02_send_msg(ts, &msg, false);
> >+ if (error) {
> >+ dev_err(dev, "Failed to send bootloader-key3: %d\n", error);
> >+ return error;
> >+ }
> >+
> >+ /* Sleep before the first read to give the device time */
> >+ fsleep(250 * USEC_PER_MSEC);
> >+
> >+ /* Wait till the device reports it is in bootloader mode */
> >+ error = regmap_read_poll_timeout(ts->regmap,
> >+ AXIOM_U31_REV1_DEVICE_ID_HIGH_REG, val,
> >+ FIELD_GET(AXIOM_U31_REV1_MODE_MASK, val) ==
> >+ AXIOM_U31_REV1_MODE_BLP,
> >+ 250 * USEC_PER_MSEC, USEC_PER_SEC);
> >+ if (error)
> >+ return error;
> >+
> >+ return 0;
> >+}
> >+
>
> ...
>
>
> Other than the above comments I have no issues with the driver.
If you're fine with the patch you could add your acked-by [2] :)
> We can support more usages in a later patch.
Sure :)
[1] https://lore.kernel.org/all/4x3dnedfzf3rqzsy3wjdoj6yaxmy6kop37xhxeao4vjer7ifdi@35ux42ztq3eb/
[2] https://docs.kernel.org/process/submitting-patches.html#when-to-use-acked-by-cc-and-co-developed-by
Regards,
Marco
--
#gernperDu
#CallMeByMyFirstName
Pengutronix e.K. | |
Steuerwalder Str. 21 | https://www.pengutronix.de/ |
31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-9 |
^ permalink raw reply
* [PATCH v2] dt-bindings: touchscreen: trivial-touch: Move allOf: after required:
From: Marek Vasut @ 2026-03-13 19:33 UTC (permalink / raw)
To: devicetree
Cc: Marek Vasut, Conor Dooley, Frank Li, Conor Dooley,
Dmitry Torokhov, Job Noorman, Krzysztof Kozlowski, Rob Herring,
linux-input, linux-renesas-soc
Majority of schemas place 'allOf:' after 'required:' . Documentation
"Documentation/devicetree/bindings/writing-schema.rst" also hints at
this ordering. Trivially update this schema. No functional change.
Acked-by: Conor Dooley <conor.dooley@microchip.com>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Signed-off-by: Marek Vasut <marek.vasut+renesas@mailbox.org>
---
NOTE: This comes from https://lore.kernel.org/all/20260117-grinning-heavy-crab-11f245@quoll/
where krzk comments "allOf: should be placed after required: block."
---
Cc: Conor Dooley <conor+dt@kernel.org>
Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Cc: Frank Li <Frank.Li@nxp.com>
Cc: Job Noorman <job@noorman.info>
Cc: Krzysztof Kozlowski <krzk+dt@kernel.org>
Cc: Rob Herring <robh@kernel.org>
Cc: devicetree@vger.kernel.org
Cc: linux-input@vger.kernel.org
Cc: linux-renesas-soc@vger.kernel.org
---
V2: - Fix up the nits from Frank
- Add RB from Frank
- Add AB from Conor
---
.../bindings/input/touchscreen/trivial-touch.yaml | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/Documentation/devicetree/bindings/input/touchscreen/trivial-touch.yaml b/Documentation/devicetree/bindings/input/touchscreen/trivial-touch.yaml
index 6441d21223caf..6316a8d32f39b 100644
--- a/Documentation/devicetree/bindings/input/touchscreen/trivial-touch.yaml
+++ b/Documentation/devicetree/bindings/input/touchscreen/trivial-touch.yaml
@@ -53,14 +53,14 @@ properties:
wakeup-source: true
-allOf:
- - $ref: touchscreen.yaml
-
required:
- compatible
- reg
- interrupts
+allOf:
+ - $ref: touchscreen.yaml
+
unevaluatedProperties: false
examples:
--
2.51.0
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox