public inbox for rust-for-linux@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/5] rust: extend I2C functionality
@ 2026-01-31 14:12 Igor Korotin via B4 Relay
  2026-01-31 14:12 ` [PATCH 1/5] rust: i2c: split client and adapter code into separate files Igor Korotin via B4 Relay
                   ` (5 more replies)
  0 siblings, 6 replies; 16+ messages in thread
From: Igor Korotin via B4 Relay @ 2026-01-31 14:12 UTC (permalink / raw)
  To: Danilo Krummrich, Daniel Almeida, Miguel Ojeda, Boqun Feng,
	Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
	Alice Ryhl, Trevor Gross, Wolfram Sang
  Cc: linux-kernel, rust-for-linux, linux-i2c, markus.probst,
	Igor Korotin

This patch series extend the existing I2C functionality with:
- Abstractions allowing to implement I2C algorithms used by I2C adapters;
- Abstractions allowing to create and add new I2C adapters;
- Safe wrappers upon I2C and SMbus transferring C API: read/write
  bytes/words/byte arrays.

The patch series contains additional new sample driver `rust_i2c_adapter`
presenting the new functionality. 

Patch series depend on 
message-id: <20260107103511.570525-1-dakr@kernel.org>

Signed-off-by: Igor Korotin <igor.korotin.linux@gmail.com>
---
Igor Korotin (5):
      rust: i2c: split client and adapter code into separate files
      rust: bits: add define_flags macro
      rust: i2c: Add I2C Adapter registration abstractions
      rust: i2c: add I2C wrappers
      samples: rust: add Rust I2C adapter registration sample

 MAINTAINERS                      |   4 +-
 include/linux/i2c.h              |   6 +
 rust/helpers/helpers.c           |   1 +
 rust/helpers/i2c.c               |  15 ++
 rust/kernel/bits.rs              |  57 ++++++++
 rust/kernel/i2c.rs               | 254 ++-------------------------------
 rust/kernel/i2c/adapter.rs       | 200 ++++++++++++++++++++++++++
 rust/kernel/i2c/algo.rs          | 300 +++++++++++++++++++++++++++++++++++++++
 rust/kernel/i2c/client.rs        | 284 ++++++++++++++++++++++++++++++++++++
 samples/rust/Kconfig             |  12 ++
 samples/rust/Makefile            |   1 +
 samples/rust/rust_driver_i2c.rs  |  10 +-
 samples/rust/rust_i2c_adapter.rs | 170 ++++++++++++++++++++++
 samples/rust/rust_i2c_client.rs  |  15 +-
 14 files changed, 1069 insertions(+), 260 deletions(-)
---
base-commit: a7c013f779530190d0c1e1aa5e7c8a61f0bd479e
change-id: 20260131-i2c-adapter-d38d330c4c25
prerequisite-message-id: <20260107103511.570525-1-dakr@kernel.org>
prerequisite-patch-id: 6e4d28a06d842ddfc7db82f410532a35b8fe317d
prerequisite-patch-id: 9739295d85b688257fca29be1e1217a05a69c9e7
prerequisite-patch-id: 78eaed3746a40713f44f13508fcf43653b0a4528
prerequisite-patch-id: 99f1881bc896896297a903affcfa4f62cab1a307
prerequisite-patch-id: ea8baa9d648f1178d6f39d140b98bddf3c183f6f
prerequisite-patch-id: f8a9e049d204bde04e80151a67dfff5ec2633e7a

Best regards,
-- 
Igor Korotin <igor.korotin.linux@gmail.com>



^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH 1/5] rust: i2c: split client and adapter code into separate files
  2026-01-31 14:12 [PATCH 0/5] rust: extend I2C functionality Igor Korotin via B4 Relay
@ 2026-01-31 14:12 ` Igor Korotin via B4 Relay
  2026-01-31 14:12 ` [PATCH 2/5] rust: bits: add define_flags macro Igor Korotin via B4 Relay
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 16+ messages in thread
From: Igor Korotin via B4 Relay @ 2026-01-31 14:12 UTC (permalink / raw)
  To: Danilo Krummrich, Daniel Almeida, Miguel Ojeda, Boqun Feng,
	Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
	Alice Ryhl, Trevor Gross, Wolfram Sang
  Cc: linux-kernel, rust-for-linux, linux-i2c, markus.probst,
	Igor Korotin

From: Igor Korotin <igor.korotin.linux@gmail.com>

Reorganize the I2C module by splitting client-related and
adapter-related code into separate files for better maintainability.

- Move client traits and implementations to i2c/client.rs
- Move adapter traits and implementations to i2c/adapter.rs

Signed-off-by: Igor Korotin <igor.korotin.linux@gmail.com>
---
 MAINTAINERS                     |   1 +
 rust/kernel/i2c.rs              | 253 ++--------------------------------------
 rust/kernel/i2c/adapter.rs      |  80 +++++++++++++
 rust/kernel/i2c/client.rs       | 192 ++++++++++++++++++++++++++++++
 samples/rust/rust_driver_i2c.rs |  10 +-
 samples/rust/rust_i2c_client.rs |  15 ++-
 6 files changed, 293 insertions(+), 258 deletions(-)

diff --git a/MAINTAINERS b/MAINTAINERS
index f6bc65de83c7..60662984176d 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -11886,6 +11886,7 @@ R:	Danilo Krummrich <dakr@kernel.org>
 R:	Daniel Almeida <daniel.almeida@collabora.com>
 L:	rust-for-linux@vger.kernel.org
 S:	Maintained
