[PATCH 05/10] rust: Read properties via single generic method

Remo Senekowitsch
2025-03-26T17:15:02Z

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 <remo@buenzli.dev>
---
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<CString> {
- 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<usize> {
self.fwnode().property_match_string(name, match_str)
}

- /// Returns firmware property `name` integer scalar value
- pub fn property_read<T: Integer>(&self, name: &CStr) -> Result<T> {
- self.fwnode().property_read(name)
- }
-
- /// Returns firmware property `name` integer array values
- pub fn property_read_array<T: Integer, const N: usize>(&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<T: Integer>(&self, name: &CStr, len: usize) -> Result<KVec<T>> {
self.fwnode().property_read_array_vec(name, len)
@@ -69,6 +49,11 @@ pub fn property_read_array_vec<T: Integer>(&self, name: &CStr, len: usize) -> Re
pub fn property_count_elem<T: Integer>(&self, name: &CStr) -> Result<usize> {
self.fwnode().property_count_elem::<T>(name)
}
+
+ /// Returns firmware property `name` integer scalar value
+ pub fn property_read<T: Property>(&self, name: &CStr) -> Result<T> {
+ 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<CString> {
- 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<usize> {
// 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<usi
Ok(ret as usize)
}

- /// Returns firmware property `name` integer scalar value
- pub fn property_read<T: Integer>(&self, name: &CStr) -> Result<T> {
- let val: [_; 1] = Self::property_read_array(self, name)?;
- Ok(val[0])
- }
-
- /// Returns firmware property `name` integer array values
- pub fn property_read_array<T: Integer, const N: usize>(&self, name: &CStr) -> Result<[T; N]> {
- let val: [MaybeUninit<T>; 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<T: Integer>(&self, name: &CStr, len: usize) -> Result<KVec<T>> {
let mut val: KVec<T> = KVec::with_capacity(len, GFP_KERNEL)?;
@@ -206,6 +139,38 @@ pub fn property_count_elem<T: Integer>(&self, name: &CStr) -> Result<usize> {
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<T: Property>(&self, name: &CStr) -> Result<T> {
+ T::read(&self, name)
+ }
}

// SAFETY: Instances of `FwNode` are always reference-counted.
@@ -220,3 +185,65 @@ unsafe fn dec_ref(obj: ptr::NonNull<Self>) {
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<Self>;
+}
+
+impl Property for bool {
+ fn read(fwnode: &FwNode, name: &CStr) -> Result<Self> {
+ // 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<Self> {
+ 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<T: Integer, const N: usize> Property for [T; N] {
+ fn read(fwnode: &FwNode, name: &CStr) -> Result<Self> {
+ let mut val: [MaybeUninit<T>; 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<T: Integer> Property for T {
+ fn read(fwnode: &FwNode, name: &CStr) -> Result<T> {
+ let val: [_; 1] = <[T; 1] as Property>::read(fwnode, name)?;
+ Ok(val[0])
+ }
+}
--
2.49.0