You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

107 lines
2.7 KiB
Rust

//! Implement the Qemu magic UART.
use core::fmt;
use crate::traits::console::{Console, Read, Write};
use crate::traits::synchronization::{DummyMutex, Mutex};
/// The address for the magic qemu uart
const QEMU_MAGIC_UART_ADDR: *mut u8 = 0x3F20_1000 as *mut u8;
const QEMU_MAGIC_UART_ADDR_FR: *mut u8 = 0x3F20_1018 as *mut u8;
/// The unique QemuUart allowing access to the qemu uart.
static QEMU_UART: QemuUart = QemuUart::new();
/// A structure allowing access to the qemu magic uart.
struct QemuUart {
inner: DummyMutex<QemuUartInner>,
}
/// Inner Qemu Uart.
struct QemuUartInner;
impl QemuUartInner {
/// Constructor for [`QemuUartInner`].
const fn new() -> Self {
Self {}
}
/// Write a character to the output.
fn write_char(&mut self, c: char) {
unsafe {
core::ptr::write_volatile(QEMU_MAGIC_UART_ADDR, c as u8);
}
}
/// Read a character from the uart.
fn read_char(&mut self) -> char {
loop {
let fr = unsafe { core::ptr::read_volatile(QEMU_MAGIC_UART_ADDR_FR) };
if fr & 0b1_0000 == 0 {
break;
}
}
unsafe {
core::ptr::read_volatile(QEMU_MAGIC_UART_ADDR) as u8 as char
}
}
}
impl QemuUart {
/// Constructor for [`QemuUart`].
pub const fn new() -> Self {
Self {
inner: DummyMutex::new(QemuUartInner::new()),
}
}
}
/// Allow to use QemuUartInner for print! and formating macros.
/// `write_str` needs `&mut self` (mutable ref), so we can implement
/// it only on the inner type, the `QemuUart` need to be manipulable
/// using unmutable references (`&self`), so we will use a custom
/// interface for it.
impl fmt::Write for QemuUartInner {
fn write_str(&mut self, s: &str) -> fmt::Result {
for c in s.chars() {
// \n -> \r\n
if c == '\n' {
self.write_char('\r');
}
self.write_char(c);
}
Ok(())
}
}
impl Write for QemuUart {
fn write_char(&self, c: char) {
self.inner.lock(|q_out| q_out.write_char(c))
}
fn write_fmt(&self, args: fmt::Arguments) -> fmt::Result {
self.inner.lock(|q_out| fmt::Write::write_fmt(q_out, args))
}
/// Empty function, the qemu uart has no buffering afaik
fn flush(&self) {
// self.inner.lock(|q_out| q_out.flush())
}
}
impl Read for QemuUart {
fn read_char(&self) -> char {
self.inner.lock(|q_in| q_in.read_char())
}
fn flush_input(&self) {}
}
impl Console for QemuUart {}
// TODO: move?
/// Return a reference to the Qemu Uart.
pub fn console() -> &'static dyn Console {
&QEMU_UART
}