Re: [PATCH v2 3/5] rust: leds: Add hardware trigger support for hardware-controlled LEDs

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Mon, Jan 13, 2025 at 01:16:18PM +0100, Fiona Behrens wrote:
> Adds abstraction for hardware trigger support in LEDs, enabling LEDs to
> be controlled by external hardware events.
> 
> An `Arc` is embedded within the `led_classdev` to manage the lifecycle
> of the hardware trigger, ensuring proper reference counting and cleanup
> when the LED is dropped.
> 
> Signed-off-by: Fiona Behrens <me@xxxxxxxxxx>
> ---
>  MAINTAINERS                  |   1 +
>  rust/kernel/leds.rs          |  95 +++++++++++++++++++++++---
>  rust/kernel/leds/triggers.rs | 128 +++++++++++++++++++++++++++++++++++
>  3 files changed, 214 insertions(+), 10 deletions(-)
>  create mode 100644 rust/kernel/leds/triggers.rs
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index cef929b57159..954dbd311a55 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -13020,6 +13020,7 @@ F:	drivers/leds/
>  F:	include/dt-bindings/leds/
>  F:	include/linux/leds.h
>  F:	rust/kernel/leds.rs
> +F:	rust/kernel/leds/
>  
>  LEGO MINDSTORMS EV3
>  R:	David Lechner <david@xxxxxxxxxxxxxx>
> diff --git a/rust/kernel/leds.rs b/rust/kernel/leds.rs
> index 980af7c405d4..f10a10b56e23 100644
> --- a/rust/kernel/leds.rs
> +++ b/rust/kernel/leds.rs
> @@ -10,9 +10,14 @@
>  use crate::error::from_result;
>  use crate::ffi::c_ulong;
>  use crate::prelude::*;
> +#[cfg(CONFIG_LEDS_TRIGGERS)]
> +use crate::sync::Arc;
>  use crate::time::Delta;
>  use crate::types::Opaque;
>  
> +#[cfg(CONFIG_LEDS_TRIGGERS)]
> +pub mod triggers;
> +
>  /// Color of an LED.
>  #[allow(missing_docs)]
>  #[derive(Copy, Clone)]
> @@ -110,12 +115,34 @@ fn try_from(value: u32) -> Result<Self, Self::Error> {
>  }
>  
>  /// Data used for led registration.
> -#[derive(Clone)]
> -pub struct LedConfig<'name> {
> +pub struct LedConfig<'name, T> {
>      /// Name to give the led.
>      pub name: Option<&'name CStr>,
>      /// Color of the LED.
>      pub color: Color,
> +    /// Private data of the LED.
> +    pub data: T,
> +
> +    /// Default trigger name.
> +    pub default_trigger: Option<&'static CStr>,
> +    /// Hardware trigger.
> +    ///
> +    /// Setting this to some also defaults the default trigger to this hardware trigger.
> +    /// Use `default_trigger: Some("none")` to overwrite this.
> +    #[cfg(CONFIG_LEDS_TRIGGERS)]
> +    pub hardware_trigger: Option<Arc<triggers::Hardware<T>>>,
> +}
> +
> +impl<'name, T> LedConfig<'name, T> {
> +    /// Create a new LedConfig
> +    pub fn new(color: Color, data: T) -> Self {
> +        Self {
> +            color,
> +            data,
> +            // SAFETY: all other fields are valid with zeroes.
> +            ..unsafe { core::mem::zeroed() }
> +        }
> +    }
>  }
>  
>  /// A Led backed by a C `struct led_classdev`, additionally offering
> @@ -141,8 +168,7 @@ impl<T> Led<T>
>      #[cfg(CONFIG_LEDS_CLASS)]
>      pub fn register<'a>(
>          device: Option<&'a Device>,
> -        config: &'a LedConfig<'a>,
> -        data: T,
> +        config: LedConfig<'a, T>,
>      ) -> impl PinInit<Self, Error> + 'a
>      where
>          T: 'a,
> @@ -188,14 +214,46 @@ pub fn register<'a>(
>                  unsafe { ptr::write(set_fn_ptr, Some(blink_set::<T>)) };
>              }
>  
> +        #[cfg(CONFIG_LEDS_TRIGGERS)]
> +        if let Some(trigger) = config.hardware_trigger {
> +            let trigger = trigger.into_raw();
> +            // SAFETY: `place` is pointing to a live allocation.
> +            let trigger_type_ptr = unsafe { ptr::addr_of_mut!((*place).trigger_type) };
> +            // SAFETY: `trigger` is a valid pointer
> +            let hw_trigger = unsafe { ptr::addr_of!((*trigger).hw_type) };
> +            // SAFETY: `trigger_type_ptr` points to a valid allocation and we have exclusive access.
> +            unsafe { ptr::write(trigger_type_ptr, hw_trigger.cast_mut().cast()) };
> +
> +            // SAFETY: trigger points to a valid hardware trigger struct.
> +            let trigger_name_ptr = unsafe { Opaque::raw_get(ptr::addr_of!( (*trigger).trigger)) };
> +            // SAFETY: trigger points to a valid hardware trigger struct.
> +            let trigger_name_ptr = unsafe { (*trigger_name_ptr).name };
> +            // SAFETY: `place` is pointing to a live allocation.
> +            let default_trigger_ptr = unsafe { ptr::addr_of_mut!((*place).default_trigger) };
> +            // SAFETY: `default_trigger_ptr` points to a valid allocation and we have exclusive access.
> +            unsafe { ptr::write(default_trigger_ptr, trigger_name_ptr) };
> +
> +            // SAFETY: `place` is pointing to a live allocation.
> +            let hw_ctrl_trigger_ptr = unsafe { ptr::addr_of_mut!((*place).hw_control_trigger) };
> +            // SAFETY: `hw_ctrl_trigger_ptr` points to a valid allocation and we have exclusive access.
> +            unsafe { ptr::write(hw_ctrl_trigger_ptr, trigger_name_ptr) };
> +        }
> +
> +        // After hw trigger impl, to overwrite default trigger
> +        if let Some(default_trigger) = config.default_trigger {
> +            // SAFETY: `place` is pointing to a live allocation.
> +            let default_trigger_ptr = unsafe { ptr::addr_of_mut!((*place).default_trigger) };
> +            // SAFETY: `default_trigger_ptr` points to a valid allocation and we have exclusive access.
> +            unsafe { ptr::write(default_trigger_ptr, default_trigger.as_char_ptr()) };
> +        }
>  
> -            let dev = device.map(|dev| dev.as_raw()).unwrap_or(ptr::null_mut());
> -            // SAFETY: `place` is a pointer to a live allocation of `bindings::led_classdev`.
> -            crate::error::to_result(unsafe {
> -                bindings::led_classdev_register_ext(dev, place, ptr::null_mut())
> -            })
> +        let dev = device.map(|dev| dev.as_raw()).unwrap_or(ptr::null_mut());
> +        // SAFETY: `place` is a pointer to a live allocation of `bindings::led_classdev`.
> +        crate::error::to_result(unsafe {
> +                    bindings::led_classdev_register_ext(dev, place, ptr::null_mut())
> +        })
>              }),
> -            data: data,
> +            data: config.data,
>          })
>      }
>  }
> @@ -220,6 +278,23 @@ fn drop(self: Pin<&mut Self>) {
>          unsafe {
>              bindings::led_classdev_unregister(self.led.get())
>          }
> +
> +        // drop trigger if there is a hw trigger defined.
> +        #[cfg(CONFIG_LEDS_TRIGGERS)]
> +        {
> +            // SAFETY: `self.led` is a valid led.
> +            let hw_trigger_type =
> +                unsafe { ptr::read(ptr::addr_of!((*self.led.get()).trigger_type)) };
> +            if !hw_trigger_type.is_null() {
> +                // SAFETY: hw_trigger_type is a valid and non null pointer into a Hardware trigger.
> +                let hw_trigger_type = unsafe {
> +                    crate::container_of!(hw_trigger_type, triggers::Hardware<T>, hw_type)
> +                };
> +                // SAFETY: `hw_trigger_type` is a valid pointer that came from an arc.
> +                let hw_trigger_type = unsafe { Arc::from_raw(hw_trigger_type) };
> +                drop(hw_trigger_type);
> +            }
> +        }
>      }
>  }
>  
> diff --git a/rust/kernel/leds/triggers.rs b/rust/kernel/leds/triggers.rs
> new file mode 100644
> index 000000000000..d5f2b8252645
> --- /dev/null
> +++ b/rust/kernel/leds/triggers.rs
> @@ -0,0 +1,128 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +//! LED trigger abstractions.
> +
> +use core::marker::PhantomData;
> +use core::ptr;
> +
> +use crate::error::{from_result, to_result};
> +use crate::prelude::*;
> +use crate::types::Opaque;
> +
> +use super::FromLedClassdev;
> +
> +/// LED Hardware trigger.
> +///
> +/// Used to impement a hardware operation mode for an LED.
> +#[pin_data(PinnedDrop)]
> +pub struct Hardware<T> {
> +    #[pin]
> +    pub(crate) hw_type: Opaque<bindings::led_hw_trigger_type>,

This should probably be called trigger_type instead of hw_type,
as it is in the C version of the code.

> +    #[pin]
> +    pub(crate) trigger: Opaque<bindings::led_trigger>,
> +    _t: PhantomData<T>,
> +}
> +
> +impl<T> Hardware<T>
> +where
> +    T: HardwareOperations,
> +{
> +    /// Register a new hardware Trigger with a given name.
> +    pub fn register(name: &'static CStr) -> impl PinInit<Self, Error> {
> +        try_pin_init!( Self {
> +            // SAFETY: `led_hw_trigger_type` is valid with all zeroes.
> +            hw_type: Opaque::new(unsafe { core::mem::zeroed() }),
> +            trigger <- Opaque::try_ffi_init(move |place: *mut bindings::led_trigger| {
> +            // SAFETY: `place` is a pointer to a live allocation, so erasing is valid.
> +            unsafe { place.write_bytes(0, 1) };
> +
> +            // Add name
> +            // SAFETY: `place` is pointing to a live allocation, so the deref is safe.
> +            let name_ptr = unsafe { ptr::addr_of_mut!((*place).name) };
> +            // SAFETY: `name_ptr` points to a valid allocation and we have exclusive access.
> +            unsafe { ptr::write(name_ptr, name.as_char_ptr()) };
> +
> +            // Add fn pointers
> +            // SAFETY: `place` is pointing to a live allocation, so the deref is safe.
> +            let activate_fn_ptr: *mut Option<_> = unsafe { ptr::addr_of_mut!((*place).activate) };
> +            // SAFETY: `activate_fn_ptr` points to a valid allocation and we have exclusive access.
> +            unsafe { ptr::write(activate_fn_ptr, Some(trigger_activate::<T>)) };
> +
> +            if T::HAS_DEACTIVATE {
> +                // SAFETY: `place` is pointing to a live allocation, so the deref is safe.
> +                let deactivate_fn_ptr: *mut Option<_> = unsafe { ptr::addr_of_mut!((*place).deactivate) };
> +                // SAFETY: `deactivate_fn_ptr` points to a valid allocation and we have exclusive access.
> +                unsafe { ptr::write(deactivate_fn_ptr, Some(trigger_deactivate::<T>)) };
> +            }
> +
> +            // Add hardware trigger
> +            // SAFETY: `place` is pointing to a live allocation, so the deref is safe.
> +            let trigger_type_ptr = unsafe { ptr::addr_of_mut!((*place).trigger_type) };
> +            // SAFETY: `place` is pointing to a live allocation, so the deref is safe.
> +            let trigger_type = unsafe { crate::container_of!(place, Self, trigger).cast_mut() };
> +            // SAFETY: `trigger_type` is pointing to a live allocation of Self.
> +            let trigger_type = unsafe { ptr::addr_of!((*trigger_type).hw_type) };
> +            // SAFETY: `trigger_type_ptr` points to a valid allocation and we have exclusive access.
> +            unsafe{ ptr::write(trigger_type_ptr, Opaque::raw_get(trigger_type)) };
> +
> +        // SAFETY: ffi call, `place` is sufficently filled with data at this point
> +            to_result(unsafe {
> +                bindings::led_trigger_register(place)
> +            })
> +            }),
> +            _t: PhantomData,
> +        })
> +    }
> +}
> +
> +#[pinned_drop]
> +impl<T> PinnedDrop for Hardware<T> {
> +    fn drop(self: Pin<&mut Self>) {
> +        // SAFETY: trigger is pointing to a live and registered allocation
> +        unsafe {
> +            bindings::led_trigger_unregister(self.trigger.get());
> +        }
> +    }
> +}
> +
> +/// Operations for the Hardware trigger
> +#[macros::vtable]
> +pub trait HardwareOperations: super::Operations {
> +    /// Activate the hardware trigger.
> +    fn activate(this: &mut Self::This) -> Result;
> +    /// Deactivate the hardware trigger.
> +    fn deactivate(_this: &mut Self::This) {
> +        crate::build_error(crate::error::VTABLE_DEFAULT_ERROR)
> +    }
> +}

