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 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.