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