use alloc::{format, vec::Vec};
use rdrive::{
DeviceId,
probe::{OnProbeError, acpi::AcpiInfo},
register::FdtInfo,
};
use crate::{BindingInfo, BindingIrq};
pub fn binding_info_from_fdt(info: &FdtInfo<'_>) -> Result<BindingInfo, OnProbeError> {
Ok(BindingInfo::with_binding_irq(resolve_fdt_irq(info)?))
}
pub fn binding_irq_from_named_fdt_interrupt(
node: &rdrive::probe::fdt::NodeType<'_>,
name: &str,
) -> Result<Option<BindingIrq>, OnProbeError> {
let interrupts = node.interrupts();
if interrupts.is_empty() {
return Ok(None);
}
let index = node
.as_node()
.get_property("interrupt-names")
.and_then(|prop| prop.as_str_iter().position(|irq_name| irq_name == name))
.ok_or_else(|| {
OnProbeError::other(format!(
"[{}] interrupt-names does not contain {name}",
node.name()
))
})?;
let interrupt = interrupts.get(index).ok_or_else(|| {
OnProbeError::other(format!(
"[{}] interrupt-names entry {name} has no matching interrupts cell",
node.name()
))
})?;
let controller =
rdrive::fdt_phandle_to_device_id(interrupt.interrupt_parent).ok_or_else(|| {
OnProbeError::other(format!(
"[{}] interrupt-parent {} is not registered",
node.name(),
interrupt.interrupt_parent
))
})?;
Ok(Some(binding_irq_from_fdt_interrupt(
controller,
interrupt.specifier.clone(),
)))
}
pub fn binding_info_from_acpi(info: &AcpiInfo<'_>) -> Result<BindingInfo, OnProbeError> {
Ok(BindingInfo::with_binding_irq(
info.irq_route().map(BindingIrq::from),
))
}
pub fn binding_info_from_acpi_route(
_path: &str,
route: Option<rdrive::probe::acpi::AcpiGsiRoute>,
) -> Result<BindingInfo, OnProbeError> {
Ok(BindingInfo::with_binding_irq(route.map(BindingIrq::from)))
}
fn resolve_fdt_irq(info: &FdtInfo<'_>) -> Result<Option<BindingIrq>, OnProbeError> {
let Some(interrupt) = info.interrupts().into_iter().next() else {
return Ok(None);
};
let controller = info
.phandle_to_device_id(interrupt.interrupt_parent)
.ok_or_else(|| {
OnProbeError::other(format!(
"interrupt-parent {} is not registered",
interrupt.interrupt_parent
))
})?;
Ok(Some(binding_irq_from_fdt_interrupt(
controller,
interrupt.specifier,
)))
}
fn binding_irq_from_fdt_interrupt(controller: DeviceId, cells: impl Into<Vec<u32>>) -> BindingIrq {
BindingIrq::fdt_interrupt_with_controller(controller, cells)
}
#[cfg(feature = "pci")]
pub fn binding_info_from_pci(
info: rdrive::probe::pci::PciInfo,
requirement: crate::PciIrqRequirement,
) -> Result<BindingInfo, OnProbeError> {
let irq = crate::pci::resolve_intx_binding(info)?;
if irq.is_none() && requirement == crate::PciIrqRequirement::Required {
return Err(OnProbeError::other(format!(
"failed to resolve IRQ for PCI endpoint {}",
info.address
)));
}
Ok(BindingInfo::with_binding_irq(irq))
}