From: Igor Korotin <igor.korotin@linux.dev>
To: Muchamad Coirul Anwar <muchamadcoirulanwar@gmail.com>
Cc: Danilo Krummrich <dakr@kernel.org>,
Jonathan Cameron <jic23@kernel.org>,
Miguel Ojeda <ojeda@kernel.org>, Gary Guo <gary@garyguo.net>,
Alice Ryhl <aliceryhl@google.com>,
Daniel Almeida <daniel.almeida@collabora.com>,
Lars-Peter Clausen <lars@metafoo.de>,
Mark Brown <broonie@kernel.org>,
Brandon StJohn <branstj@gmail.com>,
linux-iio@vger.kernel.org, linux-i2c@vger.kernel.org,
rust-for-linux@vger.kernel.org, driver-core@lists.linux.dev,
linux-kernel@vger.kernel.org
Subject: Re: [RFC PATCH v6 0/4] iio: position: add Rust driver for ams AS5600
Date: Sat, 24 Oct 2026 18:39:46 +0100 [thread overview]
Message-ID: <179286358623.55649.5397316745104390556@linux.dev> (raw)
In-Reply-To: <CAO26r3QK+Q5=JR5TeMzEX-WiLpgYWd8Zn0O1qx11LxTgN96BPg@mail.gmail.com>
Hi Coirul,
Here's the full diff, applied on top of your v6 patches 3 and 4.
diff --git a/drivers/iio/position/as5600.rs b/drivers/iio/position/as5600.rs
index b72775241e81..f2897fbf8aa2 100644
--- a/drivers/iio/position/as5600.rs
+++ b/drivers/iio/position/as5600.rs
@@ -36,7 +36,8 @@
Device,
IioChanInfo,
IioDriver,
- IioVal, //
+ IioVal,
+ Registered, //
},
io::Io,
module_i2c_driver,
@@ -65,14 +66,12 @@
i2c_device_table!(
I2C_TABLE,
- MODULE_I2C_TABLE,
<As5600 as Driver>::IdInfo,
[(DeviceId::new(c"as5600"), ())]
);
of_device_table!(
OF_TABLE,
- MODULE_OF_TABLE,
<As5600 as Driver>::IdInfo,
[(of::DeviceId::new(c"ams,as5600"), ())]
);
@@ -97,25 +96,29 @@ unsafe impl Sync for As5600Channels {}
});
#[pin_data]
-struct As5600Priv {
+struct As5600Priv<'a> {
+ client: &'a I2cClient<Bound>,
#[pin]
io_lock: Mutex<()>,
}
+// SAFETY: `I2cClient<Bound>` methods go through kernel I2C core functions
+// that provide their own synchronization (bus locking); no `&self` method
+// exposes interior mutability. The borrowed `client` field can therefore be
+// safely accessed and shared across threads like the rest of `As5600Priv`.
+unsafe impl Send for As5600Priv<'_> {}
+// SAFETY: See above.
+unsafe impl Sync for As5600Priv<'_> {}
+
// SAFETY: AS5600_CHANNELS contains one valid, immutable IIO_ANGL channel
// exposing only RAW and SCALE. It has no event or extension callbacks,
// and all optional pointer fields are null.
-unsafe impl IioDriver for As5600Priv {
- fn read_raw(
- &self,
- parent: &kernel::device::Device<Bound>,
- _chan: *const iio_chan_spec,
- info: IioChanInfo,
- ) -> Result<IioVal> {
+unsafe impl IioDriver for As5600Priv<'_> {
+ fn read_raw(&self, _chan: *const iio_chan_spec, info: IioChanInfo) -> Result<IioVal> {
match info {
IioChanInfo::Raw => {
let _guard = self.io_lock.lock();
- let client: &I2cClient<Bound> = parent.try_into()?;
+ let client = self.client;
let io = client.smbus_io();
// Read status register to verify magnet presence before
@@ -142,14 +145,17 @@ fn channels(&self) -> &'static [iio_chan_spec] {
}
}
+struct As5600;
+
#[pin_data]
-struct As5600 {
- _registration: (),
+struct As5600Data<'bound> {
+ #[pin]
+ _iio_dev: Device<As5600Priv<'bound>, Registered>,
}
impl Driver for As5600 {
type IdInfo = ();
- type Data<'bound> = As5600;
+ type Data<'bound> = As5600Data<'bound>;
const I2C_ID_TABLE: Option<IdTable<Self::IdInfo>> = Some(&I2C_TABLE);
const OF_ID_TABLE: Option<of::IdTable<Self::IdInfo>> = Some(&OF_TABLE);
@@ -162,15 +168,22 @@ fn probe<'bound>(
dev: &'bound I2cClient<Core<'_>>,
_id_info: Option<&'bound Self::IdInfo>,
) -> impl PinInit<Self::Data<'bound>, Error> + 'bound {
- try_pin_init!(As5600 {
- _registration: {
+ try_pin_init!(As5600Data {
+ _iio_dev: {
+ // Deref coercion: I2cClient<Core<'_>> -> I2cClient<Bound>.
+ let client: &'bound I2cClient<Bound> = dev;
+
let priv_init = pin_init!(As5600Priv {
+ client,
io_lock <- new_mutex!(()),
});
- Device::build_device(dev.as_ref(), c"as5600", INDIO_DIRECT_MODE, priv_init)?
- .register(&crate::THIS_MODULE)?;
+ let unregistered =
+ Device::build_device(dev.as_ref(), c"as5600", INDIO_DIRECT_MODE, priv_init)?;
+ let registered =
+ unregistered.register(kernel::module::this_module::<crate::LocalModule>())?;
dev_dbg!(dev.as_ref(), "AS5600 magnetic position sensor ready\n");
+ registered
}
})
}
diff --git a/rust/kernel/iio.rs b/rust/kernel/iio.rs
index 9ad2f50948de..4648424c7600 100644
--- a/rust/kernel/iio.rs
+++ b/rust/kernel/iio.rs
@@ -21,7 +21,6 @@
self,
Bound, //
},
- devres,
error::{
code::*,
to_result,
@@ -159,15 +158,12 @@ pub enum IioVal {
pub unsafe trait IioDriver: Send + Sync {
/// Called by the IIO core to read channel information.
///
- /// `parent` is the parent device supplied to [`Device::build_device`].
/// `chan` identifies the channel, and `info` selects the requested
- /// information, such as its raw value or scale.
- fn read_raw(
- &self,
- parent: &device::Device<Bound>,
- chan: *const iio_chan_spec,
- info: IioChanInfo,
- ) -> Result<IioVal>;
+ /// information, such as its raw value or scale. Implementors must
+ /// capture whatever bus-specific resources they need at registration
+ /// time (see [`Device::build_device`]) rather than re-deriving them
+ /// from a generic parent reference here.
+ fn read_raw(&self, chan: *const iio_chan_spec, info: IioChanInfo) -> Result<IioVal>;
/// Returns the channel specifications for this driver.
///
@@ -204,16 +200,6 @@ fn channels(&self) -> &'static [iio_chan_spec] {
// initialized `T` that remains valid for the duration of this callback.
let driver = unsafe { &*priv_ptr };
- // SAFETY: `indio_dev` is valid by the callback contract. Its parent
- // was supplied as a bound device to `build_device`.
- //
- // During registration, the builder holds that bound parent borrow.
- // After ownership is transferred, parent devres runs the registered
- // device's destructor, which unregisters the IIO device and drains
- // the supported read_raw paths before destroying the private data.
- // The parent therefore remains bound throughout this callback.
- let parent = unsafe { device::Device::<Bound>::from_raw((*indio_dev).dev.parent) };
-
// `info` is `long` from C. Negative values and values > u32::MAX
// are unsupported iio_chan_info variants; both map to EINVAL.
let info_enum = match u32::try_from(info)
@@ -224,7 +210,7 @@ fn channels(&self) -> &'static [iio_chan_spec] {
None => return EINVAL.to_errno(),
};
- match driver.read_raw(parent, chan, info_enum) {
+ match driver.read_raw(chan, info_enum) {
Ok(IioVal::Int(v)) => {
// SAFETY: `val` is valid per the function's Safety contract above.
// `val2` is not written; `IIO_VAL_INT` signals to the IIO core
@@ -271,11 +257,8 @@ fn channels(&self) -> &'static [iio_chan_spec] {
pub struct Registered;
/// An initialized IIO device awaiting registration.
-///
-/// The parent remains bound until this builder is consumed or dropped.
-pub struct UnregisteredDevice<'a, T: IioDriver> {
+pub struct UnregisteredDevice<T: IioDriver> {
device: Device<T>,
- parent: &'a device::Device<Bound>,
}
/// An owned IIO device with in-place initialized driver data.
@@ -363,12 +346,12 @@ impl<T: IioDriver> Device<T> {
/// Allocation and initialization errors are propagated.
///
/// Call [`UnregisteredDevice::register`] to register the device.
- pub fn build_device<'a, E>(
- parent: &'a device::Device<Bound>,
+ pub fn build_device<E>(
+ parent: &device::Device<Bound>,
name: &'static CStr,
modes: u32,
init: impl PinInit<T, E>,
- ) -> Result<UnregisteredDevice<'a, T>>
+ ) -> Result<UnregisteredDevice<T>>
where
Error: From<E>,
{
@@ -406,7 +389,7 @@ pub fn build_device<'a, E>(
// large enough for `T`. The private data remains at a fixed address in
// the separate IIO allocation until it is dropped. On initialization
// failure, the error path only frees the allocation.
- let init_result = unsafe { init.__pinned_init(priv_ptr) };
+ let init_result = unsafe { pin_init::raw_try_init(priv_ptr, init) };
if let Err(e) = init_result {
// SAFETY: `pin_init` guarantees partial-init rollback internally.
// `priv_` memory was not fully initialized, so we only free the
@@ -446,21 +429,23 @@ pub fn build_device<'a, E>(
(*indio_dev).modes = modes;
}
- Ok(UnregisteredDevice { device, parent })
+ Ok(UnregisteredDevice { device })
}
}
-impl<T: IioDriver + 'static> UnregisteredDevice<'_, T> {
- /// Registers the IIO device and transfers ownership to parent devres.
+impl<T: IioDriver> UnregisteredDevice<T> {
+ /// Registers the IIO device.
///
- /// Parent resource teardown unregisters the device and completes its
- /// supported `read_raw` callbacks before destroying the private data.
+ /// The caller owns the returned, pinned [`Device<T, Registered>`] and is
+ /// responsible for keeping it alive for as long as the registration
+ /// should remain visible to userspace; dropping it unregisters the
+ /// device and tears down the private data.
///
/// On failure, the device is released and any successful registration is
/// undone.
#[inline]
- pub fn register(self, module: &'static ThisModule) -> Result {
- let Self { device, parent } = self;
+ pub fn register(self, module: &'static ThisModule) -> Result<Device<T, Registered>> {
+ let Self { device } = self;
// SAFETY: `device.indio_dev` is a valid, fully-initialized `iio_dev`
// allocated by `iio_device_alloc` with its name, info vtable, channels,
@@ -475,6 +460,6 @@ pub fn register(self, module: &'static ThisModule) -> Result {
};
forget(device);
- devres::register(parent, registered, GFP_KERNEL)
+ Ok(registered)
}
}
Cheers
Igor
next prev parent reply other threads:[~2026-10-24 17:39 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-16 15:48 [RFC PATCH v6 0/4] iio: position: add Rust driver for ams AS5600 Muchamad Coirul Anwar
2026-10-16 15:48 ` [RFC PATCH v6 1/4] rust: io: add FallibleIoCapable Muchamad Coirul Anwar
2026-10-16 15:48 ` [RFC PATCH v6 2/4] i2c: rust: implement SMBus access via IoBackend Muchamad Coirul Anwar
2026-10-16 15:48 ` [RFC PATCH v6 3/4] rust: add minimal IIO subsystem abstractions Muchamad Coirul Anwar
2026-10-16 15:48 ` [RFC PATCH v6 4/4] iio: position: add Rust driver for ams AS5600 Muchamad Coirul Anwar
2026-10-21 16:59 ` [RFC PATCH v6 0/4] " Igor Korotin
2026-10-22 16:05 ` Muchamad Coirul Anwar
2026-10-24 17:39 ` Igor Korotin [this message]
2026-10-27 16:01 ` Muchamad Coirul Anwar
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=179286358623.55649.5397316745104390556@linux.dev \
--to=igor.korotin@linux.dev \
--cc=aliceryhl@google.com \
--cc=branstj@gmail.com \
--cc=broonie@kernel.org \
--cc=dakr@kernel.org \
--cc=daniel.almeida@collabora.com \
--cc=driver-core@lists.linux.dev \
--cc=gary@garyguo.net \
--cc=jic23@kernel.org \
--cc=lars@metafoo.de \
--cc=linux-i2c@vger.kernel.org \
--cc=linux-iio@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=muchamadcoirulanwar@gmail.com \
--cc=ojeda@kernel.org \
--cc=rust-for-linux@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.