Skip to contentSkip to conversation

rust: i2c: Add basic I2C driver abstractions

PATCH In review Igor Korotin 3 participants 11 messages Nov 16, 2025 – Nov 22, 2025

Subsystems (3)
lore
Share & tools
lore
#0 lore raw

v8 cover rust: i2c: Add basic I2C driver abstractions

This patch series lays the groundwork for writing Linux I2C drivers in Rust by:

1. Core abstractions
Introduce i2c::I2cClient, i2c::I2cAdapter, i2c::Driver and built on the existing struct i2c_client, struct i2c_adapter and struct i2c_driver, with safe Rust wrappers around probe, transfer, and teardown logic.

2. Manual device creation
Provide an API to register an I2C device at runtime from Rust using I2cBoardInfo and I2cAdapter, including automatic cleanup when the driver unloads.

3. Sample I2C driver (legacy table, OF & ACPI)
Add rust_driver_i2c, a sample that:
- binds to an I2C client via:
- legacy I2C-ID table,
- Open Firmware (device-tree) compatible strings, or
- ACPI IDs.
4. Sample I2C client registration (OF & ACPI)
Add rust_i2c_client, a sample that:
- binds to a platform parent device using OF/ACPI table
- registers new I2C client bound to the parent platform device using i2c::Registration::new()
- destroyes the I2C client device on exit.

Together, these four patches:

- Establish the essential Rust traits and types for I2C drivers.
- Enable driver binding via legacy ID table, device-tree (OF), or ACPI
- Enable manual device creation at runtime.
- Ship a samples showing typical usage

Igor Korotin (4):
rust: i2c: add basic I2C device and driver abstractions
rust: i2c: add manual I2C device creation abstractions
samples: rust: add Rust I2C sample driver
samples: rust: add Rust I2C client registration sample

Changelog
---------
v8:
- Rebase commits on top of [1]
- Applied kernel vertical style [2]
- Replace core::mem::zeroed() with pin_init::zeroed()
- Rename I2cAdapter::get_nr to I2cAdapter::index
- fixed some minor format issues
- Link to v7: [3]

[1] https://git.kernel.org/pub/scm/linux/kernel/git/driver-core/driver-core.git/log/?h=driver-core-next
[2] https://docs.kernel.org/rust/coding-guidelines.html#imports
[3] https://lore.kernel.org/rust-for-linux/[email protected]/

v7:
- Rebase and update i2c::Driver::probe function based on patch series [1].
- use i2c::Driver::unbind instead of drop in i2c::Adapter::remove_callback
- Implement and use i2c::Driver::unbind for I2C SampleDriver instead of Drop.
- i2c::Registration::new requires a parent device to which a new I2C client will be bound.
- i2c::Registration::new returns impl PinInit<Devres<Self>, Error>
- The existing rust_driver_i2c sample has been split into two:
rust_driver_i2c, which demonstrates the implementation of an I2C driver, and rust_i2c_client, which demonstrates registering a new I2C client bound to a parent platform device.
- Link to v6: [2]

[1] https://lore.kernel.org/all/[email protected]/
[2] https://lore.kernel.org/rust-for-linux/[email protected]/

v6:
- Add implementation of unbind for i2c::Driver trait;
- Add argument Pin<&Self> to i2c::Driver::shutdown method;
- Adjust usage of i2c::Driver::shutdown in
i2c::Adapter::shutdown_callback in i2c::Driver trait code
example and in rust_driver_i2c code;
- Remove dummy AsRef implementation for I2cAdapter. Adjust code
in rust_driver_i2c;
- Add i2c::I2cAdapter::get_nr method that returns I2cAdapter index;
- Optimize unsafe sections in inc_ref/dec_ref in AlwaysRefCounted
for I2cAdapter implementation;
- Remove unnecessary Drop implementation for I2cAdapter, because
I2cAdapter is always a reference;
- Remove unnecessary type definition Ops<T> in rust_driver_i2c
- Simplify call of i2c::I2cAdapter::get in try_pin_init! macro
for rust_driver_i2c
- Link to v5: https://lore.kernel.org/rust-for-linux/[email protected]/
v5:
- Rename missed pdev variables to idev (thanks to Daniel).
- Import crate::device_id::RawDeviceIdIndex and
crate::types::AlwaysRefCounted in i2c.rs.
- Switch dev_dbg to dev_info in the sample I2C driver messages.
- Make I2cAdapter::get() return ARef<I2cAdapter> instead of
&I2cAdapter.
- Remove TryFrom<device::Device<Ctx>> for I2cAdapter<Ctx> (unused;
to be reintroduced in a later I2C series).
- Remove AsRef<device::Device<Ctx>> for I2cAdapter<Ctx> (unused;
to be reintroduced in a later I2C series).
- Add AsRef<I2cAdapter> for I2cAdapter<Ctx>.
- Use i2c_get/put_adapter instead of get/put_device for
AlwaysRefCounted<I2cAdapter>.
- Update safety comment for unsafe impl Sync for Registration {}.
- Tweak comment for I2cBoardInfo::new.
- Adjust build-time assertion message in Adapter::register.
- Link to v4: https://lore.kernel.org/rust-for-linux/[email protected]/
v4:
- Renamed i2c::I2cAdapterRef to i2c::I2cAdapter.
- Renamed i2c::Device to i2c::I2cClient for consistency with
i2c::I2cAdapter and to avoid confusion with i2c::Adapter
- Reworked i2c::I2cAdapter to be an Opaque around i2c_adapter struct
- Implemented AlwaysRefCounted trait for i2c::I2cAdapter.
- Fixed numerous comment mistakes and typos all over the code, thanks
to Danilo and Daniel
- Got rid of all unwrap() use-cases in i2c.rs and rust_driver_i2c.rs.
This covers 0-day kernel panic <[email protected]>
- Removed unnecessary casts.
- Replaced all addr_of_mut! macros to &raw mut.
- In i2c::Adapter::register method build assert if all ID tables are
None.
- Renamed all pdrv and pdev instances to idrv and idev respectivly
- Implemented an ealry return in i2c::Adapter::i2c_id_info
- Added all missing Safety comments.
- Removed unsafe impl<Ctx: device::DeviceContext> crate::types::AlwaysRefCounted for Device<Ctx> implementation which came to v3 from v2 by mistake.
- Added more details regarding i2c-stub driver usage in rust_driver_i2c
comment.
- Changed i2c::I2cAdapter::get return type from Option<Self> to
Result<&'static Self>.
- Added Daniel Almeida as a reviewer to the "I2C Subsystem [RUST]" entry
in MAINTAINERS, per his offer.
- Link to v3: https://lore.kernel.org/rust-for-linux/[email protected]/
v3:
- removed unnecessary i2c_get_clientdata and i2c_set_clientdata rust
helpers. Using generic accessors implemented in [1] instead.
- Reimplemented i2c::DeviceId based on changes in [2].
- Using from_result in i2c::Adapter::probe_callback
- Using explicit drop() for i2c client private data in
i2c::Adapter::remove_callback
- replaced device::Device::as_ref() with device::Device::from_raw in i2c::Device::as_ref(). It is renamed in device::Device.
- Build Rust I2C only if I2C is built-in
- Reimplement overcomplicated trait i2c::DeviceOwned the same way it is
implemented in auxiliary [3].
- Merge rust_device_i2c and rust_driver_i2c samples. Resulting
rust_driver_i2c creates pined i2c_client using i2c::Registration::new
and probes newly created i2c_client.
- Created a new entry in MAINTAINERS file containing i2c.rs and
rust_driver_i2c.rs in it.
- Link to v2: [4]

[1] https://lore.kernel.org/lkml/[email protected]/
[2] https://lore.kernel.org/rust-for-linux/[email protected]/
[3] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/rust/kernel/auxiliary.rs?h=v6.16-rc4#n299
[4] https://lore.kernel.org/rust-for-linux/[email protected]/

