Add 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.
Signed-off-by: Igor Korotin <igor.korotin.linux@gmail.com>
---
MAINTAINERS | 1 +
samples/rust/Kconfig | 11 +++
samples/rust/Makefile | 1 +
samples/rust/rust_driver_i2c.rs | 128 ++++++++++++++++++++++++++++++++
4 files changed, 141 insertions(+)
create mode 100644 samples/rust/rust_driver_i2c.rs
diff --git a/MAINTAINERS b/MAINTAINERS
index c44c7ac317b1..2654a7ea0c80 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -11523,6 +11523,7 @@ R: Daniel Almeida <daniel.almeida@collabora.com>
L: rust-for-linux@vger.kernel.org
S: Maintained
F: rust/kernel/i2c.rs
+F: samples/rust/rust_driver_i2c.rs
I2C SUBSYSTEM HOST DRIVERS
M: Andi Shyti <andi.shyti@kernel.org>
diff --git a/samples/rust/Kconfig b/samples/rust/Kconfig
index 7f7371a004ee..28dae070b365 100644
--- a/samples/rust/Kconfig
+++ b/samples/rust/Kconfig
@@ -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
diff --git a/samples/rust/Makefile b/samples/rust/Makefile
index bd2faad63b4f..141d8f078248 100644
--- a/samples/rust/Makefile
+++ b/samples/rust/Makefile
@@ -5,6 +5,7 @@ obj-$(CONFIG_SAMPLE_RUST_MINIMAL) += 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
diff --git a/samples/rust/rust_driver_i2c.rs b/samples/rust/rust_driver_i2c.rs
new file mode 100644
index 000000000000..6dfc299d5aea
--- /dev/null
+++ b/samples/rust/rust_driver_i2c.rs
@@ -0,0 +1,128 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Rust I2C driver sample.
+//!
+//! This module shows how to:
+//!
+//! 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
+//!
+//! - 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 with an option `chip_addr`
+//! For example for this sample driver to emulate an I2C device with
+//! an address 0x30 you can use:
+//! `modprobe i2c-stub chip_addr=0x30`
+//!
+
+use kernel::{
+ acpi, c_str,
+ device::{Core, Normal},
+ 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::I2cClient>,
+}
+
+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::I2cClient<Core>, info: Option<&Self::IdInfo>) -> Result<Pin<KBox<Self>>> {
+ let dev = pdev.as_ref();
+
+ 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())
+ }
+
+ fn shutdown(pdev: &i2c::I2cClient<Core>) {
+ dev_dbg!(pdev.as_ref(), "Shutdown Rust I2C driver sample.\n");
+ }
+}
+
+impl Drop for SampleDriver {
+ fn drop(&mut self) {
+ dev_dbg!(self.pdev.as_ref(), "Remove Rust I2C driver sample.\n");
+ }
+}
+
+// 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>;
+
+#[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, kernel::error::Error> {
+ kernel::try_pin_init!(Self {
+ _reg <- {
+ let adapter = i2c::I2cAdapter::<Normal>::get(SAMPLE_I2C_ADAPTER_INDEX)?;
+
+ 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",
+}
--
2.43.0
Hi Igor, > On 20 Aug 2025, at 12:23, Igor Korotin <igor.korotin.linux@gmail.com> wrote: > > Add 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. > > Signed-off-by: Igor Korotin <igor.korotin.linux@gmail.com> > --- > MAINTAINERS | 1 + > samples/rust/Kconfig | 11 +++ > samples/rust/Makefile | 1 + > samples/rust/rust_driver_i2c.rs | 128 ++++++++++++++++++++++++++++++++ > 4 files changed, 141 insertions(+) > create mode 100644 samples/rust/rust_driver_i2c.rs > > diff --git a/MAINTAINERS b/MAINTAINERS > index c44c7ac317b1..2654a7ea0c80 100644 > --- a/MAINTAINERS > +++ b/MAINTAINERS > @@ -11523,6 +11523,7 @@ R: Daniel Almeida <daniel.almeida@collabora.com> > L: rust-for-linux@vger.kernel.org > S: Maintained > F: rust/kernel/i2c.rs > +F: samples/rust/rust_driver_i2c.rs > > I2C SUBSYSTEM HOST DRIVERS > M: Andi Shyti <andi.shyti@kernel.org> > diff --git a/samples/rust/Kconfig b/samples/rust/Kconfig > index 7f7371a004ee..28dae070b365 100644 > --- a/samples/rust/Kconfig > +++ b/samples/rust/Kconfig > @@ -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 > diff --git a/samples/rust/Makefile b/samples/rust/Makefile > index bd2faad63b4f..141d8f078248 100644 > --- a/samples/rust/Makefile > +++ b/samples/rust/Makefile > @@ -5,6 +5,7 @@ obj-$(CONFIG_SAMPLE_RUST_MINIMAL) += 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 > diff --git a/samples/rust/rust_driver_i2c.rs b/samples/rust/rust_driver_i2c.rs > new file mode 100644 > index 000000000000..6dfc299d5aea > --- /dev/null > +++ b/samples/rust/rust_driver_i2c.rs > @@ -0,0 +1,128 @@ > +// SPDX-License-Identifier: GPL-2.0 > + > +//! Rust I2C driver sample. > +//! > +//! This module shows how to: > +//! > +//! 1. Manually create an `i2c_client` at address `SAMPLE_I2C_CLIENT_ADDR` > +//! on the adapter with index `SAMPLE_I2C_ADAPTER_INDEX`. Blank here. > +//! 2. Register a matching Rust-based I2C driver for that client. > +//! > +//! # Requirements > +//! > +//! - The target system must expose an I2C adapter at index > +//! `SAMPLE_I2C_ADAPTER_INDEX`. Blank here > +//! - To emulate an adapter for testing, you can load the > +//! `i2c-stub` kernel module with an option `chip_addr` > +//! For example for this sample driver to emulate an I2C device with > +//! an address 0x30 you can use: > +//! `modprobe i2c-stub chip_addr=0x30` > +//! > + > +use kernel::{ > + acpi, c_str, > + device::{Core, Normal}, > + 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::I2cClient>, FYI: the pdev nomenclature is still here. > +} > + > +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::I2cClient<Core>, info: Option<&Self::IdInfo>) -> Result<Pin<KBox<Self>>> { > + let dev = pdev.as_ref(); > + > + 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()) > + } > + > + fn shutdown(pdev: &i2c::I2cClient<Core>) { > + dev_dbg!(pdev.as_ref(), "Shutdown Rust I2C driver sample.\n"); This should probably be dev_info!() I know that in general people want drivers to be quiet but: a) this a sample driver that is probably the only way to test the abstractions for the time being, so we must know whether probe() and shudown() are working by inspecting these traces. b) Unless something changed recently, there is no way to get dev_dbg() to print in Rust for now, as there is no support for dynamic debug. Andrew was working on it recently [0], but I don't think the patch was accepted yet. > + } > +} > + > +impl Drop for SampleDriver { > + 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>; I am not sure I understand. How is a type alias related to module_i2c_driver at all? Do all drivers need to introduce the alias above? > + > +#[pin_data] > +struct DriverModule { > + #[pin] > + _driver: kernel::driver::Registration<Ops<SampleDriver>>, > + _reg: i2c::Registration, > +} I was expecting this to be ARef of something, most likely I2cClient? This is where my knowledge of i2c drivers start to fall short, but others will probably chime in :) > + > +impl kernel::InPlaceModule for DriverModule { > + fn init( > + module: &'static kernel::ThisModule, > + ) -> impl ::pin_init::PinInit<Self, kernel::error::Error> { > + kernel::try_pin_init!(Self { > + _reg <- { > + let adapter = i2c::I2cAdapter::<Normal>::get(SAMPLE_I2C_ADAPTER_INDEX)?; > + > + 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", > +} > -- > 2.43.0 > > [0]: https://lore.kernel.org/rust-for-linux/20250611202952.1670168-1-andrewjballance@gmail.com/
Hello Daniel On 8/27/2025 8:38 PM, Daniel Almeida wrote: >> + } >> +} >> + >> +impl Drop for SampleDriver { >> + 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>; > > I am not sure I understand. How is a type alias related to module_i2c_driver at > all? > > Do all drivers need to introduce the alias above? > >> + >> +#[pin_data] >> +struct DriverModule { >> + #[pin] >> + _driver: kernel::driver::Registration<Ops<SampleDriver>>, >> + _reg: i2c::Registration, >> +} > > I was expecting this to be ARef of something, most likely I2cClient? > > This is where my knowledge of i2c drivers start to fall short, but others will > probably chime in :) You're right to mention this. This rust_driver_i2c is not the standard way of handling I2C devices. The idea was suggested by Danilo in the review of Patch v2: this driver merges an I2C driver sample with manual I2C device creation. The module creates a new I2cClient in its init function, and this new I2cClient is then probed by the SampleDriver. In a normal driver it should be different. For example, let’s say there’s a platform device — the probe function for this platform device would create an I2cClient, and an appropriate I2C driver would then probe it. Thanks for the review. Best Regards Igor
On Tue Sep 9, 2025 at 7:55 PM CEST, Igor Korotin wrote: > On 8/27/2025 8:38 PM, Daniel Almeida wrote: >>> +#[pin_data] >>> +struct DriverModule { >>> + #[pin] >>> + _driver: kernel::driver::Registration<Ops<SampleDriver>>, >>> + _reg: i2c::Registration, >>> +} >> >> I was expecting this to be ARef of something, most likely I2cClient? This is the Registration of an I2C device, just like auxiliary::Registration or faux::Registration. There is no point in reference counting the registration object. You can't have an ARef<I2cClient> here either because you want to manage the lifetime of the I2cClient to be registered in the system, i.e. i2c_new_client_device() and i2c_unregister_device(). >> This is where my knowledge of i2c drivers start to fall short, but others will >> probably chime in :) > > You're right to mention this. This rust_driver_i2c is not the standard > way of handling I2C devices. It is indeed the normal way of handling this. This Registration object represents the lifetime of calling i2c_new_client_device() e.g. from a platform device probe (as Igor also mentions below) and calling i2c_unregister_device() e.g. from platform device remove. The ARef<I2cClient> can be obtained from the subsequent bus callback from the I2C bus. In fact, you could add a method to i2c::Registration to provide you with an ARef<I2cClient> if that's ever needed. But the i2c::Registration itself simply manages the lifetime for the I2C device being registered in the system. > The idea was suggested by Danilo in the > review of Patch v2: this driver merges an I2C driver sample with manual > I2C device creation. The module creates a new I2cClient in its init > function, and this new I2cClient is then probed by the SampleDriver. > > In a normal driver it should be different. For example, let’s say > there’s a platform device — the probe function for this platform device > would create an I2cClient, and an appropriate I2C driver would then > probe it. > Thanks for the review. > > Best Regards > Igor
© 2016 - 2025 Red Hat, Inc.