use core::ops::{Deref, Range};
use alloc::vec::Vec;
use fdt_raw::{FdtError, Phandle};
use super::NodeView;
use crate::{NodeGeneric, NodeGenericMut, Property, ViewMutOp, ViewOp};
#[derive(Clone, Debug, PartialEq)]
pub enum PciSpace {
IO,
Memory32,
Memory64,
}
#[derive(Clone, Debug, PartialEq)]
pub struct PciRange {
pub space: PciSpace,
pub bus_address: u64,
pub cpu_address: u64,
pub size: u64,
pub prefetchable: bool,
}
#[derive(Clone, Debug)]
pub struct PciInterruptMap {
pub child_address: Vec<u32>,
pub child_irq: Vec<u32>,
pub interrupt_parent: Phandle,
pub parent_irq: Vec<u32>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct PciInterruptInfo {
pub irqs: Vec<u32>,
}
#[derive(Clone, Copy)]
pub struct PciNodeView<'a> {
pub(super) inner: NodeGeneric<'a>,
}
impl<'a> Deref for PciNodeView<'a> {
type Target = NodeGeneric<'a>;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl<'a> ViewOp<'a> for PciNodeView<'a> {
fn as_view(&self) -> NodeView<'a> {
self.inner.as_view()
}
}
impl<'a> PciNodeView<'a> {
pub(crate) fn try_from_view(view: NodeView<'a>) -> Option<Self> {
if view.as_node().is_pci() {
Some(Self {
inner: NodeGeneric { inner: view },
})
} else {
None
}
}
pub fn interrupt_cells(&self) -> u32 {
self.as_view()
.as_node()
.get_property("#interrupt-cells")
.and_then(|prop| prop.get_u32())
.unwrap_or(1)
}
pub fn interrupt_map_mask(&self) -> Option<Vec<u32>> {
self.as_view()
.as_node()
.get_property("interrupt-map-mask")
.map(|prop| prop.get_u32_iter().collect())
}
pub fn bus_range(&self) -> Option<Range<u32>> {
self.as_view()
.as_node()
.get_property("bus-range")
.and_then(|prop| {
let mut iter = prop.get_u32_iter();
let start = iter.next()?;
let end = iter.next()?;
Some(start..end)
})
}
fn decode_pci_address_space(&self, pci_hi: u32) -> (PciSpace, bool) {
let space_code = (pci_hi >> 24) & 0x03;
let prefetchable = (pci_hi >> 30) & 0x01 == 1;
let space = match space_code {
1 => PciSpace::IO,
2 => PciSpace::Memory32,
3 => PciSpace::Memory64,
_ => PciSpace::Memory32,
};
(space, prefetchable)
}
pub fn ranges(&self) -> Option<Vec<PciRange>> {
let prop = self.as_view().as_node().get_property("ranges")?;
let mut data = prop.as_reader();
let mut ranges = Vec::new();
let parent_addr_cells = if let Some(parent) = self.as_view().parent() {
parent.as_view().address_cells().unwrap_or(2) as usize
} else {
2_usize
};
let size_cells = self.as_view().size_cells().unwrap_or(2) as usize;
while let Some(pci_hi) = data.read_u32() {
let pci_mid = data.read_u32()?;
let pci_lo = data.read_u32()?;
let bus_address = ((pci_mid as u64) << 32) | (pci_lo as u64);
let mut parent_addr = 0u64;
for _ in 0..parent_addr_cells {
let cell = data.read_u32()? as u64;
parent_addr = (parent_addr << 32) | cell;
}
let mut size = 0u64;
for _ in 0..size_cells {
let cell = data.read_u32()? as u64;
size = (size << 32) | cell;
}
let (space, prefetchable) = self.decode_pci_address_space(pci_hi);
ranges.push(PciRange {
space,
bus_address,
cpu_address: parent_addr,
size,
prefetchable,
});
}
Some(ranges)
}
pub fn interrupt_map(&self) -> Result<Vec<PciInterruptMap>, FdtError> {
let prop = self
.as_view()
.as_node()
.get_property("interrupt-map")
.ok_or(FdtError::NotFound)?;
let mask: Vec<u32> = self
.interrupt_map_mask()
.ok_or(FdtError::NotFound)?
.into_iter()
.collect();
let mut data = prop.as_reader();
let mut mappings = Vec::new();
let child_addr_cells = self.as_view().address_cells().unwrap_or(3) as usize;
let child_irq_cells = self.interrupt_cells() as usize;
loop {
let mut child_address = Vec::with_capacity(child_addr_cells);
for _ in 0..child_addr_cells {
match data.read_u32() {
Some(v) => child_address.push(v),
None => return Ok(mappings), }
}
let mut child_irq = Vec::with_capacity(child_irq_cells);
for _ in 0..child_irq_cells {
match data.read_u32() {
Some(v) => child_irq.push(v),
None => return Ok(mappings),
}
}
let interrupt_parent_raw = match data.read_u32() {
Some(v) => v,
None => return Ok(mappings),
};
let interrupt_parent = Phandle::from(interrupt_parent_raw);
let (parent_addr_cells, parent_irq_cells) =
if let Some(irq_parent) = self.as_view().fdt().get_by_phandle(interrupt_parent) {
let addr_cells = irq_parent.as_view().address_cells().unwrap_or(0) as usize;
let irq_cells = irq_parent
.as_view()
.as_node()
.get_property("#interrupt-cells")
.and_then(|p| p.get_u32())
.unwrap_or(3) as usize;
(addr_cells, irq_cells)
} else {
(0, 3)
};
for _ in 0..parent_addr_cells {
if data.read_u32().is_none() {
return Ok(mappings);
}
}
let mut parent_irq = Vec::with_capacity(parent_irq_cells);
for _ in 0..parent_irq_cells {
match data.read_u32() {
Some(v) => parent_irq.push(v),
None => return Ok(mappings),
}
}
let masked_address: Vec<u32> = child_address
.iter()
.enumerate()
.map(|(i, value)| {
let mask_value = mask.get(i).copied().unwrap_or(0xffff_ffff);
value & mask_value
})
.collect();
let masked_irq: Vec<u32> = child_irq
.iter()
.enumerate()
.map(|(i, value)| {
let mask_value = mask
.get(child_addr_cells + i)
.copied()
.unwrap_or(0xffff_ffff);
value & mask_value
})
.collect();
mappings.push(PciInterruptMap {
child_address: masked_address,
child_irq: masked_irq,
interrupt_parent,
parent_irq,
});
}
}
pub fn child_interrupts(
&self,
bus: u8,
device: u8,
function: u8,
interrupt_pin: u8,
) -> Result<PciInterruptInfo, FdtError> {
let interrupt_map = self.interrupt_map()?;
let mask: Vec<u32> = self
.interrupt_map_mask()
.ok_or(FdtError::NotFound)?
.into_iter()
.collect();
let child_addr_high = ((bus as u32 & 0xff) << 16)
| ((device as u32 & 0x1f) << 11)
| ((function as u32 & 0x07) << 8);
let child_addr_mid = 0u32;
let child_addr_low = 0u32;
let child_addr_cells = self.as_view().address_cells().unwrap_or(3) as usize;
let child_irq_cells = self.interrupt_cells() as usize;
let encoded_address = [child_addr_high, child_addr_mid, child_addr_low];
let mut masked_child_address = Vec::with_capacity(child_addr_cells);
for (idx, value) in encoded_address.iter().take(child_addr_cells).enumerate() {
let mask_value = mask.get(idx).copied().unwrap_or(0xffff_ffff);
masked_child_address.push(value & mask_value);
}
let remaining = child_addr_cells.saturating_sub(encoded_address.len());
masked_child_address.extend(core::iter::repeat_n(0, remaining));
let encoded_irq = [interrupt_pin as u32];
let mut masked_child_irq = Vec::with_capacity(child_irq_cells);
for (idx, value) in encoded_irq.iter().take(child_irq_cells).enumerate() {
let mask_value = mask
.get(child_addr_cells + idx)
.copied()
.unwrap_or(0xffff_ffff);
masked_child_irq.push(value & mask_value);
}
let remaining_irq = child_irq_cells.saturating_sub(encoded_irq.len());
masked_child_irq.extend(core::iter::repeat_n(0, remaining_irq));
for mapping in &interrupt_map {
if mapping.child_address == masked_child_address
&& mapping.child_irq == masked_child_irq
{
return Ok(PciInterruptInfo {
irqs: mapping.parent_irq.clone(),
});
}
}
let simple_irq = (device as u32 * 4 + interrupt_pin as u32) % 32;
Ok(PciInterruptInfo {
irqs: vec![simple_irq],
})
}
}
pub struct PciNodeViewMut<'a> {
pub(super) inner: NodeGenericMut<'a>,
}
impl<'a> Deref for PciNodeViewMut<'a> {
type Target = NodeGenericMut<'a>;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl<'a> ViewOp<'a> for PciNodeViewMut<'a> {
fn as_view(&self) -> NodeView<'a> {
self.inner.as_view()
}
}
impl<'a> ViewMutOp<'a> for PciNodeViewMut<'a> {
fn new(node: NodeGenericMut<'a>) -> Self {
let mut s = Self { inner: node };
let n = s.inner.inner.as_node_mut();
n.set_property(Property::new("device_type", b"pci\0".to_vec()));
n.set_property(Property::new(
"#address-cells",
(3u32).to_be_bytes().to_vec(),
));
n.set_property(Property::new("#size-cells", (2u32).to_be_bytes().to_vec()));
n.set_property(Property::new(
"#interrupt-cells",
(1u32).to_be_bytes().to_vec(),
));
s
}
}
impl<'a> PciNodeViewMut<'a> {
pub(crate) fn try_from_view(view: NodeView<'a>) -> Option<Self> {
if view.as_node().is_pci() {
Some(Self {
inner: NodeGenericMut { inner: view },
})
} else {
None
}
}
}