feat!: ✨ Update ConsoleOps
Split ConsoleOps vs SerialOps, rename console methods, make them saner. Add docs. Add clear_rx() fn. Drop default trait impls for safety.
This commit is contained in:
parent
16ec45b97c
commit
61eb2f9538
|
@ -4,26 +4,51 @@
|
|||
|
||||
#![allow(dead_code)]
|
||||
|
||||
use {crate::platform, core::fmt};
|
||||
use {
|
||||
crate::{devices::SerialOps, platform},
|
||||
core::fmt,
|
||||
};
|
||||
|
||||
/// A trait that must be implemented by devices that are candidates for the
|
||||
/// global console.
|
||||
#[allow(unused_variables)]
|
||||
pub trait ConsoleOps {
|
||||
fn putc(&self, c: char) {}
|
||||
fn puts(&self, string: &str) {}
|
||||
fn getc(&self) -> char {
|
||||
' '
|
||||
}
|
||||
fn flush(&self) {}
|
||||
pub trait ConsoleOps: SerialOps {
|
||||
/// Send a character
|
||||
fn write_char(&self, c: char);
|
||||
/// Display a string
|
||||
fn write_string(&self, string: &str);
|
||||
/// Receive a character
|
||||
fn read_char(&self) -> char;
|
||||
}
|
||||
|
||||
/// A dummy console that just ignores its inputs.
|
||||
pub struct NullConsole;
|
||||
|
||||
impl Drop for NullConsole {
|
||||
fn drop(&mut self) {}
|
||||
}
|
||||
impl ConsoleOps for NullConsole {}
|
||||
|
||||
impl ConsoleOps for NullConsole {
|
||||
fn write_char(&self, _c: char) {}
|
||||
|
||||
fn write_string(&self, _string: &str) {}
|
||||
|
||||
fn read_char(&self) -> char {
|
||||
' '
|
||||
}
|
||||
}
|
||||
|
||||
impl SerialOps for NullConsole {
|
||||
fn read_byte(&self) -> u8 {
|
||||
0
|
||||
}
|
||||
|
||||
fn write_byte(&self, _byte: u8) {}
|
||||
|
||||
fn flush(&self) {}
|
||||
|
||||
fn clear_rx(&self) {}
|
||||
}
|
||||
|
||||
/// Possible outputs which the console can store.
|
||||
pub enum Output {
|
||||
|
@ -83,15 +108,15 @@ impl Console {
|
|||
|
||||
/// A command prompt.
|
||||
pub fn command_prompt<'a>(&self, buf: &'a mut [u8]) -> &'a [u8] {
|
||||
self.puts("\n$> ");
|
||||
self.write_string("\n$> ");
|
||||
|
||||
let mut i = 0;
|
||||
let mut input;
|
||||
loop {
|
||||
input = self.getc();
|
||||
input = self.read_char();
|
||||
|
||||
if input == '\n' {
|
||||
self.puts("\n"); // do \r\n output
|
||||
self.write_char('\n'); // do \r\n output
|
||||
return &buf[..i];
|
||||
} else {
|
||||
if i < buf.len() {
|
||||
|
@ -101,7 +126,7 @@ impl Console {
|
|||
return &buf[..i];
|
||||
}
|
||||
|
||||
self.putc(input);
|
||||
self.write_char(input);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -113,21 +138,32 @@ impl Drop for Console {
|
|||
|
||||
/// Dispatch the respective function to the currently stored output device.
|
||||
impl ConsoleOps for Console {
|
||||
fn putc(&self, c: char) {
|
||||
self.current_ptr().putc(c);
|
||||
fn write_char(&self, c: char) {
|
||||
self.current_ptr().write_char(c);
|
||||
}
|
||||
|
||||
fn puts(&self, string: &str) {
|
||||
self.current_ptr().puts(string);
|
||||
fn write_string(&self, string: &str) {
|
||||
self.current_ptr().write_string(string);
|
||||
}
|
||||
|
||||
fn getc(&self) -> char {
|
||||
self.current_ptr().getc()
|
||||
fn read_char(&self) -> char {
|
||||
self.current_ptr().read_char()
|
||||
}
|
||||
}
|
||||
|
||||
impl SerialOps for Console {
|
||||
fn read_byte(&self) -> u8 {
|
||||
self.current_ptr().read_byte()
|
||||
}
|
||||
fn write_byte(&self, byte: u8) {
|
||||
self.current_ptr().write_byte(byte)
|
||||
}
|
||||
fn flush(&self) {
|
||||
self.current_ptr().flush()
|
||||
}
|
||||
fn clear_rx(&self) {
|
||||
self.current_ptr().clear_rx()
|
||||
}
|
||||
}
|
||||
|
||||
/// Implementing this trait enables usage of the format_args! macros, which in
|
||||
|
@ -136,8 +172,7 @@ impl ConsoleOps for Console {
|
|||
/// See src/macros.rs.
|
||||
impl fmt::Write for Console {
|
||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||
self.current_ptr().puts(s);
|
||||
|
||||
self.current_ptr().write_string(s);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,5 +2,9 @@
|
|||
* SPDX-License-Identifier: BlueOak-1.0.0
|
||||
*/
|
||||
pub mod console;
|
||||
pub mod serial;
|
||||
|
||||
pub use console::{Console, ConsoleOps};
|
||||
pub use {
|
||||
console::{Console, ConsoleOps},
|
||||
serial::SerialOps,
|
||||
};
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
pub trait SerialOps {
|
||||
/// Read one byte from serial without translation.
|
||||
fn read_byte(&self) -> u8;
|
||||
/// Write one byte to serial without translation.
|
||||
fn write_byte(&self, byte: u8);
|
||||
/// Wait until the TX FIFO is empty, aka all characters have been put on the
|
||||
/// line.
|
||||
fn flush(&self);
|
||||
/// Consume input until RX FIFO is empty, aka all pending characters have been
|
||||
/// consumed.
|
||||
fn clear_rx(&self);
|
||||
}
|
|
@ -9,7 +9,10 @@
|
|||
use tock_registers::interfaces::{Readable, Writeable};
|
||||
use {
|
||||
super::{gpio, BcmHost},
|
||||
crate::{devices::ConsoleOps, platform::MMIODerefWrapper},
|
||||
crate::{
|
||||
devices::{ConsoleOps, SerialOps},
|
||||
platform::MMIODerefWrapper,
|
||||
},
|
||||
cfg_if::cfg_if,
|
||||
core::{convert::From, fmt},
|
||||
tock_registers::{
|
||||
|
@ -212,44 +215,24 @@ impl Drop for PreparedMiniUart {
|
|||
}
|
||||
}
|
||||
|
||||
impl ConsoleOps for PreparedMiniUart {
|
||||
impl SerialOps for PreparedMiniUart {
|
||||
cfg_if! {
|
||||
if #[cfg(not(feature = "noserial"))] {
|
||||
/// Send a character
|
||||
fn putc(&self, c: char) {
|
||||
// wait until we can send
|
||||
crate::arch::loop_until(|| self.0.registers.AUX_MU_LSR.is_set(AUX_MU_LSR::TX_EMPTY));
|
||||
|
||||
// write the character to the buffer
|
||||
self.0.registers.AUX_MU_IO.set(c as u32);
|
||||
}
|
||||
|
||||
/// Display a string
|
||||
fn puts(&self, string: &str) {
|
||||
for c in string.chars() {
|
||||
// convert newline to carriage return + newline
|
||||
if c == '\n' {
|
||||
self.putc('\r')
|
||||
}
|
||||
|
||||
self.putc(c);
|
||||
}
|
||||
}
|
||||
|
||||
/// Receive a character
|
||||
fn getc(&self) -> char {
|
||||
/// Receive a byte without console translation
|
||||
fn read_byte(&self) -> u8 {
|
||||
// wait until something is in the buffer
|
||||
crate::arch::loop_until(|| self.0.registers.AUX_MU_LSR.is_set(AUX_MU_LSR::DATA_READY));
|
||||
|
||||
// read it and return
|
||||
let mut ret = self.0.registers.AUX_MU_IO.get() as u8 as char;
|
||||
self.0.registers.AUX_MU_IO.get() as u8
|
||||
}
|
||||
|
||||
// convert carriage return to newline
|
||||
if ret == '\r' {
|
||||
ret = '\n'
|
||||
}
|
||||
fn write_byte(&self, b: u8) {
|
||||
// wait until we can send
|
||||
crate::arch::loop_until(|| self.0.registers.AUX_MU_LSR.is_set(AUX_MU_LSR::TX_EMPTY));
|
||||
|
||||
ret
|
||||
// write the character to the buffer
|
||||
self.0.registers.AUX_MU_IO.set(b as u32);
|
||||
}
|
||||
|
||||
/// Wait until the TX FIFO is empty, aka all characters have been put on the
|
||||
|
@ -257,20 +240,69 @@ impl ConsoleOps for PreparedMiniUart {
|
|||
fn flush(&self) {
|
||||
crate::arch::loop_until(|| self.0.registers.AUX_MU_LSR.is_set(AUX_MU_LSR::TX_IDLE));
|
||||
}
|
||||
|
||||
/// Consume input until RX FIFO is empty, aka all pending characters have been
|
||||
/// consumed.
|
||||
fn clear_rx(&self) {
|
||||
crate::arch::loop_while(|| {
|
||||
let pending = self.0.registers.AUX_MU_LSR.is_set(AUX_MU_LSR::DATA_READY);
|
||||
if pending { self.read_char(); }
|
||||
pending
|
||||
});
|
||||
}
|
||||
} else {
|
||||
fn putc(&self, _c: char) {}
|
||||
fn puts(&self, _string: &str) {}
|
||||
fn getc(&self) -> char {
|
||||
fn read_byte(&self) -> u8 { 0 }
|
||||
fn write_byte(&self, _byte: u8) {}
|
||||
fn flush(&self) {}
|
||||
fn clear_rx(&self) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ConsoleOps for PreparedMiniUart {
|
||||
cfg_if! {
|
||||
if #[cfg(not(feature = "noserial"))] {
|
||||
/// Send a character
|
||||
fn write_char(&self, c: char) {
|
||||
self.write_byte(c as u8);
|
||||
}
|
||||
|
||||
/// Display a string
|
||||
fn write_string(&self, string: &str) {
|
||||
for c in string.chars() {
|
||||
// convert newline to carriage return + newline
|
||||
if c == '\n' {
|
||||
self.write_char('\r')
|
||||
}
|
||||
|
||||
self.write_char(c);
|
||||
}
|
||||
}
|
||||
|
||||
/// Receive a character
|
||||
fn read_char(&self) -> char {
|
||||
let mut ret = self.read_byte() as char;
|
||||
|
||||
// convert carriage return to newline -- this doesn't work well for reading binaries...
|
||||
if ret == '\r' {
|
||||
ret = '\n'
|
||||
}
|
||||
|
||||
ret
|
||||
}
|
||||
} else {
|
||||
fn write_char(&self, _c: char) {}
|
||||
fn write_string(&self, _string: &str) {}
|
||||
fn read_char(&self) -> char {
|
||||
'\n'
|
||||
}
|
||||
fn flush(&self) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Write for PreparedMiniUart {
|
||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||
self.puts(s);
|
||||
self.write_string(s);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,11 @@ use {
|
|||
mailbox::{self, MailboxOps},
|
||||
BcmHost,
|
||||
},
|
||||
crate::{arch::loop_until, devices::ConsoleOps, platform::MMIODerefWrapper},
|
||||
crate::{
|
||||
arch::loop_until,
|
||||
devices::{ConsoleOps, SerialOps},
|
||||
platform::MMIODerefWrapper,
|
||||
},
|
||||
snafu::Snafu,
|
||||
tock_registers::{
|
||||
interfaces::{Readable, Writeable},
|
||||
|
@ -237,35 +241,49 @@ impl Drop for PreparedPL011Uart {
|
|||
}
|
||||
}
|
||||
|
||||
impl ConsoleOps for PreparedPL011Uart {
|
||||
/// Send a character
|
||||
fn putc(&self, c: char) {
|
||||
impl SerialOps for PreparedPL011Uart {
|
||||
fn write_byte(&self, b: u8) {
|
||||
// wait until we can send
|
||||
loop_until(|| !self.0.registers.FR.is_set(FR::TXFF));
|
||||
|
||||
// write the character to the buffer
|
||||
self.0.registers.DR.set(c as u32);
|
||||
self.0.registers.DR.set(b as u32);
|
||||
}
|
||||
|
||||
/// Display a string
|
||||
fn puts(&self, string: &str) {
|
||||
for c in string.chars() {
|
||||
// convert newline to carriage return + newline
|
||||
if c == '\n' {
|
||||
self.putc('\r')
|
||||
}
|
||||
|
||||
self.putc(c);
|
||||
}
|
||||
}
|
||||
|
||||
/// Receive a character
|
||||
fn getc(&self) -> char {
|
||||
fn read_byte(&self) -> u8 {
|
||||
// wait until something is in the buffer
|
||||
loop_until(|| !self.0.registers.FR.is_set(FR::RXFE));
|
||||
|
||||
// read it and return
|
||||
let mut ret = self.0.registers.DR.get() as u8 as char;
|
||||
self.0.registers.DR.get() as u8
|
||||
}
|
||||
|
||||
fn flush(&self) {}
|
||||
|
||||
fn clear_rx(&self) {}
|
||||
}
|
||||
|
||||
impl ConsoleOps for PreparedPL011Uart {
|
||||
/// Send a character
|
||||
fn write_char(&self, c: char) {
|
||||
self.write_byte(c as u8)
|
||||
}
|
||||
|
||||
/// Display a string
|
||||
fn write_string(&self, string: &str) {
|
||||
for c in string.chars() {
|
||||
// convert newline to carriage return + newline
|
||||
if c == '\n' {
|
||||
self.write_char('\r')
|
||||
}
|
||||
|
||||
self.write_char(c);
|
||||
}
|
||||
}
|
||||
|
||||
/// Receive a character
|
||||
fn read_char(&self) -> char {
|
||||
let mut ret = self.read_byte() as char;
|
||||
|
||||
// convert carriage return to newline
|
||||
if ret == '\r' {
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
|
||||
#[cfg(not(test))]
|
||||
use core::panic::PanicInfo;
|
||||
#[allow(unused_imports)]
|
||||
use machine::devices::SerialOps;
|
||||
use {
|
||||
cfg_if::cfg_if,
|
||||
machine::{
|
||||
|
@ -96,7 +98,6 @@ fn init_uart_serial() {
|
|||
// physical wires (e.g. the Framebuffer), you don't need to do this,
|
||||
// because flush() is anyways called implicitly by replace_with(). This
|
||||
// is just a special case.
|
||||
use machine::devices::console::ConsoleOps;
|
||||
CONSOLE.lock(|c| c.flush());
|
||||
|
||||
match uart.prepare(mbox, &gpio) {
|
||||
|
|
Loading…
Reference in New Issue