From nobody Tue Oct 7 22:35:59 2025 Received: from mail-wr1-f48.google.com (mail-wr1-f48.google.com [209.85.221.48]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 557D82DE710; Fri, 4 Jul 2025 15:39:05 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.48 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1751643547; cv=none; b=lRxyXAX3KanJVuHtT8dUz3qGuU39ehjaNxLiZeZpjU/RdnW6C4xof3r1OMy8yFimNWSBuDmOw2vduJoqmnbzpiRLeYRZiletwpAuc9JQnwDfHckFI6rxMq1fkT5+obSohIJBkysYezdDN/6Nt1Qq0LaZCyn0mLAWkQjB+ssvtm0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1751643547; c=relaxed/simple; bh=09JL73lDwyKAS0NATXopcslT4d7c6v+zoAS+cDGpgq4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=g2SlMcL5cUlYBZgXpgzX4fW1CkZy22Iun4ZYhbrJUB4OlGZ5FlAs4TW7OuMkrESuJrohaUJ8pArVgXvTYarcor2iEc5Vn+GlS0JZATKSzGykvGCNKxtG9ZJ9khl8V91kCO079/OYyylu9TzyZ2ovtidTarjjwlJ7V1U7H80YVJ0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=FNxtE/Cp; arc=none smtp.client-ip=209.85.221.48 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="FNxtE/Cp" Received: by mail-wr1-f48.google.com with SMTP id ffacd0b85a97d-3a54700a46eso608053f8f.1; Fri, 04 Jul 2025 08:39:05 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1751643543; x=1752248343; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:sender:from:to:cc:subject:date :message-id:reply-to; bh=14iVrnVMBU+8vP2oKEQ6H7zWd8y3qXQf/YOTfgvD75I=; b=FNxtE/CpPEpbkqGzIHUJqhMrdG1IX+vE5flKwUcPj1/gBSbhlR+RbmanS9YNWpso93 hxM+X5WiW+C/GJsqEPd3LiWtjUTtJ2BuDTAYQiuGmKh4+OtDnQSdpSba9ICcDchs4gtJ /G1GHtDmbbrPclbqLV/bVUZYhi3gV8iiDdVRH4hKnsE78TOBCpXDt+3pRDG71N/gMhQ/ bxoDiGt0uohhEpM1erLkzkd3oHBqqwUWH1YgmsTrCI04JU56wU1LnALxanJHD819mcLE +8k0tlrqWKAn6RSUg2INuvlpl93TYWZViiRpVS67GuMdT6DDVPaEs6NtwsIjGlJ8eIXV UYSw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1751643543; x=1752248343; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:sender:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=14iVrnVMBU+8vP2oKEQ6H7zWd8y3qXQf/YOTfgvD75I=; b=l6fAeOb7+lFpiGfXGQAgVlKBcAow64C4Ai41ztI7yKCQ7HNHlfVq5/wA1CUR9f+FUY QE+427I5gy1ZGVUYQ5yA6CROtmMrBTn37e9nf3yy+7SoZZGjacvOcHkvpdIY3wDIP/hX JiI0lgR9m5tx/y3KvfddDgZycGXFVP5RNkUoy5zfY8ZJJ0pTBZY5AU9gFRLFkYuKxNk2 0wO4NM3QRikpGJgVgwDysj0OTLk294E6votB/2GDfcRT9GaPT2xyXIG4yNkIGu7LphKf Oh0HomH7T/CL48kVW4kjSW3slX+zGDpp4nG73DATbEd3kuXi3R/0vOydPXWohi6ax8jA GZTQ== X-Forwarded-Encrypted: i=1; AJvYcCUoxBWh+plAr6osbtdhxMaXkJaGE++YexL2rtlE2HwkVpJuAIcOmm9NReomekXdPCLsG46mHIdicis=@vger.kernel.org, AJvYcCVIgbSP2FXzNY2IIcKatriYGqJAHqS88sbEp21ptr5BMuYlMflt5Fygy6Q22DASVyHSwCWEmiYmP0l7eisiSwA=@vger.kernel.org, AJvYcCW6dTaedshKKndMiXOaOT3kQKbmBg6RTziNvgevrkSA9YwWFy51Xszhm2CQMjZppRSHqEm8uSW3vA4r1G1N@vger.kernel.org X-Gm-Message-State: AOJu0YyODX5YavqOEKFLtR9FYBp1rlh6Q6O+1GHRjSgdjm87noruPOET YbIZuHkCzSbyzEEAGCtuO26BuGGxWVuLIl2wiQAjCicQ4Ze8ck6DKML0 X-Gm-Gg: ASbGncsr0En07wEfqrfdUNIX22hzVNPNg6fkry6pAz06aVDJt1gXWm6h3bgJKuhoHSO eqrkXDzgvifhuBNl9jcXHc4abUDOoApLizGsIThKj7RIyqbkMZPPixV/MBsET7L0h+R3roPqpT4 JGl5Yy76hWmytygNJw6sD/rLZPM63dx9CN6JUt1coQDvf6Tr/ucizTSn/Hxr3wipqsTsyatBSQ0 QPq8M6mZR4W0njHudYfEiLTvVSWwm3kg2EHfz+dBKmrXGk6VwzmhHJNc9I60wPybBE7yrFupapX MYl6zJc79fFnyXFA8RxX8Ay5ZtmvlL+hyJOcD2TxPw+XgpTar5UDdNU2n5H4D4lmgq66Os/BEBz yEt2iqA4+yjFueIEt8oBuUqmMf8WVQIDAYa7a8uFqrL/Hew0= X-Google-Smtp-Source: AGHT+IFRGI2AkmdyvGIsjw27l3CYDTCf0wgleuo5O26HxkCjLw0VkjRmXwIQQ9851Gu+Ponj54k+kw== X-Received: by 2002:a05:6000:4210:b0:3a5:2beb:7493 with SMTP id ffacd0b85a97d-3b4964c8b9emr2825465f8f.9.1751643543100; Fri, 04 Jul 2025 08:39:03 -0700 (PDT) Received: from igor-korotin-Precision-Tower-3620.airspan.com ([188.39.32.4]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-454b16866a7sm29652025e9.23.2025.07.04.08.39.02 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 04 Jul 2025 08:39:02 -0700 (PDT) Sender: Igor Korotin From: Igor Korotin To: Miguel Ojeda , Alex Gaynor , Wolfram Sang Cc: Boqun Feng , Gary Guo , =?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , Greg Kroah-Hartman , Viresh Kumar , Asahi Lina , Wedson Almeida Filho , Alex Hung , Tamir Duberstein , Xiangfei Ding , linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-i2c@vger.kernel.org Subject: [PATCH v2 1/4] rust: i2c: add basic I2C device and driver abstractions Date: Fri, 4 Jul 2025 16:36:57 +0100 Message-ID: <20250704153657.1195687-1-igor.korotin.linux@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20250704153332.1193214-1-igor.korotin.linux@gmail.com> References: <20250704153332.1193214-1-igor.korotin.linux@gmail.com> 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 Implement the core abstractions needed for I2C drivers, including: * `i2c::Driver` =E2=80=94 the trait drivers must implement, including `prob= e` * `i2c::Device` =E2=80=94 a safe wrapper around `struct i2c_client` * `i2c::Adapter` =E2=80=94 implements `driver::RegistrationOps` to hook int= o the generic `driver::Registration` machinery * `i2c::DeviceId` =E2=80=94 a `RawDeviceId` implementation for I2C device I= Ds Signed-off-by: Igor Korotin --- MAINTAINERS | 2 + rust/bindings/bindings_helper.h | 1 + rust/helpers/helpers.c | 1 + rust/helpers/i2c.c | 15 ++ rust/kernel/i2c.rs | 391 ++++++++++++++++++++++++++++++++ rust/kernel/lib.rs | 2 + 6 files changed, 412 insertions(+) create mode 100644 rust/helpers/i2c.c create mode 100644 rust/kernel/i2c.rs diff --git a/MAINTAINERS b/MAINTAINERS index 7f8ddeec3b17..688a0ff23e69 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -11363,6 +11363,8 @@ F: include/linux/i2c-smbus.h F: include/linux/i2c.h F: include/uapi/linux/i2c-*.h F: include/uapi/linux/i2c.h +F: rust/helpers/i2c.c +F: rust/kernel/i2c.rs =20 I2C SUBSYSTEM HOST DRIVERS M: Andi Shyti diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helpe= r.h index 7e8f22850647..efc9be4d9a6e 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -53,6 +53,7 @@ #include #include #include +#include #include #include #include diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c index 0b09bd0e3561..be34554b3fab 100644 --- a/rust/helpers/helpers.c +++ b/rust/helpers/helpers.c @@ -23,6 +23,7 @@ #include "drm.c" #include "err.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..5f384f8f560e --- /dev/null +++ b/rust/helpers/i2c.c @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include + +void* rust_helper_i2c_get_clientdata(struct i2c_client *client) +{ + return i2c_get_clientdata(client); +} + +void rust_helper_i2c_set_clientdata(struct i2c_client *client, void *data) +{ + i2c_set_clientdata(client, data); +} + + diff --git a/rust/kernel/i2c.rs b/rust/kernel/i2c.rs new file mode 100644 index 000000000000..4f2f3c378153 --- /dev/null +++ b/rust/kernel/i2c.rs @@ -0,0 +1,391 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! I2C Driver subsystem + +// I2C Driver abstractions. +use crate::{ + acpi, container_of, device, + device_id::RawDeviceId, + driver, + error::*, + of, + prelude::*, + types::{ForeignOwnable, Opaque}, +}; + +use core::{ + marker::PhantomData, + ptr::{addr_of_mut, NonNull}, +}; + +/// An I2C device id table. +#[repr(transparent)] +#[derive(Clone, Copy)] +pub struct DeviceId(bindings::i2c_device_id); + +impl DeviceId { + const I2C_NAME_SIZE: usize =3D 20; + + /// Create a new device id from an I2C 'id' string. + #[inline(always)] + pub const fn new(id: &'static CStr) -> Self { + build_assert!( + id.len_with_nul() <=3D Self::I2C_NAME_SIZE, + "ID exceeds 20 bytes" + ); + let src =3D id.as_bytes_with_nul(); + // Replace with `bindings::acpi_device_id::default()` once stabili= zed for `const`. + // SAFETY: FFI type is valid to be zero-initialized. + let mut i2c: bindings::i2c_device_id =3D unsafe { core::mem::zeroe= d() }; + let mut i =3D 0; + while i < src.len() { + i2c.name[i] =3D src[i]; + i +=3D 1; + } + + Self(i2c) + } +} + +// SAFETY: +// * `DeviceId` is a `#[repr(transparent)` wrapper of `i2c_device_id` and = does not add +// additional invariants, so it's safe to transmute to `RawType`. +// * `DRIVER_DATA_OFFSET` is the offset to the `driver_data` field. +unsafe impl RawDeviceId for DeviceId { + type RawType =3D bindings::i2c_device_id; + + const DRIVER_DATA_OFFSET: usize =3D core::mem::offset_of!(bindings::i2= c_device_id, driver_data); + + fn index(&self) -> usize { + self.0.driver_data as _ + } +} + +/// IdTable type for I2C +pub type IdTable =3D &'static dyn kernel::device_id::IdTable; + +/// Create a I2C `IdTable` with its alias for modpost. +#[macro_export] +macro_rules! i2c_device_table { + ($table_name:ident, $module_table_name:ident, $id_info_type: ty, $tabl= e_data: expr) =3D> { + const $table_name: $crate::device_id::IdArray< + $crate::i2c::DeviceId, + $id_info_type, + { $table_data.len() }, + > =3D $crate::device_id::IdArray::new($table_data); + + $crate::module_device_table!("i2c", $module_table_name, $table_nam= e); + }; +} + +/// An adapter for the registration of I2C drivers. +pub struct Adapter(T); + +// SAFETY: A call to `unregister` for a given instance of `RegType` is gua= ranteed to be valid if +// a preceding call to `register` has been successful. +unsafe impl driver::RegistrationOps for Adapter { + type RegType =3D bindings::i2c_driver; + + unsafe fn register( + pdrv: &Opaque, + name: &'static CStr, + module: &'static ThisModule, + ) -> Result { + let i2c_table =3D match T::I2C_ID_TABLE { + Some(table) =3D> table.as_ptr(), + None =3D> core::ptr::null(), + }; + + let of_table =3D match T::OF_ID_TABLE { + Some(table) =3D> table.as_ptr(), + None =3D> core::ptr::null(), + }; + + let acpi_table =3D match T::ACPI_ID_TABLE { + Some(table) =3D> table.as_ptr(), + None =3D> core::ptr::null(), + }; + + // SAFETY: It's safe to set the fields of `struct i2c_client` on i= nitialization. + unsafe { + (*pdrv.get()).driver.name =3D name.as_char_ptr(); + (*pdrv.get()).probe =3D Some(Self::probe_callback); + (*pdrv.get()).remove =3D Some(Self::remove_callback); + (*pdrv.get()).shutdown =3D Some(Self::shutdown_callback); + (*pdrv.get()).id_table =3D i2c_table; + (*pdrv.get()).driver.of_match_table =3D of_table; + (*pdrv.get()).driver.acpi_match_table =3D acpi_table; + } + + // SAFETY: `pdrv` is guaranteed to be a valid `RegType`. + to_result(unsafe { bindings::i2c_register_driver(module.0, pdrv.ge= t()) }) + } + + unsafe fn unregister(pdrv: &Opaque) { + // SAFETY: `pdrv` is guaranteed to be a valid `RegType`. + unsafe { bindings::i2c_del_driver(pdrv.get()) } + } +} + +impl Adapter { + extern "C" fn probe_callback(pdev: *mut bindings::i2c_client) -> kerne= l::ffi::c_int { + // SAFETY: The I2C bus only ever calls the probe callback with a v= alid pointer to a + // `struct i2c_client`. + // + // INVARIANT: `pdev` is valid for the duration of `probe_callback(= )`. + let pdev =3D unsafe { &*pdev.cast::>() }; + + let info =3D + Self::i2c_id_info(pdev).or_else(|| ::= id_info(pdev.as_ref())); + + match T::probe(pdev, info) { + Ok(data) =3D> { + unsafe { bindings::i2c_set_clientdata(pdev.as_raw(), data.= into_foreign() as _) }; + } + Err(err) =3D> return Error::to_errno(err), + } + + 0 + } + + extern "C" fn remove_callback(pdev: *mut bindings::i2c_client) { + // SAFETY: `pdev` is a valid pointer to a `struct i2c_client`. + let ptr =3D unsafe { bindings::i2c_get_clientdata(pdev) }.cast(); + + // SAFETY: `remove_callback` is only ever called after a successfu= l call to + // `probe_callback`, hence it's guaranteed that `ptr` points to a = valid and initialized + // `KBox` pointer created through `KBox::into_foreign`. + let _ =3D unsafe { KBox::::from_foreign(ptr) }; + } + + extern "C" fn shutdown_callback(pdev: *mut bindings::i2c_client) { + let pdev =3D unsafe { &*pdev.cast::>() }; + + T::shutdown(pdev); + } + + /// The [`i2c::IdTable`] of the corresponding driver. + fn i2c_id_table() -> Option::IdInfo>= > { + T::I2C_ID_TABLE + } + + /// Returns the driver's private data from the matching entry in the [= `i2c::IdTable`], if any. + /// + /// If this returns `None`, it means there is no match with an entry i= n the [`i2c::IdTable`]. + fn i2c_id_info(dev: &Device) -> Option<&'static ::IdInfo> { + let table =3D Self::i2c_id_table()?; + + // SAFETY: + // - `table` has static lifetime, hence it's valid for read, + // - `dev` is guaranteed to be valid while it's alive, and so is `= pdev.as_ref().as_raw()`. + let raw_id =3D unsafe { bindings::i2c_match_id(table.as_ptr(), dev= .as_raw()) }; + + if raw_id.is_null() { + None + } else { + // SAFETY: `DeviceId` is a `#[repr(transparent)` wrapper of `s= truct i2c_device_id` and + // does not add additional invariants, so it's safe to transmu= te. + let id =3D unsafe { &*raw_id.cast::() }; + + Some(table.info(::i= ndex(id))) + } + } +} + +impl driver::Adapter for Adapter { + type IdInfo =3D T::IdInfo; + + fn of_id_table() -> Option> { + T::OF_ID_TABLE + } + + fn acpi_id_table() -> Option> { + T::ACPI_ID_TABLE + } +} + +/// Declares a kernel module that exposes a single i2c driver. +/// +/// # Examples +/// +/// ```ignore +/// kernel::module_i2c_driver! { +/// type: MyDriver, +/// name: "Module name", +/// authors: ["Author name"], +/// description: "Description", +/// license: "GPL v2", +/// } +/// ``` +#[macro_export] +macro_rules! module_i2c_driver { + ($($f:tt)*) =3D> { + $crate::module_driver!(, $crate::i2c::Adapter, { $($f)* }); + }; +} + +/// The i2c driver trait. +/// +/// Drivers must implement this trait in order to get a i2c driver registe= red. +/// +/// # Example +/// +///``` +/// # use kernel::{acpi, bindings, c_str, device::Core, i2c, of}; +/// +/// struct MyDriver; +/// +/// kernel::acpi_device_table!( +/// ACPI_TABLE, +/// MODULE_ACPI_TABLE, +/// ::IdInfo, +/// [ +/// (acpi::DeviceId::new(c_str!("LNUXBEEF")), ()) +/// ] +/// ); +/// +/// kernel::i2c_device_table!( +/// I2C_TABLE, +/// MODULE_I2C_TABLE, +/// ::IdInfo, +/// [ +/// (i2c::DeviceId::new(c_str!("rust_driver_i2c")), ()) +/// ] +/// ); +/// +/// kernel::of_device_table!( +/// OF_TABLE, +/// MODULE_OF_TABLE, +/// ::IdInfo, +/// [ +/// (of::DeviceId::new(c_str!("test,device")), ()) +/// ] +/// ); +/// +/// impl i2c::Driver for MyDriver { +/// type IdInfo =3D (); +/// const I2C_ID_TABLE: Option> =3D Some(&I= 2C_TABLE); +/// const OF_ID_TABLE: Option> =3D Some(&OF_= TABLE); +/// const ACPI_ID_TABLE: Option> =3D Some(= &ACPI_TABLE); +/// +/// fn probe( +/// _pdev: &i2c::Device, +/// _id_info: Option<&Self::IdInfo>, +/// ) -> Result>> { +/// Err(ENODEV) +/// } +/// +/// fn shutdown(_pdev: &i2c::Device) { +/// } +/// } +///``` +pub trait Driver: Send { + /// The type holding information about each device id supported by the= driver. + // TODO: Use `associated_type_defaults` once stabilized: + // + // ``` + // type IdInfo: 'static =3D (); + // ``` + type IdInfo: 'static; + + /// The table of device ids supported by the driver. + const I2C_ID_TABLE: Option> =3D None; + + /// The table of OF device ids supported by the driver. + const OF_ID_TABLE: Option> =3D None; + + /// The table of ACPI device ids supported by the driver. + const ACPI_ID_TABLE: Option> =3D None; + + /// I2C driver probe. + /// + /// Called when a new i2c device is added or discovered. + /// Implementers should attempt to initialize the device here. + fn probe(dev: &Device, id_info: Option<&Self::IdInfo>) + -> Result>>; + + /// I2C driver shutdown + /// + /// Called when + fn shutdown(dev: &Device) { + let _ =3D dev; + } +} + +/// The i2c client representation. +/// +/// This structure represents the Rust abstraction for a C `struct i2c_cli= ent`. The +/// implementation abstracts the usage of an already existing C `struct i2= c_client` within Rust +/// code that we get passed from the C side. +/// +/// # Invariants +/// +/// A [`Device`] instance represents a valid `struct i2c_client` created b= y the C portion of +/// the kernel. +#[repr(transparent)] +pub struct Device( + Opaque, + PhantomData, +); + +impl Device { + fn as_raw(&self) -> *mut bindings::i2c_client { + self.0.get() + } +} + +// SAFETY: `Device` is a transparent wrapper of a type that doesn't depend= on `Device`'s generic +// argument. +kernel::impl_device_context_deref!(unsafe { Device }); +kernel::impl_device_context_into_aref!(Device); + +// SAFETY: Instances of `Device` are always reference-counted. +unsafe impl crate::types::AlwaysRefCounted for Device { + fn inc_ref(&self) { + // SAFETY: The existence of a shared reference guarantees that the= refcount is non-zero. + unsafe { bindings::get_device(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(addr_of_mut!((*obj.as_ref().as_raw()= ).dev)) } + } +} + +impl AsRef> for Device { + fn as_ref(&self) -> &device::Device { + // SAFETY: By the type invariant of `Self`, `self.as_raw()` is a p= ointer to a valid + // `struct i2c_client`. + let dev =3D unsafe { addr_of_mut!((*self.as_raw()).dev) }; + + // SAFETY: `dev` points to a valid `struct device`. + unsafe { device::Device::as_ref(dev) } + } +} + +impl TryFrom<&device::Device> for &Device= { + 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 pdev =3D unsafe { container_of!(dev.as_raw(), bindings::i2c_cl= ient, dev) }; + + // SAFETY: `pdev` is a valid pointer to a `struct i2c_client`. + Ok(unsafe { &*pdev.cast() }) + } +} + +// SAFETY: A `Device` is always reference-counted and can be released from= any thread. +unsafe impl Send for Device {} + +// SAFETY: `Device` can be shared among threads because all methods of `De= vice` +// (i.e. `Device) are thread safe. +unsafe impl Sync for Device {} diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 5bbf3627212f..ee1233e44a0f 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -79,6 +79,8 @@ #[cfg(CONFIG_RUST_FW_LOADER_ABSTRACTIONS)] pub mod firmware; pub mod fs; +#[cfg(CONFIG_I2C)] +pub mod i2c; pub mod init; pub mod io; pub mod ioctl; --=20 2.43.0 From nobody Tue Oct 7 22:35:59 2025 Received: from mail-wr1-f45.google.com (mail-wr1-f45.google.com [209.85.221.45]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 407B32BEC28; Fri, 4 Jul 2025 15:41:17 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.45 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1751643679; cv=none; b=UlZIIaDSI1QCGaLF1z28nMFfmbDTp5pEz4UZ4Muh2PY/wBimh8U62whM+sh3pGCbBgyxHlvwHvtfhxcwTm7MnkD8iYsT0F5UknUcXVhsQdKkVGo5rNBj2dWN+SkDGYv1cQDK0gUp/UVMs43DMMZsVfYTbUDwvCQa2NLE+o7aUmE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1751643679; c=relaxed/simple; bh=hLipmzu4F8mg8qXXCFM8HYZaEPjr8KBLcqkXB0ZttpY=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=kn9/F0Ca3iH4kTkpmsJl8q9JG6oj9UyW8VmPFlUcDXa/M60FIrnj3FJCKav9nAS11dyBxR7V1nkF+v7mu4a4QObHQR42/16rMLMijBvOmxY2lVb1TBy3/Okxjeh5F2m5ybD7adajFDOtTUEsb76MdShotQvDYIS1XTmmr36/GRY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=XasEArNN; arc=none smtp.client-ip=209.85.221.45 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="XasEArNN" Received: by mail-wr1-f45.google.com with SMTP id ffacd0b85a97d-3a54700a463so632021f8f.1; Fri, 04 Jul 2025 08:41:16 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1751643675; x=1752248475; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:sender:from:to:cc:subject:date :message-id:reply-to; bh=ZehQDJ7GYI16BJFe1KA565rbeqzvhJ2ipoQj8wKA4GA=; b=XasEArNN5BsKuecOYaiJPD5eWfC6w/jlEbL+7mPhyHSem4Gc9YwV/SsPkPKRtap4wT BPhm7cUiw0yLuOGo4IOVXVghwxgiS8c3iaYRSlyM5YJefByTMpmbJ6YVkdxNNXsPJSyB NHhzd4TY0vdQ0PQ+L8g5QucgyTpRJrqQA16B9832qhxWZQ2RzNCOIfZFqdC2Y+d6oqwA 9ctqyFcZ0jEu/TymQNnwf7l28fKYvJ6MMliXAxdTFibwJaY0epe04ek/nDVu9jXUJfUQ 2lRRhAeORzSV9NWAvd0tKqUKdwSBoyDUdXV6Qj1rnQSahLtKVkjAJFiglOaINT7kFzyw 5z8Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1751643675; x=1752248475; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:sender:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=ZehQDJ7GYI16BJFe1KA565rbeqzvhJ2ipoQj8wKA4GA=; b=enBS3hgGcngxSB2KYzZMSkw4CAis8udpajem6J2wTm78TOe6VY7K3ALUzJNR680y8u /eBPVTClXtrdFa8qODKvF04x+Tz27Nw1xTuCnGoVZZ5Ud9y+XtQJRo8JbZLj8kotZlLu rgAB9Wc6pgmEp6hqLy6CNoYpPqbpkG450Sx5CT2N25XLr+G1hd55gaJkuwx+BxsDKFQT v3SzdX6xoOmVxAHhJFm56/s/NuSkN8RJdH+V/yqxFRZsaMedLATYCRr3q4zi9p6LygpW MVNaWP9Tk4Ob9Tu/lPaF6zFVb2+fSpJWhGpuxpw9/tWJI99WLP6EHiwvVoCAGQjmvkyN at4A== X-Forwarded-Encrypted: i=1; AJvYcCVUq7GwxKEO2hL6HxFqbG7QsHIzZkzN9j/rEmm3Je5SLazjSSZNLHZiKooox2hQL4wqjNYX5x8vUqJnWB5f@vger.kernel.org, AJvYcCVVSk2cZw8+FBDOUcH/w7lODnyLNETnK2W+a8O08TVlr9MBGUM0cMOHah1q7k1J1zQlhQDFJgET0ag=@vger.kernel.org, AJvYcCVeEcAe8jxBy7uAUkihK8ZFQ8Tc0SjgF5P07W9NkRW7ZJANUErebHV4k9Y1kWJaDr1rb8RNvHMMGCT71rCxyuY=@vger.kernel.org X-Gm-Message-State: AOJu0YwQrgB4gM4cL4XvCNjIeGMjSVq5tEDhEkRFDTZDJAfEkdLak5c+ 8rupCK8OnIQlfmWqz58iOF3d4CIEm6arOuMMWjtAfOJnYppnwlz7vEEE X-Gm-Gg: ASbGncvX61nslwcvELm7ldHd5gc/dYav5MQVgNl5qeVP+XIdjNM9vtuFdCCceBAcBO6 r3iTOVcGr1VydjGL9ihXFfLEWIMu25CpoPwiekuvkK5alpUNcFo3Ql/QjlYEET/9f6DVpDlK3cT MQpPm82O6IGmkOKOoNE+dV2cJtZu61k9xLQEenmngOgHMYKaKk/3oluPVMvWPRJ90h9Lnz7IDan BwvMSgZmSKsUGbPDCXfi8MRGwSnVZl+dSXz75VgUHzRIUzaAAis/YWFqI835kmEtKU377ntQWUP hjgJ4Ld1hDMszM3fFi8j4xERWb/AdAMsjVjzipQX8zsAvMvJ32SekKpzo2LjBlefvyW/4otXTkW n7bX13Hgc8taJLkt9BCxwxb/zCKJ4sChE685VZmcL8jKWa58G5A== X-Google-Smtp-Source: AGHT+IERyK/7kX7kvDPfsA3GitiUXco1hNZ9Yv7C25VPQUc9/JklVrs18eYYPX/AUpNMA9WYXUYxoQ== X-Received: by 2002:a05:6000:230c:b0:3a5:23c6:eeee with SMTP id ffacd0b85a97d-3b495cbcb56mr3859110f8f.21.1751643675108; Fri, 04 Jul 2025 08:41:15 -0700 (PDT) Received: from igor-korotin-Precision-Tower-3620.airspan.com ([188.39.32.4]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-3b47285bdf8sm2728308f8f.87.2025.07.04.08.41.14 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 04 Jul 2025 08:41:14 -0700 (PDT) Sender: Igor Korotin From: Igor Korotin To: Miguel Ojeda , Alex Gaynor , Wolfram Sang Cc: Boqun Feng , Gary Guo , =?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , Greg Kroah-Hartman , Viresh Kumar , Asahi Lina , Wedson Almeida Filho , Alex Hung , Tamir Duberstein , Xiangfei Ding , linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-i2c@vger.kernel.org Subject: [PATCH v2 2/4] rust: i2c: add manual I2C device creation abstractions Date: Fri, 4 Jul 2025 16:39:12 +0100 Message-ID: <20250704153912.1197034-1-igor.korotin.linux@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20250704153332.1193214-1-igor.korotin.linux@gmail.com> References: <20250704153332.1193214-1-igor.korotin.linux@gmail.com> 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 In addition to the basic I2C device support, added rust abstractions upon `i2c_new_client_device`/`i2c_unregister_device` C functions. Implement additional generic parameter for i2c::Device that shows if the i2c::Device is abstraction on top of "borrowed" i2c_client struct, or it is manually created device which is "owned" by a caller. Implement the core abstractions needed for owned I2C devices, including: * `i2c::state` =E2=80=94 a generic parameter type for i2c::Device * `i2c::DeviceOwned` =E2=80=94 a wrapper around `i2c::Device` * `i2c::I2cAdapterRef` =E2=80=94 a safe reference around `struct i2c_adapt= er` * `i2c::I2cBoardInfo` =E2=80=94 a safe wrapper around `struct i2c_board_in= fo` Signed-off-by: Igor Korotin --- rust/kernel/i2c.rs | 184 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 179 insertions(+), 5 deletions(-) diff --git a/rust/kernel/i2c.rs b/rust/kernel/i2c.rs index 4f2f3c378153..43487fdd8597 100644 --- a/rust/kernel/i2c.rs +++ b/rust/kernel/i2c.rs @@ -10,7 +10,7 @@ error::*, of, prelude::*, - types::{ForeignOwnable, Opaque}, + types::{ARef, ForeignOwnable, Opaque}, }; =20 use core::{ @@ -312,6 +312,102 @@ fn shutdown(dev: &Device) { } } =20 +/// The i2c adapter reference +/// +/// This represents the Rust abstraction for a reference to an existing +/// C `struct i2c_adapter`. +/// +/// # Invariants +/// +/// A [`I2cAdapterRef`] instance represents a valid `struct i2c_adapter` c= reated by the C portion of +/// the kernel. +#[repr(transparent)] +pub struct I2cAdapterRef(NonNull); + +impl I2cAdapterRef { + fn as_raw(&self) -> *mut bindings::i2c_adapter { + self.0.as_ptr() + } + + /// Gets pointer to an `i2c_adapter` by index. + pub fn get(index: i32) -> Option { + // SAFETY: `index` must refer to a valid I=C2=B2C adapter; the ker= nel + // 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) })?; + Some(Self(adapter)) + } +} + +impl Drop for I2cAdapterRef { + fn drop(&mut self) { + // SAFETY: This `I2cAdapterRef` was obtained from `i2c_get_adapter= `, + // and calling `i2c_put_adapter` exactly once will correctly relea= se + // the reference count in the I=C2=B2C core. It is safe to call fr= om any context + unsafe { bindings::i2c_put_adapter(self.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 board=E2=80=90info for a kernel driver. + #[inline(always)] + pub const fn new(type_: &'static CStr, addr: u16) -> Self { + build_assert!( + type_.len_with_nul() <=3D Self::I2C_TYPE_SIZE, + "Type exceeds 20 bytes" + ); + let src =3D type_.as_bytes_with_nul(); + // Replace with `bindings::acpi_device_id::default()` once stabili= zed for `const`. + // SAFETY: FFI type is valid to be zero-initialized. + let mut i2c_board_info: bindings::i2c_board_info =3D unsafe { core= ::mem::zeroed() }; + 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 { + &self.0 as *const _ + } +} + +/// Marker trait for the state of a bus specific device. +pub trait DeviceState: private::Sealed {} + +/// State module which aggregates existing Device States. +pub mod state { + /// The [`Borrowed`] state is the state of a bus specific device when = it was not + /// manually created using `DeviceOwned::new` + pub struct Borrowed; + + /// The [`Owned`] state is the state of a bus specific device when it = was + /// manually created using `DeviceOwned::new` and thus will be automat= ically + /// unregistered when the corresponding `DeviceOwned` is dropped + pub struct Owned; +} + +mod private { + pub trait Sealed {} + + impl Sealed for super::state::Borrowed {} + impl Sealed for super::state::Owned {} +} + +impl DeviceState for state::Borrowed {} +impl DeviceState for state::Owned {} + /// The i2c client representation. /// /// This structure represents the Rust abstraction for a C `struct i2c_cli= ent`. The @@ -323,15 +419,19 @@ fn shutdown(dev: &Device) { /// A [`Device`] instance represents a valid `struct i2c_client` created b= y the C portion of /// the kernel. #[repr(transparent)] -pub struct Device( +pub struct Device( Opaque, PhantomData, + PhantomData, ); =20 -impl Device { +impl Device { fn as_raw(&self) -> *mut bindings::i2c_client { self.0.get() } + fn from_raw(raw: *mut bindings::i2c_client) -> &'static Self { + unsafe { &*raw.cast::>() } + } } =20 // SAFETY: `Device` is a transparent wrapper of a type that doesn't depend= on `Device`'s generic @@ -340,7 +440,9 @@ fn as_raw(&self) -> *mut bindings::i2c_client { kernel::impl_device_context_into_aref!(Device); =20 // SAFETY: Instances of `Device` are always reference-counted. -unsafe impl crate::types::AlwaysRefCounted for Device { +unsafe impl crate::types::= AlwaysRefCounted + for Device +{ fn inc_ref(&self) { // SAFETY: The existence of a shared reference guarantees that the= refcount is non-zero. unsafe { bindings::get_device(self.as_ref().as_raw()) }; @@ -352,7 +454,9 @@ unsafe fn dec_ref(obj: NonNull) { } } =20 -impl AsRef> for Device { +impl AsRef> + for Device +{ fn as_ref(&self) -> &device::Device { // SAFETY: By the type invariant of `Self`, `self.as_raw()` is a p= ointer to a valid // `struct i2c_client`. @@ -389,3 +493,73 @@ unsafe impl Send for Device {} // SAFETY: `Device` can be shared among threads because all methods of `De= vice` // (i.e. `Device) are thread safe. unsafe impl Sync for Device {} + +/// The representation of reference counted pointer to a manually created = i2c client. +/// +/// This structure represents the Rust wrapper upon i2c::Device with the i= 2c::state::Owned state +#[repr(transparent)] +pub struct DeviceOwned( + ARef>, +); + +/// The main purpose of the DeviceOwned wrapper is to automatically +/// take care of i2c client created by i2c_new_client_device. +/// +/// The example of usage: +/// +/// ``` +/// use kernel::{c_str, device::Core, i2c, prelude::*}; +/// +/// struct Context { +/// _owned: i2c::DeviceOwned, +/// } +/// +/// const BOARD_INFO: i2c::I2cBoardInfo =3D i2c::I2cBoardInfo::new(c_str!(= "rust_driver_i2c"), 0x30); +/// +/// impl Context { +/// fn init(_module: &'static ThisModule) -> Result { +/// +/// let adapter =3D i2c::I2cAdapterRef::get(0) +/// .ok_or(EINVAL)?; +/// +/// let device =3D i2c::DeviceOwned::::new(&adapter, &BOARD_= INFO) +/// .ok_or(EINVAL)?; +/// +/// Ok(Self { _owned: device }) +/// } +/// } +/// +/// impl Drop for Context { +/// fn drop(&mut self) { +/// +/// } +/// } +/// +/// ``` +impl DeviceOwned { + fn as_raw_client(&self) -> *mut bindings::i2c_client { + self.0.as_raw() + } + + /// The C `i2c_new_client_device` function wrapper for manual I2C clie= nt creation. + pub fn new(i2c_adapter: &I2cAdapterRef, i2c_board_info: &I2cBoardInfo)= -> Option { + // SAFETY: the kernel guarantees that `i2c_new_client_device()` re= turns either a valid + // pointer or NULL. `NonNull::new` guarantees the correct check. + let raw_dev =3D NonNull::new(unsafe { + bindings::i2c_new_client_device(i2c_adapter.as_raw(), i2c_boar= d_info.as_raw()) + })?; + + let dev =3D Device::::from_raw(raw_dev.as_ptr()= ); + + Some(Self(dev.into())) + } +} + +impl Drop for DeviceOwned { + fn drop(&mut self) { + unsafe { bindings::i2c_unregister_device(self.as_raw_client()) } + } +} + +// SAFETY: A `Device` is always reference-counted and can be released from= any thread. +unsafe impl Send for DeviceOwned {} --=20 2.43.0 From nobody Tue Oct 7 22:35:59 2025 Received: from mail-wm1-f54.google.com (mail-wm1-f54.google.com [209.85.128.54]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 142F01E3762; Fri, 4 Jul 2025 15:43:37 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.54 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1751643819; cv=none; b=JGH2Lgl+Gb6WMerR9723kL5vuvW6NmEX1YknCWEePVpx9PinBF8gAp8ccd3ubBxrJmkE/F+V9xzyTvaGPhANqTNip0G/uioesV4fJCuIP3aB+R61Kh2CxLUZ0V38golFaoZEIIhVe1SHiq/ZyJoc46YrPAZm3gnO3Dgxp4KAnu4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1751643819; c=relaxed/simple; bh=LjA+/5tDXbnNsqNuIEG+W6Ox8IQsd8bQElonyR5KYzs=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=fbM1MmiopfKSY8fPwm7sGm0Q+TFQM7NqwsK27xUPtdE7zre+EKIKzb2Osl7JOQ0R3VJf6WMGXP5055Yln1zdtS8Gl2cudUaIJ4uRNknY/MVOo4fzlaCEZtGmFoYoH0bSDUkptZc6EYiCDlGR4beoNNenQVTagBLhIdS2CxLjVhU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=nklSaG36; arc=none smtp.client-ip=209.85.128.54 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="nklSaG36" Received: by mail-wm1-f54.google.com with SMTP id 5b1f17b1804b1-450cf214200so8207565e9.1; Fri, 04 Jul 2025 08:43:37 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1751643816; x=1752248616; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:sender:from:to:cc:subject:date :message-id:reply-to; bh=qxhWkz+tlx+vDWtPt3y0/2Jq0yIsXTdVdLxkIQQdoBk=; b=nklSaG369TEafZkBS9vHR2odlMQej3zEpkqOF2erzwaDLgPovzeT/Em9KpFl48iCIY xYsxEQckCIQrPIFM/4g+boYAu/NuYJnxOY7i8yv/8P7e/Q7HkgYPzTpr1z40FXas65+D yQZR2rRxAIOvEawZNJ10s7yborLBwygatO28JXBIhhgl/Bt3+Pd5dW/Lz9TDxgsahVrE 1A+YD0Af+E/mpfnkxxxjfoajgp6T5iJfbyatnVhsntQC4/bRD9/OoBcAb9XcBbREcWKC h00imSkigKY2gD+gAoBT+1fpk434olYEGOPziaoww/ZxkRuXz9q/pppMdV3Iw2OPli6J M3fA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1751643816; x=1752248616; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:sender:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=qxhWkz+tlx+vDWtPt3y0/2Jq0yIsXTdVdLxkIQQdoBk=; b=il3+7y4jpgsciv8w0o5xxJ0EU6BbKtO9hHWslV3h8VtlzWRAHIq0Rr1s8Njt1kKnaq tK/d6WGvjM4tFTRWBRqnmX8HapTAcKBLZtCfHbGPIgfHi5wp4BzcaAd+IRMl08VM3chv 3KBHQ9SCtA4bNvy1vd06FRbTJLErAg6P5Dirp8IpSZbWwITjhJbwJdGQh2obVOa0kfPj 4NUbFdcaeUwwZT628ktC7UeCQo3LkReSP2P/I7eHmRPopAi3d1GznMayHplvOPbW58VJ UEJ3XMmx+ICJNBTiUCdG/x7NJ6gs5/9CIT/0pErxUG5fyA2OPJqsnrKDBohkD9Og+q+K LJaQ== X-Forwarded-Encrypted: i=1; AJvYcCUQ+vs2M5mla/v6F7E7G16uX8C8bCv6KHf2q7MTGHAxkPedlMzlFYtEjs7BE6oLJnVgqvyYWbfDjDmuDmrQ@vger.kernel.org, AJvYcCWiHcOHkXjYZCnaTULZMFx27FYQxDeOlSSxFKBE+lXNPXn0Vu++j/ETyTi0uSt0KIaTahzpKtrY8p9T0nj+Anw=@vger.kernel.org, AJvYcCXS0WXf2b6tg1HO2zSGdREozFAm/3PLhf0DftrHVUok9omBHOkZeOG/AULa8wgksdSv01ykhqL8+GI=@vger.kernel.org X-Gm-Message-State: AOJu0YyXmrPIu8ghllplszpk1ancXIbQnThP6Z2iIP/w0L1x+EoLhV8E nMfSusjEjlafJpqYFdCEx7RJouUa5iMpk3/Du3r8KET8yeYzFnCbWlRIJoCD2viipOg= X-Gm-Gg: ASbGncuyx7rGkscUDv3s1I6/OSpkdUSfUdtf7SPZcL3L2AHU/lic6yWD5WZEbOnEDUG OQTOEd/stjebNEfR7bmlhskUTY9nuwfjccPU5aM8Okfq9iysUHHKBqUfesBvAyJkZQJ3iv9AqIK I1UaoEHculVb+OlftJapxdlfzK08b8nGOiCINSjGyti6Xp7yYmUjWuIkX1UHjfWDKfebRXQfQCh 1DMFRjtl+nXuclwLMFHBFTqeW8vnF1ygiKqHxhZ9JcgUOCIRsOVWRC0IBpn+RLnzyz1O1lO/aJ7 XwFT2xSxAEZbxa+0Zo5Gux/NsWsRoWVwv5OD7mng7gLKMfB3zZ+VIwq/XPRqj4r3rZUJpQvWb+w 37FzyCNTGBhIwtsav1X59KK3/D6jAgNxoVFeUrwvQerv4NMU= X-Google-Smtp-Source: AGHT+IEZHbQ7bH1s4YT4UWXAPG4icmKBpSPilG9I3q4k3KctFtB/h7G0o0V5vwHvtJPLsh5RTMsGaA== X-Received: by 2002:a05:600c:1c90:b0:453:86cc:739c with SMTP id 5b1f17b1804b1-454b3069f37mr29398395e9.1.1751643815724; Fri, 04 Jul 2025 08:43:35 -0700 (PDT) Received: from igor-korotin-Precision-Tower-3620.airspan.com ([188.39.32.4]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-454a9bead7csm57979425e9.39.2025.07.04.08.43.34 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 04 Jul 2025 08:43:34 -0700 (PDT) Sender: Igor Korotin From: Igor Korotin To: Miguel Ojeda , Alex Gaynor , Wolfram Sang Cc: Boqun Feng , Gary Guo , =?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , Greg Kroah-Hartman , Viresh Kumar , Asahi Lina , Wedson Almeida Filho , Alex Hung , Tamir Duberstein , Xiangfei Ding , linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-i2c@vger.kernel.org Subject: [PATCH v2 3/4] samples: rust: add Rust I2C sample driver Date: Fri, 4 Jul 2025 16:41:32 +0100 Message-ID: <20250704154132.1197920-1-igor.korotin.linux@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20250704153332.1193214-1-igor.korotin.linux@gmail.com> References: <20250704153332.1193214-1-igor.korotin.linux@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add a new `rust_driver_i2c` sample, showing how to bind an I2C client in Rust via legacy I2C-ID, ACPI ID tables or OF compatible tables. Signed-off-by: Igor Korotin --- MAINTAINERS | 1 + samples/rust/Kconfig | 11 ++++++ samples/rust/Makefile | 1 + samples/rust/rust_driver_i2c.rs | 69 +++++++++++++++++++++++++++++++++ 4 files changed, 82 insertions(+) create mode 100644 samples/rust/rust_driver_i2c.rs diff --git a/MAINTAINERS b/MAINTAINERS index 688a0ff23e69..82b469b8ecb9 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -11365,6 +11365,7 @@ F: include/uapi/linux/i2c-*.h F: include/uapi/linux/i2c.h F: rust/helpers/i2c.c F: rust/kernel/i2c.rs +F: samples/rust/rust_driver_i2c.rs =20 I2C SUBSYSTEM HOST DRIVERS M: Andi Shyti diff --git a/samples/rust/Kconfig b/samples/rust/Kconfig index 7f7371a004ee..55aeb12cd7f7 100644 --- a/samples/rust/Kconfig +++ b/samples/rust/Kconfig @@ -62,6 +62,17 @@ config SAMPLE_RUST_DMA =20 If unsure, say N. =20 +config SAMPLE_RUST_DRIVER_I2C + tristate "I2C Driver" + depends on I2C + help + This option builds the Rust I2C driver sample. + + To compile this as a module, choose M here: + the module will be called rust_driver_i2c. + + If unsure, say N. + config SAMPLE_RUST_DRIVER_PCI tristate "PCI Driver" depends on PCI diff --git a/samples/rust/Makefile b/samples/rust/Makefile index bd2faad63b4f..141d8f078248 100644 --- a/samples/rust/Makefile +++ b/samples/rust/Makefile @@ -5,6 +5,7 @@ obj-$(CONFIG_SAMPLE_RUST_MINIMAL) +=3D rust_minimal.o obj-$(CONFIG_SAMPLE_RUST_MISC_DEVICE) +=3D rust_misc_device.o obj-$(CONFIG_SAMPLE_RUST_PRINT) +=3D rust_print.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_DRIVER_PCI) +=3D rust_driver_pci.o obj-$(CONFIG_SAMPLE_RUST_DRIVER_PLATFORM) +=3D rust_driver_platform.o obj-$(CONFIG_SAMPLE_RUST_DRIVER_FAUX) +=3D rust_driver_faux.o diff --git a/samples/rust/rust_driver_i2c.rs b/samples/rust/rust_driver_i2c= .rs new file mode 100644 index 000000000000..eee3bd774c46 --- /dev/null +++ b/samples/rust/rust_driver_i2c.rs @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Rust I2C driver sample. + +use kernel::{acpi, c_str, device::Core, i2c, of, prelude::*, types::ARef}; + +struct SampleDriver { + pdev: ARef, +} + +kernel::acpi_device_table! { + ACPI_TABLE, + MODULE_ACPI_TABLE, + ::IdInfo, + [(acpi::DeviceId::new(c_str!("LNUXBEEF")), 0)] +} + +kernel::i2c_device_table! { + I2C_TABLE, + MODULE_I2C_TABLE, + ::IdInfo, + [(i2c::DeviceId::new(c_str!("rust_driver_i2c")), 0)] +} + +kernel::of_device_table! { + OF_TABLE, + MODULE_OF_TABLE, + ::IdInfo, + [(of::DeviceId::new(c_str!("test,rust_driver_i2c")), 0)] +} + +impl i2c::Driver for SampleDriver { + type IdInfo =3D u32; + + const ACPI_ID_TABLE: Option> =3D Some(&ACP= I_TABLE); + const I2C_ID_TABLE: Option> =3D Some(&I2C_T= ABLE); + const OF_ID_TABLE: Option> =3D Some(&OF_TABL= E); + + fn probe(pdev: &i2c::Device, info: Option<&Self::IdInfo>) -> Res= ult>> { + let dev =3D pdev.as_ref(); + + dev_dbg!(dev, "Probe Rust I2C driver sample.\n"); + + if let Some(info) =3D info { + dev_info!(dev, "Probed with info: '{}'.\n", info); + } + + let drvdata =3D KBox::new(Self { pdev: pdev.into() }, GFP_KERNEL)?; + + Ok(drvdata.into()) + } + fn shutdown(pdev: &i2c::Device) { + dev_dbg!(pdev.as_ref(), "Shutdown Rust I2C driver sample.\n"); + } +} + +impl Drop for SampleDriver { + fn drop(&mut self) { + dev_dbg!(self.pdev.as_ref(), "Remove Rust I2C driver sample.\n"); + } +} + +kernel::module_i2c_driver! { + type: SampleDriver, + name: "rust_driver_i2c", + authors: ["Igor Korotin"], + description: "Rust I2C driver", + license: "GPL v2", +} --=20 2.43.0 From nobody Tue Oct 7 22:35:59 2025 Received: from mail-wm1-f53.google.com (mail-wm1-f53.google.com [209.85.128.53]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 72CEE140E34; Fri, 4 Jul 2025 15:45:53 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.53 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1751643956; cv=none; b=cSpAX5rRDavsfR0bedwr9WcqAZNyl8TdRDTrOgSCJAm0i7rSVS5CDTwDy7BrvSNwZQfzclxaGiAg9/Pr9/qwuXXGA2+9N6tQXXZLmL0ys6NUlVlXnWf46kVzHGdoyK6/h0FAqp1+dt9P8g0YMzM/Dn7n3+qlYuS+ISBT2gEe2Cg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1751643956; c=relaxed/simple; bh=Qfg1bTdE3fo9bxLBT9hGP1+7jI+26Qt8XFcv2TwSyc0=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=MLqjfDxKDflsE3FvaIauKDr6igGJbyHtKA1oGb2/9bvtaILnvZOekdfoewOJuLnBgvuVrgy2AZIJRBrI4jhCZSALnMQ9pFV0vquBn5qz509IHL6g8cib0TyVyyPXy0SfLJ3ID4chOaMbkithW5qpsmJwiYdcGLICtirZYvgcNMU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=hCXv4ilm; arc=none smtp.client-ip=209.85.128.53 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="hCXv4ilm" Received: by mail-wm1-f53.google.com with SMTP id 5b1f17b1804b1-450ccda1a6eso7938425e9.2; Fri, 04 Jul 2025 08:45:53 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1751643952; x=1752248752; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:sender:from:to:cc:subject:date :message-id:reply-to; bh=V7tRFxTXdKxo++BEADlGZZAUvVfHDhOeQkPo5DocB8o=; b=hCXv4ilmJo4FzBmywVrnhIkt+8o/sLkS5GTAk3l/smr6fJhKoJMCKBZAX6+kGoSKqc m37uqeuekKlXWOJW7ftW8iFqtocyaELioG64tIv+wA3mPy8asdffGiRlRhnqjFAAv/eQ huZKF2jQ07fXOsfLaG6K7sR+KBQXMGuO+ubFF20yImHag0u6ocRJE253ibiTErgKpdpn Cd8SvRQrDY6+DlyrI3HSJSFHgAEHck3JR4Is9lWFrpdQVV/2l/pDYQQTAuN0h3WvZhOA G7o0PEbnKVjsCKC4J4w5HpbouexIbmwVFLy/p0TFBqCNJR3r553fXK48UhVinv7FvbRH zAcA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1751643952; x=1752248752; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:sender:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=V7tRFxTXdKxo++BEADlGZZAUvVfHDhOeQkPo5DocB8o=; b=GCMzDDoqGZ6y7UfINhUhJ0gvx+ltldUW1aWrPRXRY1adJNQ6S0t6P8szJlIoWfRta9 kNPrqRAFK1XDFcMa9aw3wHXgz5hoGpISY798kZfDEXNOAYVGHzsxoIa9vxaSzQA6n1g8 t1i5ocx1vGhMcFog4mukUhpgVLyqoqK+ntcmzMz9p04wlEMrnV2D6B7aQlo84WaGM4Rb 9DNuF1Pals00NvZbrCEfP3cvNCG9xgcbBn+FD3XSCPbcWYQgTdC+7jxrYC9SgFhauK+W 7j8lUbSvNSGC64RIhsIcKFTLoAF+k92K9APOFm5YRmTwsEPFQeRm51CBwVIpeOYA8rvB A+8w== X-Forwarded-Encrypted: i=1; AJvYcCVN4m7WRye9aYkydQu8ejwP+uVdumOkA/sRgLmKGYPpuRsbU+eirRqYmxWkA7mRF6Q4KdY4m8BKo/n3wSQC@vger.kernel.org, AJvYcCVXJToyIlVwOWsvG2cMfgcQ5nNdzQSGqui8Tk+R8Y6AohDrsmLAe4wwkX/BCZvpmLPCb3O4nEolpeg=@vger.kernel.org, AJvYcCXWGJV8jwyKl7Ac1rvGAcVCRlJtqZd0fEB+AJgajU+dJkJLlUNI9d79u0UWXSVfy9v3uMnZPfS1aRvsTaw9SmQ=@vger.kernel.org X-Gm-Message-State: AOJu0Yz2lC94HPjVYcA5ZfSnRD3XQ1Zi98zh6wnK057HWNx3Ljobl8nC go3hpf11w0fd24en6THoO5phG4f5Y2b4cgvRzyl+MV/7umrooF6d3oU1ep+Zz5QBkrE= X-Gm-Gg: ASbGncvW8J/Iu2i561ITRMYpN3PKBG0NARbA3641abw/n7by3KD5ben5iE654F/+Fvk 6UXUHWP+3xDeHT2rxYyQHR5ZrIspLaxAkJad+TpPKx7MX6h3RPv8L2OUHwonxvxS1f5skK9THrC MOiU0WWUtXvEmOtY9zthk89HXSczUonlwjSJlhAeyXhbmV2izb7IXcnHpWJK7UNU/j7kRGdU61D JIlEFCxfMs2ws1qi1j7fij0TMrRkFL09aVO0r2G4kDDg/fju2+f4GiGlQgMNMveTW7P+JjARw9V clypCKrtPGYh9KLHAMb+XhsR204czBOhUO8PU2JDzuQ2/KgfqeOcuQ8fCFwH5qJakWfXk1vSz6u +ouOfdh8i7Zlq1xEYpl2euYvYnue6Q79yHOMM8SUfZ0SMJ3I= X-Google-Smtp-Source: AGHT+IGzGtD71lv8K59LG0FwoUFaeTU046UI05a+xTZIHpFbiVsVKRycTLR2W4Ctsi47v5MoDIab6A== X-Received: by 2002:a05:600c:6745:b0:43d:3df:42d8 with SMTP id 5b1f17b1804b1-454b3069f2cmr31628505e9.6.1751643951525; Fri, 04 Jul 2025 08:45:51 -0700 (PDT) Received: from igor-korotin-Precision-Tower-3620.airspan.com ([188.39.32.4]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-454b168664bsm30154705e9.20.2025.07.04.08.45.50 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 04 Jul 2025 08:45:50 -0700 (PDT) Sender: Igor Korotin From: Igor Korotin To: Miguel Ojeda , Alex Gaynor , Wolfram Sang Cc: Boqun Feng , Gary Guo , =?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , Greg Kroah-Hartman , Viresh Kumar , Asahi Lina , Wedson Almeida Filho , Alex Hung , Tamir Duberstein , Xiangfei Ding , linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-i2c@vger.kernel.org Subject: [PATCH v2 4/4] samples: rust: add Rust manual I2C device creation sample Date: Fri, 4 Jul 2025 16:43:41 +0100 Message-ID: <20250704154345.1198721-1-igor.korotin.linux@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20250704153332.1193214-1-igor.korotin.linux@gmail.com> References: <20250704153332.1193214-1-igor.korotin.linux@gmail.com> 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 Add a new `rust_device_i2c` sample, showing how to create I2C device on a certain `I2CAdapterRef` using `I2cBoardInfo`. Demonstrates automatic unregister of such I2C device when driver is unloaded Signed-off-by: Igor Korotin --- MAINTAINERS | 1 + samples/rust/Kconfig | 13 +++++++++ samples/rust/Makefile | 1 + samples/rust/rust_device_i2c.rs | 50 +++++++++++++++++++++++++++++++++ 4 files changed, 65 insertions(+) create mode 100644 samples/rust/rust_device_i2c.rs diff --git a/MAINTAINERS b/MAINTAINERS index 82b469b8ecb9..23bab3c8e1ef 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -11365,6 +11365,7 @@ F: include/uapi/linux/i2c-*.h F: include/uapi/linux/i2c.h F: rust/helpers/i2c.c F: rust/kernel/i2c.rs +F: samples/rust/rust_device_i2c.rs F: samples/rust/rust_driver_i2c.rs =20 I2C SUBSYSTEM HOST DRIVERS diff --git a/samples/rust/Kconfig b/samples/rust/Kconfig index 55aeb12cd7f7..394618aaf5ef 100644 --- a/samples/rust/Kconfig +++ b/samples/rust/Kconfig @@ -62,6 +62,18 @@ config SAMPLE_RUST_DMA =20 If unsure, say N. =20 +config SAMPLE_RUST_DEVICE_I2C + tristate "Manual I2C Device" + depends on I2C && I2C_CHARDEV + help + This option builds the Rust I2C device manual creation + sample. + + To compile this as a module, choose M here: + the module will be called rust_device_i2c. + + If unsure, say N. + config SAMPLE_RUST_DRIVER_I2C tristate "I2C Driver" depends on I2C @@ -124,3 +136,4 @@ config SAMPLE_RUST_HOSTPROGS If unsure, say N. =20 endif # SAMPLES_RUST + diff --git a/samples/rust/Makefile b/samples/rust/Makefile index 141d8f078248..ee830da1a9d2 100644 --- a/samples/rust/Makefile +++ b/samples/rust/Makefile @@ -5,6 +5,7 @@ obj-$(CONFIG_SAMPLE_RUST_MINIMAL) +=3D rust_minimal.o obj-$(CONFIG_SAMPLE_RUST_MISC_DEVICE) +=3D rust_misc_device.o obj-$(CONFIG_SAMPLE_RUST_PRINT) +=3D rust_print.o obj-$(CONFIG_SAMPLE_RUST_DMA) +=3D rust_dma.o +obj-$(CONFIG_SAMPLE_RUST_DEVICE_I2C) +=3D rust_device_i2c.o obj-$(CONFIG_SAMPLE_RUST_DRIVER_I2C) +=3D rust_driver_i2c.o obj-$(CONFIG_SAMPLE_RUST_DRIVER_PCI) +=3D rust_driver_pci.o obj-$(CONFIG_SAMPLE_RUST_DRIVER_PLATFORM) +=3D rust_driver_platform.o diff --git a/samples/rust/rust_device_i2c.rs b/samples/rust/rust_device_i2c= .rs new file mode 100644 index 000000000000..a056736b1b97 --- /dev/null +++ b/samples/rust/rust_device_i2c.rs @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Rust I2C DeviceOwned usage sample. +//! +//! This sample driver manually creates i2c_client using I2C board info +//! and pointer to I2C Adapter structure. +//! +//! For reproduction of the scenario one should compile kernel with i2c-de= v and i2c-stub +//! modules enabled. f + +use kernel::{c_str, device::Core, i2c, prelude::*}; + +struct SampleDriver { + _owned: i2c::DeviceOwned, +} + +// SAFETY: SampleDriver contains only one field `owned: DeviceOwned`, +// which is initialized in `init()` and dropped on module unload. +// There is no interior mutability or concurrent access to its contents +// (all I=C2=B2C operations happen in single-threaded init/drop contexts), +// so it is safe to share &SampleDriver across threads. +unsafe impl Sync for SampleDriver {} + +const BOARD_INFO: i2c::I2cBoardInfo =3D i2c::I2cBoardInfo::new(c_str!("rus= t_driver_i2c"), 0x30); + +impl kernel::Module for SampleDriver { + fn init(_module: &'static ThisModule) -> Result { + pr_debug!("Probe Rust I2C device sample.\n"); + + let adapter =3D i2c::I2cAdapterRef::get(0).ok_or(EINVAL)?; + + let device =3D i2c::DeviceOwned::::new(&adapter, &BOARD_INFO= ).ok_or(EINVAL)?; + + Ok(Self { _owned: device }) + } +} + +impl Drop for SampleDriver { + fn drop(&mut self) { + pr_debug!("Drop Rust I2C device sample.\n"); + } +} + +kernel::prelude::module! { + type:SampleDriver, + name:"rust_device_i2c", + authors:["Igor Korotin"], + description:"Rust I2C device manual creation driver ", + license:"GPL v2", +} --=20 2.43.0