+F:	rust/i2c/*
 F:	rust/kernel/i2c.rs
 F:	samples/rust/rust_driver_i2c.rs
 F:	samples/rust/rust_i2c_client.rs
diff --git a/rust/kernel/i2c.rs b/rust/kernel/i2c.rs
index 792a71b15463..0bebfde3e495 100644
--- a/rust/kernel/i2c.rs
+++ b/rust/kernel/i2c.rs
@@ -1,37 +1,25 @@
 // SPDX-License-Identifier: GPL-2.0
 
-//! I2C Driver subsystem
+//! I2C subsystem
 
 // I2C Driver abstractions.
 use crate::{
     acpi,
-    container_of,
     device,
     device_id::{
         RawDeviceId,
         RawDeviceIdIndex, //
     },
-    devres::Devres,
     driver,
     error::*,
+    i2c::client::I2cClient,
     of,
     prelude::*,
-    types::{
-        AlwaysRefCounted,
-        Opaque, //
-    }, //
+    types::Opaque, //
 };
 
-use core::{
-    marker::PhantomData,
-    mem::offset_of,
-    ptr::{
-        from_ref,
-        NonNull, //
-    }, //
-};
-
-use kernel::types::ARef;
+pub mod adapter;
+pub mod client;
 
 /// An I2C device id table.
 #[repr(transparent)]
@@ -291,13 +279,13 @@ macro_rules! module_i2c_driver {
 ///     const ACPI_ID_TABLE: Option<acpi::IdTable<Self::IdInfo>> = Some(&ACPI_TABLE);
 ///
 ///     fn probe(
-///         _idev: &i2c::I2cClient<Core>,
+///         _idev: &i2c::client::I2cClient<Core>,
 ///         _id_info: Option<&Self::IdInfo>,
 ///     ) -> impl PinInit<Self, Error> {
 ///         Err(ENODEV)
 ///     }
 ///
-///     fn shutdown(_idev: &i2c::I2cClient<Core>, this: Pin<&Self>) {
+///     fn shutdown(_idev: &i2c::client::I2cClient<Core>, this: Pin<&Self>) {
 ///     }
 /// }
 ///```
@@ -357,230 +345,3 @@ fn unbind(dev: &I2cClient<device::Core>, this: Pin<&Self>) {
         let _ = (dev, this);
     }
 }
-
-/// 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 {
-        let src = type_.to_bytes_with_nul();
-        build_assert!(src.len() <= Self::I2C_TYPE_SIZE, "Type exceeds 20 bytes");
-        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
-/// 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 `struct i2c_client`.
-// The offset is guaranteed to point to a valid device field inside `I2cClient`.
-unsafe impl<Ctx: device::DeviceContext> device::AsBusDevice<Ctx> for I2cClient<Ctx> {
-    const OFFSET: usize = offset_of!(bindings::i2c_client, dev);
-}
-
-// 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 {}
-
-/// 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 {}
diff --git a/rust/kernel/i2c/adapter.rs b/rust/kernel/i2c/adapter.rs
new file mode 100644
index 000000000000..4a0d2faaf98b
--- /dev/null
+++ b/rust/kernel/i2c/adapter.rs
@@ -0,0 +1,80 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! I2C subsystem
+
+// I2C Adapter abstractions.
+use crate::{
+    bindings,
+    device,
+    error::*,
+    prelude::*,
+    types::Opaque, //
+};
+
+use core::{
+    marker::PhantomData,
+    ptr::NonNull, //
+};
+
+use kernel::types::ARef;
+
+/// 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> {
+    pub(super) 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()) }
+    }
+}
diff --git a/rust/kernel/i2c/client.rs b/rust/kernel/i2c/client.rs
new file mode 100644
index 000000000000..a597793ff038
--- /dev/null
+++ b/rust/kernel/i2c/client.rs
@@ -0,0 +1,192 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! I2C subsystem
+
+// I2C Client abstractions.
+use crate::{
+    container_of,
+    device,
+    devres::Devres,
+    error::*,
+    i2c::adapter::I2cAdapter,
+    prelude::*,
+    types::{
+        AlwaysRefCounted,
+        Opaque, //
+    }, //
+};
+
+use core::{
+    marker::PhantomData,
+    mem::offset_of,
+    ptr::{
+        from_ref,
+        NonNull, //
+    }, //
+};
+
+/// 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 {
+        let src = type_.to_bytes_with_nul();
+        build_assert!(src.len() <= Self::I2C_TYPE_SIZE, "Type exceeds 20 bytes");
+        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
+/// 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> {
+    pub(super) fn as_raw(&self) -> *mut bindings::i2c_client {
+        self.0.get()
+    }
+}
+
+// SAFETY: `I2cClient` is a transparent wrapper of `struct i2c_client`.
+// The offset is guaranteed to point to a valid device field inside `I2cClient`.
+unsafe impl<Ctx: device::DeviceContext> device::AsBusDevice<Ctx> for I2cClient<Ctx> {
+    const OFFSET: usize = offset_of!(bindings::i2c_client, dev);
+}
+
+// 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 {}
+
+/// 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 {}
diff --git a/samples/rust/rust_driver_i2c.rs b/samples/rust/rust_driver_i2c.rs
index 6be79f9e9fb5..af5e9aae4f06 100644
--- a/samples/rust/rust_driver_i2c.rs
+++ b/samples/rust/rust_driver_i2c.rs
@@ -6,6 +6,7 @@
     acpi,
     device::Core,
     i2c,
+    i2c::client::I2cClient,
     of,
     prelude::*, //
 };
@@ -40,10 +41,7 @@ impl i2c::Driver for SampleDriver {
     const I2C_ID_TABLE: Option<i2c::IdTable<Self::IdInfo>> = Some(&I2C_TABLE);
     const OF_ID_TABLE: Option<of::IdTable<Self::IdInfo>> = Some(&OF_TABLE);
 
-    fn probe(
-        idev: &i2c::I2cClient<Core>,
-        info: Option<&Self::IdInfo>,
-    ) -> impl PinInit<Self, Error> {
+    fn probe(idev: &I2cClient<Core>, info: Option<&Self::IdInfo>) -> impl PinInit<Self, Error> {
         let dev = idev.as_ref();
 
         dev_info!(dev, "Probe Rust I2C driver sample.\n");
@@ -55,11 +53,11 @@ fn probe(
         Ok(Self)
     }
 
-    fn shutdown(idev: &i2c::I2cClient<Core>, _this: Pin<&Self>) {
+    fn shutdown(idev: &I2cClient<Core>, _this: Pin<&Self>) {
         dev_info!(idev.as_ref(), "Shutdown Rust I2C driver sample.\n");
     }
 
-    fn unbind(idev: &i2c::I2cClient<Core>, _this: Pin<&Self>) {
+    fn unbind(idev: &I2cClient<Core>, _this: Pin<&Self>) {
         dev_info!(idev.as_ref(), "Unbind Rust I2C driver sample.\n");
     }
 }
diff --git a/samples/rust/rust_i2c_client.rs b/samples/rust/rust_i2c_client.rs
index 8d2c12e535b0..4b3dcf21230c 100644
--- a/samples/rust/rust_i2c_client.rs
+++ b/samples/rust/rust_i2c_client.rs
@@ -71,7 +71,11 @@
     acpi,
     device,
     devres::Devres,
-    i2c,
+    i2c::{
+        adapter::I2cAdapter,
+        client::I2cBoardInfo,
+        client::Registration, //
+    }, //
     of,
     platform,
     prelude::*,
@@ -82,7 +86,7 @@
 struct SampleDriver {
     parent_dev: ARef<platform::Device>,
     #[pin]
-    _reg: Devres<i2c::Registration>,
+    _reg: Devres<Registration>,
 }
 
 kernel::of_device_table!(
@@ -101,8 +105,7 @@ struct SampleDriver {
 
 const SAMPLE_I2C_CLIENT_ADDR: u16 = 0x30;
 const SAMPLE_I2C_ADAPTER_INDEX: i32 = 0;
-const BOARD_INFO: i2c::I2cBoardInfo =
-    i2c::I2cBoardInfo::new(c"rust_driver_i2c", SAMPLE_I2C_CLIENT_ADDR);
+const BOARD_INFO: I2cBoardInfo = I2cBoardInfo::new(c"rust_driver_i2c", SAMPLE_I2C_CLIENT_ADDR);
 
 impl platform::Driver for SampleDriver {
     type IdInfo = ();
@@ -122,9 +125,9 @@ fn probe(
             parent_dev: pdev.into(),
 
             _reg <- {
-                let adapter = i2c::I2cAdapter::get(SAMPLE_I2C_ADAPTER_INDEX)?;
+                let adapter = I2cAdapter::get(SAMPLE_I2C_ADAPTER_INDEX)?;
 
-                i2c::Registration::new(&adapter, &BOARD_INFO, pdev.as_ref())
+                Registration::new(&adapter, &BOARD_INFO, pdev.as_ref())
             }
         })
     }

-- 
2.43.0



^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 2/5] rust: bits: add define_flags macro
  2026-01-31 14:12 [PATCH 0/5] rust: extend I2C functionality Igor Korotin via B4 Relay
  2026-01-31 14:12 ` [PATCH 1/5] rust: i2c: split client and adapter code into separate files Igor Korotin via B4 Relay
@ 2026-01-31 14:12 ` Igor Korotin via B4 Relay
  2026-02-08 13:56   ` Daniel Almeida
  2026-01-31 14:12 ` [PATCH 3/5] rust: i2c: Add I2C Adapter registration abstractions Igor Korotin via B4 Relay
                   ` (3 subsequent siblings)
  5 siblings, 1 reply; 16+ messages in thread
From: Igor Korotin via B4 Relay @ 2026-01-31 14:12 UTC (permalink / raw)
  To: Danilo Krummrich, Daniel Almeida, Miguel Ojeda, Boqun Feng,
	Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
	Alice Ryhl, Trevor Gross, Wolfram Sang
  Cc: linux-kernel, rust-for-linux, linux-i2c, markus.probst,
	Igor Korotin

From: Igor Korotin <igor.korotin.linux@gmail.com>

introduce define_flags macro that incorporates BitOr BitAnd and Not
implementations.

Signed-off-by: Igor Korotin <igor.korotin.linux@gmail.com>
---
 rust/kernel/bits.rs | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 57 insertions(+)

diff --git a/rust/kernel/bits.rs b/rust/kernel/bits.rs
index 553d50265883..6750aef18708 100644
--- a/rust/kernel/bits.rs
+++ b/rust/kernel/bits.rs
@@ -201,3 +201,60 @@ pub const fn [<genmask_ $ty>](range: RangeInclusive<u32>) -> $ty {
     /// assert_eq!(genmask_u8(0..=7), u8::MAX);
     /// ```
 );
+
+/// defines flags
+#[macro_export]
+macro_rules! define_flags {
+    (
+        $gen_name:ident($gen_type:ident),
+        $($variant:ident = $binding:expr,)+
+    ) => {
+        /// Flags that can be combined with the operators `|`, `&`, and `!`.
+        ///
+        /// Values can be used from the [`flags`] module.
+        #[derive(Clone, Copy, PartialEq)]
+        pub struct $gen_name($gen_type);
+
+        impl $gen_name {
+            /// Get the raw representation of this flag.
+            pub(crate) fn as_raw(self) -> $gen_type {
+                self.0
+            }
+
+            /// Check whether `flags` is contained in `self`.
+            pub fn contains(self, flags: $gen_name) -> bool {
+                (self & flags) == flags
+            }
+        }
+
+        impl core::ops::BitOr for $gen_name {
+            type Output = $gen_name;
+            fn bitor(self, rhs: Self) -> Self::Output {
+                Self(self.0 | rhs.0)
+            }
+        }
+
+        impl core::ops::BitAnd for $gen_name {
+            type Output = $gen_name;
+            fn bitand(self, rhs: Self) -> Self::Output {
+                Self(self.0 & rhs.0)
+            }
+        }
+
+        impl core::ops::Not for $gen_name {
+            type Output = $gen_name;
+            fn not(self) -> Self::Output {
+                Self(!self.0)
+            }
+        }
+
+        #[allow(missing_docs)]
+        pub mod flags {
+            use super::$gen_name;
+            $(
+                #[allow(missing_docs)]
+                pub const $variant: $gen_name = $gen_name($binding as $gen_type);
+            )+
+        }
+    }
+}

-- 
2.43.0



^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 3/5] rust: i2c: Add I2C Adapter registration abstractions
  2026-01-31 14:12 [PATCH 0/5] rust: extend I2C functionality Igor Korotin via B4 Relay
  2026-01-31 14:12 ` [PATCH 1/5] rust: i2c: split client and adapter code into separate files Igor Korotin via B4 Relay
  2026-01-31 14:12 ` [PATCH 2/5] rust: bits: add define_flags macro Igor Korotin via B4 Relay
@ 2026-01-31 14:12 ` Igor Korotin via B4 Relay
  2026-01-31 14:12 ` [PATCH 4/5] rust: i2c: add I2C wrappers Igor Korotin via B4 Relay
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 16+ messages in thread
From: Igor Korotin via B4 Relay @ 2026-01-31 14:12 UTC (permalink / raw)
  To: Danilo Krummrich, Daniel Almeida, Miguel Ojeda, Boqun Feng,
	Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
	Alice Ryhl, Trevor Gross, Wolfram Sang
  Cc: linux-kernel, rust-for-linux, linux-i2c, markus.probst,
	Igor Korotin

From: Igor Korotin <igor.korotin.linux@gmail.com>

Add safe Rust abstractions for I2C adapter registration and
management, wrapping the C functions `i2c_add_adapter` and
`i2c_del_adapter`.

This includes:
- Hide `struct i2c_algorithm` unions from bindgen to avoid unnecessary
  mangles.
- Safe wrappers upon `struct i2c_msg` and `struct i2c_smbus_data`
- I2C Algorithm wrapper around `struct i2c_algorithm`
- I2C Adapter Flags wrapper
- Registration/deregistration logic for I2C adapters

Signed-off-by: Igor Korotin <igor.korotin.linux@gmail.com>
---
 include/linux/i2c.h        |   6 +
 rust/kernel/i2c.rs         |   1 +
 rust/kernel/i2c/adapter.rs | 120 ++++++++++++++++++
 rust/kernel/i2c/algo.rs    | 300 +++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 427 insertions(+)

diff --git a/include/linux/i2c.h b/include/linux/i2c.h
index 20fd41b51d5c..c176243de254 100644
--- a/include/linux/i2c.h
+++ b/include/linux/i2c.h
@@ -548,18 +548,24 @@ struct i2c_algorithm {
 	 * smbus_xfer. If set to NULL, the SMBus protocol is simulated
 	 * using common I2C messages.
 	 */
+#ifndef __BINDGEN__
 	union {
+#endif
 		int (*xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,
 			    int num);
+#ifndef __BINDGEN__
 		int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,
 				   int num);
 	};
 	union {
+#endif
 		int (*xfer_atomic)(struct i2c_adapter *adap,
 				   struct i2c_msg *msgs, int num);
+#ifndef __BINDGEN__
 		int (*master_xfer_atomic)(struct i2c_adapter *adap,
 					   struct i2c_msg *msgs, int num);
 	};
+#endif
 	int (*smbus_xfer)(struct i2c_adapter *adap, u16 addr,
 			  unsigned short flags, char read_write,
 			  u8 command, int size, union i2c_smbus_data *data);