v2:
- Merged separated ACPI support patches since ACPI-table support is
merged into driver-core-next.
- Added I2cAdapterRef and I2cBoardInfo abstractions
- Added DeviceState generic parameter which is used for i2c::Device
as a sign if the device is created manually
- Added DeviceOwned abstraction which is a safe reference to a
manually created i2c::Device<Ctx, state::Owned>.
- Added Rust manual I2C device creation sample
- Link to v1: https://lore.kernel.org/rust-for-linux/[email protected]/

 MAINTAINERS                     |   9 +
 rust/bindings/bindings_helper.h |   1 +
 rust/kernel/i2c.rs              | 589 ++++++++++++++++++++++++++++++++
 rust/kernel/lib.rs              |   2 +
 samples/rust/Kconfig            |  23 ++
 samples/rust/Makefile           |   2 +
 samples/rust/rust_driver_i2c.rs |  74 ++++
 samples/rust/rust_i2c_client.rs | 157 +++++++++
 8 files changed, 857 insertions(+)
 create mode 100644 rust/kernel/i2c.rs
 create mode 100644 samples/rust/rust_driver_i2c.rs
 create mode 100644 samples/rust/rust_i2c_client.rs

base-commit: ededb7bcdfdbcfbb7af93e3a543165a9553e1683

Signature · 1 line
2.43.0
#1 lore raw

PATCH v8 1/4 rust: i2c: add basic I2C device and driver abstractions

Implement the core abstractions needed for I2C drivers, including:

* i2c::Driver — the trait drivers must implement, including probe

* i2c::I2cClient — a safe wrapper around struct i2c_client

* i2c::Adapter — implements driver::RegistrationOps to hook into the generic driver::Registration machinery

* i2c::DeviceId — a RawDeviceIdIndex implementation for I2C device IDs

---
 MAINTAINERS                     |   8 +
 rust/bindings/bindings_helper.h |   1 +
 rust/kernel/i2c.rs              | 436 ++++++++++++++++++++++++++++++++
 rust/kernel/lib.rs              |   2 +
 4 files changed, 447 insertions(+)
 create mode 100644 rust/kernel/i2c.rs
MAINTAINERS +8 -0
index 3da2c26a796b..a8dfde2f5633 100644
@@ -11743,6 +11743,14 @@ F:	include/linux/i2c.h
 F:	include/uapi/linux/i2c-*.h
 F:	include/uapi/linux/i2c.h
 
+I2C SUBSYSTEM [RUST]
+M:	Igor Korotin <[email protected]>
+R:	Danilo Krummrich <[email protected]>
+R:	Daniel Almeida <[email protected]>
+L:	[email protected]
+S:	Maintained
+F:	rust/kernel/i2c.rs
+
 I2C SUBSYSTEM HOST DRIVERS
 M:	Andi Shyti <[email protected]>
 L:	[email protected]
rust/bindings/bindings_helper.h +1 -0
index a79fd111f886..f92abb578b56 100644
@@ -58,6 +58,7 @@
 #include <linux/firmware.h>
 #include <linux/interrupt.h>
 #include <linux/fs.h>
+#include <linux/i2c.h>
 #include <linux/ioport.h>
 #include <linux/jiffies.h>
 #include <linux/jump_label.h>
rust/kernel/i2c.rs new file +436 -0

436 lines not on this page · raw message

rust/kernel/lib.rs +2 -0
index 3dd7bebe7888..8c0070a8029e 100644
@@ -94,6 +94,8 @@
 pub mod firmware;
 pub mod fmt;
 pub mod fs;
+#[cfg(CONFIG_I2C = "y")]
+pub mod i2c;
 pub mod id_pool;
 pub mod init;
 pub mod io;
Signature · 1 line
2.43.0
#2 lore raw

Re: [PATCH v8 1/4] rust: i2c: add basic I2C device and driver abstractions

Quoted 94 lines · Igor Korotin

Implement the core abstractions needed for I2C drivers, including:

* i2c::Driver — the trait drivers must implement, including probe

* i2c::I2cClient — a safe wrapper around struct i2c_client

* i2c::Adapter — implements driver::RegistrationOps to hook into the generic driver::Registration machinery

* i2c::DeviceId — a RawDeviceIdIndex implementation for I2C device IDs

---
 MAINTAINERS                     |   8 +
 rust/bindings/bindings_helper.h |   1 +
 rust/kernel/i2c.rs              | 436 ++++++++++++++++++++++++++++++++
 rust/kernel/lib.rs              |   2 +
 4 files changed, 447 insertions(+)
 create mode 100644 rust/kernel/i2c.rs
MAINTAINERS +8 -0
index 3da2c26a796b..a8dfde2f5633 100644
@@ -11743,6 +11743,14 @@ F:	include/linux/i2c.h
 F:	include/uapi/linux/i2c-*.h
 F:	include/uapi/linux/i2c.h
 
+I2C SUBSYSTEM [RUST]
+M:	Igor Korotin <[email protected]>
+R:	Danilo Krummrich <[email protected]>
+R:	Daniel Almeida <[email protected]>
+L:	[email protected]
+S:	Maintained
+F:	rust/kernel/i2c.rs
+
 I2C SUBSYSTEM HOST DRIVERS
 M:	Andi Shyti <[email protected]>
 L:	[email protected]
rust/bindings/bindings_helper.h +1 -0
index a79fd111f886..f92abb578b56 100644
@@ -58,6 +58,7 @@
 #include <linux/firmware.h>
 #include <linux/interrupt.h>
 #include <linux/fs.h>
+#include <linux/i2c.h>
 #include <linux/ioport.h>
 #include <linux/jiffies.h>
 #include <linux/jump_label.h>
