Skip to content

Commit

Permalink
rust: add fwnode api to access unified device tree
Browse files Browse the repository at this point in the history
Added abstraction APIs for accessing device's fwnode.

Signed-off-by: Vinay Varma <[email protected]>
  • Loading branch information
vvarma committed Nov 13, 2022
1 parent d9b2e84 commit 0b6ace3
Show file tree
Hide file tree
Showing 2 changed files with 134 additions and 0 deletions.
133 changes: 133 additions & 0 deletions rust/kernel/fwnode.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
// SPDX-License-Identifier: GPL-2.0

//! Unified device property interface.
//!
//! C header: [`include/linux/property.h`](../../../../include/linux/property.h)

use crate::{bindings, device, ARef, AlwaysRefCounted};
use core::{cell::UnsafeCell, ptr};

/// Represents a `struct fwnode_handle *` part of a device's fwnode graph
///
/// # Invariants
///
/// The pointer is valid.
#[repr(transparent)]
pub struct Node(pub(crate) UnsafeCell<bindings::fwnode_handle>);

// SAFETY: The type invariants guarantee that `Node` is always ref-counted.
unsafe impl AlwaysRefCounted for Node {
fn inc_ref(&self) {
// SAFETY: The existence of a shared reference means that the refcount is nonzero.
unsafe { bindings::fwnode_handle_get(self.0.get()) };
}

unsafe fn dec_ref(obj: core::ptr::NonNull<Self>) {
// SAFETY: The safety requirements guarantee that the refcount is nonzero.
unsafe { bindings::fwnode_handle_put(obj.cast().as_ptr()) }
}
}

impl Node {
/// Creates a new DeviceNode instance from an existing [`device::Device`] instance.
pub fn from(dev: &device::Device) -> Option<ARef<Self>> {
// SAFETY: By the type invariants, `dev` owns a reference, so it safe to access `ptr`.
// `fwnode_handle_get` increments the refcount of the `fwnode_handle` if it is not
// NULL or returns NULL.
let node = unsafe { bindings::fwnode_handle_get(bindings::dev_fwnode(dev.ptr)) };
let ptr = ptr::NonNull::new(node)?;
// SAFETY: `fwnode_handle_get` increments the refcount
Some(unsafe { ARef::from_raw(ptr.cast()) })
}
/// Creates an instance of `NodeIterator`
///
/// This provides an `Iterator` wrapping the internal C functionality of invoking
/// `fwnode_graph_get_next_endpoint`
pub fn endpoints(node: ARef<Self>) -> NodeIterator {
NodeIterator {
handle: node,
curr_node: None,
next_fn: next_endpoint,
}
}
/// Gets the parent to the current `Node` of the device tree
pub fn parent(node: ARef<Self>) -> Option<ARef<Self>> {
// SAFETY: By the type invariants, `self` owns a reference, so it safe to the underlying
// ptr.
let node = unsafe { bindings::fwnode_get_parent(node.0.get()) };
let ptr = ptr::NonNull::new(node)?;
// SAFETY: `fwnode_get_parent` increments the refcount
Some(unsafe { ARef::from_raw(ptr.cast()) })
}

/// Creates an instance of `NodeIterator`
///
/// This provides an `Iterator` wrapping the internal C functionality of invoking
/// `fwnode_graph_get_next_child_node`
pub fn children(node: ARef<Self>) -> NodeIterator {
NodeIterator {
handle: node,
curr_node: None,
next_fn: next_child,
}
}
}

type NodeIteratorFn = fn(root: ARef<Node>, curr: &Option<ARef<Node>>) -> Option<ARef<Node>>;

/// Implements the Iterator trait to iterate the device's endpoints given the `DeviceNode`
pub struct NodeIterator {
handle: ARef<Node>,
curr_node: Option<ARef<Node>>,
next_fn: NodeIteratorFn,
}
impl Iterator for NodeIterator {
type Item = ARef<Node>;

fn next(&mut self) -> Option<Self::Item> {
self.curr_node = (self.next_fn)(self.handle.clone(), &self.curr_node);
self.curr_node.clone()
}
}

fn next_endpoint(node: ARef<Node>, curr: &Option<ARef<Node>>) -> Option<ARef<Node>> {
let res_ptr = match curr {
// SAFETY: By the type invariants, `node` has a refcount > 1, so it is safe to access the
// underlying ptr
None => unsafe {
bindings::fwnode_graph_get_next_endpoint(
node.0.get(),
ptr::null_mut::<bindings::fwnode_handle>(),
)
},
// SAFETY: By the type invariants, `node` has a refcount > 1, so it is safe to access the
// underlying ptr. `curr`, by the type invariants has a refcount > 1, hence its safe to access the
// it's underlying ptr
Some(curr) => unsafe {
bindings::fwnode_graph_get_next_endpoint(node.0.get(), curr.0.get())
},
};
let ptr = ptr::NonNull::new(res_ptr)?;
// SAFETY: `fwnode_graph_get_next_endpoint` increments the refcount before returning
Some(unsafe { ARef::from_raw(ptr.cast()) })
}

fn next_child(node: ARef<Node>, curr: &Option<ARef<Node>>) -> Option<ARef<Node>> {
let res_ptr = match curr {
// SAFETY: By the type invariants, `node` has a refcount > 1, so it is safe to access the
// underlying ptr
None => unsafe {
bindings::fwnode_get_next_child_node(
node.0.get(),
ptr::null_mut::<bindings::fwnode_handle>(),
)
},
// SAFETY: By the type invariants, `node` has a refcount > 1, so it is safe to access the
// underlying ptr. `curr`, by the type invariants has a refcount > 1, hence its safe to access the
// it's underlying ptr
Some(curr) => unsafe { bindings::fwnode_get_next_child_node(node.0.get(), curr.0.get()) },
};
let ptr = ptr::NonNull::new(res_ptr)?;
// SAFETY: `fwnode_graph_get_next_child_node` increments the refcount before returning
Some(unsafe { ARef::from_raw(ptr.cast()) })
}
1 change: 1 addition & 0 deletions rust/kernel/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down

0 comments on commit 0b6ace3

Please sign in to comment.