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
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

## Unreleased

- Rename `Uart16550::try_send_bytes()` to `Uart16550::send_bytes()`
- Rename `Uart16550::try_receive_bytes()` to `Uart16550::receive_bytes()`
- New public methods:
- `Uart16550::ready_to_receive()`
- `Uart16550::ready_to_send()`

## 0.5.0 - 2026-03-20

- Complete rewrite of the crate
Expand Down
18 changes: 13 additions & 5 deletions src/embedded_io.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//! API glue for [`Uart16550`] with [`embedded_io`].

use core::convert::Infallible;
use core::hint;

Expand All @@ -12,12 +14,16 @@ impl<B: Backend> ErrorType for Uart16550<B> {

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

loop {
if let Ok(n) = self.try_send_bytes(buf) {
let n = self.send_bytes(buf);
if n > 0 {
return Ok(n);
}

hint::spin_loop()
hint::spin_loop();
}
}

Expand All @@ -36,12 +42,14 @@ impl<B: Backend> WriteReady for Uart16550<B> {

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

hint::spin_loop();
}
}
Expand Down
89 changes: 56 additions & 33 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -200,11 +200,11 @@ mod tty;
/// ## Non-blocking
///
/// - [`Uart16550::try_send_byte`]: Attempt to transmit a single byte.
/// - [`Uart16550::try_send_bytes`]: Attempt to transmit multiple bytes and
/// return the number of bytes written.
/// - [`Uart16550::send_bytes`]: Transmit multiple bytes without blocking,
/// returning the number of bytes written.
/// - [`Uart16550::try_receive_byte`]: Attempt to receive a single byte.
/// - [`Uart16550::try_receive_bytes`]: Attempt to receive bytes into a buffer
/// and return the number of bytes read.
/// - [`Uart16550::receive_bytes`]: Read bytes into a buffer without blocking,
/// returning the number of bytes read.
///
/// These methods return immediately if the hardware can't complete the
/// operation.
Expand Down Expand Up @@ -492,7 +492,7 @@ impl<B: Backend> Uart16550<B> {
self.configure_fcr();

// Drain any data that might be still there
while self.try_receive_bytes(&mut [0]) > 0 {}
while self.receive_bytes(&mut [0]) > 0 {}

// First: check a single byte
{
Expand Down Expand Up @@ -550,7 +550,7 @@ impl<B: Backend> Uart16550<B> {
///
/// # Hints for Real Hardware
///
/// Please note that some cables (especially when a NULL modem is included)
/// Please note that some cables (especially when a Null modem is included)
/// never raise the CD or even DSR line. So even if this checks fails,
/// connections might work.
///
Expand Down Expand Up @@ -583,7 +583,16 @@ impl<B: Backend> Uart16550<B> {
Ok(())
}

fn ready_to_receive(&mut self) -> Result<(), ByteReceiveError> {
/// Checks if there is at least one pending byte on the device that can be
/// read.
///
/// Please note that it is not required to call this before any of the
/// receive-methods, as all of them perform this check also internally
/// already.
///
/// This differs from [`Self::check_connected`] as it only checks the
/// internal in-buffer without checking for an established connection.
pub fn ready_to_receive(&mut self) -> Result<(), ByteReceiveError> {
let lsr = self.lsr();

if !lsr.contains(LSR::DATA_READY) {
Expand All @@ -593,7 +602,16 @@ impl<B: Backend> Uart16550<B> {
Ok(())
}

fn ready_to_send(&mut self) -> Result<(), ByteSendError> {
/// Determines if data can be sent.
///
/// Please note that it is not required to call this before any of the
/// send-methods, as all of them perform this check also internally
/// already.
///
/// This differs from [`Self::check_connected`] as it only checks if further
/// data can be written (e.g., internal FIFO is empty) without checking for
/// an established connection.
pub fn ready_to_send(&mut self) -> Result<(), ByteSendError> {
let lsr = self.lsr();
let msr = self.msr();
let mcr = self.mcr();
Expand Down Expand Up @@ -635,17 +653,21 @@ impl<B: Backend> Uart16550<B> {
#[inline]
pub fn try_send_byte(&mut self, byte: u8) -> Result<(), ByteSendError> {
// bytes are typically written in chunks for higher performance,
// therefore `try_send_bytes()` is our base here. Further, UART16550
// do not allow us to check if there is capacity left in the FIFO.
self.try_send_bytes(&[byte]).map(|_| ())
// therefore `send_bytes()` is our base here.
match self.send_bytes(&[byte]) {
0 => Err(ByteSendError::NoCapacity),
_ => Ok(()),
}
}

/// Tries to receive bytes from the device and writes them into the provided
/// buffer.
/// Reads bytes from the device into the provided buffer.
///
/// This function returns the number of bytes that have been received and
/// put into the buffer.
pub fn try_receive_bytes(&mut self, buffer: &mut [u8]) -> usize {
/// Returns the number of bytes actually read, which may be less than
/// `buffer.len()` if fewer bytes are available. Returns `0` if no data is
/// currently available.
///
/// Call repeatedly with a shifted buffer slice to receive all expected data.
pub fn receive_bytes(&mut self, buffer: &mut [u8]) -> usize {
buffer
.iter_mut()
.map_while(|slot: &mut u8| {
Expand All @@ -656,23 +678,21 @@ impl<B: Backend> Uart16550<B> {
.count()
}

/// Tries to send bytes to the remote without blocking.
///
/// Returns the number of bytes accepted, which is either `0` (not
/// ready), `1` (non-FIFO mode), or up to [`FIFO_SIZE`] (FIFO mode).
/// Sends bytes to the remote without blocking.
///
/// # Non-Blocking Behavior
/// Returns the number of bytes actually written: `0` if no data can
/// currently be written, `1` in non-FIFO mode, or up to [`FIFO_SIZE`]
/// in FIFO mode.
///
/// This function never spins. If the hardware is not ready it returns
/// [`ByteSendError::NoCapacity`] or
/// [`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> {
/// Call repeatedly with a shifted buffer slice to send all data.
pub fn send_bytes(&mut self, buffer: &[u8]) -> usize {
if buffer.is_empty() {
return Ok(0);
return 0;
}

self.ready_to_send()?;
if self.ready_to_send().is_err() {
return 0;
}

let fifo_enabled = self.config.fifo_trigger_level.is_some();
let bytes = if fifo_enabled {
Expand All @@ -691,10 +711,10 @@ impl<B: Backend> Uart16550<B> {
}
}

Ok(bytes.len())
bytes.len()
}

/// Similar to [`Self::try_receive_bytes`] but loops until enough bytes were
/// Similar to [`Self::receive_bytes`] but loops until enough bytes were
/// read to fully fill the buffer.
///
/// Beware that this can spin indefinitely.
Expand All @@ -712,15 +732,18 @@ impl<B: Backend> Uart16550<B> {
}
}

/// Similar to [`Self::try_send_bytes`] but loops until all bytes were
/// Similar to [`Self::send_bytes`] but loops until all bytes were
/// written entirely to the remote.
///
/// Beware that this can spin indefinitely.
pub fn send_bytes_exact(&mut self, bytes: &[u8]) {
let mut remaining_bytes = bytes;
while !remaining_bytes.is_empty() {
if let Ok(n) = self.try_send_bytes(remaining_bytes) {
remaining_bytes = &remaining_bytes[n..];
let n = self.send_bytes(remaining_bytes);
remaining_bytes = &remaining_bytes[n..];

if n > 0 {
continue;
} else {
hint::spin_loop()
}
Expand Down