From nobody Fri Dec 19 16:04:26 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 8E5A6267F75; Fri, 25 Apr 2025 15:02:07 +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=1745593331; cv=none; b=Ma6VdbpN1HJUcTY2EMOGmNvRJhnBbO6Ow4NjIVrHT3y8wF39ObADE/5jqfSBaBzO2Og3w0OHz4w+JS6ksZlZm6hlhGOwtZcff798CyKRGjEuLqAFaKR4Oy7gtP8s22FlhD4QuVnGKBBMayvYIfcSVf1BcNuv68OgojIMbUHxLOA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1745593331; c=relaxed/simple; bh=iTu3xyMaen6N4lYL60lO/zs4WK1E+diSLwDbJZm8Ee8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=oa4blcwvTO6pbHnZot0RYbkzR+pkhY7TZIEAxbVMWmH6LviALerEHMbpZrHtXcYksTvvjDagl0B3u5iupSjTBlzFIPh/U7/hC5XyuX0LOfccSm+GNcTJyCXxztw0uEVt3/lpDUbhLiVLyuo7YC9fxABvpXA1Z+ZjaIgiUJzr3dI= 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 smtp202.mailbox.org (smtp202.mailbox.org [IPv6:2001:67c:2050:b231:465::202]) (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 4ZkbfF4thPz9srM; Fri, 25 Apr 2025 17:01: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" , Dirk Behme , Remo Senekowitsch Cc: linux-kernel@vger.kernel.org, devicetree@vger.kernel.org, rust-for-linux@vger.kernel.org Subject: [PATCH v3 1/7] rust: property: Move property_present to separate file Date: Fri, 25 Apr 2025 17:01:24 +0200 Message-ID: <20250425150130.13917-2-remo@buenzli.dev> In-Reply-To: <20250425150130.13917-1-remo@buenzli.dev> References: <20250425150130.13917-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: 4ZkbfF4thPz9srM 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, the property access methods must be implemented on the abstraction over fwnode_handle. While it's possible to expose similar methods on `Device` directly for convenience, those methods would have to get the `FwNode` first, which is a fallible operation, making the API inconsistent. For this reason, such duplicated methods are omitted. Users who need to read properties of a device will have to explictily get the `FwNode` first (handle the `None` case) and then read properties on that. Signed-off-by: Remo Senekowitsch --- MAINTAINERS | 3 +- rust/helpers/helpers.c | 1 + rust/helpers/property.c | 8 +++ rust/kernel/{device.rs =3D> device/mod.rs} | 9 +-- rust/kernel/device/property.rs | 70 ++++++++++++++++++++++++ 5 files changed, 83 insertions(+), 8 deletions(-) create mode 100644 rust/helpers/property.c rename rust/kernel/{device.rs =3D> device/mod.rs} (97%) create mode 100644 rust/kernel/device/property.rs diff --git a/MAINTAINERS b/MAINTAINERS index c8d9e8187..4585f9e7f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7112,7 +7112,8 @@ F: include/linux/kobj* F: include/linux/property.h F: include/linux/sysfs.h F: lib/kobj* -F: rust/kernel/device.rs +F: rust/kernel/device/mod.rs +F: rust/kernel/device/property.rs F: rust/kernel/device_id.rs F: rust/kernel/devres.rs F: rust/kernel/driver.rs 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/mod.rs similarity index 97% rename from rust/kernel/device.rs rename to rust/kernel/device/mod.rs index db2d9658b..e49107452 100644 --- a/rust/kernel/device.rs +++ b/rust/kernel/device/mod.rs @@ -6,7 +6,6 @@ =20 use crate::{ bindings, - str::CStr, types::{ARef, Opaque}, }; use core::{fmt, ptr}; @@ -14,6 +13,8 @@ #[cfg(CONFIG_PRINTK)] use crate::c_str; =20 +pub mod property; + /// A reference-counted device. /// /// This structure represents the Rust abstraction for a C `struct device`= . This implementation @@ -181,12 +182,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/device/property.rs b/rust/kernel/device/property.rs new file mode 100644 index 000000000..d89715f7d --- /dev/null +++ b/rust/kernel/device/property.rs @@ -0,0 +1,70 @@ +// 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. + pub fn fwnode(&self) -> Option<&FwNode> { + // SAFETY: `self` is valid. + let fwnode_handle =3D unsafe { bindings::__dev_fwnode(self.as_raw(= )) }; + if fwnode_handle.is_null() { + return None; + } + // 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`. + Some(unsafe { &*fwnode_handle.cast() }) + } +} + +/// 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 16:04:26 2025 Received: from mout-p-101.mailbox.org (mout-p-101.mailbox.org [80.241.56.151]) (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 83CD425F7A7; Fri, 25 Apr 2025 15:02:11 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=80.241.56.151 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1745593333; cv=none; b=qrc1ibZdbv5fdgZ0byoFVBUlMcq3Q97Rs7FmswEUxzQFnBKFOYfOru0YO2CNmUrmEHSjXkqG/Zo0KYH/J8ukncxJJFQp0rzcyPDYSZLFRtac4uxfs5D277KqApqZGdUbGXtWi7Z+pHtyPufh2ayWxDstgBvyN8cnAFR59AKysCg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1745593333; c=relaxed/simple; bh=apsOcyCRGG4zui5ahb4QBp/DW5Zg8A1uNMLm3N7c5jI=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=TWNsv4tblpg9BaWnSn2H7tl+/l2898Ewa0+Ka3SZE/VQTjXlitK0xkJCPuBJ4jcoXFUHRD0Y2k0Ft8zNHO2Z2PrChR8K1qN0O19A9EMWBPorqZ5DZf6Z/iwNrj696IY1sYDHeFlNnRCHnbvsAQDSnqHxwTa/CpY+3JB1T2/lvM4= 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.151 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 smtp202.mailbox.org (smtp202.mailbox.org [IPv6:2001:67c:2050:b231:465::202]) (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-101.mailbox.org (Postfix) with ESMTPS id 4ZkbfK5TnFz9t1w; Fri, 25 Apr 2025 17:02:01 +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" , Dirk Behme , Remo Senekowitsch Cc: linux-kernel@vger.kernel.org, devicetree@vger.kernel.org, rust-for-linux@vger.kernel.org Subject: [PATCH v3 2/7] rust: property: Enable printing fwnode name and path Date: Fri, 25 Apr 2025 17:01:25 +0200 Message-ID: <20250425150130.13917-3-remo@buenzli.dev> In-Reply-To: <20250425150130.13917-1-remo@buenzli.dev> References: <20250425150130.13917-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: 4ZkbfK5TnFz9t1w Content-Type: text/plain; charset="utf-8" Add two new public methods `display_name` and `display_path` to `FwNode`. They can be used by driver authors for logging purposes. In addition, they will be used by core property abstractions for automatic logging, for example when a driver attempts to read a required but missing property. Signed-off-by: Remo Senekowitsch --- rust/kernel/device/property.rs | 78 ++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/rust/kernel/device/property.rs b/rust/kernel/device/property.rs index d89715f7d..28850aa3b 100644 --- a/rust/kernel/device/property.rs +++ b/rust/kernel/device/property.rs @@ -49,6 +49,84 @@ pub(crate) fn as_raw(&self) -> *mut bindings::fwnode_han= dle { self.0.get() } =20 + /// 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 + '_ { + struct FwNodeDisplayName<'a>(&'a FwNode); + + impl core::fmt::Display for FwNodeDisplayName<'_> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::= Result { + // SAFETY: self is valid by its type invariant + let name =3D unsafe { bindings::fwnode_get_name(self.0.as_= raw()) }; + if name.is_null() { + return Ok(()); + } + // SAFETY: fwnode_get_name returns null or a valid C strin= g and + // name is not null + let name =3D unsafe { CStr::from_char_ptr(name) }; + write!(f, "{name}") + } + } + + 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 + '_ { + struct FwNodeDisplayPath<'a>(&'a FwNode); + + impl core::fmt::Display for FwNodeDisplayPath<'_> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::= Result { + // The logic here is the same as the one in lib/vsprintf.c + // (fwnode_full_name_string). + + let num_parents =3D unsafe { bindings::fwnode_count_parent= s(self.0.as_raw()) }; + + for depth in (0..=3Dnum_parents).rev() { + let fwnode =3D if depth =3D=3D 0 { + self.0.as_raw() + } else { + // SAFETY: `self.0.as_raw()` is valid + unsafe { bindings::fwnode_get_nth_parent(self.0.as= _raw(), depth) } + }; + + // SAFETY: fwnode is valid, it is either `self.0.as_ra= w()` or + // the return value of `bindings::fwnode_get_nth_paren= t` which + // returns a valid pointer to a fwnode_handle if the p= rovided + // depth is within the valid range, which we know to b= e true. + let prefix =3D unsafe { bindings::fwnode_get_name_pref= ix(fwnode) }; + if !prefix.is_null() { + // SAFETY: fwnode_get_name_prefix returns null or a + // valid C string + let prefix =3D unsafe { CStr::from_char_ptr(prefix= ) }; + write!(f, "{prefix}")?; + } + // SAFETY: fwnode is valid for the reasons stated above + let name =3D unsafe { bindings::fwnode_get_name(fwnode= ) }; + if !name.is_null() { + // SAFETY: fwnode_get_name returns null or a valid + // C string + let name =3D unsafe { CStr::from_char_ptr(name) }; + write!(f, "{name}")?; + } + + if depth !=3D 0 { + // SAFETY: `fwnode` is valid, because `depth` is + // a valid depth of a parent of `self.0.as_raw()`. + // `fwnode_get_nth_parent` increments the refcount= and + // we are responsible to decrement it. + unsafe { bindings::fwnode_handle_put(fwnode) } + } + } + + Ok(()) + } + } + + FwNodeDisplayPath(self) + } + /// 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. --=20 2.49.0 From nobody Fri Dec 19 16:04:26 2025 Received: from mout-p-201.mailbox.org (mout-p-201.mailbox.org [80.241.56.171]) (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 EB0E025DD0E; Fri, 25 Apr 2025 15:02:08 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=80.241.56.171 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1745593331; cv=none; b=XomxZT0hrokbkTv0vWo2l2D4xrpG9IE0sMa1B0sFMnscmN5ApltmXFhc8hgvVoO9fk4+af+B5in+XseX0zVuhs4tgh44U1xb6N1EYsHlseSy9RVxXxuY9V3vcsV7kC4dUaCCV47pRwOfzqjhI64AUyJwahowxJ0ZSDDWvytWwLY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1745593331; c=relaxed/simple; bh=aQ3Ij2OpGe7mSIXl0a1Dra/1GeqFov69g9H9So5/nNQ=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=XXvNKQoNcCb1snaq2kGSg7safNqgQ1b0oGrTrQo9NYTqc9Ty+nuqBQehSUsG+XPe89xEl7diGT7B1R/frtOQJ8RLYUa/9MKUEc4WZZoP5oQ6iafsxk4JkzxxWmBKfMfl/91nX8TZ87wwUIZdEfmfyfYnv7ln2nWZu5kG8cU34YY= 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.171 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 smtp202.mailbox.org (smtp202.mailbox.org [10.196.197.202]) (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-201.mailbox.org (Postfix) with ESMTPS id 4ZkbfP1p27z9sPr; Fri, 25 Apr 2025 17:02:05 +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" , Dirk Behme , Remo Senekowitsch Cc: linux-kernel@vger.kernel.org, devicetree@vger.kernel.org, rust-for-linux@vger.kernel.org Subject: [PATCH v3 3/7] rust: property: Introduce PropertyGuard Date: Fri, 25 Apr 2025 17:01:26 +0200 Message-ID: <20250425150130.13917-4-remo@buenzli.dev> In-Reply-To: <20250425150130.13917-1-remo@buenzli.dev> References: <20250425150130.13917-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 Content-Type: text/plain; charset="utf-8" This abstraction 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. It will be used by upcoming methods for reading device properties. Signed-off-by: Remo Senekowitsch --- rust/kernel/device/property.rs | 57 ++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/rust/kernel/device/property.rs b/rust/kernel/device/property.rs index 28850aa3b..de31a1f56 100644 --- a/rust/kernel/device/property.rs +++ b/rust/kernel/device/property.rs @@ -146,3 +146,60 @@ unsafe fn dec_ref(obj: ptr::NonNull) { unsafe { bindings::fwnode_handle_put(obj.cast().as_ptr()) } } } + +/// 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() { + pr_err!( + "{}: 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 16:04:26 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 D498E25C705; Fri, 25 Apr 2025 15:02:18 +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=1745593341; cv=none; b=NB8C1Y10mmPlgA7NSDidXUqlDNDnfARwM+Nn4DjiQT5yMKS7Da0hu+ZveLr2HJrZJp4TC0i+qy3+YXNs2x+3+8bKDMedMoStVCUjdkdDrNfabZVEe3/hymHhO/nLRLmTmnovhb9zsIq9FFj9vPQAmtfTtO6iNv5UrypQC69hnHg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1745593341; c=relaxed/simple; bh=7C4zitTW5z/c4mv5PGjYauKo6Ey/90dfG63hbkBB8rs=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=IJlNqEz/Ac6XQzbSIGXr9Hq608AcnjcVQaXTRh17d3YaDHpI4p6z42LlAR9HjLoCTZGCt9BBUJCsf+Bh0E2xf+4RlBIPQ7ti4ZgO3xUUE/Kqm01XHm3pLRymBNu/lBjZOSYrGt3Yy8Jge6te8UWlNnJm3s0z91tCRxMy76uuExc= 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 smtp202.mailbox.org (smtp202.mailbox.org [10.196.197.202]) (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 4ZkbfS5R5Zz9t6L; Fri, 25 Apr 2025 17:02:08 +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" , Dirk Behme , Remo Senekowitsch Cc: linux-kernel@vger.kernel.org, devicetree@vger.kernel.org, rust-for-linux@vger.kernel.org Subject: [PATCH v3 4/7] rust: property: Add bindings for reading device properties Date: Fri, 25 Apr 2025 17:01:27 +0200 Message-ID: <20250425150130.13917-5-remo@buenzli.dev> In-Reply-To: <20250425150130.13917-1-remo@buenzli.dev> References: <20250425150130.13917-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 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. Co-developed-by: Rob Herring (Arm) Signed-off-by: Rob Herring (Arm) Signed-off-by: Remo Senekowitsch --- rust/kernel/device/property.rs | 232 ++++++++++++++++++++++++++++++++- 1 file changed, 230 insertions(+), 2 deletions(-) diff --git a/rust/kernel/device/property.rs b/rust/kernel/device/property.rs index de31a1f56..9505cc35d 100644 --- a/rust/kernel/device/property.rs +++ b/rust/kernel/device/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, + device::Device, + error::{to_result, Result}, + prelude::*, + str::{CStr, CString}, + types::Opaque, +}; =20 impl Device { /// Obtain the fwnode corresponding to the device. @@ -132,6 +140,104 @@ 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 kernel::{c_str, device::property::FwNode, str::CString}; + /// fn examples(fwnode: &FwNode) -> Result { + /// let b: u32 =3D fwnode.property_read(c_str!("some-number")).req= uired()?; + /// if let Some(s) =3D fwnode.property_read::(c_str!("som= e-str")).optional() { + /// // ... + /// } + /// Ok(()) + /// } + /// ``` + pub fn property_read<'fwnode, 'name, T: Property>( + &'fwnode self, + name: &'name CStr, + ) -> PropertyGuard<'fwnode, 'name, T> { + PropertyGuard { + inner: T::read_from_fwnode_property(self, name), + fwnode: self, + name, + } + } } =20 // SAFETY: Instances of `FwNode` are always reference-counted. @@ -147,6 +253,128 @@ unsafe fn dec_ref(obj: ptr::NonNull) { } } =20 +/// 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_from_fwnode_property(fwnode: &FwNode, name: &CStr) -> Result; +} + +impl Property for CString { + fn read_from_fwnode_property(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_from_fwnode_property( + 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_from_fwnode_property( + 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_from_fwnode_property(fwnode.as_raw(), name.as_char_p= tr(), out_param, len) + } +} +impl Property for [T; N] { + fn read_from_fwnode_property(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_from_fwnode_property(fwnode: &FwNode, name: &CStr) -> Result { + let val: [_; 1] =3D <[T; 1] as Property>::read_from_fwnode_propert= y(fwnode, name)?; + Ok(val[0]) + } +} + /// 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 16:04:26 2025 Received: from mout-p-201.mailbox.org (mout-p-201.mailbox.org [80.241.56.171]) (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 4252225D1F7; Fri, 25 Apr 2025 15:02:15 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=80.241.56.171 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1745593338; cv=none; b=H42LJS2VmJTXTXi52j/Rja0uaanEzcOvFIWqkK2buLvAK7g5J3pNx1muz0drbkxGMnNiN2EEMib+9e0Ar+IKwMRcFTo0+zrloEqPMRiNgbMMVwRJkCHpIc4aXX/8ZBjX8Jkh6p4EysOb2xhy5mbWwVOovS9qbzuUFyGakrrQXS4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1745593338; c=relaxed/simple; bh=KeMi7aWTKimdysNNp/dvl+FY+DnpfOJGIWKRaRPVmxc=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=EU6hgv+SA7jOGicy4ifbsjt1ro71KW9ruhQQSoxwTMzzyme0jviAfN0xQyF5n2FJyzV8+zdEE7N0UXt/nVDApAhufjr51UG3QuFf17LjULNzduiXyjqPPTphf/6nL1Joacr6DP6K3GcZqHVxuucsZsrmblvJxBwMbKcMiJ1ZXI0= 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.171 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 smtp202.mailbox.org (smtp202.mailbox.org [10.196.197.202]) (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-201.mailbox.org (Postfix) with ESMTPS id 4ZkbfX3fjYz9sv9; Fri, 25 Apr 2025 17:02:12 +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" , Dirk Behme , Remo Senekowitsch Cc: linux-kernel@vger.kernel.org, devicetree@vger.kernel.org, rust-for-linux@vger.kernel.org Subject: [PATCH v3 5/7] rust: property: Add child accessor and iterator Date: Fri, 25 Apr 2025 17:01:28 +0200 Message-ID: <20250425150130.13917-6-remo@buenzli.dev> In-Reply-To: <20250425150130.13917-1-remo@buenzli.dev> References: <20250425150130.13917-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 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/device/property.rs | 79 +++++++++++++++++++++++++++++++++- 1 file changed, 78 insertions(+), 1 deletion(-) diff --git a/rust/kernel/device/property.rs b/rust/kernel/device/property.rs index 9505cc35d..0a0cb0c02 100644 --- a/rust/kernel/device/property.rs +++ b/rust/kernel/device/property.rs @@ -13,7 +13,7 @@ error::{to_result, Result}, prelude::*, str::{CStr, CString}, - types::Opaque, + types::{ARef, Opaque}, }; =20 impl Device { @@ -52,6 +52,27 @@ pub fn fwnode(&self) -> Option<&FwNode> { pub struct FwNode(Opaque); =20 impl FwNode { + /// # Safety + /// + /// Callers must ensure that: + /// - The reference count was incremented at least once. + /// - They relinquish that 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 requirements of this function: + // - `NonNull::new_unchecked`: + // - `raw` is not null + // - `ARef::from_raw`: + // - `raw` has an incremented refcount + // - that increment is relinquished, i.e. it won't be decremented + // elsewhere. + // CAST: 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() @@ -238,6 +259,62 @@ pub fn property_read<'fwnode, 'name, T: Property>( name, } } + + /// Returns first matching named child node handle. + pub fn get_child_by_name(&self, name: &CStr) -> Option> { + // SAFETY: `self` and `name` are valid by their type invariants. + 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 its refc= ount + // incremented. + // - That increment is relinquished, i.e. the underlying object is= not + // used anymore except via the newly created `ARef`. + 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 by its type invariant. + // - `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 refount incremented. + // - The increment of `prev_ptr` is relinquished, i.e. the + // underlying object won't be unsed anymore. + let next =3D unsafe { bindings::fwnode_get_next_child_node(sel= f.as_raw(), prev_ptr) }; + if next.is_null() { + return None; + } + // SAFETY: + // - `next` is valid because `fwnode_get_next_child_node` retu= rns a + // pointer with its refcount incremented. + // - That increment is relinquished, i.e. the underlying object + // won't be used anymore, except via the newly created + // `ARef`. + 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 16:04:26 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 EEBC126E167; Fri, 25 Apr 2025 15:02:19 +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=1745593344; cv=none; b=blb0OaKcf83M5qK9FhLTbFkPKLflSVOVj7m56l1ZNdG8iJpD07Gdodue+a5fr/OygRc+xYWSKgY1HUUE5wRN+Mglcv31tA3NzWl49AnRFJuyo9UhSQ4naMEMN1073dQZSq97VpgVfA71yzRVMVMY0eP0FNmjyeEbrN61w3t/Cuc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1745593344; c=relaxed/simple; bh=ue+w52axnZG3IjPAyclUrsVnji/ojql8vUgwa5Hm2lg=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=neGFgpGR9CC9081vd+FTsO+BAdIgzx+DRt5DjFMnkle64NuqvhnaV+bWD2Iv18tV1idU2dP4YUWQiQSYRpA1hXxsbz+DcyYyi8bgeLTwKV3rwpTyDD9vlVRGBad7a9r/KaCGZiC21aRYGdrnUBrBWYQBYbfURRyN9aBnLp+2A1o= 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 smtp202.mailbox.org (smtp202.mailbox.org [IPv6:2001:67c:2050:b231:465::202]) (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 4Zkbfc0N6Nz9sbM; Fri, 25 Apr 2025 17:02:16 +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" , Dirk Behme , Remo Senekowitsch Cc: linux-kernel@vger.kernel.org, devicetree@vger.kernel.org, rust-for-linux@vger.kernel.org Subject: [PATCH v3 6/7] rust: property: Add property_get_reference_args Date: Fri, 25 Apr 2025 17:01:29 +0200 Message-ID: <20250425150130.13917-7-remo@buenzli.dev> In-Reply-To: <20250425150130.13917-1-remo@buenzli.dev> References: <20250425150130.13917-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: 4Zkbfc0N6Nz9sbM 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/device/property.rs | 68 ++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/rust/kernel/device/property.rs b/rust/kernel/device/property.rs index 0a0cb0c02..475b916d0 100644 --- a/rust/kernel/device/property.rs +++ b/rust/kernel/device/property.rs @@ -315,6 +315,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. @@ -452,6 +511,15 @@ fn read_from_fwnode_property(fwnode: &FwNode, name: &C= Str) -> 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 16:04:26 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 A15DE270541; Fri, 25 Apr 2025 15:02:23 +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=1745593345; cv=none; b=YZbGMeBmyGxKZiH91BNC9IWqEeJ2D4K983Q3Om+Q+8CgPWyTPhitdsdqfGzZVjfpOiH/jbDX1Xu6aAd8vTeV4r6deVcbuM6qwoI0X3MAHld9Tn2UgLiO7PH1hqt3B28C4TjqypfCUmTo1bPupbXhwU71rsMq+sFOd/V+vMKOjgk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1745593345; c=relaxed/simple; bh=HnZWEYm88dd+PWMdz2G4U6U9j41MJ085J+JoQtTibK8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=M0+ApnFQcRyMw/0Gf7nFGoDH7uvVz/E1dVxXj0tXFhaRbu0JO/l6iuVO1itLq+5w9rTK+ab0I4LT1u7NhQRm8s4Qc0QTeLh9Am2MLxYnC/RMwi7NYMkZWzkavI0i4dJ1hqchNZNM7KkAPNHQWAereUvSUnsZ4AhhE2qoe4CeUDM= 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 smtp202.mailbox.org (smtp202.mailbox.org [10.196.197.202]) (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 4Zkbfg6hnkz9sp5; Fri, 25 Apr 2025 17:02:19 +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" , Dirk Behme , Remo Senekowitsch Cc: linux-kernel@vger.kernel.org, devicetree@vger.kernel.org, rust-for-linux@vger.kernel.org Subject: [PATCH v3 7/7] samples: rust: platform: Add property read examples Date: Fri, 25 Apr 2025 17:01:30 +0200 Message-ID: <20250425150130.13917-8-remo@buenzli.dev> In-Reply-To: <20250425150130.13917-1-remo@buenzli.dev> References: <20250425150130.13917-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 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 | 72 +++++++++++++++++++- 2 files changed, 72 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..04731baa7 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,84 @@ 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<()> { + let fwnode =3D dev.fwnode().ok_or(ENOENT)?; + + if let Ok(idx) =3D + fwnode.property_match_string(c_str!("compatible"), c_str!("tes= t,rust-device")) + { + dev_info!(dev, "matched compatible string idx =3D {}\n", idx); + } + + if let Ok(str) =3D fwnode + .property_read::(c_str!("compatible")) + .required() + { + dev_info!(dev, "compatible string =3D {:?}\n", str); + } + + let prop =3D fwnode.property_read_bool(c_str!("test,bool-prop")); + dev_info!(dev, "bool prop is {}\n", prop); + + if fwnode.property_present(c_str!("test,u32-prop")) { + dev_info!(dev, "'test,u32-prop' is present\n"); + } + + let prop =3D fwnode + .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 fwnode + .property_read::(c_str!("test,u32-required-prop")) + .required(); + + let prop: u32 =3D fwnode.property_read(c_str!("test,u32-prop")).re= quired()?; + 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 fwnode.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", + fwnode.property_count_elem::(c_str!("test,i16-array"))?, + ); + + let prop: KVec =3D fwnode + .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