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
prev parent reply other threads:[~2026-10-24 17:39 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-16 15:48 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]
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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®