Skip to content
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
12 changes: 6 additions & 6 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,14 @@ jobs:
run: cargo build --all-targets --verbose
- name: Build (no_std, x86, aarch64, riscv)
run: |
cargo build --verbose --target aarch64-unknown-uefi
cargo build --verbose --target i686-unknown-uefi
cargo build --verbose --target riscv64gc-unknown-none-elf
cargo build --verbose --target x86_64-unknown-uefi
cargo build --verbose --all-features --target aarch64-unknown-uefi
cargo build --verbose --all-features --target i686-unknown-uefi
cargo build --verbose --all-features --target riscv64gc-unknown-none-elf
cargo build --verbose --all-features --target x86_64-unknown-uefi
- name: Build (no_std, aarch64)
run: cargo build --verbose --target thumbv7em-none-eabihf
run: cargo build --verbose --all-features --target thumbv7em-none-eabihf
- name: Run tests
run: cargo test --verbose
run: cargo test --verbose --all-features

integration_test:
runs-on: ubuntu-latest
Expand Down
7 changes: 7 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ resolver = "3"

[dependencies]
bitflags = { version = "2.11", default-features = false, features = [] }
embedded-io = { version = "0.7", optional = true }

[dev-dependencies]
assert2 = { version = "0.4.0", default-features = false, features = [] }

[features]
default = []
embedded-io = ["dep:embedded-io"]
56 changes: 56 additions & 0 deletions src/embedded_io.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
use core::convert::Infallible;
use core::hint;

use embedded_io::{ErrorType, Read, ReadReady, Write, WriteReady};

use crate::Uart16550;
use crate::backend::Backend;

impl<B: Backend> ErrorType for Uart16550<B> {
type Error = Infallible;
}

impl<B: Backend> Write for Uart16550<B> {
fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
loop {
if let Ok(n) = self.try_send_bytes(buf) {
return Ok(n);
}

hint::spin_loop()
}
}

fn flush(&mut self) -> Result<(), Self::Error> {
Ok(())
}
}

impl<B: Backend> WriteReady for Uart16550<B> {
fn write_ready(&mut self) -> Result<bool, Self::Error> {
let write_ready = self.ready_to_send().is_ok();

Ok(write_ready)
}
}

impl<B: Backend> Read for Uart16550<B> {
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
loop {
let n = self.try_receive_bytes(buf);
if n > 0 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note to self: https://docs.rs/embedded-io/latest/embedded_io/trait.Read.html

The method blocks until at least one byte becomes available;

make sense. thanks!

return Ok(n);
}

hint::spin_loop();
}
}
}

impl<B: Backend> ReadReady for Uart16550<B> {
fn read_ready(&mut self) -> Result<bool, Self::Error> {
let read_ready = self.ready_to_receive().is_ok();

Ok(read_ready)
}
}
61 changes: 37 additions & 24 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,8 @@ pub mod backend;
pub mod spec;

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

Expand Down Expand Up @@ -581,17 +583,45 @@ impl<B: Backend> Uart16550<B> {
Ok(())
}

fn ready_to_receive(&mut self) -> Result<(), ByteReceiveError> {
let lsr = self.lsr();

if !lsr.contains(LSR::DATA_READY) {
return Err(ByteReceiveError);
}

Ok(())
}

fn ready_to_send(&mut self) -> Result<(), ByteSendError> {
let lsr = self.lsr();
let msr = self.msr();
let mcr = self.mcr();

// In FIFO mode, this bit is set when the transmitter’s FIFO is
// completely empty, being 0 if there is at least one byte in the
// FIFO waiting to be passed to the TSR for transmission. In Non-FIFO
// mode we must return on error to prevent data corruption in THR.
if !lsr.contains(LSR::THR_EMPTY) {
return Err(ByteSendError::NoCapacity);
}

// Software flow control. TODO, what to do with hardware flow control?
// Is this something we can and should support?
if !mcr.contains(MCR::LOOP_BACK) && !msr.contains(MSR::CTS) {
return Err(ByteSendError::RemoteNotClearToSend);
}

Ok(())
}

/* ----- User I/O ------------------------------------------------------- */

/// Tries to read a raw byte from the device.
///
/// This will receive whatever a remote has sent to us.
pub fn try_receive_byte(&mut self) -> Result<u8, ByteReceiveError> {
let lsr = self.lsr();

if !lsr.contains(LSR::DATA_READY) {
return Err(ByteReceiveError);
}
self.ready_to_receive()?;

// SAFETY: We operate on valid register addresses.
let byte = unsafe { self.backend.read(offsets::DATA as u8) };
Expand Down Expand Up @@ -638,37 +668,20 @@ impl<B: Backend> Uart16550<B> {
/// [`ByteSendError::RemoteNotClearToSend`] immediately. Use
/// [`Self::send_bytes_exact`] if you need all bytes delivered.
pub fn try_send_bytes(&mut self, buffer: &[u8]) -> Result<usize, ByteSendError> {
let lsr = self.lsr();
let msr = self.msr();
let mcr = self.mcr();

let fifo_enabled = self.config.fifo_trigger_level.is_some();

if buffer.is_empty() {
return Ok(0);
}

// In FIFO mode, this bit is set when the transmitter’s FIFO is
// completely empty, being 0 if there is at least one byte in the
// FIFO waiting to be passed to the TSR for transmission. In Non-FIFO
// mode we must return on error to prevent data corruption in THR.
if !lsr.contains(LSR::THR_EMPTY) {
return Err(ByteSendError::NoCapacity);
}
self.ready_to_send()?;

let fifo_enabled = self.config.fifo_trigger_level.is_some();
let bytes = if fifo_enabled {
let max_index = cmp::min(FIFO_SIZE, buffer.len());
&buffer[..max_index]
} else {
&buffer[..1]
};

// Software flow control. TODO, what to do with hardware flow control?
// Is this something we can and should support?
if !mcr.contains(MCR::LOOP_BACK) && !msr.contains(MSR::CTS) {
return Err(ByteSendError::RemoteNotClearToSend);
}

// Spec: According to spec, it is fine to send multiple bytes in a row
// in FIFO mode to the data register (THR).
for &byte in bytes {
Expand Down