From 206b0d31b825404150243b863e17a73dc1726c52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Kr=C3=B6ning?= Date: Fri, 20 Mar 2026 18:15:32 +0100 Subject: [PATCH 1/3] refactor: extract `ready_to_receive` --- src/lib.rs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 5d81f85..6177836 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -581,17 +581,23 @@ impl Uart16550 { Ok(()) } + fn ready_to_receive(&mut self) -> Result<(), ByteReceiveError> { + let lsr = self.lsr(); + + if !lsr.contains(LSR::DATA_READY) { + return Err(ByteReceiveError); + } + + 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 { - 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) }; From decb57e98803aeca58c8570e8a50ac04dd724ccf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Kr=C3=B6ning?= Date: Fri, 20 Mar 2026 18:16:35 +0100 Subject: [PATCH 2/3] refactor: extract `ready_to_send` --- src/lib.rs | 43 ++++++++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 6177836..e940942 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -591,6 +591,28 @@ impl Uart16550 { 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. @@ -644,24 +666,13 @@ impl Uart16550 { /// [`ByteSendError::RemoteNotClearToSend`] immediately. Use /// [`Self::send_bytes_exact`] if you need all bytes delivered. pub fn try_send_bytes(&mut self, buffer: &[u8]) -> Result { - 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] @@ -669,12 +680,6 @@ impl Uart16550 { &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 { From 156ce4732521e90cfb3d076e671ee668b2c2b0d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Kr=C3=B6ning?= Date: Mon, 2 Mar 2026 10:51:23 +0100 Subject: [PATCH 3/3] feat: implement embedded-io traits --- .github/workflows/rust.yml | 12 ++++---- Cargo.lock | 7 +++++ Cargo.toml | 5 ++++ src/embedded_io.rs | 56 ++++++++++++++++++++++++++++++++++++++ src/lib.rs | 2 ++ 5 files changed, 76 insertions(+), 6 deletions(-) create mode 100644 src/embedded_io.rs diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index a28d65f..a825296 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -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 diff --git a/Cargo.lock b/Cargo.lock index fb1da54..79f51ac 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -39,6 +39,12 @@ version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" +[[package]] +name = "embedded-io" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9eb1aa714776b75c7e67e1da744b81a129b3ff919c8712b5e1b32252c1f07cc7" + [[package]] name = "errno" version = "0.3.14" @@ -134,6 +140,7 @@ version = "0.5.0" dependencies = [ "assert2", "bitflags", + "embedded-io", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 0018284..0362c6a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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"] diff --git a/src/embedded_io.rs b/src/embedded_io.rs new file mode 100644 index 0000000..14ced5f --- /dev/null +++ b/src/embedded_io.rs @@ -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 ErrorType for Uart16550 { + type Error = Infallible; +} + +impl Write for Uart16550 { + fn write(&mut self, buf: &[u8]) -> Result { + 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 WriteReady for Uart16550 { + fn write_ready(&mut self) -> Result { + let write_ready = self.ready_to_send().is_ok(); + + Ok(write_ready) + } +} + +impl Read for Uart16550 { + fn read(&mut self, buf: &mut [u8]) -> Result { + loop { + let n = self.try_receive_bytes(buf); + if n > 0 { + return Ok(n); + } + + hint::spin_loop(); + } + } +} + +impl ReadReady for Uart16550 { + fn read_ready(&mut self) -> Result { + let read_ready = self.ready_to_receive().is_ok(); + + Ok(read_ready) + } +} diff --git a/src/lib.rs b/src/lib.rs index e940942..8a4c2ea 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -152,6 +152,8 @@ pub mod backend; pub mod spec; mod config; +#[cfg(feature = "embedded-io")] +mod embedded_io; mod error; mod tty;