rust/kernel/i2c.rs new file +37 -0
new file mode 100644
index 000000000000..a5ad8213d9ac
@@ -0,0 +1,436 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! I2C Driver subsystem
+
+// I2C Driver abstractions.
+use crate::{
+    acpi,
+    container_of,
+    device,
+    device_id::{
+        RawDeviceId,
+        RawDeviceIdIndex, //
+    },
+    driver,
+    error::*,
+    of,
+    prelude::*,
+    types::{
+        AlwaysRefCounted,
+        Opaque, //
+    }, //
+};
+
+use core::{
+    marker::PhantomData,
+    ptr::NonNull, //
+};
+
+/// An I2C device id table.
+#[repr(transparent)]
+#[derive(Clone, Copy)]
+pub struct DeviceId(bindings::i2c_device_id);
+
+impl DeviceId {
+    const I2C_NAME_SIZE: usize = 20;
+
+    /// Create a new device id from an I2C 'id' string.

rust/kernel/i2c.rs line 47

Quoted 10 lines
rust/kernel/i2c.rs new file +10 -0
+    #[inline(always)]
+    pub const fn new(id: &'static CStr) -> Self {
+        build_assert!(
+            id.len_with_nul() <= Self::I2C_NAME_SIZE,
+            "ID exceeds 20 bytes"
+        );
+        let src = id.as_bytes_with_nul();
+        // Replace with `bindings::i2c_device_id::default()` once stabilized for `const`.
+        // SAFETY: FFI type is valid to be zero-initialized.
+        let mut i2c: bindings::i2c_device_id = pin_init::zeroed();

Clippy throws the error:

error: statement has unnecessary safety comment
--> rust/kernel/i2c.rs:54:9

   |
54 |         let mut i2c: bindings::i2c_device_id = pin_init::zeroed();
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
help: consider removing the safety comment
  --> rust/kernel/i2c.rs:53:9
   |
53 |         // SAFETY: FFI type is valid to be zero-initialized.
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

= help: for further information visit
https://rust-lang.github.io/rust-clippy/rust-1.91.0/index.html#unnecessary_safety_comment
= note: -D clippy::unnecessary-safety-comment implied by `-D
warnings`
= help: to override -D warnings add
#[allow(clippy::unnecessary_safety_comment)]

Thanks
- Markus Probst

Quoted 402 lines
rust/kernel/i2c.rs new file +389 -0
+        let mut i = 0;
+        while i < src.len() {
+            i2c.name[i] = src[i];
+            i += 1;
+        }
+
+        Self(i2c)
+    }
+}
+
+// SAFETY: `DeviceId` is a `#[repr(transparent)]` wrapper of `i2c_device_id` and does not add
+// additional invariants, so it's safe to transmute to `RawType`.
+unsafe impl RawDeviceId for DeviceId {
+    type RawType = bindings::i2c_device_id;
+}
+
+// SAFETY: `DRIVER_DATA_OFFSET` is the offset to the `driver_data` field.
+unsafe impl RawDeviceIdIndex for DeviceId {
+    const DRIVER_DATA_OFFSET: usize = core::mem::offset_of!(bindings::i2c_device_id, driver_data);
+
+    fn index(&self) -> usize {
+        self.0.driver_data
+    }
+}
+
+/// IdTable type for I2C
+pub type IdTable<T> = &'static dyn kernel::device_id::IdTable<DeviceId, T>;
+
+/// Create a I2C `IdTable` with its alias for modpost.
+#[macro_export]
+macro_rules! i2c_device_table {
+    ($table_name:ident, $module_table_name:ident, $id_info_type: ty, $table_data: expr) => {
+        const $table_name: $crate::device_id::IdArray<
+            $crate::i2c::DeviceId,
+            $id_info_type,
+            { $table_data.len() },
+        > = $crate::device_id::IdArray::new($table_data);
+
+        $crate::module_device_table!("i2c", $module_table_name, $table_name);
+    };
+}
+
+/// An adapter for the registration of I2C 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::i2c_driver;
+
+    unsafe fn register(
+        idrv: &Opaque<Self::RegType>,
+        name: &'static CStr,
+        module: &'static ThisModule,
+    ) -> Result {
+        build_assert!(
+            T::ACPI_ID_TABLE.is_some() || T::OF_ID_TABLE.is_some() || T::I2C_ID_TABLE.is_some(),
+            "At least one of ACPI/OF/Legacy tables must be present when registering an i2c driver"
+        );
+
+        let i2c_table = match T::I2C_ID_TABLE {
+            Some(table) => table.as_ptr(),
+            None => core::ptr::null(),
+        };
+
+        let of_table = match T::OF_ID_TABLE {
+            Some(table) => table.as_ptr(),
+            None => core::ptr::null(),
+        };
+
+        let acpi_table = match T::ACPI_ID_TABLE {
+            Some(table) => table.as_ptr(),
+            None => core::ptr::null(),
+        };
+
+        // SAFETY: It's safe to set the fields of `struct i2c_client` on initialization.
+        unsafe {
+            (*idrv.get()).driver.name = name.as_char_ptr();
+            (*idrv.get()).probe = Some(Self::probe_callback);
+            (*idrv.get()).remove = Some(Self::remove_callback);
+            (*idrv.get()).shutdown = Some(Self::shutdown_callback);
+            (*idrv.get()).id_table = i2c_table;
+            (*idrv.get()).driver.of_match_table = of_table;
+            (*idrv.get()).driver.acpi_match_table = acpi_table;
+        }
+
+        // SAFETY: `idrv` is guaranteed to be a valid `RegType`.
+        to_result(unsafe { bindings::i2c_register_driver(module.0, idrv.get()) })
+    }
+
+    unsafe fn unregister(idrv: &Opaque<Self::RegType>) {
+        // SAFETY: `idrv` is guaranteed to be a valid `RegType`.
+        unsafe { bindings::i2c_del_driver(idrv.get()) }
+    }
+}
+
+impl<T: Driver + 'static> Adapter<T> {
+    extern "C" fn probe_callback(idev: *mut bindings::i2c_client) -> kernel::ffi::c_int {
+        // SAFETY: The I2C bus only ever calls the probe callback with a valid pointer to a
+        // `struct i2c_client`.
+        //
+        // INVARIANT: `idev` is valid for the duration of `probe_callback()`.
+        let idev = unsafe { &*idev.cast::<I2cClient<device::CoreInternal>>() };
+
+        let info =
+            Self::i2c_id_info(idev).or_else(|| <Self as driver::Adapter>::id_info(idev.as_ref()));
+
+        from_result(|| {
+            let data = T::probe(idev, info);
+
+            idev.as_ref().set_drvdata(data)?;
+            Ok(0)
+        })
+    }
+
+    extern "C" fn remove_callback(idev: *mut bindings::i2c_client) {
+        // SAFETY: `idev` is a valid pointer to a `struct i2c_client`.
+        let idev = unsafe { &*idev.cast::<I2cClient<device::CoreInternal>>() };
+
+        // SAFETY: `remove_callback` is only ever called after a successful call to
+        // `probe_callback`, hence it's guaranteed that `I2cClient::set_drvdata()` has been called
+        // and stored a `Pin<KBox<T>>`.
+        let data = unsafe { idev.as_ref().drvdata_obtain::<T>() };
+
+        T::unbind(idev, data.as_ref());
+    }
+
+    extern "C" fn shutdown_callback(idev: *mut bindings::i2c_client) {
+        // SAFETY: `shutdown_callback` is only ever called for a valid `idev`
+        let idev = unsafe { &*idev.cast::<I2cClient<device::CoreInternal>>() };
+
+        // SAFETY: `shutdown_callback` is only ever called after a successful call to
+        // `probe_callback`, hence it's guaranteed that `Device::set_drvdata()` has been called
+        // and stored a `Pin<KBox<T>>`.
+        let data = unsafe { idev.as_ref().drvdata_obtain::<T>() };
+
+        T::shutdown(idev, data.as_ref());
+    }
+
+    /// The [`i2c::IdTable`] of the corresponding driver.
+    fn i2c_id_table() -> Option<IdTable<<Self as driver::Adapter>::IdInfo>> {
+        T::I2C_ID_TABLE
+    }
+
+    /// Returns the driver's private data from the matching entry in the [`i2c::IdTable`], if any.
+    ///
+    /// If this returns `None`, it means there is no match with an entry in the [`i2c::IdTable`].
+    fn i2c_id_info(dev: &I2cClient) -> Option<&'static <Self as driver::Adapter>::IdInfo> {
+        let table = Self::i2c_id_table()?;
+
+        // SAFETY:
+        // - `table` has static lifetime, hence it's valid for reads
+        // - `dev` is guaranteed to be valid while it's alive, and so is `dev.as_raw()`.
+        let raw_id = unsafe { bindings::i2c_match_id(table.as_ptr(), dev.as_raw()) };
+
+        if raw_id.is_null() {
+            return None;
+        }
+
+        // SAFETY: `DeviceId` is a `#[repr(transparent)` wrapper of `struct i2c_device_id` and
+        // does not add additional invariants, so it's safe to transmute.
+        let id = unsafe { &*raw_id.cast::<DeviceId>() };
+
+        Some(table.info(<DeviceId as RawDeviceIdIndex>::index(id)))
+    }
+}
+
+impl<T: Driver + 'static> driver::Adapter for Adapter<T> {
+    type IdInfo = T::IdInfo;
+
+    fn of_id_table() -> Option<of::IdTable<Self::IdInfo>> {
+        T::OF_ID_TABLE
+    }
+
+    fn acpi_id_table() -> Option<acpi::IdTable<Self::IdInfo>> {
+        T::ACPI_ID_TABLE
+    }
+}
+
+/// Declares a kernel module that exposes a single i2c driver.
+///
+/// # Examples
+///
+/// ```ignore
+/// kernel::module_i2c_driver! {
+///     type: MyDriver,
+///     name: "Module name",
+///     authors: ["Author name"],
+///     description: "Description",
+///     license: "GPL v2",
+/// }
+/// ```
+#[macro_export]
+macro_rules! module_i2c_driver {
+    ($($f:tt)*) => {
+        $crate::module_driver!(<T>, $crate::i2c::Adapter<T>, { $($f)* });
+    };
+}
+
+/// The i2c driver trait.
+///
+/// Drivers must implement this trait in order to get a i2c driver registered.
+///
+/// # Example
+///
+///```
+/// # use kernel::{acpi, bindings, c_str, device::Core, i2c, of};
+///
+/// struct MyDriver;
+///
+/// kernel::acpi_device_table!(
+///     ACPI_TABLE,
+///     MODULE_ACPI_TABLE,
+///     <MyDriver as i2c::Driver>::IdInfo,
+///     [
+///         (acpi::DeviceId::new(c_str!("LNUXBEEF")), ())
+///     ]
+/// );
+///
+/// kernel::i2c_device_table!(
+///     I2C_TABLE,
+///     MODULE_I2C_TABLE,
+///     <MyDriver as i2c::Driver>::IdInfo,
+///     [
+///          (i2c::DeviceId::new(c_str!("rust_driver_i2c")), ())
+///     ]
+/// );
+///
+/// kernel::of_device_table!(
+///     OF_TABLE,
+///     MODULE_OF_TABLE,
+///     <MyDriver as i2c::Driver>::IdInfo,
+///     [
+///         (of::DeviceId::new(c_str!("test,device")), ())
+///     ]
+/// );
+///
+/// impl i2c::Driver for MyDriver {
+///     type IdInfo = ();
+///     const I2C_ID_TABLE: Option<i2c::IdTable<Self::IdInfo>> = Some(&I2C_TABLE);
+///     const OF_ID_TABLE: Option<of::IdTable<Self::IdInfo>> = Some(&OF_TABLE);
+///     const ACPI_ID_TABLE: Option<acpi::IdTable<Self::IdInfo>> = Some(&ACPI_TABLE);
+///
+///     fn probe(
+///         _idev: &i2c::I2cClient<Core>,
+///         _id_info: Option<&Self::IdInfo>,
+///     ) -> impl PinInit<Self, Error> {
+///         Err(ENODEV)
+///     }
+///
+///     fn shutdown(_idev: &i2c::I2cClient<Core>, this: Pin<&Self>) {
+///     }
+/// }
+///```
+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 I2C_ID_TABLE: Option<IdTable<Self::IdInfo>> = None;
+
+    /// The table of OF device ids supported by the driver.
+    const OF_ID_TABLE: Option<of::IdTable<Self::IdInfo>> = None;
+
+    /// The table of ACPI device ids supported by the driver.
+    const ACPI_ID_TABLE: Option<acpi::IdTable<Self::IdInfo>> = None;
+
+    /// I2C driver probe.
+    ///
+    /// Called when a new i2c client is added or discovered.
+    /// Implementers should attempt to initialize the client here.
+    fn probe(
+        dev: &I2cClient<device::Core>,
+        id_info: Option<&Self::IdInfo>,
+    ) -> impl PinInit<Self, Error>;
+
+    /// I2C driver shutdown.
+    ///
+    /// Called by the kernel during system reboot or power-off to allow the [`Driver`] to bring the
+    /// [`Device`] into a safe state. Implementing this callback is optional.
+    ///
+    /// Typical actions include stopping transfers, disabling interrupts, or resetting the hardware
+    /// to prevent undesired behavior during shutdown.
+    ///
+    /// This callback is distinct from final resource cleanup, as the driver instance remains valid
+    /// after it returns. Any deallocation or teardown of driver-owned resources should instead be
+    /// handled in `Self::drop`.
+    fn shutdown(dev: &I2cClient<device::Core>, this: Pin<&Self>) {
+        let _ = (dev, this);
+    }
+
+    /// I2C driver unbind.
+    ///
+    /// Called when a [`Device`] is unbound from its bound [`Driver`]. Implementing this callback
+    /// is optional.
+    ///
+    /// This callback serves as a place for drivers to perform teardown operations that require a
+    /// `&Device<Core>` or `&Device<Bound>` reference. For instance, drivers may try to perform I/O
+    /// operations to gracefully tear down the device.
+    ///
+    /// Otherwise, release operations for driver resources should be performed in `Self::drop`.
+    fn unbind(dev: &I2cClient<device::Core>, this: Pin<&Self>) {
+        let _ = (dev, this);
+    }
+}
+
+/// The i2c client representation.
+///
+/// This structure represents the Rust abstraction for a C `struct i2c_client`. The
+/// implementation abstracts the usage of an existing C `struct i2c_client` that
+/// gets passed from the C side
+///
+/// # Invariants
+///
+/// A [`I2cClient`] instance represents a valid `struct i2c_client` created by the C portion of
+/// the kernel.
+#[repr(transparent)]
+pub struct I2cClient<Ctx: device::DeviceContext = device::Normal>(
+    Opaque<bindings::i2c_client>,
+    PhantomData<Ctx>,
+);
+
+impl<Ctx: device::DeviceContext> I2cClient<Ctx> {
+    fn as_raw(&self) -> *mut bindings::i2c_client {
+        self.0.get()
+    }
+}
+
+// SAFETY: `I2cClient` is a transparent wrapper of a type that doesn't depend on
+// `I2cClient`'s generic argument.
+kernel::impl_device_context_deref!(unsafe { I2cClient });
+kernel::impl_device_context_into_aref!(I2cClient);
+
+// SAFETY: Instances of `I2cClient` are always reference-counted.
+unsafe impl AlwaysRefCounted for I2cClient {
+    fn inc_ref(&self) {
+        // SAFETY: The existence of a shared reference guarantees that the refcount is non-zero.
+        unsafe { bindings::get_device(self.as_ref().as_raw()) };
+    }
+
+    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.as_ref().as_raw()).dev) }
+    }
+}
+
+impl<Ctx: device::DeviceContext> AsRef<device::Device<Ctx>> for I2cClient<Ctx> {
+    fn as_ref(&self) -> &device::Device<Ctx> {
+        let raw = self.as_raw();
+        // SAFETY: By the type invariant of `Self`, `self.as_raw()` is a pointer to a valid
+        // `struct i2c_client`.
+        let dev = unsafe { &raw mut (*raw).dev };
+
+        // SAFETY: `dev` points to a valid `struct device`.
+        unsafe { device::Device::from_raw(dev) }
+    }
+}
+
+impl<Ctx: device::DeviceContext> TryFrom<&device::Device<Ctx>> for &I2cClient<Ctx> {
+    type Error = kernel::error::Error;
+
+    fn try_from(dev: &device::Device<Ctx>) -> Result<Self, Self::Error> {
+        // SAFETY: By the type invariant of `Device`, `dev.as_raw()` is a valid pointer to a
+        // `struct device`.
+        if unsafe { bindings::i2c_verify_client(dev.as_raw()).is_null() } {
+            return Err(EINVAL);
+        }
+
+        // SAFETY: We've just verified that the type of `dev` equals to
+        // `bindings::i2c_client_type`, hence `dev` must be embedded in a valid
+        // `struct i2c_client` as guaranteed by the corresponding C code.
+        let idev = unsafe { container_of!(dev.as_raw(), bindings::i2c_client, dev) };
+
+        // SAFETY: `idev` is a valid pointer to a `struct i2c_client`.
+        Ok(unsafe { &*idev.cast() })
+    }
+}
+
+// SAFETY: A `I2cClient` is always reference-counted and can be released from any thread.
+unsafe impl Send for I2cClient {}
+
+// SAFETY: `I2cClient` can be shared among threads because all methods of `I2cClient`
+// (i.e. `I2cClient<Normal>) are thread safe.
+unsafe impl Sync for I2cClient {}
rust/kernel/lib.rs +2 -0
index 3dd7bebe7888..8c0070a8029e 100644
@@ -94,6 +94,8 @@
 pub mod firmware;
 pub mod fmt;
 pub mod fs;