diff --git a/rust/kernel/i2c.rs b/rust/kernel/i2c.rs
index 0bebfde3e495..af33a3be83c4 100644
--- a/rust/kernel/i2c.rs
+++ b/rust/kernel/i2c.rs
@@ -19,6 +19,7 @@
 };
 
 pub mod adapter;
+pub mod algo;
 pub mod client;
 
 /// An I2C device id table.
diff --git a/rust/kernel/i2c/adapter.rs b/rust/kernel/i2c/adapter.rs
index 4a0d2faaf98b..ec64c1327792 100644
--- a/rust/kernel/i2c/adapter.rs
+++ b/rust/kernel/i2c/adapter.rs
@@ -6,7 +6,9 @@
 use crate::{
     bindings,
     device,
+    devres::Devres,
     error::*,
+    i2c::algo::*,
     prelude::*,
     types::Opaque, //
 };
@@ -38,6 +40,15 @@ impl<Ctx: device::DeviceContext> I2cAdapter<Ctx> {
     pub(super) fn as_raw(&self) -> *mut bindings::i2c_adapter {
         self.0.get()
     }
+
+    /// Convert a raw C `struct i2c_adapter` pointer to a `&'a I2cAdapter`.
+    pub(super) fn from_raw<'a>(ptr: *mut bindings::i2c_adapter) -> &'a Self {
+        // SAFETY: Callers must ensure that `ptr` is valid, non-null, and has a non-zero reference
+        // count, i.e. it must be ensured that the reference count of the C `struct i2c_adapter`
+        // `ptr` points to can't drop to zero, for the duration of this function call and the entire
+        // duration when the returned reference exists.
+        unsafe { &*ptr.cast() }
+    }
 }
 
 impl I2cAdapter {
@@ -78,3 +89,112 @@ unsafe fn dec_ref(obj: NonNull<Self>) {
         unsafe { bindings::i2c_put_adapter(obj.as_ref().as_raw()) }
     }
 }
+
+impl<Ctx: device::DeviceContext> AsRef<device::Device<Ctx>> for I2cAdapter<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_adapter`.
+        let dev = unsafe { &raw mut (*raw).dev };
+
+        // SAFETY: `dev` points to a valid `struct device`.
+        unsafe { device::Device::from_raw(dev) }
+    }
+}
+
+/// Options for creating an I2C adapter device.
+pub struct I2cAdapterOptions {
+    /// The name of the I2C adapter device.
+    pub name: &'static CStr,
+}
+
+impl I2cAdapterOptions {
+    /// Create a raw `struct i2c_adapter` ready for registration.
+    pub const fn as_raw<T: I2cAlgorithm>(self) -> bindings::i2c_adapter {
+        let mut adapter: bindings::i2c_adapter = pin_init::zeroed();
+        // TODO: make it some other way... this looks like shit
+        let src = self.name.to_bytes_with_nul();
+        let mut i: usize = 0;
+        while i < src.len() {
+            adapter.name[i] = src[i];
+            i += 1;
+        }
+        adapter.algo = I2cAlgorithmVTable::<T>::build();
+
+        adapter
+    }
+}
+
+/// A registration of a I2C Adapter.
+///
+/// # Invariants
+///
+/// - `inner` contains a `struct i2c_adapter` that is registered using
+///   `i2c_add_adapter()`.
+/// - This registration remains valid for the entire lifetime of the
+///   [`i2c::adapter::Registration<T>`] instance.
+/// - Deregistration occurs exactly once in [`Drop`] via `i2c_del_adapter()`.
+/// - `inner` wraps a valid, pinned `i2c_adapter` created using
+///   [`I2cAdapterOptions::as_raw`].
+#[repr(transparent)]
+#[pin_data(PinnedDrop)]
+pub struct Registration<T> {
+    #[pin]
+    inner: Opaque<bindings::i2c_adapter>,
+    t_: PhantomData<T>,
+}
+
+impl<T: I2cAlgorithm> Registration<T> {
+    /// Register an I2C adapter.
+    pub fn register<'a>(
+        parent_dev: &'a device::Device<device::Bound>,
+        opts: I2cAdapterOptions,
+    ) -> impl PinInit<Devres<Self>, Error> + 'a
+    where
+        T: 'a,
+    {
+        Devres::new(parent_dev, Self::new(parent_dev, opts))
+    }
+
+    fn new<'a>(
+        parent_dev: &'a device::Device<device::Bound>,
+        opts: I2cAdapterOptions,
+    ) -> impl PinInit<Self, Error> + use<'a, T>
+    where
+        T: 'a,
+    {
+        try_pin_init! { Self {
+            inner <- Opaque::try_ffi_init(move |slot: *mut bindings::i2c_adapter| {
+                // SAFETY: The initializer can write to the provided `slot`.
+                unsafe {slot.write(opts.as_raw::<T>()) };
+
+                // SAFETY: `slot` is valid from the initializer; `parent_dev` outlives the adapter.
+                unsafe { (*slot).dev.parent = parent_dev.as_raw() };
+
+                // SAFETY: the `struct i2c_adapter` was just created in slot. The adapter will
+                // get unregistered before `slot` is deallocated because the memory is pinned and
+                // the destructor of this type deallocates the memory.
+                // INVARIANT: If this returns `Ok(())`, then the `slot` will contain a registered
+                // i2c adapter.
+                to_result(unsafe {bindings::i2c_add_adapter(slot)})
+            }),
+            t_: PhantomData,
+            }
+        }
+    }
+}
+
+#[pinned_drop]
+impl<T> PinnedDrop for Registration<T> {
+    fn drop(self: Pin<&mut Self>) {
+        // SAFETY: We know that the device is registered by the type invariants.
+        unsafe { bindings::i2c_del_adapter(self.inner.get()) };
+    }
+}
+
+// SAFETY: A `Registration` of a `struct i2c_client` can be released from any thread.
+unsafe impl<T> Send for Registration<T> {}
+
+// SAFETY: `Registration` offers no interior mutability (no mutation through &self
+// and no mutable access is exposed)
+unsafe impl<T> Sync for Registration<T> {}
diff --git a/rust/kernel/i2c/algo.rs b/rust/kernel/i2c/algo.rs
new file mode 100644
index 000000000000..ca9fcc385426
--- /dev/null
+++ b/rust/kernel/i2c/algo.rs
@@ -0,0 +1,300 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! I2C subsystem
+
+// I2C Algorithm abstractions.
+use crate::{
+    bindings::{
+        i2c_adapter,
+        i2c_msg,
+        i2c_smbus_data,
+        u16_,
+        u32_,
+        u8_, //
+    },
+    device::Normal,
+    error::VTABLE_DEFAULT_ERROR,
+    i2c::adapter::I2cAdapter,
+    prelude::*,
+    types::Opaque, //
+};
+
+use core::{
+    marker::PhantomData, //
+};
+
+/// The i2c msg representation.
+///
+/// This structure represents the Rust abstraction for a C `struct i2c_msg`. The
+/// implementation abstracts the usage of an existing C `struct i2c_msg` that
+/// gets passed from/to the C side
+///
+/// # Invariants
+///
+/// A [`I2cMsg`] instance represents a valid `struct i2c_msg` created by the C portion of
+/// the kernel.
+#[repr(transparent)]
+pub struct I2cMsg(Opaque<bindings::i2c_msg>);
+
+impl I2cMsg {
+    /// Convert a raw C `struct i2c_msg` pointers to `&'a mut [I2cMsg]`.
+    pub fn from_raw_parts_mut<'a>(msgs: *mut bindings::i2c_msg, len: usize) -> &'a mut [Self] {
+        // SAFETY: Callers must ensure that `msgs` is valid, non-null, for the duration of this
+        // function call and the entire duration when the returned slice exists.
+        unsafe { core::slice::from_raw_parts_mut(msgs.cast::<Self>(), len) }
+    }
+}
+
+/// The i2c smbus data representation.
+///
+/// This structure represents the Rust abstraction for a C `struct i2c_smbus_data`. The
+/// implementation abstracts the usage of an existing C `struct i2c_smbus_data` that
+/// gets passed from/to the C side
+///
+/// # Invariants
+///
+/// A [`I2cSmbusData`] instance represents a valid `struct i2c_smbus_data` created by the C
+/// portion of the kernel.
+#[repr(transparent)]
+pub struct I2cSmbusData(Opaque<bindings::i2c_smbus_data>);
+
+impl I2cSmbusData {
+    /// Convert a raw C `struct i2c_smbus_data` pointer to `&'a I2cSmbusData`.
+    fn from_raw<'a>(ptr: *const bindings::i2c_smbus_data) -> &'a Self {
+        // SAFETY: Callers must ensure that `ptr` is valid, non-null, for the duration of this
+        // function call and the entire duration when the returned reference exists.
+        unsafe { &*ptr.cast() }
+    }
+}
+
+kernel::define_flags!(
+    I2cFlags(u32),
+    I2C_FUNC_I2C = bindings::I2C_FUNC_I2C,
+    I2C_FUNC_10BIT_ADDR = bindings::I2C_FUNC_10BIT_ADDR,
+    I2C_FUNC_PROTOCOL_MANGLING = bindings::I2C_FUNC_PROTOCOL_MANGLING,
+    I2C_FUNC_SMBUS_PEC = bindings::I2C_FUNC_SMBUS_PEC,
+    I2C_FUNC_NOSTART = bindings::I2C_FUNC_NOSTART,
+    I2C_FUNC_SLAVE = bindings::I2C_FUNC_SLAVE,
+    I2C_FUNC_SMBUS_BLOCK_PROC_CALL = bindings::I2C_FUNC_SMBUS_BLOCK_PROC_CALL,
+    I2C_FUNC_SMBUS_QUICK = bindings::I2C_FUNC_SMBUS_QUICK,
+    I2C_FUNC_SMBUS_READ_BYTE = bindings::I2C_FUNC_SMBUS_READ_BYTE,
+    I2C_FUNC_SMBUS_WRITE_BYTE = bindings::I2C_FUNC_SMBUS_WRITE_BYTE,
+    I2C_FUNC_SMBUS_READ_BYTE_DATA = bindings::I2C_FUNC_SMBUS_READ_BYTE_DATA,
+    I2C_FUNC_SMBUS_WRITE_BYTE_DATA = bindings::I2C_FUNC_SMBUS_WRITE_BYTE_DATA,
+    I2C_FUNC_SMBUS_READ_WORD_DATA = bindings::I2C_FUNC_SMBUS_READ_WORD_DATA,
+    I2C_FUNC_SMBUS_WRITE_WORD_DATA = bindings::I2C_FUNC_SMBUS_WRITE_WORD_DATA,
+    I2C_FUNC_SMBUS_PROC_CALL = bindings::I2C_FUNC_SMBUS_PROC_CALL,
+    I2C_FUNC_SMBUS_READ_BLOCK_DATA = bindings::I2C_FUNC_SMBUS_READ_BLOCK_DATA,
+    I2C_FUNC_SMBUS_WRITE_BLOCK_DATA = bindings::I2C_FUNC_SMBUS_WRITE_BLOCK_DATA,
+    I2C_FUNC_SMBUS_READ_I2C_BLOCK = bindings::I2C_FUNC_SMBUS_READ_I2C_BLOCK,
+    I2C_FUNC_SMBUS_WRITE_I2C_BLOCK = bindings::I2C_FUNC_SMBUS_WRITE_I2C_BLOCK,
+    I2C_FUNC_SMBUS_HOST_NOTIFY = bindings::I2C_FUNC_SMBUS_HOST_NOTIFY,
+);
+
+/// Trait implemented by the private data of an i2c adapter.
+#[vtable]
+pub trait I2cAlgorithm {
+    /// Handler for transfer a given number of messages defined by the msgs array
+    /// via the specified adapter.
+    fn xfer(_adap: &I2cAdapter<Normal>, _msgs: &mut [I2cMsg]) -> Result {
+        build_error!(VTABLE_DEFAULT_ERROR)
+    }
+
+    /// Same as @xfer. Yet, only using atomic context so e.g. PMICs
+    /// can be accessed very late before shutdown. Optional.
+    fn xfer_atomic(_adap: &I2cAdapter<Normal>, _msgs: &mut [I2cMsg]) -> Result {
+        build_error!(VTABLE_DEFAULT_ERROR)
+    }
+
+    /// Issue SMBus transactions to the given I2C adapter. If this
+    /// is not present, then the bus layer will try and convert the SMBus calls
+    /// into I2C transfers instead.
+    fn smbus_xfer(
+        _adap: &I2cAdapter<Normal>,
+        _addr: u16,
+        _flags: u16,
+        _read_write: u8,
+        _command: u8,
+        _size: usize,
+        _data: &I2cSmbusData,
+    ) -> Result {
+        build_error!(VTABLE_DEFAULT_ERROR)
+    }
+
+    /// Same as @smbus_xfer. Yet, only using atomic context
+    /// so e.g. PMICs can be accessed very late before shutdown. Optional.
+    fn smbus_xfer_atomic(
+        _adap: &I2cAdapter<Normal>,
+        _addr: u16,
+        _flags: u16,
+        _read_write: u8,
+        _command: u8,
+        _size: usize,
+        _data: &I2cSmbusData,
+    ) -> Result {
+        build_error!(VTABLE_DEFAULT_ERROR)
+    }
+
+    /// Return the flags that this algorithm/adapter pair supports
+    /// from the ``I2C_FUNC_*`` flags.
+    fn functionality(_adap: &I2cAdapter<Normal>) -> I2cFlags {
+        build_error!(VTABLE_DEFAULT_ERROR)
+    }
+}
+
+/// A vtable for the I2C xfer operations of a Rust i2c adapter.
+pub struct I2cAlgorithmVTable<T: I2cAlgorithm>(PhantomData<T>);
+
+impl<T: I2cAlgorithm> I2cAlgorithmVTable<T> {
+    /// # Safety
+    ///
+    /// `adap` must be a valid pointer to `struct i2c_adapter` that is associated with a
+    /// `i2c::adapter::Registration<T>`.
+    /// `msgs` must be a valid pointer to `struct i2c_msg` for reading/writing.
+    unsafe extern "C" fn xfer(
+        adap: *mut i2c_adapter,
+        msgs: *mut i2c_msg,
+        num: ffi::c_int,
+    ) -> ffi::c_int {
+        let num = match usize::try_from(num) {
+            Ok(num) => num,
+            Err(_err) => return EINVAL.to_errno(),
+        };
+
+        let msg_slice = I2cMsg::from_raw_parts_mut(msgs, num);
+
+        match T::xfer(I2cAdapter::from_raw(adap), msg_slice) {
+            Ok(()) => 0,
+            Err(err) => err.to_errno(),
+        }
+    }
+
+    /// # Safety
+    ///
+    /// `adap` must be a valid pointer to `struct i2c_adapter` that is associated with a
+    /// `i2c::adapter::Registration<T>`.
+    /// `msgs` must be a valid pointer to `struct i2c_msg` for reading/writing.
+    unsafe extern "C" fn xfer_atomic(
+        adap: *mut i2c_adapter,
+        msgs: *mut i2c_msg,
+        num: ffi::c_int,
+    ) -> ffi::c_int {
+        let num = match usize::try_from(num) {
+            Ok(num) => num,
+            Err(_err) => return EINVAL.to_errno(),
+        };
+
+        let msg_slice = I2cMsg::from_raw_parts_mut(msgs, num);
+
+        match T::xfer_atomic(I2cAdapter::from_raw(adap), msg_slice) {
+            Ok(()) => 0,
+            Err(err) => err.to_errno(),
+        }
+    }
+
+    /// # Safety
+    ///
+    /// `adap` must be a valid pointer to `struct i2c_adapter` that is associated with a
+    /// `i2c::adapter::Registration<T>`.
+    /// `data` must be a valid pointer to `struct i2c_smbus_data` for reading/writing.
+    unsafe extern "C" fn smbus_xfer(
+        adap: *mut i2c_adapter,
+        addr: u16_,
+        flags: ffi::c_ushort,
+        read_write: ffi::c_char,
+        command: u8_,
+        size: ffi::c_int,
+        data: *mut i2c_smbus_data,
+    ) -> ffi::c_int {
+        let size = match usize::try_from(size) {
+            Ok(size) => size,
+            Err(_err) => return EINVAL.to_errno(),
+        };
+
+        let data = I2cSmbusData::from_raw(data);
+
+        match T::smbus_xfer(
+            I2cAdapter::from_raw(adap),
+            addr,
+            flags,
+            read_write,
+            command,
+            size,
+            data,
+        ) {
+            Ok(()) => 0,
+            Err(err) => err.to_errno(),
+        }
+    }
+
+    /// # Safety
+    ///
+    /// `adap` must be a valid pointer to `struct i2c_adapter` that is associated with a
+    /// `i2c::adapter::Registration<T>`.
+    /// `data` must be a valid pointer to `struct i2c_smbus_data` for reading/writing.
+    unsafe extern "C" fn smbus_xfer_atomic(
+        adap: *mut i2c_adapter,
+        addr: u16_,
+        flags: ffi::c_ushort,
+        read_write: ffi::c_char,
+        command: u8_,
+        size: ffi::c_int,
+        data: *mut i2c_smbus_data,
+    ) -> ffi::c_int {
+        let size = match usize::try_from(size) {
+            Ok(size) => size,
+            Err(_err) => return EINVAL.to_errno(),
+        };
+
+        let data = I2cSmbusData::from_raw(data);
+
+        match T::smbus_xfer_atomic(
+            I2cAdapter::from_raw(adap),
+            addr,
+            flags,
+            read_write,
+            command,
+            size,
+            data,
+        ) {
+            Ok(()) => 0,
+            Err(err) => err.to_errno(),
+        }
+    }
+
+    /// # Safety
+    ///
+    /// `adap` must be a valid pointer to `struct i2c_adapter` that is associated with a
+    /// `I2cAdapterRegistration<T>`.
+    unsafe extern "C" fn functionality(adap: *mut i2c_adapter) -> u32_ {
+        T::functionality(I2cAdapter::from_raw(adap)).as_raw()
+    }
+
+    const VTABLE: bindings::i2c_algorithm = bindings::i2c_algorithm {
+        xfer: if T::HAS_XFER { Some(Self::xfer) } else { None },
+        xfer_atomic: if T::HAS_XFER_ATOMIC {
+            Some(Self::xfer_atomic)
+        } else {
+            None
+        },
+        smbus_xfer: if T::HAS_SMBUS_XFER {
+            Some(Self::smbus_xfer)
+        } else {
+            None
+        },
+        smbus_xfer_atomic: if T::HAS_SMBUS_XFER_ATOMIC {
+            Some(Self::smbus_xfer_atomic)
+        } else {
+            None
+        },
+        functionality: if T::HAS_FUNCTIONALITY {
+            Some(Self::functionality)
+        } else {
+            None
+        },
+    };
+
+    pub(super) const fn build() -> &'static bindings::i2c_algorithm {
+        &Self::VTABLE
+    }
+}

