Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement fmt::Write and embedded-io traits for VirtIOConsole #157

Merged
merged 4 commits into from
Oct 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,13 @@ categories = ["hardware-support", "no-std"]
log = "0.4.22"
bitflags = "2.6.0"
enumn = "0.1.14"
embedded-io = { version = "0.6.1", optional = true }
zerocopy = { version = "0.7.35", features = ["derive"] }

[features]
default = ["alloc"]
default = ["alloc", "embedded-io"]
alloc = ["zerocopy/alloc"]
embedded-io = ["dep:embedded-io"]

[dev-dependencies]
zerocopy = { version = "0.7.35", features = ["alloc"] }
36 changes: 35 additions & 1 deletion src/device/console.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
//! Driver for VirtIO console devices.

#[cfg(feature = "embedded-io")]
mod embedded_io;

use crate::hal::Hal;
use crate::queue::VirtQueue;
use crate::transport::Transport;
use crate::volatile::{volread, ReadOnly, WriteOnly};
use crate::{Result, PAGE_SIZE};
use alloc::boxed::Box;
use bitflags::bitflags;
use core::ptr::NonNull;
use core::{
fmt::{self, Write},
ptr::NonNull,
};
use log::error;

const QUEUE_RECEIVEQ_PORT_0: u16 = 0;
const QUEUE_TRANSMITQ_PORT_0: u16 = 1;
Expand Down Expand Up @@ -45,7 +52,9 @@ pub struct VirtIOConsole<H: Hal, T: Transport> {
receiveq: VirtQueue<H, QUEUE_SIZE>,
transmitq: VirtQueue<H, QUEUE_SIZE>,
queue_buf_rx: Box<[u8; PAGE_SIZE]>,
/// The index of the next byte in `queue_buf_rx` which `recv` should return.
cursor: usize,
/// The number of bytes read into `queue_buf_rx`.
pending_len: usize,
/// The token of the outstanding receive request, if there is one.
receive_token: Option<u16>,
Expand Down Expand Up @@ -207,6 +216,31 @@ impl<H: Hal, T: Transport> VirtIOConsole<H, T> {
.add_notify_wait_pop(&[&buf], &mut [], &mut self.transport)?;
Ok(())
}

/// Sends one or more bytes to the console.
pub fn send_bytes(&mut self, buffer: &[u8]) -> Result {
self.transmitq
.add_notify_wait_pop(&[buffer], &mut [], &mut self.transport)?;
Ok(())
}

/// Blocks until at least one character is available to read.
fn wait_for_receive(&mut self) -> Result {
self.poll_retrieve()?;
while self.cursor == self.pending_len {
self.finish_receive()?;
}
Ok(())
}
}

impl<H: Hal, T: Transport> Write for VirtIOConsole<H, T> {
fn write_str(&mut self, s: &str) -> fmt::Result {
self.send_bytes(s.as_bytes()).map_err(|e| {
error!("Error writing to conosel: {}", e);
fmt::Error
})
}
}

impl<H: Hal, T: Transport> Drop for VirtIOConsole<H, T> {
Expand Down
59 changes: 59 additions & 0 deletions src/device/console/embedded_io.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
//! Implementation of `embedded-io` traits for `VirtIOConsole`.

use super::VirtIOConsole;
use crate::{transport::Transport, Error, Hal};
use core::cmp::min;
use embedded_io::{BufRead, ErrorType, Read, ReadReady, Write};

impl<H: Hal, T: Transport> ErrorType for VirtIOConsole<H, T> {
type Error = Error;
}

impl<H: Hal, T: Transport> Write for VirtIOConsole<H, T> {
fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
if buf.is_empty() {
Ok(0)
} else {
self.send_bytes(buf)?;
Ok(buf.len())
}
}

fn flush(&mut self) -> Result<(), Self::Error> {
// We don't buffer writes, so nothing to do here.
Ok(())
}
}

impl<H: Hal, T: Transport> ReadReady for VirtIOConsole<H, T> {
fn read_ready(&mut self) -> Result<bool, Self::Error> {
self.finish_receive()?;
Ok(self.cursor != self.pending_len)
}
}

