diff --git a/src/fdt/mod.rs b/src/fdt/mod.rs index 0e374ad..322e80d 100644 --- a/src/fdt/mod.rs +++ b/src/fdt/mod.rs @@ -27,7 +27,8 @@ use zerocopy::byteorder::big_endian; use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout, Unaligned}; pub use self::node::FdtNode; -pub use self::property::{Cells, FdtProperty}; +pub use self::property::FdtProperty; +use crate::Node; use crate::error::{FdtErrorKind, FdtParseError}; use crate::memreserve::MemoryReservation; @@ -487,7 +488,9 @@ impl<'a> Fdt<'a> { /// # Examples /// /// ``` - /// # use dtoolkit::fdt::Fdt; + /// use dtoolkit::Node; + /// use dtoolkit::fdt::Fdt; + /// /// # let dtb = include_bytes!("../../tests/dtb/test.dtb"); /// let fdt = Fdt::new(dtb).unwrap(); /// let root = fdt.root(); @@ -528,7 +531,9 @@ impl<'a> Fdt<'a> { /// # Examples /// /// ``` - /// # use dtoolkit::fdt::Fdt; + /// use dtoolkit::Node; + /// use dtoolkit::fdt::Fdt; + /// /// # let dtb = include_bytes!("../../tests/dtb/test_traversal.dtb"); /// let fdt = Fdt::new(dtb).unwrap(); /// let node = fdt.find_node("/a/b/c").unwrap(); @@ -536,7 +541,9 @@ impl<'a> Fdt<'a> { /// ``` /// /// ``` - /// # use dtoolkit::fdt::Fdt; + /// use dtoolkit::Node; + /// use dtoolkit::fdt::Fdt; + /// /// # let dtb = include_bytes!("../../tests/dtb/test_children.dtb"); /// let fdt = Fdt::new(dtb).unwrap(); /// let node = fdt.find_node("/child2").unwrap(); diff --git a/src/fdt/node.rs b/src/fdt/node.rs index 9536d36..cb27e3c 100644 --- a/src/fdt/node.rs +++ b/src/fdt/node.rs @@ -11,6 +11,7 @@ use core::fmt::{self, Display, Formatter}; use super::{FDT_TAGSIZE, Fdt, FdtToken}; +use crate::Node; use crate::fdt::property::{FdtPropIter, FdtProperty}; use crate::standard::AddressSpaceProperties; @@ -24,14 +25,8 @@ pub struct FdtNode<'a> { pub(crate) parent_address_space: AddressSpaceProperties, } -impl<'a> FdtNode<'a> { - pub(crate) fn new(fdt: Fdt<'a>, offset: usize) -> Self { - Self { - fdt, - offset, - parent_address_space: AddressSpaceProperties::default(), - } - } +impl<'a> Node<'a> for FdtNode<'a> { + type Property = FdtProperty<'a>; /// Returns the name of this node. /// @@ -44,71 +39,30 @@ impl<'a> FdtNode<'a> { /// # Examples /// /// ``` - /// # use dtoolkit::fdt::Fdt; + /// use dtoolkit::Node; + /// use dtoolkit::fdt::Fdt; + /// /// # let dtb = include_bytes!("../../tests/dtb/test_children.dtb"); /// let fdt = Fdt::new(dtb).unwrap(); /// let root = fdt.root(); /// let child = root.child("child1").unwrap(); /// assert_eq!(child.name(), "child1"); /// ``` - #[must_use] - pub fn name(&self) -> &'a str { + fn name(&self) -> &'a str { let name_offset = self.offset + FDT_TAGSIZE; self.fdt .string_at_offset(name_offset, None) .expect("Fdt should be valid") } - /// Returns the name of this node without the unit address, if any. - /// - /// # Panics - /// - /// Panics if the [`Fdt`] structure was constructed using - /// [`Fdt::new_unchecked`] or [`Fdt::from_raw_unchecked`] and the FDT is not - /// valid. - #[must_use] - pub fn name_without_address(&self) -> &'a str { - let name = self.name(); - if let Some((name, _)) = name.split_once('@') { - name - } else { - name - } - } - - /// Returns a property by its name. - /// - /// # Performance - /// - /// This method iterates through all properties of the node. - /// - /// # Examples - /// - /// ``` - /// # use dtoolkit::fdt::Fdt; - /// # let dtb = include_bytes!("../../tests/dtb/test_props.dtb"); - /// let fdt = Fdt::new(dtb).unwrap(); - /// let node = fdt.find_node("/test-props").unwrap(); - /// let prop = node.property("u32-prop").unwrap(); - /// assert_eq!(prop.name(), "u32-prop"); - /// ``` - /// - /// # Panics - /// - /// Panics if the [`Fdt`] structure was constructed using - /// [`Fdt::new_unchecked`] or [`Fdt::from_raw_unchecked`] and the FDT is not - /// valid. - #[must_use] - pub fn property(&self, name: &str) -> Option> { - self.properties().find(|property| property.name() == name) - } - /// Returns an iterator over the properties of this node. /// /// # Examples /// /// ``` - /// # use dtoolkit::fdt::Fdt; + /// use dtoolkit::fdt::Fdt; + /// use dtoolkit::{Node, Property}; + /// /// # let dtb = include_bytes!("../../tests/dtb/test_props.dtb"); /// let fdt = Fdt::new(dtb).unwrap(); /// let node = fdt.find_node("/test-props").unwrap(); @@ -117,78 +71,21 @@ impl<'a> FdtNode<'a> { /// assert_eq!(props.next().unwrap().name(), "u64-prop"); /// assert_eq!(props.next().unwrap().name(), "str-prop"); /// ``` - pub fn properties(&self) -> impl Iterator> + use<'a> { + fn properties(&self) -> impl Iterator> + use<'a> { FdtPropIter::Start { fdt: self.fdt, offset: self.offset, } } - /// Returns a child node by its name. - /// - /// If the given name contains a _unit-address_ (the part after the `@` - /// sign) then both the _node-name_ and _unit-address_ must match. If it - /// doesn't have a _unit-address_, then nodes with any _unit-address_ or - /// none will be allowed. - /// - /// For example, searching for `memory` as a child of `/` would match either - /// `/memory` or `/memory@4000`, while `memory@4000` would match only the - /// latter. - /// - /// # Performance - /// - /// This method's performance is linear in the number of children of this - /// node because it iterates through the children. If you need to call this - /// often, consider converting to a - /// [`DeviceTreeNode`](crate::model::DeviceTreeNode) first. Child lookup - /// on a [`DeviceTreeNode`](crate::model::DeviceTreeNode) is a - /// constant-time operation. - /// - /// # Panics - /// - /// Panics if the [`Fdt`] structure was constructed using - /// [`Fdt::new_unchecked`] or [`Fdt::from_raw_unchecked`] and the FDT is not - /// valid. - /// - /// # Examples - /// - /// ``` - /// # use dtoolkit::fdt::Fdt; - /// # let dtb = include_bytes!("../../tests/dtb/test_children.dtb"); - /// let fdt = Fdt::new(dtb).unwrap(); - /// let root = fdt.root(); - /// let child = root.child("child1").unwrap(); - /// assert_eq!(child.name(), "child1"); - /// ``` - /// - /// ``` - /// # use dtoolkit::fdt::Fdt; - /// # let dtb = include_bytes!("../../tests/dtb/test_children.dtb"); - /// let fdt = Fdt::new(dtb).unwrap(); - /// let root = fdt.root(); - /// let child = root.child("child2").unwrap(); - /// assert_eq!(child.name(), "child2@42"); - /// let child = root.child("child2@42").unwrap(); - /// assert_eq!(child.name(), "child2@42"); - /// ``` - #[must_use] - pub fn child(&self, name: &str) -> Option> { - let include_address = name.contains('@'); - self.children().find(|&child| { - if include_address { - child.name() == name - } else { - child.name_without_address() == name - } - }) - } - /// Returns an iterator over the children of this node. /// /// # Examples /// /// ``` - /// # use dtoolkit::fdt::Fdt; + /// use dtoolkit::Node; + /// use dtoolkit::fdt::Fdt; + /// /// # let dtb = include_bytes!("../../tests/dtb/test_children.dtb"); /// let fdt = Fdt::new(dtb).unwrap(); /// let root = fdt.root(); @@ -197,9 +94,19 @@ impl<'a> FdtNode<'a> { /// assert_eq!(children.next().unwrap().name(), "child2@42"); /// assert!(children.next().is_none()); /// ``` - pub fn children(&self) -> impl Iterator> + use<'a> { + fn children(&self) -> impl Iterator> + use<'a> { FdtChildIter::Start { node: *self } } +} + +impl<'a> FdtNode<'a> { + pub(crate) fn new(fdt: Fdt<'a>, offset: usize) -> Self { + Self { + fdt, + offset, + parent_address_space: AddressSpaceProperties::default(), + } + } pub(crate) fn fmt_recursive(&self, f: &mut Formatter, indent: usize) -> fmt::Result { let name = self.name(); diff --git a/src/fdt/property.rs b/src/fdt/property.rs index 83214ed..b6df84a 100644 --- a/src/fdt/property.rs +++ b/src/fdt/property.rs @@ -8,14 +8,12 @@ //! A read-only API for inspecting a device tree property. -use core::ffi::CStr; use core::fmt::{self, Display, Formatter}; -use core::ops::{BitOr, Shl}; use zerocopy::{FromBytes, big_endian}; use super::{FDT_TAGSIZE, Fdt, FdtToken}; -use crate::error::{PropertyError, StandardError}; +use crate::Property; /// A property of a device tree node. #[derive(Debug, PartialEq)] @@ -25,131 +23,17 @@ pub struct FdtProperty<'a> { value_offset: usize, } -impl<'a> FdtProperty<'a> { - /// Returns the name of this property. - #[must_use] - pub fn name(&self) -> &'a str { +impl<'a> Property<'a> for FdtProperty<'a> { + fn name(&self) -> &'a str { self.name } - /// Returns the value of this property. - #[must_use] - pub fn value(&self) -> &'a [u8] { + fn value(&self) -> &'a [u8] { self.value } +} - /// Returns the value of this property as a `u32`. - /// - /// # Errors - /// - /// Returns an [`PropertyError::InvalidLength`] if the property's value is - /// not 4 bytes long. - /// - /// # Examples - /// - /// ``` - /// # use dtoolkit::fdt::Fdt; - /// # let dtb = include_bytes!("../../tests/dtb/test_props.dtb"); - /// let fdt = Fdt::new(dtb).unwrap(); - /// let node = fdt.find_node("/test-props").unwrap(); - /// let prop = node.property("u32-prop").unwrap(); - /// assert_eq!(prop.as_u32().unwrap(), 0x12345678); - /// ``` - pub fn as_u32(&self) -> Result { - big_endian::U32::ref_from_bytes(self.value) - .map(|val| val.get()) - .map_err(|_e| PropertyError::InvalidLength) - } - - /// Returns the value of this property as a `u64`. - /// - /// # Errors - /// - /// Returns an [`PropertyError::InvalidLength`] if the property's value is - /// not 8 bytes long. - /// - /// # Examples - /// - /// ``` - /// # use dtoolkit::fdt::Fdt; - /// # let dtb = include_bytes!("../../tests/dtb/test_props.dtb"); - /// let fdt = Fdt::new(dtb).unwrap(); - /// let node = fdt.find_node("/test-props").unwrap(); - /// let prop = node.property("u64-prop").unwrap(); - /// assert_eq!(prop.as_u64().unwrap(), 0x1122334455667788); - /// ``` - pub fn as_u64(&self) -> Result { - big_endian::U64::ref_from_bytes(self.value) - .map(|val| val.get()) - .map_err(|_e| PropertyError::InvalidLength) - } - - /// Returns the value of this property as a string. - /// - /// # Errors - /// - /// Returns an [`PropertyError::InvalidString`] if the property's value is - /// not a null-terminated string or contains invalid UTF-8. - /// - /// # Examples - /// - /// ``` - /// # use dtoolkit::fdt::Fdt; - /// # let dtb = include_bytes!("../../tests/dtb/test_props.dtb"); - /// let fdt = Fdt::new(dtb).unwrap(); - /// let node = fdt.find_node("/test-props").unwrap(); - /// let prop = node.property("str-prop").unwrap(); - /// assert_eq!(prop.as_str().unwrap(), "hello world"); - /// ``` - pub fn as_str(&self) -> Result<&'a str, PropertyError> { - let cstr = - CStr::from_bytes_with_nul(self.value).map_err(|_| PropertyError::InvalidString)?; - cstr.to_str().map_err(|_| PropertyError::InvalidString) - } - - /// Returns an iterator over the strings in this property. - /// - /// # Examples - /// - /// ``` - /// # use dtoolkit::fdt::Fdt; - /// # let dtb = include_bytes!("../../tests/dtb/test_props.dtb"); - /// let fdt = Fdt::new(dtb).unwrap(); - /// let node = fdt.find_node("/test-props").unwrap(); - /// let prop = node.property("str-list-prop").unwrap(); - /// let mut str_list = prop.as_str_list(); - /// assert_eq!(str_list.next(), Some("first")); - /// assert_eq!(str_list.next(), Some("second")); - /// assert_eq!(str_list.next(), Some("third")); - /// assert_eq!(str_list.next(), None); - /// ``` - pub fn as_str_list(&self) -> impl Iterator + use<'a> { - FdtStringListIterator { value: self.value } - } - - pub(crate) fn as_prop_encoded_array( - &self, - fields_cells: [usize; N], - ) -> Result; N]> + use<'a, N>, StandardError> { - let chunk_cells = fields_cells.iter().sum(); - let chunk_bytes = chunk_cells * size_of::(); - if !self.value.len().is_multiple_of(chunk_bytes) { - return Err(StandardError::PropEncodedArraySizeMismatch { - size: self.value.len(), - chunk: chunk_cells, - }); - } - Ok(self.value.chunks_exact(chunk_bytes).map(move |chunk| { - let mut cells = <[big_endian::U32]>::ref_from_bytes(chunk) - .expect("chunk should be a multiple of 4 bytes because of chunks_exact"); - fields_cells.map(|field_cells| { - let field; - (field, cells) = cells.split_at(field_cells); - Cells(field) - }) - })) - } - +impl FdtProperty<'_> { pub(crate) fn fmt(&self, f: &mut Formatter, indent: usize) -> fmt::Result { write!(f, "{:indent$}{}", "", self.name, indent = indent)?; @@ -268,63 +152,3 @@ impl<'a> FdtPropIter<'a> { } } } - -struct FdtStringListIterator<'a> { - value: &'a [u8], -} - -impl<'a> Iterator for FdtStringListIterator<'a> { - type Item = &'a str; - - fn next(&mut self) -> Option { - if self.value.is_empty() { - return None; - } - let cstr = CStr::from_bytes_until_nul(self.value).ok()?; - let s = cstr.to_str().ok()?; - self.value = &self.value[s.len() + 1..]; - Some(s) - } -} - -/// An integer value split into several big-endian u32 parts. -/// -/// This is generally used in prop-encoded-array properties. -#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct Cells<'a>(pub(crate) &'a [big_endian::U32]); - -impl Cells<'_> { - /// Converts the value to the given integer type. - /// - /// # Errors - /// - /// Returns `FdtError::TooManyCells` if the value has too many cells to fit - /// in the given type. - pub fn to_int + Shl + BitOr>( - self, - ) -> Result { - if size_of::() < self.0.len() * size_of::() { - Err(StandardError::TooManyCells { - cells: self.0.len(), - }) - } else if let [size] = self.0 { - Ok(size.get().into()) - } else { - let mut value = Default::default(); - for cell in self.0 { - value = value << 32 | cell.get().into(); - } - Ok(value) - } - } -} - -impl Display for Cells<'_> { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - f.write_str("0x")?; - for part in self.0 { - write!(f, "{part:08x}")?; - } - Ok(()) - } -} diff --git a/src/lib.rs b/src/lib.rs index f5fd81f..020c16e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -47,6 +47,7 @@ //! ``` //! use dtoolkit::fdt::Fdt; //! use dtoolkit::model::{DeviceTree, DeviceTreeNode, DeviceTreeProperty}; +//! use dtoolkit::{Node, Property}; //! //! // Create a new device tree from scratch. //! let mut tree = DeviceTree::new(); @@ -86,3 +87,414 @@ pub mod memreserve; #[cfg(feature = "write")] pub mod model; pub mod standard; + +use core::ffi::CStr; +use core::fmt::{self, Display, Formatter}; +use core::ops::{BitOr, Shl}; + +use zerocopy::{FromBytes, big_endian}; + +use crate::error::{PropertyError, StandardError}; +use crate::standard::{AddressSpaceProperties, DEFAULT_ADDRESS_CELLS, DEFAULT_SIZE_CELLS, Status}; + +/// A device tree node. +pub trait Node<'a>: Sized { + /// The type used for properties of the node. + type Property: Property<'a>; + + /// Returns the name of this node. + /// + /// # Errors + /// + /// Returns an error if there was a problem parsing the device tree. + fn name(&self) -> &'a str; + + /// Returns the name of this node without the unit address, if any. + /// + /// # Errors + /// + /// Returns an + /// [`FdtErrorKind::InvalidOffset`](crate::error::FdtErrorKind::InvalidOffset) + /// if the name offset is invalid or an + /// [`FdtErrorKind::InvalidString`](crate::error::FdtErrorKind::InvalidString) if the string at the offset is not null-terminated + /// or contains invalid UTF-8. + fn name_without_address(&self) -> &'a str { + let name = self.name(); + if let Some((name, _)) = name.split_once('@') { + name + } else { + name + } + } + + /// Returns the property with the given name, if any. + /// + /// # Performance + /// + /// This method iterates through all properties of the node. + /// + /// # Panics + /// + /// Panics if the [`Fdt`] structure was constructed using + /// [`Fdt::new_unchecked`] or [`Fdt::from_raw_unchecked`] and the FDT is not + /// valid. + fn property(&self, name: &str) -> Option { + self.properties().find(|property| property.name() == name) + } + + /// Returns an iterator over the properties of this node. + fn properties(&self) -> impl Iterator + use<'a, Self>; + + /// Returns a child node by its name. + /// + /// If the given name contains a _unit-address_ (the part after the `@` + /// sign) then both the _node-name_ and _unit-address_ must match. If it + /// doesn't have a _unit-address_, then nodes with any _unit-address_ or + /// none will be allowed. + /// + /// For example, searching for `memory` as a child of `/` would match either + /// `/memory` or `/memory@4000`, while `memory@4000` would match only the + /// latter. + /// + /// # Errors + /// + /// Returns an error if a child node's name cannot be read. + fn child(&self, name: &str) -> Option { + let include_address = name.contains('@'); + self.children().find(|child| { + if include_address { + child.name() == name + } else { + child.name_without_address() == name + } + }) + } + + /// Returns an iterator over the children of this node. + fn children(&self) -> impl Iterator + use<'a, Self>; + + /// Returns the value of the standard `compatible` property. + #[must_use] + fn compatible(&self) -> Option + use<'a, Self>> { + self.property("compatible") + .map(|property| property.as_str_list()) + } + + /// Returns whether this node has a `compatible` properties containing the + /// given string. + #[must_use] + fn is_compatible(&self, compatible_filter: &str) -> bool { + if let Some(mut compatible) = self.compatible() { + compatible.any(|c| c == compatible_filter) + } else { + false + } + } + + /// Finds all child nodes with a `compatible` property containing the given + /// string. + fn find_compatible<'f>( + &self, + compatible_filter: &'f str, + ) -> impl Iterator + use<'a, 'f, Self> { + self.children() + .filter(move |child| child.is_compatible(compatible_filter)) + } + + /// Returns the value of the standard `model` property. + /// + /// # Errors + /// + /// Returns an error if the value isn't a valid UTF-8 string. + fn model(&self) -> Result, StandardError> { + if let Some(model) = self.property("model") { + Ok(Some(model.as_str()?)) + } else { + Ok(None) + } + } + + /// Returns the value of the standard `phandle` property. + /// + /// # Errors + /// + /// Returns an error if the value isn't a valid u32. + fn phandle(&self) -> Result, StandardError> { + if let Some(property) = self.property("phandle") { + Ok(Some(property.as_u32()?)) + } else { + Ok(None) + } + } + + /// Returns the value of the standard `status` property. + /// + /// If there is no `status` property then `okay` is assumed. + /// + /// # Errors + /// + /// Returns an error if the value isn't a valid status. + fn status(&self) -> Result { + if let Some(status) = self.property("status") { + Ok(status.as_str()?.parse()?) + } else { + Ok(Status::Okay) + } + } + + /// Returns the value of the standard `#address-cells` property. + /// + /// # Errors + /// + /// Returns an error if the value isn't a valid u32. + fn address_cells(&self) -> Result { + if let Some(property) = self.property("#address-cells") { + Ok(property.as_u32()?) + } else { + Ok(DEFAULT_ADDRESS_CELLS) + } + } + + /// Returns the value of the standard `#size-cells` property. + /// + /// # Errors + /// + /// Returns an error if the value isn't a valid u32. + fn size_cells(&self) -> Result { + if let Some(model) = self.property("#size-cells") { + Ok(model.as_u32()?) + } else { + Ok(DEFAULT_SIZE_CELLS) + } + } + + /// Returns the values of the standard `#address-cells` and `#size_cells` + /// properties. + #[must_use] + fn address_space(&self) -> AddressSpaceProperties { + AddressSpaceProperties { + address_cells: self.address_cells().unwrap_or(DEFAULT_ADDRESS_CELLS), + size_cells: self.size_cells().unwrap_or(DEFAULT_SIZE_CELLS), + } + } + + /// Returns the value of the standard `virtual-reg` property. + /// + /// # Errors + /// + /// Returns an error if the value isn't a valid u32. + fn virtual_reg(&self) -> Result, StandardError> { + if let Some(property) = self.property("virtual-reg") { + Ok(Some(property.as_u32()?)) + } else { + Ok(None) + } + } + + /// Returns whether the standard `dma-coherent` property is present. + #[must_use] + fn dma_coherent(&self) -> bool { + self.property("dma-coherent").is_some() + } +} + +/// A property of a device tree node. +pub trait Property<'a>: Sized { + /// Returns the name of this property. + #[must_use] + fn name(&self) -> &'a str; + + /// Returns the value of this property. + #[must_use] + fn value(&self) -> &'a [u8]; + + /// Returns the value of this property as a `u32`. + /// + /// # Errors + /// + /// Returns an [`PropertyError::InvalidLength`] if the property's value is + /// not 4 bytes long. + /// + /// # Examples + /// + /// ``` + /// use dtoolkit::fdt::Fdt; + /// use dtoolkit::{Node, Property}; + /// + /// # let dtb = include_bytes!("../tests/dtb/test_props.dtb"); + /// let fdt = Fdt::new(dtb).unwrap(); + /// let node = fdt.find_node("/test-props").unwrap(); + /// let prop = node.property("u32-prop").unwrap(); + /// assert_eq!(prop.as_u32().unwrap(), 0x12345678); + /// ``` + fn as_u32(&self) -> Result { + self.value() + .try_into() + .map(u32::from_be_bytes) + .map_err(|_| PropertyError::InvalidLength) + } + + /// Returns the value of this property as a `u64`. + /// + /// # Errors + /// + /// Returns an [`PropertyError::InvalidLength`] if the property's value is + /// not 8 bytes long. + /// + /// # Examples + /// + /// ``` + /// use dtoolkit::fdt::Fdt; + /// use dtoolkit::{Node, Property}; + /// + /// # let dtb = include_bytes!("../tests/dtb/test_props.dtb"); + /// let fdt = Fdt::new(dtb).unwrap(); + /// let node = fdt.find_node("/test-props").unwrap(); + /// let prop = node.property("u64-prop").unwrap(); + /// assert_eq!(prop.as_u64().unwrap(), 0x1122334455667788); + /// ``` + fn as_u64(&self) -> Result { + self.value() + .try_into() + .map(u64::from_be_bytes) + .map_err(|_| PropertyError::InvalidLength) + } + + /// Returns the value of this property as a string. + /// + /// # Errors + /// + /// Returns an [`PropertyError::InvalidString`] if the property's value is + /// not a null-terminated string or contains invalid UTF-8. + /// + /// # Examples + /// + /// ``` + /// use dtoolkit::fdt::Fdt; + /// use dtoolkit::{Node, Property}; + /// + /// # let dtb = include_bytes!("../tests/dtb/test_props.dtb"); + /// let fdt = Fdt::new(dtb).unwrap(); + /// let node = fdt.find_node("/test-props").unwrap(); + /// let prop = node.property("str-prop").unwrap(); + /// assert_eq!(prop.as_str().unwrap(), "hello world"); + /// ``` + fn as_str(&self) -> Result<&'a str, PropertyError> { + let cstr = + CStr::from_bytes_with_nul(self.value()).map_err(|_| PropertyError::InvalidString)?; + cstr.to_str().map_err(|_| PropertyError::InvalidString) + } + + /// Returns an iterator over the strings in this property. + /// + /// # Examples + /// + /// ``` + /// use dtoolkit::fdt::Fdt; + /// use dtoolkit::{Node, Property}; + /// + /// # let dtb = include_bytes!("../tests/dtb/test_props.dtb"); + /// let fdt = Fdt::new(dtb).unwrap(); + /// let node = fdt.find_node("/test-props").unwrap(); + /// let prop = node.property("str-list-prop").unwrap(); + /// let mut str_list = prop.as_str_list(); + /// assert_eq!(str_list.next(), Some("first")); + /// assert_eq!(str_list.next(), Some("second")); + /// assert_eq!(str_list.next(), Some("third")); + /// assert_eq!(str_list.next(), None); + /// ``` + fn as_str_list(&self) -> impl Iterator + use<'a, Self> { + FdtStringListIterator { + value: self.value(), + } + } + + /// Returns an iterator over the elements of the property interpreted as a + /// `prop-encoded-array`. + /// + /// Each element of the array will have will have the same number of fields, + /// where each field has the number of cells specified by the corresponding + /// entry in `fields_cells`. + fn as_prop_encoded_array( + &self, + fields_cells: [usize; N], + ) -> Result; N]> + use<'a, N, Self>, StandardError> { + let chunk_cells = fields_cells.iter().sum(); + let chunk_bytes = chunk_cells * size_of::(); + if !self.value().len().is_multiple_of(chunk_bytes) { + return Err(StandardError::PropEncodedArraySizeMismatch { + size: self.value().len(), + chunk: chunk_cells, + }); + } + Ok(self.value().chunks_exact(chunk_bytes).map(move |chunk| { + let mut cells = <[big_endian::U32]>::ref_from_bytes(chunk) + .expect("chunk should be a multiple of 4 bytes because of chunks_exact"); + fields_cells.map(|field_cells| { + let field; + (field, cells) = cells.split_at(field_cells); + Cells(field) + }) + })) + } +} + +struct FdtStringListIterator<'a> { + value: &'a [u8], +} + +impl<'a> Iterator for FdtStringListIterator<'a> { + type Item = &'a str; + + fn next(&mut self) -> Option { + if self.value.is_empty() { + return None; + } + let cstr = CStr::from_bytes_until_nul(self.value).ok()?; + let s = cstr.to_str().ok()?; + self.value = &self.value[s.len() + 1..]; + Some(s) + } +} + +/// An integer value split into several big-endian u32 parts. +/// +/// This is generally used in prop-encoded-array properties. +#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct Cells<'a>(pub(crate) &'a [big_endian::U32]); + +impl Cells<'_> { + /// Converts the value to the given integer type. + /// + /// # Errors + /// + /// Returns `FdtError::TooManyCells` if the value has too many cells to fit + /// in the given type. + pub fn to_int + Shl + BitOr>( + self, + ) -> Result { + if size_of::() < self.0.len() * size_of::() { + Err(StandardError::TooManyCells { + cells: self.0.len(), + }) + } else if let [size] = self.0 { + Ok(size.get().into()) + } else { + let mut value = Default::default(); + for cell in self.0 { + value = value << 32 | cell.get().into(); + } + Ok(value) + } + } +} + +impl Display for Cells<'_> { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + f.write_str("0x")?; + for part in self.0 { + write!(f, "{part:08x}")?; + } + Ok(()) + } +} diff --git a/src/model/mod.rs b/src/model/mod.rs index f721fe1..8d8c416 100644 --- a/src/model/mod.rs +++ b/src/model/mod.rs @@ -13,17 +13,19 @@ //! device tree in memory. The [`DeviceTree`] can then be serialized to a //! flattened device tree blob. +mod node; +mod property; +mod writer; + use alloc::vec::Vec; use core::fmt::Display; +pub use node::{DeviceTreeNode, DeviceTreeNodeBuilder}; +pub use property::DeviceTreeProperty; + use crate::error::FdtParseError; use crate::fdt::Fdt; use crate::memreserve::MemoryReservation; -mod node; -mod property; -mod writer; -pub use node::{DeviceTreeNode, DeviceTreeNodeBuilder}; -pub use property::DeviceTreeProperty; /// A mutable, in-memory representation of a device tree. /// @@ -98,11 +100,13 @@ impl DeviceTree { /// # Examples /// /// ``` - /// # use dtoolkit::model::{DeviceTree, DeviceTreeNode}; + /// use dtoolkit::Node; + /// use dtoolkit::model::{DeviceTree, DeviceTreeNode}; + /// /// let mut tree = DeviceTree::new(); /// tree.root.add_child(DeviceTreeNode::new("child")); /// let child = tree.find_node_mut("/child").unwrap(); - /// assert_eq!(child.name(), "child"); + /// assert_eq!((&*child).name(), "child"); /// ``` pub fn find_node_mut(&mut self, path: &str) -> Option<&mut DeviceTreeNode> { if !path.starts_with('/') { diff --git a/src/model/node.rs b/src/model/node.rs index 97075da..a8b6709 100644 --- a/src/model/node.rs +++ b/src/model/node.rs @@ -16,6 +16,7 @@ use twox_hash::xxhash64; use super::property::DeviceTreeProperty; use crate::error::FdtParseError; use crate::fdt::FdtNode; +use crate::{Node, Property}; /// A mutable, in-memory representation of a device tree node. /// @@ -38,55 +39,40 @@ impl Default for DeviceTreeNode { } } -impl DeviceTreeNode { - /// Creates a new [`DeviceTreeNode`] with the given name. - /// - /// # Examples - /// - /// ``` - /// # use dtoolkit::model::DeviceTreeNode; - /// let node = DeviceTreeNode::new("my-node"); - /// assert_eq!(node.name(), "my-node"); - /// ``` - #[must_use] - pub fn new(name: impl Into) -> Self { - Self { - name: name.into(), - ..Default::default() - } - } +impl<'a> Node<'a> for &'a DeviceTreeNode { + type Property = &'a DeviceTreeProperty; - /// Creates a new [`DeviceTreeNodeBuilder`] with the given name. - #[must_use] - pub fn builder(name: impl Into) -> DeviceTreeNodeBuilder { - DeviceTreeNodeBuilder::new(name) + fn name(&self) -> &'a str { + &self.name } - /// Returns the name of this node. + /// Finds a property by its name and returns a reference to it. + /// + /// # Performance + /// + /// This is a constant-time operation. /// /// # Examples /// /// ``` - /// # use dtoolkit::model::DeviceTreeNode; - /// let node = DeviceTreeNode::new("my-node"); - /// assert_eq!(node.name(), "my-node"); + /// use dtoolkit::model::{DeviceTreeNode, DeviceTreeProperty}; + /// use dtoolkit::{Node, Property}; + /// + /// let mut node = DeviceTreeNode::new("my-node"); + /// node.add_property(DeviceTreeProperty::new("my-prop", vec![1, 2, 3, 4])); + /// let prop = (&node).property("my-prop").unwrap(); + /// assert_eq!(prop.value(), &[1, 2, 3, 4]); /// ``` - #[must_use] - pub fn name(&self) -> &str { - &self.name + fn property(&self, name: &str) -> Option<&'a DeviceTreeProperty> { + self.properties.get(name) } /// Returns an iterator over the properties of this node. - pub fn properties(&self) -> impl Iterator { + fn properties(&self) -> impl Iterator + use<'a> { self.properties.values() } - /// Returns a mutable iterator over the properties of this node. - pub fn properties_mut(&mut self) -> impl Iterator { - self.properties.values_mut() - } - - /// Finds a property by its name and returns a reference to it. + /// Finds a child by its name and returns a reference to it. /// /// # Performance /// @@ -95,15 +81,54 @@ impl DeviceTreeNode { /// # Examples /// /// ``` - /// # use dtoolkit::model::{DeviceTreeNode, DeviceTreeProperty}; + /// use dtoolkit::Node; + /// use dtoolkit::model::{DeviceTreeNode, DeviceTreeProperty}; + /// /// let mut node = DeviceTreeNode::new("my-node"); - /// node.add_property(DeviceTreeProperty::new("my-prop", vec![1, 2, 3, 4])); - /// let prop = node.property("my-prop").unwrap(); - /// assert_eq!(prop.value(), &[1, 2, 3, 4]); + /// node.add_child(DeviceTreeNode::new("child")); + /// let child = (&node).child("child"); + /// assert!(child.is_some()); + /// ``` + fn child(&self, name: &str) -> Option { + self.children.get(name) + // TODO: Handle case where name doesn't contain `@` but child name does. + } + + /// Returns an iterator over the children of this node. + fn children(&self) -> impl Iterator + use<'a> { + self.children.values() + } +} + +impl DeviceTreeNode { + /// Creates a new [`DeviceTreeNode`] with the given name. + /// + /// # Examples + /// + /// ``` + /// use dtoolkit::Node; + /// use dtoolkit::model::DeviceTreeNode; + /// + /// let node = DeviceTreeNode::new("my-node"); + /// assert_eq!((&node).name(), "my-node"); /// ``` #[must_use] - pub fn property(&self, name: &str) -> Option<&DeviceTreeProperty> { - self.properties.get(name) + pub fn new(name: impl Into) -> Self { + Self { + name: name.into(), + ..Default::default() + } + } + + /// Creates a new [`DeviceTreeNodeBuilder`] with the given name. + #[must_use] + pub fn builder(name: impl Into) -> DeviceTreeNodeBuilder { + DeviceTreeNodeBuilder::new(name) + } + + /// Returns a mutable iterator over the properties of this node. + pub fn properties_mut(&mut self) -> impl Iterator { + self.properties.values_mut() } /// Finds a property by its name and returns a mutable reference to it. @@ -115,12 +140,14 @@ impl DeviceTreeNode { /// # Examples /// /// ``` - /// # use dtoolkit::model::{DeviceTreeNode, DeviceTreeProperty}; + /// use dtoolkit::Property; + /// use dtoolkit::model::{DeviceTreeNode, DeviceTreeProperty}; + /// /// let mut node = DeviceTreeNode::new("my-node"); /// node.add_property(DeviceTreeProperty::new("my-prop", vec![1, 2, 3, 4])); /// let prop = node.property_mut("my-prop").unwrap(); /// prop.set_value(vec![5, 6, 7, 8]); - /// assert_eq!(prop.value(), &[5, 6, 7, 8]); + /// assert_eq!((&*prop).value(), &[5, 6, 7, 8]); /// ``` #[must_use] pub fn property_mut(&mut self, name: &str) -> Option<&mut DeviceTreeProperty> { @@ -136,13 +163,16 @@ impl DeviceTreeNode { /// # Examples /// /// ``` - /// # use dtoolkit::model::{DeviceTreeNode, DeviceTreeProperty}; + /// use dtoolkit::model::{DeviceTreeNode, DeviceTreeProperty}; + /// use dtoolkit::{Node, Property}; + /// /// let mut node = DeviceTreeNode::new("my-node"); /// node.add_property(DeviceTreeProperty::new("my-prop", vec![1, 2, 3, 4])); - /// assert_eq!(node.property("my-prop").unwrap().value(), &[1, 2, 3, 4]); + /// assert_eq!((&node).property("my-prop").unwrap().value(), &[1, 2, 3, 4]); /// ``` pub fn add_property(&mut self, property: DeviceTreeProperty) { - self.properties.insert(property.name().to_owned(), property); + self.properties + .insert((&property).name().to_owned(), property); } /// Removes a property from this node by its name. @@ -155,47 +185,24 @@ impl DeviceTreeNode { /// # Examples /// /// ``` - /// # use dtoolkit::model::{DeviceTreeNode, DeviceTreeProperty}; + /// use dtoolkit::model::{DeviceTreeNode, DeviceTreeProperty}; + /// use dtoolkit::{Node, Property}; + /// /// let mut node = DeviceTreeNode::new("my-node"); /// node.add_property(DeviceTreeProperty::new("my-prop", vec![1, 2, 3, 4])); /// let prop = node.remove_property("my-prop").unwrap(); - /// assert_eq!(prop.value(), &[1, 2, 3, 4]); - /// assert!(node.property("my-prop").is_none()); + /// assert_eq!((&prop).value(), &[1, 2, 3, 4]); + /// assert!((&node).property("my-prop").is_none()); /// ``` pub fn remove_property(&mut self, name: &str) -> Option { self.properties.shift_remove(name) } - /// Returns an iterator over the children of this node. - pub fn children(&self) -> impl Iterator { - self.children.values() - } - /// Returns a mutable iterator over the children of this node. pub fn children_mut(&mut self) -> impl Iterator { self.children.values_mut() } - /// Finds a child by its name and returns a reference to it. - /// - /// # Performance - /// - /// This is a constant-time operation. - /// - /// # Examples - /// - /// ``` - /// # use dtoolkit::model::{DeviceTreeNode, DeviceTreeProperty}; - /// let mut node = DeviceTreeNode::new("my-node"); - /// node.add_child(DeviceTreeNode::new("child")); - /// let child = node.child("child"); - /// assert!(child.is_some()); - /// ``` - #[must_use] - pub fn child(&self, name: &str) -> Option<&DeviceTreeNode> { - self.children.get(name) - } - /// Finds a child by its name and returns a mutable reference to it. /// /// # Performance @@ -205,12 +212,17 @@ impl DeviceTreeNode { /// # Examples /// /// ``` - /// # use dtoolkit::model::{DeviceTreeNode, DeviceTreeProperty}; + /// use dtoolkit::model::{DeviceTreeNode, DeviceTreeProperty}; + /// use dtoolkit::{Node, Property}; + /// /// let mut node = DeviceTreeNode::new("my-node"); /// node.add_child(DeviceTreeNode::new("child")); /// let child = node.child_mut("child").unwrap(); /// child.add_property(DeviceTreeProperty::new("my-prop", vec![1, 2, 3, 4])); - /// assert_eq!(child.property("my-prop").unwrap().value(), &[1, 2, 3, 4]); + /// assert_eq!( + /// (&*child).property("my-prop").unwrap().value(), + /// &[1, 2, 3, 4] + /// ); /// ``` #[must_use] pub fn child_mut(&mut self, name: &str) -> Option<&mut DeviceTreeNode> { @@ -226,13 +238,15 @@ impl DeviceTreeNode { /// # Examples /// /// ``` - /// # use dtoolkit::model::DeviceTreeNode; + /// use dtoolkit::Node; + /// use dtoolkit::model::DeviceTreeNode; + /// /// let mut node = DeviceTreeNode::new("my-node"); /// node.add_child(DeviceTreeNode::new("child")); - /// assert_eq!(node.child("child").unwrap().name(), "child"); + /// assert_eq!((&node).child("child").unwrap().name(), "child"); /// ``` pub fn add_child(&mut self, child: DeviceTreeNode) { - self.children.insert(child.name().to_owned(), child); + self.children.insert(child.name.clone(), child); } /// Removes a child from this node by its name. @@ -245,12 +259,14 @@ impl DeviceTreeNode { /// # Examples /// /// ``` - /// # use dtoolkit::model::DeviceTreeNode; + /// use dtoolkit::Node; + /// use dtoolkit::model::DeviceTreeNode; + /// /// let mut node = DeviceTreeNode::new("my-node"); /// node.add_child(DeviceTreeNode::new("child")); /// let child = node.remove_child("child").unwrap(); - /// assert_eq!(child.name(), "child"); - /// assert!(node.child("child").is_none()); + /// assert_eq!((&child).name(), "child"); + /// assert!((&node).child("child").is_none()); /// ``` pub fn remove_child(&mut self, name: &str) -> Option { self.children.shift_remove(name) @@ -269,7 +285,7 @@ impl<'a> TryFrom> for DeviceTreeNode { let mut property_map = IndexMap::with_capacity_and_hasher(properties.len(), default_hash_state()); for property in properties { - property_map.insert(property.name().to_owned(), property); + property_map.insert((&property).name().to_owned(), property); } let children_vec: Vec = node @@ -279,7 +295,7 @@ impl<'a> TryFrom> for DeviceTreeNode { let mut children = IndexMap::with_capacity_and_hasher(children_vec.len(), default_hash_state()); for child in children_vec { - children.insert(child.name().to_owned(), child); + children.insert(child.name.clone(), child); } Ok(DeviceTreeNode { diff --git a/src/model/property.rs b/src/model/property.rs index 6f69da3..971c9e8 100644 --- a/src/model/property.rs +++ b/src/model/property.rs @@ -10,7 +10,8 @@ use alloc::string::{String, ToString}; use alloc::vec::Vec; use core::str; -use crate::error::{FdtParseError, PropertyError}; +use crate::Property; +use crate::error::FdtParseError; use crate::fdt::FdtProperty; /// A mutable, in-memory representation of a device tree property. @@ -20,16 +21,28 @@ pub struct DeviceTreeProperty { value: Vec, } +impl<'a> Property<'a> for &'a DeviceTreeProperty { + fn name(&self) -> &'a str { + &self.name + } + + fn value(&self) -> &'a [u8] { + &self.value + } +} + impl DeviceTreeProperty { /// Creates a new `DeviceTreeProperty` with the given name and value. /// /// # Examples /// /// ``` - /// # use dtoolkit::model::DeviceTreeProperty; + /// use dtoolkit::Property; + /// use dtoolkit::model::DeviceTreeProperty; + /// /// let prop = DeviceTreeProperty::new("my-prop", vec![1, 2, 3, 4]); - /// assert_eq!(prop.name(), "my-prop"); - /// assert_eq!(prop.value(), &[1, 2, 3, 4]); + /// assert_eq!((&prop).name(), "my-prop"); + /// assert_eq!((&prop).value(), &[1, 2, 3, 4]); /// ``` #[must_use] pub fn new(name: impl Into, value: impl Into>) -> Self { @@ -39,70 +52,21 @@ impl DeviceTreeProperty { } } - /// Returns the name of this property. - #[must_use] - pub fn name(&self) -> &str { - &self.name - } - - /// Returns the value of this property. - #[must_use] - pub fn value(&self) -> &[u8] { - &self.value - } - /// Sets the value of this property. /// /// # Examples /// /// ``` - /// # use dtoolkit::model::DeviceTreeProperty; + /// use dtoolkit::Property; + /// use dtoolkit::model::DeviceTreeProperty; + /// /// let mut prop = DeviceTreeProperty::new("my-prop", vec![1, 2, 3, 4]); /// prop.set_value(vec![5, 6, 7, 8]); - /// assert_eq!(prop.value(), &[5, 6, 7, 8]); + /// assert_eq!((&prop).value(), &[5, 6, 7, 8]); /// ``` pub fn set_value(&mut self, value: impl Into>) { self.value = value.into(); } - - /// Returns the value of this property as a `u32`. - /// - /// # Examples - /// - /// ``` - /// # use dtoolkit::model::DeviceTreeProperty; - /// let prop = DeviceTreeProperty::new("my-prop", 1234u32.to_be_bytes()); - /// assert_eq!(prop.as_u32(), Ok(1234)); - /// ``` - /// - /// # Errors - /// - /// Returns an error if the property's value is not 4 bytes long. - pub fn as_u32(&self) -> Result { - self.value - .as_slice() - .try_into() - .map(u32::from_be_bytes) - .map_err(|_| PropertyError::InvalidLength) - } - - /// Returns the value of this property as a string. - /// - /// # Examples - /// - /// ``` - /// # use dtoolkit::model::DeviceTreeProperty; - /// let prop = DeviceTreeProperty::new("my-prop", "hello"); - /// assert_eq!(prop.as_str(), Ok("hello")); - /// ``` - /// # Errors - /// - /// Returns an error if the property's value is not a valid UTF-8 string. - pub fn as_str(&self) -> Result<&str, PropertyError> { - str::from_utf8(&self.value) - .map(|s| s.trim_end_matches('\0')) - .map_err(|_| PropertyError::InvalidString) - } } impl<'a> TryFrom> for DeviceTreeProperty { diff --git a/src/model/writer.rs b/src/model/writer.rs index 2d634f1..234d552 100644 --- a/src/model/writer.rs +++ b/src/model/writer.rs @@ -18,6 +18,7 @@ use crate::fdt::{ }; use crate::memreserve::MemoryReservation; use crate::model::{DeviceTree, DeviceTreeNode, DeviceTreeProperty}; +use crate::{Node, Property}; // https://devicetree-specification.readthedocs.io/en/latest/chapter5-flattened-format.html#header const LAST_VERSION: u32 = 17; diff --git a/src/standard/cpus.rs b/src/standard/cpus.rs index af7c512..bbfe257 100644 --- a/src/standard/cpus.rs +++ b/src/standard/cpus.rs @@ -10,7 +10,8 @@ use core::fmt::{self, Display, Formatter}; use core::ops::Deref; use crate::error::StandardError; -use crate::fdt::{Cells, Fdt, FdtNode}; +use crate::fdt::{Fdt, FdtNode}; +use crate::{Cells, Node}; impl<'a> Fdt<'a> { /// Returns the `/cpus` node. @@ -22,7 +23,7 @@ impl<'a> Fdt<'a> { /// Returns a parse error if there was a problem reading the FDT structure /// to find the node, or `FdtError::CpusMissing` if the CPUs node is /// missing. - pub fn cpus(self) -> Result, StandardError> { + pub fn cpus(self) -> Result>, StandardError> { let node = self.find_node("/cpus").ok_or(StandardError::CpusMissing)?; Ok(Cpus { node }) } @@ -30,27 +31,27 @@ impl<'a> Fdt<'a> { /// Typed wrapper for a `/cpus` node. #[derive(Clone, Copy, Debug)] -pub struct Cpus<'a> { - node: FdtNode<'a>, +pub struct Cpus { + node: N, } -impl<'a> Deref for Cpus<'a> { - type Target = FdtNode<'a>; +impl Deref for Cpus { + type Target = N; fn deref(&self) -> &Self::Target { &self.node } } -impl Display for Cpus<'_> { +impl Display for Cpus { fn fmt(&self, f: &mut Formatter) -> fmt::Result { self.node.fmt(f) } } -impl<'a> Cpus<'a> { +impl<'a, N: Node<'a>> Cpus { /// Returns an iterator over the `/cpus/cpu@*` nodes. - pub fn cpus(&self) -> impl Iterator> + use<'a> { + pub fn cpus(&self) -> impl Iterator> + use<'a, N> { self.node.children().filter_map(|child| { if child.name_without_address() == "cpu" { Some(Cpu { node: child }) @@ -63,25 +64,25 @@ impl<'a> Cpus<'a> { /// Typed wrapper for a `/cpus/cpu` node. #[derive(Clone, Copy, Debug)] -pub struct Cpu<'a> { - node: FdtNode<'a>, +pub struct Cpu { + node: N, } -impl<'a> Deref for Cpu<'a> { - type Target = FdtNode<'a>; +impl Deref for Cpu { + type Target = N; fn deref(&self) -> &Self::Target { &self.node } } -impl Display for Cpu<'_> { +impl Display for Cpu { fn fmt(&self, f: &mut Formatter) -> fmt::Result { self.node.fmt(f) } } -impl<'a> Cpu<'a> { +impl<'a> Cpu> { /// Returns an iterator over the IDs of the CPU, from the standard `reg` /// property. /// @@ -92,6 +93,7 @@ impl<'a> Cpu<'a> { /// the expected number of address and size cells. pub fn ids(&self) -> Result> + use<'a>, StandardError> { Ok(self + .node .reg()? .ok_or(StandardError::CpuMissingReg)? .map(|reg| reg.address)) diff --git a/src/standard/memory.rs b/src/standard/memory.rs index 804dcf3..d23f74f 100644 --- a/src/standard/memory.rs +++ b/src/standard/memory.rs @@ -10,7 +10,8 @@ use core::fmt::{self, Display, Formatter}; use core::ops::Deref; use crate::error::StandardError; -use crate::fdt::{Cells, Fdt, FdtNode}; +use crate::fdt::{Fdt, FdtNode}; +use crate::{Cells, Node, Property}; impl<'a> Fdt<'a> { /// Returns the `/memory` node. @@ -22,7 +23,7 @@ impl<'a> Fdt<'a> { /// Returns a parse error if there was a problem reading the FDT structure /// to find the node, or `FdtError::MemoryMissing` if the memory node is /// missing. - pub fn memory(self) -> Result, StandardError> { + pub fn memory(self) -> Result>, StandardError> { let node = self .find_node("/memory") .ok_or(StandardError::MemoryMissing)?; @@ -32,25 +33,25 @@ impl<'a> Fdt<'a> { /// Typed wrapper for a `/memory` node. #[derive(Clone, Copy, Debug)] -pub struct Memory<'a> { - node: FdtNode<'a>, +pub struct Memory { + node: N, } -impl<'a> Deref for Memory<'a> { - type Target = FdtNode<'a>; +impl Deref for Memory { + type Target = N; fn deref(&self) -> &Self::Target { &self.node } } -impl Display for Memory<'_> { +impl Display for Memory { fn fmt(&self, f: &mut Formatter) -> fmt::Result { self.node.fmt(f) } } -impl<'a> Memory<'a> { +impl<'a, N: Node<'a>> Memory { /// Returns the value of the standard `initial-mapped-area` property of the /// memory node. /// @@ -60,7 +61,7 @@ impl<'a> Memory<'a> { /// size of the value isn't a multiple of 5 cells. pub fn initial_mapped_area( &self, - ) -> Result + use<'a>>, StandardError> { + ) -> Result + use<'a, N>>, StandardError> { if let Some(property) = self.node.property("initial-mapped-area") { Ok(Some( property diff --git a/src/standard/mod.rs b/src/standard/mod.rs index eacad83..32bc076 100644 --- a/src/standard/mod.rs +++ b/src/standard/mod.rs @@ -21,116 +21,12 @@ pub use self::reg::Reg; pub use self::status::Status; use crate::error::StandardError; use crate::fdt::FdtNode; +use crate::{Node, Property}; pub(crate) const DEFAULT_ADDRESS_CELLS: u32 = 2; pub(crate) const DEFAULT_SIZE_CELLS: u32 = 1; impl<'a> FdtNode<'a> { - /// Returns the value of the standard `compatible` property. - #[must_use] - pub fn compatible(&self) -> Option + use<'a>> { - self.property("compatible") - .map(|property| property.as_str_list()) - } - - /// Returns whether this node has a `compatible` properties containing the - /// given string. - #[must_use] - pub fn is_compatible(&self, compatible_filter: &str) -> bool { - if let Some(mut compatible) = self.compatible() { - compatible.any(|c| c == compatible_filter) - } else { - false - } - } - - /// Finds all child nodes with a `compatible` property containing the given - /// string. - pub fn find_compatible<'f>( - &self, - compatible_filter: &'f str, - ) -> impl Iterator> + use<'a, 'f> { - self.children() - .filter(move |child| child.is_compatible(compatible_filter)) - } - - /// Returns the value of the standard `model` property. - /// - /// # Errors - /// - /// Returns an error if the value isn't a valid UTF-8 string. - pub fn model(&self) -> Result, StandardError> { - if let Some(model) = self.property("model") { - Ok(Some(model.as_str()?)) - } else { - Ok(None) - } - } - - /// Returns the value of the standard `phandle` property. - /// - /// # Errors - /// - /// Returns an error if the value isn't a valid u32. - pub fn phandle(&self) -> Result, StandardError> { - if let Some(property) = self.property("phandle") { - Ok(Some(property.as_u32()?)) - } else { - Ok(None) - } - } - - /// Returns the value of the standard `status` property. - /// - /// If there is no `status` property then `okay` is assumed. - /// - /// # Errors - /// - /// Returns an error if the value isn't a valid status. - pub fn status(&self) -> Result { - if let Some(status) = self.property("status") { - Ok(status.as_str()?.parse()?) - } else { - Ok(Status::Okay) - } - } - - /// Returns the value of the standard `#address-cells` property. - /// - /// # Errors - /// - /// Returns an error if the value isn't a valid u32. - pub fn address_cells(&self) -> Result { - if let Some(property) = self.property("#address-cells") { - Ok(property.as_u32()?) - } else { - Ok(DEFAULT_ADDRESS_CELLS) - } - } - - /// Returns the value of the standard `#size-cells` property. - /// - /// # Errors - /// - /// Returns an error if the value isn't a valid u32. - pub fn size_cells(&self) -> Result { - if let Some(model) = self.property("#size-cells") { - Ok(model.as_u32()?) - } else { - Ok(DEFAULT_SIZE_CELLS) - } - } - - /// Returns the values of the standard `#address-cells` and `#size_cells` - /// properties. - #[must_use] - pub fn address_space(&self) -> AddressSpaceProperties { - AddressSpaceProperties { - address_cells: self.address_cells().unwrap_or(DEFAULT_ADDRESS_CELLS), - size_cells: self.size_cells().unwrap_or(DEFAULT_SIZE_CELLS), - } - } - /// Returns the value of the standard `reg` property. /// /// # Errors @@ -151,19 +47,6 @@ impl<'a> FdtNode<'a> { } } - /// Returns the value of the standard `virtual-reg` property. - /// - /// # Errors - /// - /// Returns an error if the value isn't a valid u32. - pub fn virtual_reg(&self) -> Result, StandardError> { - if let Some(property) = self.property("virtual-reg") { - Ok(Some(property.as_u32()?)) - } else { - Ok(None) - } - } - /// Returns the value of the standard `ranges` property. /// /// # Errors @@ -211,12 +94,6 @@ impl<'a> FdtNode<'a> { Ok(None) } } - - /// Returns whether the standard `dma-coherent` property is present. - #[must_use] - pub fn dma_coherent(&self) -> bool { - self.property("dma-coherent").is_some() - } } /// The `#address-cells` and `#size-cells` properties of a node. diff --git a/src/standard/ranges.rs b/src/standard/ranges.rs index a1adbd4..712d453 100644 --- a/src/standard/ranges.rs +++ b/src/standard/ranges.rs @@ -9,8 +9,8 @@ use core::fmt::{self, Display, Formatter}; use core::ops::{BitOr, Shl}; +use crate::Cells; use crate::error::StandardError; -use crate::fdt::Cells; /// One of the values of a `ranges` property. #[derive(Clone, Debug, Default, Eq, PartialEq)] diff --git a/src/standard/reg.rs b/src/standard/reg.rs index 5c223ba..1d72ea0 100644 --- a/src/standard/reg.rs +++ b/src/standard/reg.rs @@ -9,8 +9,8 @@ use core::fmt::{self, Debug, Display, Formatter}; use core::ops::{BitOr, Shl}; +use crate::Cells; use crate::error::StandardError; -use crate::fdt::Cells; /// The value of a `reg` property. #[derive(Clone, Copy, Default, Eq, PartialEq)] diff --git a/tests/fdt.rs b/tests/fdt.rs index 39bdb57..4b39614 100644 --- a/tests/fdt.rs +++ b/tests/fdt.rs @@ -10,6 +10,7 @@ use dtoolkit::fdt::Fdt; #[cfg(feature = "write")] use dtoolkit::model::DeviceTree; use dtoolkit::standard::{InitialMappedArea, Status}; +use dtoolkit::{Node, Property}; #[test] fn read_child_nodes() { diff --git a/tests/model.rs b/tests/model.rs index 1b632ca..83deb26 100644 --- a/tests/model.rs +++ b/tests/model.rs @@ -9,6 +9,7 @@ #![cfg(feature = "write")] use dtoolkit::model::{DeviceTree, DeviceTreeNode, DeviceTreeProperty}; +use dtoolkit::{Node, Property}; #[test] fn tree_creation() { @@ -19,12 +20,12 @@ fn tree_creation() { .add_property(DeviceTreeProperty::new("prop-u32", 1u32.to_be_bytes())); tree.root.add_child( DeviceTreeNode::builder("child-a") - .property(DeviceTreeProperty::new("child-prop", "a")) + .property(DeviceTreeProperty::new("child-prop", "a\0")) .build(), ); tree.root.add_child( DeviceTreeNode::builder("child-b") - .property(DeviceTreeProperty::new("child-prop", "b")) + .property(DeviceTreeProperty::new("child-prop", "b\0")) .build(), ); @@ -47,12 +48,12 @@ fn tree_modification() { // Add a child let child = DeviceTreeNode::new("child"); tree.root.add_child(child); - assert_eq!(tree.root.children().count(), 1); + assert_eq!((&tree.root).children().count(), 1); // Add a property to the child let child = tree.root.child_mut("child").unwrap(); - child.add_property(DeviceTreeProperty::new("prop", "value")); - assert_eq!(child.properties().count(), 1); + child.add_property(DeviceTreeProperty::new("prop", "value\0")); + assert_eq!((&*child).properties().count(), 1); // Find and modify the property let prop = tree @@ -61,22 +62,25 @@ fn tree_modification() { .unwrap() .property_mut("prop") .unwrap(); - prop.set_value("new-value".as_bytes()); + prop.set_value("new-value\0"); // Verify the modification - let child = tree.root.children().find(|c| c.name() == "child").unwrap(); + let child = (&tree.root) + .children() + .find(|c| c.name() == "child") + .unwrap(); assert_eq!(child.property("prop").unwrap().as_str(), Ok("new-value")); // Remove the property let child = tree.root.child_mut("child").unwrap(); let removed_prop = child.remove_property("prop"); assert!(removed_prop.is_some()); - assert_eq!(child.properties().count(), 0); + assert_eq!((&*child).properties().count(), 0); // Remove the child let removed_child = tree.root.remove_child("child"); assert!(removed_child.is_some()); - assert_eq!(tree.root.children().count(), 0); + assert_eq!((&tree.root).children().count(), 0); } #[test] @@ -92,11 +96,10 @@ fn find_node_mut() { // Find a nested child and modify it let child_a_a = tree.find_node_mut("/child-a/child-a-a").unwrap(); - child_a_a.add_property(DeviceTreeProperty::new("prop", "value")); + child_a_a.add_property(DeviceTreeProperty::new("prop", "value\0")); // Verify the modification - let child_a = tree - .root + let child_a = (&tree.root) .children() .find(|c| c.name() == "child-a") .unwrap();