kernel/src/uart.rs

89 lines
2.5 KiB
Rust

// src/uart.rs
//! This module provides access to the UART console.
use spin::Mutex; // Replaces spinning_top crate
pub static CONSOLE: Mutex<Option<Device>> = Mutex::new(None);
/// Represents an initialised UART device.
pub struct Device {
base: usize,
}
impl Device {
/// Create a new UART device.
/// # Safety
/// `base` must be the base address of a UART device.
pub fn new(base: usize) -> Self {
use core::ptr::write_volatile;
let addr = base as *mut u8;
// Set data size to 8 bits.
unsafe { write_volatile(addr.offset(3), 0b11) };
// Enable FIFO.
unsafe { write_volatile(addr.offset(2), 0b1) };
// Enable receiver buffer interrupts.
unsafe { write_volatile(addr.offset(1), 0b1) };
// Return a new, initialised UART device.
Device { base }
}
pub fn put(&mut self, character: u8) {
let ptr = self.base as *mut u8;
// UNSAFE: fine as long as self.base is valid
unsafe {
core::ptr::write_volatile(ptr, character);
}
}
/// Gets a character from the UART input.
pub fn get(&mut self) -> Option<u8> {
const READY: u8 = 0b1;
let ptr = self.base as *mut u8;
// SAFETY: Fine as long as base is correct.
let lsr = unsafe { ptr.offset(5) };
unsafe {
if core::ptr::read_volatile(lsr) & READY == READY {
Some(core::ptr::read_volatile(ptr))
} else {
None
}
}
}
}
impl core::fmt::Write for Device {
fn write_str(&mut self, s: &str) -> core::fmt::Result {
for c in s.bytes() {
self.put(c);
}
Ok(()) // there are never errors writing to UART :)
}
}
/// Initialise the UART debugging console.
/// # Safety
/// `base` must point to the base address of a UART device.
pub fn init_console(base: usize) {
let mut console = CONSOLE.lock();
*console = Some(Device::new(base));
}
/// Prints a formatted string to the [CONSOLE].
#[macro_export]
macro_rules! print {
($($arg:tt)*) => ({
use core::fmt::Write;
$crate::uart::CONSOLE.lock().as_mut().map(|writer| {
writer.write_fmt(format_args!($($arg)*)).unwrap()
});
});
}
/// println prints a formatted string to the [CONSOLE] with a trailing newline character.
#[macro_export]
macro_rules! println {
($fmt:expr) => ($crate::print!(concat!($fmt, "\n")));
($fmt:expr, $($arg:tt)*) => ($crate::print!(concat!($fmt, "\n"), $($arg)*));
}