+#[cfg(CONFIG_I2C = "y")]
+pub mod i2c;
 pub mod id_pool;
 pub mod init;
 pub mod io;
#3 lore raw

PATCH v8 2/4 rust: i2c: add manual I2C device creation abstractions

In reply to Igor Korotin

In addition to the basic I2C device support, add rust abstractions upon i2c_new_client_device/i2c_unregister_device C functions.

Implement the core abstractions needed for manual creation/deletion of I2C devices, including:

* i2c::Registration — a NonNull pointer created by the function

                          `i2c_new_client_device`

* i2c::I2cAdapter — a ref counted wrapper around struct i2c_adapter

* i2c::I2cBoardInfo — a safe wrapper around struct i2c_board_info

---
 rust/kernel/i2c.rs | 155 ++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 154 insertions(+), 1 deletion(-)
rust/kernel/i2c.rs +154 -1
index a5ad8213d9ac..3695b4adf436 100644
@@ -11,6 +11,7 @@
         RawDeviceId,
         RawDeviceIdIndex, //
     },
+    devres::Devres,
     driver,
     error::*,
     of,
@@ -23,9 +24,14 @@
 
 use core::{
     marker::PhantomData,
-    ptr::NonNull, //
+    ptr::{
+        from_ref,
+        NonNull, //
+    }, //
 };
 
