1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195
//! [ArceOS](https://github.com/rcore-os/arceos) device drivers.
//!
//! # Usage
//!
//! All detected devices are composed into a large struct [`AllDevices`]
//! and returned by the [`init_drivers`] function. The upperlayer subsystems
//! (e.g., the network stack) may unpack the struct to get the specified device
//! driver they want.
//!
//! For each device category (i.e., net, block, display, etc.), an unified type
//! is used to represent all devices in that category. Currently, there are 3
//! categories: [`AxNetDevice`], [`AxBlockDevice`], and [`AxDisplayDevice`].
//!
//! # Concepts
//!
//! This crate supports two device models depending on the `dyn` feature:
//!
//! - **Static**: The type of all devices is static, it is determined at compile
//! time by corresponding cargo features. For example, [`AxNetDevice`] will be
//! an alias of [`VirtioNetDev`] if the `virtio-net` feature is enabled. This
//! model provides the best performance as it avoids dynamic dispatch. But on
//! limitation, only one device instance is supported for each device category.
//! - **Dynamic**: All device instance is using [trait objects] and wrapped in a
//! `Box<dyn Trait>`. For example, [`AxNetDevice`] will be [`Box<dyn NetDriverOps>`].
//! When call a method provided by the device, it uses [dynamic dispatch][dyn]
//! that may introduce a little overhead. But on the other hand, it is more
//! flexible, multiple instances of each device category are supported.
//!
//! # Supported Devices
//!
//! | Device Category | Cargo Feature | Description |
//! |-|-|-|
//! | Block | `ramdisk` | A RAM disk that stores data in a vector |
//! | Block | `virtio-blk` | VirtIO block device |
//! | Network | `virtio-net` | VirtIO network device |
//! | Display | `virtio-gpu` | VirtIO graphics device |
//!
//! # Other Cargo Features
//!
//! - `dyn`: use the dynamic device model (see above).
//! - `bus-mmio`: use device tree to probe all MMIO devices. This feature is
//! enabeld by default.
//! - `bus-pci`: use PCI bus to probe all PCI devices.
//! - `virtio`: use VirtIO devices. This is enabled if any of `virtio-blk`,
//! `virtio-net` or `virtio-gpu` is enabled.
//! - `net`: use network devices. This is enabled if any feature of network
//! devices is selected. If this feature is enabled without any network device
//! features, a dummy struct is used for [`AxNetDevice`].
//! - `block`: use block storage devices. Similar to the `net` feature.
//! - `display`: use graphics display devices. Similar to the `net` feature.
//!
//! [`VirtioNetDev`]: driver_virtio::VirtIoNetDev
//! [`Box<dyn NetDriverOps>`]: driver_net::NetDriverOps
//! [trait objects]: https://doc.rust-lang.org/book/ch17-02-trait-objects.html
//! [dyn]: https://doc.rust-lang.org/std/keyword.dyn.html
#![no_std]
#![feature(doc_auto_cfg)]
#![feature(associated_type_defaults)]
#[macro_use]
extern crate log;
#[cfg(feature = "dyn")]
extern crate alloc;
#[macro_use]
mod macros;
mod bus;
mod drivers;
mod dummy;
mod structs;
#[cfg(feature = "virtio")]
mod virtio;
#[cfg(feature = "ixgbe")]
mod ixgbe;
pub mod prelude;
#[allow(unused_imports)]
use self::prelude::*;
pub use self::structs::{AxDeviceContainer, AxDeviceEnum};
#[cfg(feature = "block")]
pub use self::structs::AxBlockDevice;
#[cfg(feature = "display")]
pub use self::structs::AxDisplayDevice;
#[cfg(feature = "net")]
pub use self::structs::AxNetDevice;
/// A structure that contains all device drivers, organized by their category.
#[derive(Default)]
pub struct AllDevices {
/// All network device drivers.
#[cfg(feature = "net")]
pub net: AxDeviceContainer<AxNetDevice>,
/// All block device drivers.
#[cfg(feature = "block")]
pub block: AxDeviceContainer<AxBlockDevice>,
/// All graphics device drivers.
#[cfg(feature = "display")]
pub display: AxDeviceContainer<AxDisplayDevice>,
}
impl AllDevices {
/// Returns the device model used, either `dyn` or `static`.
///
/// See the [crate-level documentation](crate) for more details.
pub const fn device_model() -> &'static str {
if cfg!(feature = "dyn") {
"dyn"
} else {
"static"
}
}
/// Probes all supported devices.
fn probe(&mut self) {
for_each_drivers!(type Driver, {
if let Some(dev) = Driver::probe_global() {
info!(
"registered a new {:?} device: {:?}",
dev.device_type(),
dev.device_name(),
);
self.add_device(dev);
}
});
self.probe_bus_devices();
}
/// Adds one device into the corresponding container, according to its device category.
#[allow(dead_code)]
fn add_device(&mut self, dev: AxDeviceEnum) {
match dev {
#[cfg(feature = "net")]
AxDeviceEnum::Net(dev) => self.net.push(dev),
#[cfg(feature = "block")]
AxDeviceEnum::Block(dev) => self.block.push(dev),
#[cfg(feature = "display")]
AxDeviceEnum::Display(dev) => self.display.push(dev),
}
}
}
/// Probes and initializes all device drivers, returns the [`AllDevices`] struct.
pub fn init_drivers() -> AllDevices {
info!("Initialize device drivers...");
info!(" device model: {}", AllDevices::device_model());
let mut all_devs = AllDevices::default();
#[cfg(feature = "img")]
{
use axconfig::{PHYS_VIRT_OFFSET, TESTCASE_MEMORY_SIZE, TESTCASE_MEMORY_START};
let mut ram_disk = driver_block::ramdisk::RamDisk::new(TESTCASE_MEMORY_SIZE);
unsafe {
ram_disk.copy_from_slice((TESTCASE_MEMORY_START + PHYS_VIRT_OFFSET) as *const u8)
};
all_devs.add_device(AxDeviceEnum::from_block(ram_disk));
}
all_devs.probe();
#[cfg(feature = "net")]
{
debug!("number of NICs: {}", all_devs.net.len());
for (i, dev) in all_devs.net.iter().enumerate() {
assert_eq!(dev.device_type(), DeviceType::Net);
debug!(" NIC {}: {:?}", i, dev.device_name());
}
}
#[cfg(feature = "block")]
{
debug!("number of block devices: {}", all_devs.block.len());
for (i, dev) in all_devs.block.iter().enumerate() {
assert_eq!(dev.device_type(), DeviceType::Block);
debug!(" block device {}: {:?}", i, dev.device_name());
}
}
#[cfg(feature = "display")]
{
debug!("number of graphics devices: {}", all_devs.display.len());
for (i, dev) in all_devs.display.iter().enumerate() {
assert_eq!(dev.device_type(), DeviceType::Display);
debug!(" graphics device {}: {:?}", i, dev.device_name());
}
}
all_devs
}