use alloc::{sync::Arc, vec::Vec};
use ax_errno::{AxResult, ax_err};
use axdevice_base::{InterruptTriggerMode, IrqLine};
use axvm_types::{EmulatedDeviceConfig, EmulatedDeviceType};
use crate::DeviceBundle;
pub trait IrqResolver: Send + Sync {
fn resolve_irq(&self, line: usize, trigger: InterruptTriggerMode) -> AxResult<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) -> AxResult<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<'_>,
) -> AxResult<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>) -> AxResult {
let device_type = factory.device_type();
if self.get(device_type).is_some() {
return ax_err!(
AlreadyExists,
format_args!("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<'_>,
) -> AxResult<DeviceBundle> {
let Some(factory) = self.get(config.emu_type) else {
return ax_err!(
Unsupported,
format_args!(
"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<'_>,
) -> AxResult<DeviceBundle> {
Ok(DeviceBundle::new())
}
}
pub fn register_builtin_factories(registry: &mut DeviceFactoryRegistry) -> AxResult {
registry.register(Arc::new(MetaDeviceFactory))
}