741 lines · 27.4 KB · Rust
RawDownload
// SPDX-License-Identifier: GPL-2.0// Copyright (c) 2025 Samsung Electronics Co., Ltd.// Author: Michal Wilczynski <m.wilczynski@samsung.com> //! PWM subsystem abstractions.//!//! C header: [`include/linux/pwm.h`](srctree/include/linux/pwm.h). use crate::{    bindings,    container_of,    device::{self, Bound},    devres,    error::{self, to_result},    prelude::*,    sync::aref::{ARef, AlwaysRefCounted},    types::Opaque, //};use core::{    marker::PhantomData,    ops::Deref,    ptr::NonNull, //}; /// Represents a PWM waveform configuration./// Mirrors struct [`struct pwm_waveform`](srctree/include/linux/pwm.h).#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]pub struct Waveform {    /// Total duration of one complete PWM cycle, in nanoseconds.    pub period_length_ns: u64,     /// Duty-cycle active time, in nanoseconds.    ///    /// For a typical normal polarity configuration (active-high) this is the    /// high time of the signal.    pub duty_length_ns: u64,     /// Duty-cycle start offset, in nanoseconds.    ///    /// Delay from the beginning of the period to the first active edge.    /// In most simple PWM setups this is `0`, so the duty cycle starts    /// immediately at each period’s start.    pub duty_offset_ns: u64,} impl From<bindings::pwm_waveform> for Waveform {    fn from(wf: bindings::pwm_waveform) -> Self {        Waveform {            period_length_ns: wf.period_length_ns,            duty_length_ns: wf.duty_length_ns,            duty_offset_ns: wf.duty_offset_ns,        }    }} impl From<Waveform> for bindings::pwm_waveform {    fn from(wf: Waveform) -> Self {        bindings::pwm_waveform {            period_length_ns: wf.period_length_ns,            duty_length_ns: wf.duty_length_ns,            duty_offset_ns: wf.duty_offset_ns,        }    }} /// Describes the outcome of a `round_waveform` operation.#[derive(Debug, Clone, Copy, PartialEq, Eq)]pub enum RoundingOutcome {    /// The requested waveform was achievable exactly or by rounding values down.    ExactOrRoundedDown,     /// The requested waveform could only be achieved by rounding up.    RoundedUp,} /// Wrapper for a PWM device [`struct pwm_device`](srctree/include/linux/pwm.h).#[repr(transparent)]pub struct Device(Opaque<bindings::pwm_device>); impl Device {    /// Creates a reference to a [`Device`] from a valid C pointer.    ///    /// # Safety    ///    /// The caller must ensure that `ptr` is valid and remains valid for the lifetime of the    /// returned [`Device`] reference.    pub(crate) unsafe fn from_raw<'a>(ptr: *mut bindings::pwm_device) -> &'a Self {        // SAFETY: The safety requirements guarantee the validity of the dereference, while the        // `Device` type being transparent makes the cast ok.        unsafe { &*ptr.cast::<Self>() }    }     /// Returns a raw pointer to the underlying `pwm_device`.    fn as_raw(&self) -> *mut bindings::pwm_device {        self.0.get()    }     /// Gets the hardware PWM index for this device within its chip.    pub fn hwpwm(&self) -> u32 {        // SAFETY: `self.as_raw()` provides a valid pointer for `self`'s lifetime.        unsafe { (*self.as_raw()).hwpwm }    }     /// Gets a reference to the parent `Chip` that this device belongs to.    pub fn chip<T: PwmOps>(&self) -> &Chip<T> {        // SAFETY: `self.as_raw()` provides a valid pointer. (*self.as_raw()).chip        // is assumed to be a valid pointer to `pwm_chip` managed by the kernel.        // Chip::from_raw's safety conditions must be met.        unsafe { Chip::<T>::from_raw((*self.as_raw()).chip) }    }     /// Gets the label for this PWM device, if any.    pub fn label(&self) -> Option<&CStr> {        // SAFETY: self.as_raw() provides a valid pointer.        let label_ptr = unsafe { (*self.as_raw()).label };        if label_ptr.is_null() {            return None;        }         // SAFETY: label_ptr is non-null and points to a C string        // managed by the kernel, valid for the lifetime of the PWM device.        Some(unsafe { CStr::from_char_ptr(label_ptr) })    }     /// Sets the PWM waveform configuration and enables the PWM signal.    pub fn set_waveform(&self, wf: &Waveform, exact: bool) -> Result {        let c_wf = bindings::pwm_waveform::from(*wf);         // SAFETY: `self.as_raw()` provides a valid `*mut pwm_device` pointer.        // `&c_wf` is a valid pointer to a `pwm_waveform` struct. The C function        // handles all necessary internal locking.        to_result(unsafe { bindings::pwm_set_waveform_might_sleep(self.as_raw(), &c_wf, exact) })    }     /// Queries the hardware for the configuration it would apply for a given    /// request.    pub fn round_waveform(&self, wf: &mut Waveform) -> Result<RoundingOutcome> {        let mut c_wf = bindings::pwm_waveform::from(*wf);         // SAFETY: `self.as_raw()` provides a valid `*mut pwm_device` pointer.        // `&mut c_wf` is a valid pointer to a mutable `pwm_waveform` struct that        // the C function will update.        let ret = unsafe { bindings::pwm_round_waveform_might_sleep(self.as_raw(), &mut c_wf) };         to_result(ret)?;         *wf = Waveform::from(c_wf);         if ret == 1 {            Ok(RoundingOutcome::RoundedUp)        } else {            Ok(RoundingOutcome::ExactOrRoundedDown)        }    }     /// Reads the current waveform configuration directly from the hardware.    pub fn get_waveform(&self) -> Result<Waveform> {        let mut c_wf = bindings::pwm_waveform::default();         // SAFETY: `self.as_raw()` is a valid pointer. We provide a valid pointer        // to a stack-allocated `pwm_waveform` struct for the kernel to fill.        to_result(unsafe { bindings::pwm_get_waveform_might_sleep(self.as_raw(), &mut c_wf) })?;         Ok(Waveform::from(c_wf))    }} /// The result of a `round_waveform_tohw` operation.#[derive(Debug, Clone, Copy, PartialEq, Eq)]pub struct RoundedWaveform<WfHw> {    /// A status code, 0 for success or 1 if values were rounded up.    pub status: c_int,    /// The driver-specific hardware representation of the waveform.    pub hardware_waveform: WfHw,} /// Trait defining the operations for a PWM driver.pub trait PwmOps: 'static + Send + Sync + Sized {    /// The driver-specific hardware representation of a waveform.    ///    /// This type must be [`Copy`], [`Default`], and fit within `PWM_WFHWSIZE`.    type WfHw: Copy + Default;     /// Optional hook for when a PWM device is requested.    fn request(_chip: &Chip<Self>, _pwm: &Device, _parent_dev: &device::Device<Bound>) -> Result {        Ok(())    }     /// Optional hook for capturing a PWM signal.    fn capture(        _chip: &Chip<Self>,        _pwm: &Device,        _result: &mut bindings::pwm_capture,        _timeout: usize,        _parent_dev: &device::Device<Bound>,    ) -> Result {        Err(ENOTSUPP)    }     /// Convert a generic waveform to the hardware-specific representation.    /// This is typically a pure calculation and does not perform I/O.    fn round_waveform_tohw(        _chip: &Chip<Self>,        _pwm: &Device,        _wf: &Waveform,    ) -> Result<RoundedWaveform<Self::WfHw>> {        Err(ENOTSUPP)    }     /// Convert a hardware-specific representation back to a generic waveform.    /// This is typically a pure calculation and does not perform I/O.    fn round_waveform_fromhw(        _chip: &Chip<Self>,        _pwm: &Device,        _wfhw: &Self::WfHw,        _wf: &mut Waveform,    ) -> Result {        Err(ENOTSUPP)    }     /// Read the current hardware configuration into the hardware-specific representation.    fn read_waveform(        _chip: &Chip<Self>,        _pwm: &Device,        _parent_dev: &device::Device<Bound>,    ) -> Result<Self::WfHw> {        Err(ENOTSUPP)    }     /// Write a hardware-specific waveform configuration to the hardware.    fn write_waveform(        _chip: &Chip<Self>,        _pwm: &Device,        _wfhw: &Self::WfHw,        _parent_dev: &device::Device<Bound>,    ) -> Result {        Err(ENOTSUPP)    }} /// Bridges Rust `PwmOps` to the C `pwm_ops` vtable.struct Adapter<T: PwmOps> {    _p: PhantomData<T>,} impl<T: PwmOps> Adapter<T> {    const VTABLE: PwmOpsVTable = create_pwm_ops::<T>();     /// # Safety    ///    /// `wfhw_ptr` must be valid for writes of `size_of::<T::WfHw>()` bytes.    unsafe fn serialize_wfhw(wfhw: &T::WfHw, wfhw_ptr: *mut c_void) -> Result {        let size = core::mem::size_of::<T::WfHw>();         build_assert!(size <= bindings::PWM_WFHWSIZE as usize);         // SAFETY: The caller ensures `wfhw_ptr` is valid for `size` bytes.        unsafe {            core::ptr::copy_nonoverlapping(                core::ptr::from_ref::<T::WfHw>(wfhw).cast::<u8>(),                wfhw_ptr.cast::<u8>(),                size,            )        };         Ok(())    }     /// # Safety    ///    /// `wfhw_ptr` must be valid for reads of `size_of::<T::WfHw>()` bytes.    unsafe fn deserialize_wfhw(wfhw_ptr: *const c_void) -> Result<T::WfHw> {        let size = core::mem::size_of::<T::WfHw>();         build_assert!(size <= bindings::PWM_WFHWSIZE as usize);         let mut wfhw = T::WfHw::default();        // SAFETY: The caller ensures `wfhw_ptr` is valid for `size` bytes.        unsafe {            core::ptr::copy_nonoverlapping(                wfhw_ptr.cast::<u8>(),                core::ptr::from_mut::<T::WfHw>(&mut wfhw).cast::<u8>(),                size,            )        };         Ok(wfhw)    }     /// # Safety    ///    /// `dev` must be a valid pointer to a `bindings::device` embedded within a    /// `bindings::pwm_chip`. This function is called by the device core when the    /// last reference to the device is dropped.    unsafe extern "C" fn release_callback(dev: *mut bindings::device) {        // SAFETY: The function's contract guarantees that `dev` points to a `device`        // field embedded within a valid `pwm_chip`. `container_of!` can therefore        // safely calculate the address of the containing struct.        let c_chip_ptr = unsafe { container_of!(dev, bindings::pwm_chip, dev) };         // SAFETY: `c_chip_ptr` is a valid pointer to a `pwm_chip` as established        // above. Calling this FFI function is safe.        let drvdata_ptr = unsafe { bindings::pwmchip_get_drvdata(c_chip_ptr) };         // SAFETY: The driver data was initialized in `new`. We run its destructor here.        unsafe { core::ptr::drop_in_place(drvdata_ptr.cast::<T>()) };         // Now, call the original release function to free the `pwm_chip` itself.        // SAFETY: `dev` is the valid pointer passed into this callback, which is        // the expected argument for `pwmchip_release`.        unsafe { bindings::pwmchip_release(dev) };    }     /// # Safety    ///    /// Pointers from C must be valid.    unsafe extern "C" fn request_callback(        chip_ptr: *mut bindings::pwm_chip,        pwm_ptr: *mut bindings::pwm_device,    ) -> c_int {        // SAFETY: PWM core guarentees `chip_ptr` and `pwm_ptr` are valid pointers.        let (chip, pwm) = unsafe { (Chip::<T>::from_raw(chip_ptr), Device::from_raw(pwm_ptr)) };         // SAFETY: The PWM core guarantees the parent device exists and is bound during callbacks.        let bound_parent = unsafe { chip.bound_parent_device() };        match T::request(chip, pwm, bound_parent) {            Ok(()) => 0,            Err(e) => e.to_errno(),        }    }     /// # Safety    ///    /// Pointers from C must be valid.    unsafe extern "C" fn capture_callback(        chip_ptr: *mut bindings::pwm_chip,        pwm_ptr: *mut bindings::pwm_device,        res: *mut bindings::pwm_capture,        timeout: usize,    ) -> c_int {        // SAFETY: Relies on the function's contract that `chip_ptr` and `pwm_ptr` are valid        // pointers.        let (chip, pwm, result) = unsafe {            (                Chip::<T>::from_raw(chip_ptr),                Device::from_raw(pwm_ptr),                &mut *res,            )        };         // SAFETY: The PWM core guarantees the parent device exists and is bound during callbacks.        let bound_parent = unsafe { chip.bound_parent_device() };        match T::capture(chip, pwm, result, timeout, bound_parent) {            Ok(()) => 0,            Err(e) => e.to_errno(),        }    }     /// # Safety    ///    /// Pointers from C must be valid.    unsafe extern "C" fn round_waveform_tohw_callback(        chip_ptr: *mut bindings::pwm_chip,        pwm_ptr: *mut bindings::pwm_device,        wf_ptr: *const bindings::pwm_waveform,        wfhw_ptr: *mut c_void,    ) -> c_int {        // SAFETY: Relies on the function's contract that `chip_ptr` and `pwm_ptr` are valid        // pointers.        let (chip, pwm, wf) = unsafe {            (                Chip::<T>::from_raw(chip_ptr),                Device::from_raw(pwm_ptr),                Waveform::from(*wf_ptr),            )        };        match T::round_waveform_tohw(chip, pwm, &wf) {            Ok(rounded) => {                // SAFETY: `wfhw_ptr` is valid per this function's safety contract.                if unsafe { Self::serialize_wfhw(&rounded.hardware_waveform, wfhw_ptr) }.is_err() {                    return EINVAL.to_errno();                }                rounded.status            }            Err(e) => e.to_errno(),        }    }     /// # Safety    ///    /// Pointers from C must be valid.    unsafe extern "C" fn round_waveform_fromhw_callback(        chip_ptr: *mut bindings::pwm_chip,        pwm_ptr: *mut bindings::pwm_device,        wfhw_ptr: *const c_void,        wf_ptr: *mut bindings::pwm_waveform,    ) -> c_int {        // SAFETY: Relies on the function's contract that `chip_ptr` and `pwm_ptr` are valid        // pointers.        let (chip, pwm) = unsafe { (Chip::<T>::from_raw(chip_ptr), Device::from_raw(pwm_ptr)) };        // SAFETY: `deserialize_wfhw`'s safety contract is met by this function's contract.        let wfhw = match unsafe { Self::deserialize_wfhw(wfhw_ptr) } {            Ok(v) => v,            Err(e) => return e.to_errno(),        };         let mut rust_wf = Waveform::default();        match T::round_waveform_fromhw(chip, pwm, &wfhw, &mut rust_wf) {            Ok(()) => {                // SAFETY: `wf_ptr` is guaranteed valid by the C caller.                unsafe { *wf_ptr = rust_wf.into() };                0            }            Err(e) => e.to_errno(),        }    }     /// # Safety    ///    /// Pointers from C must be valid.    unsafe extern "C" fn read_waveform_callback(        chip_ptr: *mut bindings::pwm_chip,        pwm_ptr: *mut bindings::pwm_device,        wfhw_ptr: *mut c_void,    ) -> c_int {        // SAFETY: Relies on the function's contract that `chip_ptr` and `pwm_ptr` are valid        // pointers.        let (chip, pwm) = unsafe { (Chip::<T>::from_raw(chip_ptr), Device::from_raw(pwm_ptr)) };         // SAFETY: The PWM core guarantees the parent device exists and is bound during callbacks.        let bound_parent = unsafe { chip.bound_parent_device() };        match T::read_waveform(chip, pwm, bound_parent) {            // SAFETY: `wfhw_ptr` is valid per this function's safety contract.            Ok(wfhw) => match unsafe { Self::serialize_wfhw(&wfhw, wfhw_ptr) } {                Ok(()) => 0,                Err(e) => e.to_errno(),            },            Err(e) => e.to_errno(),        }    }     /// # Safety    ///    /// Pointers from C must be valid.    unsafe extern "C" fn write_waveform_callback(        chip_ptr: *mut bindings::pwm_chip,        pwm_ptr: *mut bindings::pwm_device,        wfhw_ptr: *const c_void,    ) -> c_int {        // SAFETY: Relies on the function's contract that `chip_ptr` and `pwm_ptr` are valid        // pointers.        let (chip, pwm) = unsafe { (Chip::<T>::from_raw(chip_ptr), Device::from_raw(pwm_ptr)) };         // SAFETY: The PWM core guarantees the parent device exists and is bound during callbacks.        let bound_parent = unsafe { chip.bound_parent_device() };         // SAFETY: `wfhw_ptr` is valid per this function's safety contract.        let wfhw = match unsafe { Self::deserialize_wfhw(wfhw_ptr) } {            Ok(v) => v,            Err(e) => return e.to_errno(),        };        match T::write_waveform(chip, pwm, &wfhw, bound_parent) {            Ok(()) => 0,            Err(e) => e.to_errno(),        }    }} /// VTable structure wrapper for PWM operations./// Mirrors [`struct pwm_ops`](srctree/include/linux/pwm.h).#[repr(transparent)]pub struct PwmOpsVTable(bindings::pwm_ops); // SAFETY: PwmOpsVTable is Send. The vtable contains only function pointers// and a size, which are simple data types that can be safely moved across// threads. The thread-safety of calling these functions is handled by the// kernel's locking mechanisms.unsafe impl Send for PwmOpsVTable {} // SAFETY: PwmOpsVTable is Sync. The vtable is immutable after it is created,// so it can be safely referenced and accessed concurrently by multiple threads// e.g. to read the function pointers.unsafe impl Sync for PwmOpsVTable {} impl PwmOpsVTable {    /// Returns a raw pointer to the underlying `pwm_ops` struct.    pub(crate) fn as_raw(&self) -> *const bindings::pwm_ops {        &self.0    }} /// Creates a PWM operations vtable for a type `T` that implements `PwmOps`.////// This is used to bridge Rust trait implementations to the C `struct pwm_ops`/// expected by the kernel.pub const fn create_pwm_ops<T: PwmOps>() -> PwmOpsVTable {    // SAFETY: `core::mem::zeroed()` is unsafe. For `pwm_ops`, all fields are    // `Option<extern "C" fn(...)>` or data, so a zeroed pattern (None/0) is valid initially.    let mut ops: bindings::pwm_ops = unsafe { core::mem::zeroed() }; 

Lines 1–500 of 741.