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
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
>
>
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
>>
>>
© 2016 - 2026 Red Hat, Inc.