From patchwork Wed Mar 26 17:13:40 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Remo Senekowitsch X-Patchwork-Id: 14030371 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 4FBB9191461; Wed, 26 Mar 2025 17:14:47 +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=1743009290; cv=none; b=RpWNkoKWA7IR5NrlFqbiPVXmMf14i53Ob+C3cz1XBGieKtZRg2XMCkfN+nEvMnK4DRE56r8GItdpIuy7eqw4SbLFxvDu/YNBGeIkZA8v0lG3gZ1Z1MTx4ZIPrxYPY3W41K0e1Gu49NnaCk+E7qtR0WSf4y0F5EEYa+Uu92u6db8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1743009290; c=relaxed/simple; bh=Tw/2+nSVi9h/5nQCeDcX0MAEOnDWfwqXhh3Hxy0trxc=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=MXyr3RN1y2C3XPaR/yGVNdUSVvPKWbdQeHG6098Vx40jeZADEuqzQUSSti9aOYYXXyaEC21pbXK3Z7B5DPKopRt0ekRXO9dMjwW4/sGZx+kjnSPww5SPSTQlB2iTEzmZ4GmeC1gYY+djVnWxhsmsIqWRHB+YyQm0L0Nfe39TDuw= 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 smtp2.mailbox.org (smtp2.mailbox.org [IPv6:2001:67c:2050:b231:465::2]) (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 4ZND1J2ls7z9sc4; Wed, 26 Mar 2025 18:14:44 +0100 (CET) From: Remo Senekowitsch To: Andy Shevchenko , Daniel Scally , Heikki Krogerus , Sakari Ailus , Rob Herring Cc: Dirk Behme , Greg Kroah-Hartman , "Rafael J. Wysocki" , Danilo Krummrich , Saravana Kannan , Miguel Ojeda , Alex Gaynor , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Remo Senekowitsch , linux-kernel@vger.kernel.org, linux-acpi@vger.kernel.org, devicetree@vger.kernel.org, rust-for-linux@vger.kernel.org Subject: [PATCH 01/10] rust: Move property_present to property.rs Date: Wed, 26 Mar 2025 18:13:40 +0100 Message-ID: <20250326171411.590681-2-remo@buenzli.dev> In-Reply-To: <20250326171411.590681-1-remo@buenzli.dev> References: <20250326171411.590681-1-remo@buenzli.dev> Precedence: bulk X-Mailing-List: linux-acpi@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Rspamd-Queue-Id: 4ZND1J2ls7z9sc4 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 APIs has to be duplicated on a fwnode as they are in C. A related discussion can be found on the R4L Zulip[1]. [1] https://rust-for-linux.zulipchat.com/#narrow/channel/288089-General/topic/DS90UB954.20driver.20done.2C.20ready.20to.20upstream.3F/near/505415697 Signed-off-by: Remo Senekowitsch --- rust/helpers/helpers.c | 1 + rust/helpers/property.c | 13 ++++++++ rust/kernel/device.rs | 7 ---- rust/kernel/lib.rs | 1 + rust/kernel/property.rs | 73 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 88 insertions(+), 7 deletions(-) create mode 100644 rust/helpers/property.c create mode 100644 rust/kernel/property.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..c37c74488 --- /dev/null +++ b/rust/helpers/property.c @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include + +struct fwnode_handle *rust_helper_dev_fwnode(struct device *dev) +{ + return dev_fwnode(dev); +} + +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 @@ use crate::{ bindings, - str::CStr, types::{ARef, Opaque}, }; use core::{fmt, ptr}; @@ -181,12 +180,6 @@ unsafe fn printk(&self, klevel: &[u8], msg: fmt::Arguments<'_>) { ) }; } - - /// 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_const(), name.as_char_ptr()) } - } } // 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..b0a4bb63a --- /dev/null +++ b/rust/kernel/property.rs @@ -0,0 +1,73 @@ +// 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 = 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 `&self`. We + // return a reference instead of an `ARef` because `dev_fwnode()` + // doesn't increment the refcount. + 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_const(), 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()) } + } +} From patchwork Wed Mar 26 17:13:41 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Remo Senekowitsch X-Patchwork-Id: 14030392 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 02972202F93; Wed, 26 Mar 2025 17:23:25 +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=1743009807; cv=none; b=olvEwnyS0GIfdoHej4j+/8/hygLy++V/voCKZdABy2jkgZHDFI1BkQ813TsoaIAsij/BJ4NW8/m3c6d/zfJSCrzfOs0Uzr0Rlmg/SccTMA4GR+BWIybIh1LWbIC/7UgPQqhVWglkt1n/sjJ1rXPwZfY3nLbTaDBaVdDAeHnjY4g= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1743009807; c=relaxed/simple; bh=rs9bOE/ilaGLGVSwM1Ef1ijs+11j18W4WaeUmdZRUUo=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=SLZ9WzrwboipooHn3uO5C4vLkKyo+6ypxwPg5T+e2SdopmJq1WGhmCIMmnnRiqfCUu/gUfM6BbmEN721ExRatk3N244PuB6wZj0+Rv7IVPVKvu87rI0uEY6JcAuEJ9dUKOSywSX+uFAoKomHddXzrRN+cTO5mQojW9uH0prakaw= 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 smtp2.mailbox.org (smtp2.mailbox.org [IPv6:2001:67c:2050:b231:465::2]) (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 4ZND1N0Jbqz9smk; Wed, 26 Mar 2025 18:14:48 +0100 (CET) From: Remo Senekowitsch To: Andy Shevchenko , Daniel Scally , Heikki Krogerus , Sakari Ailus , Rob Herring Cc: Dirk Behme , Greg Kroah-Hartman , "Rafael J. Wysocki" , Danilo Krummrich , Saravana Kannan , Miguel Ojeda , Alex Gaynor , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Remo Senekowitsch , linux-kernel@vger.kernel.org, linux-acpi@vger.kernel.org, devicetree@vger.kernel.org, rust-for-linux@vger.kernel.org Subject: [PATCH 02/10] rust: Add an Integer trait Date: Wed, 26 Mar 2025 18:13:41 +0100 Message-ID: <20250326171411.590681-3-remo@buenzli.dev> In-Reply-To: <20250326171411.590681-1-remo@buenzli.dev> References: <20250326171411.590681-1-remo@buenzli.dev> Precedence: bulk X-Mailing-List: linux-acpi@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Rspamd-Queue-Id: 4ZND1N0Jbqz9smk From: "Rob Herring (Arm)" Add an "Integer" trait similar to crate::num::Integer. This is useful for implementing generic methods which operate on different sizes of integers. One example is reading DT/ACPI firmware properties. This was originally proposed by Alice Ryhl[1]. [1] https://lore.kernel.org/rust-for-linux/CAH5fLgiXPZqKpWSSNdx-Ww-E9h2tOLcF3_8Y4C_JQ0eU8EMwFw@mail.gmail.com/ Suggested-by: Alice Ryhl Signed-off-by: Rob Herring (Arm) --- rust/kernel/types.rs | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/rust/kernel/types.rs b/rust/kernel/types.rs index 2bbaab83b..21647b7ba 100644 --- a/rust/kernel/types.rs +++ b/rust/kernel/types.rs @@ -3,10 +3,11 @@ //! Kernel types. use crate::init::{self, PinInit}; +use crate::transmute::{AsBytes, FromBytes}; use core::{ cell::UnsafeCell, marker::{PhantomData, PhantomPinned}, - mem::{ManuallyDrop, MaybeUninit}, + mem::{size_of, ManuallyDrop, MaybeUninit}, ops::{Deref, DerefMut}, ptr::NonNull, }; @@ -553,6 +554,25 @@ pub enum Either { Right(R), } +/// Trait defined for all integer types similar to `crate::num::Integer` +pub trait Integer: FromBytes + AsBytes + Copy { + /// Size of the integer in bytes + const SIZE: usize; +} + +macro_rules! impl_int { + ($($typ:ty),* $(,)?) => {$( + impl Integer for $typ { + const SIZE: usize = size_of::(); + } + )*}; +} + +impl_int! { + u8, u16, u32, u64, usize, + i8, i16, i32, i64, isize, +} + /// Zero-sized type to mark types not [`Send`]. /// /// Add this type as a field to your struct if your type should not be sent to a different task. From patchwork Wed Mar 26 17:13:42 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Remo Senekowitsch X-Patchwork-Id: 14030372 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 943881F892D; Wed, 26 Mar 2025 17:15:01 +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=1743009303; cv=none; b=oN4+cNY924lp9OcTq574Jg2ns64IkmmgUBDxFDJoulsGLapZ08QEkPOxJvartcxqQi5501RzgdUNt2iAEpb5wIJUQF/fpr/3zcIjySLF5jQVA9+UpXX5mHyRKynMC9S1ph1jH1PBITwmjJRTzW9mtXFwQ3LVTNtcEQxjqwsdhgU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1743009303; c=relaxed/simple; bh=p41HMt0OF9U47gVvXP5P+GHZwTOVB4E6ivu+PU66GvE=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=rml4xp2+uicfe9z5EXyVJwr3yLTHlIrCHT7IWXPirHbpepW3xywNuc4DQS9Ss7OUSK2Bq2CMjY0MCpPSKA6lwtKbD8iykC9vJTpxakfUiM08inyC1GZKXreYO6hDf06snjY4ED+cHwGVWnKtfCBkHOzTRDqxtCFzU9FsvUgM34Y= 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 smtp2.mailbox.org (smtp2.mailbox.org [10.196.197.2]) (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 4ZND1R5yXbz9sq9; Wed, 26 Mar 2025 18:14:51 +0100 (CET) From: Remo Senekowitsch To: Andy Shevchenko , Daniel Scally , Heikki Krogerus , Sakari Ailus , Rob Herring Cc: Dirk Behme , Greg Kroah-Hartman , "Rafael J. Wysocki" , Danilo Krummrich , Saravana Kannan , Miguel Ojeda , Alex Gaynor , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Remo Senekowitsch , linux-kernel@vger.kernel.org, linux-acpi@vger.kernel.org, devicetree@vger.kernel.org, rust-for-linux@vger.kernel.org Subject: [PATCH 03/10] device property: Add fwnode_property_read_int_array() Date: Wed, 26 Mar 2025 18:13:42 +0100 Message-ID: <20250326171411.590681-4-remo@buenzli.dev> In-Reply-To: <20250326171411.590681-1-remo@buenzli.dev> References: <20250326171411.590681-1-remo@buenzli.dev> Precedence: bulk X-Mailing-List: linux-acpi@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 The rust bindings for reading device properties has a single implementation supporting differing sizes of integers. The fwnode C API already has a similar interface, but it is not exposed with the fwnode_property_ API. Add the fwnode_property_read_int_array() wrapper. Co-developed-by: Rob Herring (Arm) Signed-off-by: Rob Herring (Arm) Signed-off-by: Remo Senekowitsch --- drivers/base/property.c | 9 +++++---- include/linux/property.h | 2 ++ 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/base/property.c b/drivers/base/property.c index c1392743d..64d5b7055 100644 --- a/drivers/base/property.c +++ b/drivers/base/property.c @@ -292,10 +292,10 @@ int device_property_match_string(const struct device *dev, const char *propname, } EXPORT_SYMBOL_GPL(device_property_match_string); -static int fwnode_property_read_int_array(const struct fwnode_handle *fwnode, - const char *propname, - unsigned int elem_size, void *val, - size_t nval) +int fwnode_property_read_int_array(const struct fwnode_handle *fwnode, + const char *propname, + unsigned int elem_size, void *val, + size_t nval) { int ret; @@ -310,6 +310,7 @@ static int fwnode_property_read_int_array(const struct fwnode_handle *fwnode, return fwnode_call_int_op(fwnode->secondary, property_read_int_array, propname, elem_size, val, nval); } +EXPORT_SYMBOL_GPL(fwnode_property_read_int_array); /** * fwnode_property_read_u8_array - return a u8 array property of firmware node diff --git a/include/linux/property.h b/include/linux/property.h index e214ecd24..441a1ad76 100644 --- a/include/linux/property.h +++ b/include/linux/property.h @@ -57,6 +57,8 @@ bool fwnode_property_present(const struct fwnode_handle *fwnode, const char *propname); bool fwnode_property_read_bool(const struct fwnode_handle *fwnode, const char *propname); +int fwnode_property_read_int_array(const struct fwnode_handle *fwnode, const char *propname, + unsigned int elem_size, void *val, size_t nval); int fwnode_property_read_u8_array(const struct fwnode_handle *fwnode, const char *propname, u8 *val, size_t nval); From patchwork Wed Mar 26 17:13:43 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Remo Senekowitsch X-Patchwork-Id: 14030374 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 6691B1FBC89; Wed, 26 Mar 2025 17:15:04 +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=1743009306; cv=none; b=QD9f88Vt6iS7VFUH2eAuRYTYYCCC1XmGaDajl1nwmRe7mVJPWLLBBf1sXuVF5BZJ9nKg/3k7k6Rx9D7XB8aX/grHCUlZu0uBXxhkB7pferjUms1g5eavX0nDr9tOU7FAayvbLA0GwnJxPhUT4ws55R/pFnxjkGQfmso1K7ZQvw4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1743009306; c=relaxed/simple; bh=g8jiBgUmirygyBk4J0uJSx25wPYIMJmKBYqNb9X1hIM=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=C7q1Elgn7V48Wn4s5AGgN50BIQ7Pzfy0Sp8jfsgpfZuNKOL2Yk7CO6iRxckmDZ0NodhUMrAEMbK/sTolkW3pA457I/23ge3JmQ1UebaQDSwwpi+YPngGQCDN7B/shQ+gW8qbMlyhd0vkvd5h7V9YTPcpdeYjHqKOveHLYO+c2ao= 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 smtp2.mailbox.org (smtp2.mailbox.org [10.196.197.2]) (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 4ZND1W4jmjz9thB; Wed, 26 Mar 2025 18:14:55 +0100 (CET) From: Remo Senekowitsch To: Andy Shevchenko , Daniel Scally , Heikki Krogerus , Sakari Ailus , Rob Herring Cc: Dirk Behme , Greg Kroah-Hartman , "Rafael J. Wysocki" , Danilo Krummrich , Saravana Kannan , Miguel Ojeda , Alex Gaynor , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Remo Senekowitsch , linux-kernel@vger.kernel.org, linux-acpi@vger.kernel.org, devicetree@vger.kernel.org, rust-for-linux@vger.kernel.org Subject: [PATCH 04/10] rust: Add bindings for reading device properties Date: Wed, 26 Mar 2025 18:13:43 +0100 Message-ID: <20250326171411.590681-5-remo@buenzli.dev> In-Reply-To: <20250326171411.590681-1-remo@buenzli.dev> References: <20250326171411.590681-1-remo@buenzli.dev> Precedence: bulk X-Mailing-List: linux-acpi@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 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 sizes of properties (e.g. u8, u16, u32). Co-developed-by: Rob Herring (Arm) Signed-off-by: Rob Herring (Arm) Signed-off-by: Remo Senekowitsch --- rust/kernel/property.rs | 153 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 151 insertions(+), 2 deletions(-) diff --git a/rust/kernel/property.rs b/rust/kernel/property.rs index b0a4bb63a..4756ea766 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) -use core::ptr; +use core::{ffi::c_void, mem::MaybeUninit, ptr}; -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::{Integer, Opaque}, +}; impl Device { /// Obtain the fwnode corresponding to the device. @@ -26,6 +34,41 @@ fn fwnode(&self) -> &FwNode { pub fn property_present(&self, name: &CStr) -> bool { self.fwnode().property_present(name) } + + /// Returns if a firmware property `name` is true or false + 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 string property `name` + pub fn property_read_string(&self, name: &CStr) -> Result { + self.fwnode().property_read_string(name) + } + + /// Returns the index of matching string `match_str` for firmware string 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 scalar value + pub fn property_read(&self, name: &CStr) -> Result { + self.fwnode().property_read(name) + } + + /// Returns firmware property `name` integer array values + pub fn property_read_array(&self, name: &CStr) -> Result<[T; N]> { + self.fwnode().property_read_array(name) + } + + /// Returns firmware property `name` integer array values in a KVec + pub fn property_read_array_vec(&self, 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) -> Result { + self.fwnode().property_count_elem::(name) + } } /// A reference-counted fwnode_handle. @@ -57,6 +100,112 @@ 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_const(), name.as_char_ptr()) } } + + /// Returns if a firmware property `name` is true or false + 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.as_char_ptr()) } + } + + /// Returns the index of matching string `match_str` for firmware string property `name` + pub fn property_read_string(&self, name: &CStr) -> Result { + let mut str = core::ptr::null_mut(); + let pstr: *mut _ = &mut str; + + // SAFETY: `name` is non-null and null-terminated. `self.as_raw` is + // valid because `self` is valid. + let ret = unsafe { + bindings::fwnode_property_read_string(self.as_raw(), name.as_char_ptr(), pstr as _) + }; + to_result(ret)?; + + // SAFETY: `pstr` contains a non-null ptr on success + let str = unsafe { CStr::from_char_ptr(*pstr) }; + Ok(str.try_into()?) + } + + /// Returns the index of matching string `match_str` for firmware string 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 = 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 scalar value + pub fn property_read(&self, name: &CStr) -> Result { + let val: [_; 1] = Self::property_read_array(self, name)?; + Ok(val[0]) + } + + /// Returns firmware property `name` integer array values + pub fn property_read_array(&self, name: &CStr) -> Result<[T; N]> { + let val: [MaybeUninit; N] = [const { MaybeUninit::uninit() }; N]; + + // SAFETY: `name` is non-null and null-terminated. `self.as_raw` is valid + // because `self` is valid. `val.as_ptr` is valid because `val` is valid. + let ret = unsafe { + bindings::fwnode_property_read_int_array( + self.as_raw(), + name.as_char_ptr(), + T::SIZE as u32, + val.as_ptr() as *mut c_void, + val.len(), + ) + }; + to_result(ret)?; + + // SAFETY: `val` is always initialized when + // fwnode_property_read_int_array is successful. + Ok(val.map(|v| unsafe { v.assume_init() })) + } + + /// Returns firmware property `name` integer array values in a KVec + pub fn property_read_array_vec(&self, name: &CStr, len: usize) -> Result> { + let mut val: KVec = KVec::with_capacity(len, GFP_KERNEL)?; + + // SAFETY: `name` is non-null and null-terminated. `self.as_raw` is valid + // because `self` is valid. `val.as_ptr` is valid because `val` is valid. + to_result(unsafe { + bindings::fwnode_property_read_int_array( + self.as_raw(), + name.as_char_ptr(), + T::SIZE as u32, + val.as_ptr() as *mut c_void, + len, + ) + })?; + + // SAFETY: fwnode_property_read_int_array() writes exactly `len` entries on success + unsafe { val.set_len(len) } + Ok(val) + } + + /// Returns integer array length for firmware property `name` + pub fn property_count_elem(&self, name: &CStr) -> Result { + // SAFETY: `name` is non-null and null-terminated. `self.as_raw` is valid + // because `self` is valid. Passing null pointer buffer is valid to obtain + // the number of elements in the property array. + let ret = unsafe { + bindings::fwnode_property_read_int_array( + self.as_raw(), + name.as_char_ptr(), + T::SIZE as u32, + ptr::null_mut(), + 0, + ) + }; + to_result(ret)?; + Ok(ret as usize) + } } // SAFETY: Instances of `FwNode` are always reference-counted. From patchwork Wed Mar 26 17:13:44 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Remo Senekowitsch X-Patchwork-Id: 14030373 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 C30AA191461; Wed, 26 Mar 2025 17:15:02 +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=1743009304; cv=none; b=QNyoc2DTwMcUhWNDVwjMF2htPIxZk+n5jfg56Kdcg9GlLBjfRxbI0Xss3WZxelVU3gbxs/EPdIBZp3+6BBEUCuFhtwjUHvzw3mZ7A/1UND/HXa95wCJclyTEAaBFkeitnjZUFsIyi+5QXxz554pltPmougN1uCYEvw158/IKv8I= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1743009304; c=relaxed/simple; bh=JKX2V1B64mbQVg8EwAHLfQBMzztwg14JNlI2w/Y5OSo=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Yg0hE1x2TyqlrvUEQF8QklykPuCb0JYbVTG/CzGtv5Rj33XewfQPo+n1A51TnxGpMu8g9ny5aIXi4uYvYtZdj+xCpLK/oRyXS2uokoCcdyCKkoD8at9A0sHrfrRMNboYhNA3sKA6Ha5otSyz7IAciflHuB2Y0JZRvNBYfIV1vKA= 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 smtp2.mailbox.org (smtp2.mailbox.org [10.196.197.2]) (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 4ZND1Z6Jd4z9v5v; Wed, 26 Mar 2025 18:14:58 +0100 (CET) From: Remo Senekowitsch To: Andy Shevchenko , Daniel Scally , Heikki Krogerus , Sakari Ailus , Rob Herring Cc: Dirk Behme , Greg Kroah-Hartman , "Rafael J. Wysocki" , Danilo Krummrich , Saravana Kannan , Miguel Ojeda , Alex Gaynor , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Remo Senekowitsch , linux-kernel@vger.kernel.org, linux-acpi@vger.kernel.org, devicetree@vger.kernel.org, rust-for-linux@vger.kernel.org Subject: [PATCH 05/10] rust: Read properties via single generic method Date: Wed, 26 Mar 2025 18:13:44 +0100 Message-ID: <20250326171411.590681-6-remo@buenzli.dev> In-Reply-To: <20250326171411.590681-1-remo@buenzli.dev> References: <20250326171411.590681-1-remo@buenzli.dev> Precedence: bulk X-Mailing-List: linux-acpi@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 This can probably be squashed into its parent. I haven't done that yet in case there is something wrong with the generic approach and the previous one is preferred. Signed-off-by: Remo Senekowitsch --- rust/kernel/property.rs | 171 +++++++++++++++++++++++----------------- 1 file changed, 99 insertions(+), 72 deletions(-) diff --git a/rust/kernel/property.rs b/rust/kernel/property.rs index 4756ea766..4a03008ce 100644 --- a/rust/kernel/property.rs +++ b/rust/kernel/property.rs @@ -35,31 +35,11 @@ pub fn property_present(&self, name: &CStr) -> bool { self.fwnode().property_present(name) } - /// Returns if a firmware property `name` is true or false - 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 string property `name` - pub fn property_read_string(&self, name: &CStr) -> Result { - self.fwnode().property_read_string(name) - } - /// Returns the index of matching string `match_str` for firmware string 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 scalar value - pub fn property_read(&self, name: &CStr) -> Result { - self.fwnode().property_read(name) - } - - /// Returns firmware property `name` integer array values - pub fn property_read_array(&self, name: &CStr) -> Result<[T; N]> { - self.fwnode().property_read_array(name) - } - /// Returns firmware property `name` integer array values in a KVec pub fn property_read_array_vec(&self, name: &CStr, len: usize) -> Result> { self.fwnode().property_read_array_vec(name, len) @@ -69,6 +49,11 @@ pub fn property_read_array_vec(&self, name: &CStr, len: usize) -> Re pub fn property_count_elem(&self, name: &CStr) -> Result { self.fwnode().property_count_elem::(name) } + + /// Returns firmware property `name` integer scalar value + pub fn property_read(&self, name: &CStr) -> Result { + self.fwnode().property_read(name) + } } /// A reference-counted fwnode_handle. @@ -101,30 +86,6 @@ pub fn property_present(&self, name: &CStr) -> bool { unsafe { bindings::fwnode_property_present(self.as_raw().cast_const(), name.as_char_ptr()) } } - /// Returns if a firmware property `name` is true or false - 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.as_char_ptr()) } - } - - /// Returns the index of matching string `match_str` for firmware string property `name` - pub fn property_read_string(&self, name: &CStr) -> Result { - let mut str = core::ptr::null_mut(); - let pstr: *mut _ = &mut str; - - // SAFETY: `name` is non-null and null-terminated. `self.as_raw` is - // valid because `self` is valid. - let ret = unsafe { - bindings::fwnode_property_read_string(self.as_raw(), name.as_char_ptr(), pstr as _) - }; - to_result(ret)?; - - // SAFETY: `pstr` contains a non-null ptr on success - let str = unsafe { CStr::from_char_ptr(*pstr) }; - Ok(str.try_into()?) - } - /// Returns the index of matching string `match_str` for firmware string 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 @@ -140,34 +101,6 @@ pub fn property_match_string(&self, name: &CStr, match_str: &CStr) -> Result(&self, name: &CStr) -> Result { - let val: [_; 1] = Self::property_read_array(self, name)?; - Ok(val[0]) - } - - /// Returns firmware property `name` integer array values - pub fn property_read_array(&self, name: &CStr) -> Result<[T; N]> { - let val: [MaybeUninit; N] = [const { MaybeUninit::uninit() }; N]; - - // SAFETY: `name` is non-null and null-terminated. `self.as_raw` is valid - // because `self` is valid. `val.as_ptr` is valid because `val` is valid. - let ret = unsafe { - bindings::fwnode_property_read_int_array( - self.as_raw(), - name.as_char_ptr(), - T::SIZE as u32, - val.as_ptr() as *mut c_void, - val.len(), - ) - }; - to_result(ret)?; - - // SAFETY: `val` is always initialized when - // fwnode_property_read_int_array is successful. - Ok(val.map(|v| unsafe { v.assume_init() })) - } - /// Returns firmware property `name` integer array values in a KVec pub fn property_read_array_vec(&self, name: &CStr, len: usize) -> Result> { let mut val: KVec = KVec::with_capacity(len, GFP_KERNEL)?; @@ -206,6 +139,38 @@ pub fn property_count_elem(&self, name: &CStr) -> Result { 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, integers and arrays + /// of 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 interpreted + /// 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 = dev.fwnode(); + /// let b: bool = fwnode.property_read("some-bool")?; + /// let s: CString = fwnode.property_read("some-str")?; + /// } + /// ``` + pub fn property_read(&self, name: &CStr) -> Result { + T::read(&self, name) + } } // SAFETY: Instances of `FwNode` are always reference-counted. @@ -220,3 +185,65 @@ unsafe fn dec_ref(obj: ptr::NonNull) { unsafe { bindings::fwnode_handle_put(obj.cast().as_ptr()) } } } + +/// Implemented for all types that can be read as properties. +/// +/// This is used to make [FwNode::property_read] generic over the type of +/// property being read. +pub trait Property: Sized { + /// Used to make [FwNode::property_read] generic. + fn read(fwnode: &FwNode, name: &CStr) -> Result; +} + +impl Property for bool { + fn read(fwnode: &FwNode, name: &CStr) -> Result { + // SAFETY: `name` is non-null and null-terminated. `fwnode.as_raw` is valid + // because `fwnode` is valid. + Ok(unsafe { bindings::fwnode_property_read_bool(fwnode.as_raw(), name.as_char_ptr()) }) + } +} +impl Property for CString { + fn read(fwnode: &FwNode, name: &CStr) -> Result { + let mut str: *mut u8 = ptr::null_mut(); + let pstr: *mut _ = &mut str; + + // SAFETY: `name` is non-null and null-terminated. `fwnode.as_raw` is + // valid because `fwnode` is valid. + let ret = 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 = unsafe { CStr::from_char_ptr(*pstr) }; + Ok(str.try_into()?) + } +} +impl Property for [T; N] { + fn read(fwnode: &FwNode, name: &CStr) -> Result { + let mut val: [MaybeUninit; N] = [const { MaybeUninit::uninit() }; N]; + + // SAFETY: `name` is non-null and null-terminated. `fwnode.as_raw` is valid + // because `fwnode` is valid. `val.as_ptr` is valid because `val` is valid. + let ret = unsafe { + bindings::fwnode_property_read_int_array( + fwnode.as_raw(), + name.as_char_ptr(), + T::SIZE as u32, + val.as_mut_ptr().cast(), + val.len(), + ) + }; + to_result(ret)?; + + // SAFETY: `val` is always initialized when + // fwnode_property_read_int_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] = <[T; 1] as Property>::read(fwnode, name)?; + Ok(val[0]) + } +} From patchwork Wed Mar 26 17:13:45 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Remo Senekowitsch X-Patchwork-Id: 14030375 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 528D61F91C7; Wed, 26 Mar 2025 17:15:06 +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=1743009307; cv=none; b=OTFyzo56dmtg1T9Io4UPYZLigFCVC2yMMcG/2z9EVZwbSkhhPPKgj4mNzUwQsRTPZ1Yvgkevnbao96xKQisq3glPgI0/ucjO+s1XaVxR1jOyofrf6+oEgCn6uSplwVRSESw7T/cwgjWZy4N+xnLrpbCQ7iitRZ4C5WSmrNGPocs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1743009307; c=relaxed/simple; bh=8ycTyKAu8Ysd62/+z/c9faBury4UhlNST+28a4jGdGs=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=h9Cq8EsYtivW5oPc9FEaYRROLtp6Cgv0rrr/LJBnU+kA7z5cQCJV/xWpM2vBMzkJ5nHurwwZq6BJcCDOP1pehd2nxF89GZsUrsV7vPR1R2nnOhYbiH0wrU3AXCGudpg3TExZdFmBnP86wtkpPN2dWEe0tvWHdoAfl42z7b2O0NY= 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 smtp2.mailbox.org (smtp2.mailbox.org [10.196.197.2]) (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 4ZND1f4Mnjz9tct; Wed, 26 Mar 2025 18:15:02 +0100 (CET) From: Remo Senekowitsch To: Andy Shevchenko , Daniel Scally , Heikki Krogerus , Sakari Ailus , Rob Herring Cc: Dirk Behme , Greg Kroah-Hartman , "Rafael J. Wysocki" , Danilo Krummrich , Saravana Kannan , Miguel Ojeda , Alex Gaynor , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Remo Senekowitsch , linux-kernel@vger.kernel.org, linux-acpi@vger.kernel.org, devicetree@vger.kernel.org, rust-for-linux@vger.kernel.org Subject: [PATCH 06/10] rust: property: Add child accessor and iterator Date: Wed, 26 Mar 2025 18:13:45 +0100 Message-ID: <20250326171411.590681-7-remo@buenzli.dev> In-Reply-To: <20250326171411.590681-1-remo@buenzli.dev> References: <20250326171411.590681-1-remo@buenzli.dev> Precedence: bulk X-Mailing-List: linux-acpi@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Signed-off-by: Remo Senekowitsch --- rust/kernel/property.rs | 57 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/rust/kernel/property.rs b/rust/kernel/property.rs index 4a03008ce..dc927ad93 100644 --- a/rust/kernel/property.rs +++ b/rust/kernel/property.rs @@ -13,7 +13,7 @@ error::{to_result, Result}, prelude::*, str::{CStr, CString}, - types::{Integer, Opaque}, + types::{ARef, Integer, Opaque}, }; impl Device { @@ -54,6 +54,16 @@ pub fn property_count_elem(&self, name: &CStr) -> Result { pub fn property_read(&self, name: &CStr) -> Result { 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() + } } /// A reference-counted fwnode_handle. @@ -75,6 +85,11 @@ pub fn property_read(&self, name: &CStr) -> Result { pub struct FwNode(Opaque); impl FwNode { + // SAFETY: `raw` must have its refcount incremented. + unsafe fn from_raw(raw: *mut bindings::fwnode_handle) -> ARef { + 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() @@ -171,6 +186,46 @@ pub fn property_count_elem(&self, name: &CStr) -> Result { pub fn property_read(&self, name: &CStr) -> Result { T::read(&self, name) } + + /// 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 = + 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 refcount 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> = None; + + core::iter::from_fn(move || { + let prev_ptr = match prev.take() { + None => ptr::null_mut(), + Some(prev) => { + // 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 = ARef::into_raw(prev); + prev.as_ptr().cast() + } + }; + let next = unsafe { bindings::fwnode_get_next_child_node(self.as_raw(), prev_ptr) }; + if next.is_null() { + return None; + } + // SAFETY: `fwnode_get_next_child_node` returns a pointer with + // refcount incremented. + let next = unsafe { FwNode::from_raw(next) }; + prev = Some(next.clone()); + Some(next) + }) + } } // SAFETY: Instances of `FwNode` are always reference-counted. From patchwork Wed Mar 26 17:13:46 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Remo Senekowitsch X-Patchwork-Id: 14030376 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 680981F891C; Wed, 26 Mar 2025 17:15:10 +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=1743009312; cv=none; b=Z7z1Yultm3TycINNOVF/kpkU1N+gn3AqIxFctUk3LytdRoiVZ2+Tsgs+uFKSFoP432DyQnRQMTrnb4hSopG2hfBUdyZMbQgk4SPHhLgeZ8kLcN+BaL2zvj3e+fkZyfHTIVHk2BAwP4PqBzTdXKd2YsspSvOWo6XahBbTQrIyLvg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1743009312; c=relaxed/simple; bh=g3w3UNJdidUDKl+iR6KhiWQ5a6E0oggHsoN9a3L+pGs=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=uV8nyO1P2clAZpibhdsCFYYOYWZEtalxms7peD/6fdOloSQtWczjCg40FRVh0gHeX97WJtGL0Dkl6wydrym1A9tCRHnDL2VkiBRR0OMwmICvjrUmYelFCaMM32T5mPrs0r1aUdnplFsQ+MU4tfnJj9eiUyXYBNNO+fMF7+NpOrE= 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 smtp2.mailbox.org (smtp2.mailbox.org [10.196.197.2]) (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 4ZND1k25Crz9sc4; Wed, 26 Mar 2025 18:15:06 +0100 (CET) From: Remo Senekowitsch To: Andy Shevchenko , Daniel Scally , Heikki Krogerus , Sakari Ailus , Rob Herring Cc: Dirk Behme , Greg Kroah-Hartman , "Rafael J. Wysocki" , Danilo Krummrich , Saravana Kannan , Miguel Ojeda , Alex Gaynor , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Remo Senekowitsch , linux-kernel@vger.kernel.org, linux-acpi@vger.kernel.org, devicetree@vger.kernel.org, rust-for-linux@vger.kernel.org Subject: [PATCH 07/10] rust: Add arrayvec Date: Wed, 26 Mar 2025 18:13:46 +0100 Message-ID: <20250326171411.590681-8-remo@buenzli.dev> In-Reply-To: <20250326171411.590681-1-remo@buenzli.dev> References: <20250326171411.590681-1-remo@buenzli.dev> Precedence: bulk X-Mailing-List: linux-acpi@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 This patch is basically a proof of concept intendend to gather feedback about how to do this properly. Normally I would want to use the crate from crates.io[1], but that's not an option in the kernel. We could also vendor the entire source code of arrayvec. I'm not sure if people will be happy with that. [1] https://crates.io/crates/arrayvec Signed-off-by: Remo Senekowitsch --- rust/kernel/arrayvec.rs | 81 +++++++++++++++++++++++++++++++++++++++++ rust/kernel/lib.rs | 1 + 2 files changed, 82 insertions(+) create mode 100644 rust/kernel/arrayvec.rs diff --git a/rust/kernel/arrayvec.rs b/rust/kernel/arrayvec.rs new file mode 100644 index 000000000..041e7dcce --- /dev/null +++ b/rust/kernel/arrayvec.rs @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Provides [ArrayVec], a stack-allocated vector with static capacity. + +use core::mem::MaybeUninit; + +/// A stack-allocated vector with statically fixed capacity. +/// +/// This can be useful to avoid heap allocation and still ensure safety where a +/// small but dynamic number of elements is needed. +/// +/// For example, consider a function that returns a variable number of values, +/// but no more than 8. In C, one might achieve this by passing a pointer to +/// a stack-allocated array as an out-parameter and making the function return +/// the actual number of elements. This is not safe, because nothing prevents +/// the caller from reading elements from the array that weren't actually +/// initialized by the function. `ArrayVec` solves this problem, users are +/// prevented from accessing uninitialized elements. +/// +/// This basically exists already (in a much more mature form) on crates.io: +/// +#[derive(Debug)] +pub struct ArrayVec { + array: [core::mem::MaybeUninit; N], + len: usize, +} + +impl ArrayVec { + /// Adds a new element to the end of the vector. + /// + /// # Panics + /// + /// Panics if the vector is already full. + pub fn push(&mut self, elem: T) { + if self.len == N { + panic!("OOM") + } + self.array[self.len] = MaybeUninit::new(elem); + self.len += 1; + } + + /// Returns the length of the vector. + pub fn len(&self) -> usize { + self.len + } +} + +impl Default for ArrayVec { + fn default() -> Self { + Self { + array: [const { MaybeUninit::uninit() }; N], + len: 0, + } + } +} + +impl AsRef<[T]> for ArrayVec { + fn as_ref(&self) -> &[T] { + // SAFETY: As per the type invariant, all elements at index < self.len + // are initialized. + unsafe { core::mem::transmute(&self.array[..self.len]) } + } +} + +impl AsMut<[T]> for ArrayVec { + fn as_mut(&mut self) -> &mut [T] { + // SAFETY: As per the type invariant, all elements at index < self.len + // are initialized. + unsafe { core::mem::transmute(&mut self.array[..self.len]) } + } +} + +impl Drop for ArrayVec { + fn drop(&mut self) { + unsafe { + let slice: &mut [T] = + core::slice::from_raw_parts_mut(self.array.as_mut_ptr().cast(), self.len); + core::ptr::drop_in_place(slice); + } + } +} diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index ca233fd20..0777f7a42 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -36,6 +36,7 @@ pub use ffi; pub mod alloc; +pub mod arrayvec; #[cfg(CONFIG_BLOCK)] pub mod block; #[doc(hidden)] From patchwork Wed Mar 26 17:13:47 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Remo Senekowitsch X-Patchwork-Id: 14030377 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 E73F61E1E15; Wed, 26 Mar 2025 17:15: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=1743009321; cv=none; b=KjUybphXQHWGQrZyw5PFoWt/E4TF1CHcPVGhrHYb1aqN+ZZx0jsWYNaZbVi5kMqOFB97FJoFugPT/sSmDJqyOCkgdG8yex5ueD6FMZlNHZcNPiwc/QZnIPHsIxAQvCFkXczi4iFV3enqPysePlVbHN98WyGbt0W+YkOi+sZYeYM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1743009321; c=relaxed/simple; bh=aIm/JGb5KR23/j5XE+CLIq3kVoApEZuw53qSIlgtvmY=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=sa0F6JLG6tagPiyBtXEdYQmhzW/whm0B5G/me54fuwvW9ipzAg16V44gZW82gJXndhbNR/sP3vSCzjTv1ISlyj/cXFifCY9ptVPkSD0twJ7DJr9yl7iaeOo8oEoA30ZUBiy4IOKIAtoLZ+ib5fD2h7n57y9/ZGidIgFMwXF1V6Y= 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 smtp2.mailbox.org (smtp2.mailbox.org [10.196.197.2]) (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 4ZND1p2XCfz9ssD; Wed, 26 Mar 2025 18:15:10 +0100 (CET) From: Remo Senekowitsch To: Andy Shevchenko , Daniel Scally , Heikki Krogerus , Sakari Ailus , Rob Herring Cc: Dirk Behme , Greg Kroah-Hartman , "Rafael J. Wysocki" , Danilo Krummrich , Saravana Kannan , Miguel Ojeda , Alex Gaynor , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Remo Senekowitsch , linux-kernel@vger.kernel.org, linux-acpi@vger.kernel.org, devicetree@vger.kernel.org, rust-for-linux@vger.kernel.org Subject: [PATCH 08/10] rust: property: Add property_get_reference_args Date: Wed, 26 Mar 2025 18:13:47 +0100 Message-ID: <20250326171411.590681-9-remo@buenzli.dev> In-Reply-To: <20250326171411.590681-1-remo@buenzli.dev> References: <20250326171411.590681-1-remo@buenzli.dev> Precedence: bulk X-Mailing-List: linux-acpi@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Signed-off-by: Remo Senekowitsch --- rust/kernel/property.rs | 63 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/rust/kernel/property.rs b/rust/kernel/property.rs index dc927ad93..f1d0a33ba 100644 --- a/rust/kernel/property.rs +++ b/rust/kernel/property.rs @@ -8,6 +8,7 @@ use crate::{ alloc::KVec, + arrayvec::ArrayVec, bindings, device::Device, error::{to_result, Result}, @@ -64,6 +65,20 @@ 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<( + ARef, + ArrayVec<{ bindings::NR_FWNODE_REFERENCE_ARGS as usize }, u64>, + )> { + self.fwnode() + .property_get_reference_args(prop, nargs, index) + } } /// A reference-counted fwnode_handle. @@ -226,6 +241,45 @@ 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<( + ARef, + ArrayVec<{ bindings::NR_FWNODE_REFERENCE_ARGS as usize }, u64>, + )> { + let mut out_args = bindings::fwnode_reference_args::default(); + + let (nargs_prop, nargs) = match nargs { + NArgs::Prop(nargs_prop) => (nargs_prop.as_char_ptr(), 0), + NArgs::N(nargs) => (ptr::null(), nargs), + }; + + let ret = unsafe { + bindings::fwnode_property_get_reference_args( + self.0.get(), + prop.as_char_ptr(), + nargs_prop, + nargs, + index, + &mut out_args, + ) + }; + to_result(ret)?; + + let node = unsafe { FwNode::from_raw(out_args.fwnode) }; + let mut args = ArrayVec::default(); + + for i in 0..out_args.nargs { + args.push(out_args.args[i as usize]); + } + + Ok((node, args)) + } } // SAFETY: Instances of `FwNode` are always reference-counted. @@ -302,3 +356,12 @@ fn read(fwnode: &FwNode, name: &CStr) -> Result { Ok(val[0]) } } + +/// 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), +} From patchwork Wed Mar 26 17:13:48 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Remo Senekowitsch X-Patchwork-Id: 14030379 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 7B7321FECC2; Wed, 26 Mar 2025 17:15:23 +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=1743009325; cv=none; b=p0IRSoqBVz4QacW+WXVLuh6HJK/f2n/ymwR995PvB0iZQsleSdHVdsVfC6vg+Sx0fxwfkvopKcHS2tk+iQ2E0zZTx5SOjeUBW+01jJE79EIFYdZRmjlWpQAus9nKrPCmARbp6S+JJd9aXhqw42l4obGV2ihQl3yLmr38VH4+suw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1743009325; c=relaxed/simple; bh=s56M1HA3ha2K/OQmzA+ilqI8odqNZ9uJMiyFYK8oGPw=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=gpnIlmwthZgwYdTYUGQ+rXNOIzQJJiUcTjaSKt7zGAiI5HRnF4DMmCvyTRSXgGfNOkcEE/30pggeik1O2u5eNxFQhJGu7FfSuYMcoQJYrbY1q0YqAmTa7mBqlPhVkdXsBR9p/ggV3jLUWp9cJmenxOiAueKjf8sKDhWbzwvr7FM= 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 smtp2.mailbox.org (smtp2.mailbox.org [IPv6:2001:67c:2050:b231:465::2]) (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 4ZND1s6nVHz9tFH; Wed, 26 Mar 2025 18:15:13 +0100 (CET) From: Remo Senekowitsch To: Andy Shevchenko , Daniel Scally , Heikki Krogerus , Sakari Ailus , Rob Herring Cc: Dirk Behme , Greg Kroah-Hartman , "Rafael J. Wysocki" , Danilo Krummrich , Saravana Kannan , Miguel Ojeda , Alex Gaynor , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Remo Senekowitsch , linux-kernel@vger.kernel.org, linux-acpi@vger.kernel.org, devicetree@vger.kernel.org, rust-for-linux@vger.kernel.org Subject: [PATCH 09/10] rust: property: Add PropertyGuard Date: Wed, 26 Mar 2025 18:13:48 +0100 Message-ID: <20250326171411.590681-10-remo@buenzli.dev> In-Reply-To: <20250326171411.590681-1-remo@buenzli.dev> References: <20250326171411.590681-1-remo@buenzli.dev> Precedence: bulk X-Mailing-List: linux-acpi@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Rspamd-Queue-Id: 4ZND1s6nVHz9tFH Forcing users to specify whether a property is supposed to be required or not allows us to move error logging of missing required properties into core, preventing a lot of boilerplate in drivers. Signed-off-by: Remo Senekowitsch --- rust/kernel/property.rs | 146 ++++++++++++++++++++++++++++++++++------ 1 file changed, 127 insertions(+), 19 deletions(-) diff --git a/rust/kernel/property.rs b/rust/kernel/property.rs index f1d0a33ba..e4f0e5f97 100644 --- a/rust/kernel/property.rs +++ b/rust/kernel/property.rs @@ -42,7 +42,11 @@ pub fn property_match_string(&self, name: &CStr, match_str: &CStr) -> Result(&self, name: &CStr, len: usize) -> Result> { + pub fn property_read_array_vec<'fwnode, 'name, T: Integer>( + &'fwnode self, + name: &'name CStr, + len: usize, + ) -> Result>> { self.fwnode().property_read_array_vec(name, len) } @@ -52,12 +56,18 @@ pub fn property_count_elem(&self, name: &CStr) -> Result { } /// Returns firmware property `name` integer scalar value - pub fn property_read(&self, name: &CStr) -> Result { + pub fn property_read<'fwnode, 'name, T: Property>( + &'fwnode self, + name: &'name CStr, + ) -> 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> { + pub fn get_child_by_name<'fwnode, 'name>( + &'fwnode self, + name: &'name CStr, + ) -> PropertyGuard<'fwnode, 'name, ARef> { self.fwnode().get_child_by_name(name) } @@ -132,12 +142,16 @@ pub fn property_match_string(&self, name: &CStr, match_str: &CStr) -> Result(&self, name: &CStr, len: usize) -> Result> { + pub fn property_read_array_vec<'fwnode, 'name, T: Integer>( + &'fwnode self, + name: &'name CStr, + len: usize, + ) -> Result>> { let mut val: KVec = KVec::with_capacity(len, GFP_KERNEL)?; // SAFETY: `name` is non-null and null-terminated. `self.as_raw` is valid // because `self` is valid. `val.as_ptr` is valid because `val` is valid. - to_result(unsafe { + let err = unsafe { bindings::fwnode_property_read_int_array( self.as_raw(), name.as_char_ptr(), @@ -145,11 +159,19 @@ pub fn property_read_array_vec(&self, name: &CStr, len: usize) -> Re val.as_ptr() as *mut c_void, len, ) - })?; - - // SAFETY: fwnode_property_read_int_array() writes exactly `len` entries on success - unsafe { val.set_len(len) } - Ok(val) + }; + let res = if err < 0 { + Err(Error::from_errno(err)) + } else { + // SAFETY: fwnode_property_read_int_array() writes exactly `len` 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` @@ -194,24 +216,42 @@ pub fn property_count_elem(&self, name: &CStr) -> Result { /// # use crate::{device::Device, types::CString}; /// fn examples(dev: &Device) -> Result { /// let fwnode = dev.fwnode(); - /// let b: bool = fwnode.property_read("some-bool")?; - /// let s: CString = fwnode.property_read("some-str")?; + /// let b: bool = fwnode.property_read("some-bool").required()?; + /// if let Some(s) = fwnode.property_read::("some-str").optional() { + /// // ... + /// } /// } /// ``` - pub fn property_read(&self, name: &CStr) -> Result { - T::read(&self, name) + 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, + } } /// Returns first matching named child node handle. - pub fn get_child_by_name(&self, name: &CStr) -> Option> { + pub fn get_child_by_name<'fwnode, 'name>( + &'fwnode self, + name: &'name CStr, + ) -> PropertyGuard<'fwnode, 'name, ARef> { // SAFETY: `self` and `name` are valid. let child = unsafe { bindings::fwnode_get_named_child_node(self.as_raw(), name.as_char_ptr()) }; - if child.is_null() { - return None; + let res = if child.is_null() { + Err(ENOENT) + } else { + // SAFETY: `fwnode_get_named_child_node` returns a pointer with refcount incremented. + Ok(unsafe { Self::from_raw(child) }) + }; + PropertyGuard { + inner: res, + fwnode: self, + name, } - // SAFETY: `fwnode_get_named_child_node` returns a pointer with refcount incremented. - Some(unsafe { Self::from_raw(child) }) } /// Returns an iterator over a node's children. @@ -365,3 +405,71 @@ pub enum NArgs<'a> { /// 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 +/// [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" case. + 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-associated + // logging. + // TODO: Are we allowed to do this? The field `fwnode_handle.dev` + // 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/linux/fwnode.h#L51 + // SAFETY: According to the invariant of FwNode, it is valid. + let dev = unsafe { (*self.fwnode.as_raw()).dev }; + + if dev.is_null() { + pr_err!("property '{}' is missing\n", self.name); + } else { + // SAFETY: If dev is not null, it points to a valid device. + let dev: &Device = unsafe { &*dev.cast() }; + dev_err!(dev, "property '{}' is missing\n", 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 property. + /// 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() + } +} From patchwork Wed Mar 26 17:13:49 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Remo Senekowitsch X-Patchwork-Id: 14030378 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 6D4AA1F872C; Wed, 26 Mar 2025 17:15:21 +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=1743009323; cv=none; b=ainOMWoDTCg0v7+6PYSjXCgxIu8yIzkGTQFuEWdkCOIZWKesMyDqWeeoUIXpQDPmSW0X6bYRTH0nnLIXzJCEQb64tfopWrGLkfrX0MlGmLU5/Mf9X6ebytgevXbzsNCLxRpOyx6IO0fuwx4ixuLpnoOac6RZ+geFHf05NFlihNU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1743009323; c=relaxed/simple; bh=GMrrzdfst3UrCx5Hw9DPHzzI0KYSCppNF/wVPqqk5rY=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=VLcNQkda8C7a7roh0gQArmEUP899stN1Og7KCENFQgoQ8fl7A9yY3ssibhfqkWwzfRGHUWW1a4dHSyTcVhg20ra7jE88HWLA+bzkBDZs3RR6kwGpxosOJ4a52Rxvhj78y+OMoRNmugb0VNOXHlPOWqN1vmG1tIC28dxd2yN9zzA= 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 smtp2.mailbox.org (smtp2.mailbox.org [IPv6:2001:67c:2050:b231:465::2]) (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 4ZND1x5lL5z9sq9; Wed, 26 Mar 2025 18:15:17 +0100 (CET) From: Remo Senekowitsch To: Andy Shevchenko , Daniel Scally , Heikki Krogerus , Sakari Ailus , Rob Herring Cc: Dirk Behme , Greg Kroah-Hartman , "Rafael J. Wysocki" , Danilo Krummrich , Saravana Kannan , Miguel Ojeda , Alex Gaynor , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Remo Senekowitsch , linux-kernel@vger.kernel.org, linux-acpi@vger.kernel.org, devicetree@vger.kernel.org, rust-for-linux@vger.kernel.org Subject: [PATCH 10/10] samples: rust: platform: Add property read examples Date: Wed, 26 Mar 2025 18:13:49 +0100 Message-ID: <20250326171411.590681-11-remo@buenzli.dev> In-Reply-To: <20250326171411.590681-1-remo@buenzli.dev> References: <20250326171411.590681-1-remo@buenzli.dev> Precedence: bulk X-Mailing-List: linux-acpi@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Rspamd-Queue-Id: 4ZND1x5lL5z9sq9 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 | 56 +++++++++++++++++++- 2 files changed, 58 insertions(+), 1 deletion(-) diff --git a/drivers/of/unittest-data/tests-platform.dtsi b/drivers/of/unittest-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 = "test,rust-device"; reg = <0x2>; + + test,u32-prop = <0xdeadbeef>; + test,i16-array = /bits/ 16 <1 2 (-3) (-4)>; }; }; diff --git a/samples/rust/rust_driver_platform.rs b/samples/rust/rust_driver_platform.rs index 8120609e2..ed25a3781 100644 --- a/samples/rust/rust_driver_platform.rs +++ b/samples/rust/rust_driver_platform.rs @@ -2,7 +2,7 @@ //! Rust Platform driver sample. -use kernel::{c_str, of, platform, prelude::*}; +use kernel::{c_str, of, platform, prelude::*, str::CString}; struct SampleDriver { pdev: platform::Device, @@ -28,6 +28,60 @@ fn probe(pdev: &mut platform::Device, info: Option<&Self::IdInfo>) -> Result(c_str!("compatible")) + .required() + { + dev_info!(pdev.as_ref(), "compatible string = {:?}\n", str); + } + + let prop = dev + .property_read::(c_str!("test,bool-prop")) + .required()?; + 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 = dev + .property_read::(c_str!("test,u32-optional-prop")) + .or(0x12); + dev_info!( + dev, + "'test,u32-optional-prop' is {:#x} (default = {:#x})\n", + prop, + 0x12 + ); + + // Missing property without a default will print an error + let _ = dev + .property_read::(c_str!("test,u32-required-prop")) + .required()?; + + let prop: u32 = dev.property_read(c_str!("test,u32-prop")).required()?; + dev_info!(dev, "'test,u32-prop' is {:#x}\n", prop); + + let prop: [i16; 4] = 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")) + .unwrap() + ); + + let prop: KVec = dev + .property_read_array_vec(c_str!("test,i16-array"), 4)? + .required()?; + dev_info!(dev, "'test,i16-array' is KVec {:?}\n", prop); + let drvdata = KBox::new(Self { pdev: pdev.clone() }, GFP_KERNEL)?; Ok(drvdata.into())