+use kernel::types::ARef;
+
 /// An I2C device id table.
 #[repr(transparent)]
 #[derive(Clone, Copy)]
@@ -356,6 +362,102 @@ fn unbind(dev: &I2cClient<device::Core>, this: Pin<&Self>) {
     }
 }
 
+/// The i2c adapter representation.
+///
+/// This structure represents the Rust abstraction for a C `struct i2c_adapter`. The
+/// implementation abstracts the usage of an existing C `struct i2c_adapter` that
+/// gets passed from the C side
+///
+/// # Invariants
+///
+/// A [`I2cAdapter`] instance represents a valid `struct i2c_adapter` created by the C portion of
+/// the kernel.
+#[repr(transparent)]
+pub struct I2cAdapter<Ctx: device::DeviceContext = device::Normal>(
+    Opaque<bindings::i2c_adapter>,
+    PhantomData<Ctx>,
+);
+
+impl<Ctx: device::DeviceContext> I2cAdapter<Ctx> {
+    fn as_raw(&self) -> *mut bindings::i2c_adapter {
+        self.0.get()
+    }
+}
+
+impl I2cAdapter {
+    /// Returns the I2C Adapter index.
+    #[inline]
+    pub fn index(&self) -> i32 {
+        // SAFETY: `self.as_raw` is a valid pointer to a `struct i2c_adapter`.
+        unsafe { (*self.as_raw()).nr }
+    }
+
+    /// Gets pointer to an `i2c_adapter` by index.
+    pub fn get(index: i32) -> Result<ARef<Self>> {
+        // SAFETY: `index` must refer to a valid I2C adapter; the kernel
+        // guarantees that `i2c_get_adapter(index)` returns either a valid
+        // pointer or NULL. `NonNull::new` guarantees the correct check.
+        let adapter = NonNull::new(unsafe { bindings::i2c_get_adapter(index) }).ok_or(ENODEV)?;
+
+        // SAFETY: `adapter` is non-null and points to a live `i2c_adapter`.
+        // `I2cAdapter` is #[repr(transparent)], so this cast is valid.
+        Ok(unsafe { (&*adapter.as_ptr().cast::<I2cAdapter<device::Normal>>()).into() })
+    }
+}
+
+// SAFETY: `I2cAdapter` is a transparent wrapper of a type that doesn't depend on
+// `I2cAdapter`'s generic argument.
+kernel::impl_device_context_deref!(unsafe { I2cAdapter });
+kernel::impl_device_context_into_aref!(I2cAdapter);
+
+// SAFETY: Instances of `I2cAdapter` are always reference-counted.
+unsafe impl crate::types::AlwaysRefCounted for I2cAdapter {
+    fn inc_ref(&self) {
+        // SAFETY: The existence of a shared reference guarantees that the refcount is non-zero.
+        unsafe { bindings::i2c_get_adapter(self.index()) };
+    }
+
+    unsafe fn dec_ref(obj: NonNull<Self>) {
+        // SAFETY: The safety requirements guarantee that the refcount is non-zero.
+        unsafe { bindings::i2c_put_adapter(obj.as_ref().as_raw()) }
+    }
+}
+
+/// The i2c board info representation
+///
+/// This structure represents the Rust abstraction for a C `struct i2c_board_info` structure,
+/// which is used for manual I2C client creation.
+#[repr(transparent)]
+pub struct I2cBoardInfo(bindings::i2c_board_info);
+
+impl I2cBoardInfo {
+    const I2C_TYPE_SIZE: usize = 20;
+    /// Create a new [`I2cBoardInfo`] for a kernel driver.
+    #[inline(always)]
+    pub const fn new(type_: &'static CStr, addr: u16) -> Self {
+        build_assert!(
+            type_.len_with_nul() <= Self::I2C_TYPE_SIZE,
+            "Type exceeds 20 bytes"
+        );
+        let src = type_.as_bytes_with_nul();
+        // Replace with `bindings::i2c_board_info::default()` once stabilized for `const`.
+        // SAFETY: FFI type is valid to be zero-initialized.
+        let mut i2c_board_info: bindings::i2c_board_info = pin_init::zeroed();
+        let mut i: usize = 0;
+        while i < src.len() {
+            i2c_board_info.type_[i] = src[i];
+            i += 1;
+        }
+
+        i2c_board_info.addr = addr;
+        Self(i2c_board_info)
+    }
+
+    fn as_raw(&self) -> *const bindings::i2c_board_info {
+        from_ref(&self.0)
+    }
+}
+
 /// The i2c client representation.
 ///
 /// This structure represents the Rust abstraction for a C `struct i2c_client`. The
@@ -434,3 +536,54 @@ unsafe impl Send for I2cClient {}
 // SAFETY: `I2cClient` can be shared among threads because all methods of `I2cClient`
 // (i.e. `I2cClient<Normal>) are thread safe.
 unsafe impl Sync for I2cClient {}
+
+/// The registration of an i2c client device.
+///
+/// This type represents the registration of a [`struct i2c_client`]. When an instance of this
+/// type is dropped, its respective i2c client device will be unregistered from the system.
+///
+/// # Invariants
+///
+/// `self.0` always holds a valid pointer to an initialized and registered
+/// [`struct i2c_client`].
+#[repr(transparent)]
+pub struct Registration(NonNull<bindings::i2c_client>);
+
+impl Registration {
+    /// The C `i2c_new_client_device` function wrapper for manual I2C client creation.
+    pub fn new<'a>(
+        i2c_adapter: &I2cAdapter,
+        i2c_board_info: &I2cBoardInfo,
+        parent_dev: &'a device::Device<device::Bound>,
+    ) -> impl PinInit<Devres<Self>, Error> + 'a {
+        Devres::new(parent_dev, Self::try_new(i2c_adapter, i2c_board_info))
+    }
+
+    fn try_new(i2c_adapter: &I2cAdapter, i2c_board_info: &I2cBoardInfo) -> Result<Self> {
+        // SAFETY: the kernel guarantees that `i2c_new_client_device()` returns either a valid
+        // pointer or NULL. `from_err_ptr` separates errors. Following `NonNull::new`
+        // checks for NULL.
+        let raw_dev = from_err_ptr(unsafe {
+            bindings::i2c_new_client_device(i2c_adapter.as_raw(), i2c_board_info.as_raw())
+        })?;
+
+        let dev_ptr = NonNull::new(raw_dev).ok_or(ENODEV)?;
+
+        Ok(Self(dev_ptr))
+    }
+}
+
+impl Drop for Registration {
+    fn drop(&mut self) {
+        // SAFETY: `Drop` is only called for a valid `Registration`, which by invariant
+        // always contains a non-null pointer to an `i2c_client`.
+        unsafe { bindings::i2c_unregister_device(self.0.as_ptr()) }
+    }
+}
+
+// SAFETY: A `Registration` of a `struct i2c_client` can be released from any thread.
+unsafe impl Send for Registration {}
+
+// SAFETY: `Registration` offers no interior mutability (no mutation through &self
+// and no mutable access is exposed)
+unsafe impl Sync for Registration {}
Signature · 1 line
2.43.0
#4 lore raw

Re: [PATCH v8 2/4] rust: i2c: add manual I2C device creation abstractions

Quoted 121 lines · Igor Korotin

In addition to the basic I2C device support, add rust abstractions upon i2c_new_client_device/i2c_unregister_device C functions.

Implement the core abstractions needed for manual creation/deletion of I2C devices, including:

* i2c::Registration — a NonNull pointer created by the function

                          `i2c_new_client_device`

* i2c::I2cAdapter — a ref counted wrapper around struct i2c_adapter

* i2c::I2cBoardInfo — a safe wrapper around struct i2c_board_info

---
 rust/kernel/i2c.rs | 155 ++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 154 insertions(+), 1 deletion(-)
rust/kernel/i2c.rs +78 -1
index a5ad8213d9ac..3695b4adf436 100644
@@ -11,6 +11,7 @@
         RawDeviceId,
         RawDeviceIdIndex, //
     },
+    devres::Devres,
     driver,
     error::*,
     of,
