use alloc::{sync::Arc, vec::Vec};
use axdevice_base::{InterruptTriggerMode, IrqLine};
use axvm_types::{EmulatedDeviceConfig, EmulatedDeviceType};
use crate::{DeviceBundle, DeviceManagerError, DeviceManagerResult};
pub trait IrqResolver: Send + Sync {
fn resolve_irq(
&self,
line: usize,
trigger: InterruptTriggerMode,
) -> DeviceManagerResult<IrqLine>;
}
pub struct DeviceBuildContext<'a> {
irq_resolver: &'a dyn IrqResolver,
}
impl<'a> DeviceBuildContext<'a> {
pub const fn new(irq_resolver: &'a dyn IrqResolver) -> Self {
Self { irq_resolver }
}
pub fn resolve_irq(
&self,
line: usize,
trigger: InterruptTriggerMode,
) -> DeviceManagerResult<IrqLine> {
self.irq_resolver.resolve_irq(line, trigger)
}
}
pub trait DeviceFactory: Send + Sync {
fn device_type(&self) -> EmulatedDeviceType;
fn build(
&self,
config: &EmulatedDeviceConfig,
context: &DeviceBuildContext<'_>,
) -> DeviceManagerResult<DeviceBundle>;
}
#[derive(Default)]
pub struct DeviceFactoryRegistry {
factories: Vec<(EmulatedDeviceType, Arc<dyn DeviceFactory>)>,
}
impl DeviceFactoryRegistry {
pub const fn new() -> Self {
Self {
factories: Vec::new(),
}
}
pub fn register(&mut self, factory: Arc<dyn DeviceFactory>) -> DeviceManagerResult {
let device_type = factory.device_type();
if self.get(device_type).is_some() {
return Err(DeviceManagerError::ResourceConflict {
operation: "register device factory",
detail: alloc::format!(
"factory for device type {device_type} is already registered"
),
});
}
self.factories.push((device_type, factory));
Ok(())
}
pub fn get(&self, device_type: EmulatedDeviceType) -> Option<&dyn DeviceFactory> {
self.factories
.iter()
.find(|(registered_type, _)| *registered_type == device_type)
.map(|(_, factory)| factory.as_ref())
}
pub fn build(
&self,
config: &EmulatedDeviceConfig,
context: &DeviceBuildContext<'_>,
) -> DeviceManagerResult<DeviceBundle> {
let Some(factory) = self.get(config.emu_type) else {
return Err(DeviceManagerError::Unsupported {
operation: "build emulated device",
detail: alloc::format!(
"no factory is registered for emulated device '{}' of type {}",
config.name,
config.emu_type
),
});
};
factory.build(config, context)
}
}
struct MetaDeviceFactory;
impl DeviceFactory for MetaDeviceFactory {
fn device_type(&self) -> EmulatedDeviceType {
EmulatedDeviceType::Dummy
}
fn build(
&self,
_config: &EmulatedDeviceConfig,
_context: &DeviceBuildContext<'_>,
) -> DeviceManagerResult<DeviceBundle> {
Ok(DeviceBundle::new())
}
}
pub fn register_builtin_factories(registry: &mut DeviceFactoryRegistry) -> DeviceManagerResult {
registry.register(Arc::new(MetaDeviceFactory))
}