diff --git a/rust/kernel/fwnode.rs b/rust/kernel/fwnode.rs new file mode 100644 index 00000000000000..a45453b5d6ef77 --- /dev/null +++ b/rust/kernel/fwnode.rs @@ -0,0 +1,111 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Unified device property interface. +//! +//! C header: [`include/linux/property.h`](../../../../include/linux/property.h) + +use crate::{bindings, device, sync::Arc}; +use core::ptr::null_mut; + +fn next_endpoint(handle: &DeviceNode, curr: &Option>) -> Option> { + let res_ptr = match curr { + // SAFETY: By the type invariants, `handle` owns a reference, so it is safe to access `ptr` + None => unsafe { + bindings::fwnode_graph_get_next_endpoint( + handle.ptr, + null_mut::(), + ) + }, + // SAFETY: By the type invariants, `handle` owns a reference, so it safe to access it's + // `ptr`. `node` is a reference counted object (Arc) and hence its safe to access the + // internal `ptr` + Some(node) => unsafe { + bindings::fwnode_graph_get_next_endpoint(handle.ptr, node.as_ref().ptr) + }, + }; + if res_ptr.is_null() { + None + } else { + Arc::try_new(Node { ptr: res_ptr }).ok() + } +} + +/// Represents the root `struct fwnode_handle *` associated to a device +/// +/// # Invariants +/// +/// The pointer is valid. +pub struct DeviceNode { + ptr: *mut bindings::fwnode_handle, +} + +impl DeviceNode { + /// Creates a new DeviceNode instance from an existing [`device::Device`] instance. + pub fn from(dev: &device::Device) -> Self { + Self { + // SAFETY: By the type invariants, `dev` owns a reference, so it is safe to access + // `ptr` + ptr: unsafe { bindings::dev_fwnode(dev.ptr) }, + } + } + /// Creates an instance of `NodeIterator` + /// + /// This provides an `Iterator` wrapping the internal C functionality of invoking + /// `fwnode_graph_get_next_endpoint` + pub fn endpoints(self) -> NodeIterator { + NodeIterator { + handle: self, + curr_node: None, + iter_fn: next_endpoint, + } + } +} + +// SAFETY: `DeviceNode` only holds a pointer to a C fwnode_handle, which is safe to be used from any thread. +unsafe impl Send for DeviceNode {} + +// SAFETY: `DeviceNode` only holds a pointer to a C fwnode_handle, references to which are safe to be used +// from any thread. +unsafe impl Sync for DeviceNode {} +impl Drop for DeviceNode { + fn drop(&mut self) { + // SAFETY: By the type invariants, `self` owns a reference, so it is safe to + // relinquish it now. + unsafe { bindings::fwnode_handle_put(self.ptr) } + } +} + +/// Represents a `struct fwnode_handle *` part of a device's fwnode graph +/// +/// # Invariants +/// +/// The pointer is valid. +pub struct Node { + pub(crate) ptr: *mut bindings::fwnode_handle, +} + +impl Drop for Node { + fn drop(&mut self) { + // SAFETY: By the type invariants, `self` owns a reference, so it is safe to + // relinquish it now. + unsafe { bindings::fwnode_handle_put(self.ptr) } + } +} + +/// A function that optionally returns a `Node` given the `parent` and `prev` +type NodeIteratorFn = fn(parent: &DeviceNode, prev: &Option>) -> Option>; + +/// Implements the Iterator trait to iterate the device's fwnode graph given the `DeviceNode` and a `NodeIteratorFn` +pub struct NodeIterator { + handle: DeviceNode, + curr_node: Option>, + iter_fn: NodeIteratorFn, +} +impl Iterator for NodeIterator { + type Item = Arc; + + fn next(&mut self) -> Option { + self.curr_node = (self.iter_fn)(&self.handle, &self.curr_node); + self.curr_node.clone() + } +} diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 6a322effa60c78..0c38287558bfd0 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -55,6 +55,7 @@ pub mod driver; pub mod error; pub mod file; pub mod fs; +pub mod fwnode; pub mod gpio; pub mod hwrng; pub mod irq;