use core::{mem::MaybeUninit, ptr};
use super::private::Sealed;
use crate::{
alloc::KVec,
bindings,
error::{to_result, Result},
fmt,
prelude::*,
str::{CStr, CString},
sync::aref::ARef,
types::Opaque,
};
#[repr(transparent)]
pub struct FwNode(Opaque<bindings::fwnode_handle>);
impl FwNode {
unsafe fn from_raw(raw: *mut bindings::fwnode_handle) -> ARef<Self> {
unsafe { ARef::from_raw(ptr::NonNull::new_unchecked(raw.cast())) }
}
pub(crate) fn as_raw(&self) -> *mut bindings::fwnode_handle {
self.0.get()
}
pub fn is_of_node(&self) -> bool {
unsafe { bindings::is_of_node(self.as_raw()) }
}
pub fn display_name(&self) -> impl fmt::Display + '_ {
struct FwNodeDisplayName<'a>(&'a FwNode);
impl fmt::Display for FwNodeDisplayName<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let name = unsafe { bindings::fwnode_get_name(self.0.as_raw()) };
if name.is_null() {
return Ok(());
}
let name = unsafe { CStr::from_char_ptr(name) };
fmt::Display::fmt(name, f)
}
}
FwNodeDisplayName(self)
}
pub fn property_present(&self, name: &CStr) -> bool {
unsafe { bindings::fwnode_property_present(self.as_raw().cast_const(), name.as_char_ptr()) }
}
pub fn property_read_bool(&self, name: &CStr) -> bool {
unsafe { bindings::fwnode_property_read_bool(self.as_raw(), name.as_char_ptr()) }
}
pub fn property_match_string(&self, name: &CStr, match_str: &CStr) -> Result<usize> {
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)
}
pub fn property_read_array_vec<'fwnode, 'name, T: PropertyInt>(
&'fwnode self,
name: &'name CStr,
len: usize,
) -> Result<PropertyGuard<'fwnode, 'name, KVec<T>>> {
let mut val: KVec<T> = KVec::with_capacity(len, GFP_KERNEL)?;
let res = T::read_array_from_fwnode_property(self, name, val.spare_capacity_mut());
let res = match res {
Ok(_) => {
unsafe { val.inc_len(len) }
Ok(val)
}
Err(e) => Err(e),
};
Ok(PropertyGuard {
inner: res,
fwnode: self,
name,
})
}
pub fn property_count_elem<T: PropertyInt>(&self, name: &CStr) -> Result<usize> {
T::read_array_len_from_fwnode_property(self, name)
}
pub fn property_read<'fwnode, 'name, T: Property>(
&'fwnode self,
name: &'name CStr,
) -> PropertyGuard<'fwnode, 'name, T> {
PropertyGuard {
inner: T::read_from_fwnode_property(self, name),
fwnode: self,
name,
}
}
pub fn get_child_by_name(&self, name: &CStr) -> Option<ARef<Self>> {
let child =
unsafe { bindings::fwnode_get_named_child_node(self.as_raw(), name.as_char_ptr()) };
if child.is_null() {
return None;
}
Some(unsafe { Self::from_raw(child) })
}
pub fn children<'a>(&'a self) -> impl Iterator<Item = ARef<FwNode>> + 'a {
let mut prev: Option<ARef<FwNode>> = None;
core::iter::from_fn(move || {
let prev_ptr = match prev.take() {
None => ptr::null_mut(),
Some(prev) => {
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;
}
let next = unsafe { FwNode::from_raw(next) };
prev = Some(next.clone());
Some(next)
})
}
pub fn property_get_reference_args(
&self,
prop: &CStr,
nargs: NArgs<'_>,
index: u32,
) -> Result<FwNodeReferenceArgs> {
let mut out_args = FwNodeReferenceArgs::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.0,
)
};
to_result(ret)?;
Ok(out_args)
}
}
pub enum NArgs<'a> {
Prop(&'a CStr),
N(u32),
}
#[repr(transparent)]
#[derive(Default)]
pub struct FwNodeReferenceArgs(bindings::fwnode_reference_args);
impl Drop for FwNodeReferenceArgs {
fn drop(&mut self) {
if !self.0.fwnode.is_null() {
let _ = unsafe { FwNode::from_raw(self.0.fwnode) };
}
}
}
impl FwNodeReferenceArgs {
pub fn as_slice(&self) -> &[u64] {
unsafe { core::slice::from_raw_parts(self.0.args.as_ptr(), self.0.nargs as usize) }
}
pub fn len(&self) -> usize {
self.0.nargs as usize
}
pub fn is_empty(&self) -> bool {
self.0.nargs == 0
}
}
impl fmt::Debug for FwNodeReferenceArgs {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?}", self.as_slice())
}
}
unsafe impl crate::sync::aref::AlwaysRefCounted for FwNode {
fn inc_ref(&self) {
unsafe { bindings::fwnode_handle_get(self.as_raw()) };
}
unsafe fn dec_ref(obj: ptr::NonNull<Self>) {
unsafe { bindings::fwnode_handle_put(obj.cast().as_ptr()) }
}
}
enum Node<'a> {
Borrowed(&'a FwNode),
Owned(ARef<FwNode>),
}
impl fmt::Display for FwNode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let num_parents = unsafe { bindings::fwnode_count_parents(self.as_raw()) };
for depth in (0..=num_parents).rev() {
let fwnode = if depth == 0 {
Node::Borrowed(self)
} else {
let ptr = unsafe { bindings::fwnode_get_nth_parent(self.as_raw(), depth) };
Node::Owned(unsafe { FwNode::from_raw(ptr) })
};
let fwnode: &FwNode = match &fwnode {
Node::Borrowed(f) => f,
Node::Owned(f) => f,
};
let prefix = unsafe { bindings::fwnode_get_name_prefix(fwnode.as_raw()) };
if !prefix.is_null() {
let prefix = unsafe { CStr::from_char_ptr(prefix) };
fmt::Display::fmt(prefix, f)?;
}
fmt::Display::fmt(&fwnode.display_name(), f)?;
}
Ok(())
}
}
pub trait Property: Sized + Sealed {
fn read_from_fwnode_property(fwnode: &FwNode, name: &CStr) -> Result<Self>;
}
impl Sealed for CString {}
impl Property for CString {
fn read_from_fwnode_property(fwnode: &FwNode, name: &CStr) -> Result<Self> {
let mut str: *mut u8 = ptr::null_mut();
let pstr: *mut _ = &mut str;
let ret = unsafe {
bindings::fwnode_property_read_string(fwnode.as_raw(), name.as_char_ptr(), pstr.cast())
};
to_result(ret)?;
let str = unsafe { CStr::from_char_ptr(*pstr) };
Ok(str.try_into()?)
}
}
pub trait PropertyInt: Copy + Sealed {
fn read_array_from_fwnode_property<'a>(
fwnode: &FwNode,
name: &CStr,
out: &'a mut [MaybeUninit<Self>],
) -> Result<&'a mut [Self]>;
fn read_array_len_from_fwnode_property(fwnode: &FwNode, name: &CStr) -> Result<usize>;
}
macro_rules! impl_property_for_int {
($($int:ty: $f:ident),* $(,)?) => { $(
impl Sealed for $int {}
impl<const N: usize> Sealed for [$int; N] {}
impl PropertyInt for $int {
fn read_array_from_fwnode_property<'a>(
fwnode: &FwNode,
name: &CStr,
out: &'a mut [MaybeUninit<Self>],
) -> Result<&'a mut [Self]> {
let ret = unsafe {
bindings::$f(
fwnode.as_raw(),
name.as_char_ptr(),
out.as_mut_ptr().cast(),
out.len(),
)
};
to_result(ret)?;
Ok(unsafe { core::mem::transmute::<&mut [MaybeUninit<Self>], &mut [Self]>(out) })
}
fn read_array_len_from_fwnode_property(fwnode: &FwNode, name: &CStr) -> Result<usize> {
let ret = unsafe {
bindings::$f(
fwnode.as_raw(),
name.as_char_ptr(),
ptr::null_mut(),
0,
)
};
to_result(ret)?;
Ok(ret as usize)
}
}
impl Property for $int {
fn read_from_fwnode_property(fwnode: &FwNode, name: &CStr) -> Result<Self> {
let val: [_; 1] = <[$int; 1]>::read_from_fwnode_property(fwnode, name)?;
Ok(val[0])
}
}
impl<const N: usize> Property for [$int; N] {
fn read_from_fwnode_property(fwnode: &FwNode, name: &CStr) -> Result<Self> {
let mut val: [MaybeUninit<$int>; N] = [const { MaybeUninit::uninit() }; N];
<$int>::read_array_from_fwnode_property(fwnode, name, &mut val)?;
Ok(val.map(|v| unsafe { v.assume_init() }))
}
}
)* };
}
impl_property_for_int! {
u8: fwnode_property_read_u8_array,
u16: fwnode_property_read_u16_array,
u32: fwnode_property_read_u32_array,
u64: fwnode_property_read_u64_array,
i8: fwnode_property_read_u8_array,
i16: fwnode_property_read_u16_array,
i32: fwnode_property_read_u32_array,
i64: fwnode_property_read_u64_array,
}
pub struct PropertyGuard<'fwnode, 'name, T> {
inner: Result<T>,
fwnode: &'fwnode FwNode,
name: &'name CStr,
}
impl<T> PropertyGuard<'_, '_, T> {
pub fn required_by(self, dev: &super::Device) -> Result<T> {
if self.inner.is_err() {
dev_err!(
dev,
"{}: property '{}' is missing\n",
self.fwnode,
self.name
);
}
self.inner
}
pub fn optional(self) -> Option<T> {
self.inner.ok()
}
pub fn or(self, default: T) -> T {
self.inner.unwrap_or(default)
}
}
impl<T: Default> PropertyGuard<'_, '_, T> {
pub fn or_default(self) -> T {
self.inner.unwrap_or_default()
}
}