From: Alice Ryhl <aliceryhl@google.com>
To: Markus Probst <markus.probst@posteo.de>
Cc: Danilo Krummrich <dakr@kernel.org>,
Miguel Ojeda <ojeda@kernel.org>,
Alex Gaynor <alex.gaynor@gmail.com>, Lee Jones <lee@kernel.org>,
Pavel Machek <pavel@kernel.org>,
Lorenzo Stoakes <lorenzo.stoakes@oracle.com>,
Vlastimil Babka <vbabka@suse.cz>,
"Liam R. Howlett" <Liam.Howlett@oracle.com>,
Uladzislau Rezki <urezki@gmail.com>,
Boqun Feng <boqun.feng@gmail.com>, Gary Guo <gary@garyguo.net>,
bjorn3_gh@protonmail.com, Benno Lossin <lossin@kernel.org>,
Andreas Hindborg <a.hindborg@kernel.org>,
Trevor Gross <tmgross@umich.edu>,
rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org,
linux-leds@vger.kernel.org
Subject: Re: [PATCH v2 2/2] rust: leds: add basic led classdev abstractions
Date: Fri, 10 Oct 2025 11:41:29 +0000 [thread overview]
Message-ID: <aOjw6QtVArJ807MX@google.com> (raw)
In-Reply-To: <20251009181203.248471-3-markus.probst@posteo.de>
On Thu, Oct 09, 2025 at 06:12:34PM +0000, Markus Probst wrote:
> Implement the core abstractions needed for led class devices, including:
>
> * `led::Handler` - the trait for handling leds, including
> `brightness_set`
>
> * `led::InitData` - data set for the led class device
>
> * `led::Device` - a safe wrapper around `led_classdev`
>
> Signed-off-by: Markus Probst <markus.probst@posteo.de>
> +/// The led class device representation.
> +///
> +/// This structure represents the Rust abstraction for a C `struct led_classdev`.
> +#[pin_data(PinnedDrop)]
> +pub struct Device {
> + handler: KBox<dyn HandlerImpl>,
> + #[pin]
> + classdev: Opaque<bindings::led_classdev>,
> +}
Is it not possible to use Device<T> with a `handler: T` field here? That
avoids the box. I don't think other abstractions have needed that. If
it's needed, then why is LED special?
> +/// The led handler trait.
> +///
> +/// # Examples
> +///
> +///```
> +/// # use kernel::{c_str, led, alloc::KBox, error::{Result, code::ENOSYS}};
> +/// # use core::pin::Pin;
> +///
> +/// struct MyHandler;
> +///
> +///
> +/// impl led::Handler for MyHandler {
> +/// const BLOCKING = false;
> +/// const MAX_BRIGHTNESS = 255;
> +///
> +/// fn brightness_set(&self, _brightness: u32) -> Result<()> {
> +/// // Set the brightness for the led here
> +/// Ok(())
> +/// }
> +/// }
> +///
> +/// fn register_my_led() -> Result<Pin<KBox<led::Device>>> {
> +/// let handler = MyHandler;
> +/// KBox::pin_init(led::Device::new(
> +/// None,
> +/// None,
> +/// led::InitData::new()
> +/// .default_label(c_str!("my_led")),
> +/// handler,
> +/// ))
> +/// }
> +///```
> +/// Led drivers must implement this trait in order to register and handle a [`Device`].
> +pub trait Handler {
> + /// If set true, [`Handler::brightness_set`] and [`Handler::blink_set`] must not sleep
> + /// and perform the operation immediately.
> + const BLOCKING: bool;
> + /// Set this to true, if [`Handler::blink_set`] is implemented.
> + const BLINK: bool = false;
We have a macro called #[vtable] that automatically generates this kind
of constant for you. Please use it.
> +impl Device {
> + /// Registers a new led classdev.
> + ///
> + /// The [`Device`] will be unregistered and drop.
> + pub fn new<'a, T: Handler + 'static>(
> + parent: Option<&'a device::Device>,
> + init_data: InitData<'a>,
> + handler: T,
> + ) -> impl PinInit<Self, Error> + use<'a, T> {
> + try_pin_init!(Self {
> + handler <- {
> + let handler: KBox<dyn HandlerImpl> = KBox::<T>::new(handler, GFP_KERNEL)?;
> + Ok::<_, Error>(handler)
> + },
> + classdev <- Opaque::try_ffi_init(|ptr: *mut bindings::led_classdev| {
> + // SAFETY: `try_ffi_init` guarantees that `ptr` is valid for write.
> + unsafe { ptr.write(bindings::led_classdev {
> + max_brightness: T::MAX_BRIGHTNESS,
> + brightness_set: T::BLOCKING.then_some(
> + brightness_set as unsafe extern "C" fn(*mut bindings::led_classdev, u32)
> + ),
> + brightness_set_blocking: (!T::BLOCKING).then_some(
> + brightness_set_blocking
> + as unsafe extern "C" fn(*mut bindings::led_classdev,u32) -> i32
> + ),
> + blink_set: T::BLINK.then_some(
> + blink_set
> + as unsafe extern "C" fn(*mut bindings::led_classdev, *mut usize,
> + *mut usize) -> i32
I think you can just do `blink_set as _`.
> + ),
> + .. bindings::led_classdev::default()
> + }) };
> +
> + let mut init_data = bindings::led_init_data {
> + fwnode: init_data.fwnode.map_or(core::ptr::null_mut(), FwNode::as_raw),
> + default_label: init_data.default_label
> + .map_or(core::ptr::null(), CStr::as_char_ptr),
> + devicename: init_data.devicename.map_or(core::ptr::null(), CStr::as_char_ptr),
> + devname_mandatory: init_data.devname_mandatory,
> + };
> +
> + let parent = parent
> + .map_or(core::ptr::null_mut(), device::Device::as_raw);
> +
> + // SAFETY:
> + // - `parent` is guaranteed to be a pointer to a valid `device`
> + // or a null pointer.
> + // - `ptr` is guaranteed to be a pointer to an initialized `led_classdev`.
> + to_result(unsafe {
> + bindings::led_classdev_register_ext(parent, ptr, &mut init_data)
So it's ok that `init_data` is valid for the duration of this call and
no longer? It doesn't stash a pointer to it anywhere?
> +extern "C" fn brightness_set(led_cdev: *mut bindings::led_classdev, brightness: u32) {
> + // SAFETY: `led_cdev` is a valid pointer to a `led_classdev` stored inside a `Device`.
> + let classdev = unsafe {
> + &*container_of!(
> + led_cdev.cast::<Opaque<bindings::led_classdev>>(),
Please use Opaque::cast_from instead.
> + // SAFETY: `led_cdev` is a valid pointer to a `led_classdev` stored inside a `Device`.
> + let classdev = unsafe {
> + &*container_of!(
> + led_cdev.cast::<Opaque<bindings::led_classdev>>(),
> + Device,
> + classdev
> + )
> + };
Instead of repeating this logic many times, I suggest a Device::from_raw
method.
> +extern "C" fn blink_set(
> + led_cdev: *mut bindings::led_classdev,
> + delay_on: *mut usize,
> + delay_off: *mut usize,
> +) -> i32 {
> + // SAFETY: `led_cdev` is a valid pointer to a `led_classdev` stored inside a `Device`.
> + let classdev = unsafe {
> + &*container_of!(
> + led_cdev.cast::<Opaque<bindings::led_classdev>>(),
> + Device,
> + classdev
> + )
> + };
> + from_result(|| {
> + classdev.handler.blink_set(
> + // SAFETY: `delay_on` is guaranteed to be a valid pointer to usize
> + unsafe { &mut *delay_on },
> + // SAFETY: `delay_on` is guaranteed to be a valid pointer to usize
> + unsafe { &mut *delay_off },
To create a mutable reference, this safety comment should argue why it
is the case that nobody will access these fields for the duration of
`blink_set`. (For example, from another thread?)
Alice
next prev parent reply other threads:[~2025-10-10 11:41 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-10-09 18:12 [PATCH v2 0/2] rust: leds: add led classdev abstractions Markus Probst
2025-10-09 18:12 ` [PATCH v2 1/2] rust: add basic Pin<Vec<T, A>> abstractions Markus Probst
2025-10-10 11:33 ` Alice Ryhl
2025-10-10 21:14 ` Markus Probst
2025-10-09 18:12 ` [PATCH v2 2/2] rust: leds: add basic led classdev abstractions Markus Probst
2025-10-10 11:41 ` Alice Ryhl [this message]
2025-10-10 13:03 ` Markus Probst
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=aOjw6QtVArJ807MX@google.com \
--to=aliceryhl@google.com \
--cc=Liam.Howlett@oracle.com \
--cc=a.hindborg@kernel.org \
--cc=alex.gaynor@gmail.com \
--cc=bjorn3_gh@protonmail.com \
--cc=boqun.feng@gmail.com \
--cc=dakr@kernel.org \
--cc=gary@garyguo.net \
--cc=lee@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-leds@vger.kernel.org \
--cc=lorenzo.stoakes@oracle.com \
--cc=lossin@kernel.org \
--cc=markus.probst@posteo.de \
--cc=ojeda@kernel.org \
--cc=pavel@kernel.org \
--cc=rust-for-linux@vger.kernel.org \
--cc=tmgross@umich.edu \
--cc=urezki@gmail.com \
--cc=vbabka@suse.cz \
/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