std: xous: add output support for stdio

Add support for stdout. This enables basic console printing via
`println!()`. Output is written to the log server.

Signed-off-by: Sean Cross <sean@xobs.io>
This commit is contained in:
Sean Cross 2022-11-07 11:10:42 +08:00
parent 10dad67f89
commit 4af7d2cf8d
2 changed files with 131 additions and 1 deletions

View File

@ -26,7 +26,6 @@ pub mod path;
pub mod pipe;
#[path = "../unsupported/process.rs"]
pub mod process;
#[path = "../unsupported/stdio.rs"]
pub mod stdio;
#[path = "../unsupported/thread.rs"]
pub mod thread;

View File

@ -0,0 +1,131 @@
use crate::io;
pub struct Stdin;
pub struct Stdout {}
pub struct Stderr;
use crate::os::xous::ffi::{lend, try_lend, try_scalar, Connection};
use crate::os::xous::services::{log_server, try_connect, LogScalar};
impl Stdin {
pub const fn new() -> Stdin {
Stdin
}
}
impl io::Read for Stdin {
fn read(&mut self, _buf: &mut [u8]) -> io::Result<usize> {
Ok(0)
}
}
impl Stdout {
pub const fn new() -> Stdout {
Stdout {}
}
}
impl io::Write for Stdout {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
#[repr(align(4096))]
struct LendBuffer([u8; 4096]);
let mut lend_buffer = LendBuffer([0u8; 4096]);
let connection = log_server();
for chunk in buf.chunks(lend_buffer.0.len()) {
for (dest, src) in lend_buffer.0.iter_mut().zip(chunk) {
*dest = *src;
}
lend(connection, 1, &lend_buffer.0, 0, chunk.len()).unwrap();
}
Ok(buf.len())
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
impl Stderr {
pub const fn new() -> Stderr {
Stderr
}
}
impl io::Write for Stderr {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
#[repr(align(4096))]
struct LendBuffer([u8; 4096]);
let mut lend_buffer = LendBuffer([0u8; 4096]);
let connection = log_server();
for chunk in buf.chunks(lend_buffer.0.len()) {
for (dest, src) in lend_buffer.0.iter_mut().zip(chunk) {
*dest = *src;
}
lend(connection, 1, &lend_buffer.0, 0, chunk.len()).unwrap();
}
Ok(buf.len())
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
pub const STDIN_BUF_SIZE: usize = 0;
pub fn is_ebadf(_err: &io::Error) -> bool {
true
}
#[derive(Copy, Clone)]
pub struct PanicWriter {
log: Connection,
gfx: Option<Connection>,
}
impl io::Write for PanicWriter {
fn write(&mut self, s: &[u8]) -> core::result::Result<usize, io::Error> {
for c in s.chunks(core::mem::size_of::<usize>() * 4) {
// Text is grouped into 4x `usize` words. The id is 1100 plus
// the number of characters in this message.
// Ignore errors since we're already panicking.
try_scalar(self.log, LogScalar::AppendPanicMessage(&c).into()).ok();
}
// Serialize the text to the graphics panic handler, only if we were able
// to acquire a connection to it. Text length is encoded in the `valid` field,
// the data itself in the buffer. Typically several messages are require to
// fully transmit the entire panic message.
if let Some(gfx) = self.gfx {
#[repr(C, align(4096))]
struct Request([u8; 4096]);
let mut request = Request([0u8; 4096]);
for (&s, d) in s.iter().zip(request.0.iter_mut()) {
*d = s;
}
try_lend(gfx, 0 /* AppendPanicText */, &request.0, 0, s.len()).ok();
}
Ok(s.len())
}
// Tests show that this does not seem to be reliably called at the end of a panic
// print, so, we can't rely on this to e.g. trigger a graphics update.
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
pub fn panic_output() -> Option<impl io::Write> {
// Generally this won't fail because every server has already connected, so
// this is likely to succeed.
let log = log_server();
// Send the "We're panicking" message (1000).
try_scalar(log, LogScalar::BeginPanic.into()).ok();
// This is will fail in the case that the connection table is full, or if the
// graphics server is not running. Most servers do not already have this connection.
let gfx = try_connect("panic-to-screen!");
Some(PanicWriter { log, gfx })
}