Skip to content

Commit

Permalink
Rework driver subsystem
Browse files Browse the repository at this point in the history
This update significantly decouples the generic kernel code
from the BSP code.
Prior to this patch, the BSP had way too much business logic
that should have always been the generic kernel's concern.
  • Loading branch information
andre-richter committed Oct 23, 2022
1 parent 7014c0c commit 2e72a84
Show file tree
Hide file tree
Showing 236 changed files with 7,448 additions and 4,235 deletions.
442 changes: 351 additions & 91 deletions 05_drivers_gpio_uart/README.md

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion 05_drivers_gpio_uart/src/bsp/raspberrypi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

//! Top-level BSP file for the Raspberry Pi 3 and 4.
pub mod console;
pub mod cpu;
pub mod driver;
pub mod memory;
Expand Down
76 changes: 46 additions & 30 deletions 05_drivers_gpio_uart/src/bsp/raspberrypi/driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,51 +5,67 @@
//! BSP driver support.
use super::memory::map::mmio;
use crate::{bsp::device_driver, driver};
use crate::{bsp::device_driver, console, driver as generic_driver};
use core::sync::atomic::{AtomicBool, Ordering};

//--------------------------------------------------------------------------------------------------
// Private Definitions
// Global instances
//--------------------------------------------------------------------------------------------------

/// Device Driver Manager type.
struct BSPDriverManager {
device_drivers: [&'static (dyn DeviceDriver + Sync); 2],
}
static PL011_UART: device_driver::PL011Uart =
unsafe { device_driver::PL011Uart::new(mmio::PL011_UART_START) };
static GPIO: device_driver::GPIO = unsafe { device_driver::GPIO::new(mmio::GPIO_START) };

//--------------------------------------------------------------------------------------------------
// Global instances
// Private Code
//--------------------------------------------------------------------------------------------------

pub(super) static PL011_UART: device_driver::PL011Uart =
unsafe { device_driver::PL011Uart::new(mmio::PL011_UART_START) };
/// This must be called only after successful init of the UART driver.
fn post_init_uart() -> Result<(), &'static str> {
console::register_console(&PL011_UART);

static GPIO: device_driver::GPIO = unsafe { device_driver::GPIO::new(mmio::GPIO_START) };
Ok(())
}

static BSP_DRIVER_MANAGER: BSPDriverManager = BSPDriverManager {
device_drivers: [&PL011_UART, &GPIO],
};
/// This must be called only after successful init of the GPIO driver.
fn post_init_gpio() -> Result<(), &'static str> {
GPIO.map_pl011_uart();
Ok(())
}

//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
fn driver_uart() -> Result<(), &'static str> {
let uart_descriptor =
generic_driver::DeviceDriverDescriptor::new(&PL011_UART, Some(post_init_uart));
generic_driver::driver_manager().register_driver(uart_descriptor);

/// Return a reference to the driver manager.
pub fn driver_manager() -> &'static impl driver::interface::DriverManager {
&BSP_DRIVER_MANAGER
Ok(())
}

//------------------------------------------------------------------------------
// OS Interface Code
//------------------------------------------------------------------------------
use driver::interface::DeviceDriver;
fn driver_gpio() -> Result<(), &'static str> {
let gpio_descriptor = generic_driver::DeviceDriverDescriptor::new(&GPIO, Some(post_init_gpio));
generic_driver::driver_manager().register_driver(gpio_descriptor);

impl driver::interface::DriverManager for BSPDriverManager {
fn all_device_drivers(&self) -> &[&'static (dyn DeviceDriver + Sync)] {
&self.device_drivers[..]
}
Ok(())
}

//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------

fn post_device_driver_init(&self) {
// Configure PL011Uart's output pins.
GPIO.map_pl011_uart();
/// Initialize the driver subsystem.
///
/// # Safety
///
/// See child function calls.
pub unsafe fn init() -> Result<(), &'static str> {
static INIT_DONE: AtomicBool = AtomicBool::new(false);
if INIT_DONE.load(Ordering::Relaxed) {
return Err("Init already done");
}

driver_uart()?;
driver_gpio()?;

INIT_DONE.store(true, Ordering::Relaxed);
Ok(())
}
21 changes: 18 additions & 3 deletions 05_drivers_gpio_uart/src/console.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@

