Re: [PATCH v21 1/3] rust: leds: add basic led classdev abstractions
Markus Probst <[email protected]>
| Newsgroups | org.kernel.vger.linux-leds,org.kernel.vger.linux-kernel,org.kernel.vger.linux-pci,org.kernel.vger.rust-for-linux |
|---|---|
| Message-ID | <[email protected]> |
On Thu, 2026-07-02 at 10:47 +0000, Alice Ryhl wrote: > On Mon, Jun 29, 2026 at 01:10:28PM +0000, Markus Probst wrote: > > Implement the core abstractions needed for led class devices, including: > > > > * `led::LedOps` - the trait for handling leds, including > > `brightness_set`, `brightness_get` and `blink_set` > > > > * `led::DeviceBuilder` - the builder for the led class device > > > > * `led::Device` - a safe wrapper around `led_classdev` > > > > Signed-off-by: Markus Probst <[email protected]> > > --- > > MAINTAINERS | 8 ++ > > rust/kernel/led.rs | 288 ++++++++++++++++++++++++++++++++++++++++++++++ > > rust/kernel/led/normal.rs | 230 ++++++++++++++++++++++++++++++++++++ > > rust/kernel/lib.rs | 1 + > > 4 files changed, 527 insertions(+) > > > > diff --git a/MAINTAINERS b/MAINTAINERS > > index 15011f5752a9..ceb2285366ff 100644 > > --- a/MAINTAINERS > > +++ b/MAINTAINERS > > @@ -14662,6 +14662,14 @@ F: drivers/leds/ > > F: include/dt-bindings/leds/ > > F: include/linux/leds.h > > > > +LED SUBSYSTEM [RUST] > > +M: Markus Probst <[email protected]> > > +L: [email protected] > > +L: [email protected] > > +S: Maintained > > +F: rust/kernel/led.rs > > +F: rust/kernel/led/ > > + > > LEGO MINDSTORMS EV3 > > R: David Lechner <[email protected]> > > S: Maintained > > diff --git a/rust/kernel/led.rs b/rust/kernel/led.rs > > new file mode 100644 > > index 000000000000..c92d99d68497 > > --- /dev/null > > +++ b/rust/kernel/led.rs > > @@ -0,0 +1,288 @@ > > +// SPDX-License-Identifier: GPL-2.0 > > + > > +//! Abstractions for the leds driver model. > > +//! > > +//! C header: [`include/linux/leds.h`](srctree/include/linux/leds.h) > > + > > +use core::{ > > + marker::PhantomData, > > + mem::transmute, > > + ptr::NonNull, // > > +}; > > + > > +use crate::{ > > + container_of, > > + device::{ > > + self, > > + property::FwNode, > > + AsBusDevice, > > + Bound, // > > + }, > > + error::{ > > + from_result, > > + to_result, > > + VTABLE_DEFAULT_ERROR, // > > + }, > > + macros::vtable, > > + prelude::*, > > + str::CStrExt, > > CStrExt is in the prelude. Please check for unnecessary imports. There is a `use super::*;` in rust/kernel/led/normal.rs and rust/kernel/led/multicolor.rs, which both make use of CStrExt. The latter being in patch 3. > > > diff --git a/rust/kernel/led/normal.rs b/rust/kernel/led/normal.rs > > new file mode 100644 > > index 000000000000..2769f690bb24 > > --- /dev/null > > +++ b/rust/kernel/led/normal.rs > > @@ -0,0 +1,230 @@ > > +// SPDX-License-Identifier: GPL-2.0 > > + > > +//! Led mode for the `struct led_classdev`. > > +//! > > +//! C header: [`include/linux/leds.h`](srctree/include/linux/leds.h) > > + > > +use super::*; > > + > > +/// The led class device representation. > > +/// > > +/// This structure represents the Rust abstraction for a led class device. > > +#[pin_data(PinnedDrop)] > > +pub struct Device<'bound, T: LedOps + 'bound> { > > + #[pin] > > + ops: T, > > + #[pin] > > + classdev: Opaque<bindings::led_classdev>, > > + _p: PhantomData<&'bound ()>, > > +} > > + > > +impl<'a, S: DeviceBuilderState> DeviceBuilder<'a, S> { > > + /// Registers a new [`Device`]. > > + pub fn build<'bound: 'a, T: LedOps + 'bound>( > > + self, > > + parent: &'bound T::Bus, > > + ops: impl PinInit<T, Error> + 'a, > > + ) -> impl PinInit<Device<'bound, T>, Error> + 'a { > > I think it would be useful to separate out the two lifetimes more > clearly. You have two sets of lifetimes: > > * 'bound which is the duration in which the bus device is bound. > * 'a which is the duration in which the `name`/`devicename` fields are > valid. > > And these have different constraints because 'bound is much larger than > 'a. The 'bound lifetime is longer than the entire Device struct, but the > 'a lifetime only needs to last for the duration of the initialization > because (I assume) the strings are copied by `led_classdev_register_ext` > > So under that logic, I would rename 'a to 'name or something like that > to indicate what it's the lifetime of. Preferably 'init, which can be used in other class device abstractions as well if needed. > > Note that if I'm wrong about the lifetime of the name strings, then this > code should be changed accordingly. It looks like you're actually > stashing the pointers in the led_classdev, and if that outlives this > initializer, then the current lifetimes are wrong, and Device must also > be annotated with 'name to indicate this additional lifetime. name and devicename only need to be valid for the duration of `led_classdev_register_ext`. > > > + const_assert!(T::MAX_BRIGHTNESS <= i32::MAX.unsigned_abs() || !T::HAS_BRIGHTNESS_GET); > > + > > + try_pin_init!(Device { > > + ops <- ops, > > + classdev <- Opaque::try_ffi_init(|ptr: *mut bindings::led_classdev| { > > + // SAFETY: `try_ffi_init` guarantees that `ptr` is valid for write. > > + // `led_classdev` gets fully initialized in-place by > > + // `led_classdev_register_ext` including `mutex` and `list_head`. > > + unsafe { > > + ptr.write(bindings::led_classdev { > > + brightness_set: (!T::BLOCKING) > > + .then_some(Adapter::<T>::brightness_set_callback), > > + brightness_set_blocking: T::BLOCKING > > + .then_some(Adapter::<T>::brightness_set_blocking_callback), > > + brightness_get: T::HAS_BRIGHTNESS_GET > > + .then_some(Adapter::<T>::brightness_get_callback), > > + blink_set: T::HAS_BLINK_SET.then_some(Adapter::<T>::blink_set_callback), > > + max_brightness: T::MAX_BRIGHTNESS, > > + brightness: self.initial_brightness, > > + color: self.color as u32, > > + name: self.name.map_or(core::ptr::null(), CStrExt::as_char_ptr), > > + ..bindings::led_classdev::default() > > + }) > > + }; > > + > > + let mut init_data = bindings::led_init_data { > > + fwnode: self > > + .fwnode > > + .as_ref() > > + .map_or(core::ptr::null_mut(), |fwnode| fwnode.as_raw()), > > + default_label: core::ptr::null(), > > + devicename: self > > + .devicename > > + .map_or(core::ptr::null(), CStrExt::as_char_ptr), > > + devname_mandatory: self.devname_mandatory, > > + }; > > + > > + // SAFETY: > > + // - `parent.as_ref().as_raw()` is guaranteed to be a pointer to a valid > > + // `device`. > > + // - `ptr` is guaranteed to be a pointer to an initialized `led_classdev`. > > + to_result(unsafe { > > + bindings::led_classdev_register_ext( > > + parent.as_ref().as_raw(), > > + ptr, > > + if self.name.is_none() { > > + &raw mut init_data > > + } else { > > + core::ptr::null_mut() > > + }, > > + ) > > + })?; > > + > > + core::mem::forget(self.fwnode); // keep the reference count incremented > > + > > + Ok::<_, Error>(()) > > + }), > > + _p: PhantomData, > > + }) > > + } > > +} Thanks - Markus Probst > > Alice
signature.asc
(application/pgp-signature, 870 B)
-----BEGIN PGP SIGNATURE----- iQJPBAABCAA5FiEEgnQYxPSsWOdyMMRzNHYf+OetQ9IFAmpGRMgbFIAAAAAABAAO bWFudTIsMi41KzEuMTIsMiwyAAoJEDR2H/jnrUPSstgP/jeRhMo6ufOlvpqQ8e6k QXE8GZP3zMcxatGN6muc70zN9JwvtkveUebLL2P8G+eotsIm6vIvahrj1QRGPeWO B0shZowBe0hN8os3NjCh7f19bxDVXnwtOyzy9knRUaWuOhIwfuu8B3jhNWrWVreN w9q82dO0dnQpMhteeKMulnsSdgx+JJ9aP1H0D4COf1l890bf6jk2AKjOOUry/9mg z6lsy4T9QD/7ipmdpJf81kLZUVAXjqYJt7uqqy4yV+S3nTKFA5fxoexGruBTGZDI lt6eBXmhdmYkGm2QNYcpplOtx+KOCP53luMeg1jVCPahxHyVTegmQLwpQHB4QPxz v9rFWCZPYdcU5ToHZ9Zz28ykRZy4Xgg6RkDnnNb8MyDtfQeNIrie5F8WCfBuqKjE p6o0bkWk3I5lyXYigV2jq4iet/SXEKT8xNiE+4NfdG24xCZaYoymtDj7a7JbkuFe Zkxw3yi9p2GJCJ27eZRkikg8b4sIy6YwyP1ckM9wA8j8AgBWB+Vy8XouYVCG6sHQ QCaCY+hougwkoCQP7TI+fWBThc+QtWYODqT3X1iekiM8/3/ynDFV3UqYnYMdJNpH /BRFgC2uqY1WpjX2tl7ud0bDn/PYbc4Nxo/WzePnoYST5HKm9o1agolvZN+yPE0d VFzvJ3qCeqEVCRyNtQWnq6bU =hQoD -----END PGP SIGNATURE-----