186 lines
5.3 KiB
Rust
Raw Normal View History

2023-07-18 18:03:45 +03:00
//! Device management and interfaces
use core::mem::size_of;
2023-08-04 10:07:21 +03:00
use abi::error::Error;
use alloc::boxed::Box;
use device_api::{manager::DeviceManager, Device, DeviceId};
use fdt_rs::{index::DevTreeIndexNode, prelude::PropReader};
use crate::{
arch::aarch64::devtree,
sync::{IrqSafeSpinlock, IrqSafeSpinlockGuard},
};
2023-08-04 10:07:21 +03:00
pub mod bus;
pub mod power;
pub mod serial;
pub mod tty;
2023-07-18 18:03:45 +03:00
// pub mod display;
static DEVICE_MANAGER: IrqSafeSpinlock<DeviceManager> = IrqSafeSpinlock::new(DeviceManager::new());
/// Helper macro to return the count of expressions supplied to it
#[macro_export]
macro_rules! count {
() => (0usize);
($x:tt $($xs:tt)*) => (1usize + $crate::count!($($xs)*));
}
/// Registers a device driver for compatible device tree nodes
///
/// # Usage example
///
/// ```
/// device_tree_driver! {
/// compatible: ["arm,pl011"],
/// probe(of) => {
/// let my_device = ...; // ... extract some info about the device ...
/// Some(Box::new(my_device))
/// }
/// }
/// ```
#[macro_export]
macro_rules! device_tree_driver {
(
compatible: [$($compatible:literal),+],
probe ($node:ident) => $probe_body:block $(,)?
) => {
const __COMPATIBLE_LEN: usize = $crate::count!($($compatible )+);
static __COMPATIBLE: [&str; __COMPATIBLE_LEN] = [$($compatible),+];
fn __probe($node: &$crate::device::DevTreeNodeInfo) ->
Option<alloc::boxed::Box<dyn device_api::Device>> $probe_body
core::arch::global_asm!(r#"
.pushsection .dt_probes, "a"
.quad {compatible}
.quad {compatible_len}
.quad {probe_func}
.popsection
"#,
compatible = sym __COMPATIBLE,
compatible_len = const __COMPATIBLE_LEN,
probe_func = sym __probe
);
};
}
struct DevTreeProbe<'a> {
compatible: &'static [&'static str],
probe_func: fn(&'a DevTreeNodeInfo<'a, 'a, 'a>) -> Option<Box<dyn Device>>,
}
/// Provides information about a device tree node to device driver's "probe" function
pub struct DevTreeNodeInfo<'a, 'i, 'dt> {
/// #address-cells property of the parent bus/system
pub address_cells: usize,
/// #size-cells property of the parent bus/system
pub size_cells: usize,
/// Device tree node being probed
pub node: DevTreeIndexNode<'a, 'i, 'dt>,
}
fn iter_dt_probes<'a>() -> impl Iterator<Item = DevTreeProbe<'a>> {
extern "C" {
static __dt_probes_start: u64;
static __dt_probes_end: u64;
}
unsafe {
let base = &__dt_probes_start as *const u64;
let end = &__dt_probes_end as *const u64;
let len = (end as usize - base as usize) / (size_of::<u64>() * 3);
(0..len).map(move |i| {
let compatible_ptr = *base.add(i * 3);
let compatible_len = *base.add(i * 3 + 1);
let probe_func_ptr = *base.add(i * 3 + 2);
let compatible =
core::slice::from_raw_parts(compatible_ptr as *const &str, compatible_len as usize);
let probe_func = core::mem::transmute(probe_func_ptr);
DevTreeProbe {
compatible,
probe_func,
}
})
}
}
fn dt_match_compatible(compatible: &str) -> Option<DevTreeProbe> {
iter_dt_probes().find(|probe| probe.compatible.contains(&compatible))
}
/// "Probes" a device tree node for any matching device, registering it if a compatible driver is
/// found
pub fn probe_dt_node(dt: &DevTreeNodeInfo) -> Option<(&'static dyn Device, DeviceId)> {
// TODO use list, not just the first item
let Some(compatible) =
devtree::find_prop(&dt.node, "compatible").and_then(|prop| prop.str().ok())
else {
return None;
};
let probe = dt_match_compatible(compatible)?;
let device = Box::leak((probe.probe_func)(dt)?);
let id = register_device(device);
Some((device, id))
2023-07-18 18:03:45 +03:00
}
2023-08-04 10:07:21 +03:00
/// Performs shallow walk of a device tree node and executes the visitor function on each node
pub fn enumerate_dt<
'a,
I: Iterator<Item = DevTreeIndexNode<'a, 'a, 'a>>,
F: Fn(&str, DevTreeNodeInfo) -> Result<(), Error>,
>(
address_cells: usize,
size_cells: usize,
nodes: I,
f: F,
) -> Result<(), usize> {
let mut failed_count = 0;
for node in nodes {
// Skip /cpus and /memory*
let probe = DevTreeNodeInfo {
address_cells,
size_cells,
node,
};
let Ok(name) = probe.node.name() else {
continue;
};
let Some(compatible) =
devtree::find_prop(&probe.node, "compatible").and_then(|prop| prop.str().ok())
else {
continue;
};
if let Err(error) = f(compatible, probe) {
warnln!("{}: {:?}", name, error);
failed_count += 1;
}
}
if failed_count == 0 {
Ok(())
} else {
Err(failed_count)
}
}
/// Adds a device to the kernel's device table and returns the ID assigned to it
pub fn register_device(device: &'static dyn Device) -> DeviceId {
debugln!("Register {:?}", device.display_name());
DEVICE_MANAGER.lock().register(device)
}
2023-08-04 10:07:21 +03:00
/// Returns a safe reference to the kernel's [DeviceManager] instance
pub fn manager_lock<'a>() -> IrqSafeSpinlockGuard<'a, DeviceManager> {
DEVICE_MANAGER.lock()
2023-08-04 10:07:21 +03:00
}