From: Danilo Krummrich <dakr@kernel.org>
To: Michal Wilczynski <m.wilczynski@samsung.com>
Cc: "Uwe Kleine-König" <ukleinek@kernel.org>,
"Miguel Ojeda" <ojeda@kernel.org>,
"Alex Gaynor" <alex.gaynor@gmail.com>,
"Boqun Feng" <boqun.feng@gmail.com>,
"Gary Guo" <gary@garyguo.net>,
"Björn Roy Baron" <bjorn3_gh@protonmail.com>,
"Andreas Hindborg" <a.hindborg@kernel.org>,
"Alice Ryhl" <aliceryhl@google.com>,
"Trevor Gross" <tmgross@umich.edu>, "Guo Ren" <guoren@kernel.org>,
"Fu Wei" <wefu@redhat.com>, "Rob Herring" <robh@kernel.org>,
"Krzysztof Kozlowski" <krzk+dt@kernel.org>,
"Conor Dooley" <conor+dt@kernel.org>,
"Paul Walmsley" <paul.walmsley@sifive.com>,
"Palmer Dabbelt" <palmer@dabbelt.com>,
"Albert Ou" <aou@eecs.berkeley.edu>,
"Alexandre Ghiti" <alex@ghiti.fr>,
"Marek Szyprowski" <m.szyprowski@samsung.com>,
"Benno Lossin" <lossin@kernel.org>,
"Michael Turquette" <mturquette@baylibre.com>,
"Drew Fustini" <fustini@kernel.org>,
linux-kernel@vger.kernel.org, linux-pwm@vger.kernel.org,
rust-for-linux@vger.kernel.org, linux-riscv@lists.infradead.org,
devicetree@vger.kernel.org
Subject: Re: [PATCH v7 3/8] rust: pwm: Add core 'Device' and 'Chip' object wrappers
Date: Wed, 2 Jul 2025 17:13:34 +0200 [thread overview]
Message-ID: <aGVMnmoepIVSS0yK@pollux> (raw)
In-Reply-To: <20250702-rust-next-pwm-working-fan-for-sending-v7-3-67ef39ff1d29@samsung.com>
On Wed, Jul 02, 2025 at 03:45:31PM +0200, Michal Wilczynski wrote:
> Building on the basic data types, this commit introduces the central
> object abstractions for the PWM subsystem: Device and Chip. It also
> includes the core trait implementations that make the Chip wrapper a
> complete, safe, and managed object.
>
> The main components of this change are:
> - Device and Chip Structs: These structs wrap the underlying struct
> pwm_device and struct pwm_chip C objects, providing safe, idiomatic
> methods to access their fields.
>
> - High-Level `Device` API: Exposes safe wrappers for the modern
> `waveform` API, allowing consumers to apply, read, and pre-validate
> hardware configurations.
>
> - Core Trait Implementations for Chip:
> - AlwaysRefCounted: Links the Chip's lifetime to its embedded
> struct device reference counter. This enables automatic lifetime
> management via ARef.
> - Send and Sync: Marks the Chip wrapper as safe for use across
> threads. This is sound because the C core handles all necessary
> locking for the underlying object's state.
>
> These wrappers and traits form a robust foundation for building PWM
> drivers in Rust.
>
> Signed-off-by: Michal Wilczynski <m.wilczynski@samsung.com>
Few more comments below, with those fixed:
Reviewed-by: Danilo Krummrich <dakr@kernel.org>
> +/// Wrapper for a PWM device [`struct pwm_device`](srctree/include/linux/pwm.h).
> +#[repr(transparent)]
> +pub struct Device(Opaque<bindings::pwm_device>);
> +
> +impl Device {
<snip>
> + /// Gets a reference to the parent `Chip` that this device belongs to.
> + pub fn chip(&self) -> &Chip {
> + // 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::as_ref's safety conditions must be met.
> + unsafe { Chip::as_ref((*self.as_raw()).chip) }
I assume the C API does guarantee that a struct pwm_device *always* holds a
valid pointer to a struct pwm_chip?
> +
> +/// Wrapper for a PWM chip/controller ([`struct pwm_chip`](srctree/include/linux/pwm.h)).
> +#[repr(transparent)]
> +pub struct Chip(Opaque<bindings::pwm_chip>);
> +
> +impl Chip {
> + /// Creates a reference to a [`Chip`] from a valid pointer.
> + ///
> + /// # Safety
> + ///
> + /// The caller must ensure that `ptr` is valid and remains valid for the lifetime of the
> + /// returned [`Chip`] reference.
> + pub(crate) unsafe fn as_ref<'a>(ptr: *mut bindings::pwm_chip) -> &'a Self {
> + // SAFETY: The safety requirements guarantee the validity of the dereference, while the
> + // `Chip` type being transparent makes the cast ok.
> + unsafe { &*ptr.cast::<Self>() }
> + }
> +
> + /// Returns a raw pointer to the underlying `pwm_chip`.
> + pub(crate) fn as_raw(&self) -> *mut bindings::pwm_chip {
> + self.0.get()
> + }
> +
> + /// Gets the number of PWM channels (hardware PWMs) on this chip.
> + pub fn npwm(&self) -> u32 {
> + // SAFETY: `self.as_raw()` provides a valid pointer for `self`'s lifetime.
> + unsafe { (*self.as_raw()).npwm }
> + }
> +
> + /// Returns `true` if the chip supports atomic operations for configuration.
> + pub fn is_atomic(&self) -> bool {
> + // SAFETY: `self.as_raw()` provides a valid pointer for `self`'s lifetime.
> + unsafe { (*self.as_raw()).atomic }
> + }
> +
> + /// Returns a reference to the embedded `struct device` abstraction.
> + pub fn device(&self) -> &device::Device {
> + // SAFETY: `self.as_raw()` provides a valid pointer to `bindings::pwm_chip`.
> + // The `dev` field is an instance of `bindings::device` embedded within `pwm_chip`.
> + // Taking a pointer to this embedded field is valid.
> + // `device::Device` is `#[repr(transparent)]`.
> + // The lifetime of the returned reference is tied to `self`.
> + let dev_field_ptr = unsafe { core::ptr::addr_of!((*self.as_raw()).dev) };
I think you can use `&raw` instead.
> + // SAFETY: `dev_field_ptr` is a valid pointer to `bindings::device`.
> + // Casting and dereferencing is safe due to `repr(transparent)` and lifetime.
> + unsafe { &*(dev_field_ptr.cast::<device::Device>()) }
Please use Device::as_ref() instead.
> + }
> +
> + /// Gets the *typed* driver-specific data associated with this chip's embedded device.
> + pub fn drvdata<T: 'static>(&self) -> &T {
You need to make the whole Chip structure generic over T, i.e.
Chip<T: ForeignOwnable>.
Otherwise the API is unsafe, since the caller can pass in any T when calling
`chip.drvdata()` regardless of whether you actually stored as private data
through Chip::new().
Also, given that `T: ForeignOwnable`, you should return `T::Borrowed`.
> + // SAFETY: `self.as_raw()` gives a valid pwm_chip pointer.
> + // `bindings::pwmchip_get_drvdata` is the C function to retrieve driver data.
> + let ptr = unsafe { bindings::pwmchip_get_drvdata(self.as_raw()) };
> +
> + // SAFETY: The only way to create a chip is through Chip::new, which initializes
> + // this pointer.
> + unsafe { &*ptr.cast::<T>() }
> + }
> +
> + /// Allocates and wraps a PWM chip using `bindings::pwmchip_alloc`.
> + ///
> + /// Returns an [`ARef<Chip>`] managing the chip's lifetime via refcounting
> + /// on its embedded `struct device`.
> + pub fn new<T: 'static + ForeignOwnable>(
> + parent_dev: &device::Device,
> + npwm: u32,
> + sizeof_priv: usize,
> + drvdata: T,
As mentioned above, the whole Chip structure needs to be generic over T,
otherwise you can't guarantee that this T is the same T as the one in drvdata().
> +// SAFETY: Implements refcounting for `Chip` using the embedded `struct device`.
> +unsafe impl AlwaysRefCounted for Chip {
> + #[inline]
> + fn inc_ref(&self) {
> + // SAFETY: `self.0.get()` points to a valid `pwm_chip` because `self` exists.
> + // The embedded `dev` is valid. `get_device` increments its refcount.
> + unsafe {
> + bindings::get_device(core::ptr::addr_of_mut!((*self.0.get()).dev));
I think you can use `&raw mut` instead.
Also, if you move the semicolon at the end of the unsafe block, this goes in one
line.
> + }
> + }
> +
> + #[inline]
> + unsafe fn dec_ref(obj: NonNull<Chip>) {
> + let c_chip_ptr = obj.cast::<bindings::pwm_chip>().as_ptr();
> +
> + // SAFETY: `obj` is a valid pointer to a `Chip` (and thus `bindings::pwm_chip`)
> + // with a non-zero refcount. `put_device` handles decrement and final release.
> + unsafe {
> + bindings::put_device(core::ptr::addr_of_mut!((*c_chip_ptr).dev));
> + }
Same here.
> + }
> +}
next prev parent reply other threads:[~2025-07-02 15:13 UTC|newest]
Thread overview: 17+ messages / expand[flat|nested] mbox.gz Atom feed top
[not found] <CGME20250702134953eucas1p2271cd783b615897d24e8432ece4f91cd@eucas1p2.samsung.com>
2025-07-02 13:45 ` [PATCH v7 0/8] Rust Abstractions for PWM subsystem with TH1520 PWM driver Michal Wilczynski
[not found] ` <CGME20250702134955eucas1p2ca553f63f44c63a56ba0b2c605edd097@eucas1p2.samsung.com>
2025-07-02 13:45 ` [PATCH v7 1/8] pwm: Expose PWM_WFHWSIZE in public header Michal Wilczynski
2025-07-03 6:39 ` Uwe Kleine-König
[not found] ` <CGME20250702134956eucas1p16cacd6588b9f7f9677fba8aa8345524b@eucas1p1.samsung.com>
2025-07-02 13:45 ` [PATCH v7 2/8] rust: pwm: Add Kconfig and basic data structures Michal Wilczynski
[not found] ` <CGME20250702134957eucas1p1d84f2ed3014cf98ea3a077c7fae6dea6@eucas1p1.samsung.com>
2025-07-02 13:45 ` [PATCH v7 3/8] rust: pwm: Add core 'Device' and 'Chip' object wrappers Michal Wilczynski
2025-07-02 15:13 ` Danilo Krummrich [this message]
2025-07-03 6:42 ` Uwe Kleine-König
2025-07-03 11:37 ` Michal Wilczynski
2025-07-03 21:42 ` Danilo Krummrich
[not found] ` <CGME20250702134958eucas1p26baf0f661006f5b79c31b2afa683baee@eucas1p2.samsung.com>
2025-07-02 13:45 ` [PATCH v7 4/8] rust: pwm: Add driver operations trait and registration support Michal Wilczynski
2025-07-02 15:21 ` Danilo Krummrich
[not found] ` <CGME20250702135000eucas1p12d5bc0076b71eb73e5f7de6119903c64@eucas1p1.samsung.com>
2025-07-02 13:45 ` [PATCH v7 5/8] pwm: Add Rust driver for T-HEAD TH1520 SoC Michal Wilczynski
[not found] ` <CGME20250702135001eucas1p299eaebac8b9af1efa56184dcdfcfde37@eucas1p2.samsung.com>
2025-07-02 13:45 ` [PATCH v7 6/8] dt-bindings: pwm: thead: Add T-HEAD TH1520 PWM controller Michal Wilczynski
[not found] ` <CGME20250702135002eucas1p29d0a0393fec6158a3ca158ea09b61cf1@eucas1p2.samsung.com>
2025-07-02 13:45 ` [PATCH v7 7/8] riscv: dts: thead: Add PWM controller node Michal Wilczynski
2025-07-03 21:14 ` Drew Fustini
[not found] ` <CGME20250702135003eucas1p114a5ce5dea469242940b7e2e44a7ad59@eucas1p1.samsung.com>
2025-07-02 13:45 ` [PATCH v7 8/8] riscv: dts: thead: Add PWM fan and thermal control Michal Wilczynski
2025-07-03 21:12 ` Drew Fustini
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=aGVMnmoepIVSS0yK@pollux \
--to=dakr@kernel.org \
--cc=a.hindborg@kernel.org \
--cc=alex.gaynor@gmail.com \
--cc=alex@ghiti.fr \
--cc=aliceryhl@google.com \
--cc=aou@eecs.berkeley.edu \
--cc=bjorn3_gh@protonmail.com \
--cc=boqun.feng@gmail.com \
--cc=conor+dt@kernel.org \
--cc=devicetree@vger.kernel.org \
--cc=fustini@kernel.org \
--cc=gary@garyguo.net \
--cc=guoren@kernel.org \
--cc=krzk+dt@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-pwm@vger.kernel.org \
--cc=linux-riscv@lists.infradead.org \
--cc=lossin@kernel.org \
--cc=m.szyprowski@samsung.com \
--cc=m.wilczynski@samsung.com \
--cc=mturquette@baylibre.com \
--cc=ojeda@kernel.org \
--cc=palmer@dabbelt.com \
--cc=paul.walmsley@sifive.com \
--cc=robh@kernel.org \
--cc=rust-for-linux@vger.kernel.org \
--cc=tmgross@umich.edu \
--cc=ukleinek@kernel.org \
--cc=wefu@redhat.com \
/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;
as well as URLs for NNTP newsgroup(s).