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)]
|
#![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
|
/// A trait that must be implemented by devices that are candidates for the
|
||||||
/// global console.
|
/// global console.
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
pub trait ConsoleOps {
|
pub trait ConsoleOps: SerialOps {
|
||||||
fn putc(&self, c: char) {}
|
/// Send a character
|
||||||
fn puts(&self, string: &str) {}
|
fn write_char(&self, c: char);
|
||||||
fn getc(&self) -> char {
|
/// Display a string
|
||||||
' '
|
fn write_string(&self, string: &str);
|
||||||
}
|
/// Receive a character
|
||||||
fn flush(&self) {}
|
fn read_char(&self) -> char;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A dummy console that just ignores its inputs.
|
/// A dummy console that just ignores its inputs.
|
||||||
pub struct NullConsole;
|
pub struct NullConsole;
|
||||||
|
|
||||||
impl Drop for NullConsole {
|
impl Drop for NullConsole {
|
||||||
fn drop(&mut self) {}
|
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.
|
/// Possible outputs which the console can store.
|
||||||
pub enum Output {
|
pub enum Output {
|
||||||
|
@ -83,15 +108,15 @@ impl Console {
|
||||||
|
|
||||||
/// A command prompt.
|
/// A command prompt.
|
||||||
pub fn command_prompt<'a>(&self, buf: &'a mut [u8]) -> &'a [u8] {
|
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 i = 0;
|
||||||
let mut input;
|
let mut input;
|
||||||
loop {
|
loop {
|
||||||
input = self.getc();
|
input = self.read_char();
|
||||||
|
|
||||||
if input == '\n' {
|
if input == '\n' {
|
||||||
self.puts("\n"); // do \r\n output
|
self.write_char('\n'); // do \r\n output
|
||||||
return &buf[..i];
|
return &buf[..i];
|
||||||
} else {
|
} else {
|
||||||
if i < buf.len() {
|
if i < buf.len() {
|
||||||
|
@ -101,7 +126,7 @@ impl Console {
|
||||||
return &buf[..i];
|
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.
|
/// Dispatch the respective function to the currently stored output device.
|
||||||
impl ConsoleOps for Console {
|
impl ConsoleOps for Console {
|
||||||
fn putc(&self, c: char) {
|
fn write_char(&self, c: char) {
|
||||||
self.current_ptr().putc(c);
|
self.current_ptr().write_char(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn puts(&self, string: &str) {
|
fn write_string(&self, string: &str) {
|
||||||
self.current_ptr().puts(string);
|
self.current_ptr().write_string(string);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn getc(&self) -> char {
|
fn read_char(&self) -> char {
|
||||||
self.current_ptr().getc()
|
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) {
|
fn flush(&self) {
|
||||||
self.current_ptr().flush()
|
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
|
/// Implementing this trait enables usage of the format_args! macros, which in
|
||||||
|
@ -136,8 +172,7 @@ impl ConsoleOps for Console {
|
||||||
/// See src/macros.rs.
|
/// See src/macros.rs.
|
||||||
impl fmt::Write for Console {
|
impl fmt::Write for Console {
|
||||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||||
self.current_ptr().puts(s);
|
self.current_ptr().write_string(s);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,5 +2,9 @@
|
||||||
* SPDX-License-Identifier: BlueOak-1.0.0
|
* SPDX-License-Identifier: BlueOak-1.0.0
|
||||||
*/
|
*/
|
||||||
pub mod console;
|
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 tock_registers::interfaces::{Readable, Writeable};
|
||||||
use {
|
use {
|
||||||
super::{gpio, BcmHost},
|
super::{gpio, BcmHost},
|
||||||
crate::{devices::ConsoleOps, platform::MMIODerefWrapper},
|
crate::{
|
||||||
|
devices::{ConsoleOps, SerialOps},
|
||||||
|
platform::MMIODerefWrapper,
|
||||||
|
},
|
||||||
cfg_if::cfg_if,
|
cfg_if::cfg_if,
|
||||||
core::{convert::From, fmt},
|
core::{convert::From, fmt},
|
||||||
tock_registers::{
|
tock_registers::{
|
||||||
|
@ -212,44 +215,24 @@ impl Drop for PreparedMiniUart {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ConsoleOps for PreparedMiniUart {
|
impl SerialOps for PreparedMiniUart {
|
||||||
cfg_if! {
|
cfg_if! {
|
||||||
if #[cfg(not(feature = "noserial"))] {
|
if #[cfg(not(feature = "noserial"))] {
|
||||||
/// Send a character
|
/// Receive a byte without console translation
|
||||||
fn putc(&self, c: char) {
|
fn read_byte(&self) -> u8 {
|
||||||
// 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 {
|
|
||||||
// wait until something is in the buffer
|
// wait until something is in the buffer
|
||||||
crate::arch::loop_until(|| self.0.registers.AUX_MU_LSR.is_set(AUX_MU_LSR::DATA_READY));
|
crate::arch::loop_until(|| self.0.registers.AUX_MU_LSR.is_set(AUX_MU_LSR::DATA_READY));
|
||||||
|
|
||||||
// read it and return
|
// 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
|
fn write_byte(&self, b: u8) {
|
||||||
if ret == '\r' {
|
// wait until we can send
|
||||||
ret = '\n'
|
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
|
/// 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) {
|
fn flush(&self) {
|
||||||
crate::arch::loop_until(|| self.0.registers.AUX_MU_LSR.is_set(AUX_MU_LSR::TX_IDLE));
|
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 {
|
} else {
|
||||||
fn putc(&self, _c: char) {}
|
fn read_byte(&self) -> u8 { 0 }
|
||||||
fn puts(&self, _string: &str) {}
|
fn write_byte(&self, _byte: u8) {}
|
||||||
fn getc(&self) -> char {
|
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'
|
'\n'
|
||||||
}
|
}
|
||||||
fn flush(&self) {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Write for PreparedMiniUart {
|
impl fmt::Write for PreparedMiniUart {
|
||||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||||
self.puts(s);
|
self.write_string(s);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,11 @@ use {
|
||||||
mailbox::{self, MailboxOps},
|
mailbox::{self, MailboxOps},
|
||||||
BcmHost,
|
BcmHost,
|
||||||
},
|
},
|
||||||
crate::{arch::loop_until, devices::ConsoleOps, platform::MMIODerefWrapper},
|
crate::{
|
||||||
|
arch::loop_until,
|
||||||
|
devices::{ConsoleOps, SerialOps},
|
||||||
|
platform::MMIODerefWrapper,
|
||||||
|
},
|
||||||
snafu::Snafu,
|
snafu::Snafu,
|
||||||
tock_registers::{
|
tock_registers::{
|
||||||
interfaces::{Readable, Writeable},
|
interfaces::{Readable, Writeable},
|
||||||
|
@ -237,35 +241,49 @@ impl Drop for PreparedPL011Uart {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ConsoleOps for PreparedPL011Uart {
|
impl SerialOps for PreparedPL011Uart {
|
||||||
/// Send a character
|
fn write_byte(&self, b: u8) {
|
||||||
fn putc(&self, c: char) {
|
|
||||||
// wait until we can send
|
// wait until we can send
|
||||||
loop_until(|| !self.0.registers.FR.is_set(FR::TXFF));
|
loop_until(|| !self.0.registers.FR.is_set(FR::TXFF));
|
||||||
|
|
||||||
// write the character to the buffer
|
// 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 read_byte(&self) -> u8 {
|
||||||
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 {
|
|
||||||
// wait until something is in the buffer
|
// wait until something is in the buffer
|
||||||
loop_until(|| !self.0.registers.FR.is_set(FR::RXFE));
|
loop_until(|| !self.0.registers.FR.is_set(FR::RXFE));
|
||||||
|
|
||||||
// read it and return
|
// 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
|
// convert carriage return to newline
|
||||||
if ret == '\r' {
|
if ret == '\r' {
|
||||||
|
|
|
@ -19,6 +19,8 @@
|
||||||
|
|
||||||
#[cfg(not(test))]
|
#[cfg(not(test))]
|
||||||
use core::panic::PanicInfo;
|
use core::panic::PanicInfo;
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
use machine::devices::SerialOps;
|
||||||
use {
|
use {
|
||||||
cfg_if::cfg_if,
|
cfg_if::cfg_if,
|
||||||
machine::{
|
machine::{
|
||||||
|
@ -96,7 +98,6 @@ fn init_uart_serial() {
|
||||||
// physical wires (e.g. the Framebuffer), you don't need to do this,
|
// physical wires (e.g. the Framebuffer), you don't need to do this,
|
||||||
// because flush() is anyways called implicitly by replace_with(). This
|
// because flush() is anyways called implicitly by replace_with(). This
|
||||||
// is just a special case.
|
// is just a special case.
|
||||||
use machine::devices::console::ConsoleOps;
|
|
||||||
CONSOLE.lock(|c| c.flush());
|
CONSOLE.lock(|c| c.flush());
|
||||||
|
|
||||||
match uart.prepare(mbox, &gpio) {
|
match uart.prepare(mbox, &gpio) {
|
||||||
|
|
Loading…
Reference in New Issue