2023-07-18 18:03:45 +03:00
|
|
|
//! Device management and interfaces
|
|
|
|
|
2023-08-13 21:23:58 +03:00
|
|
|
use core::mem::size_of;
|
|
|
|
|
2023-08-04 10:07:21 +03:00
|
|
|
use abi::error::Error;
|
2023-08-13 21:23:58 +03:00
|
|
|
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
|
|
|
|
2023-08-13 21:23:58 +03:00
|
|
|
pub mod bus;
|
|
|
|
pub mod power;
|
2023-08-02 19:53:54 +03:00
|
|
|
pub mod serial;
|
|
|
|
pub mod tty;
|
2023-07-18 18:03:45 +03:00
|
|
|
|
2023-08-13 21:23:58 +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
|
|
|
|
2023-08-13 21:23:58 +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
|
|
|
|
2023-08-13 21:23:58 +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
|
|
|
}
|