-- 
2.43.0



^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 4/5] rust: i2c: add I2C wrappers
  2026-01-31 14:12 [PATCH 0/5] rust: extend I2C functionality Igor Korotin via B4 Relay
                   ` (2 preceding siblings ...)
  2026-01-31 14:12 ` [PATCH 3/5] rust: i2c: Add I2C Adapter registration abstractions Igor Korotin via B4 Relay
@ 2026-01-31 14:12 ` Igor Korotin via B4 Relay
  2026-01-31 14:28   ` Markus Probst
  2026-01-31 14:12 ` [PATCH 5/5] samples: rust: add Rust I2C adapter registration sample Igor Korotin via B4 Relay
  2026-01-31 14:26 ` [PATCH 0/5] rust: extend I2C functionality Danilo Krummrich
  5 siblings, 1 reply; 16+ messages in thread
From: Igor Korotin via B4 Relay @ 2026-01-31 14:12 UTC (permalink / raw)
  To: Danilo Krummrich, Daniel Almeida, Miguel Ojeda, Boqun Feng,
	Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
	Alice Ryhl, Trevor Gross, Wolfram Sang
  Cc: linux-kernel, rust-for-linux, linux-i2c, markus.probst,
	Igor Korotin

From: Igor Korotin <igor.korotin.linux@gmail.com>

Add safe Rust wrappers for common I2C/SMbus operations on I2C clients.

These wrappers provide safe abstractions over low-level C API calls:
- SMbus byte operations: read/write byte and byte data
- SMbus word operations: read/write word data
- SMbus block operations: read/write I2C block data
- Master I2C operations: master receive and master send

All operations include proper error handling through the Result type.

Signed-off-by: Igor Korotin <igor.korotin.linux@gmail.com>
---
 rust/helpers/helpers.c    |  1 +
 rust/helpers/i2c.c        | 15 ++++++++
 rust/kernel/i2c/client.rs | 92 +++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 108 insertions(+)

diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c
index 79c72762ad9c..6f6b0ff8713a 100644
--- a/rust/helpers/helpers.c
+++ b/rust/helpers/helpers.c
@@ -29,6 +29,7 @@
 #include "err.c"
 #include "irq.c"
 #include "fs.c"
+#include "i2c.c"
 #include "io.c"
 #include "jump_label.c"
 #include "kunit.c"
diff --git a/rust/helpers/i2c.c b/rust/helpers/i2c.c
new file mode 100644
index 000000000000..05bbd26eaaf8
--- /dev/null
+++ b/rust/helpers/i2c.c
@@ -0,0 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/i2c.h>
+
+__rust_helper int rust_helper_i2c_master_recv(const struct i2c_client *client,
+				  char *buf, int count)
+{
+	return i2c_master_recv(client, buf, count);
+}
+
+__rust_helper int rust_helper_i2c_master_send(const struct i2c_client *client,
+				  const char *buf, int count)
+{
+	return i2c_master_send(client, buf, count);
+}
diff --git a/rust/kernel/i2c/client.rs b/rust/kernel/i2c/client.rs
index a597793ff038..76fa01cb0351 100644
--- a/rust/kernel/i2c/client.rs
+++ b/rust/kernel/i2c/client.rs
@@ -140,6 +140,98 @@ unsafe impl Send for I2cClient {}
 // (i.e. `I2cClient<Normal>) are thread safe.
 unsafe impl Sync for I2cClient {}
 
+impl I2cClient<device::Normal> {
+    /// The C `i2c_smbus_read_byte` function wrapper for SMbus "read byte" protocol
+    pub fn i2c_smbus_read_byte(&self) -> Result<u8> {
+        // SAFETY: self is a valid reference to a I2cClient<Normal>
+        let result = unsafe { bindings::i2c_smbus_read_byte(self.as_raw()) };
+        to_result(result)?;
+        Ok(result as u8)
+    }
+
+    /// The C `i2c_smbus_write_byte` function wrapper for SMbus "write byte" protocol
+    pub fn i2c_smbus_write_byte(&self, value: u8) -> Result {
+        // SAFETY: self is a valid reference to a I2cClient<Normal>
+        to_result(unsafe { bindings::i2c_smbus_write_byte(self.as_raw(), value) })
+    }
+
+    /// The C `i2c_smbus_read_byte_data` function wrapper for SMbus "read byte" protocol
+    pub fn i2c_smbus_read_byte_data(&self, command: u8) -> Result<u8> {
+        // SAFETY: self is a valid reference to a I2cClient<Normal>
+        let result = unsafe { bindings::i2c_smbus_read_byte_data(self.as_raw(), command) };
+        to_result(result)?;
+        Ok(result as u8)
+    }
+
+    /// The C `i2c_smbus_write_byte_data` function wrapper for SMbus "write byte" protocol
+    pub fn i2c_smbus_write_byte_data(&self, command: u8, value: u8) -> Result {
+        // SAFETY: self is a valid reference to a I2cClient<Normal>
+        to_result(unsafe { bindings::i2c_smbus_write_byte_data(self.as_raw(), command, value) })
+    }
+
+    /// The C `i2c_smbus_read_word_data` function wrapper for SMbus "read word" protocol
+    pub fn i2c_smbus_read_word_data(&self, command: u8) -> Result<u16> {
+        // SAFETY: self is a valid reference to a I2cClient<Normal>
+        let result = unsafe { bindings::i2c_smbus_read_word_data(self.as_raw(), command) };
+        to_result(result)?;
+        Ok(result as u16)
+    }
+
+    /// The C `i2c_smbus_write_word_data` function wrapper for SMbus "write word" protocol
+    pub fn i2c_smbus_write_word_data(&self, command: u8, value: u16) -> Result {
+        // SAFETY: self is a valid reference to a I2cClient<Normal>
+        to_result(unsafe { bindings::i2c_smbus_write_word_data(self.as_raw(), command, value) })
+    }
+
+    /// The C `i2c_smbus_read_i2c_block_data` function wrapper for SMbus "block read" protocol
+    pub fn i2c_smbus_read_i2c_block_data(&self, command: u8, values: &mut [u8]) -> Result<usize> {
+        // SAFETY: self is a valid reference to a I2cClient<Normal>
+        let result = unsafe {
+            bindings::i2c_smbus_read_i2c_block_data(
+                self.as_raw(),
+                command,
+                values.len() as u8,
+                values.as_mut_ptr(),
+            )
+        };
+        to_result(result)?;
+        Ok(result as usize)
+    }
+
+    /// The C `i2c_smbus_write_i2c_block_data` function wrapper for SMbus "block write" protocol
+    pub fn i2c_smbus_write_i2c_block_data(&self, command: u8, values: &[u8]) -> Result {
+        // SAFETY: self is a valid reference to a I2cClient<Normal>
+        to_result(unsafe {
+            bindings::i2c_smbus_write_i2c_block_data(
+                self.as_raw(),
+                command,
+                values.len() as u8,
+                values.as_ptr(),
+            )
+        })
+    }
+
+    /// The C `i2c_master_recv` function wrapper to issue a single I2C message in master
+    /// receive mode
+    pub fn i2c_master_recv(&self, buf: &mut [u8]) -> Result<usize> {
+        // SAFETY: self is a valid reference to a I2cClient<Normal>
+        let result =
+            unsafe { bindings::i2c_master_recv(self.as_raw(), buf.as_mut_ptr(), buf.len() as i32) };
+        to_result(result)?;
+        Ok(result as usize)
+    }
+
+    /// The C `i2c_master_send` function wrapper to issue a single I2C message in master
+    /// transmit mode
+    pub fn i2c_master_send(&self, buf: &[u8]) -> Result<usize> {
+        // SAFETY: self is a valid reference to a I2cClient<Normal>
+        let result =
+            unsafe { bindings::i2c_master_send(self.as_raw(), buf.as_ptr(), buf.len() as i32) };
+        to_result(result)?;
+        Ok(result as usize)
+    }
+}
+
 /// The registration of an i2c client device.
 ///
 /// This type represents the registration of a [`struct i2c_client`]. When an instance of this

-- 
2.43.0



^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 5/5] samples: rust: add Rust I2C adapter registration sample
  2026-01-31 14:12 [PATCH 0/5] rust: extend I2C functionality Igor Korotin via B4 Relay
                   ` (3 preceding siblings ...)
  2026-01-31 14:12 ` [PATCH 4/5] rust: i2c: add I2C wrappers Igor Korotin via B4 Relay
@ 2026-01-31 14:12 ` Igor Korotin via B4 Relay
  2026-01-31 14:26 ` [PATCH 0/5] rust: extend I2C functionality Danilo Krummrich
  5 siblings, 0 replies; 16+ messages in thread
From: Igor Korotin via B4 Relay @ 2026-01-31 14:12 UTC (permalink / raw)
  To: Danilo Krummrich, Daniel Almeida, Miguel Ojeda, Boqun Feng,
	Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
	Alice Ryhl, Trevor Gross, Wolfram Sang
  Cc: linux-kernel, rust-for-linux, linux-i2c, markus.probst,
	Igor Korotin

From: Igor Korotin <igor.korotin.linux@gmail.com>

Add a new `rust_i2c_adapter` sample, showing how to create a new
i2c adapter using `i2c::adapter::Registration`

Signed-off-by: Igor Korotin <igor.korotin.linux@gmail.com>
---
 MAINTAINERS                      |   3 +-
 samples/rust/Kconfig             |  12 +++
 samples/rust/Makefile            |   1 +
 samples/rust/rust_i2c_adapter.rs | 170 +++++++++++++++++++++++++++++++++++++++
 4 files changed, 184 insertions(+), 2 deletions(-)