@@ -23,9 +24,14 @@
 
 use core::{
     marker::PhantomData,
-    ptr::NonNull, //
+    ptr::{
+        from_ref,
+        NonNull, //
+    }, //
 };
 
+use kernel::types::ARef;
+
 /// An I2C device id table.
 #[repr(transparent)]
 #[derive(Clone, Copy)]
@@ -356,6 +362,102 @@ fn unbind(dev: &I2cClient<device::Core>, this: Pin<&Self>) {
     }
 }
 
+/// The i2c adapter representation.
+///
+/// This structure represents the Rust abstraction for a C `struct i2c_adapter`. The
+/// implementation abstracts the usage of an existing C `struct i2c_adapter` that
+/// gets passed from the C side
+///
+/// # Invariants
+///
+/// A [`I2cAdapter`] instance represents a valid `struct i2c_adapter` created by the C portion of
+/// the kernel.
+#[repr(transparent)]
+pub struct I2cAdapter<Ctx: device::DeviceContext = device::Normal>(
+    Opaque<bindings::i2c_adapter>,
+    PhantomData<Ctx>,
+);
+
+impl<Ctx: device::DeviceContext> I2cAdapter<Ctx> {
+    fn as_raw(&self) -> *mut bindings::i2c_adapter {
+        self.0.get()
+    }
+}
+
+impl I2cAdapter {
+    /// Returns the I2C Adapter index.
+    #[inline]
+    pub fn index(&self) -> i32 {
+        // SAFETY: `self.as_raw` is a valid pointer to a `struct i2c_adapter`.
+        unsafe { (*self.as_raw()).nr }
+    }
+
+    /// Gets pointer to an `i2c_adapter` by index.
+    pub fn get(index: i32) -> Result<ARef<Self>> {
+        // SAFETY: `index` must refer to a valid I2C adapter; the kernel
+        // guarantees that `i2c_get_adapter(index)` returns either a valid
+        // pointer or NULL. `NonNull::new` guarantees the correct check.
+        let adapter = NonNull::new(unsafe { bindings::i2c_get_adapter(index) }).ok_or(ENODEV)?;
+
+        // SAFETY: `adapter` is non-null and points to a live `i2c_adapter`.
+        // `I2cAdapter` is #[repr(transparent)], so this cast is valid.
+        Ok(unsafe { (&*adapter.as_ptr().cast::<I2cAdapter<device::Normal>>()).into() })
+    }
+}
+
+// SAFETY: `I2cAdapter` is a transparent wrapper of a type that doesn't depend on
+// `I2cAdapter`'s generic argument.
+kernel::impl_device_context_deref!(unsafe { I2cAdapter });
+kernel::impl_device_context_into_aref!(I2cAdapter);
+
+// SAFETY: Instances of `I2cAdapter` are always reference-counted.
+unsafe impl crate::types::AlwaysRefCounted for I2cAdapter {
+    fn inc_ref(&self) {
+        // SAFETY: The existence of a shared reference guarantees that the refcount is non-zero.
+        unsafe { bindings::i2c_get_adapter(self.index()) };
+    }
+
+    unsafe fn dec_ref(obj: NonNull<Self>) {
+        // SAFETY: The safety requirements guarantee that the refcount is non-zero.
+        unsafe { bindings::i2c_put_adapter(obj.as_ref().as_raw()) }
+    }
+}
+
+/// The i2c board info representation
+///
+/// This structure represents the Rust abstraction for a C `struct i2c_board_info` structure,
+/// which is used for manual I2C client creation.
+#[repr(transparent)]
+pub struct I2cBoardInfo(bindings::i2c_board_info);
+
+impl I2cBoardInfo {
+    const I2C_TYPE_SIZE: usize = 20;
+    /// Create a new [`I2cBoardInfo`] for a kernel driver.

rust/kernel/i2c.rs fn unbind(dev: &I2cClient<device::Core>, this: Pin<&Self>) { · line 445

Quoted 10 lines
rust/kernel/i2c.rs +10 -0
+    #[inline(always)]
+    pub const fn new(type_: &'static CStr, addr: u16) -> Self {
+        build_assert!(
+            type_.len_with_nul() <= Self::I2C_TYPE_SIZE,
+            "Type exceeds 20 bytes"
+        );
+        let src = type_.as_bytes_with_nul();
+        // Replace with `bindings::i2c_board_info::default()` once stabilized for `const`.
+        // SAFETY: FFI type is valid to be zero-initialized.
+        let mut i2c_board_info: bindings::i2c_board_info = pin_init::zeroed();

Same clippy error here:

error: statement has unnecessary safety comment
--> rust/kernel/i2c.rs:446:9

    |
446 |         let mut i2c_board_info: bindings::i2c_board_info =
pin_init::zeroed();
    |        
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
help: consider removing the safety comment
   --> rust/kernel/i2c.rs:445:9
    |
445 |         // SAFETY: FFI type is valid to be zero-initialized.
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

= help: for further information visit
https://rust-lang.github.io/rust-clippy/rust-1.91.0/index.html#unnecessary_safety_comment

Thanks
- Markus Probst

Quoted 73 lines
rust/kernel/i2c.rs +66 -0
+        let mut i: usize = 0;
+        while i < src.len() {
+            i2c_board_info.type_[i] = src[i];
+            i += 1;
+        }
+
+        i2c_board_info.addr = addr;
+        Self(i2c_board_info)
+    }
+
+    fn as_raw(&self) -> *const bindings::i2c_board_info {
+        from_ref(&self.0)
+    }
+}
+
 /// The i2c client representation.
 ///
 /// This structure represents the Rust abstraction for a C `struct i2c_client`. The
@@ -434,3 +536,54 @@ unsafe impl Send for I2cClient {}
 // SAFETY: `I2cClient` can be shared among threads because all methods of `I2cClient`
 // (i.e. `I2cClient<Normal>) are thread safe.
 unsafe impl Sync for I2cClient {}
+
+/// The registration of an i2c client device.
+///
+/// This type represents the registration of a [`struct i2c_client`]. When an instance of this
+/// type is dropped, its respective i2c client device will be unregistered from the system.
+///
+/// # Invariants
+///
+/// `self.0` always holds a valid pointer to an initialized and registered
+/// [`struct i2c_client`].
+#[repr(transparent)]
+pub struct Registration(NonNull<bindings::i2c_client>);
+
+impl Registration {
+    /// The C `i2c_new_client_device` function wrapper for manual I2C client creation.
+    pub fn new<'a>(
+        i2c_adapter: &I2cAdapter,
+        i2c_board_info: &I2cBoardInfo,
+        parent_dev: &'a device::Device<device::Bound>,
+    ) -> impl PinInit<Devres<Self>, Error> + 'a {
+        Devres::new(parent_dev, Self::try_new(i2c_adapter, i2c_board_info))
+    }
+
+    fn try_new(i2c_adapter: &I2cAdapter, i2c_board_info: &I2cBoardInfo) -> Result<Self> {
+        // SAFETY: the kernel guarantees that `i2c_new_client_device()` returns either a valid
+        // pointer or NULL. `from_err_ptr` separates errors. Following `NonNull::new`
+        // checks for NULL.
+        let raw_dev = from_err_ptr(unsafe {
+            bindings::i2c_new_client_device(i2c_adapter.as_raw(), i2c_board_info.as_raw())
+        })?;
+
+        let dev_ptr = NonNull::new(raw_dev).ok_or(ENODEV)?;
+
+        Ok(Self(dev_ptr))
+    }
+}
+
+impl Drop for Registration {
+    fn drop(&mut self) {
+        // SAFETY: `Drop` is only called for a valid `Registration`, which by invariant
+        // always contains a non-null pointer to an `i2c_client`.
+        unsafe { bindings::i2c_unregister_device(self.0.as_ptr()) }
+    }
+}
+
+// SAFETY: A `Registration` of a `struct i2c_client` can be released from any thread.
+unsafe impl Send for Registration {}
+
+// SAFETY: `Registration` offers no interior mutability (no mutation through &self
+// and no mutable access is exposed)
+unsafe impl Sync for Registration {}
#5 lore raw

PATCH v8 3/4 samples: rust: add Rust I2C sample driver

In reply to Igor Korotin

Add a new rust_driver_i2c sample, showing how to create a new i2c driver using ACPI/OF/Legacy ID tables.

