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) <robh@xxxxxxxxxx> Signed-off-by: Rob Herring (Arm) <robh@xxxxxxxxxx> Signed-off-by: Remo Senekowitsch <remo@xxxxxxxxxxx> --- 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<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) + } + + /// Returns integer array length for firmware property `name` + pub fn property_count_elem<T: Integer>(&self, name: &CStr) -> Result<usize> { + self.fwnode().property_count_elem::<T>(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<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 + // 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<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)?; + + // 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<T: Integer>(&self, name: &CStr) -> Result<usize> { + // 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. -- 2.49.0