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]) + } +}