---
 MAINTAINERS                     |  1 +
 samples/rust/Kconfig            | 11 +++++
 samples/rust/Makefile           |  1 +
 samples/rust/rust_driver_i2c.rs | 74 +++++++++++++++++++++++++++++++++
 4 files changed, 87 insertions(+)
 create mode 100644 samples/rust/rust_driver_i2c.rs
MAINTAINERS +1 -0
index a8dfde2f5633..8133987c4b54 100644
@@ -11750,6 +11750,7 @@ R:	Daniel Almeida <[email protected]>
 L:	[email protected]
 S:	Maintained
 F:	rust/kernel/i2c.rs
+F:	samples/rust/rust_driver_i2c.rs
 
 I2C SUBSYSTEM HOST DRIVERS
 M:	Andi Shyti <[email protected]>
samples/rust/Kconfig +11 -0
index c376eb899b7a..181466319588 100644
@@ -84,6 +84,17 @@ config SAMPLE_RUST_DEBUGFS_SCOPED
 
 	  If unsure, say N.
 
+config SAMPLE_RUST_DRIVER_I2C
+	tristate "I2C Driver"
+	depends on I2C=y
+	help
+	  This option builds the Rust I2C driver sample.
+
+	  To compile this as a module, choose M here:
+	  the module will be called rust_driver_i2c.
+
+	  If unsure, say N.
+
 config SAMPLE_RUST_DRIVER_PCI
 	tristate "PCI Driver"
 	depends on PCI
samples/rust/Makefile +1 -0
index cf8422f8f219..aec865e2adef 100644
@@ -7,6 +7,7 @@ obj-$(CONFIG_SAMPLE_RUST_PRINT)			+= rust_print.o
 obj-$(CONFIG_SAMPLE_RUST_DEBUGFS)		+= rust_debugfs.o
 obj-$(CONFIG_SAMPLE_RUST_DEBUGFS_SCOPED)	+= rust_debugfs_scoped.o
 obj-$(CONFIG_SAMPLE_RUST_DMA)			+= rust_dma.o
+obj-$(CONFIG_SAMPLE_RUST_DRIVER_I2C)		+= rust_driver_i2c.o
 obj-$(CONFIG_SAMPLE_RUST_DRIVER_PCI)		+= rust_driver_pci.o
 obj-$(CONFIG_SAMPLE_RUST_DRIVER_PLATFORM)	+= rust_driver_platform.o
 obj-$(CONFIG_SAMPLE_RUST_DRIVER_USB)		+= rust_driver_usb.o
samples/rust/rust_driver_i2c.rs new file +74 -0

74 lines not on this page · raw message

Signature · 1 line
2.43.0
#6 lore raw

PATCH v8 4/4 samples: rust: add Rust I2C client registration sample

In reply to Igor Korotin

Add a new rust_i2c_client sample, showing how to create a new i2c client using i2c::Registration

---
 samples/rust/Kconfig            |  12 +++
 samples/rust/Makefile           |   1 +
 samples/rust/rust_i2c_client.rs | 157 ++++++++++++++++++++++++++++++++
 3 files changed, 170 insertions(+)
 create mode 100644 samples/rust/rust_i2c_client.rs
samples/rust/Kconfig +12 -0
index 181466319588..a952220cdff7 100644
@@ -95,6 +95,18 @@ config SAMPLE_RUST_DRIVER_I2C
 
 	  If unsure, say N.
 
+config SAMPLE_RUST_I2C_CLIENT
+	tristate "Manual I2C Client"
+	depends on I2C && I2C_CHARDEV
+	help
+	  This option builds the Rust I2C client manual creation
+	  sample.
+
+	  To compile this as a module, choose M here:
+	  the module will be called rust_i2c_client.
+
+	  If unsure, say N.
+
 config SAMPLE_RUST_DRIVER_PCI
 	tristate "PCI Driver"
 	depends on PCI
samples/rust/Makefile +1 -0
index aec865e2adef..f65885d1d62b 100644
@@ -8,6 +8,7 @@ obj-$(CONFIG_SAMPLE_RUST_DEBUGFS)		+= rust_debugfs.o
 obj-$(CONFIG_SAMPLE_RUST_DEBUGFS_SCOPED)	+= rust_debugfs_scoped.o
 obj-$(CONFIG_SAMPLE_RUST_DMA)			+= rust_dma.o
 obj-$(CONFIG_SAMPLE_RUST_DRIVER_I2C)		+= rust_driver_i2c.o
+obj-$(CONFIG_SAMPLE_RUST_I2C_CLIENT)		+= rust_i2c_client.o
 obj-$(CONFIG_SAMPLE_RUST_DRIVER_PCI)		+= rust_driver_pci.o
 obj-$(CONFIG_SAMPLE_RUST_DRIVER_PLATFORM)	+= rust_driver_platform.o
 obj-$(CONFIG_SAMPLE_RUST_DRIVER_USB)		+= rust_driver_usb.o
samples/rust/rust_i2c_client.rs new file +157 -0

157 lines not on this page · raw message

Signature · 1 line
2.43.0
#7 lore raw
In reply to Igor Korotin

add me to cc please.

My led driver currently relies on the i2c bindings.

Thanks
- Markus Probst

[1]
https://lore.kernel.org/rust-for-linux/[email protected]/

Quoted 184 lines · Igor Korotin

This patch series lays the groundwork for writing Linux I2C drivers in Rust by:

1. Core abstractions
Introduce i2c::I2cClient, i2c::I2cAdapter, i2c::Driver and built on the existing struct i2c_client, struct i2c_adapter and struct i2c_driver, with safe Rust wrappers around probe, transfer, and teardown logic.

2. Manual device creation
Provide an API to register an I2C device at runtime from Rust using I2cBoardInfo and I2cAdapter, including automatic cleanup when the driver unloads.

3. Sample I2C driver (legacy table, OF & ACPI)
Add rust_driver_i2c, a sample that:
- binds to an I2C client via:
- legacy I2C-ID table,
- Open Firmware (device-tree) compatible strings, or
- ACPI IDs.
4. Sample I2C client registration (OF & ACPI)
Add rust_i2c_client, a sample that:
- binds to a platform parent device using OF/ACPI table
- registers new I2C client bound to the parent platform device using i2c::Registration::new()
- destroyes the I2C client device on exit.

Together, these four patches:

- Establish the essential Rust traits and types for I2C drivers.
- Enable driver binding via legacy ID table, device-tree (OF), or ACPI
- Enable manual device creation at runtime.
- Ship a samples showing typical usage

Igor Korotin (4):
rust: i2c: add basic I2C device and driver abstractions
rust: i2c: add manual I2C device creation abstractions
samples: rust: add Rust I2C sample driver
samples: rust: add Rust I2C client registration sample

Changelog
---------
v8:
- Rebase commits on top of [1]
- Applied kernel vertical style [2]
- Replace core::mem::zeroed() with pin_init::zeroed()
- Rename I2cAdapter::get_nr to I2cAdapter::index
- fixed some minor format issues
- Link to v7: [3]

[1] https://git.kernel.org/pub/scm/linux/kernel/git/driver-core/driver-core.git/log/?h=driver-core-next
[2] https://docs.kernel.org/rust/coding-guidelines.html#imports
[3] https://lore.kernel.org/rust-for-linux/[email protected]/

v7:
- Rebase and update i2c::Driver::probe function based on patch series [1].
- use i2c::Driver::unbind instead of drop in i2c::Adapter::remove_callback
- Implement and use i2c::Driver::unbind for I2C SampleDriver instead of Drop.
- i2c::Registration::new requires a parent device to which a new I2C client will be bound.
- i2c::Registration::new returns impl PinInit<Devres<Self>, Error>
- The existing rust_driver_i2c sample has been split into two:
rust_driver_i2c, which demonstrates the implementation of an I2C driver, and rust_i2c_client, which demonstrates registering a new I2C client bound to a parent platform device.
- Link to v6: [2]

[1] https://lore.kernel.org/all/[email protected]/
[2] https://lore.kernel.org/rust-for-linux/[email protected]/