diff --git a/MAINTAINERS b/MAINTAINERS
index 60662984176d..bae13f1f9a23 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -11888,8 +11888,7 @@ L:	rust-for-linux@vger.kernel.org
 S:	Maintained
 F:	rust/i2c/*
 F:	rust/kernel/i2c.rs
-F:	samples/rust/rust_driver_i2c.rs
-F:	samples/rust/rust_i2c_client.rs
+F:	samples/rust/rust_*i2c*.rs
 
 I2C SUBSYSTEM HOST DRIVERS
 M:	Andi Shyti <andi.shyti@kernel.org>
diff --git a/samples/rust/Kconfig b/samples/rust/Kconfig
index 3efa51bfc8ef..a5157b50e27c 100644
--- a/samples/rust/Kconfig
+++ b/samples/rust/Kconfig
@@ -107,6 +107,18 @@ config SAMPLE_RUST_I2C_CLIENT
 
 	  If unsure, say N.
 
+config SAMPLE_RUST_I2C_ADAPTER
+	tristate "I2C Adapter Registration"
+	depends on I2C=y
+	help
+	  This option builds the Rust I2C adapter manual creation
+	  sample.
+
+	  To compile this as a module, choose M here:
+	  the module will be called rust_i2c_adapter.
+
+	  If unsure, say N.
+
 config SAMPLE_RUST_DRIVER_PCI
 	tristate "PCI Driver"
 	depends on PCI
diff --git a/samples/rust/Makefile b/samples/rust/Makefile
index f65885d1d62b..7026eb46ae50 100644
--- a/samples/rust/Makefile
+++ b/samples/rust/Makefile
@@ -9,6 +9,7 @@ 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_I2C_ADAPTER)		+= rust_i2c_adapter.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
diff --git a/samples/rust/rust_i2c_adapter.rs b/samples/rust/rust_i2c_adapter.rs
new file mode 100644
index 000000000000..542766e27d6f
--- /dev/null
+++ b/samples/rust/rust_i2c_adapter.rs
@@ -0,0 +1,170 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Rust I2C adapter registration sample.
+//!
+//! An I2C adapter in Rust cannot exist on its own. To register a new I2C adapter,
+//! it must be bound to a parent device. In this sample driver, a platform device
+//! is used as the parent.
+
+//! ACPI match table test
+//!
+//! This demonstrates how to test an ACPI-based Rust I2C adapter registration driver
+//! using QEMU with a custom SSDT.
+//!
+//! Steps:
+//!
+//! 1. **Create an SSDT source file** (`ssdt.dsl`) with the following content:
+//!
+//!     ```asl
+//!     DefinitionBlock ("", "SSDT", 2, "TEST", "VIRTACPI", 0x00000001)
+//!     {
+//!         Scope (\_SB)
+//!         {
+//!             Device (T432)
+//!             {
+//!                 Name (_HID, "LNUXBEEF")  // ACPI hardware ID to match
+//!                 Name (_UID, 1)
+//!                 Name (_STA, 0x0F)        // Device present, enabled
+//!                 Name (_CRS, ResourceTemplate ()
+//!                 {
+//!                     Memory32Fixed (ReadWrite, 0xFED00000, 0x1000)
+//!                 })
+//!             }
+//!         }
+//!     }
+//!     ```
+//!
+//! 2. **Compile the table**:
+//!
+//!     ```sh
+//!     iasl -tc ssdt.dsl
+//!     ```
+//!
+//!     This generates `ssdt.aml`
+//!
+//! 3. **Run QEMU** with the compiled AML file:
+//!
+//!     ```sh
+//!     qemu-system-x86_64 -m 512M \
+//!         -enable-kvm \
+//!         -kernel path/to/bzImage \
+//!         -append "root=/dev/sda console=ttyS0" \
+//!         -hda rootfs.img \
+//!         -serial stdio \
+//!         -acpitable file=ssdt.aml
+//!     ```
+//!
+//!     Requirements:
+//!     - The `rust_i2c_adapter` must be present either:
+//!         - built directly into the kernel (`bzImage`), or
+//!         - available as a `.ko` file and loadable from `rootfs.img`
+//!
+//! 4. **Verify it worked** by checking `dmesg`:
+//!
+//!     ```
+//!     rust_i2c_adapter LNUXBEEF:00: Probe Rust I2C Adapter registration sample.
+//!     ```
+//!
+
+use kernel::{
+    acpi,
+    device,
+    devres::Devres,
+    i2c::adapter::{
+        I2cAdapter,
+        I2cAdapterOptions,
+        Registration, //
+    },
+    i2c::algo::{
+        flags, //
+        I2cAlgorithm,
+        I2cFlags,
+        I2cSmbusData,
+    },
+    platform,
+    prelude::*,
+    sync::aref::ARef, //
+};
+
+#[pin_data]
+struct SampleDriver {
+    parent_dev: ARef<platform::Device>,
+    #[pin]
+    i2c_adap: Devres<Registration<SampleDevice>>,
+}
+
+struct SampleDevice {}
+
+#[vtable]
+impl I2cAlgorithm for SampleDevice {
+    fn smbus_xfer(
+        _adap: &I2cAdapter<device::Normal>,
+        _addr: u16,
+        _flags: u16,
+        _read_write: u8,
+        _command: u8,
+        _size: usize,
+        _data: &I2cSmbusData,
+    ) -> Result {
+        dev_info!(_adap.as_ref(), "SMBus xfer: request handler called");
+        Ok(())
+    }
+
+    fn functionality(_adap: &I2cAdapter<device::Normal>) -> I2cFlags {
+        flags::I2C_FUNC_SMBUS_READ_BYTE | flags::I2C_FUNC_SMBUS_WRITE_BYTE
+    }
+}
+
+kernel::acpi_device_table!(
+    ACPI_TABLE,
+    MODULE_ACPI_TABLE,
+    <SampleDriver as platform::Driver>::IdInfo,
+    [(acpi::DeviceId::new(c"LNUXBEEF"), ())]
+);
+
+impl platform::Driver for SampleDriver {
+    type IdInfo = ();
+
+    const ACPI_ID_TABLE: Option<acpi::IdTable<Self::IdInfo>> = Some(&ACPI_TABLE);
+
+    fn probe(
+        pdev: &platform::Device<device::Core>,
+        _id_info: Option<&Self::IdInfo>,
+    ) -> impl PinInit<Self, Error> {
+        pin_init::pin_init_scope(move || {
+            dev_info!(
+                pdev.as_ref(),
+                "Probe Rust I2C Adapter registration sample.\n"
+            );
+
+            Ok(try_pin_init!(Self {
+                parent_dev: pdev.into(),
+                i2c_adap <- {
+                    let name = I2cAdapterOptions{ name: c"rust_i2c_adapter"};
+                    Registration::register(pdev.as_ref(), name)
+                },
+            }))
+        })
+    }
+
+    fn unbind(pdev: &platform::Device<device::Core>, _this: Pin<&Self>) {
+        dev_info!(
+            pdev.as_ref(),
+            "Unbind start: Rust I2C Adapter registration sample.\n"
+        );
+        // The i2c_adap (Devres<Registration<SampleDevice>>) will be automatically
+        // dropped here, which will call i2c_del_adapter() in its Drop impl
+        dev_info!(
+            pdev.as_ref(),
+            "Unbind complete: Rust I2C Adapter registration sample.\n"
+        );
+    }
+}
+
+kernel::module_platform_driver! {
+    type: SampleDriver,
+    name: "rust_i2c_adapter",
+    authors: ["Igor Korotin"],
+    description: "Sample I2C Adapter registration",
+    license: "GPL",
+}

-- 
2.43.0



^ permalink raw reply related	[flat|nested] 16+ messages in thread

* Re: [PATCH 0/5] rust: extend I2C functionality
  2026-01-31 14:12 [PATCH 0/5] rust: extend I2C functionality Igor Korotin via B4 Relay
                   ` (4 preceding siblings ...)
  2026-01-31 14:12 ` [PATCH 5/5] samples: rust: add Rust I2C adapter registration sample Igor Korotin via B4 Relay
@ 2026-01-31 14:26 ` Danilo Krummrich
  2026-02-08 12:34   ` Igor Korotin
  2026-02-09 11:31   ` Bartosz Golaszewski
  5 siblings, 2 replies; 16+ messages in thread
From: Danilo Krummrich @ 2026-01-31 14:26 UTC (permalink / raw)
  To: Igor Korotin via B4 Relay
  Cc: igor.korotin.linux, Daniel Almeida, Miguel Ojeda, Boqun Feng,
	Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
	Alice Ryhl, Trevor Gross, Wolfram Sang, linux-kernel,
	rust-for-linux, linux-i2c, markus.probst, brgl, gregkh, rafael,
	driver-core

(Cc: Bartosz, Greg, Rafael, driver-core)

On Sat Jan 31, 2026 at 3:12 PM CET, Igor Korotin via B4 Relay wrote:
> This patch series extend the existing I2C functionality with:
> - Abstractions allowing to implement I2C algorithms used by I2C adapters;
> - Abstractions allowing to create and add new I2C adapters;
> - Safe wrappers upon I2C and SMbus transferring C API: read/write
>   bytes/words/byte arrays.
>
> The patch series contains additional new sample driver `rust_i2c_adapter`
> presenting the new functionality. 

The i2c_adapter code on the C side has some lifetime issues, Bartosz looks into
resolving.

My biggest concern is that struct i2c_adapter is a bus device implementation,
but does not use the reference count of the embedded struct device.

Instead, I2C bus drivers embedd the i2c_adapter in their driver specific
structure, which is typically freed in the I2C bus driver's remove() callback.

This violates struct device reference counts.

Until this is fixed, the Rust abstractions should probably work around this, to
subsequent painful rework.

> Patch series depend on 
> message-id: <20260107103511.570525-1-dakr@kernel.org>
>
> Signed-off-by: Igor Korotin <igor.korotin.linux@gmail.com>
> ---
> Igor Korotin (5):
>       rust: i2c: split client and adapter code into separate files
>       rust: bits: add define_flags macro
>       rust: i2c: Add I2C Adapter registration abstractions
>       rust: i2c: add I2C wrappers
>       samples: rust: add Rust I2C adapter registration sample
>
>  MAINTAINERS                      |   4 +-
>  include/linux/i2c.h              |   6 +
>  rust/helpers/helpers.c           |   1 +
>  rust/helpers/i2c.c               |  15 ++
>  rust/kernel/bits.rs              |  57 ++++++++
>  rust/kernel/i2c.rs               | 254 ++-------------------------------
>  rust/kernel/i2c/adapter.rs       | 200 ++++++++++++++++++++++++++
>  rust/kernel/i2c/algo.rs          | 300 +++++++++++++++++++++++++++++++++++++++
>  rust/kernel/i2c/client.rs        | 284 ++++++++++++++++++++++++++++++++++++
>  samples/rust/Kconfig             |  12 ++
>  samples/rust/Makefile            |   1 +
>  samples/rust/rust_driver_i2c.rs  |  10 +-
>  samples/rust/rust_i2c_adapter.rs | 170 ++++++++++++++++++++++
>  samples/rust/rust_i2c_client.rs  |  15 +-
>  14 files changed, 1069 insertions(+), 260 deletions(-)
> ---
> base-commit: a7c013f779530190d0c1e1aa5e7c8a61f0bd479e
> change-id: 20260131-i2c-adapter-d38d330c4c25
> prerequisite-message-id: <20260107103511.570525-1-dakr@kernel.org>
> prerequisite-patch-id: 6e4d28a06d842ddfc7db82f410532a35b8fe317d
> prerequisite-patch-id: 9739295d85b688257fca29be1e1217a05a69c9e7
> prerequisite-patch-id: 78eaed3746a40713f44f13508fcf43653b0a4528
> prerequisite-patch-id: 99f1881bc896896297a903affcfa4f62cab1a307
> prerequisite-patch-id: ea8baa9d648f1178d6f39d140b98bddf3c183f6f
> prerequisite-patch-id: f8a9e049d204bde04e80151a67dfff5ec2633e7a
>
> Best regards,
> -- 
> Igor Korotin <igor.korotin.linux@gmail.com>


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH 4/5] rust: i2c: add I2C wrappers
  2026-01-31 14:12 ` [PATCH 4/5] rust: i2c: add I2C wrappers Igor Korotin via B4 Relay
@ 2026-01-31 14:28   ` Markus Probst
  2026-02-04 16:49     ` Igor Korotin
  0 siblings, 1 reply; 16+ messages in thread
From: Markus Probst @ 2026-01-31 14:28 UTC (permalink / raw)
  To: igor.korotin.linux, Danilo Krummrich, Daniel Almeida,
	Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
	Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
	Wolfram Sang
  Cc: linux-kernel, rust-for-linux, linux-i2c

[-- Attachment #1: Type: text/plain, Size: 1415 bytes --]

On Sat, 2026-01-31 at 14:12 +0000, Igor Korotin via B4 Relay wrote:
> From: Igor Korotin <igor.korotin.linux@gmail.com>
> 
> Add safe Rust wrappers for common I2C/SMbus operations on I2C clients.
> 
> These wrappers provide safe abstractions over low-level C API calls:
> - SMbus byte operations: read/write byte and byte data
> - SMbus word operations: read/write word data
> - SMbus block operations: read/write I2C block data
> - Master I2C operations: master receive and master send
> 
> All operations include proper error handling through the Result type.
> 
> Signed-off-by: Igor Korotin <igor.korotin.linux@gmail.com>
> ---
>  rust/helpers/helpers.c    |  1 +
>  rust/helpers/i2c.c        | 15 ++++++++
>  rust/kernel/i2c/client.rs | 92 +++++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 108 insertions(+)
> 

I think I2CClient should implement the IO [1] trait instead, as
suggested by Danilo [2].

Also I think it is a little odd that read and write is possible, on
I2CClient<Normal> and not I2CClient<Bound>. Shouldn't the assigned
driver have exclusive read and write access to the device?

Thanks
- Markus Probst

[1]
https://git.kernel.org/pub/scm/linux/kernel/git/driver-core/driver-core.git/commit/?h=driver-core-testing&id=121d87b28e1d9061d3aaa156c43a627d3cb5e620
[2]
https://lore.kernel.org/rust-for-linux/DDDS2V0V2NVJ.16ZKXCKUA1HUV@kernel.org/

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 870 bytes --]

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH 4/5] rust: i2c: add I2C wrappers
  2026-01-31 14:28   ` Markus Probst
@ 2026-02-04 16:49     ` Igor Korotin
  2026-02-04 16:59       ` Danilo Krummrich
  2026-02-04 17:03       ` Danilo Krummrich
  0 siblings, 2 replies; 16+ messages in thread
From: Igor Korotin @ 2026-02-04 16:49 UTC (permalink / raw)
  To: Markus Probst, igor.korotin.linux, Danilo Krummrich,
	Daniel Almeida, Miguel Ojeda, Boqun Feng, Gary Guo,
	Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
	Trevor Gross, Wolfram Sang
  Cc: linux-kernel, rust-for-linux, linux-i2c

Hello Markus

On 1/31/2026 2:28 PM, Markus Probst wrote:
> I think I2CClient should implement the IO [1] trait instead, as
> suggested by Danilo [2].

I'm not sure it is appropriate to use IO and register! here. I2C devices
are different. Not all of them use register like access. For example 
EEPROM I2C devices allow random read/write operations inside their 
address space. After all I2C doesn't implement the same way of accessing 
its memory space as for example PCI devices.

> Also I think it is a little odd that read and write is possible, on
> I2CClient<Normal> and not I2CClient<Bound>. Shouldn't the assigned
> driver have exclusive read and write access to the device?

`Bound` can be safely dereferenced to `Normal`. Since `Normal` represents
the minimal required typestate for device operations, any API that works
with `Normal` automatically works with `Bound` or `Core` as well. Requiring
`Bound` would unnecessarily restrict the API and force duplication or 
unsafe casts.

> Thanks
> - Markus Probst
> 
> [1]
> https://git.kernel.org/pub/scm/linux/kernel/git/driver-core/driver-core.git/commit/?h=driver-core-testing&id=121d87b28e1d9061d3aaa156c43a627d3cb5e620
> [2]
> https://lore.kernel.org/rust-for-linux/DDDS2V0V2NVJ.16ZKXCKUA1HUV@kernel.org/
Cheers
Igor

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH 4/5] rust: i2c: add I2C wrappers
  2026-02-04 16:49     ` Igor Korotin
@ 2026-02-04 16:59       ` Danilo Krummrich
  2026-02-08 12:44         ` Igor Korotin
  2026-02-04 17:03       ` Danilo Krummrich
  1 sibling, 1 reply; 16+ messages in thread
From: Danilo Krummrich @ 2026-02-04 16:59 UTC (permalink / raw)
  To: Igor Korotin
  Cc: Markus Probst, igor.korotin.linux, Daniel Almeida, Miguel Ojeda,
	Boqun Feng, Gary Guo, Björn Roy Baron, Benno Lossin,
	Andreas Hindborg, Alice Ryhl, Trevor Gross, Wolfram Sang,
	linux-kernel, rust-for-linux, linux-i2c

On Wed Feb 4, 2026 at 5:49 PM CET, Igor Korotin wrote:
> I'm not sure it is appropriate to use IO and register! here. I2C devices
> are different. Not all of them use register like access. For example 
> EEPROM I2C devices allow random read/write operations inside their 
> address space. After all I2C doesn't implement the same way of accessing 
> its memory space as for example PCI devices.

Conceptually, it is not different, it is some device memory connected through
some bus.

Memory mapped I/O allows "random read/write operations" as well, see
memcpy_fromio() and memcpy_toio().

The same thing is true for DMA memory. While it's not owned by the device, it's
still shared with a device.

Gary just started working on a generic IoView<'io, T> type which will serve as a
general abstraction to interact with any kind of memory that is shared between a
device and the CPU [1].

So, for I2C this would mean that if you have register like accesses you can use
the register!() abstractions. If you want random read/write operations, you can
use IoView to copy from / to system memory or even to a user buffer directly.

Of course you can also still use Io::read32() and friends.

[1] https://rust-for-linux.zulipchat.com/#narrow/channel/288089-General/topic/Generic.20I.2FO.20backends/with/571786051

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH 4/5] rust: i2c: add I2C wrappers
  2026-02-04 16:49     ` Igor Korotin
  2026-02-04 16:59       ` Danilo Krummrich
@ 2026-02-04 17:03       ` Danilo Krummrich
  1 sibling, 0 replies; 16+ messages in thread
From: Danilo Krummrich @ 2026-02-04 17:03 UTC (permalink / raw)
  To: Igor Korotin
  Cc: Markus Probst, igor.korotin.linux, Daniel Almeida, Miguel Ojeda,
	Boqun Feng, Gary Guo, Björn Roy Baron, Benno Lossin,
	Andreas Hindborg, Alice Ryhl, Trevor Gross, Wolfram Sang,
	linux-kernel, rust-for-linux, linux-i2c

On Wed Feb 4, 2026 at 5:49 PM CET, Igor Korotin wrote:
> `Bound` can be safely dereferenced to `Normal`. Since `Normal` represents
> the minimal required typestate for device operations, any API that works
> with `Normal` automatically works with `Bound` or `Core` as well. Requiring
> `Bound` would unnecessarily restrict the API and force duplication or 
> unsafe casts.

We cannot allow drivers to retain access to device resources of unbound devices.
To prevent that we never give out a pci::Bar or IoMem instance without a Devres
container.

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH 0/5] rust: extend I2C functionality
  2026-01-31 14:26 ` [PATCH 0/5] rust: extend I2C functionality Danilo Krummrich
@ 2026-02-08 12:34   ` Igor Korotin
  2026-02-08 17:07     ` Danilo Krummrich
  2026-02-09 11:31   ` Bartosz Golaszewski
  1 sibling, 1 reply; 16+ messages in thread
From: Igor Korotin @ 2026-02-08 12:34 UTC (permalink / raw)
  To: Danilo Krummrich, Igor Korotin via B4 Relay
  Cc: igor.korotin.linux, Daniel Almeida, Miguel Ojeda, Boqun Feng,
	Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
	Alice Ryhl, Trevor Gross, Wolfram Sang, linux-kernel,
	rust-for-linux, linux-i2c, markus.probst, brgl, gregkh, rafael,
	driver-core

Hello Danilo

On 1/31/2026 2:26 PM, Danilo Krummrich wrote:
> (Cc: Bartosz, Greg, Rafael, driver-core)
> 
> On Sat Jan 31, 2026 at 3:12 PM CET, Igor Korotin via B4 Relay wrote:
>> This patch series extend the existing I2C functionality with:
>> - Abstractions allowing to implement I2C algorithms used by I2C adapters;
>> - Abstractions allowing to create and add new I2C adapters;
>> - Safe wrappers upon I2C and SMbus transferring C API: read/write
>>    bytes/words/byte arrays.
>>
>> The patch series contains additional new sample driver `rust_i2c_adapter`
>> presenting the new functionality.
> 
> The i2c_adapter code on the C side has some lifetime issues, Bartosz looks into
> resolving.
> 
> My biggest concern is that struct i2c_adapter is a bus device implementation,
> but does not use the reference count of the embedded struct device.
> 
> Instead, I2C bus drivers embedd the i2c_adapter in their driver specific
> structure, which is typically freed in the I2C bus driver's remove() callback.
> 
> This violates struct device reference counts.
> 
> Until this is fixed, the Rust abstractions should probably work around this, to
> subsequent painful rework.

just to clarify what is currently implemented on the Rust side.

The Rust I2cAdapter is required to be bound to a parent Device<Bound>. 
The implementation keeps a reference to the embedded struct device 
inside struct i2c_adapter for the entire lifetime of the Rust adapter, 
so its lifetime is tied to the device model.

I am not entirely sure which specific lifetime or refcounting scenario 
you are referring to. If you have a concrete case in mind where this 
would still be problematic, could you please point it out?

Cheers
Igor




^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH 4/5] rust: i2c: add I2C wrappers
  2026-02-04 16:59       ` Danilo Krummrich
@ 2026-02-08 12:44         ` Igor Korotin
  0 siblings, 0 replies; 16+ messages in thread
From: Igor Korotin @ 2026-02-08 12:44 UTC (permalink / raw)
  To: Danilo Krummrich
  Cc: Markus Probst, igor.korotin.linux, Daniel Almeida, Miguel Ojeda,
	Boqun Feng, Gary Guo, Björn Roy Baron, Benno Lossin,
	Andreas Hindborg, Alice Ryhl, Trevor Gross, Wolfram Sang,
	linux-kernel, rust-for-linux, linux-i2c

Hello Danilo

On 2/4/2026 4:59 PM, Danilo Krummrich wrote:
> On Wed Feb 4, 2026 at 5:49 PM CET, Igor Korotin wrote:
>> I'm not sure it is appropriate to use IO and register! here. I2C devices
>> are different. Not all of them use register like access. For example
>> EEPROM I2C devices allow random read/write operations inside their
>> address space. After all I2C doesn't implement the same way of accessing
>> its memory space as for example PCI devices.
> 
> Conceptually, it is not different, it is some device memory connected through
> some bus.
> 
> Memory mapped I/O allows "random read/write operations" as well, see
> memcpy_fromio() and memcpy_toio().
> 
> The same thing is true for DMA memory. While it's not owned by the device, it's
> still shared with a device.
> 
> Gary just started working on a generic IoView<'io, T> type which will serve as a
> general abstraction to interact with any kind of memory that is shared between a
> device and the CPU [1].
> 
> So, for I2C this would mean that if you have register like accesses you can use
> the register!() abstractions. If you want random read/write operations, you can
> use IoView to copy from / to system memory or even to a user buffer directly.
> 
> Of course you can also still use Io::read32() and friends.
> 
> [1] https://rust-for-linux.zulipchat.com/#narrow/channel/288089-General/topic/Generic.20I.2FO.20backends/with/571786051

thanks, that makes sense.

I don’t have a strong prior opinion on how this should look yet, so I’m 
fine aligning the I2C abstractions with the generic direction you 
describe. Using register!() for register-like accesses and IoView for 
more general read/write patterns seems reasonable.

At this point I’ll treat this as the intended model and adjust the I2C 
side accordingly once IoView is in place and its API settles.

Regards,
Igor

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH 2/5] rust: bits: add define_flags macro
  2026-01-31 14:12 ` [PATCH 2/5] rust: bits: add define_flags macro Igor Korotin via B4 Relay
@ 2026-02-08 13:56   ` Daniel Almeida
  0 siblings, 0 replies; 16+ messages in thread
From: Daniel Almeida @ 2026-02-08 13:56 UTC (permalink / raw)
  To: igor.korotin.linux
  Cc: Danilo Krummrich, Miguel Ojeda, Boqun Feng, Gary Guo,
	Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
	Trevor Gross, Wolfram Sang, linux-kernel, rust-for-linux,
	linux-i2c, markus.probst

Igor,

> On 31 Jan 2026, at 11:12, Igor Korotin via B4 Relay <devnull+igor.korotin.linux.gmail.com@kernel.org> wrote:
> 
> From: Igor Korotin <igor.korotin.linux@gmail.com>
> 
> introduce define_flags macro that incorporates BitOr BitAnd and Not
> implementations.
> 
> Signed-off-by: Igor Korotin <igor.korotin.linux@gmail.com>
> ---
> rust/kernel/bits.rs | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 57 insertions(+)
> 
> diff --git a/rust/kernel/bits.rs b/rust/kernel/bits.rs
> index 553d50265883..6750aef18708 100644
> --- a/rust/kernel/bits.rs
> +++ b/rust/kernel/bits.rs
> @@ -201,3 +201,60 @@ pub const fn [<genmask_ $ty>](range: RangeInclusive<u32>) -> $ty {
>     /// assert_eq!(genmask_u8(0..=7), u8::MAX);
>     /// ```
> );
> +
> +/// defines flags
> +#[macro_export]
> +macro_rules! define_flags {
> +    (
> +        $gen_name:ident($gen_type:ident),
> +        $($variant:ident = $binding:expr,)+
> +    ) => {
> +        /// Flags that can be combined with the operators `|`, `&`, and `!`.
> +        ///
> +        /// Values can be used from the [`flags`] module.
> +        #[derive(Clone, Copy, PartialEq)]
> +        pub struct $gen_name($gen_type);
> +
> +        impl $gen_name {
> +            /// Get the raw representation of this flag.
> +            pub(crate) fn as_raw(self) -> $gen_type {
> +                self.0
> +            }
> +
> +            /// Check whether `flags` is contained in `self`.
> +            pub fn contains(self, flags: $gen_name) -> bool {
> +                (self & flags) == flags
> +            }
> +        }
> +
> +        impl core::ops::BitOr for $gen_name {
> +            type Output = $gen_name;
> +            fn bitor(self, rhs: Self) -> Self::Output {
> +                Self(self.0 | rhs.0)
> +            }
> +        }
> +
> +        impl core::ops::BitAnd for $gen_name {
> +            type Output = $gen_name;
> +            fn bitand(self, rhs: Self) -> Self::Output {
> +                Self(self.0 & rhs.0)
> +            }
> +        }
> +
> +        impl core::ops::Not for $gen_name {
> +            type Output = $gen_name;
> +            fn not(self) -> Self::Output {
> +                Self(!self.0)
> +            }
> +        }
> +
> +        #[allow(missing_docs)]
> +        pub mod flags {
> +            use super::$gen_name;
> +            $(
> +                #[allow(missing_docs)]
> +                pub const $variant: $gen_name = $gen_name($binding as $gen_type);
> +            )+
> +        }
> +    }
> +}
> 
> -- 
> 2.43.0
> 
> 
> 

Please drop this patch in favor of the patch from Filipe Xavier. It does the same thing.

— Daniel

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH 0/5] rust: extend I2C functionality
  2026-02-08 12:34   ` Igor Korotin
@ 2026-02-08 17:07     ` Danilo Krummrich
  0 siblings, 0 replies; 16+ messages in thread
From: Danilo Krummrich @ 2026-02-08 17:07 UTC (permalink / raw)
  To: Igor Korotin
  Cc: Igor Korotin via B4 Relay, igor.korotin.linux, Daniel Almeida,
	Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
	Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
	Wolfram Sang, linux-kernel, rust-for-linux, linux-i2c,
	markus.probst, brgl, gregkh, rafael, driver-core

On Sun Feb 8, 2026 at 1:34 PM CET, Igor Korotin wrote:
> Hello Danilo
>
> On 1/31/2026 2:26 PM, Danilo Krummrich wrote:
>> (Cc: Bartosz, Greg, Rafael, driver-core)
>> 
>> On Sat Jan 31, 2026 at 3:12 PM CET, Igor Korotin via B4 Relay wrote:
>>> This patch series extend the existing I2C functionality with:
>>> - Abstractions allowing to implement I2C algorithms used by I2C adapters;
>>> - Abstractions allowing to create and add new I2C adapters;
>>> - Safe wrappers upon I2C and SMbus transferring C API: read/write
>>>    bytes/words/byte arrays.
>>>
>>> The patch series contains additional new sample driver `rust_i2c_adapter`
>>> presenting the new functionality.
>> 
>> The i2c_adapter code on the C side has some lifetime issues, Bartosz looks into
>> resolving.
>> 
>> My biggest concern is that struct i2c_adapter is a bus device implementation,
>> but does not use the reference count of the embedded struct device.
>> 
>> Instead, I2C bus drivers embedd the i2c_adapter in their driver specific
>> structure, which is typically freed in the I2C bus driver's remove() callback.
>> 
>> This violates struct device reference counts.
>> 
>> Until this is fixed, the Rust abstractions should probably work around this, to
>> subsequent painful rework.
>
> just to clarify what is currently implemented on the Rust side.
>
> The Rust I2cAdapter is required to be bound to a parent Device<Bound>. 
> The implementation keeps a reference to the embedded struct device 
> inside struct i2c_adapter for the entire lifetime of the Rust adapter, 
> so its lifetime is tied to the device model.
>
> I am not entirely sure which specific lifetime or refcounting scenario 
> you are referring to. If you have a concrete case in mind where this 
> would still be problematic, could you please point it out?

The implementation on the C side does not consider the reference count of the
embedded struct device, which is not correct.

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH 0/5] rust: extend I2C functionality
  2026-01-31 14:26 ` [PATCH 0/5] rust: extend I2C functionality Danilo Krummrich
  2026-02-08 12:34   ` Igor Korotin
@ 2026-02-09 11:31   ` Bartosz Golaszewski
  1 sibling, 0 replies; 16+ messages in thread
From: Bartosz Golaszewski @ 2026-02-09 11:31 UTC (permalink / raw)
  To: Danilo Krummrich
  Cc: igor.korotin.linux, Daniel Almeida, Miguel Ojeda, Boqun Feng,
	Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
	Alice Ryhl, Trevor Gross, Wolfram Sang, linux-kernel,
	rust-for-linux, linux-i2c, markus.probst, brgl, gregkh, rafael,
	driver-core, Igor Korotin via B4 Relay

On Sat, 31 Jan 2026 15:26:22 +0100, Danilo Krummrich <dakr@kernel.org> said:
> (Cc: Bartosz, Greg, Rafael, driver-core)
>
> On Sat Jan 31, 2026 at 3:12 PM CET, Igor Korotin via B4 Relay wrote:
>> This patch series extend the existing I2C functionality with:
>> - Abstractions allowing to implement I2C algorithms used by I2C adapters;
>> - Abstractions allowing to create and add new I2C adapters;
>> - Safe wrappers upon I2C and SMbus transferring C API: read/write
>>   bytes/words/byte arrays.
>>
>> The patch series contains additional new sample driver `rust_i2c_adapter`
>> presenting the new functionality.
>
> The i2c_adapter code on the C side has some lifetime issues, Bartosz looks into
> resolving.
>
> My biggest concern is that struct i2c_adapter is a bus device implementation,
> but does not use the reference count of the embedded struct device.
>
> Instead, I2C bus drivers embedd the i2c_adapter in their driver specific
> structure, which is typically freed in the I2C bus driver's remove() callback.
>

Correct.

> This violates struct device reference counts.
>

What's currently done in order to not crash is: we wait forever until all users
release their references[1].

> Until this is fixed, the Rust abstractions should probably work around this, to
> subsequent painful rework.
>

The rework of the C part will be long an painful already. I estimated the work
will take at least a year. More info here[2].

Bart

[1] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/i2c/i2c-core-base.c#n1814
[2] https://lore.kernel.org/all/aWYYZEPX-_1GfQtL@ninjato/

^ permalink raw reply	[flat|nested] 16+ messages in thread

end of thread, other threads:[~2026-02-09 11:31 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-01-31 14:12 [PATCH 0/5] rust: extend I2C functionality Igor Korotin via B4 Relay
2026-01-31 14:12 ` [PATCH 1/5] rust: i2c: split client and adapter code into separate files Igor Korotin via B4 Relay
2026-01-31 14:12 ` [PATCH 2/5] rust: bits: add define_flags macro Igor Korotin via B4 Relay
2026-02-08 13:56   ` Daniel Almeida
2026-01-31 14:12 ` [PATCH 3/5] rust: i2c: Add I2C Adapter registration abstractions Igor Korotin via B4 Relay
2026-01-31 14:12 ` [PATCH 4/5] rust: i2c: add I2C wrappers Igor Korotin via B4 Relay
2026-01-31 14:28   ` Markus Probst
2026-02-04 16:49     ` Igor Korotin
2026-02-04 16:59       ` Danilo Krummrich
2026-02-08 12:44         ` Igor Korotin
2026-02-04 17:03       ` Danilo Krummrich
2026-01-31 14:12 ` [PATCH 5/5] samples: rust: add Rust I2C adapter registration sample Igor Korotin via B4 Relay
2026-01-31 14:26 ` [PATCH 0/5] rust: extend I2C functionality Danilo Krummrich
2026-02-08 12:34   ` Igor Korotin
2026-02-08 17:07     ` Danilo Krummrich
2026-02-09 11:31   ` Bartosz Golaszewski

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox