From nobody Sat Feb 7 18:29:10 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 40FEE23B615; Sat, 31 Jan 2026 14:12:53 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769868773; cv=none; b=Ihg541I43yhSdzw6aaIyODmLtnKnFnT+Bd0i4TR034Afgij7/OxtdX0Tyojz+IDwEAE4Pbz7ePJ87mRzwjesNla11W+qsxOiTW4Tfiru+imChc4njawaYgmt3RdCJ2osV651gj+JvWxg/WXuHwf/rbnjwmzExLnBMPSRGM1hops= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769868773; c=relaxed/simple; bh=q5zH/kd0YnQOH/sGRv4mO1/eKjjswiRBBjuLeoMaatA=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=mDFdu6or55SxN/OxnKbDDmtKmcXr1bftYnXaO8/Uu7+/jutRHz4HECHUUG8d2hwK+FldaU6PtyYPdJuMW3p12Hbyi6a796MfUz5jnZv2D5RZZj5J44seb6da+ptFTsfZjuhIZUpWepdwH2pm0ERwqDoRfNAMom27LCunPBWSrW4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=Xdkfaz78; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="Xdkfaz78" Received: by smtp.kernel.org (Postfix) with ESMTPS id E3458C4CEF1; Sat, 31 Jan 2026 14:12:52 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1769868772; bh=q5zH/kd0YnQOH/sGRv4mO1/eKjjswiRBBjuLeoMaatA=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=Xdkfaz78/vRgSc1AKaiftctjeqUe1MknPLrcv+uM7oVhVOkKULugq7uzPZmASm+MR NYc0zEaAbos+x0lATTksl1V8T8f7laYYoiJtyLywYuQdremS/4n+o6dLcw10TedUV0 MyZ5zWtTEOVSrinn78zttdGtyXQurvLQYo3mhfTOjAJP6pAjBFRCJtDPQcTBuYpMyw 3YEJTr2yJK6eNxGZoyyv995Wf/SM/pMPLKEInw3I0pKmjr01QrpcMPMJsewUdwHKnz vMsoNL30Qn0yG4EBIv7z6u1Nz0OvYs/3QSSpcLUCF9+yQQw0BGwesLr7FqcTqnm3tW 9sBkRlZFZ+Hpw== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id D4E66D7976C; Sat, 31 Jan 2026 14:12:52 +0000 (UTC) From: Igor Korotin via B4 Relay Date: Sat, 31 Jan 2026 14:12:43 +0000 Subject: [PATCH 1/5] rust: i2c: split client and adapter code into separate files Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260131-i2c-adapter-v1-1-5a436e34cd1a@gmail.com> References: <20260131-i2c-adapter-v1-0-5a436e34cd1a@gmail.com> In-Reply-To: <20260131-i2c-adapter-v1-0-5a436e34cd1a@gmail.com> To: Danilo Krummrich , Daniel Almeida , Miguel Ojeda , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Wolfram Sang Cc: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-i2c@vger.kernel.org, markus.probst@posteo.de, Igor Korotin X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1769868772; l=24758; i=igor.korotin.linux@gmail.com; s=20260125; h=from:subject:message-id; bh=CWpKvsEF4lsRAwSev7dxPtlbm4RTfe9OSNpltobCOxc=; b=RAoYsPaqJqAJUfYs3yZPm2bn1sQD/qcMOMPVPMEggGu6R7b3HHKSjSWaBdFUZ/WG3541Msos3 yskRH0nCWzSD9CffxfWb9jOEUXQS8pZYixVwhXUUGFBmKT2nod2BskI X-Developer-Key: i=igor.korotin.linux@gmail.com; a=ed25519; pk=SjZ//wxnynZgJJgTqANyU10A7hdQQdDXLvXIr+zWroE= X-Endpoint-Received: by B4 Relay for igor.korotin.linux@gmail.com/20260125 with auth_id=615 X-Original-From: Igor Korotin Reply-To: igor.korotin.linux@gmail.com From: Igor Korotin 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 --- 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 R: Daniel Almeida 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 =20 -//! I2C Driver subsystem +//! I2C subsystem =20 // 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, // }; =20 -use core::{ - marker::PhantomData, - mem::offset_of, - ptr::{ - from_ref, - NonNull, // - }, // -}; - -use kernel::types::ARef; +pub mod adapter; +pub mod client; =20 /// An I2C device id table. #[repr(transparent)] @@ -291,13 +279,13 @@ macro_rules! module_i2c_driver { /// const ACPI_ID_TABLE: Option> =3D Some(= &ACPI_TABLE); /// /// fn probe( -/// _idev: &i2c::I2cClient, +/// _idev: &i2c::client::I2cClient, /// _id_info: Option<&Self::IdInfo>, /// ) -> impl PinInit { /// Err(ENODEV) /// } /// -/// fn shutdown(_idev: &i2c::I2cClient, this: Pin<&Self>) { +/// fn shutdown(_idev: &i2c::client::I2cClient, this: Pin<&Self>= ) { /// } /// } ///``` @@ -357,230 +345,3 @@ fn unbind(dev: &I2cClient, this: Pin<&S= elf>) { let _ =3D (dev, this); } } - -/// The i2c adapter representation. -/// -/// This structure represents the Rust abstraction for a C `struct i2c_ada= pter`. The -/// implementation abstracts the usage of an existing C `struct i2c_adapte= r` that -/// gets passed from the C side -/// -/// # Invariants -/// -/// A [`I2cAdapter`] instance represents a valid `struct i2c_adapter` crea= ted by the C portion of -/// the kernel. -#[repr(transparent)] -pub struct I2cAdapter( - Opaque, - PhantomData, -); - -impl I2cAdapter { - 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_adapt= er`. - unsafe { (*self.as_raw()).nr } - } - - /// Gets pointer to an `i2c_adapter` by index. - pub fn get(index: i32) -> Result> { - // 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 =3D NonNull::new(unsafe { bindings::i2c_get_adapter(in= dex) }).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::>= ()).into() }) - } -} - -// SAFETY: `I2cAdapter` is a transparent wrapper of a type that doesn't de= pend 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) { - // 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_boa= rd_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 =3D 20; - /// Create a new [`I2cBoardInfo`] for a kernel driver. - #[inline(always)] - pub const fn new(type_: &'static CStr, addr: u16) -> Self { - let src =3D type_.to_bytes_with_nul(); - build_assert!(src.len() <=3D Self::I2C_TYPE_SIZE, "Type exceeds 20= bytes"); - let mut i2c_board_info: bindings::i2c_board_info =3D pin_init::zer= oed(); - let mut i: usize =3D 0; - while i < src.len() { - i2c_board_info.type_[i] =3D src[i]; - i +=3D 1; - } - - i2c_board_info.addr =3D 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_cli= ent`. 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` create= d by the C portion of -/// the kernel. -#[repr(transparent)] -pub struct I2cClient( - Opaque, - PhantomData, -); - -impl I2cClient { - 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 `I2cCl= ient`. -unsafe impl device::AsBusDevice for I2cCl= ient { - const OFFSET: usize =3D offset_of!(bindings::i2c_client, dev); -} - -// SAFETY: `I2cClient` is a transparent wrapper of a type that doesn't dep= end 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) { - // SAFETY: The safety requirements guarantee that the refcount is = non-zero. - unsafe { bindings::put_device(&raw mut (*obj.as_ref().as_raw()).de= v) } - } -} - -impl AsRef> for I2cClient<= Ctx> { - fn as_ref(&self) -> &device::Device { - let raw =3D self.as_raw(); - // SAFETY: By the type invariant of `Self`, `self.as_raw()` is a p= ointer to a valid - // `struct i2c_client`. - let dev =3D unsafe { &raw mut (*raw).dev }; - - // SAFETY: `dev` points to a valid `struct device`. - unsafe { device::Device::from_raw(dev) } - } -} - -impl TryFrom<&device::Device> for &I2cCli= ent { - type Error =3D kernel::error::Error; - - fn try_from(dev: &device::Device) -> Result { - // 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 =3D unsafe { container_of!(dev.as_raw(), bindings::i2c_cl= ient, 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 f= rom any thread. -unsafe impl Send for I2cClient {} - -// SAFETY: `I2cClient` can be shared among threads because all methods of = `I2cClient` -// (i.e. `I2cClient) 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); - -impl Registration { - /// The C `i2c_new_client_device` function wrapper for manual I2C clie= nt creation. - pub fn new<'a>( - i2c_adapter: &I2cAdapter, - i2c_board_info: &I2cBoardInfo, - parent_dev: &'a device::Device, - ) -> impl PinInit, 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 { - // SAFETY: the kernel guarantees that `i2c_new_client_device()` re= turns either a valid - // pointer or NULL. `from_err_ptr` separates errors. Following `No= nNull::new` - // checks for NULL. - let raw_dev =3D from_err_ptr(unsafe { - bindings::i2c_new_client_device(i2c_adapter.as_raw(), i2c_boar= d_info.as_raw()) - })?; - - let dev_ptr =3D 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 throu= gh &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_ada= pter`. The +/// implementation abstracts the usage of an existing C `struct i2c_adapte= r` that +/// gets passed from the C side +/// +/// # Invariants +/// +/// A [`I2cAdapter`] instance represents a valid `struct i2c_adapter` crea= ted by the C portion of +/// the kernel. +#[repr(transparent)] +pub struct I2cAdapter( + Opaque, + PhantomData, +); + +impl I2cAdapter { + 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_adapt= er`. + unsafe { (*self.as_raw()).nr } + } + + /// Gets pointer to an `i2c_adapter` by index. + pub fn get(index: i32) -> Result> { + // 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 =3D NonNull::new(unsafe { bindings::i2c_get_adapter(in= dex) }).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::>= ()).into() }) + } +} + +// SAFETY: `I2cAdapter` is a transparent wrapper of a type that doesn't de= pend 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) { + // 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_boa= rd_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 =3D 20; + /// Create a new [`I2cBoardInfo`] for a kernel driver. + #[inline(always)] + pub const fn new(type_: &'static CStr, addr: u16) -> Self { + let src =3D type_.to_bytes_with_nul(); + build_assert!(src.len() <=3D Self::I2C_TYPE_SIZE, "Type exceeds 20= bytes"); + let mut i2c_board_info: bindings::i2c_board_info =3D pin_init::zer= oed(); + let mut i: usize =3D 0; + while i < src.len() { + i2c_board_info.type_[i] =3D src[i]; + i +=3D 1; + } + + i2c_board_info.addr =3D 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_cli= ent`. 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` create= d by the C portion of +/// the kernel. +#[repr(transparent)] +pub struct I2cClient( + Opaque, + PhantomData, +); + +impl I2cClient { + 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 `I2cCl= ient`. +unsafe impl device::AsBusDevice for I2cCl= ient { + const OFFSET: usize =3D offset_of!(bindings::i2c_client, dev); +} + +// SAFETY: `I2cClient` is a transparent wrapper of a type that doesn't dep= end 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) { + // SAFETY: The safety requirements guarantee that the refcount is = non-zero. + unsafe { bindings::put_device(&raw mut (*obj.as_ref().as_raw()).de= v) } + } +} + +impl AsRef> for I2cClient<= Ctx> { + fn as_ref(&self) -> &device::Device { + let raw =3D self.as_raw(); + // SAFETY: By the type invariant of `Self`, `self.as_raw()` is a p= ointer to a valid + // `struct i2c_client`. + let dev =3D unsafe { &raw mut (*raw).dev }; + + // SAFETY: `dev` points to a valid `struct device`. + unsafe { device::Device::from_raw(dev) } + } +} + +impl TryFrom<&device::Device> for &I2cCli= ent { + type Error =3D kernel::error::Error; + + fn try_from(dev: &device::Device) -> Result { + // 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 =3D unsafe { container_of!(dev.as_raw(), bindings::i2c_cl= ient, 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 f= rom any thread. +unsafe impl Send for I2cClient {} + +// SAFETY: `I2cClient` can be shared among threads because all methods of = `I2cClient` +// (i.e. `I2cClient) 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); + +impl Registration { + /// The C `i2c_new_client_device` function wrapper for manual I2C clie= nt creation. + pub fn new<'a>( + i2c_adapter: &I2cAdapter, + i2c_board_info: &I2cBoardInfo, + parent_dev: &'a device::Device, + ) -> impl PinInit, 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 { + // SAFETY: the kernel guarantees that `i2c_new_client_device()` re= turns either a valid + // pointer or NULL. `from_err_ptr` separates errors. Following `No= nNull::new` + // checks for NULL. + let raw_dev =3D from_err_ptr(unsafe { + bindings::i2c_new_client_device(i2c_adapter.as_raw(), i2c_boar= d_info.as_raw()) + })?; + + let dev_ptr =3D 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 throu= gh &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> =3D Some(&I2C_T= ABLE); const OF_ID_TABLE: Option> =3D Some(&OF_TABL= E); =20 - fn probe( - idev: &i2c::I2cClient, - info: Option<&Self::IdInfo>, - ) -> impl PinInit { + fn probe(idev: &I2cClient, info: Option<&Self::IdInfo>) -> impl = PinInit { let dev =3D idev.as_ref(); =20 dev_info!(dev, "Probe Rust I2C driver sample.\n"); @@ -55,11 +53,11 @@ fn probe( Ok(Self) } =20 - fn shutdown(idev: &i2c::I2cClient, _this: Pin<&Self>) { + fn shutdown(idev: &I2cClient, _this: Pin<&Self>) { dev_info!(idev.as_ref(), "Shutdown Rust I2C driver sample.\n"); } =20 - fn unbind(idev: &i2c::I2cClient, _this: Pin<&Self>) { + fn unbind(idev: &I2cClient, _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, #[pin] - _reg: Devres, + _reg: Devres, } =20 kernel::of_device_table!( @@ -101,8 +105,7 @@ struct SampleDriver { =20 const SAMPLE_I2C_CLIENT_ADDR: u16 =3D 0x30; const SAMPLE_I2C_ADAPTER_INDEX: i32 =3D 0; -const BOARD_INFO: i2c::I2cBoardInfo =3D - i2c::I2cBoardInfo::new(c"rust_driver_i2c", SAMPLE_I2C_CLIENT_ADDR); +const BOARD_INFO: I2cBoardInfo =3D I2cBoardInfo::new(c"rust_driver_i2c", S= AMPLE_I2C_CLIENT_ADDR); =20 impl platform::Driver for SampleDriver { type IdInfo =3D (); @@ -122,9 +125,9 @@ fn probe( parent_dev: pdev.into(), =20 _reg <- { - let adapter =3D i2c::I2cAdapter::get(SAMPLE_I2C_ADAPTER_IN= DEX)?; + let adapter =3D I2cAdapter::get(SAMPLE_I2C_ADAPTER_INDEX)?; =20 - i2c::Registration::new(&adapter, &BOARD_INFO, pdev.as_ref(= )) + Registration::new(&adapter, &BOARD_INFO, pdev.as_ref()) } }) } --=20 2.43.0 From nobody Sat Feb 7 18:29:10 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 4105123BCEE; Sat, 31 Jan 2026 14:12:53 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769868773; cv=none; b=rlOJjM3Ha3BWJL6/mK2G0opurc4pOv8lEo778BgzYKh5V2B0DaYQ8QjCUjAjpSytma8v8+rubwa1LZlAMF/I1hzXJ6FuCgdVTFhHgzzqmLSlbkc2gVNM2124vpcUx+zVZgwkTnOsls7Vv06fLD1kVR291cTUxchEapbkv5qjx0w= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769868773; c=relaxed/simple; bh=079AUMoHViksAspHbH7ag8128A6gK7HVSqZMHGf8rkk=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=XUZPJO7/aO8fBhL43HGWUa0a/6ij/VS4TCCMaE345fudNaJmhoNkYpY7mBrsdccMBRiakZS6eQ4O5o1/stPdjfbwk9IDpwZXWV2Qaefgf0WwAwUIWj/p8Tu6Qdk3+E96u43laCj/MTVjmPPFkddkJZzxlpSQSir8YZB7sQgs+zI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=mC0M0ObH; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="mC0M0ObH" Received: by smtp.kernel.org (Postfix) with ESMTPS id F22ABC19422; Sat, 31 Jan 2026 14:12:52 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1769868773; bh=079AUMoHViksAspHbH7ag8128A6gK7HVSqZMHGf8rkk=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=mC0M0ObHTpYVumFwFJM/9fz5UywXHOZKJDgkIVjMwHyfD96hT12bTdzFvrT6IwU9R /3jabASYniGatpDg0tPYnBhPhFC4cCM8nyAnNvzsVPd/5lKlNT0tfHIYTyUpNqw4BF WoIjY8MxTWej0RC3kVB+LpMB6gC1FEF+Q3CF1OmsVvCUP4a84fcc6tCIScRIXX2tLt qeTPy6NL/ReBCAANuFqlKMw36EVwmGLOiUm9ob4PzDYziiskfb30+lO4X3+ED775ap 7Tw1sTS6F95/DG9R9VUzi6Lpw70hmYvMtAgown7MiQbHGBeLUfx04cMwO7OU+5hXRW bxGKkMGNhcYdQ== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id E2AD6D79774; Sat, 31 Jan 2026 14:12:52 +0000 (UTC) From: Igor Korotin via B4 Relay Date: Sat, 31 Jan 2026 14:12:44 +0000 Subject: [PATCH 2/5] rust: bits: add define_flags macro Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260131-i2c-adapter-v1-2-5a436e34cd1a@gmail.com> References: <20260131-i2c-adapter-v1-0-5a436e34cd1a@gmail.com> In-Reply-To: <20260131-i2c-adapter-v1-0-5a436e34cd1a@gmail.com> To: Danilo Krummrich , Daniel Almeida , Miguel Ojeda , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Wolfram Sang Cc: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-i2c@vger.kernel.org, markus.probst@posteo.de, Igor Korotin X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1769868772; l=2311; i=igor.korotin.linux@gmail.com; s=20260125; h=from:subject:message-id; bh=sn8tyhgAOXgu5sl9ZR6vPMjhDT98q30bKO29ZzpI3ac=; b=yEMAIEqB8thmwTAB8YbWq5ph+DxUCzWFu6c9XkTqwOAGPvGImN/YzflWGoHFNqjokbugMGrXA tOVxoF2cyRiApPT1aIc7zoqf5nUuZTZRBRKaHXq/FBe13LhUl20XPdX X-Developer-Key: i=igor.korotin.linux@gmail.com; a=ed25519; pk=SjZ//wxnynZgJJgTqANyU10A7hdQQdDXLvXIr+zWroE= X-Endpoint-Received: by B4 Relay for igor.korotin.linux@gmail.com/20260125 with auth_id=615 X-Original-From: Igor Korotin Reply-To: igor.korotin.linux@gmail.com From: Igor Korotin introduce define_flags macro that incorporates BitOr BitAnd and Not implementations. Signed-off-by: Igor Korotin --- 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 [](range: RangeInclusive) -> $ty { /// assert_eq!(genmask_u8(0..=3D7), u8::MAX); /// ``` ); + +/// defines flags +#[macro_export] +macro_rules! define_flags { + ( + $gen_name:ident($gen_type:ident), + $($variant:ident =3D $binding:expr,)+ + ) =3D> { + /// 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) =3D=3D flags + } + } + + impl core::ops::BitOr for $gen_name { + type Output =3D $gen_name; + fn bitor(self, rhs: Self) -> Self::Output { + Self(self.0 | rhs.0) + } + } + + impl core::ops::BitAnd for $gen_name { + type Output =3D $gen_name; + fn bitand(self, rhs: Self) -> Self::Output { + Self(self.0 & rhs.0) + } + } + + impl core::ops::Not for $gen_name { + type Output =3D $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 =3D $gen_name($binding as $g= en_type); + )+ + } + } +} --=20 2.43.0 From nobody Sat Feb 7 18:29:10 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 40F6C1DF74F; Sat, 31 Jan 2026 14:12:53 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769868773; cv=none; b=u3/dZ3I8VGlHc2iFPycA0ufqA9dFUGy9+NAsoh/HeJBR6zLmekVyBXzSgMCij4BkEvHeKPgMPMz0b5YPOsFWdy01bYv7LsdaUjdKt03m42V6xmQCYWPGWVnVadBhzvi8wF7ztVuTHRVqCqICh9aaoLodcRlSN5S9y/u7imx0XCQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769868773; c=relaxed/simple; bh=hp23VIwMy10ONa2wQWefma68fpJyhyAlZMhA9o9qHW0=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=cnQqeV4dyf7s7VrWOaMu/IjG3fLIGrVA9mQRDC3jzE1aImNzHn/mHpDdqGeQrSRkWNgrYxwtvf+sZT6SiumSsCjMAiPgX2IiDbIRrV3jDJ2tI7fz/lUX0OBxC6HalYTtgiJUV4cHFYtPvhS20Cso076AtlX18diSBYYsY1I3uKI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=CSwEV1W1; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="CSwEV1W1" Received: by smtp.kernel.org (Postfix) with ESMTPS id 069D2C2BC9E; Sat, 31 Jan 2026 14:12:53 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1769868773; bh=hp23VIwMy10ONa2wQWefma68fpJyhyAlZMhA9o9qHW0=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=CSwEV1W1RVcf1eAfV0QkvqBYzB6YNzu9GHuHHsYxR0KGz/V1AkOMtMO4rYTB0Xzv3 c5OK2TyB8upCDXX3fkuRYE6xU7L0T5DCzMOIDKDqJkEnz/iyb2PG/YVAyhuDN0rja2 Ae8FhLP8C2d7qaWCQ7RcD/qsmskghuk5l+116ugYgBRAck7qVikDrUV7fDZ3NJY3el OnmTCLdqXD7EWJyKoME6ses6+JCMhF/0VL8vhM4rzNR6ohHBz9XiZwnFWXTeW80y+n GrNroMFx1ZP2CpMHAM04sSwmRJ76bWWfELATDEK8UfZDeMcJUmRzw5DXeI6WjmLAgp PL/XLKGpFXbcg== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id F02E4D79773; Sat, 31 Jan 2026 14:12:52 +0000 (UTC) From: Igor Korotin via B4 Relay Date: Sat, 31 Jan 2026 14:12:45 +0000 Subject: [PATCH 3/5] rust: i2c: Add I2C Adapter registration abstractions Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260131-i2c-adapter-v1-3-5a436e34cd1a@gmail.com> References: <20260131-i2c-adapter-v1-0-5a436e34cd1a@gmail.com> In-Reply-To: <20260131-i2c-adapter-v1-0-5a436e34cd1a@gmail.com> To: Danilo Krummrich , Daniel Almeida , Miguel Ojeda , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Wolfram Sang Cc: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-i2c@vger.kernel.org, markus.probst@posteo.de, Igor Korotin X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1769868772; l=17907; i=igor.korotin.linux@gmail.com; s=20260125; h=from:subject:message-id; bh=O00AxujwITCbYlKLKHLjBJTRZG5L0086MO+yva6+Jr4=; b=LxB+rq1ASkCN+in6cH21y6M6VHepp11Uq292hpMHHaCkpy5Ly9cB5ZYm2tziGKZ6z/8F0CToL IEwMwh1V+a7ACCXsRDws/S+z/zU9cv5JOP+12oQBhNP+CodhGCieLMd X-Developer-Key: i=igor.korotin.linux@gmail.com; a=ed25519; pk=SjZ//wxnynZgJJgTqANyU10A7hdQQdDXLvXIr+zWroE= X-Endpoint-Received: by B4 Relay for igor.korotin.linux@gmail.com/20260125 with auth_id=615 X-Original-From: Igor Korotin Reply-To: igor.korotin.linux@gmail.com From: Igor Korotin 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 --- 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 @@ }; =20 pub mod adapter; +pub mod algo; pub mod client; =20 /// 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 I2cAdapter { 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 Sel= f { + // 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 fu= nction call and the entire + // duration when the returned reference exists. + unsafe { &*ptr.cast() } + } } =20 impl I2cAdapter { @@ -78,3 +89,112 @@ unsafe fn dec_ref(obj: NonNull) { unsafe { bindings::i2c_put_adapter(obj.as_ref().as_raw()) } } } + +impl AsRef> for I2cAdapter= { + fn as_ref(&self) -> &device::Device { + let raw =3D self.as_raw(); + // SAFETY: By the type invariant of `Self`, `self.as_raw()` is a p= ointer to a valid + // `struct i2c_adapter`. + let dev =3D 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(self) -> bindings::i2c_adapter { + let mut adapter: bindings::i2c_adapter =3D pin_init::zeroed(); + // TODO: make it some other way... this looks like shit + let src =3D self.name.to_bytes_with_nul(); + let mut i: usize =3D 0; + while i < src.len() { + adapter.name[i] =3D src[i]; + i +=3D 1; + } + adapter.algo =3D I2cAlgorithmVTable::::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`] 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 { + #[pin] + inner: Opaque, + t_: PhantomData, +} + +impl Registration { + /// Register an I2C adapter. + pub fn register<'a>( + parent_dev: &'a device::Device, + opts: I2cAdapterOptions, + ) -> impl PinInit, Error> + 'a + where + T: 'a, + { + Devres::new(parent_dev, Self::new(parent_dev, opts)) + } + + fn new<'a>( + parent_dev: &'a device::Device, + opts: I2cAdapterOptions, + ) -> impl PinInit + use<'a, T> + where + T: 'a, + { + try_pin_init! { Self { + inner <- Opaque::try_ffi_init(move |slot: *mut bindings::i2c_a= dapter| { + // SAFETY: The initializer can write to the provided `slot= `. + unsafe {slot.write(opts.as_raw::()) }; + + // SAFETY: `slot` is valid from the initializer; `parent_d= ev` outlives the adapter. + unsafe { (*slot).dev.parent =3D parent_dev.as_raw() }; + + // SAFETY: the `struct i2c_adapter` was just created in sl= ot. The adapter will + // get unregistered before `slot` is deallocated because t= he memory is pinned and + // the destructor of this type deallocates the memory. + // INVARIANT: If this returns `Ok(())`, then the `slot` wi= ll contain a registered + // i2c adapter. + to_result(unsafe {bindings::i2c_add_adapter(slot)}) + }), + t_: PhantomData, + } + } + } +} + +#[pinned_drop] +impl PinnedDrop for Registration { + fn drop(self: Pin<&mut Self>) { + // SAFETY: We know that the device is registered by the type invar= iants. + unsafe { bindings::i2c_del_adapter(self.inner.get()) }; + } +} + +// 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 throu= gh &self +// and no mutable access is exposed) +unsafe impl Sync for Registration {} 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` t= hat +/// gets passed from/to the C side +/// +/// # Invariants +/// +/// A [`I2cMsg`] instance represents a valid `struct i2c_msg` created by t= he C portion of +/// the kernel. +#[repr(transparent)] +pub struct I2cMsg(Opaque); + +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 e= xists. + unsafe { core::slice::from_raw_parts_mut(msgs.cast::(), len)= } + } +} + +/// The i2c smbus data representation. +/// +/// This structure represents the Rust abstraction for a C `struct i2c_smb= us_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); + +impl I2cSmbusData { + /// Convert a raw C `struct i2c_smbus_data` pointer to `&'a I2cSmbusDa= ta`. + 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 referen= ce exists. + unsafe { &*ptr.cast() } + } +} + +kernel::define_flags!( + I2cFlags(u32), + I2C_FUNC_I2C =3D bindings::I2C_FUNC_I2C, + I2C_FUNC_10BIT_ADDR =3D bindings::I2C_FUNC_10BIT_ADDR, + I2C_FUNC_PROTOCOL_MANGLING =3D bindings::I2C_FUNC_PROTOCOL_MANGLING, + I2C_FUNC_SMBUS_PEC =3D bindings::I2C_FUNC_SMBUS_PEC, + I2C_FUNC_NOSTART =3D bindings::I2C_FUNC_NOSTART, + I2C_FUNC_SLAVE =3D bindings::I2C_FUNC_SLAVE, + I2C_FUNC_SMBUS_BLOCK_PROC_CALL =3D bindings::I2C_FUNC_SMBUS_BLOCK_PROC= _CALL, + I2C_FUNC_SMBUS_QUICK =3D bindings::I2C_FUNC_SMBUS_QUICK, + I2C_FUNC_SMBUS_READ_BYTE =3D bindings::I2C_FUNC_SMBUS_READ_BYTE, + I2C_FUNC_SMBUS_WRITE_BYTE =3D bindings::I2C_FUNC_SMBUS_WRITE_BYTE, + I2C_FUNC_SMBUS_READ_BYTE_DATA =3D bindings::I2C_FUNC_SMBUS_READ_BYTE_D= ATA, + I2C_FUNC_SMBUS_WRITE_BYTE_DATA =3D bindings::I2C_FUNC_SMBUS_WRITE_BYTE= _DATA, + I2C_FUNC_SMBUS_READ_WORD_DATA =3D bindings::I2C_FUNC_SMBUS_READ_WORD_D= ATA, + I2C_FUNC_SMBUS_WRITE_WORD_DATA =3D bindings::I2C_FUNC_SMBUS_WRITE_WORD= _DATA, + I2C_FUNC_SMBUS_PROC_CALL =3D bindings::I2C_FUNC_SMBUS_PROC_CALL, + I2C_FUNC_SMBUS_READ_BLOCK_DATA =3D bindings::I2C_FUNC_SMBUS_READ_BLOCK= _DATA, + I2C_FUNC_SMBUS_WRITE_BLOCK_DATA =3D bindings::I2C_FUNC_SMBUS_WRITE_BLO= CK_DATA, + I2C_FUNC_SMBUS_READ_I2C_BLOCK =3D bindings::I2C_FUNC_SMBUS_READ_I2C_BL= OCK, + I2C_FUNC_SMBUS_WRITE_I2C_BLOCK =3D bindings::I2C_FUNC_SMBUS_WRITE_I2C_= BLOCK, + I2C_FUNC_SMBUS_HOST_NOTIFY =3D 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 msg= s array + /// via the specified adapter. + fn xfer(_adap: &I2cAdapter, _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, _msgs: &mut [I2cMsg]) -> Re= sult { + 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, + _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, + _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) -> I2cFlags { + build_error!(VTABLE_DEFAULT_ERROR) + } +} + +/// A vtable for the I2C xfer operations of a Rust i2c adapter. +pub struct I2cAlgorithmVTable(PhantomData); + +impl I2cAlgorithmVTable { + /// # Safety + /// + /// `adap` must be a valid pointer to `struct i2c_adapter` that is ass= ociated with a + /// `i2c::adapter::Registration`. + /// `msgs` must be a valid pointer to `struct i2c_msg` for reading/wri= ting. + unsafe extern "C" fn xfer( + adap: *mut i2c_adapter, + msgs: *mut i2c_msg, + num: ffi::c_int, + ) -> ffi::c_int { + let num =3D match usize::try_from(num) { + Ok(num) =3D> num, + Err(_err) =3D> return EINVAL.to_errno(), + }; + + let msg_slice =3D I2cMsg::from_raw_parts_mut(msgs, num); + + match T::xfer(I2cAdapter::from_raw(adap), msg_slice) { + Ok(()) =3D> 0, + Err(err) =3D> err.to_errno(), + } + } + + /// # Safety + /// + /// `adap` must be a valid pointer to `struct i2c_adapter` that is ass= ociated with a + /// `i2c::adapter::Registration`. + /// `msgs` must be a valid pointer to `struct i2c_msg` for reading/wri= ting. + unsafe extern "C" fn xfer_atomic( + adap: *mut i2c_adapter, + msgs: *mut i2c_msg, + num: ffi::c_int, + ) -> ffi::c_int { + let num =3D match usize::try_from(num) { + Ok(num) =3D> num, + Err(_err) =3D> return EINVAL.to_errno(), + }; + + let msg_slice =3D I2cMsg::from_raw_parts_mut(msgs, num); + + match T::xfer_atomic(I2cAdapter::from_raw(adap), msg_slice) { + Ok(()) =3D> 0, + Err(err) =3D> err.to_errno(), + } + } + + /// # Safety + /// + /// `adap` must be a valid pointer to `struct i2c_adapter` that is ass= ociated with a + /// `i2c::adapter::Registration`. + /// `data` must be a valid pointer to `struct i2c_smbus_data` for read= ing/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 =3D match usize::try_from(size) { + Ok(size) =3D> size, + Err(_err) =3D> return EINVAL.to_errno(), + }; + + let data =3D I2cSmbusData::from_raw(data); + + match T::smbus_xfer( + I2cAdapter::from_raw(adap), + addr, + flags, + read_write, + command, + size, + data, + ) { + Ok(()) =3D> 0, + Err(err) =3D> err.to_errno(), + } + } + + /// # Safety + /// + /// `adap` must be a valid pointer to `struct i2c_adapter` that is ass= ociated with a + /// `i2c::adapter::Registration`. + /// `data` must be a valid pointer to `struct i2c_smbus_data` for read= ing/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 =3D match usize::try_from(size) { + Ok(size) =3D> size, + Err(_err) =3D> return EINVAL.to_errno(), + }; + + let data =3D I2cSmbusData::from_raw(data); + + match T::smbus_xfer_atomic( + I2cAdapter::from_raw(adap), + addr, + flags, + read_write, + command, + size, + data, + ) { + Ok(()) =3D> 0, + Err(err) =3D> err.to_errno(), + } + } + + /// # Safety + /// + /// `adap` must be a valid pointer to `struct i2c_adapter` that is ass= ociated with a + /// `I2cAdapterRegistration`. + unsafe extern "C" fn functionality(adap: *mut i2c_adapter) -> u32_ { + T::functionality(I2cAdapter::from_raw(adap)).as_raw() + } + + const VTABLE: bindings::i2c_algorithm =3D 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 + } +} --=20 2.43.0 From nobody Sat Feb 7 18:29:10 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 45FBD29AAF7; Sat, 31 Jan 2026 14:12:53 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769868773; cv=none; b=Mued8wfGHaK8PC89LO4931z97fmdv7N4VWS9qH6DLRWrgFCw2DSroh37JQX31Lfm/NgBdjtGG8388pi1pyre0h7zFXAK39Y13tPzBmzrDUnD8SW/SXtlSFeRqAY2oIhCODIKMSsJbKeAZ5mrL5qNfc2r2Zj6Z4FguWRmaiOzOuA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769868773; c=relaxed/simple; bh=zOg0kfQjDP0HR54Wb/4zXiPTRv5hsERO3K0bsnnYcx8=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=NkVTTD2IHvt6QOshF7/PdfHL6iLvEXrS3q+eqngo7ne65faXeHyQbOHz2+ZcQzTIHWh9tUVgOCGBbm5MesIIZ0XqvYNMMUPWpSXiWs09xTPHJuFn6UqfkY7JqZIsEnpdyLr8QZekeZJ4PHxBrZURPNdZRJTyaCJRBmoQ/6Sv1Yo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=S2/xg7Jg; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="S2/xg7Jg" Received: by smtp.kernel.org (Postfix) with ESMTPS id 174FBC2BCB2; Sat, 31 Jan 2026 14:12:53 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1769868773; bh=zOg0kfQjDP0HR54Wb/4zXiPTRv5hsERO3K0bsnnYcx8=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=S2/xg7JgBnzjpU/HCU12BcmQgj3EFodwrYj9BS6jr8KjFICixpTcyq2LB9wO2EoXI cRK7AqP94xQ8Kf09DdeMQHapPzViRgBPgyJI4NB0Wcv10mAhUTkTL+yqV8iV7reUw1 di8AYwgRJ+XGhFbgaj22/c+b+u9QDyb5DXIB6gdLlaRm1jKw39ZnIMxf4PXXmq8fNb OFKO/LN5UchB2hkWtznCV4pSF2UKSrxWlBGp9Aty1PgYiwWzqpQv4BlcRD6bz1rTm/ Ivjc58cXH0mM0PHopGDR7/r0u1ZTBYbuQNfiHk8kvfmNyRC/vhSq6gXghjjVfx6wBw Cckuz9X+pxxpA== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 0D8E7D7976C; Sat, 31 Jan 2026 14:12:53 +0000 (UTC) From: Igor Korotin via B4 Relay Date: Sat, 31 Jan 2026 14:12:46 +0000 Subject: [PATCH 4/5] rust: i2c: add I2C wrappers Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260131-i2c-adapter-v1-4-5a436e34cd1a@gmail.com> References: <20260131-i2c-adapter-v1-0-5a436e34cd1a@gmail.com> In-Reply-To: <20260131-i2c-adapter-v1-0-5a436e34cd1a@gmail.com> To: Danilo Krummrich , Daniel Almeida , Miguel Ojeda , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Wolfram Sang Cc: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-i2c@vger.kernel.org, markus.probst@posteo.de, Igor Korotin X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1769868772; l=6310; i=igor.korotin.linux@gmail.com; s=20260125; h=from:subject:message-id; bh=xiNYPB3pS7P0xBQVQAX6qDtWUnKw7npZg8SdNzhXYa4=; b=Ckru9qcxNiYGmCdDkhI5Mp1Q6P6bSRliUUAAG3x2YwBrN/6CsaVzRzwyYvPr8nGcyykkr7P8o OVAJmGG4V0cDQMzxLD+rP9JKr9S/AG074vB3APb3xYv9NS8WTiImeKy X-Developer-Key: i=igor.korotin.linux@gmail.com; a=ed25519; pk=SjZ//wxnynZgJJgTqANyU10A7hdQQdDXLvXIr+zWroE= X-Endpoint-Received: by B4 Relay for igor.korotin.linux@gmail.com/20260125 with auth_id=615 X-Original-From: Igor Korotin Reply-To: igor.korotin.linux@gmail.com From: Igor Korotin 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 --- 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 + +__rust_helper int rust_helper_i2c_master_recv(const struct i2c_client *cli= ent, + char *buf, int count) +{ + return i2c_master_recv(client, buf, count); +} + +__rust_helper int rust_helper_i2c_master_send(const struct i2c_client *cli= ent, + 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) are thread safe. unsafe impl Sync for I2cClient {} =20 +impl I2cClient { + /// The C `i2c_smbus_read_byte` function wrapper for SMbus "read byte"= protocol + pub fn i2c_smbus_read_byte(&self) -> Result { + // SAFETY: self is a valid reference to a I2cClient + let result =3D 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 byt= e" protocol + pub fn i2c_smbus_write_byte(&self, value: u8) -> Result { + // SAFETY: self is a valid reference to a I2cClient + to_result(unsafe { bindings::i2c_smbus_write_byte(self.as_raw(), v= alue) }) + } + + /// 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 { + // SAFETY: self is a valid reference to a I2cClient + let result =3D 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 "writ= e byte" protocol + pub fn i2c_smbus_write_byte_data(&self, command: u8, value: u8) -> Res= ult { + // SAFETY: self is a valid reference to a I2cClient + 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 { + // SAFETY: self is a valid reference to a I2cClient + let result =3D 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 "writ= e word" protocol + pub fn i2c_smbus_write_word_data(&self, command: u8, value: u16) -> Re= sult { + // SAFETY: self is a valid reference to a I2cClient + 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 { + // SAFETY: self is a valid reference to a I2cClient + let result =3D 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 + 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 mes= sage in master + /// receive mode + pub fn i2c_master_recv(&self, buf: &mut [u8]) -> Result { + // SAFETY: self is a valid reference to a I2cClient + let result =3D + unsafe { bindings::i2c_master_recv(self.as_raw(), buf.as_mut_p= tr(), buf.len() as i32) }; + to_result(result)?; + Ok(result as usize) + } + + /// The C `i2c_master_send` function wrapper to issue a single I2C mes= sage in master + /// transmit mode + pub fn i2c_master_send(&self, buf: &[u8]) -> Result { + // SAFETY: self is a valid reference to a I2cClient + let result =3D + 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 --=20 2.43.0 From nobody Sat Feb 7 18:29:10 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6AB1F355022; Sat, 31 Jan 2026 14:12:53 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769868773; cv=none; b=bOYzBFL4o9yprmyXu+plkUFKhjsQclB3DPV7YHf9nzjwTjdkAXYlCBEXxqgexR9wrqx5Lmybm78MYFUxAj+Yu7GBfNirQZ7TrJ0Q/oMrrPARPuG254B69lBAcgpi5NnCBjLcvKlMCbYkSrbI2T2jYXGTgCh0Ip+uUp76Qvh8ZwY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769868773; c=relaxed/simple; bh=pN7PB1pCh1IWJygKI2Vu4CWLqsurcsYp3Gs/ABkgY6I=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=LhUBk2tZFGxp3MoLlrKgOHaM8rHupkn9/F+Q1+nQ8zEK6YAl55xD7I+5R6mLWoiTN3P7tLhOxbrR3ahPiEivifDFXPMpVp2jQI6a5sBVyWHlEGWQcGfqF7cJtgEDEphgWvAWshi/iiS8N9hWsBN3dAluTqY+iudTOoe4ydPrPRE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=EgYYYSV9; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="EgYYYSV9" Received: by smtp.kernel.org (Postfix) with ESMTPS id 263E5C2BCB4; Sat, 31 Jan 2026 14:12:53 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1769868773; bh=pN7PB1pCh1IWJygKI2Vu4CWLqsurcsYp3Gs/ABkgY6I=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=EgYYYSV91B/xdRpxt4pz2j4nC/1je+kvqwwvxqKSUHi0ByiLoaqcVXMQQvkJkEGQW 4Rv7+zu7aj2U4ZFY+85ukwyr6R6zrMhxgwFT3Rius36Dt0hg5FS25+ftQAoj9/1k2u f8q+ObbdS1xTmQZAaFGSR7KXPPgKeOvyPaDNJ+deJNXlcq+yRkvtz3UFXE6459QEc4 SVDZXZMood/0QgA7r5VM16MADi4d3D6ph7JcKTOHsy2iqVh19Tq6xteC3/MkOvAg9S qGlLcLUM3eIf7dSeYzg7QLY6uMnjxmIN2g4kPvAZJpBzvVm0mNIfltTsaCX7EcVIfs aU7X/KM78sPrw== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 1BB35D79770; Sat, 31 Jan 2026 14:12:53 +0000 (UTC) From: Igor Korotin via B4 Relay Date: Sat, 31 Jan 2026 14:12:47 +0000 Subject: [PATCH 5/5] samples: rust: add Rust I2C adapter registration sample Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260131-i2c-adapter-v1-5-5a436e34cd1a@gmail.com> References: <20260131-i2c-adapter-v1-0-5a436e34cd1a@gmail.com> In-Reply-To: <20260131-i2c-adapter-v1-0-5a436e34cd1a@gmail.com> To: Danilo Krummrich , Daniel Almeida , Miguel Ojeda , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Wolfram Sang Cc: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-i2c@vger.kernel.org, markus.probst@posteo.de, Igor Korotin X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1769868772; l=7280; i=igor.korotin.linux@gmail.com; s=20260125; h=from:subject:message-id; bh=UoiJITbB7wSdqa0+19oS9e0ghILI5njDjJv+rjpQTNo=; b=OJMA6QqqxEoPFMUi/s6s6vUTrFKJwRrspekSjPnBxBTRXVy2rYv7XsxvuwQ2sLrxN9izdsPPP eVuBxGsChbWCKUGKyTtelRLz2VjTCBrCm7z+Z8P954G66OF/8b0QJBK X-Developer-Key: i=igor.korotin.linux@gmail.com; a=ed25519; pk=SjZ//wxnynZgJJgTqANyU10A7hdQQdDXLvXIr+zWroE= X-Endpoint-Received: by B4 Relay for igor.korotin.linux@gmail.com/20260125 with auth_id=615 X-Original-From: Igor Korotin Reply-To: igor.korotin.linux@gmail.com From: Igor Korotin Add a new `rust_i2c_adapter` sample, showing how to create a new i2c adapter using `i2c::adapter::Registration` Signed-off-by: Igor Korotin --- 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 =20 I2C SUBSYSTEM HOST DRIVERS M: Andi Shyti 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 =20 If unsure, say N. =20 +config SAMPLE_RUST_I2C_ADAPTER + tristate "I2C Adapter Registration" + depends on I2C=3Dy + 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) +=3D rust_debugfs_= scoped.o obj-$(CONFIG_SAMPLE_RUST_DMA) +=3D rust_dma.o obj-$(CONFIG_SAMPLE_RUST_DRIVER_I2C) +=3D rust_driver_i2c.o obj-$(CONFIG_SAMPLE_RUST_I2C_CLIENT) +=3D rust_i2c_client.o +obj-$(CONFIG_SAMPLE_RUST_I2C_ADAPTER) +=3D rust_i2c_adapter.o obj-$(CONFIG_SAMPLE_RUST_DRIVER_PCI) +=3D rust_driver_pci.o obj-$(CONFIG_SAMPLE_RUST_DRIVER_PLATFORM) +=3D rust_driver_platform.o obj-$(CONFIG_SAMPLE_RUST_DRIVER_USB) +=3D rust_driver_usb.o diff --git a/samples/rust/rust_i2c_adapter.rs b/samples/rust/rust_i2c_adapt= er.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 registrat= ion driver +//! using QEMU with a custom SSDT. +//! +//! Steps: +//! +//! 1. **Create an SSDT source file** (`ssdt.dsl`) with the following cont= ent: +//! +//! ```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=3D/dev/sda console=3DttyS0" \ +//! -hda rootfs.img \ +//! -serial stdio \ +//! -acpitable file=3Dssdt.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, + #[pin] + i2c_adap: Devres>, +} + +struct SampleDevice {} + +#[vtable] +impl I2cAlgorithm for SampleDevice { + fn smbus_xfer( + _adap: &I2cAdapter, + _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) -> I2cFlags { + flags::I2C_FUNC_SMBUS_READ_BYTE | flags::I2C_FUNC_SMBUS_WRITE_BYTE + } +} + +kernel::acpi_device_table!( + ACPI_TABLE, + MODULE_ACPI_TABLE, + ::IdInfo, + [(acpi::DeviceId::new(c"LNUXBEEF"), ())] +); + +impl platform::Driver for SampleDriver { + type IdInfo =3D (); + + const ACPI_ID_TABLE: Option> =3D Some(&ACP= I_TABLE); + + fn probe( + pdev: &platform::Device, + _id_info: Option<&Self::IdInfo>, + ) -> impl PinInit { + 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 =3D I2cAdapterOptions{ name: c"rust_i2c_adapt= er"}; + Registration::register(pdev.as_ref(), name) + }, + })) + }) + } + + fn unbind(pdev: &platform::Device, _this: Pin<&Self>) { + dev_info!( + pdev.as_ref(), + "Unbind start: Rust I2C Adapter registration sample.\n" + ); + // The i2c_adap (Devres>) will be autom= atically + // 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", +} --=20 2.43.0