From nobody Fri Dec 19 06:31:07 2025 Received: from mout-p-103.mailbox.org (mout-p-103.mailbox.org [80.241.56.161]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A6B5E25E804; Mon, 14 Apr 2025 15:33:24 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=80.241.56.161 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744644806; cv=none; b=b5WGKOI0uiufU8jOEJ1/SOyOLEEjxpyiJfM0fzj7Tt4+3vyt8QOD/iKIiqFGkcuxLUVsdChZymXNUD1FmJ3/LhTyFNaFhn6SrF3IGQH5YLR034JUDhPh5DSJVkyr0O2gPZ1YVrIapfh57fIFkZOUZSBJoixIdhpwx9hBrt1qwgQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744644806; c=relaxed/simple; bh=/5e3b4eP8pS4/WvG/ZgIGg0AVs+8eFE99KZLo+hs5uQ=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=V47KCh1TZCYg2A6MZSbqbY92y1RJgHdR7k/0DYs221RH9q4jk9pgLKoZbs6zYYgbGPJdlzupPCR9v1Q1AF5311eKzStjva4eqspKGgW/M8liWPhwKIcbP4HT3jlJH0VfKetpKcNXB3MEDlPkyS1Wh/ocAz+B/cPB2+tnIgEENx0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=buenzli.dev; spf=pass smtp.mailfrom=buenzli.dev; arc=none smtp.client-ip=80.241.56.161 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=buenzli.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=buenzli.dev Received: from smtp1.mailbox.org (smtp1.mailbox.org [IPv6:2001:67c:2050:b231:465::1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by mout-p-103.mailbox.org (Postfix) with ESMTPS id 4Zbrjz1Bc2z9t9v; Mon, 14 Apr 2025 17:26:47 +0200 (CEST) From: Remo Senekowitsch To: Rob Herring , Saravana Kannan , Miguel Ojeda , Alex Gaynor , Boqun Feng , Gary Guo , =?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , Greg Kroah-Hartman , "Rafael J. Wysocki" , Remo Senekowitsch Cc: linux-kernel@vger.kernel.org, devicetree@vger.kernel.org, rust-for-linux@vger.kernel.org Subject: [PATCH v2 1/5] rust: Move property_present to separate file Date: Mon, 14 Apr 2025 17:26:26 +0200 Message-ID: <20250414152630.1691179-2-remo@buenzli.dev> In-Reply-To: <20250414152630.1691179-1-remo@buenzli.dev> References: <20250326171411.590681-1-remo@buenzli.dev> <20250414152630.1691179-1-remo@buenzli.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Rspamd-Queue-Id: 4Zbrjz1Bc2z9t9v Content-Type: text/plain; charset="utf-8" Not all property-related APIs can be exposed directly on a device. For example, iterating over child nodes of a device will yield fwnode_handle. Thus, in order to access properties on these child nodes, duplicate the APIs on a fwnode as they are in C. Signed-off-by: Remo Senekowitsch --- MAINTAINERS | 1 + rust/helpers/helpers.c | 1 + rust/helpers/property.c | 8 +++++ rust/kernel/device.rs | 7 ---- rust/kernel/lib.rs | 1 + rust/kernel/property.rs | 75 +++++++++++++++++++++++++++++++++++++++++ 6 files changed, 86 insertions(+), 7 deletions(-) create mode 100644 rust/helpers/property.c create mode 100644 rust/kernel/property.rs diff --git a/MAINTAINERS b/MAINTAINERS index c8d9e8187..a34471662 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7117,6 +7117,7 @@ F: rust/kernel/device_id.rs F: rust/kernel/devres.rs F: rust/kernel/driver.rs F: rust/kernel/platform.rs +F: rust/kernel/property.rs F: samples/rust/rust_driver_platform.rs =20 DRIVERS FOR OMAP ADAPTIVE VOLTAGE SCALING (AVS) diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c index 0640b7e11..b4eec5bf2 100644 --- a/rust/helpers/helpers.c +++ b/rust/helpers/helpers.c @@ -23,6 +23,7 @@ #include "platform.c" #include "pci.c" #include "pid_namespace.c" +#include "property.c" #include "rbtree.c" #include "rcu.c" #include "refcount.c" diff --git a/rust/helpers/property.c b/rust/helpers/property.c new file mode 100644 index 000000000..08f68e2da --- /dev/null +++ b/rust/helpers/property.c @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include + +void rust_helper_fwnode_handle_put(struct fwnode_handle *fwnode) +{ + fwnode_handle_put(fwnode); +} diff --git a/rust/kernel/device.rs b/rust/kernel/device.rs index db2d9658b..d5e6a19ff 100644 --- a/rust/kernel/device.rs +++ b/rust/kernel/device.rs @@ -6,7 +6,6 @@ =20 use crate::{ bindings, - str::CStr, types::{ARef, Opaque}, }; use core::{fmt, ptr}; @@ -181,12 +180,6 @@ unsafe fn printk(&self, klevel: &[u8], msg: fmt::Argum= ents<'_>) { ) }; } - - /// Checks if property is present or not. - pub fn property_present(&self, name: &CStr) -> bool { - // SAFETY: By the invariant of `CStr`, `name` is null-terminated. - unsafe { bindings::device_property_present(self.as_raw().cast_cons= t(), name.as_char_ptr()) } - } } =20 // SAFETY: Instances of `Device` are always reference-counted. diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 496ed32b0..ca233fd20 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -67,6 +67,7 @@ pub mod platform; pub mod prelude; pub mod print; +pub mod property; pub mod rbtree; pub mod revocable; pub mod security; diff --git a/rust/kernel/property.rs b/rust/kernel/property.rs new file mode 100644 index 000000000..f6e6c980d --- /dev/null +++ b/rust/kernel/property.rs @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Unified device property interface. +//! +//! C header: [`include/linux/property.h`](srctree/include/linux/property.= h) + +use core::ptr; + +use crate::{bindings, device::Device, str::CStr, types::Opaque}; + +impl Device { + /// Obtain the fwnode corresponding to the device. + fn fwnode(&self) -> &FwNode { + // SAFETY: `self` is valid. + let fwnode_handle =3D unsafe { bindings::__dev_fwnode(self.as_raw(= )) }; + if fwnode_handle.is_null() { + panic!("fwnode_handle cannot be null"); + } + // SAFETY: `fwnode_handle` is valid. Its lifetime is tied to `&sel= f`. We + // return a reference instead of an `ARef` because `dev_fw= node()` + // doesn't increment the refcount. It is safe to cast from a + // `struct fwnode_handle*` to a `*const FwNode` because `FwNode` is + // defined as a `#[repr(transparent)]` wrapper around `fwnode_hand= le`. + unsafe { &*fwnode_handle.cast() } + } + + /// Checks if property is present or not. + pub fn property_present(&self, name: &CStr) -> bool { + self.fwnode().property_present(name) + } +} + +/// A reference-counted fwnode_handle. +/// +/// This structure represents the Rust abstraction for a +/// C `struct fwnode_handle`. This implementation abstracts the usage of an +/// already existing C `struct fwnode_handle` within Rust code that we get +/// passed from the C side. +/// +/// # Invariants +/// +/// A `FwNode` instance represents a valid `struct fwnode_handle` created = by the +/// C portion of the kernel. +/// +/// Instances of this type are always reference-counted, that is, a call to +/// `fwnode_handle_get` ensures that the allocation remains valid at least= until +/// the matching call to `fwnode_handle_put`. +#[repr(transparent)] +pub struct FwNode(Opaque); + +impl FwNode { + /// Obtain the raw `struct fwnode_handle *`. + pub(crate) fn as_raw(&self) -> *mut bindings::fwnode_handle { + self.0.get() + } + + /// Checks if property is present or not. + pub fn property_present(&self, name: &CStr) -> bool { + // SAFETY: By the invariant of `CStr`, `name` is null-terminated. + unsafe { bindings::fwnode_property_present(self.as_raw().cast_cons= t(), name.as_char_ptr()) } + } +} + +// SAFETY: Instances of `FwNode` are always reference-counted. +unsafe impl crate::types::AlwaysRefCounted for FwNode { + fn inc_ref(&self) { + // SAFETY: The existence of a shared reference guarantees that the= refcount is non-zero. + unsafe { bindings::fwnode_handle_get(self.as_raw()) }; + } + + unsafe fn dec_ref(obj: ptr::NonNull) { + // SAFETY: The safety requirements guarantee that the refcount is = non-zero. + unsafe { bindings::fwnode_handle_put(obj.cast().as_ptr()) } + } +} --=20 2.49.0 From nobody Fri Dec 19 06:31:07 2025 Received: from mout-p-202.mailbox.org (mout-p-202.mailbox.org [80.241.56.172]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 694602797A5; Mon, 14 Apr 2025 15:26:59 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=80.241.56.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744644421; cv=none; b=uBMEGxtpC79s6PRKm9cTkAiNU0UteHFzKDjBlBXZFWo39nhlGd2qcEuVtJkvgMVatIU/pz8soFyorSmhTSJTjetsxbqEqfDqXLV+8PT9uaDmPp8upiHP/uTOEXDEjq9ASbxJ8OMErffnUXbm2n+EOJWbFh9hCBcMvohT6U+Z7LA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744644421; c=relaxed/simple; bh=/OOniz5y97ufY6GpsnjnVPhCO3eR2pFGm+lUHlJq+/k=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=roIj6gxyiE7QfJVocoDR3qiwiymdMf0/wAIfbNwEYkKI3fTJwOwhodDJ+TpBgDqBoEhodCSSBLB0W+lIok47Y1CtQ01IE9LU8o3iWIjkOswgLbM6O2lHJ2qQSb0ukIA8oD6FnXNrwbbFzrAW8VhzqKYgtVU16SR00JZHbgRvUHk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=buenzli.dev; spf=pass smtp.mailfrom=buenzli.dev; arc=none smtp.client-ip=80.241.56.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=buenzli.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=buenzli.dev Received: from smtp1.mailbox.org (smtp1.mailbox.org [IPv6:2001:67c:2050:b231:465::1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by mout-p-202.mailbox.org (Postfix) with ESMTPS id 4Zbrk14hh4z9tF2; Mon, 14 Apr 2025 17:26:49 +0200 (CEST) From: Remo Senekowitsch To: Rob Herring , Saravana Kannan , Miguel Ojeda , Alex Gaynor , Boqun Feng , Gary Guo , =?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , Greg Kroah-Hartman , "Rafael J. Wysocki" , Remo Senekowitsch Cc: linux-kernel@vger.kernel.org, devicetree@vger.kernel.org, rust-for-linux@vger.kernel.org Subject: [PATCH v2 2/5] rust: Add bindings for reading device properties Date: Mon, 14 Apr 2025 17:26:27 +0200 Message-ID: <20250414152630.1691179-3-remo@buenzli.dev> In-Reply-To: <20250414152630.1691179-1-remo@buenzli.dev> References: <20250326171411.590681-1-remo@buenzli.dev> <20250414152630.1691179-1-remo@buenzli.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Rspamd-Queue-Id: 4Zbrk14hh4z9tF2 Content-Type: text/plain; charset="utf-8" The device property API is a firmware agnostic API for reading properties from firmware (DT/ACPI) devices nodes and swnodes. While the C API takes a pointer to a caller allocated variable/buffer, the rust API is designed to return a value and can be used in struct initialization. Rust generics are also utilized to support different types of properties where appropriate. The PropertyGuard is a way to force users to specify whether a property is supposed to be required or not. This allows us to move error logging of missing required properties into core, preventing a lot of boilerplate in drivers. Co-developed-by: Rob Herring (Arm) Signed-off-by: Rob Herring (Arm) Signed-off-by: Remo Senekowitsch --- rust/kernel/property.rs | 385 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 383 insertions(+), 2 deletions(-) diff --git a/rust/kernel/property.rs b/rust/kernel/property.rs index f6e6c980d..0d4ea3168 100644 --- a/rust/kernel/property.rs +++ b/rust/kernel/property.rs @@ -4,9 +4,17 @@ //! //! C header: [`include/linux/property.h`](srctree/include/linux/property.= h) =20 -use core::ptr; +use core::{mem::MaybeUninit, ptr}; =20 -use crate::{bindings, device::Device, str::CStr, types::Opaque}; +use crate::{ + alloc::KVec, + bindings, c_str, + device::Device, + error::{to_result, Result}, + prelude::*, + str::{BStr, CStr, CString}, + types::Opaque, +}; =20 impl Device { /// Obtain the fwnode corresponding to the device. @@ -28,6 +36,38 @@ fn fwnode(&self) -> &FwNode { pub fn property_present(&self, name: &CStr) -> bool { self.fwnode().property_present(name) } + + /// Returns firmware property `name` boolean value + pub fn property_read_bool(&self, name: &CStr) -> bool { + self.fwnode().property_read_bool(name) + } + + /// Returns the index of matching string `match_str` for firmware stri= ng property `name` + pub fn property_match_string(&self, name: &CStr, match_str: &CStr) -> = Result { + self.fwnode().property_match_string(name, match_str) + } + + /// Returns firmware property `name` integer array values in a KVec + pub fn property_read_array_vec<'fwnode, 'name, T: PropertyInt>( + &'fwnode self, + name: &'name CStr, + len: usize, + ) -> Result>> { + self.fwnode().property_read_array_vec(name, len) + } + + /// Returns integer array length for firmware property `name` + pub fn property_count_elem(&self, name: &CStr) -> Resu= lt { + self.fwnode().property_count_elem::(name) + } + + /// Returns firmware property `name` integer scalar value + pub fn property_read<'fwnode, 'name, T: Property>( + &'fwnode self, + name: &'name CStr, + ) -> PropertyGuard<'fwnode, 'name, T> { + self.fwnode().property_read(name) + } } =20 /// A reference-counted fwnode_handle. @@ -59,6 +99,150 @@ pub fn property_present(&self, name: &CStr) -> bool { // SAFETY: By the invariant of `CStr`, `name` is null-terminated. unsafe { bindings::fwnode_property_present(self.as_raw().cast_cons= t(), name.as_char_ptr()) } } + + /// Returns firmware property `name` boolean value + pub fn property_read_bool(&self, name: &CStr) -> bool { + // SAFETY: `name` is non-null and null-terminated. `self.as_raw()`= is valid + // because `self` is valid. + unsafe { bindings::fwnode_property_read_bool(self.as_raw(), name.a= s_char_ptr()) } + } + + /// Returns the index of matching string `match_str` for firmware stri= ng property `name` + pub fn property_match_string(&self, name: &CStr, match_str: &CStr) -> = Result { + // SAFETY: `name` and `match_str` are non-null and null-terminated= . `self.as_raw` is + // valid because `self` is valid. + let ret =3D unsafe { + bindings::fwnode_property_match_string( + self.as_raw(), + name.as_char_ptr(), + match_str.as_char_ptr(), + ) + }; + to_result(ret)?; + Ok(ret as usize) + } + + /// Returns firmware property `name` integer array values in a KVec + pub fn property_read_array_vec<'fwnode, 'name, T: PropertyInt>( + &'fwnode self, + name: &'name CStr, + len: usize, + ) -> Result>> { + let mut val: KVec =3D KVec::with_capacity(len, GFP_KERNEL)?; + + // SAFETY: `val.as_mut_ptr()` is valid because `KVec::with_capacit= y` + // didn't return an error and it has at least space for `len` numb= er + // of elements. + let err =3D unsafe { read_array_out_param::(self, name, val.as_= mut_ptr(), len) }; + let res =3D if err < 0 { + Err(Error::from_errno(err)) + } else { + // SAFETY: fwnode_property_read_int_array() writes exactly `le= n` entries on success + unsafe { val.set_len(len) } + Ok(val) + }; + Ok(PropertyGuard { + inner: res, + fwnode: self, + name, + }) + } + + /// Returns integer array length for firmware property `name` + pub fn property_count_elem(&self, name: &CStr) -> Resu= lt { + // SAFETY: `out_param` is allowed to be null because `len` is zero. + let ret =3D unsafe { read_array_out_param::(self, name, ptr::nu= ll_mut(), 0) }; + to_result(ret)?; + Ok(ret as usize) + } + + /// Returns the value of firmware property `name`. + /// + /// This method is generic over the type of value to read. Informally, + /// the types that can be read are booleans, strings, unsigned integer= s and + /// arrays of unsigned integers. + /// + /// Reading a `KVec` of integers is done with the separate + /// method [`Self::property_read_array_vec`], because it takes an + /// additional `len` argument. + /// + /// When reading a boolean, this method never fails. A missing property + /// is interpreted as `false`, whereas a present property is interpret= ed + /// as `true`. + /// + /// For more precise documentation about what types can be read, see + /// the [implementors of Property][Property#implementors] and [its + /// implementations on foreign types][Property#foreign-impls]. + /// + /// # Examples + /// + /// ``` + /// # use crate::{device::Device, types::CString}; + /// fn examples(dev: &Device) -> Result { + /// let fwnode =3D dev.fwnode(); + /// let b: bool =3D fwnode.property_read("some-bool").required()?; + /// if let Some(s) =3D fwnode.property_read::("some-str")= .optional() { + /// // ... + /// } + /// } + /// ``` + pub fn property_read<'fwnode, 'name, T: Property>( + &'fwnode self, + name: &'name CStr, + ) -> PropertyGuard<'fwnode, 'name, T> { + PropertyGuard { + inner: T::read(self, name), + fwnode: self, + name, + } + } + + /// helper used to display name or path of a fwnode + /// + /// # Safety + /// + /// Callers must provide a valid format string for a fwnode. + unsafe fn fmt(&self, f: &mut core::fmt::Formatter<'_>, fmt_str: &CStr)= -> core::fmt::Result { + let mut buf =3D [0; 256]; + // SAFETY: `buf` is valid and `buf.len()` is its length. `self.as_= raw()` is + // valid because `self` is valid. + let written =3D unsafe { + bindings::scnprintf(buf.as_mut_ptr(), buf.len(), fmt_str.as_pt= r(), self.as_raw()) + }; + // SAFETY: `written` is smaller or equal to `buf.len()`. + let b: &[u8] =3D unsafe { core::slice::from_raw_parts(buf.as_ptr()= , written as usize) }; + write!(f, "{}", BStr::from_bytes(b)) + } + + /// Returns an object that implements [`Display`](core::fmt::Display) = for + /// printing the name of a node. + pub fn display_name(&self) -> impl core::fmt::Display + use<'_> { + struct FwNodeDisplayName<'a>(&'a FwNode); + + impl core::fmt::Display for FwNodeDisplayName<'_> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::= Result { + // SAFETY: "%pfwP" is a valid format string for fwnode + unsafe { self.0.fmt(f, c_str!("%pfwP")) } + } + } + + FwNodeDisplayName(self) + } + + /// Returns an object that implements [`Display`](core::fmt::Display) = for + /// printing the full path of a node. + pub fn display_path(&self) -> impl core::fmt::Display + use<'_> { + struct FwNodeDisplayPath<'a>(&'a FwNode); + + impl core::fmt::Display for FwNodeDisplayPath<'_> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::= Result { + // SAFETY: "%pfwf" is a valid format string for fwnode + unsafe { self.0.fmt(f, c_str!("%pfwf")) } + } + } + + FwNodeDisplayPath(self) + } } =20 // SAFETY: Instances of `FwNode` are always reference-counted. @@ -73,3 +257,200 @@ unsafe fn dec_ref(obj: ptr::NonNull) { unsafe { bindings::fwnode_handle_put(obj.cast().as_ptr()) } } } + +/// Implemented for several types that can be read as properties. +/// +/// Informally, this is implemented for strings, integers and arrays of +/// integers. It's used to make [`FwNode::property_read`] generic over the +/// type of property being read. There are also two dedicated methods to r= ead +/// other types, because they require more specialized function signatures: +/// - [`property_read_bool`](Device::property_read_bool) +/// - [`property_read_array_vec`](Device::property_read_array_vec) +pub trait Property: Sized { + /// Used to make [`FwNode::property_read`] generic. + fn read(fwnode: &FwNode, name: &CStr) -> Result; +} + +impl Property for CString { + fn read(fwnode: &FwNode, name: &CStr) -> Result { + let mut str: *mut u8 =3D ptr::null_mut(); + let pstr: *mut _ =3D &mut str; + + // SAFETY: `name` is non-null and null-terminated. `fwnode.as_raw`= is + // valid because `fwnode` is valid. + let ret =3D unsafe { + bindings::fwnode_property_read_string(fwnode.as_raw(), name.as= _char_ptr(), pstr.cast()) + }; + to_result(ret)?; + + // SAFETY: `pstr` contains a non-null ptr on success + let str =3D unsafe { CStr::from_char_ptr(*pstr) }; + Ok(str.try_into()?) + } +} +/// Implemented for all integers that can be read as properties. +/// +/// This helper trait is needed on top of the existing [`Property`] +/// trait to associate the integer types of various sizes with their +/// corresponding `fwnode_property_read_*_array` functions. +pub trait PropertyInt: Copy { + /// # Safety + /// + /// Callers must uphold the same safety invariants as for the various + /// `fwnode_property_read_*_array` functions. + unsafe fn read_array( + fwnode: *const bindings::fwnode_handle, + propname: *const ffi::c_char, + val: *mut Self, + nval: usize, + ) -> ffi::c_int; +} +// This macro generates implementations of the traits `Property` and +// `PropertyInt` for integers of various sizes. Its input is a list +// of pairs separated by commas. The first element of the pair is the +// type of the integer, the second one is the name of its corresponding +// `fwnode_property_read_*_array` function. +macro_rules! impl_property_for_int { + ($($int:ty: $f:ident),* $(,)?) =3D> { $( + impl PropertyInt for $int { + unsafe fn read_array( + fwnode: *const bindings::fwnode_handle, + propname: *const ffi::c_char, + val: *mut Self, + nval: usize, + ) -> ffi::c_int { + // SAFETY: The safety invariants on the trait require + // callers to uphold the invariants of the functions + // this macro is called with. + unsafe { + bindings::$f(fwnode, propname, val.cast(), nval) + } + } + } + )* }; +} +impl_property_for_int! { + u8: fwnode_property_read_u8_array, + u16: fwnode_property_read_u16_array, + u32: fwnode_property_read_u32_array, + u64: fwnode_property_read_u64_array, + i8: fwnode_property_read_u8_array, + i16: fwnode_property_read_u16_array, + i32: fwnode_property_read_u32_array, + i64: fwnode_property_read_u64_array, +} +/// # Safety +/// +/// Callers must ensure that if `len` is non-zero, `out_param` must be +/// valid and point to memory that has enough space to hold at least +/// `len` number of elements. +unsafe fn read_array_out_param( + fwnode: &FwNode, + name: &CStr, + out_param: *mut T, + len: usize, +) -> ffi::c_int { + // SAFETY: `name` is non-null and null-terminated. + // `fwnode.as_raw` is valid because `fwnode` is valid. + // `out_param` is valid and has enough space for at least + // `len` number of elements as per the safety requirement. + unsafe { T::read_array(fwnode.as_raw(), name.as_char_ptr(), out_param,= len) } +} +impl Property for [T; N] { + fn read(fwnode: &FwNode, name: &CStr) -> Result { + let mut val: [MaybeUninit; N] =3D [const { MaybeUninit::uninit(= ) }; N]; + + // SAFETY: `val.as_mut_ptr()` is valid and points to enough space = for + // `N` elements. Casting from `*mut MaybeUninit` to `*mut T` is= safe + // because `MaybeUninit` has the same memory layout as `T`. + let ret =3D unsafe { read_array_out_param::(fwnode, name, val.a= s_mut_ptr().cast(), N) }; + to_result(ret)?; + + // SAFETY: `val` is always initialized when + // fwnode_property_read__array is successful. + Ok(val.map(|v| unsafe { v.assume_init() })) + } +} +impl Property for T { + fn read(fwnode: &FwNode, name: &CStr) -> Result { + let val: [_; 1] =3D <[T; 1] as Property>::read(fwnode, name)?; + Ok(val[0]) + } +} + +/// A helper for reading device properties. +/// +/// Use [`Self::required`] if a missing property is considered a bug and +/// [`Self::optional`] otherwise. +/// +/// For convenience, [`Self::or`] and [`Self::or_default`] are provided. +pub struct PropertyGuard<'fwnode, 'name, T> { + /// The result of reading the property. + inner: Result, + /// The fwnode of the property, used for logging in the "required" cas= e. + fwnode: &'fwnode FwNode, + /// The name of the property, used for logging in the "required" case. + name: &'name CStr, +} + +impl PropertyGuard<'_, '_, T> { + /// Access the property, indicating it is required. + /// + /// If the property is not present, the error is automatically logged.= If a + /// missing property is not an error, use [`Self::optional`] instead. + pub fn required(self) -> Result { + if self.inner.is_err() { + // Get the device associated with the fwnode for device-associ= ated + // logging. + // TODO: Are we allowed to do this? The field `fwnode_handle.d= ev` + // has a somewhat vague comment, which could mean we're not + // supposed to access it: + // https://elixir.bootlin.com/linux/v6.13.6/source/include/lin= ux/fwnode.h#L51 + // SAFETY: According to the invariant of FwNode, it is valid. + let dev =3D unsafe { (*self.fwnode.as_raw()).dev }; + + if dev.is_null() { + pr_err!( + "{}: property '{}' is missing\n", + self.fwnode.display_path(), + self.name + ); + } else { + // SAFETY: If dev is not null, it points to a valid device. + let dev: &Device =3D unsafe { &*dev.cast() }; + dev_err!( + dev, + "{}: property '{}' is missing\n", + self.fwnode.display_path(), + self.name + ); + }; + } + self.inner + } + + /// Access the property, indicating it is optional. + /// + /// In contrast to [`Self::required`], no error message is logged if + /// the property is not present. + pub fn optional(self) -> Option { + self.inner.ok() + } + + /// Access the property or the specified default value. + /// + /// Do not pass a sentinel value as default to detect a missing proper= ty. + /// Use [`Self::required`] or [`Self::optional`] instead. + pub fn or(self, default: T) -> T { + self.inner.unwrap_or(default) + } +} + +impl PropertyGuard<'_, '_, T> { + /// Access the property or a default value. + /// + /// Use [`Self::or`] to specify a custom default value. + pub fn or_default(self) -> T { + self.inner.unwrap_or_default() + } +} --=20 2.49.0 From nobody Fri Dec 19 06:31:07 2025 Received: from mout-p-202.mailbox.org (mout-p-202.mailbox.org [80.241.56.172]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 275332750F7; Mon, 14 Apr 2025 15:26:55 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=80.241.56.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744644418; cv=none; b=p2O5sy0GhKkdH0y4MTVLOnEqUaebav0jqe7X2inQ3AFinmG0OpZYWrfg5P1dNI0ShRc5AUILqvTFRcs56237zNuckLZrnVgIIMHVTv7Hsld9xonNaRYnGpuKfdQzqNnj+uKoe+FJU8HuMLxXNhpDcHg/A0WLXhxffxKj7Ryq5Oo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744644418; c=relaxed/simple; bh=tljC7nX6BIWJmnp//ntgU0wE8rfph+aklnYeois2pjs=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=T79BvpQmenoovwW7aMp8EnmIjlQEubLuFDdBmn+VdNRC8BlqCQgPH+h06+MIFAy+YvlIoknPnQ9etVPz+uTX7yXfKAA2ngKhFv9uYqCnmpdzu9umcAnBfbmWMYT8aKxG5fGyLRXdqMPFiIgAJxbB9L/wc7ym4Y2PsF9Of55Lcys= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=buenzli.dev; spf=pass smtp.mailfrom=buenzli.dev; arc=none smtp.client-ip=80.241.56.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=buenzli.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=buenzli.dev Received: from smtp1.mailbox.org (smtp1.mailbox.org [IPv6:2001:67c:2050:b231:465::1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by mout-p-202.mailbox.org (Postfix) with ESMTPS id 4Zbrk41926z9tFN; Mon, 14 Apr 2025 17:26:52 +0200 (CEST) From: Remo Senekowitsch To: Rob Herring , Saravana Kannan , Miguel Ojeda , Alex Gaynor , Boqun Feng , Gary Guo , =?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , Greg Kroah-Hartman , "Rafael J. Wysocki" , Remo Senekowitsch Cc: linux-kernel@vger.kernel.org, devicetree@vger.kernel.org, rust-for-linux@vger.kernel.org Subject: [PATCH v2 3/5] rust: property: Add child accessor and iterator Date: Mon, 14 Apr 2025 17:26:28 +0200 Message-ID: <20250414152630.1691179-4-remo@buenzli.dev> In-Reply-To: <20250414152630.1691179-1-remo@buenzli.dev> References: <20250326171411.590681-1-remo@buenzli.dev> <20250414152630.1691179-1-remo@buenzli.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Rspamd-Queue-Id: 4Zbrk41926z9tFN Content-Type: text/plain; charset="utf-8" Allow Rust drivers to access children of a fwnode either by name or by iterating over all of them. In C, there is the function `fwnode_get_next_child_node` for iteration and the macro `fwnode_for_each_child_node` that helps with handling the pointers. Instead of a macro, a native iterator is used in Rust such that regular for-loops can be used. Signed-off-by: Remo Senekowitsch --- rust/kernel/property.rs | 75 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 74 insertions(+), 1 deletion(-) diff --git a/rust/kernel/property.rs b/rust/kernel/property.rs index 0d4ea3168..d9fb773b6 100644 --- a/rust/kernel/property.rs +++ b/rust/kernel/property.rs @@ -13,7 +13,7 @@ error::{to_result, Result}, prelude::*, str::{BStr, CStr, CString}, - types::Opaque, + types::{ARef, Opaque}, }; =20 impl Device { @@ -68,6 +68,16 @@ pub fn property_read<'fwnode, 'name, T: Property>( ) -> PropertyGuard<'fwnode, 'name, T> { self.fwnode().property_read(name) } + + /// Returns first matching named child node handle. + pub fn get_child_by_name(&self, name: &CStr) -> Option> { + self.fwnode().get_child_by_name(name) + } + + /// Returns an iterator over a node's children. + pub fn children<'a>(&'a self) -> impl Iterator> = + 'a { + self.fwnode().children() + } } =20 /// A reference-counted fwnode_handle. @@ -89,6 +99,22 @@ pub fn property_read<'fwnode, 'name, T: Property>( pub struct FwNode(Opaque); =20 impl FwNode { + /// # Safety + /// + /// Callers must ensure that the reference count was incremented at le= ast + /// once, and that they are properly relinquishing one increment. That= is, + /// if there is only one increment, callers must not use the underlying + /// object anymore -- it is only safe to do so via the newly created + /// [`ARef`]. + unsafe fn from_raw(raw: *mut bindings::fwnode_handle) -> ARef { + // SAFETY: As per the safety requirement, raw has an incremented + // refcount which won't be decremented elsewhere. It also it not n= ull. + // It is safe to cast from a `*mut fwnode_handle` to `*mut FwNode`, + // because `FwNode` is defined as a `#[repr(transparent)]` wrapper + // around `fwnode_handle`. + unsafe { ARef::from_raw(ptr::NonNull::new_unchecked(raw.cast())) } + } + /// Obtain the raw `struct fwnode_handle *`. pub(crate) fn as_raw(&self) -> *mut bindings::fwnode_handle { self.0.get() @@ -243,6 +269,53 @@ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> cor= e::fmt::Result { =20 FwNodeDisplayPath(self) } + + /// Returns first matching named child node handle. + pub fn get_child_by_name(&self, name: &CStr) -> Option> { + // SAFETY: `self` and `name` are valid. + let child =3D + unsafe { bindings::fwnode_get_named_child_node(self.as_raw(), = name.as_char_ptr()) }; + if child.is_null() { + return None; + } + // SAFETY: `fwnode_get_named_child_node` returns a pointer with re= fcount incremented. + Some(unsafe { Self::from_raw(child) }) + } + + /// Returns an iterator over a node's children. + pub fn children<'a>(&'a self) -> impl Iterator> = + 'a { + let mut prev: Option> =3D None; + + core::iter::from_fn(move || { + let prev_ptr =3D match prev.take() { + None =3D> ptr::null_mut(), + Some(prev) =3D> { + // We will pass `prev` to `fwnode_get_next_child_node`, + // which decrements its refcount, so we use + // `ARef::into_raw` to avoid decrementing the refcount + // twice. + let prev =3D ARef::into_raw(prev); + prev.as_ptr().cast() + } + }; + // SAFETY: `self.as_raw()` is valid. `prev_ptr` may be null, + // which is allowed and corresponds to getting the first child. + // Otherwise, `prev_ptr` is valid, as it is the stored return + // value from the previous invocation. `prev_ptr` has its refo= unt + // incremented and it won't be decremented anymore on the Rust + // side. `fwnode_get_next_child_node` decrements the refcount = of + // `prev_ptr`, so the refcount is handled correctly. + let next =3D unsafe { bindings::fwnode_get_next_child_node(sel= f.as_raw(), prev_ptr) }; + if next.is_null() { + return None; + } + // SAFETY: `fwnode_get_next_child_node` returns a pointer with + // refcount incremented. + let next =3D unsafe { FwNode::from_raw(next) }; + prev =3D Some(next.clone()); + Some(next) + }) + } } =20 // SAFETY: Instances of `FwNode` are always reference-counted. --=20 2.49.0 From nobody Fri Dec 19 06:31:07 2025 Received: from mout-p-103.mailbox.org (mout-p-103.mailbox.org [80.241.56.161]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A27BB27979A; Mon, 14 Apr 2025 15:26:58 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=80.241.56.161 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744644420; cv=none; b=ZBrfZ2S0bOK3mfBgApN4OU96CKkuIPc79seP6WhyTpXahQfTbDir2ZJMjQXbGSdT3rYl/5Uhptl+XvXCyuOVj9082ecWe5FDnSejfqDszvrnv6RksxdpBqOQ1drmCXwdObmy90Fnm0diTbR9MuLlUC5ieDgjy8M4hblqzRVvlek= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744644420; c=relaxed/simple; bh=5U49g9YpNRw3gJcz/M8aGe2hUaNK6XKvyw07O3jSlLo=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=D+9VUz8Xbz0eSBBtC7HajkJhfAwCWFcaKUBeh0tLV6wpS43lgtE6AoMk/V3EOZ0cIuy6TGy8dktJCRZQ2g0BJIeyBjvHwOXuNnrXRR77qL7msKaQdAFhZcrEyxMwbHrE0UeqbYbB2i/E5yWlBfdL4tZWJ9y35LAksKxN0Ezpo5M= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=buenzli.dev; spf=pass smtp.mailfrom=buenzli.dev; arc=none smtp.client-ip=80.241.56.161 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=buenzli.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=buenzli.dev Received: from smtp1.mailbox.org (smtp1.mailbox.org [IPv6:2001:67c:2050:b231:465::1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by mout-p-103.mailbox.org (Postfix) with ESMTPS id 4Zbrk666rnz9ttn; Mon, 14 Apr 2025 17:26:54 +0200 (CEST) From: Remo Senekowitsch To: Rob Herring , Saravana Kannan , Miguel Ojeda , Alex Gaynor , Boqun Feng , Gary Guo , =?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , Greg Kroah-Hartman , "Rafael J. Wysocki" , Remo Senekowitsch Cc: linux-kernel@vger.kernel.org, devicetree@vger.kernel.org, rust-for-linux@vger.kernel.org Subject: [PATCH v2 4/5] rust: property: Add property_get_reference_args Date: Mon, 14 Apr 2025 17:26:29 +0200 Message-ID: <20250414152630.1691179-5-remo@buenzli.dev> In-Reply-To: <20250414152630.1691179-1-remo@buenzli.dev> References: <20250326171411.590681-1-remo@buenzli.dev> <20250414152630.1691179-1-remo@buenzli.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Rspamd-Queue-Id: 4Zbrk666rnz9ttn Content-Type: text/plain; charset="utf-8" Allow Rust code to read reference args from device properties. The wrapper type `FwNodeReferenceArgs` allows callers to access the buffer of read args safely. Signed-off-by: Remo Senekowitsch --- rust/kernel/property.rs | 79 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/rust/kernel/property.rs b/rust/kernel/property.rs index d9fb773b6..17ad17637 100644 --- a/rust/kernel/property.rs +++ b/rust/kernel/property.rs @@ -78,6 +78,17 @@ pub fn get_child_by_name(&self, name: &CStr) -> Option> { pub fn children<'a>(&'a self) -> impl Iterator> = + 'a { self.fwnode().children() } + + /// Finds a reference with arguments. + pub fn property_get_reference_args( + &self, + prop: &CStr, + nargs: NArgs<'_>, + index: u32, + ) -> Result { + self.fwnode() + .property_get_reference_args(prop, nargs, index) + } } =20 /// A reference-counted fwnode_handle. @@ -316,6 +327,65 @@ pub fn children<'a>(&'a self) -> impl Iterator> + 'a { Some(next) }) } + + /// Finds a reference with arguments. + pub fn property_get_reference_args( + &self, + prop: &CStr, + nargs: NArgs<'_>, + index: u32, + ) -> Result { + let mut out_args =3D FwNodeReferenceArgs::default(); + + let (nargs_prop, nargs) =3D match nargs { + NArgs::Prop(nargs_prop) =3D> (nargs_prop.as_char_ptr(), 0), + NArgs::N(nargs) =3D> (ptr::null(), nargs), + }; + + // SAFETY: `self.0.get()` is valid. `prop.as_char_ptr()` is valid = and + // zero-terminated. `nargs_prop` is valid and zero-terminated if `= nargs` + // is zero, otherwise it is allowed to be a null-pointer. + let ret =3D unsafe { + bindings::fwnode_property_get_reference_args( + self.0.get(), + prop.as_char_ptr(), + nargs_prop, + nargs, + index, + &mut out_args.0, + ) + }; + to_result(ret)?; + + Ok(out_args) + } +} + +/// The return value of `property_get_reference_args`. +/// +/// - [`Device::property_get_reference_args`] +/// - [`FwNode::property_get_reference_args`] +#[repr(transparent)] +#[derive(Copy, Clone, Default)] +pub struct FwNodeReferenceArgs(bindings::fwnode_reference_args); + +impl FwNodeReferenceArgs { + /// Returns the slice of reference arguments. + pub fn as_slice(&self) -> &[u64] { + // SAFETY: As per the safety invariant of FwNodeReferenceArgs, `na= rgs` + // is the number of elements in `args` that is valid. + unsafe { core::slice::from_raw_parts(self.0.args.as_ptr(), self.0.= nargs as usize) } + } + + /// Returns the number of reference arguments. + pub fn len(&self) -> usize { + self.0.nargs as usize + } + + /// Returns `true` if there are no reference arguments. + pub fn is_empty(&self) -> bool { + self.0.nargs =3D=3D 0 + } } =20 // SAFETY: Instances of `FwNode` are always reference-counted. @@ -451,6 +521,15 @@ fn read(fwnode: &FwNode, name: &CStr) -> Result { } } =20 +/// The number of arguments of a reference. +pub enum NArgs<'a> { + /// The name of the property of the reference indicating the number of + /// arguments. + Prop(&'a CStr), + /// The known number of arguments. + N(u32), +} + /// A helper for reading device properties. /// /// Use [`Self::required`] if a missing property is considered a bug and --=20 2.49.0 From nobody Fri Dec 19 06:31:07 2025 Received: from mout-p-102.mailbox.org (mout-p-102.mailbox.org [80.241.56.152]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 126AF274642; Mon, 14 Apr 2025 15:27:00 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=80.241.56.152 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744644422; cv=none; b=sq6roskqF47De0PunvhVSQWLTcMEZ9cIHD/MeargdjNYa6aA3Ux9D4vZjSPGTLDKJ0V4XWexlLCf0I0bDRejCP4kkKPTj+joaD5TiIuxd7B7ihvNp9vWGpu1jCZts+wcQk2jc07Qe0DtmnhgXw5qJQj32zqR8Jmz/GWDX02M17o= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744644422; c=relaxed/simple; bh=myCQWYQ5yFgd2BuNSiywex37N2f8bLHvBD8YV269Ips=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=nbSkVvBfbnuAyqdztd/ki0CuOHPUGxjDx0chebThjcBpSPbWCxLuLUSgZ5xJeLNm+NGoa6ZY0yjbD4jsvIbHHb8c+grBWXW638nd5HPlBPFYKvHY97fowkbFC/kwjdWumjSoSHwHzaiY4ALrY833T1MjabYZA0DZDOK/e3C4mXg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=buenzli.dev; spf=pass smtp.mailfrom=buenzli.dev; arc=none smtp.client-ip=80.241.56.152 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=buenzli.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=buenzli.dev Received: from smtp1.mailbox.org (smtp1.mailbox.org [IPv6:2001:67c:2050:b231:465::1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by mout-p-102.mailbox.org (Postfix) with ESMTPS id 4Zbrk92D4Yz9vGD; Mon, 14 Apr 2025 17:26:57 +0200 (CEST) From: Remo Senekowitsch To: Rob Herring , Saravana Kannan , Miguel Ojeda , Alex Gaynor , Boqun Feng , Gary Guo , =?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , Greg Kroah-Hartman , "Rafael J. Wysocki" , Remo Senekowitsch Cc: linux-kernel@vger.kernel.org, devicetree@vger.kernel.org, rust-for-linux@vger.kernel.org Subject: [PATCH v2 5/5] samples: rust: platform: Add property read examples Date: Mon, 14 Apr 2025 17:26:30 +0200 Message-ID: <20250414152630.1691179-6-remo@buenzli.dev> In-Reply-To: <20250414152630.1691179-1-remo@buenzli.dev> References: <20250326171411.590681-1-remo@buenzli.dev> <20250414152630.1691179-1-remo@buenzli.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Rspamd-Queue-Id: 4Zbrk92D4Yz9vGD Content-Type: text/plain; charset="utf-8" Add some example usage of the device property read methods for DT/ACPI/swnode properties. Co-developed-by: Rob Herring (Arm) Signed-off-by: Rob Herring (Arm) Signed-off-by: Remo Senekowitsch --- drivers/of/unittest-data/tests-platform.dtsi | 3 + samples/rust/rust_driver_platform.rs | 69 +++++++++++++++++++- 2 files changed, 69 insertions(+), 3 deletions(-) diff --git a/drivers/of/unittest-data/tests-platform.dtsi b/drivers/of/unit= test-data/tests-platform.dtsi index 4171f43cf..50a51f38a 100644 --- a/drivers/of/unittest-data/tests-platform.dtsi +++ b/drivers/of/unittest-data/tests-platform.dtsi @@ -37,6 +37,9 @@ dev@100 { test-device@2 { compatible =3D "test,rust-device"; reg =3D <0x2>; + + test,u32-prop =3D <0xdeadbeef>; + test,i16-array =3D /bits/ 16 <1 2 (-3) (-4)>; }; }; =20 diff --git a/samples/rust/rust_driver_platform.rs b/samples/rust/rust_drive= r_platform.rs index 8120609e2..0284f1840 100644 --- a/samples/rust/rust_driver_platform.rs +++ b/samples/rust/rust_driver_platform.rs @@ -2,7 +2,7 @@ =20 //! Rust Platform driver sample. =20 -use kernel::{c_str, of, platform, prelude::*}; +use kernel::{c_str, of, platform, prelude::*, str::CString}; =20 struct SampleDriver { pdev: platform::Device, @@ -22,18 +22,81 @@ impl platform::Driver for SampleDriver { const OF_ID_TABLE: Option> =3D Some(&OF_TABL= E); =20 fn probe(pdev: &mut platform::Device, info: Option<&Self::IdInfo>) -> = Result>> { - dev_dbg!(pdev.as_ref(), "Probe Rust Platform driver sample.\n"); + let dev =3D pdev.as_ref(); + + dev_dbg!(dev, "Probe Rust Platform driver sample.\n"); =20 if let Some(info) =3D info { - dev_info!(pdev.as_ref(), "Probed with info: '{}'.\n", info.0); + dev_info!(dev, "Probed with info: '{}'.\n", info.0); } =20 + Self::properties_parse(dev)?; + let drvdata =3D KBox::new(Self { pdev: pdev.clone() }, GFP_KERNEL)= ?; =20 Ok(drvdata.into()) } } =20 +impl SampleDriver { + fn properties_parse(dev: &kernel::device::Device) -> Result<()> { + if let Ok(idx) =3D dev.property_match_string(c_str!("compatible"),= c_str!("test,rust-device")) + { + dev_info!(dev, "matched compatible string idx =3D {}\n", idx); + } + + if let Ok(str) =3D dev + .property_read::(c_str!("compatible")) + .required() + { + dev_info!(dev, "compatible string =3D {:?}\n", str); + } + + let prop =3D dev.property_read_bool(c_str!("test,bool-prop")); + dev_info!(dev, "bool prop is {}\n", prop); + + if dev.property_present(c_str!("test,u32-prop")) { + dev_info!(dev, "'test,u32-prop' is present\n"); + } + + let prop =3D dev + .property_read::(c_str!("test,u32-optional-prop")) + .or(0x12); + dev_info!( + dev, + "'test,u32-optional-prop' is {:#x} (default =3D {:#x})\n", + prop, + 0x12 + ); + + // Missing property without a default will print an error + let _ =3D dev + .property_read::(c_str!("test,u32-required-prop")) + .required()?; + + let prop: u32 =3D dev.property_read(c_str!("test,u32-prop")).requi= red()?; + dev_info!(dev, "'test,u32-prop' is {:#x}\n", prop); + + // TODO: remove or replace with u16? `Property` is not implemented= for + // unsigned integers, as suggested by Andy Shevchenko. + + let prop: [i16; 4] =3D dev.property_read(c_str!("test,i16-array"))= .required()?; + dev_info!(dev, "'test,i16-array' is {:?}\n", prop); + dev_info!( + dev, + "'test,i16-array' length is {}\n", + dev.property_count_elem::(c_str!("test,i16-array"))?, + ); + + let prop: KVec =3D dev + .property_read_array_vec(c_str!("test,i16-array"), 4)? + .required()?; + dev_info!(dev, "'test,i16-array' is KVec {:?}\n", prop); + + Ok(()) + } +} + impl Drop for SampleDriver { fn drop(&mut self) { dev_dbg!(self.pdev.as_ref(), "Remove Rust Platform driver sample.\= n"); --=20 2.49.0