From 6798f102a94125c59b6bb11b596feee25ecbfaa1 Mon Sep 17 00:00:00 2001 From: kadiwa Date: Tue, 3 Oct 2023 14:39:38 +0200 Subject: [PATCH] add `model` --- Cargo.toml | 1 + src/blob/node.rs | 6 +-- src/lib.rs | 2 + src/model.rs | 128 ++++++++++++++++++++++++++++++++++++++++++++++ src/prop_value.rs | 52 +++++++++++++++---- 5 files changed, 176 insertions(+), 13 deletions(-) create mode 100644 src/model.rs diff --git a/Cargo.toml b/Cargo.toml index 2d604cb..261f212 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,4 +36,5 @@ zerocopy = { version = "0.7.0", features = ["derive"] } [features] alloc = ["fallible-iterator/alloc"] derive = ["devicetree_derive"] +model = [] std = ["alloc", "fallible-iterator/std"] diff --git a/src/blob/node.rs b/src/blob/node.rs index 23abc9d..0d2ff6a 100644 --- a/src/blob/node.rs +++ b/src/blob/node.rs @@ -16,9 +16,9 @@ use crate::{ /// It contains [`Property`]s and child nodes. #[derive(Clone, Debug)] pub struct Node<'dtb> { - pub(super) dt: &'dtb Devicetree, - pub(super) name: &'dtb [u8], - pub(super) contents: Cursor, + pub(crate) dt: &'dtb Devicetree, + pub(crate) name: &'dtb [u8], + pub(crate) contents: Cursor, } impl<'dtb> Node<'dtb> { diff --git a/src/lib.rs b/src/lib.rs index e32914c..942511f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,6 +14,8 @@ extern crate alloc as std_alloc; #[cfg(feature = "alloc")] pub mod alloc; pub mod blob; +#[cfg(feature = "model")] +pub mod model; pub mod prop_value; #[cfg(feature = "derive")] diff --git a/src/model.rs b/src/model.rs new file mode 100644 index 0000000..e1a8dc4 --- /dev/null +++ b/src/model.rs @@ -0,0 +1,128 @@ +//! Higher level of abstraction. + +use crate::{ + blob::{self, Cursor, Devicetree}, + prop_value::{self, RangesIter}, + Cells, DeserializeNode, DeserializeProperty, Error, NodeContext, Result, +}; + +/// A devicetree node representing some sort of device, meaning it has a +/// `compatible` property. +#[derive(Clone, Debug)] +pub struct Device<'dtb> { + node_name: &'dtb str, + content_cursor: Cursor, + parent_address_cells: Cells, + parent_size_cells: Cells, + address_cells: Cells, + size_cells: Cells, + phandle: Option, + compatible: prop_value::Strings<'dtb>, + status: prop_value::Status<'dtb>, + reg: Option<&'dtb [u32]>, + ranges: Option<&'dtb [u32]>, +} + +impl<'dtb> Device<'dtb> { + pub fn node_name(&self) -> &'dtb str { + self.node_name + } + + pub fn content_cursor(&self) -> Cursor { + self.content_cursor + } + + pub fn blob_node(&self, dt: &'dtb Devicetree) -> blob::Node<'dtb> { + blob::Node { + dt, + name: self.node_name.as_bytes(), + contents: self.content_cursor, + } + } + + pub fn address_cells(&self) -> Cells { + self.address_cells + } + + pub fn size_cells(&self) -> Cells { + self.size_cells + } + + pub fn phandle(&self) -> Option { + self.phandle + } + + pub fn compatible(&self) -> prop_value::Strings<'dtb> { + self.compatible.clone() + } + + pub fn status(&self) -> prop_value::Status<'dtb> { + self.status + } + + pub fn reg(&self) -> Option> { + self.reg.map(|value| prop_value::Reg { + value, + address_cells: self.parent_address_cells, + size_cells: self.parent_size_cells, + }) + } + + pub fn ranges(&self) -> Option> { + self.ranges.map(|value| RangesIter { + value, + child_address_cells: self.address_cells, + address_cells: self.parent_address_cells, + child_size_cells: self.size_cells, + }) + } +} + +impl<'dtb> DeserializeNode<'dtb> for Device<'dtb> { + fn deserialize(blob_node: &blob::Node<'dtb>, cx: NodeContext<'_>) -> Result<(Self, Cursor)> { + let mut this = Self { + node_name: blob_node.name()?, + content_cursor: blob_node.contents, + parent_address_cells: cx.address_cells, + parent_size_cells: cx.size_cells, + address_cells: 0, + size_cells: 0, + phandle: None, + compatible: prop_value::Strings::EMPTY, + status: prop_value::Status::Ok, + reg: None, + ranges: None, + }; + let mut compatible = None; + let (child_cx, cursor) = cx.deserialize_node( + blob_node, + |name, prop| { + match name { + "phandle" => this.phandle = Some(u32::deserialize(prop, cx)?), + "compatible" => compatible = Some(prop_value::Strings::deserialize(prop, cx)?), + "status" => this.status = prop_value::Status::deserialize(prop, cx)?, + "reg" => this.reg = Some(prop_value::Reg::deserialize(prop, cx)?.value), + "ranges" => this.reg = Some(<&[u32]>::deserialize(prop, cx)?), + _ => (), + }; + Ok(()) + }, + |node, _, cursor| { + *cursor = node.end_cursor()?; + Ok(()) + }, + )?; + this.compatible = compatible.ok_or(Error::UnsuitableNode)?; + if let Some(value) = this.ranges { + RangesIter::new( + value, + child_cx.address_cells, + cx.address_cells, + child_cx.size_cells, + )?; + } + this.address_cells = child_cx.address_cells; + this.size_cells = child_cx.size_cells; + Ok((this, cursor)) + } +} diff --git a/src/prop_value.rs b/src/prop_value.rs index 2031d2c..4201777 100644 --- a/src/prop_value.rs +++ b/src/prop_value.rs @@ -2,7 +2,7 @@ use crate::{blob::Property, util, Cells, DeserializeProperty, Error, NodeContext, Result}; use core::{ - fmt::{Display, Formatter}, + fmt::{self, Display, Formatter}, iter::FusedIterator, }; @@ -88,7 +88,7 @@ impl<'a> TryFrom<&'a str> for Status<'a> { } impl Display for Status<'_> { - fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match *self { Self::Ok => f.write_str("okay"), Self::Disabled => f.write_str("disabled"), @@ -160,7 +160,7 @@ impl<'a> DoubleEndedFallibleIterator for Strings<'a> { } } -impl<'a> Default for Strings<'a> { +impl Default for Strings<'_> { fn default() -> Self { Self::EMPTY } @@ -169,9 +169,9 @@ impl<'a> Default for Strings<'a> { /// Iterator over the _(address, length)_ pairs of `reg`'s value. #[derive(Clone, Debug, Default)] pub struct Reg<'dtb> { - value: &'dtb [u32], - address_cells: Cells, - size_cells: Cells, + pub(crate) value: &'dtb [u32], + pub(crate) address_cells: Cells, + pub(crate) size_cells: Cells, } impl<'dtb> Reg<'dtb> { @@ -309,10 +309,10 @@ impl<'dtb> DeserializeProperty<'dtb> for Ranges<'dtb> { /// ``` #[derive(Clone, Debug)] pub struct RangesIter<'dtb> { - value: &'dtb [u32], - child_address_cells: Cells, - address_cells: Cells, - child_size_cells: Cells, + pub(crate) value: &'dtb [u32], + pub(crate) child_address_cells: Cells, + pub(crate) address_cells: Cells, + pub(crate) child_size_cells: Cells, } impl<'dtb> RangesIter<'dtb> { @@ -406,6 +406,38 @@ impl RangesBlock { let length = util::parse_cells(bytes, child_size_cells)?; Some(Self(child_bus_address, parent_bus_address, length)) } + + /// Maps a child address to the parent address. + /// + /// The address at the end of the range is not considered part of the range. + /// + /// # Examples + /// ``` + /// # use devicetree::prop_value::RangesBlock; + /// let ranges = RangesBlock(0x1000, 0x4000, 0x0800); + /// assert_eq!(ranges.map_to_parent(0x1234), Some(0x4234)); + /// assert_eq!(ranges.map_to_parent(0x1800), None); + /// ``` + pub fn map_to_parent(self, child_address: u128) -> Option { + let offset = u128::checked_sub(child_address, self.0); + offset.filter(|&o| o < self.2).map(|o| self.1 + o) + } + + /// Maps a parent address to the child address. + /// + /// The address at the end of the range is not considered part of the range. + /// + /// # Examples + /// ``` + /// # use devicetree::prop_value::RangesBlock; + /// let ranges = RangesBlock(0x1000, 0x4000, 0x0800); + /// assert_eq!(ranges.map_to_child(0x4321), Some(0x1321)); + /// assert_eq!(ranges.map_to_child(0x4800), None); + /// ``` + pub fn map_to_child(self, parent_address: u128) -> Option { + let offset = u128::checked_sub(parent_address, self.1); + offset.filter(|&o| o < self.2).map(|o| self.0 + o) + } } /// Value of `initial-mapped-area` property.