* [PATCH v3 RESEND RESEND 0/3] Initial work for Rust abstraction for HID device driver development
@ 2025-09-13 16:12 Rahul Rameshbabu
2025-09-13 16:12 ` [PATCH v3 RESEND RESEND 1/3] HID: core: Change hid_driver to use a const char* for name Rahul Rameshbabu
` (3 more replies)
0 siblings, 4 replies; 9+ messages in thread
From: Rahul Rameshbabu @ 2025-09-13 16:12 UTC (permalink / raw)
To: linux-input, linux-kernel, rust-for-linux
Cc: Jiri Kosina, a.hindborg, alex.gaynor, aliceryhl, benno.lossin,
Benjamin Tissoires, bjorn3_gh, boqun.feng, dakr, db48x, gary,
ojeda, tmgross, peter.hutterer, Rahul Rameshbabu
Hello again,
I am doing another resend. Let me know if it makes sense to start sending out
work I have on top of these changes. I wanted to wait till these changes got
merged first but maybe that is not the right strategy?
https://lore.kernel.org/rust-for-linux/20250721020211.196394-2-sergeantsagara@protonmail.com/
I incorporated Danilo's and Miguel's feedback from my v2. Additionally, I
noticed I had basic formatting issues when running scripts/checkpatch.pl.
I made sure to check the generated rustdocs and that the Rust examples
compile as part of the kunit infrastructure. I dropped the kref bindings
as they are no longer needed for this series.
Link: https://lore.kernel.org/rust-for-linux/20250808061223.3770-1-sergeantsagara@protonmail.com/
Link: https://lore.kernel.org/rust-for-linux/20250721020211.196394-2-sergeantsagara@protonmail.com/
Link: https://lore.kernel.org/rust-for-linux/20250713211012.101476-4-sergeantsagara@protonmail.com/
Link: https://lore.kernel.org/rust-for-linux/20250629045031.92358-2-sergeantsagara@protonmail.com/
Link: https://lore.kernel.org/rust-for-linux/20250313160220.6410-2-sergeantsagara@protonmail.com/
Link: https://binary-eater.github.io/tags/usb-monitor-control/
Rahul Rameshbabu (3):
HID: core: Change hid_driver to use a const char* for name
rust: core abstractions for HID drivers
rust: hid: Glorious PC Gaming Race Model O and O- mice reference
driver
MAINTAINERS | 16 +
drivers/hid/Kconfig | 16 +
drivers/hid/Makefile | 1 +
drivers/hid/hid-glorious.c | 2 +
drivers/hid/hid_glorious_rust.rs | 60 ++++
include/linux/hid.h | 2 +-
rust/bindings/bindings_helper.h | 3 +
rust/kernel/hid.rs | 503 +++++++++++++++++++++++++++++++
rust/kernel/lib.rs | 2 +
9 files changed, 604 insertions(+), 1 deletion(-)
create mode 100644 drivers/hid/hid_glorious_rust.rs
create mode 100644 rust/kernel/hid.rs
base-commit: 1523590203786bf4e1d29b7d08a7100c783f20ba
--
2.47.2
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v3 RESEND RESEND 1/3] HID: core: Change hid_driver to use a const char* for name
2025-09-13 16:12 [PATCH v3 RESEND RESEND 0/3] Initial work for Rust abstraction for HID device driver development Rahul Rameshbabu
@ 2025-09-13 16:12 ` Rahul Rameshbabu
2025-09-17 9:51 ` Benjamin Tissoires
2025-09-13 16:13 ` [PATCH v3 RESEND RESEND 2/3] rust: core abstractions for HID drivers Rahul Rameshbabu
` (2 subsequent siblings)
3 siblings, 1 reply; 9+ messages in thread
From: Rahul Rameshbabu @ 2025-09-13 16:12 UTC (permalink / raw)
To: linux-input, linux-kernel, rust-for-linux
Cc: Jiri Kosina, a.hindborg, alex.gaynor, aliceryhl, benno.lossin,
Benjamin Tissoires, bjorn3_gh, boqun.feng, dakr, db48x, gary,
ojeda, tmgross, peter.hutterer, Rahul Rameshbabu
name is never mutated by the core HID stack. Making name a const char*
simplifies passing the string from Rust to C. Otherwise, it becomes
difficult to pass a 'static lifetime CStr from Rust to a char*, rather than
a const char*, due to lack of guarantee that the underlying data of the
CStr will not be mutated by the C code.
Signed-off-by: Rahul Rameshbabu <sergeantsagara@protonmail.com>
---
include/linux/hid.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/include/linux/hid.h b/include/linux/hid.h
index 568a9d8c749b..d65c202783da 100644
--- a/include/linux/hid.h
+++ b/include/linux/hid.h
@@ -816,7 +816,7 @@ struct hid_usage_id {
* zero from them.
*/
struct hid_driver {
- char *name;
+ const char *name;
const struct hid_device_id *id_table;
struct list_head dyn_list;
--
2.47.2
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v3 RESEND RESEND 2/3] rust: core abstractions for HID drivers
2025-09-13 16:12 [PATCH v3 RESEND RESEND 0/3] Initial work for Rust abstraction for HID device driver development Rahul Rameshbabu
2025-09-13 16:12 ` [PATCH v3 RESEND RESEND 1/3] HID: core: Change hid_driver to use a const char* for name Rahul Rameshbabu
@ 2025-09-13 16:13 ` Rahul Rameshbabu
2025-09-17 10:04 ` Benjamin Tissoires
2025-09-13 16:13 ` [PATCH v3 RESEND RESEND 3/3] rust: hid: Glorious PC Gaming Race Model O and O- mice reference driver Rahul Rameshbabu
2025-09-17 10:25 ` (subset) [PATCH v3 RESEND RESEND 0/3] Initial work for Rust abstraction for HID device driver development Benjamin Tissoires
3 siblings, 1 reply; 9+ messages in thread
From: Rahul Rameshbabu @ 2025-09-13 16:13 UTC (permalink / raw)
To: linux-input, linux-kernel, rust-for-linux
Cc: Jiri Kosina, a.hindborg, alex.gaynor, aliceryhl, benno.lossin,
Benjamin Tissoires, bjorn3_gh, boqun.feng, dakr, db48x, gary,
ojeda, tmgross, peter.hutterer, Rahul Rameshbabu
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:
Some points I did not address from the last review cycle:
* I did not look into autogenerating all the getter functions for various
fields exported from the binded C structures.
- I would be interested in hearing opinions from folks actively involved
with Rust for Linux on this topic.
Changelog:
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 | 9 +
drivers/hid/Kconfig | 8 +
rust/bindings/bindings_helper.h | 3 +
rust/kernel/hid.rs | 503 ++++++++++++++++++++++++++++++++
rust/kernel/lib.rs | 2 +
5 files changed, 525 insertions(+)
create mode 100644 rust/kernel/hid.rs
diff --git a/MAINTAINERS b/MAINTAINERS
index dd810da5261b..6c60765f2aaa 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -10686,6 +10686,15 @@ 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
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid.git rust
+F: drivers/hid/*.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 43859fc75747..922e76e18af2 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -744,6 +744,14 @@ config HID_MEGAWORLD_FF
Say Y here if you have a Mega World based game controller and want
to have force feedback support for it.
+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.
+
config HID_REDRAGON
tristate "Redragon keyboards"
default !EXPERT
diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
index 8cbb660e2ec2..7145fb1cdff1 100644
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@ -45,6 +45,7 @@
#include <linux/cpufreq.h>
#include <linux/cpumask.h>
#include <linux/cred.h>
+#include <linux/device.h>
#include <linux/device/faux.h>
#include <linux/dma-mapping.h>
#include <linux/errname.h>
@@ -52,6 +53,8 @@
#include <linux/file.h>
#include <linux/firmware.h>
#include <linux/fs.h>
+#include <linux/hid.h>
+#include "../../drivers/hid/hid-ids.h"
#include <linux/jiffies.h>
#include <linux/jump_label.h>
#include <linux/mdio.h>
diff --git a/rust/kernel/hid.rs b/rust/kernel/hid.rs
new file mode 100644
index 000000000000..a93804af8b78
--- /dev/null
+++ b/rust/kernel/hid.rs
@@ -0,0 +1,503 @@
+// SPDX-License-Identifier: GPL-2.0
+
+// Copyright (C) 2025 Rahul Rameshbabu <sergeantsagara@protonmail.com>
+
+//! Abstractions for the HID interface.
+//!
+//! C header: [`include/linux/hid.h`](srctree/include/linux/hid.h)
+
+use crate::{device, device_id::RawDeviceId, driver, error::*, prelude::*, types::Opaque};
+use core::{
+ marker::PhantomData,
+ ptr::{addr_of_mut, NonNull},
+};
+
+/// Indicates the item is static read-only.
+///
+/// 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
+pub const MAIN_ITEM_CONSTANT: u8 = bindings::HID_MAIN_ITEM_CONSTANT as u8;
+
+/// Indicates the item represents data from a physical control.
+///
+/// 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
+pub const MAIN_ITEM_VARIABLE: u8 = bindings::HID_MAIN_ITEM_VARIABLE as u8;
+
+/// Indicates the item should be treated as a relative change from the previous
+/// report.
+///
+/// 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
+pub const MAIN_ITEM_RELATIVE: u8 = bindings::HID_MAIN_ITEM_RELATIVE as u8;
+
+/// Indicates the item should wrap around when reaching the extreme high or
+/// extreme low values.
+///
+/// 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
+pub const MAIN_ITEM_WRAP: u8 = bindings::HID_MAIN_ITEM_WRAP as u8;
+
+/// Indicates the item should wrap around when reaching the extreme high or
+/// extreme low values.
+///
+/// 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
+pub const MAIN_ITEM_NONLINEAR: u8 = bindings::HID_MAIN_ITEM_NONLINEAR as u8;
+
+/// Indicates whether the control has a preferred state it will physically
+/// return to without user intervention.
+///
+/// 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
+pub const MAIN_ITEM_NO_PREFERRED: u8 = bindings::HID_MAIN_ITEM_NO_PREFERRED as u8;
+
+/// Indicates whether the control has a physical state where it will not send
+/// any reports.
+///
+/// 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
+pub const MAIN_ITEM_NULL_STATE: u8 = bindings::HID_MAIN_ITEM_NULL_STATE as u8;
+
+/// Indicates whether the control requires host system logic to change state.
+///
+/// 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
+pub const MAIN_ITEM_VOLATILE: u8 = bindings::HID_MAIN_ITEM_VOLATILE as u8;
+
+/// Indicates whether the item is fixed size or a variable buffer of bytes.
+///
+/// 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
+pub const MAIN_ITEM_BUFFERED_BYTE: u8 = bindings::HID_MAIN_ITEM_BUFFERED_BYTE as u8;
+
+/// 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`].
+ const fn into(self) -> u16 {
+ self as u16
+ }
+}
+
+impl From<u16> for Group {
+ /// [`u16`] values can be safely converted to [`Group`] variants.
+ fn from(value: u16) -> Self {
+ match value.into() {
+ bindings::HID_GROUP_GENERIC => Group::Generic,
+ bindings::HID_GROUP_MULTITOUCH => Group::Multitouch,
+ bindings::HID_GROUP_SENSOR_HUB => Group::SensorHub,
+ bindings::HID_GROUP_MULTITOUCH_WIN_8 => Group::MultitouchWin8,
+ bindings::HID_GROUP_RMI => Group::RMI,
+ bindings::HID_GROUP_WACOM => Group::Wacom,
+ bindings::HID_GROUP_LOGITECH_DJ_DEVICE => Group::LogitechDJDevice,
+ bindings::HID_GROUP_STEAM => Group::Steam,
+ bindings::HID_GROUP_LOGITECH_27MHZ_DEVICE => Group::Logitech27MHzDevice,
+ bindings::HID_GROUP_VIVALDI => Group::Vivaldi,
+ _ => Group::Any,
+ }
+ }
+}
+
+/// 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> {
+ 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) -> Group {
+ // SAFETY: `self.as_raw` is a valid pointer to a `struct hid_device`
+ unsafe { *self.as_raw() }.group.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::as_ref(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 {
+ /// Equivalent to C's `HID_USB_DEVICE` macro.
+ ///
+ /// Create a new `hid::DeviceId` from a group, vendor ID, and device ID
+ /// number.
+ pub const fn new_usb(group: Group, vendor: u32, product: u32) -> Self {
+ Self(bindings::hid_device_id {
+ bus: 0x3, // BUS_USB
+ group: group.into(),
+ 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) -> Group {
+ self.0.group.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;
+
+ 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, 'b: 'a>(_hdev: &hid::Device<device::Core>, rdesc: &'b 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, 'b: 'a>(_hdev: &Device<device::Core>, _rdesc: &'b mut [u8]) -> &'a [u8] {
+ build_error!(VTABLE_DEFAULT_ERROR)
+ }
+}
+
+/// An adapter for the registration of HID drivers.
+pub struct Adapter<T: Driver>(T);
+
+// SAFETY: A call to `unregister` for a given instance of `RegType` is guaranteed to be valid if
+// a preceding call to `register` has been successful.
+unsafe impl<T: Driver + 'static> driver::RegistrationOps for Adapter<T> {
+ type RegType = bindings::hid_driver;
+
+ unsafe fn register(
+ hdrv: &Opaque<Self::RegType>,
+ 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 `RegType`
+ to_result(unsafe {
+ bindings::__hid_register_driver(hdrv.get(), module.0, name.as_char_ptr())
+ })
+ }
+
+ unsafe fn unregister(hdrv: &Opaque<Self::RegType>) {
+ // SAFETY: `hdrv` is guaranteed to be a valid `RegType`
+ 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 = match unsafe { *size }.try_into() {
+ Ok(len) => len,
+ Err(e) => {
+ dev_err!(
+ hdev.as_ref(),
+ "Cannot fix report description due to {}!\n",
+ e
+ );
+
+ return buf;
+ }
+ };
+
+ // 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);
+
+ match rdesc_slice.len().try_into() {
+ // 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()`.
+ Ok(len) => unsafe { *size = len },
+ Err(e) => {
+ dev_err!(
+ hdev.as_ref(),
+ "Fixed report description will not be used due to {}!\n",
+ e
+ );
+
+ return buf;
+ }
+ }
+
+ 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 e88bc4b27d6e..44c107f20174 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -80,6 +80,8 @@
pub mod firmware;
pub mod fmt;
pub mod fs;
+#[cfg(CONFIG_RUST_HID_ABSTRACTIONS)]
+pub mod hid;
pub mod init;
pub mod io;
pub mod ioctl;
--
2.47.2
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v3 RESEND RESEND 3/3] rust: hid: Glorious PC Gaming Race Model O and O- mice reference driver
2025-09-13 16:12 [PATCH v3 RESEND RESEND 0/3] Initial work for Rust abstraction for HID device driver development Rahul Rameshbabu
2025-09-13 16:12 ` [PATCH v3 RESEND RESEND 1/3] HID: core: Change hid_driver to use a const char* for name Rahul Rameshbabu
2025-09-13 16:13 ` [PATCH v3 RESEND RESEND 2/3] rust: core abstractions for HID drivers Rahul Rameshbabu
@ 2025-09-13 16:13 ` Rahul Rameshbabu
2025-09-17 10:25 ` (subset) [PATCH v3 RESEND RESEND 0/3] Initial work for Rust abstraction for HID device driver development Benjamin Tissoires
3 siblings, 0 replies; 9+ messages in thread
From: Rahul Rameshbabu @ 2025-09-13 16:13 UTC (permalink / raw)
To: linux-input, linux-kernel, rust-for-linux
Cc: Jiri Kosina, a.hindborg, alex.gaynor, aliceryhl, benno.lossin,
Benjamin Tissoires, bjorn3_gh, boqun.feng, dakr, db48x, gary,
ojeda, tmgross, peter.hutterer, Rahul Rameshbabu
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:
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 | 7 ++++
drivers/hid/Kconfig | 8 +++++
drivers/hid/Makefile | 1 +
drivers/hid/hid-glorious.c | 2 ++
drivers/hid/hid_glorious_rust.rs | 60 ++++++++++++++++++++++++++++++++
5 files changed, 78 insertions(+)
create mode 100644 drivers/hid/hid_glorious_rust.rs
diff --git a/MAINTAINERS b/MAINTAINERS
index 6c60765f2aaa..eee9a33914ef 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -10200,6 +10200,13 @@ 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
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid.git rust
+F: drivers/hid/hid_glorious_rust.rs
+
GNSS SUBSYSTEM
M: Johan Hovold <johan@kernel.org>
S: Maintained
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index 922e76e18af2..b8ef750fb8b6 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -406,6 +406,14 @@ config HID_GLORIOUS
Support for Glorious PC Gaming Race mice such as
the Glorious Model O, O- and D.
+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.
+
config HID_HOLTEK
tristate "Holtek HID devices"
depends on USB_HID
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index 10ae5dedbd84..bd86b3db5d88 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -55,6 +55,7 @@ obj-$(CONFIG_HID_FT260) += hid-ft260.o
obj-$(CONFIG_HID_GEMBIRD) += hid-gembird.o
obj-$(CONFIG_HID_GFRM) += hid-gfrm.o
obj-$(CONFIG_HID_GLORIOUS) += hid-glorious.o
+obj-$(CONFIG_HID_GLORIOUS_RUST) += hid_glorious_rust.o
obj-$(CONFIG_HID_VIVALDI_COMMON) += hid-vivaldi-common.o
obj-$(CONFIG_HID_GOODIX_SPI) += hid-goodix-spi.o
obj-$(CONFIG_HID_GOOGLE_HAMMER) += hid-google-hammer.o
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/hid_glorious_rust.rs b/drivers/hid/hid_glorious_rust.rs
new file mode 100644
index 000000000000..8cffc1c605dd
--- /dev/null
+++ b/drivers/hid/hid_glorious_rust.rs
@@ -0,0 +1,60 @@
+// SPDX-License-Identifier: GPL-2.0
+
+// Copyright (C) 2025 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, 'b: 'a>(hdev: &hid::Device<device::Core>, rdesc: &'b mut [u8]) -> &'a [u8] {
+ if rdesc.len() == 213
+ && (rdesc[84] == 129 && rdesc[85] == 3)
+ && (rdesc[112] == 129 && rdesc[113] == 3)
+ && (rdesc[140] == 129 && rdesc[141] == 3)
+ {
+ dev_info!(
+ hdev.as_ref(),
+ "patching Glorious Model O consumer control report descriptor\n"
+ );
+
+ rdesc[85] = hid::MAIN_ITEM_VARIABLE | hid::MAIN_ITEM_RELATIVE;
+ rdesc[113] = hid::MAIN_ITEM_VARIABLE | hid::MAIN_ITEM_RELATIVE;
+ rdesc[141] = hid::MAIN_ITEM_VARIABLE | hid::MAIN_ITEM_RELATIVE;
+ }
+
+ 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.47.2
^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH v3 RESEND RESEND 1/3] HID: core: Change hid_driver to use a const char* for name
2025-09-13 16:12 ` [PATCH v3 RESEND RESEND 1/3] HID: core: Change hid_driver to use a const char* for name Rahul Rameshbabu
@ 2025-09-17 9:51 ` Benjamin Tissoires
0 siblings, 0 replies; 9+ messages in thread
From: Benjamin Tissoires @ 2025-09-17 9:51 UTC (permalink / raw)
To: Rahul Rameshbabu
Cc: linux-input, linux-kernel, rust-for-linux, Jiri Kosina,
a.hindborg, alex.gaynor, aliceryhl, benno.lossin,
Benjamin Tissoires, bjorn3_gh, boqun.feng, dakr, db48x, gary,
ojeda, tmgross, peter.hutterer
On Sep 13 2025, Rahul Rameshbabu wrote:
> name is never mutated by the core HID stack. Making name a const char*
> simplifies passing the string from Rust to C. Otherwise, it becomes
> difficult to pass a 'static lifetime CStr from Rust to a char*, rather than
> a const char*, due to lack of guarantee that the underlying data of the
> CStr will not be mutated by the C code.
>
> Signed-off-by: Rahul Rameshbabu <sergeantsagara@protonmail.com>
While we figure out the rest, I'm queueing this one patch in my test
setup and will probably merge it for 6.18. This way, the rest of the
series is purely rust and doesn't depend on anything on the HID tree.
Cheers,
Benjamin
> ---
> include/linux/hid.h | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/include/linux/hid.h b/include/linux/hid.h
> index 568a9d8c749b..d65c202783da 100644
> --- a/include/linux/hid.h
> +++ b/include/linux/hid.h
> @@ -816,7 +816,7 @@ struct hid_usage_id {
> * zero from them.
> */
> struct hid_driver {
> - char *name;
> + const char *name;
> const struct hid_device_id *id_table;
>
> struct list_head dyn_list;
> --
> 2.47.2
>
>
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v3 RESEND RESEND 2/3] rust: core abstractions for HID drivers
2025-09-13 16:13 ` [PATCH v3 RESEND RESEND 2/3] rust: core abstractions for HID drivers Rahul Rameshbabu
@ 2025-09-17 10:04 ` Benjamin Tissoires
2025-09-17 12:08 ` Rahul Rameshbabu
0 siblings, 1 reply; 9+ messages in thread
From: Benjamin Tissoires @ 2025-09-17 10:04 UTC (permalink / raw)
To: Rahul Rameshbabu
Cc: linux-input, linux-kernel, rust-for-linux, Jiri Kosina,
a.hindborg, alex.gaynor, aliceryhl, benno.lossin,
Benjamin Tissoires, bjorn3_gh, boqun.feng, dakr, db48x, gary,
ojeda, tmgross, peter.hutterer
On Sep 13 2025, Rahul Rameshbabu wrote:
> 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:
> Some points I did not address from the last review cycle:
>
> * I did not look into autogenerating all the getter functions for various
> fields exported from the binded C structures.
> - I would be interested in hearing opinions from folks actively involved
> with Rust for Linux on this topic.
>
> Changelog:
>
> 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 | 9 +
> drivers/hid/Kconfig | 8 +
> rust/bindings/bindings_helper.h | 3 +
> rust/kernel/hid.rs | 503 ++++++++++++++++++++++++++++++++
> rust/kernel/lib.rs | 2 +
> 5 files changed, 525 insertions(+)
> create mode 100644 rust/kernel/hid.rs
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index dd810da5261b..6c60765f2aaa 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -10686,6 +10686,15 @@ 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
> +T: git git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid.git rust
FWIW, we (HID maintainers) are still undecided on how to handle that,
and so it's a little bit postponed for now
> +F: drivers/hid/*.rs
Could you instead make it really independant by relying on
drivers/hid/rust instead?
We already have drivers/hid/bpf for HID-BPF related stuff, so it doesn't
seem to be that much of an issue to have a separate rust dir.
This should allow for a cleaner separation without tinkering in Makefile
or Kconfig if the HID rust tree is handled separately.
Cheers,
Benjamin
> +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 43859fc75747..922e76e18af2 100644
> --- a/drivers/hid/Kconfig
> +++ b/drivers/hid/Kconfig
> @@ -744,6 +744,14 @@ config HID_MEGAWORLD_FF
> Say Y here if you have a Mega World based game controller and want
> to have force feedback support for it.
>
> +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.
> +
> config HID_REDRAGON
> tristate "Redragon keyboards"
> default !EXPERT
> diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
> index 8cbb660e2ec2..7145fb1cdff1 100644
> --- a/rust/bindings/bindings_helper.h
> +++ b/rust/bindings/bindings_helper.h
> @@ -45,6 +45,7 @@
> #include <linux/cpufreq.h>
> #include <linux/cpumask.h>
> #include <linux/cred.h>
> +#include <linux/device.h>
> #include <linux/device/faux.h>
> #include <linux/dma-mapping.h>
> #include <linux/errname.h>
> @@ -52,6 +53,8 @@
> #include <linux/file.h>
> #include <linux/firmware.h>
> #include <linux/fs.h>
> +#include <linux/hid.h>
> +#include "../../drivers/hid/hid-ids.h"
> #include <linux/jiffies.h>
> #include <linux/jump_label.h>
> #include <linux/mdio.h>
> diff --git a/rust/kernel/hid.rs b/rust/kernel/hid.rs
> new file mode 100644
> index 000000000000..a93804af8b78
> --- /dev/null
> +++ b/rust/kernel/hid.rs
> @@ -0,0 +1,503 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +// Copyright (C) 2025 Rahul Rameshbabu <sergeantsagara@protonmail.com>
> +
> +//! Abstractions for the HID interface.
> +//!
> +//! C header: [`include/linux/hid.h`](srctree/include/linux/hid.h)
> +
> +use crate::{device, device_id::RawDeviceId, driver, error::*, prelude::*, types::Opaque};
> +use core::{
> + marker::PhantomData,
> + ptr::{addr_of_mut, NonNull},
> +};
> +
> +/// Indicates the item is static read-only.
> +///
> +/// 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
> +pub const MAIN_ITEM_CONSTANT: u8 = bindings::HID_MAIN_ITEM_CONSTANT as u8;
> +
> +/// Indicates the item represents data from a physical control.
> +///
> +/// 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
> +pub const MAIN_ITEM_VARIABLE: u8 = bindings::HID_MAIN_ITEM_VARIABLE as u8;
> +
> +/// Indicates the item should be treated as a relative change from the previous
> +/// report.
> +///
> +/// 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
> +pub const MAIN_ITEM_RELATIVE: u8 = bindings::HID_MAIN_ITEM_RELATIVE as u8;
> +
> +/// Indicates the item should wrap around when reaching the extreme high or
> +/// extreme low values.
> +///
> +/// 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
> +pub const MAIN_ITEM_WRAP: u8 = bindings::HID_MAIN_ITEM_WRAP as u8;
> +
> +/// Indicates the item should wrap around when reaching the extreme high or
> +/// extreme low values.
> +///
> +/// 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
> +pub const MAIN_ITEM_NONLINEAR: u8 = bindings::HID_MAIN_ITEM_NONLINEAR as u8;
> +
> +/// Indicates whether the control has a preferred state it will physically
> +/// return to without user intervention.
> +///
> +/// 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
> +pub const MAIN_ITEM_NO_PREFERRED: u8 = bindings::HID_MAIN_ITEM_NO_PREFERRED as u8;
> +
> +/// Indicates whether the control has a physical state where it will not send
> +/// any reports.
> +///
> +/// 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
> +pub const MAIN_ITEM_NULL_STATE: u8 = bindings::HID_MAIN_ITEM_NULL_STATE as u8;
> +
> +/// Indicates whether the control requires host system logic to change state.
> +///
> +/// 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
> +pub const MAIN_ITEM_VOLATILE: u8 = bindings::HID_MAIN_ITEM_VOLATILE as u8;
> +
> +/// Indicates whether the item is fixed size or a variable buffer of bytes.
> +///
> +/// 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
> +pub const MAIN_ITEM_BUFFERED_BYTE: u8 = bindings::HID_MAIN_ITEM_BUFFERED_BYTE as u8;
> +
> +/// 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`].
> + const fn into(self) -> u16 {
> + self as u16
> + }
> +}
> +
> +impl From<u16> for Group {
> + /// [`u16`] values can be safely converted to [`Group`] variants.
> + fn from(value: u16) -> Self {
> + match value.into() {
> + bindings::HID_GROUP_GENERIC => Group::Generic,
> + bindings::HID_GROUP_MULTITOUCH => Group::Multitouch,
> + bindings::HID_GROUP_SENSOR_HUB => Group::SensorHub,
> + bindings::HID_GROUP_MULTITOUCH_WIN_8 => Group::MultitouchWin8,
> + bindings::HID_GROUP_RMI => Group::RMI,
> + bindings::HID_GROUP_WACOM => Group::Wacom,
> + bindings::HID_GROUP_LOGITECH_DJ_DEVICE => Group::LogitechDJDevice,
> + bindings::HID_GROUP_STEAM => Group::Steam,
> + bindings::HID_GROUP_LOGITECH_27MHZ_DEVICE => Group::Logitech27MHzDevice,
> + bindings::HID_GROUP_VIVALDI => Group::Vivaldi,
> + _ => Group::Any,
> + }
> + }
> +}
> +
> +/// 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> {
> + 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) -> Group {
> + // SAFETY: `self.as_raw` is a valid pointer to a `struct hid_device`
> + unsafe { *self.as_raw() }.group.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::as_ref(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 {
> + /// Equivalent to C's `HID_USB_DEVICE` macro.
> + ///
> + /// Create a new `hid::DeviceId` from a group, vendor ID, and device ID
> + /// number.
> + pub const fn new_usb(group: Group, vendor: u32, product: u32) -> Self {
> + Self(bindings::hid_device_id {
> + bus: 0x3, // BUS_USB
> + group: group.into(),
> + 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) -> Group {
> + self.0.group.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;
> +
> + 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, 'b: 'a>(_hdev: &hid::Device<device::Core>, rdesc: &'b 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, 'b: 'a>(_hdev: &Device<device::Core>, _rdesc: &'b mut [u8]) -> &'a [u8] {
> + build_error!(VTABLE_DEFAULT_ERROR)
> + }
> +}
> +
> +/// An adapter for the registration of HID drivers.
> +pub struct Adapter<T: Driver>(T);
> +
> +// SAFETY: A call to `unregister` for a given instance of `RegType` is guaranteed to be valid if
> +// a preceding call to `register` has been successful.
> +unsafe impl<T: Driver + 'static> driver::RegistrationOps for Adapter<T> {
> + type RegType = bindings::hid_driver;
> +
> + unsafe fn register(
> + hdrv: &Opaque<Self::RegType>,
> + 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 `RegType`
> + to_result(unsafe {
> + bindings::__hid_register_driver(hdrv.get(), module.0, name.as_char_ptr())
> + })
> + }
> +
> + unsafe fn unregister(hdrv: &Opaque<Self::RegType>) {
> + // SAFETY: `hdrv` is guaranteed to be a valid `RegType`
> + 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 = match unsafe { *size }.try_into() {
> + Ok(len) => len,
> + Err(e) => {
> + dev_err!(
> + hdev.as_ref(),
> + "Cannot fix report description due to {}!\n",
> + e
> + );
> +
> + return buf;
> + }
> + };
> +
> + // 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);
> +
> + match rdesc_slice.len().try_into() {
> + // 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()`.
> + Ok(len) => unsafe { *size = len },
> + Err(e) => {
> + dev_err!(
> + hdev.as_ref(),
> + "Fixed report description will not be used due to {}!\n",
> + e
> + );
> +
> + return buf;
> + }
> + }
> +
> + 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 e88bc4b27d6e..44c107f20174 100644
> --- a/rust/kernel/lib.rs
> +++ b/rust/kernel/lib.rs
> @@ -80,6 +80,8 @@
> pub mod firmware;
> pub mod fmt;
> pub mod fs;
> +#[cfg(CONFIG_RUST_HID_ABSTRACTIONS)]
> +pub mod hid;
> pub mod init;
> pub mod io;
> pub mod ioctl;
> --
> 2.47.2
>
>
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: (subset) [PATCH v3 RESEND RESEND 0/3] Initial work for Rust abstraction for HID device driver development
2025-09-13 16:12 [PATCH v3 RESEND RESEND 0/3] Initial work for Rust abstraction for HID device driver development Rahul Rameshbabu
` (2 preceding siblings ...)
2025-09-13 16:13 ` [PATCH v3 RESEND RESEND 3/3] rust: hid: Glorious PC Gaming Race Model O and O- mice reference driver Rahul Rameshbabu
@ 2025-09-17 10:25 ` Benjamin Tissoires
2025-09-17 11:59 ` Rahul Rameshbabu
3 siblings, 1 reply; 9+ messages in thread
From: Benjamin Tissoires @ 2025-09-17 10:25 UTC (permalink / raw)
To: linux-input, linux-kernel, rust-for-linux, Rahul Rameshbabu
Cc: Jiri Kosina, a.hindborg, alex.gaynor, aliceryhl, bjorn3_gh,
boqun.feng, dakr, db48x, gary, ojeda, tmgross, peter.hutterer,
Benno Lossin, Benjamin Tissoires
On Sat, 13 Sep 2025 16:12:43 +0000, Rahul Rameshbabu wrote:
> I am doing another resend. Let me know if it makes sense to start sending out
> work I have on top of these changes. I wanted to wait till these changes got
> merged first but maybe that is not the right strategy?
>
> https://lore.kernel.org/rust-for-linux/20250721020211.196394-2-sergeantsagara@protonmail.com/
>
> I incorporated Danilo's and Miguel's feedback from my v2. Additionally, I
> noticed I had basic formatting issues when running scripts/checkpatch.pl.
> I made sure to check the generated rustdocs and that the Rust examples
> compile as part of the kunit infrastructure. I dropped the kref bindings
> as they are no longer needed for this series.
>
> [...]
Applied to hid/hid.git (for-6.18/core), thanks!
[1/3] HID: core: Change hid_driver to use a const char* for name
https://git.kernel.org/hid/hid/c/d1dd75c6500c
Cheers,
--
Benjamin Tissoires <bentiss@kernel.org>
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: (subset) [PATCH v3 RESEND RESEND 0/3] Initial work for Rust abstraction for HID device driver development
2025-09-17 10:25 ` (subset) [PATCH v3 RESEND RESEND 0/3] Initial work for Rust abstraction for HID device driver development Benjamin Tissoires
@ 2025-09-17 11:59 ` Rahul Rameshbabu
0 siblings, 0 replies; 9+ messages in thread
From: Rahul Rameshbabu @ 2025-09-17 11:59 UTC (permalink / raw)
To: Benjamin Tissoires
Cc: linux-input, linux-kernel, rust-for-linux, Jiri Kosina,
a.hindborg, alex.gaynor, aliceryhl, bjorn3_gh, boqun.feng, dakr,
db48x, gary, ojeda, tmgross, peter.hutterer, Benno Lossin
On Wed, 17 Sep, 2025 12:25:33 +0200 "Benjamin Tissoires" <bentiss@kernel.org> wrote:
> On Sat, 13 Sep 2025 16:12:43 +0000, Rahul Rameshbabu wrote:
>> I am doing another resend. Let me know if it makes sense to start sending out
>> work I have on top of these changes. I wanted to wait till these changes got
>> merged first but maybe that is not the right strategy?
>>
>> https://lore.kernel.org/rust-for-linux/20250721020211.196394-2-sergeantsagara@protonmail.com/
>>
>> I incorporated Danilo's and Miguel's feedback from my v2. Additionally, I
>> noticed I had basic formatting issues when running scripts/checkpatch.pl.
>> I made sure to check the generated rustdocs and that the Rust examples
>> compile as part of the kunit infrastructure. I dropped the kref bindings
>> as they are no longer needed for this series.
>>
>> [...]
>
> Applied to hid/hid.git (for-6.18/core), thanks!
>
> [1/3] HID: core: Change hid_driver to use a const char* for name
> https://git.kernel.org/hid/hid/c/d1dd75c6500c
>
Thanks!
It will be nice not having to carry around that small cleanup patch.
-- Rahul Rameshbabu
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v3 RESEND RESEND 2/3] rust: core abstractions for HID drivers
2025-09-17 10:04 ` Benjamin Tissoires
@ 2025-09-17 12:08 ` Rahul Rameshbabu
0 siblings, 0 replies; 9+ messages in thread
From: Rahul Rameshbabu @ 2025-09-17 12:08 UTC (permalink / raw)
To: Benjamin Tissoires
Cc: linux-input, linux-kernel, rust-for-linux, Jiri Kosina,
a.hindborg, alex.gaynor, aliceryhl, benno.lossin,
Benjamin Tissoires, bjorn3_gh, boqun.feng, dakr, db48x, gary,
ojeda, tmgross, peter.hutterer
On Wed, 17 Sep, 2025 12:04:30 +0200 "Benjamin Tissoires" <bentiss@kernel.org> wrote:
> On Sep 13 2025, Rahul Rameshbabu wrote:
>> 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:
>> Some points I did not address from the last review cycle:
>>
>> * I did not look into autogenerating all the getter functions for various
>> fields exported from the binded C structures.
>> - I would be interested in hearing opinions from folks actively involved
>> with Rust for Linux on this topic.
>>
>> Changelog:
>>
>> 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 | 9 +
>> drivers/hid/Kconfig | 8 +
>> rust/bindings/bindings_helper.h | 3 +
>> rust/kernel/hid.rs | 503 ++++++++++++++++++++++++++++++++
>> rust/kernel/lib.rs | 2 +
>> 5 files changed, 525 insertions(+)
>> create mode 100644 rust/kernel/hid.rs
>>
>> diff --git a/MAINTAINERS b/MAINTAINERS
>> index dd810da5261b..6c60765f2aaa 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -10686,6 +10686,15 @@ 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
>> +T: git git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid.git rust
>
> FWIW, we (HID maintainers) are still undecided on how to handle that,
> and so it's a little bit postponed for now
Sure, understandable. I am going to send v4 without updating the
MAINTAINERS file, so we are aligned that this is still up for
discussion. My only concern is that I have been holding off patches for
a more complex HID driver in Rust. I am wondering if it would be good to
send out those changes as a separate series that builds on top of this
one while this discussion occurs. Ideally, I do not want to end up in
the situation where I'll have to maintain too many patches out of tree
long term (6+ months). I think starting out of tree and documenting this
on the rust-for-linux project page might be viable. Maybe some input on
the rust-for-linux side would help.
>
>> +F: drivers/hid/*.rs
>
> Could you instead make it really independant by relying on
> drivers/hid/rust instead?
> We already have drivers/hid/bpf for HID-BPF related stuff, so it doesn't
> seem to be that much of an issue to have a separate rust dir.
>
> This should allow for a cleaner separation without tinkering in Makefile
> or Kconfig if the HID rust tree is handled separately.
Yeah, having the drivers live in drivers/hid/rust should not be a
problem. Let me send out a v4 to handle this as well as another minor
cleanup in the reference driver I implemented (using hex integer
constants instead of decimal).
>
> Cheers,
> Benjamin
>
>> +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 43859fc75747..922e76e18af2 100644
>> --- a/drivers/hid/Kconfig
>> +++ b/drivers/hid/Kconfig
>> @@ -744,6 +744,14 @@ config HID_MEGAWORLD_FF
>> Say Y here if you have a Mega World based game controller and want
>> to have force feedback support for it.
>>
>> +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.
>> +
>> config HID_REDRAGON
>> tristate "Redragon keyboards"
>> default !EXPERT
>> diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
>> index 8cbb660e2ec2..7145fb1cdff1 100644
>> --- a/rust/bindings/bindings_helper.h
>> +++ b/rust/bindings/bindings_helper.h
>> @@ -45,6 +45,7 @@
>> #include <linux/cpufreq.h>
>> #include <linux/cpumask.h>
>> #include <linux/cred.h>
>> +#include <linux/device.h>
>> #include <linux/device/faux.h>
>> #include <linux/dma-mapping.h>
>> #include <linux/errname.h>
>> @@ -52,6 +53,8 @@
>> #include <linux/file.h>
>> #include <linux/firmware.h>
>> #include <linux/fs.h>
>> +#include <linux/hid.h>
>> +#include "../../drivers/hid/hid-ids.h"
>> #include <linux/jiffies.h>
>> #include <linux/jump_label.h>
>> #include <linux/mdio.h>
>> diff --git a/rust/kernel/hid.rs b/rust/kernel/hid.rs
>> new file mode 100644
>> index 000000000000..a93804af8b78
>> --- /dev/null
>> +++ b/rust/kernel/hid.rs
>> @@ -0,0 +1,503 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +
>> +// Copyright (C) 2025 Rahul Rameshbabu <sergeantsagara@protonmail.com>
>> +
>> +//! Abstractions for the HID interface.
>> +//!
>> +//! C header: [`include/linux/hid.h`](srctree/include/linux/hid.h)
>> +
>> +use crate::{device, device_id::RawDeviceId, driver, error::*, prelude::*, types::Opaque};
>> +use core::{
>> + marker::PhantomData,
>> + ptr::{addr_of_mut, NonNull},
>> +};
>> +
>> +/// Indicates the item is static read-only.
>> +///
>> +/// 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
>> +pub const MAIN_ITEM_CONSTANT: u8 = bindings::HID_MAIN_ITEM_CONSTANT as u8;
>> +
>> +/// Indicates the item represents data from a physical control.
>> +///
>> +/// 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
>> +pub const MAIN_ITEM_VARIABLE: u8 = bindings::HID_MAIN_ITEM_VARIABLE as u8;
>> +
>> +/// Indicates the item should be treated as a relative change from the previous
>> +/// report.
>> +///
>> +/// 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
>> +pub const MAIN_ITEM_RELATIVE: u8 = bindings::HID_MAIN_ITEM_RELATIVE as u8;
>> +
>> +/// Indicates the item should wrap around when reaching the extreme high or
>> +/// extreme low values.
>> +///
>> +/// 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
>> +pub const MAIN_ITEM_WRAP: u8 = bindings::HID_MAIN_ITEM_WRAP as u8;
>> +
>> +/// Indicates the item should wrap around when reaching the extreme high or
>> +/// extreme low values.
>> +///
>> +/// 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
>> +pub const MAIN_ITEM_NONLINEAR: u8 = bindings::HID_MAIN_ITEM_NONLINEAR as u8;
>> +
>> +/// Indicates whether the control has a preferred state it will physically
>> +/// return to without user intervention.
>> +///
>> +/// 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
>> +pub const MAIN_ITEM_NO_PREFERRED: u8 = bindings::HID_MAIN_ITEM_NO_PREFERRED as u8;
>> +
>> +/// Indicates whether the control has a physical state where it will not send
>> +/// any reports.
>> +///
>> +/// 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
>> +pub const MAIN_ITEM_NULL_STATE: u8 = bindings::HID_MAIN_ITEM_NULL_STATE as u8;
>> +
>> +/// Indicates whether the control requires host system logic to change state.
>> +///
>> +/// 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
>> +pub const MAIN_ITEM_VOLATILE: u8 = bindings::HID_MAIN_ITEM_VOLATILE as u8;
>> +
>> +/// Indicates whether the item is fixed size or a variable buffer of bytes.
>> +///
>> +/// 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
>> +pub const MAIN_ITEM_BUFFERED_BYTE: u8 = bindings::HID_MAIN_ITEM_BUFFERED_BYTE as u8;
>> +
>> +/// 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`].
>> + const fn into(self) -> u16 {
>> + self as u16
>> + }
>> +}
>> +
>> +impl From<u16> for Group {
>> + /// [`u16`] values can be safely converted to [`Group`] variants.
>> + fn from(value: u16) -> Self {
>> + match value.into() {
>> + bindings::HID_GROUP_GENERIC => Group::Generic,
>> + bindings::HID_GROUP_MULTITOUCH => Group::Multitouch,
>> + bindings::HID_GROUP_SENSOR_HUB => Group::SensorHub,
>> + bindings::HID_GROUP_MULTITOUCH_WIN_8 => Group::MultitouchWin8,
>> + bindings::HID_GROUP_RMI => Group::RMI,
>> + bindings::HID_GROUP_WACOM => Group::Wacom,
>> + bindings::HID_GROUP_LOGITECH_DJ_DEVICE => Group::LogitechDJDevice,
>> + bindings::HID_GROUP_STEAM => Group::Steam,
>> + bindings::HID_GROUP_LOGITECH_27MHZ_DEVICE => Group::Logitech27MHzDevice,
>> + bindings::HID_GROUP_VIVALDI => Group::Vivaldi,
>> + _ => Group::Any,
>> + }
>> + }
>> +}
>> +
>> +/// 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> {
>> + 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) -> Group {
>> + // SAFETY: `self.as_raw` is a valid pointer to a `struct hid_device`
>> + unsafe { *self.as_raw() }.group.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::as_ref(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 {
>> + /// Equivalent to C's `HID_USB_DEVICE` macro.
>> + ///
>> + /// Create a new `hid::DeviceId` from a group, vendor ID, and device ID
>> + /// number.
>> + pub const fn new_usb(group: Group, vendor: u32, product: u32) -> Self {
>> + Self(bindings::hid_device_id {
>> + bus: 0x3, // BUS_USB
>> + group: group.into(),
>> + 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) -> Group {
>> + self.0.group.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;
>> +
>> + 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, 'b: 'a>(_hdev: &hid::Device<device::Core>, rdesc: &'b 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, 'b: 'a>(_hdev: &Device<device::Core>, _rdesc: &'b mut [u8]) -> &'a [u8] {
>> + build_error!(VTABLE_DEFAULT_ERROR)
>> + }
>> +}
>> +
>> +/// An adapter for the registration of HID drivers.
>> +pub struct Adapter<T: Driver>(T);
>> +
>> +// SAFETY: A call to `unregister` for a given instance of `RegType` is guaranteed to be valid if
>> +// a preceding call to `register` has been successful.
>> +unsafe impl<T: Driver + 'static> driver::RegistrationOps for Adapter<T> {
>> + type RegType = bindings::hid_driver;
>> +
>> + unsafe fn register(
>> + hdrv: &Opaque<Self::RegType>,
>> + 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 `RegType`
>> + to_result(unsafe {
>> + bindings::__hid_register_driver(hdrv.get(), module.0, name.as_char_ptr())
>> + })
>> + }
>> +
>> + unsafe fn unregister(hdrv: &Opaque<Self::RegType>) {
>> + // SAFETY: `hdrv` is guaranteed to be a valid `RegType`
>> + 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 = match unsafe { *size }.try_into() {
>> + Ok(len) => len,
>> + Err(e) => {
>> + dev_err!(
>> + hdev.as_ref(),
>> + "Cannot fix report description due to {}!\n",
>> + e
>> + );
>> +
>> + return buf;
>> + }
>> + };
>> +
>> + // 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);
>> +
>> + match rdesc_slice.len().try_into() {
>> + // 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()`.
>> + Ok(len) => unsafe { *size = len },
>> + Err(e) => {
>> + dev_err!(
>> + hdev.as_ref(),
>> + "Fixed report description will not be used due to {}!\n",
>> + e
>> + );
>> +
>> + return buf;
>> + }
>> + }
>> +
>> + 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 e88bc4b27d6e..44c107f20174 100644
>> --- a/rust/kernel/lib.rs
>> +++ b/rust/kernel/lib.rs
>> @@ -80,6 +80,8 @@
>> pub mod firmware;
>> pub mod fmt;
>> pub mod fs;
>> +#[cfg(CONFIG_RUST_HID_ABSTRACTIONS)]
>> +pub mod hid;
>> pub mod init;
>> pub mod io;
>> pub mod ioctl;
>> --
>> 2.47.2
>>
>>
^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2025-09-17 12:08 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-09-13 16:12 [PATCH v3 RESEND RESEND 0/3] Initial work for Rust abstraction for HID device driver development Rahul Rameshbabu
2025-09-13 16:12 ` [PATCH v3 RESEND RESEND 1/3] HID: core: Change hid_driver to use a const char* for name Rahul Rameshbabu
2025-09-17 9:51 ` Benjamin Tissoires
2025-09-13 16:13 ` [PATCH v3 RESEND RESEND 2/3] rust: core abstractions for HID drivers Rahul Rameshbabu
2025-09-17 10:04 ` Benjamin Tissoires
2025-09-17 12:08 ` Rahul Rameshbabu
2025-09-13 16:13 ` [PATCH v3 RESEND RESEND 3/3] rust: hid: Glorious PC Gaming Race Model O and O- mice reference driver Rahul Rameshbabu
2025-09-17 10:25 ` (subset) [PATCH v3 RESEND RESEND 0/3] Initial work for Rust abstraction for HID device driver development Benjamin Tissoires
2025-09-17 11:59 ` Rahul Rameshbabu
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).