Define console trait Write and implement POC for the QEMU uart

This commit is contained in:
histausse 2022-11-15 23:37:52 +01:00
parent 571a2c995e
commit ca47faf168
Signed by: histausse
GPG key ID: 67486F107F62E9E9
4 changed files with 99 additions and 30 deletions

View file

@ -2,26 +2,26 @@
use core::fmt; use core::fmt;
use crate::traits::console::{Console, Write}; use crate::traits::console::{Console, Read, Write};
use crate::traits::synchronization::{DummyMutex, Mutex}; use crate::traits::synchronization::{DummyMutex, Mutex};
/// The address for the magic qemu output /// The address for the magic qemu uart
const QEMU_MAGIC_OUTPUT_ADDR: *mut u8 = 0x3F20_1000 as *mut u8; 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 qemu output allowing access to the qemu output. /// The unique QemuUart allowing access to the qemu uart.
static QEMU_OUTPUT: QemuOutput = QemuOutput::new(); static QEMU_UART: QemuUart = QemuUart::new();
/// A structure allowing access to the qemu magic output. /// A structure allowing access to the qemu magic uart.
struct QemuOutput { struct QemuUart {
inner: DummyMutex<QemuOutputInner>, inner: DummyMutex<QemuUartInner>,
} }
/// Inner Qemu output. /// Inner Qemu Uart.
struct QemuOutputInner; struct QemuUartInner;
impl QemuOutputInner { impl QemuUartInner {
/// Constructor for [`QemuOutputInner`]. /// Constructor for [`QemuUartInner`].
const fn new() -> Self { const fn new() -> Self {
Self {} Self {}
} }
@ -29,26 +29,39 @@ impl QemuOutputInner {
/// Write a character to the output. /// Write a character to the output.
fn write_char(&mut self, c: char) { fn write_char(&mut self, c: char) {
unsafe { unsafe {
core::ptr::write_volatile(QEMU_MAGIC_OUTPUT_ADDR, c as u8); 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 QemuOutput { impl QemuUart {
/// Constructor for [`QemuOutput`]. /// Constructor for [`QemuUart`].
pub const fn new() -> Self { pub const fn new() -> Self {
Self { Self {
inner: DummyMutex::new(QemuOutputInner::new()), inner: DummyMutex::new(QemuUartInner::new()),
} }
} }
} }
/// Allow to use QemuOutputInner for print! and formating macros. /// Allow to use QemuUartInner for print! and formating macros.
/// `write_str` needs `&mut self` (mutable ref), so we can implement /// `write_str` needs `&mut self` (mutable ref), so we can implement
/// it only on the inner type, the `QemuOutput` need to be manipulable /// it only on the inner type, the `QemuUart` need to be manipulable
/// using unmutable references (`&self`), so we will use a custom /// using unmutable references (`&self`), so we will use a custom
/// interface for it. /// interface for it.
impl fmt::Write for QemuOutputInner { impl fmt::Write for QemuUartInner {
fn write_str(&mut self, s: &str) -> fmt::Result { fn write_str(&mut self, s: &str) -> fmt::Result {
for c in s.chars() { for c in s.chars() {
// \n -> \r\n // \n -> \r\n
@ -61,7 +74,7 @@ impl fmt::Write for QemuOutputInner {
} }
} }
impl Write for QemuOutput { impl Write for QemuUart {
fn write_char(&self, c: char) { fn write_char(&self, c: char) {
self.inner.lock(|q_out| q_out.write_char(c)) self.inner.lock(|q_out| q_out.write_char(c))
} }
@ -76,10 +89,18 @@ impl Write for QemuOutput {
} }
} }
impl Console for QemuOutput {} 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? // TODO: move?
/// Return a reference to the Qemu Output. /// Return a reference to the Qemu Uart.
pub fn console() -> &'static dyn Console { pub fn console() -> &'static dyn Console {
&QEMU_OUTPUT &QEMU_UART
} }

View file

@ -3,7 +3,7 @@
use core::arch::asm; use core::arch::asm;
use core::fmt; use core::fmt;
use crate::traits::console::{Write, Console}; use crate::traits::console::{Console, Read, Write};
use crate::traits::synchronization::{DummyMutex, Mutex}; use crate::traits::synchronization::{DummyMutex, Mutex};
use super::memory_map::uart as mm; use super::memory_map::uart as mm;
@ -120,6 +120,14 @@ impl Write for Uart<'_> {
} }
} }
impl Read for Uart<'_> {
fn read_char(&self) -> char {
todo!()
}
fn flush_input(&self) {}
}
impl<'a> Uart<'a> { impl<'a> Uart<'a> {
// TODO: not sure this should be public? public in bsp only? // TODO: not sure this should be public? public in bsp only?

View file

@ -27,9 +27,9 @@ use core::arch::global_asm;
use core::arch::asm; use core::arch::asm;
// TODO: handle this with features // TODO: handle this with features
//use crate::bsp::qemu::console::console; use crate::bsp::qemu::console::console;
#[cfg(not(test))] //#[cfg(not(test))]
use crate::bsp::rpi3::uart::console; //use crate::bsp::rpi3::uart::console;
#[cfg(not(test))] #[cfg(not(test))]
use crate::bsp::rpi3::gpio; use crate::bsp::rpi3::gpio;
@ -66,9 +66,29 @@ pub unsafe fn _start_rust() -> ! {
match gpio::set_pin_output_state(20, gpio::PinOutputState::Low) { match gpio::set_pin_output_state(20, gpio::PinOutputState::Low) {
_ => (), _ => (),
} }
bsp::rpi3::uart::init(); //bsp::rpi3::uart::init();
println!("Hello there"); println!("Hello there");
let mut buffer = ['X'; 200];
let mut i = 0;
let mut c = console().read_char();
console().write_char(c);
while c != '\r' && i < 199 {
buffer[i] = c;
c = console().read_char();
console().write_char(c);
i += 1;
}
buffer[i] = c;
i += 1;
let mut j = 0;
print!("Received: ");
while j < i {
print!("{}", buffer[j]);
j += 1;
}
println!("");
match gpio::set_pin_fonction(21, gpio::PinFunction::Output) { match gpio::set_pin_fonction(21, gpio::PinFunction::Output) {
Ok(()) => println!("Successfully set pin to output"), Ok(()) => println!("Successfully set pin to output"),
Err(err) => println!("Failled to set pin: {err}"), Err(err) => println!("Failled to set pin: {err}"),

View file

@ -3,7 +3,7 @@
use core::fmt; use core::fmt;
/// Trait allowing a structure to be used as a console. /// Trait allowing a structure to be used as a console.
pub trait Console: Write {} pub trait Console: Write + Read {}
/// Trait allowing to write to an object. /// Trait allowing to write to an object.
pub trait Write { pub trait Write {
@ -17,6 +17,17 @@ pub trait Write {
fn flush(&self); fn flush(&self);
} }
/// Trait allowing to read from an object.
pub trait Read {
// TODO: return an option?
/// Read a single character.
fn read_char(&self) -> char;
/// Clear the input buffer if any.
fn flush_input(&self);
}
/// A Dummy Console. /// A Dummy Console.
/// ///
/// The DummyConsole implement the [`Console`] trait, and do nothing. /// The DummyConsole implement the [`Console`] trait, and do nothing.
@ -32,4 +43,13 @@ impl Write for DummyConsole {
fn flush(&self) {} fn flush(&self) {}
} }
impl Read for DummyConsole {
fn read_char(&self) -> char {
' '
}
fn flush_input(&self) {}
}
impl Console for DummyConsole {} impl Console for DummyConsole {}