v6:
- Add implementation of unbind for i2c::Driver trait;
- Add argument Pin<&Self> to i2c::Driver::shutdown method;
- Adjust usage of i2c::Driver::shutdown in
i2c::Adapter::shutdown_callback in i2c::Driver trait code
example and in rust_driver_i2c code;
- Remove dummy AsRef implementation for I2cAdapter. Adjust code
in rust_driver_i2c;
- Add i2c::I2cAdapter::get_nr method that returns I2cAdapter index;
- Optimize unsafe sections in inc_ref/dec_ref in AlwaysRefCounted
for I2cAdapter implementation;
- Remove unnecessary Drop implementation for I2cAdapter, because
I2cAdapter is always a reference;
- Remove unnecessary type definition Ops<T> in rust_driver_i2c
- Simplify call of i2c::I2cAdapter::get in try_pin_init! macro
for rust_driver_i2c
- Link to v5: https://lore.kernel.org/rust-for-linux/[email protected]/
v5:
- Rename missed pdev variables to idev (thanks to Daniel).
- Import crate::device_id::RawDeviceIdIndex and
crate::types::AlwaysRefCounted in i2c.rs.
- Switch dev_dbg to dev_info in the sample I2C driver messages.
- Make I2cAdapter::get() return ARef<I2cAdapter> instead of
&I2cAdapter.
- Remove TryFrom<device::Device<Ctx>> for I2cAdapter<Ctx> (unused;
to be reintroduced in a later I2C series).
- Remove AsRef<device::Device<Ctx>> for I2cAdapter<Ctx> (unused;
to be reintroduced in a later I2C series).
- Add AsRef<I2cAdapter> for I2cAdapter<Ctx>.
- Use i2c_get/put_adapter instead of get/put_device for
AlwaysRefCounted<I2cAdapter>.
- Update safety comment for unsafe impl Sync for Registration {}.
- Tweak comment for I2cBoardInfo::new.
- Adjust build-time assertion message in Adapter::register.
- Link to v4: https://lore.kernel.org/rust-for-linux/[email protected]/
v4:
- Renamed i2c::I2cAdapterRef to i2c::I2cAdapter.
- Renamed i2c::Device to i2c::I2cClient for consistency with
i2c::I2cAdapter and to avoid confusion with i2c::Adapter
- Reworked i2c::I2cAdapter to be an Opaque around i2c_adapter struct
- Implemented AlwaysRefCounted trait for i2c::I2cAdapter.
- Fixed numerous comment mistakes and typos all over the code, thanks
to Danilo and Daniel
- Got rid of all unwrap() use-cases in i2c.rs and rust_driver_i2c.rs.
This covers 0-day kernel panic <[email protected]>
- Removed unnecessary casts.
- Replaced all addr_of_mut! macros to &raw mut.
- In i2c::Adapter::register method build assert if all ID tables are
None.
- Renamed all pdrv and pdev instances to idrv and idev respectivly
- Implemented an ealry return in i2c::Adapter::i2c_id_info
- Added all missing Safety comments.
- Removed unsafe impl<Ctx: device::DeviceContext> crate::types::AlwaysRefCounted for Device<Ctx> implementation which came to v3 from v2 by mistake.
- Added more details regarding i2c-stub driver usage in rust_driver_i2c
comment.
- Changed i2c::I2cAdapter::get return type from Option<Self> to
Result<&'static Self>.
- Added Daniel Almeida as a reviewer to the "I2C Subsystem [RUST]" entry
in MAINTAINERS, per his offer.
- Link to v3: https://lore.kernel.org/rust-for-linux/[email protected]/
v3:
- removed unnecessary i2c_get_clientdata and i2c_set_clientdata rust
helpers. Using generic accessors implemented in [1] instead.
- Reimplemented i2c::DeviceId based on changes in [2].
- Using from_result in i2c::Adapter::probe_callback
- Using explicit drop() for i2c client private data in
i2c::Adapter::remove_callback
- replaced device::Device::as_ref() with device::Device::from_raw in i2c::Device::as_ref(). It is renamed in device::Device.
- Build Rust I2C only if I2C is built-in
- Reimplement overcomplicated trait i2c::DeviceOwned the same way it is
implemented in auxiliary [3].
- Merge rust_device_i2c and rust_driver_i2c samples. Resulting
rust_driver_i2c creates pined i2c_client using i2c::Registration::new
and probes newly created i2c_client.
- Created a new entry in MAINTAINERS file containing i2c.rs and
rust_driver_i2c.rs in it.
- Link to v2: [4]

[1] https://lore.kernel.org/lkml/[email protected]/
[2] https://lore.kernel.org/rust-for-linux/[email protected]/
[3] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/rust/kernel/auxiliary.rs?h=v6.16-rc4#n299
[4] https://lore.kernel.org/rust-for-linux/[email protected]/

v2:
- Merged separated ACPI support patches since ACPI-table support is
merged into driver-core-next.
- Added I2cAdapterRef and I2cBoardInfo abstractions
- Added DeviceState generic parameter which is used for i2c::Device
as a sign if the device is created manually
- Added DeviceOwned abstraction which is a safe reference to a
manually created i2c::Device<Ctx, state::Owned>.
- Added Rust manual I2C device creation sample
- Link to v1: https://lore.kernel.org/rust-for-linux/[email protected]/

 MAINTAINERS                     |   9 +
 rust/bindings/bindings_helper.h |   1 +
 rust/kernel/i2c.rs              | 589 ++++++++++++++++++++++++++++++++
 rust/kernel/lib.rs              |   2 +
 samples/rust/Kconfig            |  23 ++
 samples/rust/Makefile           |   2 +
 samples/rust/rust_driver_i2c.rs |  74 ++++
 samples/rust/rust_i2c_client.rs | 157 +++++++++
 8 files changed, 857 insertions(+)
 create mode 100644 rust/kernel/i2c.rs
 create mode 100644 samples/rust/rust_driver_i2c.rs
 create mode 100644 samples/rust/rust_i2c_client.rs

base-commit: ededb7bcdfdbcfbb7af93e3a543165a9553e1683

#8 lore raw

Hello Markus

Quoted 6 lines · Markus Probst

add me to cc please.

My led driver currently relies on the i2c bindings.

Thanks
- Markus Probst

Sure, no problem

Thanks
Igor

#9 lore raw
In reply to Igor Korotin

(Cc: Markus)

On Mon Nov 17, 2025 at 3:21 AM AEDT, Igor Korotin wrote:

Applied to driver-core-testing, thanks!

Quoted 2 lines

Igor Korotin (4):
rust: i2c: add basic I2C device and driver abstractions

[ Remove unnecessary safety comment; fix rustdoc Device -> I2cClient.
- Danilo ]

Quoted 1 line

rust: i2c: add manual I2C device creation abstractions

[ Remove unnecessary safety comment. - Danilo ]

Quoted 2 lines

samples: rust: add Rust I2C sample driver
samples: rust: add Rust I2C client registration sample

[ * Remove dependency to I2C_CHARDEV, depend on I2C=y.
* Remove unnecessary impl Drop for SampleDriver.
* Rename i2c::Registration, import Devres.
* Fixup module description.
* Add new source file to MAINTAINERS.
- Danilo ]

#10 lore raw

Hello Danilo

Quoted 19 lines · Danilo Krummrich

(Cc: Markus)

On Mon Nov 17, 2025 at 3:21 AM AEDT, Igor Korotin wrote:

Applied to driver-core-testing, thanks!

Quoted 2 lines

Igor Korotin (4):
rust: i2c: add basic I2C device and driver abstractions

[ Remove unnecessary safety comment; fix rustdoc Device -> I2cClient.
- Danilo ]

Quoted 1 line

rust: i2c: add manual I2C device creation abstractions

[ Remove unnecessary safety comment. - Danilo ]

Quoted 2 lines

samples: rust: add Rust I2C sample driver
samples: rust: add Rust I2C client registration sample

Quoted 6 lines

[ * Remove dependency to I2C_CHARDEV, depend on I2C=y.
* Remove unnecessary impl Drop for SampleDriver.
* Rename i2c::Registration, import Devres.
* Fixup module description.
* Add new source file to MAINTAINERS.
- Danilo ]

Thanks a lot for the review and all the fixes of yours.

Cheers
Igor