This looks as if you are doing a Rust binding for struct led_trigger.
But you keep calling it Hardware trigger, which makes me thing that
you are confused about what is a LED trigger and what is a hardware
trigger.

Why do you keep putting "Hardware" into the names of these symbols?

I fear that you may be confused about some stuff here. In order to
determine whether this is the case, could you answer the following
questions please?
- What is the purpose of `struct led_hw_trigger_type`?
- What is the purpose of the `dummy` member of this struct? What
  value should be assigned to it?
- If a LED class device (LED cdev) has the `trigger_type` member set
  to NULL, which LED triggers will be listed in the sysfs `trigger`
  file for this LED cdev? And which triggers will be listed if the
  `trigger_type` member is not NULL?
- Why does both `struct led_classdev` and `struct led_trigger` have
  the `trigger_type` member?

> +/// `trigger_activate` function pointer
> +///
> +/// # Safety
> +///
> +/// `led_cdev` must be passed by the corresponding callback in `led_trigger`.
> +unsafe extern "C" fn trigger_activate<T>(led_cdev: *mut bindings::led_classdev) -> i32
> +where
> +    T: HardwareOperations,
> +{
> +    from_result(|| {
> +        // SAFETY: By the safety requirement of this function `led_cdev` is embedded inside a `T::This<T>`.
> +        let led = unsafe { &mut *(T::This::led_container_of(led_cdev.cast())) };
> +        T::activate(led)?;
> +        Ok(0)
> +    })
> +}
> +
> +/// `trigger_deactivate` function pointer
> +///
> +/// # Safety
> +///
> +/// `led_cdev` must be passed by the corresponding callback in `led_trigger`.
> +unsafe extern "C" fn trigger_deactivate<T>(led_cdev: *mut bindings::led_classdev)
> +where
> +    T: HardwareOperations,
> +{
> +    // SAFETY: By the safety requirement of this function `led_cdev` is embedded inside a `T::This<T>`.
> +    let led = unsafe { &mut *(T::This::led_container_of(led_cdev.cast())) };
> +    T::deactivate(led)
> +}
> -- 
> 2.47.0
> 
> 




[Index of Archives]     [Linux ARM Kernel]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [IETF Annouce]     [Security]     [Bugtraq]     [Linux OMAP]     [Linux MIPS]     [ECOS]     [Asterisk Internet PBX]     [Linux API]

  Powered by Linux