//! System console.
use crate::bsp;
mod null_console;

use crate::synchronization::{self, NullLock};

//--------------------------------------------------------------------------------------------------
// Public Definitions
Expand Down Expand Up @@ -54,13 +56,26 @@ pub mod interface {
pub trait All: Write + Read + Statistics {}
}

//--------------------------------------------------------------------------------------------------
// Global instances
//--------------------------------------------------------------------------------------------------

static CUR_CONSOLE: NullLock<&'static (dyn interface::All + Sync)> =
NullLock::new(&null_console::NULL_CONSOLE);

//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
use synchronization::interface::Mutex;

/// Register a new console.
pub fn register_console(new_console: &'static (dyn interface::All + Sync)) {
CUR_CONSOLE.lock(|con| *con = new_console);
}

/// Return a reference to the console.
/// Return a reference to the currently registered console.
///
/// This is the global console used by all printing macros.
pub fn console() -> &'static dyn interface::All {
bsp::console::console()
CUR_CONSOLE.lock(|con| *con)
}
41 changes: 41 additions & 0 deletions 05_drivers_gpio_uart/src/console/null_console.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2022 Andre Richter <[email protected]>

//! Null console.
use super::interface;
use core::fmt;

//--------------------------------------------------------------------------------------------------
// Public Definitions
//--------------------------------------------------------------------------------------------------

pub struct NullConsole;

//--------------------------------------------------------------------------------------------------
// Global instances
//--------------------------------------------------------------------------------------------------

pub static NULL_CONSOLE: NullConsole = NullConsole {};

//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------

impl interface::Write for NullConsole {
fn write_char(&self, _c: char) {}

fn write_fmt(&self, _args: fmt::Arguments) -> fmt::Result {
fmt::Result::Ok(())
}

fn flush(&self) {}
}

impl interface::Read for NullConsole {
fn clear_rx(&self) {}
}

impl interface::Statistics for NullConsole {}
impl interface::All for NullConsole {}
149 changes: 136 additions & 13 deletions 05_drivers_gpio_uart/src/driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,22 @@

//! Driver support.
use crate::{
println,
synchronization::{interface::Mutex, NullLock},
};

//--------------------------------------------------------------------------------------------------
// Private Definitions
//--------------------------------------------------------------------------------------------------

const NUM_DRIVERS: usize = 5;

struct DriverManagerInner {
next_index: usize,
descriptors: [Option<DeviceDriverDescriptor>; NUM_DRIVERS],
}

//--------------------------------------------------------------------------------------------------
// Public Definitions
//--------------------------------------------------------------------------------------------------
Expand All @@ -24,21 +40,128 @@ pub mod interface {
Ok(())
}
}
}

/// Tpye to be used as an optional callback after a driver's init() has run.
pub type DeviceDriverPostInitCallback = unsafe fn() -> Result<(), &'static str>;

/// A descriptor for device drivers.
#[derive(Copy, Clone)]
pub struct DeviceDriverDescriptor {
device_driver: &'static (dyn interface::DeviceDriver + Sync),
post_init_callback: Option<DeviceDriverPostInitCallback>,
}

/// Provides device driver management functions.
pub struct DriverManager {
inner: NullLock<DriverManagerInner>,
}

//--------------------------------------------------------------------------------------------------
// Global instances
//--------------------------------------------------------------------------------------------------

static DRIVER_MANAGER: DriverManager = DriverManager::new();

//--------------------------------------------------------------------------------------------------
// Private Code
//--------------------------------------------------------------------------------------------------

impl DriverManagerInner {
/// Create an instance.
pub const fn new() -> Self {
Self {
next_index: 0,
descriptors: [None; NUM_DRIVERS],
}
}
}

//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------

impl DeviceDriverDescriptor {
/// Create an instance.
pub fn new(
device_driver: &'static (dyn interface::DeviceDriver + Sync),
post_init_callback: Option<DeviceDriverPostInitCallback>,
) -> Self {
Self {
device_driver,
post_init_callback,
}
}
}