impl<H: Hal, T: Transport> Read for VirtIOConsole<H, T> {
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
if buf.is_empty() {
Ok(0)
} else {
self.wait_for_receive()?;
let read_length = min(buf.len(), self.pending_len - self.cursor);
buf[..read_length]
.copy_from_slice(&self.queue_buf_rx[self.cursor..self.cursor + read_length]);
Ok(read_length)
}
}
}

impl<H: Hal, T: Transport> BufRead for VirtIOConsole<H, T> {
fn fill_buf(&mut self) -> Result<&[u8], Self::Error> {
self.wait_for_receive()?;
Ok(&self.queue_buf_rx[self.cursor..self.pending_len])
}

fn consume(&mut self, amt: usize) {
assert!(self.cursor + amt <= self.pending_len);
self.cursor += amt;
}
}
8 changes: 0 additions & 8 deletions src/device/socket/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,10 @@ use core::{fmt, result};
pub enum SocketError {
/// There is an existing connection.
ConnectionExists,
/// Failed to establish the connection.
ConnectionFailed,
/// The device is not connected to any peer.
NotConnected,
/// Peer socket is shutdown.
PeerSocketShutdown,
/// No response received.
NoResponseReceived,
/// The given buffer is shorter than expected.
BufferTooShort,
/// The given buffer for output is shorter than expected.
Expand All @@ -41,12 +37,8 @@ impl fmt::Display for SocketError {
Self::ConnectionExists => write!(
f,
"There is an existing connection. Please close the current connection before attempting to connect again."),
Self::ConnectionFailed => write!(
f, "Failed to establish the connection. The packet sent may have an unknown type value"
),
Self::NotConnected => write!(f, "The device is not connected to any peer. Please connect it to a peer first."),
Self::PeerSocketShutdown => write!(f, "The peer socket is shutdown."),
Self::NoResponseReceived => write!(f, "No response received"),
Self::BufferTooShort => write!(f, "The given buffer is shorter than expected"),
Self::BufferTooLong(actual, max) => {
write!(f, "The given buffer length '{actual}' has exceeded the maximum allowed buffer length '{max}'")
Expand Down
35 changes: 35 additions & 0 deletions src/embedded_io.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//! Implementation of `embedded-io::Error' trait for `Error`.

use crate::{device::socket::SocketError, Error};
use embedded_io::ErrorKind;

impl embedded_io::Error for Error {
fn kind(&self) -> ErrorKind {
match self {
Error::InvalidParam => ErrorKind::InvalidInput,
Error::DmaError => ErrorKind::OutOfMemory,
Error::Unsupported => ErrorKind::Unsupported,
Error::SocketDeviceError(e) => match e {
&SocketError::ConnectionExists => ErrorKind::AddrInUse,
SocketError::NotConnected => ErrorKind::NotConnected,
SocketError::PeerSocketShutdown => ErrorKind::ConnectionAborted,
SocketError::BufferTooShort => ErrorKind::InvalidInput,
SocketError::OutputBufferTooShort(_) => ErrorKind::InvalidInput,
SocketError::BufferTooLong(_, _) => ErrorKind::InvalidInput,
SocketError::InsufficientBufferSpaceInPeer => ErrorKind::WriteZero,
SocketError::UnknownOperation(_)
| SocketError::InvalidOperation
| SocketError::InvalidNumber
| SocketError::UnexpectedDataInPacket
| SocketError::RecycledWrongBuffer => ErrorKind::Other,
},
Error::QueueFull
| Error::NotReady
| Error::WrongToken
| Error::AlreadyUsed
| Error::IoError
| Error::ConfigSpaceTooSmall
| Error::ConfigSpaceMissing => ErrorKind::Other,
}
}
}
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@
extern crate alloc;

pub mod device;
#[cfg(feature = "embedded-io")]
mod embedded_io;
mod hal;
mod queue;
pub mod transport;
Expand Down
Loading