rust: i2c: Add basic I2C driver abstractions
v3 cover rust: i2c: Add basic I2C driver abstractions
This patch series lays the groundwork for writing Linux I2C drivers in Rust by:
1. Core abstractions
Introduce i2c::Device, i2c::Driver and i2c::Adapter built on the existing struct i2c_client and struct i2c_driver, with safe Rust wrappers around probe, transfer, and teardown logic.
2. Manual device creation
Provide an API to register an I2C device at runtime from Rust using I2cBoardInfo, including automatic cleanup when the driver unloads.
3. Sample driver (legacy table, OF & ACPI)
Add rust_driver_i2c, a sample that:
- creates an I2C client device using i2c::Registration::new()
- binds to an I2C client via:
- legacy I2C-ID table,
- Open Firmware (device-tree) compatible strings, or
- ACPI IDs.
- destroyes the I2C client device on exit.
Together, these three patches:
- Establish the essential Rust traits and types for I2C drivers.
- Enable driver binding via legacy ID table, device-tree (OF), or ACPI
- Enable manual device creation at runtime.
- Ship a samples showing typical usage
Igor Korotin (3):
rust: i2c: add basic I2C device and driver abstractions
rust: i2c: add manual I2C device creation abstractions
samples: rust: add Rust I2C sample driver
Changelog
---------
v3:
- removed unnecessary i2c_get_clientdata and i2c_set_clientdata rust helpers. Using generic accessors implemented in [1] instead.
- Reimplemented i2c::DeviceId based on changes in [2].
- Using from_result in i2c::Adapter::probe_callback
- Using explicit drop() for i2c client private data in i2c::Adapter::remove_callback
- replaced device::Device::as_ref() with device::Device::from_raw in i2c::Device::as_ref(). It is renamed in device::Device.
- Build Rust I2C only if I2C is built-in
- Reimplement overcomplicated trait i2c::DeviceOwned the same way it is implemented in auxiliary [3].
- Merge rust_device_i2c and rust_driver_i2c samples. Resulting rust_driver_i2c creates pined i2c_client using i2c::Registration::new and probes newly created i2c_client.
- Created a new entry in MAINTAINERS file containing i2c.rs and rust_driver_i2c.rs in it.
- Link to v2: [4]
[1] https://lore.kernel.org/lkml/[email protected]/
[2] https://lore.kernel.org/rust-for-linux/[email protected]/
[3] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/rust/kernel/auxiliary.rs?h=v6.16-rc4#n299
[4] https://lore.kernel.org/rust-for-linux/[email protected]/
v2:
- Merged separated ACPI support patches since ACPI-table support is
merged into driver-core-next.
- Added I2cAdapterRef and I2cBoardInfo abstractions
- Added DeviceState generic parameter which is used for i2c::Device
as a sign if the device is created manually
- Added DeviceOwned abstraction which is a safe reference to a
manually created i2c::Device<Ctx, state::Owned>.
- Added Rust manual I2C device creation sample
- Link to v1: https://lore.kernel.org/rust-for-linux/[email protected]/
MAINTAINERS | 8 + rust/bindings/bindings_helper.h | 1 + rust/kernel/i2c.rs | 498 ++++++++++++++++++++++++++++++++ rust/kernel/lib.rs | 2 + samples/rust/Kconfig | 11 + samples/rust/Makefile | 1 + samples/rust/rust_driver_i2c.rs | 106 +++++++ 7 files changed, 627 insertions(+) create mode 100644 rust/kernel/i2c.rs create mode 100644 samples/rust/rust_driver_i2c.rs
base-commit: 260f6f4fda93c8485c8037865c941b42b9cba5d2
Signature · 1 line
2.43.0
PATCH v3 1/3 rust: i2c: add basic I2C device and driver abstractions
Implement the core abstractions needed for I2C drivers, including:
* i2c::Driver — the trait drivers must implement, including probe
* i2c::Device — a safe wrapper around struct i2c_client
* i2c::Adapter — implements driver::RegistrationOps to hook into the generic driver::Registration machinery
* i2c::DeviceId — a RawDeviceIdIndex implementation for I2C device IDs
--- MAINTAINERS | 7 + rust/bindings/bindings_helper.h | 1 + rust/kernel/i2c.rs | 391 ++++++++++++++++++++++++++++++++ rust/kernel/lib.rs | 2 + 4 files changed, 401 insertions(+) create mode 100644 rust/kernel/i2c.rs
MAINTAINERS +7 -0
@@ -11514,6 +11514,13 @@ F: include/linux/i2c.h F: include/uapi/linux/i2c-*.h F: include/uapi/linux/i2c.h +I2C SUBSYSTEM [RUST] +M: Igor Korotin <[email protected]> +R: Danilo Krummrich <[email protected]> +L: [email protected] +S: Maintained +F: rust/kernel/i2c.rs + I2C SUBSYSTEM HOST DRIVERS M: Andi Shyti <[email protected]> L: [email protected]
rust/bindings/bindings_helper.h +1 -0
@@ -53,6 +53,7 @@#include <linux/file.h> #include <linux/firmware.h> #include <linux/fs.h> +#include <linux/i2c.h> #include <linux/ioport.h> #include <linux/jiffies.h> #include <linux/jump_label.h>
rust/kernel/i2c.rs new file +391 -0
391 lines not on this page · raw message
rust/kernel/lib.rs +2 -0
@@ -86,6 +86,8 @@#[cfg( CONFIG_RUST_FW_LOADER_ABSTRACTIONS )] pub mod firmware;pub mod fs; +#[cfg( CONFIG_I2C = "y" )] +pub mod i2c;pub mod init;pub mod io;pub mod ioctl;
Signature · 1 line
2.43.0
Daniel Almeida
reviewerRe: [PATCH v3 1/3] rust: i2c: add basic I2C device and driver abstractions
Hi Igor, this is looking good AFAICT :)
Some minor comments inline.
Quoted 58 lines · Igor Korotin
Implement the core abstractions needed for I2C drivers, including:
*
i2c::Driver— the trait drivers must implement, includingprobe*
i2c::Device— a safe wrapper aroundstruct i2c_client*
i2c::Adapter— implementsdriver::RegistrationOpsto hook into the genericdriver::Registrationmachinery*
i2c::DeviceId— aRawDeviceIdIndeximplementation for I2C device IDsSigned-off-by Igor Korotin <[email protected]>---
MAINTAINERS | 7 + rust/bindings/bindings_helper.h | 1 + rust/kernel/i2c.rs | 391 ++++++++++++++++++++++++++++++++ rust/kernel/lib.rs | 2 + 4 files changed, 401 insertions(+)create mode 100644
rust/kernel/i2c.rsMAINTAINERS +0
-0@@ -11514,6 +11514,13 @@ F: include/linux/i2c.hF:
include/uapi/linux/i2c-*.h
F:include/uapi/linux/i2c.hMAINTAINERS +7
-0+I2C SUBSYSTEM [RUST] +M: Igor Korotin <[email protected]> +R: Danilo Krummrich <[email protected]> +L: [email protected] +S: Maintained +F: rust/kernel/i2c.rs +I2C SUBSYSTEM HOST DRIVERS
M: Andi Shyti <[email protected]>
L: [email protected]rust/bindings/bindings_helper.h +1
-0@@ -53,6 +53,7 @@#include <linux/file.h> #include <linux/firmware.h> #include <linux/fs.h> +#include <linux/i2c.h> #include <linux/ioport.h> #include <linux/jiffies.h> #include <linux/jump_label.h> rust/kernel/i2c.rs new file +0
-0
rust/kernel/i2c.rs line 3
Quoted 3 lines
rust/kernel/i2c.rs new file +3
-0@@ -0,0 +1,391 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! I2C Driver subsystem
Missing period?
Quoted 32 lines
rust/kernel/i2c.rs new file +31
-0+ +// I2C Driver abstractions. +use crate ::{ + acpi, container_of, device, + device_id::{RawDeviceId ,RawDeviceIdIndex }, + driver, + error::*, + of, + prelude::*, + types::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 =20 ; + +/// Create a new device id from an I2C 'id' string. +#[inline(always)] +pub const fn new (id: &'staticCStr ) ->Self { +build_assert! ( + id.len_with_nul () <=Self ::I2C_NAME_SIZE , +"ID exceeds 20 bytes"
rust/kernel/i2c.rs line 44
Quoted 10 lines
rust/kernel/i2c.rs new file +10
-0+ ); +let src = id.as_bytes_with_nul (); +// Replace with `bindings::acpi_device_id::default()` once stabilized for `const`. +// SAFETY: FFI type is valid to be zero-initialized. +let mut i2c: bindings::i2c_device_id =unsafe { core::mem::zeroed () }; +let mut i =0 ; +while i < src.len () { + i2c.name[i] = src[i]; + i +=1 ; + }
Nit:
for i in 0..src.len() {
}
Saves you from manually keeping track of the loop variable.
Quoted 54 lines
rust/kernel/i2c.rs new file +54
-0+ +Self (i2c) + } +} + +// SAFETY: `DeviceId` is a `#[repr(transparent)]` wrapper of `i2c_device_id` and does not add +// additional invariants, so it's safe to transmute to `RawType`. +unsafe impl RawDeviceId for DeviceId { +type RawType = bindings::i2c_device_id ; +} + +// SAFETY: `DRIVER_DATA_OFFSET` is the offset to the `driver_data` field. +unsafe impl RawDeviceIdIndex for DeviceId { +const DRIVER_DATA_OFFSET :usize = core::mem::offset_of!(bindings::i2c_device_id, driver_data); + +fn index (&self) ->usize { + self.0 .driver_dataas _ + } +} + +/// IdTable type for I2C +pub type IdTable <T > = &'staticdyn kernel::device_id::IdTable <DeviceId ,T >; + +/// Create a I2C `IdTable` with its alias for modpost. +#[macro_export] +macro_rules! i2c_device_table { + ($table_name:ident, $module_table_name:ident, $id_info_type: ty, $table_data: expr) => { +const $table_name: $crate::device_id::IdArray < + $crate::i2c::DeviceId , + $id_info_type, + { $table_data.len () }, + > = $crate::device_id::IdArray ::new ($table_data); + + $crate::module_device_table!("i2c" , $module_table_name, $table_name); + }; +} + +/// An adapter for the registration of I2C drivers. +pub struct Adapter <T :Driver >(T ); + +// SAFETY: A call to `unregister` for a given instance of `RegType` is guaranteed to be valid if +// a preceding call to `register` has been successful. +unsafe impl <T :Driver + 'static> driver::RegistrationOps for Adapter <T > { +type RegType = bindings::i2c_driver ; + +unsafe fn register ( + pdrv: &Opaque <Self ::RegType >, + name: &'staticCStr , + module: &'staticThisModule , + ) ->Result { +let i2c_table =match T ::I2C_ID_TABLE { +Some (table) => table.as_ptr (), +None => core::ptr::null (), + };
rust/kernel/i2c.rs line 108
Quoted 10 lines
rust/kernel/i2c.rs new file +10
-0+ +let of_table =match T ::OF_ID_TABLE { +Some (table) => table.as_ptr (), +None => core::ptr::null (), + }; + +let acpi_table =match T ::ACPI_ID_TABLE { +Some (table) => table.as_ptr (), +None => core::ptr::null (), + };
I wonder what happens if these are all None?
Quoted 14 lines
rust/kernel/i2c.rs new file +14
-0+ +// SAFETY: It's safe to set the fields of `struct i2c_client` on initialization. +unsafe { + (*pdrv.get ()).driver.name = name.as_char_ptr (); + (*pdrv.get ()).probe =Some (Self ::probe_callback); + (*pdrv.get ()).remove =Some (Self ::remove_callback); + (*pdrv.get ()).shutdown =Some (Self ::shutdown_callback); + (*pdrv.get ()).id_table = i2c_table; + (*pdrv.get ()).driver.of_match_table = of_table; + (*pdrv.get ()).driver.acpi_match_table = acpi_table; + } + +// SAFETY: `pdrv` is guaranteed to be a valid `RegType`. +to_result (unsafe { bindings::i2c_register_driver (module.0 , pdrv.get ()) })
rust/kernel/i2c.rs line 132
Quoted 10 lines
rust/kernel/i2c.rs new file +10
-0+ } + +unsafe fn unregister (pdrv: &Opaque <Self ::RegType >) { +// SAFETY: `pdrv` is guaranteed to be a valid `RegType`. +unsafe { bindings::i2c_del_driver (pdrv.get ()) } + } +} + +impl <T :Driver + 'static>Adapter <T > { +extern "C" fn probe_callback (pdev: *mut bindings::i2c_client ) -> kernel::ffi::c_int {
Nit: throughout the file: I think that pdev comes either from pci or platform devices? Maybe a different variable name should be used?
Quoted 20 lines
rust/kernel/i2c.rs new file +20
-0+// SAFETY: The I2C bus only ever calls the probe callback with a valid pointer to a +// `struct i2c_client`. +// +// INVARIANT: `pdev` is valid for the duration of `probe_callback()`. +let pdev =unsafe { &*pdev.cast ::<Device <device::CoreInternal >>() }; + +let info = +Self ::i2c_id_info (pdev).or_else (|| <Self as driver::Adapter >::id_info (pdev.as_ref ())); + + +from_result (|| { +let data =T ::probe (pdev, info)?; + + pdev.as_ref ().set_drvdata (data); +Ok (0 ) + }) + } + +extern "C" fn remove_callback (pdev: *mut bindings::i2c_client ) { +// SAFETY: `pdev` is a valid pointer to a `struct i2c_client`.
rust/kernel/i2c.rs line 162
Quoted 10 lines
rust/kernel/i2c.rs new file +10
-0+let pdev =unsafe { &*pdev.cast ::<Device <device::CoreInternal >>() }; + +// SAFETY: `remove_callback` is only ever called after a successful call to +// `probe_callback`, hence it's guaranteed that `Device::set_drvdata()` has been called +// and stored a `Pin<KBox<T>>`. +drop (unsafe { pdev.as_ref ().drvdata_obtain ::<Pin <KBox <T >>>() }); + } + +extern "C" fn shutdown_callback (pdev: *mut bindings::i2c_client ) { +let pdev =unsafe { &*pdev.cast ::<Device <device::Core >>() };
Missing SAFETY comment. There’s a lint for this now, thankfully :)
Quoted 7 lines
rust/kernel/i2c.rs new file +7
-0+ +T ::shutdown (pdev); + } + +/// The [`i2c::IdTable`] of the corresponding driver. +fn i2c_id_table () ->Option <IdTable <<Self as driver::Adapter >::IdInfo >> { +T ::I2C_ID_TABLE
rust/kernel/i2c.rs line 179
Quoted 10 lines
rust/kernel/i2c.rs new file +10
-0+ } + +/// Returns the driver's private data from the matching entry in the [`i2c::IdTable`], if any. +/// +/// If this returns `None`, it means there is no match with an entry in the [`i2c::IdTable`]. +fn i2c_id_info (dev: &Device ) ->Option <&'static <Self as driver::Adapter >::IdInfo > { +let table =Self ::i2c_id_table ()?; + +// SAFETY: +// - `table` has static lifetime, hence it's valid for read,
nit: for reads? Or perhaps a native English speaker can chime in.
rust/kernel/i2c.rs line 181
Quoted 2 lines
rust/kernel/i2c.rs new file +2
-0+// - `dev` is guaranteed to be valid while it's alive, and so is `pdev.as_ref().as_raw()`. +let raw_id =unsafe { bindings::i2c_match_id (table.as_ptr (), dev.as_raw ()) };
Wait, where’s pdev.as_ref().as_raw() here?
rust/kernel/i2c.rs line 191
Quoted 10 lines
rust/kernel/i2c.rs new file +10
-0+ +if raw_id.is_null () { +None + }else { +// SAFETY: `DeviceId` is a `#[repr(transparent)` wrapper of `struct i2c_device_id` and +// does not add additional invariants, so it's safe to transmute. +let id =unsafe { &*raw_id.cast ::<DeviceId >() }; + +Some (table.info (<DeviceId as crate ::device_id::RawDeviceIdIndex >::index (id))) + }
This could perhaps benefit from an early return to reduce the indentation?
Quoted 118 lines
rust/kernel/i2c.rs new file +118
-0+ } +} + +impl <T :Driver + 'static> driver::Adapter for Adapter <T > { +type IdInfo =T ::IdInfo ; + +fn of_id_table () ->Option <of::IdTable <Self ::IdInfo >> { +T ::OF_ID_TABLE + } + +fn acpi_id_table () ->Option <acpi::IdTable <Self ::IdInfo >> { +T ::ACPI_ID_TABLE + } +} + +/// Declares a kernel module that exposes a single i2c driver. +/// +/// # Examples +/// +/// ```ignore +/// kernel::module_i2c_driver! { +/// type: MyDriver, +/// name: "Module name", +/// authors: ["Author name"], +/// description: "Description", +/// license: "GPL v2", +/// } +/// ``` +#[macro_export] +macro_rules! module_i2c_driver { + ($($f:tt)*) => { + $crate::module_driver!(<T >, $crate ::i2c::Adapter <T >, { $($f)* }); + }; +} + +/// The i2c driver trait. +/// +/// Drivers must implement this trait in order to get a i2c driver registered. +/// +/// # Example +/// +///``` +/// # use kernel::{acpi, bindings, c_str, device::Core, i2c, of}; +/// +/// struct MyDriver; +/// +/// kernel::acpi_device_table!( +/// ACPI_TABLE, +/// MODULE_ACPI_TABLE, +/// <MyDriver as i2c::Driver>::IdInfo, +/// [ +/// (acpi::DeviceId::new(c_str!("LNUXBEEF")), ()) +/// ] +/// ); +/// +/// kernel::i2c_device_table!( +/// I2C_TABLE, +/// MODULE_I2C_TABLE, +/// <MyDriver as i2c::Driver>::IdInfo, +/// [ +/// (i2c::DeviceId::new(c_str!("rust_driver_i2c")), ()) +/// ] +/// ); +/// +/// kernel::of_device_table!( +/// OF_TABLE, +/// MODULE_OF_TABLE, +/// <MyDriver as i2c::Driver>::IdInfo, +/// [ +/// (of::DeviceId::new(c_str!("test,device")), ()) +/// ] +/// ); +/// +/// impl i2c::Driver for MyDriver { +/// type IdInfo = (); +/// const I2C_ID_TABLE: Option<i2c::IdTable<Self::IdInfo>> = Some(&I2C_TABLE); +/// const OF_ID_TABLE: Option<of::IdTable<Self::IdInfo>> = Some(&OF_TABLE); +/// const ACPI_ID_TABLE: Option<acpi::IdTable<Self::IdInfo>> = Some(&ACPI_TABLE); +/// +/// fn probe( +/// _pdev: &i2c::Device<Core>, +/// _id_info: Option<&Self::IdInfo>, +/// ) -> Result<Pin<KBox<Self>>> { +/// Err(ENODEV) +/// } +/// +/// fn shutdown(_pdev: &i2c::Device<Core>) { +/// } +/// } +///``` +pub trait Driver :Send { +/// The type holding information about each device id supported by the driver. +// TODO: Use `associated_type_defaults` once stabilized: +// +// ``` +// type IdInfo: 'static = (); +// ``` +type IdInfo : 'static; + +/// The table of device ids supported by the driver. +const I2C_ID_TABLE :Option <IdTable <Self ::IdInfo >> =None ; + +/// The table of OF device ids supported by the driver. +const OF_ID_TABLE :Option <of::IdTable <Self ::IdInfo >> =None ; + +/// The table of ACPI device ids supported by the driver. +const ACPI_ID_TABLE :Option <acpi::IdTable <Self ::IdInfo >> =None ; + +/// I2C driver probe. +/// +/// Called when a new i2c device is added or discovered. +/// Implementers should attempt to initialize the device here. +fn probe (dev: &Device <device::Core >, id_info:Option <&Self ::IdInfo >) + ->Result <Pin <KBox <Self >>>; + +/// I2C driver shutdown +/// +/// Called when
rust/kernel/i2c.rs line 319
Quoted 10 lines
rust/kernel/i2c.rs new file +10
-0+fn shutdown (dev: &Device <device::Core >) { +let _ = dev; + } +} + +/// The i2c client representation. +/// +/// This structure represents the Rust abstraction for a C `struct i2c_client`. The +/// implementation abstracts the usage of an already existing C `struct i2c_client` within Rust +/// code that we get passed from the C side.
nit: "An existing C struct i2c_client that gets passed from the C side” reads a bit better, perhaps?
Quoted 30 lines
rust/kernel/i2c.rs new file +30
-0+/// +/// # Invariants +/// +/// A [`Device`] instance represents a valid `struct i2c_client` created by the C portion of +/// the kernel. +#[repr(transparent)] +pub struct Device <Ctx : device::DeviceContext = device::Normal >( +Opaque <bindings::i2c_client >, +PhantomData <Ctx >, +); + +impl <Ctx : device::DeviceContext >Device <Ctx > { +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 <Self >) {
rust/kernel/i2c.rs line 359
Quoted 10 lines
rust/kernel/i2c.rs new file +10
-0+// 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 <Ctx : device::DeviceContext >AsRef <device::Device <Ctx >>for Device <Ctx > { +fn as_ref (&self) -> &device::Device <Ctx > { +// SAFETY: By the type invariant of `Self`, `self.as_raw()` is a pointer to a valid +// `struct i2c_client`. +let dev =unsafe {addr_of_mut! ((*self.as_raw ()).dev) };
&raw mut
I also wonder whether this unsafe block can be made shorter, as only the dereference is unsafe.
Quoted 49 lines
rust/kernel/i2c.rs new file +32
-0+ +// SAFETY: `dev` points to a valid `struct device`. +unsafe { device::Device ::from_raw (dev) } + } +} + +impl <Ctx : device::DeviceContext >TryFrom <&device::Device <Ctx >>for &Device <Ctx > { +type Error = kernel::error::Error ; + +fn try_from (dev: &device::Device <Ctx >) ->Result <Self ,Self ::Error > { +// SAFETY: By the type invariant of `Device`, `dev.as_raw()` is a valid pointer to a +// `struct device`. +if unsafe { bindings::i2c_verify_client (dev.as_raw ()).is_null () } { +return Err (EINVAL ); + } + +// SAFETY: We've just verified that the type of `dev` equals to +// `bindings::i2c_client_type`, hence `dev` must be embedded in a valid +// `struct i2c_client` as guaranteed by the corresponding C code. +let pdev =unsafe {container_of! (dev.as_raw (), bindings::i2c_client, 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 `Device` +// (i.e. `Device<Normal>) are thread safe. +unsafe impl Sync for Device {}rust/kernel/lib.rs +2
-0@@ -86,6 +86,8 @@#[cfg( CONFIG_RUST_FW_LOADER_ABSTRACTIONS )] pub mod firmware;pub mod fs; +#[cfg( CONFIG_I2C = "y" )] +pub mod i2c;pub mod init;pub mod io;pub mod ioctl;Signature · 1 line
2.43.0
— Daniel
Danilo Krummrich
reviewerRe: [PATCH v3 1/3] rust: i2c: add basic I2C device and driver abstractions
Quoted 22 lines · Daniel Almeida
Quoted 9 lines
rust/kernel/i2c.rs new file +9
-0+impl DeviceId { +const I2C_NAME_SIZE :usize =20 ; + +/// Create a new device id from an I2C 'id' string. +#[inline(always)] +pub const fn new (id: &'staticCStr ) ->Self { +build_assert! ( + id.len_with_nul () <=Self ::I2C_NAME_SIZE , +"ID exceeds 20 bytes" rust/kernel/i2c.rs line 44
Quoted 10 lines
rust/kernel/i2c.rs new file +10
-0+ ); +let src = id.as_bytes_with_nul (); +// Replace with `bindings::acpi_device_id::default()` once stabilized for `const`. +// SAFETY: FFI type is valid to be zero-initialized. +let mut i2c: bindings::i2c_device_id =unsafe { core::mem::zeroed () }; +let mut i =0 ; +while i < src.len () { + i2c.name[i] = src[i]; + i +=1 ; + }Nit:
Quoted 5 lines
for i in 0..src.len() {
}
Saves you from manually keeping track of the loop variable.
That would be nice, but we can't do this in cost context. :(
Quoted 21 lines
Quoted 8 lines
rust/kernel/i2c.rs new file +8
-0+// 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 <Self >) {rust/kernel/i2c.rs line 359
Quoted 10 lines
rust/kernel/i2c.rs new file +10
-0+// 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 <Ctx : device::DeviceContext >AsRef <device::Device <Ctx >>for Device <Ctx > { +fn as_ref (&self) -> &device::Device <Ctx > { +// SAFETY: By the type invariant of `Self`, `self.as_raw()` is a pointer to a valid +// `struct i2c_client`. +let dev =unsafe {addr_of_mut! ((*self.as_raw ()).dev) };&raw mut
Quoted 2 lines
I also wonder whether this unsafe block can be made shorter, as only the dereference is unsafe.
No, this would create a temporary value.
Re: [PATCH v3 1/3] rust: i2c: add basic I2C device and driver abstractions
In reply to Daniel AlmeidaHello Daniel
Thank you for the review.
Quoted 58 lines · Daniel Almeida
Quoted 58 lines
rust/kernel/i2c.rs new file +58
-0+ +Self (i2c) + } +} + +// SAFETY: `DeviceId` is a `#[repr(transparent)]` wrapper of `i2c_device_id` and does not add +// additional invariants, so it's safe to transmute to `RawType`. +unsafe impl RawDeviceId for DeviceId { +type RawType = bindings::i2c_device_id ; +} + +// SAFETY: `DRIVER_DATA_OFFSET` is the offset to the `driver_data` field. +unsafe impl RawDeviceIdIndex for DeviceId { +const DRIVER_DATA_OFFSET :usize = core::mem::offset_of!(bindings::i2c_device_id, driver_data); + +fn index (&self) ->usize { + self.0 .driver_dataas _ + } +} + +/// IdTable type for I2C +pub type IdTable <T > = &'staticdyn kernel::device_id::IdTable <DeviceId ,T >; + +/// Create a I2C `IdTable` with its alias for modpost. +#[macro_export] +macro_rules! i2c_device_table { + ($table_name:ident, $module_table_name:ident, $id_info_type: ty, $table_data: expr) => { +const $table_name: $crate::device_id::IdArray < + $crate::i2c::DeviceId , + $id_info_type, + { $table_data.len () }, + > = $crate::device_id::IdArray ::new ($table_data); + + $crate::module_device_table!("i2c" , $module_table_name, $table_name); + }; +} + +/// An adapter for the registration of I2C drivers. +pub struct Adapter <T :Driver >(T ); + +// SAFETY: A call to `unregister` for a given instance of `RegType` is guaranteed to be valid if +// a preceding call to `register` has been successful. +unsafe impl <T :Driver + 'static> driver::RegistrationOps for Adapter <T > { +type RegType = bindings::i2c_driver ; + +unsafe fn register ( + pdrv: &Opaque <Self ::RegType >, + name: &'staticCStr , + module: &'staticThisModule , + ) ->Result { +let i2c_table =match T ::I2C_ID_TABLE { +Some (table) => table.as_ptr (), +None => core::ptr::null (), + }; + +let of_table =match T ::OF_ID_TABLE { +Some (table) => table.as_ptr (), + None => core::ptr::null(),
Quoted 9 lines
rust/kernel/i2c.rs line 108
Quoted 6 lines
rust/kernel/i2c.rs new file +6
-0+ }; + +let acpi_table =match T ::ACPI_ID_TABLE { +Some (table) => table.as_ptr (), +None => core::ptr::null (), + };I wonder what happens if these are all None?
C code in the i2c_register_driver function doesn't require id_table, OF table, or ACPI table to be non-NULL. Technically, there's no issue registering such an I2C driver. Though there's the following code in i2c_device_probe:
/*
* An I2C ID table is not mandatory, if and only if, a suitable OF
* or ACPI ID table is supplied for the probing device.
*/
if (!driver->id_table &&
!acpi_driver_match_device(dev, dev->driver) &&
!i2c_of_match_device(dev->driver->of_match_table, client)) {
status = -ENODEV;
goto put_sync_adapter;
}Based on that, maybe it makes sense to add some kind of compile-time/build-time assertion to make sure the developer defines at least one of those tables. If so, I'm happy to think about how to implement something like that.
Quoted 19 lines
Quoted 19 lines
rust/kernel/i2c.rs new file +19
-0+ +// SAFETY: It's safe to set the fields of `struct i2c_client` on initialization. +unsafe { + (*pdrv.get ()).driver.name = name.as_char_ptr (); + (*pdrv.get ()).probe =Some (Self ::probe_callback); + (*pdrv.get ()).remove =Some (Self ::remove_callback); + (*pdrv.get ()).shutdown =Some (Self ::shutdown_callback); + (*pdrv.get ()).id_table = i2c_table; + (*pdrv.get ()).driver.of_match_table = of_table; + (*pdrv.get ()).driver.acpi_match_table = acpi_table; + } + +// SAFETY: `pdrv` is guaranteed to be a valid `RegType`. +to_result (unsafe { bindings::i2c_register_driver (module.0 , pdrv.get ()) }) + } + +unsafe fn unregister (pdrv: &Opaque <Self ::RegType >) { +// SAFETY: `pdrv` is guaranteed to be a valid `RegType`. +unsafe { bindings::i2c_del_driver (pdrv.get ()) }
Quoted 9 lines
rust/kernel/i2c.rs line 132
Quoted 5 lines
rust/kernel/i2c.rs new file +5
-0+ } +} + +impl <T :Driver + 'static>Adapter <T > { +extern "C" fn probe_callback (pdev: *mut bindings::i2c_client ) -> kernel::ffi::c_int {Nit: throughout the file: I think that pdev comes either from pci or platform devices? Maybe a different variable name should be used?
To be honest, I always thought pdev/pdrv/etc just meant "pointer to device"/"pointer to driver" and not necessarily PCI/platform. If it actually implies platform/PCI, then yeah, I agree — better to use something like idev/idrv, as Danilo also pointed out.
Quoted 5 lines
rust/kernel/i2c.rs line 181
Quoted 2 lines
rust/kernel/i2c.rs new file +2
-0+// - `dev` is guaranteed to be valid while it's alive, and so is `pdev.as_ref().as_raw()`. +let raw_id =unsafe { bindings::i2c_match_id (table.as_ptr (), dev.as_raw ()) };Wait, where’s
pdev.as_ref().as_raw()here?
Yeah, good catch. There was a significant rework between v2 and v3, and I didn’t clean up all the comments properly. My bad.
Noted all the other comments too — I’ll fix those in the next drop.
Best Regards
Igor
Danilo Krummrich
reviewerRe: [PATCH v3 1/3] rust: i2c: add basic I2C device and driver abstractions
In reply to Igor Korotin(Cc: Wolfram)
Quoted 25 lines · Igor Korotin
Implement the core abstractions needed for I2C drivers, including:
*
i2c::Driver— the trait drivers must implement, includingprobe*
i2c::Device— a safe wrapper aroundstruct i2c_client*
i2c::Adapter— implementsdriver::RegistrationOpsto hook into the genericdriver::Registrationmachinery*
i2c::DeviceId— aRawDeviceIdIndeximplementation for I2C device IDsSigned-off-by Igor Korotin <[email protected]>--- MAINTAINERS | 7 + rust/bindings/bindings_helper.h | 1 + rust/kernel/i2c.rs | 391 ++++++++++++++++++++++++++++++++ rust/kernel/lib.rs | 2 + 4 files changed, 401 insertions(+) create mode 100644 rust/kernel/i2c.rsMAINTAINERS +0
-0
MAINTAINERS F: include/linux/i2c.h · line 11517
Quoted 4 lines
MAINTAINERS +1
-0@@ -11514,6 +11514,13 @@ F: include/linux/i2c.h F: include/uapi/linux/i2c-*.h F: include/uapi/linux/i2c.h +I2C SUBSYSTEM [RUST]
Has this been agreed with Wolfram off-list? (I'm asking since I haven't seen a reply from him in v2.)
In case you just did not receive an answer yet, it's fine to proceed with a proposal as you do here. However, please make sure to keep Wolfram in the loop. :)
In either case, which tree should patches flow through?
MAINTAINERS F: include/linux/i2c.h · line 11519
Quoted 2 lines
MAINTAINERS +2
-0+M: Igor Korotin <[email protected]> +R: Danilo Krummrich <[email protected]>
If you'd like me to keep an eye on this as well, this is fine with me.
Quoted 76 lines
MAINTAINERS +4
-0+L: [email protected] +S: Maintained +F: rust/kernel/i2c.rs + I2C SUBSYSTEM HOST DRIVERS M: Andi Shyti <[email protected]> L: [email protected]rust/bindings/bindings_helper.h +1
-0@@ -53,6 +53,7 @@#include <linux/file.h> #include <linux/firmware.h> #include <linux/fs.h> +#include <linux/i2c.h> #include <linux/ioport.h> #include <linux/jiffies.h> #include <linux/jump_label.h> rust/kernel/i2c.rs new file +51
-0@@ -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 ,RawDeviceIdIndex }, + driver, + error::*, + of, + prelude::*, + types::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 =20 ; + +/// Create a new device id from an I2C 'id' string. +#[inline(always)] +pub const fn new (id: &'staticCStr ) ->Self { +build_assert! ( + id.len_with_nul () <=Self ::I2C_NAME_SIZE , +"ID exceeds 20 bytes" + ); +let src = id.as_bytes_with_nul (); +// Replace with `bindings::acpi_device_id::default()` once stabilized for `const`. +// SAFETY: FFI type is valid to be zero-initialized. +let mut i2c: bindings::i2c_device_id =unsafe { core::mem::zeroed () }; +let mut i =0 ; +while i < src.len () { + i2c.name[i] = src[i]; + i +=1 ; + } + +Self (i2c) + } +} + +// SAFETY: `DeviceId` is a `#[repr(transparent)]` wrapper of `i2c_device_id` and does not add +// additional invariants, so it's safe to transmute to `RawType`.
rust/kernel/i2c.rs line 61
Quoted 10 lines
rust/kernel/i2c.rs new file +10
-0+unsafe impl RawDeviceId for DeviceId { +type RawType = bindings::i2c_device_id ; +} + +// SAFETY: `DRIVER_DATA_OFFSET` is the offset to the `driver_data` field. +unsafe impl RawDeviceIdIndex for DeviceId { +const DRIVER_DATA_OFFSET :usize = core::mem::offset_of!(bindings::i2c_device_id, driver_data); + +fn index (&self) ->usize { + self.0 .driver_dataas _
This cast shouldn't be needed.
Quoted 20 lines
rust/kernel/i2c.rs new file +20
-0+ } +} + +/// IdTable type for I2C +pub type IdTable <T > = &'staticdyn kernel::device_id::IdTable <DeviceId ,T >; + +/// Create a I2C `IdTable` with its alias for modpost. +#[macro_export] +macro_rules! i2c_device_table { + ($table_name:ident, $module_table_name:ident, $id_info_type: ty, $table_data: expr) => { +const $table_name: $crate::device_id::IdArray < + $crate::i2c::DeviceId , + $id_info_type, + { $table_data.len () }, + > = $crate::device_id::IdArray ::new ($table_data); + + $crate::module_device_table!("i2c" , $module_table_name, $table_name); + }; +} +
rust/kernel/i2c.rs line 91
Quoted 10 lines
rust/kernel/i2c.rs new file +10
-0+/// An adapter for the registration of I2C drivers. +pub struct Adapter <T :Driver >(T ); + +// SAFETY: A call to `unregister` for a given instance of `RegType` is guaranteed to be valid if +// a preceding call to `register` has been successful. +unsafe impl <T :Driver + 'static> driver::RegistrationOps for Adapter <T > { +type RegType = bindings::i2c_driver ; + +unsafe fn register ( + pdrv: &Opaque<Self::RegType>,
Rather idrv or similar I think; same for various cases below.
Quoted 7 lines
rust/kernel/i2c.rs new file +7
-0+ name: &'static CStr, + module: &'static ThisModule, + ) -> Result { + let i2c_table = match T::I2C_ID_TABLE { +Some (table) => table.as_ptr (), +None => core::ptr::null (), + };
rust/kernel/i2c.rs line 108
Quoted 10 lines
rust/kernel/i2c.rs new file +10
-0+ +let of_table =match T ::OF_ID_TABLE { +Some (table) => table.as_ptr (), +None => core::ptr::null (), + }; + +let acpi_table =match T ::ACPI_ID_TABLE { +Some (table) => table.as_ptr (), +None => core::ptr::null (), + };
I suppose other than platform drivers, I2C drivers can't match by name, hence I think we should fail if none of the above is provided.
Quoted 44 lines
rust/kernel/i2c.rs new file +44
-0+ +// SAFETY: It's safe to set the fields of `struct i2c_client` on initialization. +unsafe { + (*pdrv.get ()).driver.name = name.as_char_ptr (); + (*pdrv.get ()).probe =Some (Self ::probe_callback); + (*pdrv.get ()).remove =Some (Self ::remove_callback); + (*pdrv.get ()).shutdown =Some (Self ::shutdown_callback); + (*pdrv.get ()).id_table = i2c_table; + (*pdrv.get ()).driver.of_match_table = of_table; + (*pdrv.get ()).driver.acpi_match_table = acpi_table; + } + +// SAFETY: `pdrv` is guaranteed to be a valid `RegType`. +to_result (unsafe { bindings::i2c_register_driver (module.0 , pdrv.get ()) }) + } + +unsafe fn unregister (pdrv: &Opaque <Self ::RegType >) { +// SAFETY: `pdrv` is guaranteed to be a valid `RegType`. +unsafe { bindings::i2c_del_driver (pdrv.get ()) } + } +} + +impl <T :Driver + 'static>Adapter <T > { +extern "C" fn probe_callback (pdev: *mut bindings::i2c_client ) -> kernel::ffi::c_int { +// SAFETY: The I2C bus only ever calls the probe callback with a valid pointer to a +// `struct i2c_client`. +// +// INVARIANT: `pdev` is valid for the duration of `probe_callback()`. +let pdev =unsafe { &*pdev.cast ::<Device <device::CoreInternal >>() }; + +let info = +Self ::i2c_id_info (pdev).or_else (|| <Self as driver::Adapter >::id_info (pdev.as_ref ())); + + +from_result (|| { +let data =T ::probe (pdev, info)?; + + pdev.as_ref ().set_drvdata (data); +Ok (0 ) + }) + } + +extern "C" fn remove_callback (pdev: *mut bindings::i2c_client ) { +// SAFETY: `pdev` is a valid pointer to a `struct i2c_client`.
rust/kernel/i2c.rs line 162
Quoted 10 lines
rust/kernel/i2c.rs new file +10
-0+let pdev =unsafe { &*pdev.cast ::<Device <device::CoreInternal >>() }; + +// SAFETY: `remove_callback` is only ever called after a successful call to +// `probe_callback`, hence it's guaranteed that `Device::set_drvdata()` has been called +// and stored a `Pin<KBox<T>>`. +drop (unsafe { pdev.as_ref ().drvdata_obtain ::<Pin <KBox <T >>>() }); + } + +extern "C" fn shutdown_callback (pdev: *mut bindings::i2c_client ) { +let pdev =unsafe { &*pdev.cast ::<Device <device::Core >>() };
Missing safety comment, please make sure to compile with CLIPPY=1. I think I also saw some checkpatch.pl warnings.
Quoted 137 lines
rust/kernel/i2c.rs new file +137
-0+ +T ::shutdown (pdev); + } + +/// The [`i2c::IdTable`] of the corresponding driver. +fn i2c_id_table () ->Option <IdTable <<Self as driver::Adapter >::IdInfo >> { +T ::I2C_ID_TABLE + } + +/// Returns the driver's private data from the matching entry in the [`i2c::IdTable`], if any. +/// +/// If this returns `None`, it means there is no match with an entry in the [`i2c::IdTable`]. +fn i2c_id_info (dev: &Device ) ->Option <&'static <Self as driver::Adapter >::IdInfo > { +let table =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 =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 `struct i2c_device_id` and +// does not add additional invariants, so it's safe to transmute. +let id =unsafe { &*raw_id.cast ::<DeviceId >() }; + +Some (table.info (<DeviceId as crate ::device_id::RawDeviceIdIndex >::index (id))) + } + } +} + +impl <T :Driver + 'static> driver::Adapter for Adapter <T > { +type IdInfo =T ::IdInfo ; + +fn of_id_table () ->Option <of::IdTable <Self ::IdInfo >> { +T ::OF_ID_TABLE + } + +fn acpi_id_table () ->Option <acpi::IdTable <Self ::IdInfo >> { +T ::ACPI_ID_TABLE + } +} + +/// Declares a kernel module that exposes a single i2c driver. +/// +/// # Examples +/// +/// ```ignore +/// kernel::module_i2c_driver! { +/// type: MyDriver, +/// name: "Module name", +/// authors: ["Author name"], +/// description: "Description", +/// license: "GPL v2", +/// } +/// ``` +#[macro_export] +macro_rules! module_i2c_driver { + ($($f:tt)*) => { + $crate::module_driver!(<T >, $crate ::i2c::Adapter <T >, { $($f)* }); + }; +} + +/// The i2c driver trait. +/// +/// Drivers must implement this trait in order to get a i2c driver registered. +/// +/// # Example +/// +///``` +/// # use kernel::{acpi, bindings, c_str, device::Core, i2c, of}; +/// +/// struct MyDriver; +/// +/// kernel::acpi_device_table!( +/// ACPI_TABLE, +/// MODULE_ACPI_TABLE, +/// <MyDriver as i2c::Driver>::IdInfo, +/// [ +/// (acpi::DeviceId::new(c_str!("LNUXBEEF")), ()) +/// ] +/// ); +/// +/// kernel::i2c_device_table!( +/// I2C_TABLE, +/// MODULE_I2C_TABLE, +/// <MyDriver as i2c::Driver>::IdInfo, +/// [ +/// (i2c::DeviceId::new(c_str!("rust_driver_i2c")), ()) +/// ] +/// ); +/// +/// kernel::of_device_table!( +/// OF_TABLE, +/// MODULE_OF_TABLE, +/// <MyDriver as i2c::Driver>::IdInfo, +/// [ +/// (of::DeviceId::new(c_str!("test,device")), ()) +/// ] +/// ); +/// +/// impl i2c::Driver for MyDriver { +/// type IdInfo = (); +/// const I2C_ID_TABLE: Option<i2c::IdTable<Self::IdInfo>> = Some(&I2C_TABLE); +/// const OF_ID_TABLE: Option<of::IdTable<Self::IdInfo>> = Some(&OF_TABLE); +/// const ACPI_ID_TABLE: Option<acpi::IdTable<Self::IdInfo>> = Some(&ACPI_TABLE); +/// +/// fn probe( +/// _pdev: &i2c::Device<Core>, +/// _id_info: Option<&Self::IdInfo>, +/// ) -> Result<Pin<KBox<Self>>> { +/// Err(ENODEV) +/// } +/// +/// fn shutdown(_pdev: &i2c::Device<Core>) { +/// } +/// } +///``` +pub trait Driver :Send { +/// The type holding information about each device id supported by the driver. +// TODO: Use `associated_type_defaults` once stabilized: +// +// ``` +// type IdInfo: 'static = (); +// ``` +type IdInfo : 'static; + +/// The table of device ids supported by the driver. +const I2C_ID_TABLE :Option <IdTable <Self ::IdInfo >> =None ; + +/// The table of OF device ids supported by the driver. +const OF_ID_TABLE :Option <of::IdTable <Self ::IdInfo >> =None ; + +/// The table of ACPI device ids supported by the driver. +const ACPI_ID_TABLE :Option <acpi::IdTable <Self ::IdInfo >> =None ; +
rust/kernel/i2c.rs line 309
Quoted 10 lines
rust/kernel/i2c.rs new file +10
-0+/// 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 <device::Core >, id_info:Option <&Self ::IdInfo >) + ->Result <Pin <KBox <Self >>>; + +/// I2C driver shutdown +/// +/// Called when
Yes? :)
Quoted 32 lines
rust/kernel/i2c.rs new file +32
-0+fn shutdown (dev: &Device <device::Core >) { +let _ = dev; + } +} + +/// The i2c client representation. +/// +/// This structure represents the Rust abstraction for a C `struct i2c_client`. The +/// implementation abstracts the usage of an already existing C `struct i2c_client` within Rust +/// code that we get passed from the C side. +/// +/// # Invariants +/// +/// A [`Device`] instance represents a valid `struct i2c_client` created by the C portion of +/// the kernel. +#[repr(transparent)] +pub struct Device <Ctx : device::DeviceContext = device::Normal >( +Opaque <bindings::i2c_client >, +PhantomData <Ctx >, +); + +impl <Ctx : device::DeviceContext >Device <Ctx > { +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 ); +
rust/kernel/i2c.rs line 351
Quoted 10 lines
rust/kernel/i2c.rs new file +10
-0+// 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 <Self >) { +// SAFETY: The safety requirements guarantee that the refcount is non-zero. +unsafe { bindings::put_device (addr_of_mut! ((*obj.as_ref ().as_raw ()).dev)) }
You can also use &raw mut.
rust/kernel/i2c.rs line 359
Quoted 8 lines
rust/kernel/i2c.rs new file +8
-0+ } +} + +impl <Ctx : device::DeviceContext >AsRef <device::Device <Ctx >>for Device <Ctx > { +fn as_ref (&self) -> &device::Device <Ctx > { +// SAFETY: By the type invariant of `Self`, `self.as_raw()` is a pointer to a valid +// `struct i2c_client`. +let dev =unsafe {addr_of_mut! ((*self.as_raw ()).dev) };
Same here.
Quoted 47 lines
rust/kernel/i2c.rs new file +32
-0+ +// SAFETY: `dev` points to a valid `struct device`. +unsafe { device::Device ::from_raw (dev) } + } +} + +impl <Ctx : device::DeviceContext >TryFrom <&device::Device <Ctx >>for &Device <Ctx > { +type Error = kernel::error::Error ; + +fn try_from (dev: &device::Device <Ctx >) ->Result <Self ,Self ::Error > { +// SAFETY: By the type invariant of `Device`, `dev.as_raw()` is a valid pointer to a +// `struct device`. +if unsafe { bindings::i2c_verify_client (dev.as_raw ()).is_null () } { +return Err (EINVAL ); + } + +// SAFETY: We've just verified that the type of `dev` equals to +// `bindings::i2c_client_type`, hence `dev` must be embedded in a valid +// `struct i2c_client` as guaranteed by the corresponding C code. +let pdev =unsafe {container_of! (dev.as_raw (), bindings::i2c_client, 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 `Device` +// (i.e. `Device<Normal>) are thread safe. +unsafe impl Sync for Device {}rust/kernel/lib.rs +2
-0@@ -86,6 +86,8 @@#[cfg( CONFIG_RUST_FW_LOADER_ABSTRACTIONS )] pub mod firmware;pub mod fs; +#[cfg( CONFIG_I2C = "y" )] +pub mod i2c;pub mod init;pub mod io;pub mod ioctl;Signature · 1 line
2.43.0
Wolfram Sang
Acked-byRe: [PATCH v3 1/3] rust: i2c: add basic I2C device and driver abstractions
Hi all,
Quoted 8 lines
Quoted 8 lines
MAINTAINERS +0
-0@@ -11514,6 +11514,13 @@ F: include/linux/i2c.h F: include/uapi/linux/i2c-*.h F: include/uapi/linux/i2c.h
Quoted 8 lines
MAINTAINERS F: include/linux/i2c.h · line 11517
Quoted 1 line
MAINTAINERS +1
-0+I2C SUBSYSTEM [RUST]Has this been agreed with Wolfram off-list? (I'm asking since I haven't seen a reply from him in v2.)
In case you just did not receive an answer yet, it's fine to proceed with a proposal as you do here. However, please make sure to keep Wolfram in the loop. :)
Thank you for pointing out this thread, much appreciated. I think (I hope) I said previously that I am supportive of the bindings in general but I can't help at all because I don't speak Rust. Given that, I am more than happy to see this MAINTAINERS entry for Rust I2C. This part is definitely:
Quoted 1 line
In either case, which tree should patches flow through?
As long as you don't touch generic I2C header files, I suggest some Rust tree. I guess patches will have more dependencies on what is in that tree.
Quoted 6 lines
MAINTAINERS F: include/linux/i2c.h · line 11519
Quoted 2 lines
MAINTAINERS +2
-0+M: Igor Korotin <[email protected]> +R: Danilo Krummrich <[email protected]>If you'd like me to keep an eye on this as well, this is fine with me.
Quoted 1 line
MAINTAINERS +1
-0+L: [email protected]
I think it is worth adding the Linux I2C list here as well. Other people there do speak Rust.
Quoted 2 lines
MAINTAINERS F: include/linux/i2c.h · line 11522
Quoted 2 lines
MAINTAINERS +2
-0+S: Maintained +F: rust/kernel/i2c.rs
Thanks,
Wolfram
Danilo Krummrich
reviewerRe: [PATCH v3 1/3] rust: i2c: add basic I2C device and driver abstractions
Quoted 25 lines · Wolfram Sang
Hi all,
Quoted 8 lines
Quoted 8 lines
MAINTAINERS +0
-0@@ -11514,6 +11514,13 @@ F: include/linux/i2c.h F: include/uapi/linux/i2c-*.h F: include/uapi/linux/i2c.hQuoted 8 lines
MAINTAINERS F: include/linux/i2c.h · line 11517
Quoted 1 line
MAINTAINERS +1
-0+I2C SUBSYSTEM [RUST]Has this been agreed with Wolfram off-list? (I'm asking since I haven't seen a reply from him in v2.)
In case you just did not receive an answer yet, it's fine to proceed with a proposal as you do here. However, please make sure to keep Wolfram in the loop. :)
Thank you for pointing out this thread, much appreciated. I think (I hope) I said previously that I am supportive of the bindings in general but I can't help at all because I don't speak Rust. Given that, I am more than happy to see this
MAINTAINERSentry for Rust I2C. This part is definitely:
Quoted 7 lines
Acked-by Wolfram Sang <[email protected]>Quoted 1 line
In either case, which tree should patches flow through?
As long as you don't touch generic I2C header files, I suggest some Rust tree. I guess patches will have more dependencies on what is in that tree.
Maybe it would also be a good option to take PRs from Igor? I think it could be a great way to get in touch with some of the Rust work over time without having to commit to maintaining it. (Of course, this is entirely up to you though.)
While I encourage the above, one fallback alternative could be to take patches through the driver-core tree -- at least for now. Given that this is a bus abstraction and hence most infrastructure used is generic device / driver infrastructure, there's some synergy.
Quoted 9 lines
Quoted 6 lines
MAINTAINERS F: include/linux/i2c.h · line 11519
Quoted 2 lines
MAINTAINERS +2
-0+M: Igor Korotin <[email protected]> +R: Danilo Krummrich <[email protected]>If you'd like me to keep an eye on this as well, this is fine with me.
Quoted 1 line
MAINTAINERS +1
-0+L: [email protected]I think it is worth adding the Linux I2C list here as well. Other people there do speak Rust.
Agreed, maybe it'd also be good for you to be listed as reviewer?
Re: [PATCH v3 1/3] rust: i2c: add basic I2C device and driver abstractions
Hi,
Quoted 3 lines
Maybe it would also be a good option to take PRs from Igor? I think it could be a great way to get in touch with some of the Rust work over time without having to commit to maintaining it. (Of course, this is entirely up to you though.)
Makes sense...
Quoted 4 lines
While I encourage the above, one fallback alternative could be to take patches through the driver-core tree -- at least for now. Given that this is a bus abstraction and hence most infrastructure used is generic device / driver infrastructure, there's some synergy.
... especially if the alternative is "only" the driver core tree. My tree makes more sense then. I thought there might be a more fitting "Rust" tree. If that's not the case, I can pull from Igor, no problem. We will see how this goes dependency-wise.
Quoted 6 lines
Quoted 4 lines
Quoted 1 line
Quoted 1 line
I think it is worth adding the Linux I2C list here as well. Other people there do speak Rust.
Agreed, maybe it'd also be good for you to be listed as reviewer?
Please no. I really can't review these patches. I will have a short look at them when I pull from Igor. If you need my experience while developing, just CC me in those (probably few) cases.
Have a nice weekend,
Wolfram
Danilo Krummrich
reviewerRe: [PATCH v3 1/3] rust: i2c: add basic I2C device and driver abstractions
Quoted 7 lines · Wolfram Sang
Hi,
Quoted 3 lines
Maybe it would also be a good option to take PRs from Igor? I think it could be a great way to get in touch with some of the Rust work over time without having to commit to maintaining it. (Of course, this is entirely up to you though.)
Makes sense...
Great!
Quoted 9 lines
Quoted 4 lines
While I encourage the above, one fallback alternative could be to take patches through the driver-core tree -- at least for now. Given that this is a bus abstraction and hence most infrastructure used is generic device / driver infrastructure, there's some synergy.
... especially if the alternative is "only" the driver core tree. My tree makes more sense then. I thought there might be a more fitting "Rust" tree. If that's not the case, I can pull from Igor, no problem. We will see how this goes dependency-wise.
There's of course also Miguel's Rust tree, but this is more for "core Rust infrastructure".
Citing Miguel [1]:
"We try to reserve the global Rust tree fallback for cases where there is really no other way, e.g. when a key subsystem doesn't want anything to do with Rust at all and thus would block many others otherwise."
[1] https://lore.kernel.org/all/CANiq72=+UUFVemSGHvzkX5FoOv-U5YRDKQkbKjYwqEfex-ey6w@mail.gmail.com/
Re: [PATCH v3 1/3] rust: i2c: add basic I2C device and driver abstractions
In reply to Danilo KrummrichHello Danilo
Quoted 40 lines · Danilo Krummrich
(Cc: Wolfram)
Quoted 25 lines · Igor Korotin
Implement the core abstractions needed for I2C drivers, including:
*
i2c::Driver— the trait drivers must implement, includingprobe*
i2c::Device— a safe wrapper aroundstruct i2c_client*
i2c::Adapter— implementsdriver::RegistrationOpsto hook into the genericdriver::Registrationmachinery*
i2c::DeviceId— aRawDeviceIdIndeximplementation for I2C device IDsSigned-off-by Igor Korotin <[email protected]>--- MAINTAINERS | 7 + rust/bindings/bindings_helper.h | 1 + rust/kernel/i2c.rs | 391 ++++++++++++++++++++++++++++++++ rust/kernel/lib.rs | 2 + 4 files changed, 401 insertions(+) create mode 100644 rust/kernel/i2c.rsMAINTAINERS +0
-0MAINTAINERS F: include/linux/i2c.h · line 11517
Quoted 4 lines
MAINTAINERS +1
-0@@ -11514,6 +11514,13 @@ F: include/linux/i2c.h F: include/uapi/linux/i2c-*.h F: include/uapi/linux/i2c.h +I2C SUBSYSTEM [RUST]Has this been agreed with Wolfram off-list? (I'm asking since I haven't seen a reply from him in v2.)
In case you just did not receive an answer yet, it's fine to proceed with a proposal as you do here. However, please make sure to keep Wolfram in the loop. :)
Quoted 7 lines
In either case, which tree should patches flow through?
MAINTAINERS F: include/linux/i2c.h · line 11519
Quoted 2 lines
MAINTAINERS +2
-0+M: Igor Korotin <[email protected]> +R: Danilo Krummrich <[email protected]>If you'd like me to keep an eye on this as well, this is fine with me.
Yeah. I really need your help if you don't mind. Thanks.
Could you also briefly tell me finally which tree for patches should I use, how do I do it and if I need to represent it in the MAINTAINERS file somehow?
Thanks a lot for your help and review.
Best Regards
Igor
Danilo Krummrich
reviewerRe: [PATCH v3 1/3] rust: i2c: add basic I2C device and driver abstractions
Quoted 8 lines · Igor Korotin
Quoted 6 lines · Danilo Krummrich
MAINTAINERS F: include/linux/i2c.h · line 11519
Quoted 2 lines · Igor Korotin
MAINTAINERS +2
-0+M: Igor Korotin <[email protected]> +R: Danilo Krummrich <[email protected]>If you'd like me to keep an eye on this as well, this is fine with me.
Quoted 5 lines
Yeah. I really need your help if you don't mind. Thanks.
Could you also briefly tell me finally which tree for patches should I use, how do I do it and if I need to represent it in the
MAINTAINERSfile somehow?
Given that Wolfram said that he prefers you to maintain the code under a separate entry and send him PRs, you would need your own git tree where you pick up patches, which you then send as a PR to Wolfram.
The link to the tree should go under "T:" in the MAINTAINERS file in the entry your patch already contains.
I understand you haven't been a maintainer so far and haven't picked up patches and send pull requests, so it can be a bit much in the beginning.
A good place to start is the "Kernel Maintainer Handbook" [1].
Additionally, if you're willing to pick up this responsibility, I'd happy to offer you a call where we could go through the details of applying patches and send PRs and clarify questions to ensure it's going to be a smooth experience for everyone involved.
Once things are in place, you should clarify with Wolfram when in the release cycle he expects you to send PRs, i.e. until which -rc before the merge window for new patches, and if he has any additional expectations.
I know it may look a bit complicated at a first glance, but it's not as complicated as it may appear either. :)
Re: [PATCH v3 1/3] rust: i2c: add basic I2C device and driver abstractions
Quoted 18 lines · Danilo Krummrich
Given that Wolfram said that he prefers you to maintain the code under a separate entry and send him PRs, you would need your own git tree where you pick up patches, which you then send as a PR to Wolfram.
The link to the tree should go under "T:" in the
MAINTAINERSfile in the entry your patch already contains.I understand you haven't been a maintainer so far and haven't picked up patches and send pull requests, so it can be a bit much in the beginning.
A good place to start is the "Kernel Maintainer Handbook" [1].
Additionally, if you're willing to pick up this responsibility, I'd happy to offer you a call where we could go through the details of applying patches and send PRs and clarify questions to ensure it's going to be a smooth experience for everyone involved.
Quoted 6 lines
Once things are in place, you should clarify with Wolfram when in the release cycle he expects you to send PRs, i.e. until which -rc before the merge window for new patches, and if he has any additional expectations.
I know it may look a bit complicated at a first glance, but it's not as complicated as it may appear either. :)
+1 to all this and happy to help as well on learning how to be a maintainer.
Thanks Igor (and Wolfram)!
Cheers,
Miguel
Re: [PATCH v3 1/3] rust: i2c: add basic I2C device and driver abstractions
In reply to Danilo KrummrichHello Danilo
Quoted 32 lines · Danilo Krummrich
Quoted 8 lines · Igor Korotin
Quoted 6 lines · Danilo Krummrich
MAINTAINERS F: include/linux/i2c.h · line 11519
Quoted 2 lines · Igor Korotin
MAINTAINERS +2
-0+M: Igor Korotin <[email protected]> +R: Danilo Krummrich <[email protected]>If you'd like me to keep an eye on this as well, this is fine with me.
Quoted 5 lines
Yeah. I really need your help if you don't mind. Thanks.
Could you also briefly tell me finally which tree for patches should I use, how do I do it and if I need to represent it in the
MAINTAINERSfile somehow?Given that Wolfram said that he prefers you to maintain the code under a separate entry and send him PRs, you would need your own git tree where you pick up patches, which you then send as a PR to Wolfram.
The link to the tree should go under "T:" in the
MAINTAINERSfile in the entry your patch already contains.I understand you haven't been a maintainer so far and haven't picked up patches and send pull requests, so it can be a bit much in the beginning.
A good place to start is the "Kernel Maintainer Handbook" [1].
Additionally, if you're willing to pick up this responsibility, I'd happy to offer you a call where we could go through the details of applying patches and send PRs and clarify questions to ensure it's going to be a smooth experience for everyone involved.
Quoted 8 lines
Once things are in place, you should clarify with Wolfram when in the release cycle he expects you to send PRs, i.e. until which -rc before the merge window for new patches, and if he has any additional expectations.
I know it may look a bit complicated at a first glance, but it's not as complicated as it may appear either. :)
Wow, that’s a lot to take in — but thanks a lot for the detailed explanation.
I’m a bit worried about it, to be honest, but I’ll give it a try at least.
I’ll start by going through [1] in the next few days to get the basics.
Thanks again for the info and for offering help.
Best regards,
Igor
Re: [PATCH v3 1/3] rust: i2c: add basic I2C device and driver abstractions
Quoted 5 lines
Wow, that’s a lot to take in — but thanks a lot for the detailed explanation.
I’m a bit worried about it, to be honest, but I’ll give it a try at least.
Then, we can also start simple.
If there is a Rust related patch on the i2c-list, I will not do anything with it unless it got a Reviewed-by from Igor. If it got one, I will apply it to my tree. If there any dependencies, it would be helpful if this will be mentioned.
So, for starters, we can use my tree in the seperate I2C RUST entry. We did a similar thing with the I2C MUX subsubsystem. By the time, we can move to a dedicated tree, if there is a need. Thinking about it, there will not be a lot of patches for I2C Rust, or?
Danilo Krummrich
reviewerRe: [PATCH v3 1/3] rust: i2c: add basic I2C device and driver abstractions
Quoted 1 line · Wolfram Sang
Thinking about it, there will not be a lot of patches for I2C Rust, or?
I think the changes to expect are mostly in three categories.
(1) I2C infrastructure changes. For instance, this series contains the bus abstractions to probe an I2C (client) driver. So, I assume subsequent work may contain infrastructure for I2C regmap, adapter drivers, xfer algorithms, etc.
(2) driver-core infrastructure changes related to the device / driver lifecycle.
(3) Common Rust infrastructure changes.
(2) and (3) may also go through the driver-core and Rust trees, the frequency is not too high, but I'd expect a few patches every cycle.
As for (1) I know of at least two Rust I2C drivers people are working on, DS90UB954 (FDP-Link deserializer) and NCV6336, which I think is a buck converter.
Depending on what Igor works on (I thought I read it somewhere, but do not remember), we probably get away without adapter drivers and stuff for now.
Besides the I2C infrastructure they require (which doesn't seem too much at a first glance), I think we also have to consider how drivers should be routed eventually.
- Danilo
Re: [PATCH v3 1/3] rust: i2c: add basic I2C device and driver abstractions
Quoted 4 lines
Besides the I2C infrastructure they require (which doesn't seem too much at a first glance), I think we also have to consider how drivers should be routed eventually.
Well, I2C client drivers go to their specific subsystem, of course. I2C controller drivers will go to I2C. But as I can't review them, I need a maintainer for them. Is Igor willing to do this? Review I2C controller drivers written in Rust? Or is he only interested in the Rust I2C bindings? Anything is fine with me, we should only make sure that the MAINTATINERS file describes this correctly.
Re: [PATCH v3 1/3] rust: i2c: add basic I2C device and driver abstractions
Hello Wolfram
Quoted 7 lines · Wolfram Sang
Well, I2C client drivers go to their specific subsystem, of course. I2C controller drivers will go to I2C. But as I can't review them, I need a maintainer for them. Is Igor willing to do this? Review I2C controller drivers written in Rust? Or is he only interested in the Rust I2C bindings? Anything is fine with me, we should only make sure that the MAINTATINERS file describes this correctly.
I'm definitely interested, but going through a tough period right now, so I’m not sure I can commit fully just yet. Happy to stay involved and ramp up over time.
Thanks
Igor
Re: [PATCH v3 1/3] rust: i2c: add basic I2C device and driver abstractions
Quoted 11 lines
Quoted 7 lines
Well, I2C client drivers go to their specific subsystem, of course. I2C controller drivers will go to I2C. But as I can't review them, I need a maintainer for them. Is Igor willing to do this? Review I2C controller drivers written in Rust? Or is he only interested in the Rust I2C bindings? Anything is fine with me, we should only make sure that the MAINTATINERS file describes this correctly.
I'm definitely interested, but going through a tough period right now, so I’m not sure I can commit fully just yet. Happy to stay involved and ramp up over time.
Perfectly understandable. Let's just deal with it when it happens. Unlike client drivers, it might take a while until a controller driver shows up (famous last words). We will figure a way then. Thanks for your current commitment despite the tough period and all the best going through it!
Daniel Almeida
reviewerRe: [PATCH v3 1/3] rust: i2c: add basic I2C device and driver abstractions
In reply to Wolfram SangHi,
Quoted 7 lines · Wolfram Sang
Quoted 5 lines
Besides the I2C infrastructure they require (which doesn't seem too much at a first glance), I think we also have to consider how drivers should be routed eventually.
Quoted 7 lines
Well, I2C client drivers go to their specific subsystem, of course. I2C controller drivers will go to I2C. But as I can't review them, I need a maintainer for them. Is Igor willing to do this? Review I2C controller drivers written in Rust? Or is he only interested in the Rust I2C bindings? Anything is fine with me, we should only make sure that the MAINTATINERS file describes this correctly.
FWIW, I can help Igor and Danilo as a reviewer here, if needed.
I never wrote a I2C controller driver before, but as most things, it can be learned. And the review part can fit into the overall bandwidth I have to review Rust patches in general.
I guess it will make Igor more comfortable by having yet another person to rely on.
— Daniel
Re: [PATCH v3 1/3] rust: i2c: add basic I2C device and driver abstractions
In reply to Wolfram SangHello Wolfram
Quoted 4 lines · Wolfram Sang
So, for starters, we can use my tree in the seperate I2C RUST entry. We did a similar thing with the I2C MUX subsubsystem. By the time, we can move to a dedicated tree, if there is a need. Thinking about it, there will not be a lot of patches for I2C Rust, or?
Can't say for sure. I don’t expect much from my side, but from time to time there are Rust commits that change multiple subsystems at once — kind of like a wildcard hit.
Anyway, as far as I see, we can always adjust the workflow later if needed. For now, we just need to start somewhere.
Thanks,
Igor
Re: [PATCH v3 1/3] rust: i2c: add basic I2C device and driver abstractions
Quoted 4 lines · Igor Korotin
Can't say for sure. I don’t expect much from my side, but from time to time there are Rust commits that change multiple subsystems at once — kind of like a wildcard hit.
Yeah, I think that should be fine, e.g. those would be expected from time to time in C too. In Rust we may have a few more since things are still evolving and we try to enable useful lints if possible (since the early we do it the easier it is).
In some cases, we try to take those "global" cleanups through the Rust tree with Acked-bys etc. to reduce churn, but it depends on the particular change.
Quoted 2 lines
Anyway, as far as I see, we can always adjust the workflow later if needed. For now, we just need to start somewhere.
Yeah, Wolfram's approach is also a good opportunity to learn on your side with low stress, i.e. I would usually suggest that when he picks a patch, then you can try to understand what he did and why, and whether you can replicate the commit locally as if it were you picking it up etc.
Thanks!
Cheers,
Miguel
Re: [PATCH v3 1/3] rust: i2c: add basic I2C device and driver abstractions
In reply to Igor KorotinQuoted 3 lines
Anyway, as far as I see, we can always adjust the workflow later if needed. For now, we just need to start somewhere.
Yes. So, if you send v4 of your patches, please include the linux-i2c list, so patchwork will pick them up. Reviews from other Rust maintainers are welcome, of course.
Re: [PATCH v3 1/3] rust: i2c: add basic I2C device and driver abstractions
In reply to Danilo KrummrichHello Guys
Preparing v4, I keep thinking if for I2C I should rename i2c::Device to i2c::I2cClient or i2c::Client. The reason why I made it Device for the first iterations is so it would be the same code as for PCI/Platform code for example.
But the original I2C C code has other terminology. Do you think it worth keeping it in the Rust code?
Best Regards
Igor
Danilo Krummrich
reviewerRe: [PATCH v3 1/3] rust: i2c: add basic I2C device and driver abstractions
Quoted 8 lines · Igor Korotin
Hello Guys
Preparing v4, I keep thinking if for I2C I should rename
i2c::Devicetoi2c::I2cClientori2c::Client. The reason why I made itDevicefor the first iterations is so it would be the same code as for PCI/Platform code for example.
But the original I2C C code has other terminology. Do you think it worth keeping it in the Rust code?
I think the reason I2C names this i2c_client and i2c_adapter is that technically both are I2C devices, hence just i2c_device would have been ambiguous.
What about i2c::ClientDevice and i2c::AdapterDevice?
Re: [PATCH v3 1/3] rust: i2c: add basic I2C device and driver abstractions
Quoted 10 lines · Danilo Krummrich
Quoted 8 lines · Igor Korotin
Hello Guys
Preparing v4, I keep thinking if for I2C I should rename
i2c::Devicetoi2c::I2cClientori2c::Client. The reason why I made itDevicefor the first iterations is so it would be the same code as for PCI/Platform code for example.
But the original I2C C code has other terminology. Do you think it worth keeping it in the Rust code?
Quoted 2 lines
I think the reason I2C names this
i2c_clientandi2c_adapteris that technically both are I2C devices, hence justi2c_devicewould have been ambiguous.
Yes, when I did that code 20+ years ago, it was hard to pick out good names. Also we were converting over from the existing non-driver-model i2c code, which had those names at the time, so we kept them as much as possible.
Quoted 1 line
What about
i2c::ClientDeviceandi2c::AdapterDevice?
I would try to keep them matching the same as the C side, as i2c_client_device and i2c_adapter_device wouldn't make much sense there either.
thanks,
greg k-h
Danilo Krummrich
reviewerRe: [PATCH v3 1/3] rust: i2c: add basic I2C device and driver abstractions
Quoted 3 lines · Greg KH
I would try to keep them matching the same as the C side, as
i2c_client_deviceandi2c_adapter_devicewouldn't make much sense there either.
Another option would be separate modules, i.e. i2c::client::Device and i2c::adapter::Device.
But I guess just i2c::Client and i2c::Adapter are fine too.
The former would fit a bit better into the existing Rust naming scheme, the latter matches a bit better with the C code.
PATCH v3 2/3 rust: i2c: add manual I2C device creation abstractions
In reply to Igor KorotinIn addition to the basic I2C device support, added rust abstractions upon i2c_new_client_device/i2c_unregister_device C functions.
Implement the core abstractions needed for manual creation/deletion of I2C devices, including:
* i2c::Registration — a NonNull pointer created by the function
`i2c_new_client_device`
* i2c::I2cAdapterRef — a safe reference around struct i2c_adapter
* i2c::I2cBoardInfo — a safe wrapper around struct i2c_board_info
--- rust/kernel/i2c.rs | 111 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 109 insertions(+), 2 deletions(-)
rust/kernel/i2c.rs +109 -2
@@ -139,7 +139,6 @@ extern "C" fn probe_callback(pdev: *mut bindings::i2c_client) -> kernel::ffi::c_let info =Self ::i2c_id_info (pdev).or_else (|| <Self as driver::Adapter >::id_info (pdev.as_ref ())); -from_result (|| {let data =T ::probe (pdev, info)?;
@@ -312,6 +311,77 @@ fn shutdown(dev: &Device<device::Core>) { } } +/// 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` created by the C portion of +/// the kernel. +#[repr(transparent)] +pub struct I2cAdapterRef (NonNull <bindings::i2c_adapter >); + +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 <Self > { +// SAFETY: `index` must refer to a valid I2C adapter; the kernel +// guarantees that `i2c_get_adapter(index)` returns either a valid +// pointer or NULL. `NonNull::new` guarantees the correct check. +let adapter =NonNull ::new (unsafe { bindings::i2c_get_adapter (index) })?; +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 release +// the reference count in the I2C core. It is safe to call from 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_board_info` structure, +/// which is used for manual I2C client creation. +#[repr(transparent)] +pub struct I2cBoardInfo (bindings::i2c_board_info ); + +impl I2cBoardInfo { +const I2C_TYPE_SIZE :usize =20 ; +/// Create a new board‐info for a kernel driver. +#[inline(always)] +pub const fn new (type_: &'staticCStr , addr:u16 ) ->Self { +build_assert! ( + type_.len_with_nul () <=Self ::I2C_TYPE_SIZE , +"Type exceeds 20 bytes" + ); +let src = type_.as_bytes_with_nul (); +// Replace with `bindings::acpi_device_id::default()` once stabilized for `const`. +// SAFETY: FFI type is valid to be zero-initialized. +let mut i2c_board_info: bindings::i2c_board_info =unsafe { core::mem::zeroed () }; +let mut i:usize =0 ; +while i < src.len () { + i2c_board_info.type_[i] = src[i]; + i +=1 ; + } + + i2c_board_info.addr = addr; +Self (i2c_board_info) + } + +fn as_raw (&self) -> *const bindings::i2c_board_info { + &self.0 as *const _ + } +} +/// The i2c client representation. /// /// This structure represents the Rust abstraction for a C `struct i2c_client`. The
@@ -340,7 +410,7 @@ fn as_raw(&self) -> *mut bindings::i2c_client { kernel::impl_device_context_into_aref!(Device );// SAFETY: Instances of `Device` are always reference-counted. -unsafe impl crate ::types::AlwaysRefCounted for Device { +unsafe impl <Ctx : device::DeviceContext >crate ::types::AlwaysRefCounted for Device <Ctx > {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 ()) };
@@ -389,3 +459,40 @@ unsafe impl Send for Device {}// SAFETY: `Device` can be shared among threads because all methods of `Device` // (i.e. `Device<Normal>) are thread safe. unsafe impl Sync for Device {} + +/// The registration of an i2c client device. +/// +/// This type represents the registration of a [`struct i2c_client`]. When an instance of this +/// type is dropped, its respective i2c client device will be unregistered from the system. +/// +/// # Invariants +/// +/// `self.0` always holds a valid pointer to an initialized and registered +/// [`struct i2c_client`]. +#[repr(transparent)] +pub struct Registration (NonNull <bindings::i2c_client >); + +impl Registration { +/// The C `i2c_new_client_device` function wrapper for manual I2C client creation. +pub fn new (i2c_adapter: &I2cAdapterRef , i2c_board_info: &I2cBoardInfo ) ->Result <Self > { +// SAFETY: the kernel guarantees that `i2c_new_client_device()` returns either a valid +// pointer or NULL. `NonNull::new` guarantees the correct check. +let raw_dev =from_err_ptr (unsafe { + bindings::i2c_new_client_device (i2c_adapter.as_raw (), i2c_board_info.as_raw ()) + })?; + +Ok (Self (NonNull ::new (raw_dev).unwrap ())) + } +} + +impl Drop for Registration { +fn drop (&mut self) { +unsafe { bindings::i2c_unregister_device (self.0 .as_ptr ()) } + } +} + +// SAFETY: A `Device` is always reference-counted and can be released from any thread. +unsafe impl Send for Registration {} + +// SAFETY: A `Device` is always reference-counted and can be released from any thread. +unsafe impl Sync for Registration {}
Signature · 1 line
2.43.0
Daniel Almeida
reviewerRe: [PATCH v3 2/3] rust: i2c: add manual I2C device creation abstractions
Hi Igor,
Quoted 25 lines · Igor Korotin
In addition to the basic I2C device support, added rust abstractions upon
i2c_new_client_device/i2c_unregister_deviceC functions.Implement the core abstractions needed for manual creation/deletion of I2C devices, including:
*
i2c::Registration— a NonNull pointer created by the function`i2c_new_client_device`*
i2c::I2cAdapterRef— a safe reference aroundstruct i2c_adapter*
i2c::I2cBoardInfo— a safe wrapper aroundstruct i2c_board_infoSigned-off-by Igor Korotin <[email protected]>---
rust/kernel/i2c.rs| 111 ++++++++++++++++++++++++++++++++++++++++++++-1 file changed, 109 insertions(+), 2 deletions(-)rust/kernel/i2c.rs +0
-0
rust/kernel/i2c.rs extern "C" fn probe_callback(pdev: *mut bindings::i2c_client) -> kernel::ffi::c_ · line 142
Quoted 4 lines
rust/kernel/i2c.rs +0
-1@@ -139,7 +139,6 @@ extern "C" fn probe_callback(pdev: *mut bindings::i2c_client) -> kernel::ffi::c_let info =Self ::i2c_id_info (pdev).or_else (|| <Self as driver::Adapter >::id_info (pdev.as_ref ())); -
Unrelated change?
Quoted 8 lines
rust/kernel/i2c.rs +1
-0from_result (|| {let data =T ::probe (pdev, info)?;@@ -312,6 +311,77 @@ fn shutdown(dev: &Device<device::Core>) { } } +/// The i2c adapter reference
rust/kernel/i2c.rs fn shutdown(dev: &Device<device::Core>) { · line 324
Quoted 10 lines
rust/kernel/i2c.rs +10
-0+/// +/// 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` created by the C portion of +/// the kernel. +#[repr(transparent)] +pub struct I2cAdapterRef (NonNull <bindings::i2c_adapter >);
Is there a plan for an owned version? Otherwise I’d personally drop the “Ref” nomenclature.
Quoted 12 lines
rust/kernel/i2c.rs +12
-0+ +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 <Self > { +// SAFETY: `index` must refer to a valid I2C adapter; the kernel +// guarantees that `i2c_get_adapter(index)` returns either a valid +// pointer or NULL. `NonNull::new` guarantees the correct check. +let adapter =NonNull ::new (unsafe { bindings::i2c_get_adapter (index) })?;
rust/kernel/i2c.rs fn shutdown(dev: &Device<device::Core>) { · line 346
Quoted 10 lines
rust/kernel/i2c.rs +10
-0+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 release +// the reference count in the I2C core. It is safe to call from any context +unsafe { bindings::i2c_put_adapter (self.as_raw ()) }
Should this implement AlwasyRefCounted?
Quoted 18 lines
rust/kernel/i2c.rs +18
-0+ } +} + +/// The i2c board info representation +/// +/// This structure represents the Rust abstraction for a C `struct i2c_board_info` structure, +/// which is used for manual I2C client creation. +#[repr(transparent)] +pub struct I2cBoardInfo (bindings::i2c_board_info ); + +impl I2cBoardInfo { +const I2C_TYPE_SIZE :usize =20 ; +/// Create a new board‐info for a kernel driver. +#[inline(always)] +pub const fn new (type_: &'staticCStr , addr:u16 ) ->Self { +build_assert! ( + type_.len_with_nul () <=Self ::I2C_TYPE_SIZE , +"Type exceeds 20 bytes"
rust/kernel/i2c.rs fn shutdown(dev: &Device<device::Core>) { · line 374
Quoted 10 lines
rust/kernel/i2c.rs +10
-0+ ); +let src = type_.as_bytes_with_nul (); +// Replace with `bindings::acpi_device_id::default()` once stabilized for `const`. +// SAFETY: FFI type is valid to be zero-initialized. +let mut i2c_board_info: bindings::i2c_board_info =unsafe { core::mem::zeroed () }; +let mut i:usize =0 ; +while i < src.len () { + i2c_board_info.type_[i] = src[i]; + i +=1 ; + }
Same comment as the last patch.
Quoted 53 lines
rust/kernel/i2c.rs +38
-1+ + i2c_board_info.addr = addr; +Self (i2c_board_info) + } + +fn as_raw (&self) -> *const bindings::i2c_board_info { + &self.0 as *const _ + } +} +/// The i2c client representation. /// /// This structure represents the Rust abstraction for a C `struct i2c_client`. The @@ -340,7 +410,7 @@ fn as_raw(&self) -> *mut bindings::i2c_client { kernel::impl_device_context_into_aref!(Device );// SAFETY: Instances of `Device` are always reference-counted. -unsafe impl crate ::types::AlwaysRefCounted for Device { +unsafe impl <Ctx : device::DeviceContext >crate ::types::AlwaysRefCounted for Device <Ctx > {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 ()) };@@ -389,3 +459,40 @@ unsafe impl Send for Device {}// SAFETY: `Device` can be shared among threads because all methods of `Device` // (i.e. `Device<Normal>) are thread safe. unsafe impl Sync for Device {} + +/// The registration of an i2c client device. +/// +/// This type represents the registration of a [`struct i2c_client`]. When an instance of this +/// type is dropped, its respective i2c client device will be unregistered from the system. +/// +/// # Invariants +/// +/// `self.0` always holds a valid pointer to an initialized and registered +/// [`struct i2c_client`]. +#[repr(transparent)] +pub struct Registration (NonNull <bindings::i2c_client >); + +impl Registration { +/// The C `i2c_new_client_device` function wrapper for manual I2C client creation. +pub fn new (i2c_adapter: &I2cAdapterRef , i2c_board_info: &I2cBoardInfo ) ->Result <Self > { +// SAFETY: the kernel guarantees that `i2c_new_client_device()` returns either a valid +// pointer or NULL. `NonNull::new` guarantees the correct check. +let raw_dev =from_err_ptr (unsafe { + bindings::i2c_new_client_device (i2c_adapter.as_raw (), i2c_board_info.as_raw ()) + })?; + +Ok (Self (NonNull ::new (raw_dev).unwrap ())) + } +} + +impl Drop for Registration {
rust/kernel/i2c.rs unsafe impl Send for Device {} · line 498
Quoted 10 lines
rust/kernel/i2c.rs +10
-0+fn drop (&mut self) { +unsafe { bindings::i2c_unregister_device (self.0 .as_ptr ()) } + } +} + +// SAFETY: A `Device` is always reference-counted and can be released from any thread. +unsafe impl Send for Registration {} + +// SAFETY: A `Device` is always reference-counted and can be released from any thread. +unsafe impl Sync for Registration {}
A Device ?
Quoted 4 lines
Signature · 1 line
2.43.0
— Daniel
Danilo Krummrich
reviewerRe: [PATCH v3 2/3] rust: i2c: add manual I2C device creation abstractions
Quoted 31 lines · Daniel Almeida
rust/kernel/i2c.rs fn shutdown(dev: &Device<device::Core>) { · line 324
Quoted 11 lines
rust/kernel/i2c.rs +11
-0+/// 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` created by the C portion of +/// the kernel. +#[repr(transparent)] +pub struct I2cAdapterRef (NonNull <bindings::i2c_adapter >);Is there a plan for an owned version? Otherwise I’d personally drop the “Ref” nomenclature.
Quoted 16 lines
rust/kernel/i2c.rs +16
-0+ +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 <Self > { +// SAFETY: `index` must refer to a valid I2C adapter; the kernel +// guarantees that `i2c_get_adapter(index)` returns either a valid +// pointer or NULL. `NonNull::new` guarantees the correct check. +let adapter =NonNull ::new (unsafe { bindings::i2c_get_adapter (index) })?; +Some (Self (adapter)) + } +} +
Quoted 8 lines
rust/kernel/i2c.rs fn shutdown(dev: &Device<device::Core>) { · line 346
Quoted 6 lines
rust/kernel/i2c.rs +6
-0+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 release +// the reference count in the I2C core. It is safe to call from any context +unsafe { bindings::i2c_put_adapter (self.as_raw ()) }Should this implement AlwasyRefCounted?
I don't think it's necessary given the context this is used in, but it's also not much more code to do so, and ARef<I2cAdapter> turns out much cleaner I think.
So, yes I agree this should implement AlwasyRefCounted.
Quoted 22 lines
Quoted 22 lines
rust/kernel/i2c.rs +22
-0+ } +} + +/// The i2c board info representation +/// +/// This structure represents the Rust abstraction for a C `struct i2c_board_info` structure, +/// which is used for manual I2C client creation. +#[repr(transparent)] +pub struct I2cBoardInfo (bindings::i2c_board_info ); + +impl I2cBoardInfo { +const I2C_TYPE_SIZE :usize =20 ; +/// Create a new board‐info for a kernel driver. +#[inline(always)] +pub const fn new (type_: &'staticCStr , addr:u16 ) ->Self { +build_assert! ( + type_.len_with_nul () <=Self ::I2C_TYPE_SIZE , +"Type exceeds 20 bytes" + ); +let src = type_.as_bytes_with_nul (); +// Replace with `bindings::acpi_device_id::default()` once stabilized for `const`. +// SAFETY: FFI type is valid to be zero-initialized.
Quoted 8 lines
rust/kernel/i2c.rs fn shutdown(dev: &Device<device::Core>) { · line 374
Quoted 6 lines
rust/kernel/i2c.rs +6
-0+let mut i2c_board_info: bindings::i2c_board_info =unsafe { core::mem::zeroed () }; +let mut i:usize =0 ; +while i < src.len () { + i2c_board_info.type_[i] = src[i]; + i +=1 ; + }Same comment as the last patch.
Still not possible unfortunately. :(
Re: [PATCH v3 2/3] rust: i2c: add manual I2C device creation abstractions
In reply to Daniel AlmeidaHello Daniel
Quoted 7 lines · Daniel Almeida
Quoted 7 lines
from_result(|| { let data = T::probe(pdev, info)?;(quoted diff) +0
-0@@ -312,6 +311,77 @@ fn shutdown(dev: &Device<device::Core>) { } }
Quoted 15 lines
rust/kernel/i2c.rs fn shutdown(dev: &Device<device::Core>) { · line 324
Quoted 11 lines
rust/kernel/i2c.rs +11
-0+/// 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` created by the C portion of +/// the kernel. +#[repr(transparent)] +pub struct I2cAdapterRef (NonNull <bindings::i2c_adapter >);Is there a plan for an owned version? Otherwise I’d personally drop the “Ref” nomenclature.
Not at the moment at least. I'll remove this Ref nomenclature in the next drop
Thank you for the review!
Best Regards
Igor
Danilo Krummrich
reviewerRe: [PATCH v3 2/3] rust: i2c: add manual I2C device creation abstractions
In reply to Igor Korotinrust/kernel/i2c.rs extern "C" fn probe_callback(pdev: *mut bindings::i2c_client) -> kernel::ffi::c_ · line 142
Quoted 9 lines · Igor Korotin
rust/kernel/i2c.rs +0
-1@@ -139,7 +139,6 @@ extern "C" fn probe_callback(pdev: *mut bindings::i2c_client) -> kernel::ffi::c_let info =Self ::i2c_id_info (pdev).or_else (|| <Self as driver::Adapter >::id_info (pdev.as_ref ())); -
Seems like this hunk should be in patch 1.
Quoted 22 lines
rust/kernel/i2c.rs +15
-0from_result (|| {let data =T ::probe (pdev, info)?;@@ -312,6 +311,77 @@ fn shutdown(dev: &Device<device::Core>) { } } +/// 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` created by the C portion of +/// the kernel. +#[repr(transparent)] +pub struct I2cAdapterRef (NonNull <bindings::i2c_adapter >); + +impl I2cAdapterRef { +fn as_raw (&self) -> *mut bindings::i2c_adapter { + self.0 .as_ptr ()
rust/kernel/i2c.rs fn shutdown(dev: &Device<device::Core>) { · line 338
Quoted 10 lines
rust/kernel/i2c.rs +10
-0+ } + +/// Gets pointer to an `i2c_adapter` by index. +pub fn get (index:i32 ) ->Option <Self > { +// SAFETY: `index` must refer to a valid I2C adapter; the kernel +// guarantees that `i2c_get_adapter(index)` returns either a valid +// pointer or NULL. `NonNull::new` guarantees the correct check. +let adapter =NonNull ::new (unsafe { bindings::i2c_get_adapter (index) })?; +Some (Self (adapter)) + }
I think this should just return Result<Self> and if i2c_get_adapter() is NULL it should just return ENOENT.
Quoted 33 lines
rust/kernel/i2c.rs +33
-0+} + +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 release +// the reference count in the I2C core. It is safe to call from 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_board_info` structure, +/// which is used for manual I2C client creation. +#[repr(transparent)] +pub struct I2cBoardInfo (bindings::i2c_board_info ); + +impl I2cBoardInfo { +const I2C_TYPE_SIZE :usize =20 ; +/// Create a new board‐info for a kernel driver. +#[inline(always)] +pub const fn new (type_: &'staticCStr , addr:u16 ) ->Self { +build_assert! ( + type_.len_with_nul () <=Self ::I2C_TYPE_SIZE , +"Type exceeds 20 bytes" + ); +let src = type_.as_bytes_with_nul (); +// Replace with `bindings::acpi_device_id::default()` once stabilized for `const`. +// SAFETY: FFI type is valid to be zero-initialized. +let mut i2c_board_info: bindings::i2c_board_info =unsafe { core::mem::zeroed () }; +let mut i:usize =0 ; +while i < src.len () {
rust/kernel/i2c.rs fn shutdown(dev: &Device<device::Core>) { · line 381
Quoted 10 lines
rust/kernel/i2c.rs +10
-0+ i2c_board_info.type_[i] = src[i]; + i +=1 ; + } + + i2c_board_info.addr = addr; +Self (i2c_board_info) + } + +fn as_raw (&self) -> *const bindings::i2c_board_info { + &self.0 as *const _
I think we don't need this cast.
rust/kernel/i2c.rs fn as_raw(&self) -> *mut bindings::i2c_client { · line 413
Quoted 12 lines
rust/kernel/i2c.rs +4
-1+ } +} +/// The i2c client representation. /// /// This structure represents the Rust abstraction for a C `struct i2c_client`. The @@ -340,7 +410,7 @@ fn as_raw(&self) -> *mut bindings::i2c_client { kernel::impl_device_context_into_aref!(Device );// SAFETY: Instances of `Device` are always reference-counted. -unsafe impl crate ::types::AlwaysRefCounted for Device { +unsafe impl <Ctx : device::DeviceContext >crate ::types::AlwaysRefCounted for Device <Ctx > {
I think you forgot to remove this change.
In case it's intentional, it's wrong. We can't allow the existance of an ARef<Device<Ctx>>, where Ctx is anything else than device::Normal. All device contexts other than Normal are bound to a specific scope and must only ever exist as reference, e.g. &Device<Bound>.
Quoted 46 lines
rust/kernel/i2c.rs +37
-0fn 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 ()) };@@ -389,3 +459,40 @@ unsafe impl Send for Device {}// SAFETY: `Device` can be shared among threads because all methods of `Device` // (i.e. `Device<Normal>) are thread safe. unsafe impl Sync for Device {} + +/// The registration of an i2c client device. +/// +/// This type represents the registration of a [`struct i2c_client`]. When an instance of this +/// type is dropped, its respective i2c client device will be unregistered from the system. +/// +/// # Invariants +/// +/// `self.0` always holds a valid pointer to an initialized and registered +/// [`struct i2c_client`]. +#[repr(transparent)] +pub struct Registration (NonNull <bindings::i2c_client >); + +impl Registration { +/// The C `i2c_new_client_device` function wrapper for manual I2C client creation. +pub fn new (i2c_adapter: &I2cAdapterRef , i2c_board_info: &I2cBoardInfo ) ->Result <Self > { +// SAFETY: the kernel guarantees that `i2c_new_client_device()` returns either a valid +// pointer or NULL. `NonNull::new` guarantees the correct check. +let raw_dev =from_err_ptr (unsafe { + bindings::i2c_new_client_device (i2c_adapter.as_raw (), i2c_board_info.as_raw ()) + })?; + +Ok (Self (NonNull ::new (raw_dev).unwrap ())) + } +} + +impl Drop for Registration { +fn drop (&mut self) { +unsafe { bindings::i2c_unregister_device (self.0 .as_ptr ()) } + } +} + +// SAFETY: A `Device` is always reference-counted and can be released from any thread. +unsafe impl Send for Registration {} + +// SAFETY: A `Device` is always reference-counted and can be released from any thread. +unsafe impl Sync for Registration {}Signature · 1 line
2.43.0
PATCH v3 3/3 samples: rust: add Rust I2C sample driver
In reply to Igor KorotinAdd a new rust_driver_i2c sample, showing how to create a new i2c client using i2c::Registration and bind a driver to it via legacy I2C-ID table.
--- MAINTAINERS | 1 + samples/rust/Kconfig | 11 ++++ samples/rust/Makefile | 1 + samples/rust/rust_driver_i2c.rs | 106 ++++++++++++++++++++++++++++++++ 4 files changed, 119 insertions(+) create mode 100644 samples/rust/rust_driver_i2c.rs
MAINTAINERS +1 -0
@@ -11520,6 +11520,7 @@ R: Danilo Krummrich <[email protected]> L: [email protected] S: Maintained F: rust/kernel/i2c.rs +F: samples/rust/rust_driver_i2c.rs I2C SUBSYSTEM HOST DRIVERS M: Andi Shyti <[email protected]>
samples/rust/Kconfig +11 -0
@@ -62,6 +62,17 @@ config SAMPLE_RUST_DMA If unsure, say N. +config SAMPLE_RUST_DRIVER_I2C + tristate "I2C Driver" + depends on I2C=y + help + This option builds the Rust I2C driver sample. + + To compile this as a module, choose M here: + the module will be called rust_driver_i2c. + + If unsure, say N. + config SAMPLE_RUST_DRIVER_PCI tristate "PCI Driver" depends on PCI
samples/rust/Makefile +1 -0
@@ -5,6 +5,7 @@ obj-$(CONFIG_SAMPLE_RUST_MINIMAL) += rust_minimal.o obj-$(CONFIG_SAMPLE_RUST_MISC_DEVICE) += rust_misc_device.o obj-$(CONFIG_SAMPLE_RUST_PRINT) += rust_print.o obj-$(CONFIG_SAMPLE_RUST_DMA) += rust_dma.o +obj-$(CONFIG_SAMPLE_RUST_DRIVER_I2C) += rust_driver_i2c.o obj-$(CONFIG_SAMPLE_RUST_DRIVER_PCI) += rust_driver_pci.o obj-$(CONFIG_SAMPLE_RUST_DRIVER_PLATFORM) += rust_driver_platform.o obj-$(CONFIG_SAMPLE_RUST_DRIVER_FAUX) += rust_driver_faux.o
samples/rust/rust_driver_i2c.rs new file +106 -0
106 lines not on this page · raw message
Signature · 1 line
2.43.0
Daniel Almeida
reviewerRe: [PATCH v3 3/3] samples: rust: add Rust I2C sample driver
Hi Igor,
Overall looks good, some minor comments below.
Quoted 67 lines · Igor Korotin
Add a new
rust_driver_i2csample, showing how to create a new i2c client usingi2c::Registrationand bind a driver to it via legacy I2C-ID table.Signed-off-by Igor Korotin <[email protected]>---
MAINTAINERS | 1 + samples/rust/Kconfig | 11 ++++ samples/rust/Makefile | 1 + samples/rust/rust_driver_i2c.rs | 106 ++++++++++++++++++++++++++++++++ 4 files changed, 119 insertions(+)create mode 100644
samples/rust/rust_driver_i2c.rsMAINTAINERS +0
-0@@ -11520,6 +11520,7 @@ R: Danilo Krummrich <[email protected]>L: [email protected]
S: Maintained
F:rust/kernel/i2c.rs
+F:samples/rust/rust_driver_i2c.rsI2C SUBSYSTEM HOST DRIVERS
M: Andi Shyti <[email protected]>samples/rust/Kconfig +11
-0@@ -62,6 +62,17 @@ config SAMPLE_RUST_DMA If unsure, say N. +config SAMPLE_RUST_DRIVER_I2C + tristate "I2C Driver" + depends on I2C=y + help + This option builds the Rust I2C driver sample. + + To compile this as a module, choose M here: + the module will be called rust_driver_i2c. + + If unsure, say N. +config
SAMPLE_RUST_DRIVER_PCI
tristate "PCI Driver"
depends on PCIsamples/rust/Makefile +0
-0@@ -5,6 +5,7 @@ obj-$(CONFIG_SAMPLE_RUST_MINIMAL) += rust_minimal.oobj-$(CONFIG_SAMPLE_RUST_MISC_DEVICE) += rust_misc_device.o obj-$(CONFIG_SAMPLE_RUST_PRINT) += rust_print.o obj-$(CONFIG_SAMPLE_RUST_DMA) += rust_dma.o +obj-$(CONFIG_SAMPLE_RUST_DRIVER_I2C) += rust_driver_i2c.o obj-$(CONFIG_SAMPLE_RUST_DRIVER_PCI) += rust_driver_pci.o obj-$(CONFIG_SAMPLE_RUST_DRIVER_PLATFORM) += rust_driver_platform.o obj-$(CONFIG_SAMPLE_RUST_DRIVER_FAUX) += rust_driver_faux.o
samples/rust/rust_driver_i2c.rs new file +0
-0
samples/rust/rust_driver_i2c.rs line 5
Quoted 5 lines
samples/rust/rust_driver_i2c.rs new file +5
-0@@ -0,0 +1,106 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Rust I2C driver sample. +//! +//! This module shows how to:
Blank line here?
samples/rust/rust_driver_i2c.rs line 10
Quoted 5 lines
samples/rust/rust_driver_i2c.rs new file +5
-0+//! 1. Manually create an `i2c_client` at address `SAMPLE_I2C_CLIENT_ADDR` +//! on the adapter with index `SAMPLE_I2C_ADAPTER_INDEX`. +//! 2. Register a matching Rust-based I2C driver for that client. +//! +//! # Requirements
Blank line here?
samples/rust/rust_driver_i2c.rs line 14
Quoted 4 lines
samples/rust/rust_driver_i2c.rs new file +4
-0+//! - The target system must expose an I2C adapter at index +//! `SAMPLE_I2C_ADAPTER_INDEX`. +//! - To emulate an adapter for testing, you can load the +//! `i2c-stub` kernel module.
Anything else needs to be done?
Quoted 44 lines
samples/rust/rust_driver_i2c.rs new file +44
-0+//! + +use kernel::{acpi, c_str, device::Core , i2c, of, prelude::*, types::ARef }; + +const SAMPLE_I2C_CLIENT_ADDR :u16 =0x30 ; +const SAMPLE_I2C_ADAPTER_INDEX :i32 =0 ; +const BOARD_INFO : i2c::I2cBoardInfo = + i2c::I2cBoardInfo ::new (c_str! ("rust_driver_i2c" ),SAMPLE_I2C_CLIENT_ADDR ); + +struct SampleDriver { + pdev:ARef <i2c::Device >, +} + +kernel::acpi_device_table! { +ACPI_TABLE , +MODULE_ACPI_TABLE , + <SampleDriver as i2c::Driver >::IdInfo , + [(acpi::DeviceId ::new (c_str! ("LNUXBEEF" )),0 )] +} + +kernel::i2c_device_table! { +I2C_TABLE , +MODULE_I2C_TABLE , + <SampleDriver as i2c::Driver >::IdInfo , + [(i2c::DeviceId ::new (c_str! ("rust_driver_i2c" )),0 )] +} + +kernel::of_device_table! { +OF_TABLE , +MODULE_OF_TABLE , + <SampleDriver as i2c::Driver >::IdInfo , + [(of::DeviceId ::new (c_str! ("test,rust_driver_i2c" )),0 )] +} + +impl i2c::Driver for SampleDriver { +type IdInfo =u32 ; + +const ACPI_ID_TABLE :Option <acpi::IdTable <Self ::IdInfo >> =Some (&ACPI_TABLE ); +const I2C_ID_TABLE :Option <i2c::IdTable <Self ::IdInfo >> =Some (&I2C_TABLE ); +const OF_ID_TABLE :Option <of::IdTable <Self ::IdInfo >> =Some (&OF_TABLE ); + +fn probe (pdev: &i2c::Device <Core >, info:Option <&Self ::IdInfo >) ->Result <Pin <KBox <Self >>> { +let dev = pdev.as_ref (); +
samples/rust/rust_driver_i2c.rs line 68
Quoted 10 lines
samples/rust/rust_driver_i2c.rs new file +10
-0+dev_dbg! (dev,"Probe Rust I2C driver sample.\n" ); + +if let Some (info) = info { +dev_info! (dev,"Probed with info: '{}'.\n" , info); + } + +let drvdata =KBox ::new (Self { pdev: pdev.into () },GFP_KERNEL )?; + +Ok (drvdata.into ()) + }
Blank line here.
Quoted 6 lines
samples/rust/rust_driver_i2c.rs new file +6
-0+fn shutdown (pdev: &i2c::Device <Core >) { +dev_dbg! (pdev.as_ref (),"Shutdown Rust I2C driver sample.\n" ); + } +} + +impl Drop for SampleDriver {
samples/rust/rust_driver_i2c.rs line 84
Quoted 10 lines
samples/rust/rust_driver_i2c.rs new file +10
-0+fn drop (&mut self) { +dev_dbg! (self.pdev.as_ref (),"Remove Rust I2C driver sample.\n" ); + } +} + +// NOTE: The code below is expanded macro module_i2c_driver. It is not used here +// because we need to manually create an I2C client in `init()`. The macro +// hides `init()`, so to demo client creation on adapter SAMPLE_I2C_ADAPTER_INDEX +// we expand it by hand. +type Ops <T > = kernel::i2c::Adapter <T >;
Blank here.
Also, just “pin_data” ?
Quoted 6 lines
samples/rust/rust_driver_i2c.rs new file +6
-0+#[kernel::prelude::pin_data] +struct DriverModule { +#[pin] + _driver: kernel::driver::Registration <Ops <SampleDriver >>, + _reg: i2c::Registration , +}
samples/rust/rust_driver_i2c.rs line 100
Quoted 10 lines
samples/rust/rust_driver_i2c.rs new file +10
-0+ +impl kernel::InPlaceModule for DriverModule { +fn init ( + module: &'static kernel::ThisModule , + ) ->impl ::pin_init::PinInit <Self , kernel::error::Error > { +let adapter = i2c::I2cAdapterRef ::get (SAMPLE_I2C_ADAPTER_INDEX ); + + kernel::try_pin_init!(Self { + _reg <- i2c::Registration ::new (&adapter.unwrap (), &BOARD_INFO ), + _driver<-kernel::driver::Registration ::new (<Self as kernel::ModuleMetadata >::NAME ,module,),
Missing space in “<-“
Quoted 10 lines
samples/rust/rust_driver_i2c.rs new file +6
-0+ }) + } +} +kernel::prelude::module! { +type :DriverModule ,name:"rust_driver_i2c" ,authors:["Igor Korotin" ],description:"Rust I2C driver" ,license:"GPL v2" , +}Signature · 1 line
2.43.0
This is usually broken into multiple lines?
— Daniel
Re: [PATCH v3 3/3] samples: rust: add Rust I2C sample driver
Hello Daniel
Quoted 7 lines · Daniel Almeida
samples/rust/rust_driver_i2c.rs line 14
Quoted 4 lines
samples/rust/rust_driver_i2c.rs new file +4
-0+//! - The target system must expose an I2C adapter at index +//! `SAMPLE_I2C_ADAPTER_INDEX`. +//! - To emulate an adapter for testing, you can load the +//! `i2c-stub` kernel module. Anything else needs to be done?
Sorry, not sure I understood — can you clarify?
Thank you for the review.
Thanks
Igor
Daniel Almeida
reviewerRe: [PATCH v3 3/3] samples: rust: add Rust I2C sample driver
Hi Igor,
Quoted 13 lines · Igor Korotin
Hello Daniel
Quoted 7 lines · Daniel Almeida
samples/rust/rust_driver_i2c.rs line 14
Quoted 4 lines
samples/rust/rust_driver_i2c.rs new file +4
-0+//! - The target system must expose an I2C adapter at index +//! `SAMPLE_I2C_ADAPTER_INDEX`. +//! - To emulate an adapter for testing, you can load the +//! `i2c-stub` kernel module. Anything else needs to be done?
Quoted 6 lines
Sorry, not sure I understood — can you clarify?
Thank you for the review.
Thanks
Igor
I was asking whether you have more details on how i2c-stub can be used to test your code, as I'm unfamiliar with it. Are there any more parameters that I need to set up?
You probably have this written down for your own testing, so if you can share more instructions on how you're doing it, I can also test it for you and give you a Tested-by tag.
— Daniel
Danilo Krummrich
reviewerRe: [PATCH v3 3/3] samples: rust: add Rust I2C sample driver
In reply to Igor KorotinQuoted 1 line · Igor Korotin
+#[kernel::prelude::pin_data]
Can just be #[pin_data].
Quoted 4 lines
samples/rust/rust_driver_i2c.rs new file +4
-0+struct DriverModule { +#[pin] + _driver: kernel::driver::Registration <Ops <SampleDriver >>, + _reg: i2c::Registration ,
samples/rust/rust_driver_i2c.rs line 99
Quoted 10 lines
samples/rust/rust_driver_i2c.rs new file +10
-0+} + +impl kernel::InPlaceModule for DriverModule { +fn init ( + module: &'static kernel::ThisModule , + ) ->impl ::pin_init::PinInit <Self , kernel::error::Error > { +let adapter = i2c::I2cAdapterRef ::get (SAMPLE_I2C_ADAPTER_INDEX ); + + kernel::try_pin_init!(Self { + _reg <- i2c::Registration ::new (&adapter.unwrap (), &BOARD_INFO ),
Please don't use unwrap(), this panics the kernel in case of failure. Please see below for a better solution.
samples/rust/rust_driver_i2c.rs line 106
Quoted 7 lines
samples/rust/rust_driver_i2c.rs new file +7
-0+ _driver<-kernel::driver::Registration::new(<Self as kernel::ModuleMetadata>::NAME,module,), + }) + } +} +kernel::prelude::module! { +type :DriverModule ,name:"rust_driver_i2c" ,authors:["Igor Korotin" ],description:"Rust I2C driver" ,license:"GPL v2" , +}
I suggest to write this like this:
#[pin_data]
struct DriverModule {
#[pin]
_driver: kernel::driver::Registration<Ops<SampleDriver>>,
_reg: i2c::Registration,
}
impl kernel::InPlaceModule for DriverModule {
fn init(
module: &'static kernel::ThisModule,
) -> impl ::pin_init::PinInit<Self, Error> {
try_pin_init!(Self {
_reg <- {
let adapter = i2c::I2cAdapterRef::get(SAMPLE_I2C_ADAPTER_INDEX).ok_or(ENOENT)?;
i2c::Registration::new(&adapter, &BOARD_INFO)
},
_driver <- kernel::driver::Registration::new(<Self as kernel::ModuleMetadata>::NAME, module),
})
}
}
kernel::prelude::module! {
type:DriverModule,
name:"rust_driver_i2c",
authors:["Igor Korotin"],
description:"Rust I2C driver",
license:"GPL v2",
}kernel test robot
build botRe: [PATCH v3 3/3] samples: rust: add Rust I2C sample driver
In reply to Igor Korotinkernel test robot noticed "kernel_BUG_at_rust/helpers/bug.c" on:
Hello,
kernel test robot noticed "kernel_BUG_at_rust/helpers/bug.c" on:
commit: 65aee29875487338caa52e3a4ffc41165d4798ad ("[PATCH v3 3/3] samples: rust: add Rust I2C sample driver")
url: https://github.com/intel-lab-lkp/linux/commits/Igor-Korotin/rust-i2c-add-basic-I2C-device-and-driver-abstractions/20250801-234859
patch link: https://lore.kernel.org/all/[email protected]/
patch subject: [PATCH v3 3/3] samples: rust: add Rust I2C sample driver
in testcase: boot
config: x86_64-rhel-9.4-rust
compiler: clang-20
test machine: qemu-system-x86_64 -enable-kvm -cpu SandyBridge -smp 2 -m 16G
(please refer to attached dmesg/kmsg for entire log/backtrace)
+------------------------------------------+------------+------------+ | | 6c0f429b5b | 65aee29875 | +------------------------------------------+------------+------------+ | boot_successes | 18 | 0 | | boot_failures | 0 | 18 | | kernel_BUG_at_rust/helpers/bug.c | 0 | 18 | | Oops:invalid_opcode:#[##]SMP_PTI | 0 | 18 | | RIP:rust_helper_BUG | 0 | 18 | | Kernel_panic-not_syncing:Fatal_exception | 0 | 18 | +------------------------------------------+------------+------------+
If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <[email protected]>
| Closes: https://lore.kernel.org/oe-lkp/[email protected]
[ 17.294743][ T1] ------------[ cut here ]------------ [ 17.295458][ T1] kernel BUG at rust/helpers/bug.c:7! [ 17.296172][ T1] Oops: invalid opcode: 0000 [#1] SMP PTI [ 17.296925][ T1] CPU: 1 UID: 0 PID: 1 Comm: swapper/0 Not tainted 6.16.0-08688-g65aee2987548 #1 PREEMPT(voluntary) [ 17.296974][ T1] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.16.2-debian-1.16.2-1 04/01/2014 [ 17.296974][ T1] RIP: 0010:rust_helper_BUG (rust/helpers/bug.c:7) [ 17.296974][ T1] Code: 00 00 00 00 00 0f 1f 44 00 00 48 8d 87 10 ff ff ff e9 cf 22 82 00 cc 66 66 66 66 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 <0f> 0b 66 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 e9 e6 00 0a 00 66 All code ======== 0: 00 00 add %al,(%rax) 2: 00 00 add %al,(%rax) 4: 00 0f add %cl,(%rdi) 6: 1f (bad) 7: 44 00 00 add %r8b,(%rax) a: 48 8d 87 10 ff ff ff lea -0xf0(%rdi),%rax 11: e9 cf 22 82 00 jmp 0x8222e5 16: cc int3 17: 66 66 66 66 66 2e 0f data16 data16 data16 data16 cs nopw 0x0(%rax,%rax,1) 1e: 1f 84 00 00 00 00 00 25: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1) 2a:* 0f 0b ud2 <-- trapping instruction 2c: 66 0f 1f 84 00 00 00 nopw 0x0(%rax,%rax,1) 33: 00 00 35: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1) 3a: e9 e6 00 0a 00 jmp 0xa0125 3f: 66 data16
Code starting with the faulting instruction
===========================================
0: 0f 0b ud2
2: 66 0f 1f 84 00 00 00 nopw 0x0(%rax,%rax,1)
9: 00 00
b: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1) 10: e9 e6 00 0a 00 jmp 0xa00fb 15: 66 data16 [ 17.296974][ T1] RSP: 0000:ffffd25cc0013948 EFLAGS: 00010246 [ 17.296974][ T1] RAX: 0000000000000080 RBX: ffffffffb55c2ea0 RCX: 0a0c9e9b2c6f9900 [ 17.296974][ T1] RDX: 0000000000000002 RSI: 00000000ffff7fff RDI: ffffffffb651ce50 [ 17.296974][ T1] RBP: ffffd25cc0013e28 R08: 0000000000007fff R09: ffffffffb645cdf0 [ 17.296974][ T1] R10: 0000000000017ffd R11: 00000000ffff7fff R12: 0000000000000000 [ 17.296974][ T1] R13: 0000000000000000 R14: ffffffffb7172dd4 R15: 0000000000000000 [ 17.296974][ T1] FS: 0000000000000000(0000) GS:ffff8c5bb8b33000(0000) knlGS:0000000000000000 [ 17.296974][ T1] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 17.296974][ T1] CR2: 0000000000000000 CR3: 0000000048420000 CR4: 00000000000406f0 [ 17.296974][ T1] Call Trace: [ 17.296974][ T1] <TASK> [ 17.296974][ T1] __rustc::rust_begin_unwind (rust/kernel/lib.rs:213) [ 17.296974][ T1] ? <&kernel::str::CStr as core::fmt::Display>::fmt (opt/cross/rustc-1.88.0-bindgen-0.72.0/rustup/toolchains/1.88.0-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/fmt/mod.rs:2636) [ 17.296974][ T1] ? work_grab_pending (kernel/workqueue.c:2064) [ 17.296974][ T1] core::panicking::panic_fmt (opt/cross/rustc-1.88.0-bindgen-0.72.0/rustup/toolchains/1.88.0-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/panicking.rs:75) [ 17.296974][ T1] core::panicking::panic (opt/cross/rustc-1.88.0-bindgen-0.72.0/rustup/toolchains/1.88.0-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/panicking.rs:145) [ 17.296974][ T1] core::option::unwrap_failed (opt/cross/rustc-1.88.0-bindgen-0.72.0/rustup/toolchains/1.88.0-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/option.rs:2040) [ 17.296974][ T1] __rust_driver_i2c_init (samples/rust/rust_driver_i2c.rs:104) [ 17.296974][ T1] ? blake2s_update (lib/crypto/blake2s.c:45) [ 17.296974][ T1] ? add_device_randomness (drivers/char/random.c:952) [ 17.296974][ T1] ? <rust_dma::DmaSampleDriver as core::ops::drop::Drop>::drop (rust/kernel/driver.rs:127) [ 17.296974][ T1] ? <rust_driver_i2c::DriverModule as kernel::InPlaceModule>::init (samples/rust/rust_driver_i2c.rs:104 samples/rust/rust_driver_i2c.rs:104 samples/rust/rust_driver_i2c.rs:104) [ 17.296974][ T1] do_one_initcall (init/main.c:1269) [ 17.296974][ T1] ? crng_make_state (include/linux/spinlock.h:406 drivers/char/random.c:363) [ 17.296974][ T1] ? get_random_u32 (include/linux/string.h:366 include/crypto/chacha.h:119 drivers/char/random.c:425 drivers/char/random.c:554) [ 17.296974][ T1] ? __get_random_u32_below (drivers/char/random.c:568) [ 17.296974][ T1] ? allocate_slab (mm/slub.c:2578) [ 17.296974][ T1] ? sysvec_apic_timer_interrupt (arch/x86/kernel/apic/apic.c:1050) [ 17.296974][ T1] ? asm_sysvec_apic_timer_interrupt (arch/x86/include/asm/idtentry.h:702) [ 17.296974][ T1] ? parameq (kernel/params.c:81 kernel/params.c:91 kernel/params.c:99) [ 17.296974][ T1] ? parameq (kernel/params.c:90 kernel/params.c:99) [ 17.296974][ T1] ? do_initcall_level (init/main.c:1315) [ 17.296974][ T1] ? parse_args (kernel/params.c:153) [ 17.296974][ T1] do_initcall_level (init/main.c:1330) [ 17.296974][ T1] do_initcalls (init/main.c:1344) [ 17.296974][ T1] kernel_init_freeable (init/main.c:1581) [ 17.296974][ T1] ? rest_init (init/main.c:1461) [ 17.296974][ T1] kernel_init (init/main.c:1471) [ 17.296974][ T1] ret_from_fork (arch/x86/kernel/process.c:154) [ 17.296974][ T1] ? rest_init (init/main.c:1461) [ 17.296974][ T1] ret_from_fork_asm (arch/x86/entry/entry_64.S:258) [ 17.296974][ T1] </TASK> [ 17.296974][ T1] Modules linked in: [ 17.339320][ T1] ---[ end trace 0000000000000000 ]--- [ 17.340041][ T1] RIP: 0010:rust_helper_BUG (rust/helpers/bug.c:7) [ 17.340745][ T1] Code: 00 00 00 00 00 0f 1f 44 00 00 48 8d 87 10 ff ff ff e9 cf 22 82 00 cc 66 66 66 66 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 <0f> 0b 66 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 e9 e6 00 0a 00 66 All code ======== 0: 00 00 add %al,(%rax) 2: 00 00 add %al,(%rax) 4: 00 0f add %cl,(%rdi) 6: 1f (bad) 7: 44 00 00 add %r8b,(%rax) a: 48 8d 87 10 ff ff ff lea -0xf0(%rdi),%rax 11: e9 cf 22 82 00 jmp 0x8222e5 16: cc int3 17: 66 66 66 66 66 2e 0f data16 data16 data16 data16 cs nopw 0x0(%rax,%rax,1) 1e: 1f 84 00 00 00 00 00 25: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1) 2a:* 0f 0b ud2 <-- trapping instruction 2c: 66 0f 1f 84 00 00 00 nopw 0x0(%rax,%rax,1) 33: 00 00 35: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1) 3a: e9 e6 00 0a 00 jmp 0xa0125 3f: 66 data16
Code starting with the faulting instruction
===========================================
0: 0f 0b ud2
2: 66 0f 1f 84 00 00 00 nopw 0x0(%rax,%rax,1)
9: 00 00
b: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1) 10: e9 e6 00 0a 00 jmp 0xa00fb 15: 66 data16
The kernel config and materials to reproduce are available at:
https://download.01.org/0day-ci/archive/20250807/[email protected]
Signature · 2 lines
0-DAY CI Kernel Test Service https://github.com/intel/lkp-tests/wiki
Daniel Almeida
reviewerIgor,
Quoted 80 lines · Igor Korotin
This patch series lays the groundwork for writing Linux I2C drivers in Rust by:
1. Core abstractions
Introducei2c::Device,i2c::Driverandi2c::Adapterbuilt on the existingstruct i2c_clientandstruct i2c_driver, with safe Rust wrappers around probe, transfer, and teardown logic.2. Manual device creation
Provide an API to register an I2C device at runtime from Rust usingI2cBoardInfo, including automatic cleanup when the driver unloads.3. Sample driver (legacy table, OF & ACPI)
Addrust_driver_i2c, a sample that:
- creates an I2C client device usingi2c::Registration::new()
- binds to an I2C client via:
- legacy I2C-ID table,
- Open Firmware (device-tree) compatible strings, or
- ACPI IDs.
- destroyes the I2C client device on exit.Together, these three patches:
- Establish the essential Rust traits and types for I2C drivers.
- Enable driver binding via legacy ID table, device-tree (OF), or ACPI
- Enable manual device creation at runtime.
- Ship a samples showing typical usageIgor Korotin (3):
rust: i2c: add basic I2C device and driver abstractions
rust: i2c: add manual I2C device creation abstractions
samples: rust: add Rust I2C sample driverChangelog
---------
v3:
- removed unnecessaryi2c_get_clientdataandi2c_set_clientdatarust helpers. Using generic accessors implemented in [1] instead.
- Reimplementedi2c::DeviceIdbased on changes in [2].
- Usingfrom_resultini2c::Adapter::probe_callback
- Using explicitdrop()for i2c client private data ini2c::Adapter::remove_callback
- replaceddevice::Device::as_ref()withdevice::Device::from_rawini2c::Device::as_ref(). It is renamed indevice::Device.
- Build Rust I2C only if I2C is built-in
- Reimplement overcomplicated traiti2c::DeviceOwnedthe same way it is implemented in auxiliary [3].
- Mergerust_device_i2candrust_driver_i2csamples. Resultingrust_driver_i2ccreates pinedi2c_clientusingi2c::Registration::newand probes newly createdi2c_client.
- Created a new entry inMAINTAINERSfile containingi2c.rsandrust_driver_i2c.rsin it.
- Link to v2: [4][1] https://lore.kernel.org/lkml/[email protected]/
[2] https://lore.kernel.org/rust-for-linux/[email protected]/
[3] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/rust/kernel/auxiliary.rs?h=v6.16-rc4#n299
[4] https://lore.kernel.org/rust-for-linux/[email protected]/v2:
- Merged separated ACPI support patches since ACPI-table support is
merged into driver-core-next.
- Added I2cAdapterRef and I2cBoardInfo abstractions
- Added DeviceState generic parameter which is used fori2c::Device
as a sign if the device is created manually
- AddedDeviceOwnedabstraction which is a safe reference to a
manually createdi2c::Device<Ctx, state::Owned>.
- Added Rust manual I2C device creation sample
- Link to v1: https://lore.kernel.org/rust-for-linux/[email protected]/MAINTAINERS | 8 + rust/bindings/bindings_helper.h | 1 + rust/kernel/i2c.rs | 498 ++++++++++++++++++++++++++++++++ rust/kernel/lib.rs | 2 + samples/rust/Kconfig | 11 + samples/rust/Makefile | 1 + samples/rust/rust_driver_i2c.rs | 106 +++++++ 7 files changed, 627 insertions(+)
Quoted 9 lines
create mode 100644
rust/kernel/i2c.rscreate mode 100644samples/rust/rust_driver_i2c.rsbase-commit:
260f6f4fda93c8485c8037865c941b42b9cba5d2Signature · 1 line
2.43.0
I’m not sure whether this has been mentioned yet: c_int and friends are in the prelude. There are quite a few places where kernel::ffi::c_int shows up in this series. That can be just “c_int” instead, for example.
— Daniel