/// Return a reference to the global DriverManager.
pub fn driver_manager() -> &'static DriverManager {
&DRIVER_MANAGER
}

impl DriverManager {
/// Create an instance.
pub const fn new() -> Self {
Self {
inner: NullLock::new(DriverManagerInner::new()),
}
}

/// Device driver management functions.
/// Register a device driver with the kernel.
pub fn register_driver(&self, descriptor: DeviceDriverDescriptor) {
self.inner.lock(|inner| {
inner.descriptors[inner.next_index] = Some(descriptor);
inner.next_index += 1;
})
}

/// Helper for iterating over registered drivers.
fn for_each_descriptor<'a>(&'a self, f: impl FnMut(&'a DeviceDriverDescriptor)) {
self.inner.lock(|inner| {
inner
.descriptors
.iter()
.filter_map(|x| x.as_ref())
.for_each(f)
})
}

/// Fully initialize all drivers.
///
/// The `BSP` is supposed to supply one global instance.
pub trait DriverManager {
/// Return a slice of references to all `BSP`-instantiated drivers.
///
/// # Safety
///
/// - The order of devices is the order in which `DeviceDriver::init()` is called.
fn all_device_drivers(&self) -> &[&'static (dyn DeviceDriver + Sync)];
/// # Safety
///
/// - During init, drivers might do stuff with system-wide impact.
pub unsafe fn init_drivers(&self) {
self.for_each_descriptor(|descriptor| {
// 1. Initialize driver.
if let Err(x) = descriptor.device_driver.init() {
panic!(
"Error initializing driver: {}: {}",
descriptor.device_driver.compatible(),
x
);
}

/// Initialization code that runs after driver init.
///
/// For example, device driver code that depends on other drivers already being online.
fn post_device_driver_init(&self);
// 2. Call corresponding post init callback.
if let Some(callback) = &descriptor.post_init_callback {
if let Err(x) = callback() {
panic!(
"Error during driver post-init callback: {}: {}",
descriptor.device_driver.compatible(),
x
);
}
}
});
}

/// Enumerate all registered device drivers.
pub fn enumerate(&self) {
let mut i: usize = 1;
self.for_each_descriptor(|descriptor| {
println!(" {}. {}", i, descriptor.device_driver.compatible());

i += 1;
});
}
}
22 changes: 7 additions & 15 deletions 05_drivers_gpio_uart/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,14 +129,13 @@ mod synchronization;
/// - Only a single core must be active and running this function.
/// - The init calls in this function must appear in the correct order.
unsafe fn kernel_init() -> ! {
use driver::interface::DriverManager;

for i in bsp::driver::driver_manager().all_device_drivers().iter() {
if let Err(x) = i.init() {
panic!("Error loading driver: {}: {}", i.compatible(), x);
}
// Initialize the BSP driver subsystem.
if let Err(x) = bsp::driver::init() {
panic!("Error initializing BSP driver subsystem: {}", x);
}
bsp::driver::driver_manager().post_device_driver_init();

// Initialize all device drivers.
driver::driver_manager().init_drivers();
// println! is usable from here on.

// Transition from unsafe to safe.
Expand All @@ -146,7 +145,6 @@ unsafe fn kernel_init() -> ! {
/// The main function running after the early init.
fn kernel_main() -> ! {
use console::console;
use driver::interface::DriverManager;

println!(
"[0] {} version {}",
Expand All @@ -156,13 +154,7 @@ fn kernel_main() -> ! {
println!("[1] Booting on: {}", bsp::board_name());

println!("[2] Drivers loaded:");
for (i, driver) in bsp::driver::driver_manager()
.all_device_drivers()
.iter()
.enumerate()
{
println!(" {}. {}", i + 1, driver.compatible());
}
driver::driver_manager().enumerate();

println!("[3] Chars written: {}", console().chars_written());
println!("[4] Echoing input now");
Expand Down
Loading

0 comments on commit 2e72a84

Please sign in to comment.