Skip to content

hal::stream

Byte streams — the subsystem a UART actually is.

Bus models an addressed transaction: a master names a device, moves a bounded payload, and the exchange completes. SPI and I²C fit — there is an address (or a chip-select), the bytes out and in are one transfer, and when it returns it is done.

A UART is none of that. Bytes flow: there is no address, no chip-select, and what arrives is unrelated to what was sent (in a loopback it happens to match; on a real link it never does). Modelling it as a Bus forces a transfer(tx, rx) whose “rx” is a fiction — which is exactly the pretense this module retires.

The rule, written down so the next peripheral does not have to relitigate it:

If a transfer has an address and completes, it is a Bus. If it flows, or is locked to a clock, it is a subsystem with its own small API.

By that rule the streaming, block, audio, and timing peripherals each get their own shape rather than being bent onto Bus:

Traffic Peripheral The shape it wants
stream UART / USB CDC [ByteStream] — non-blocking byte counts, line errors
block SD / SDIO addressed 512-byte LBA sectors, not a byte payload
audio I²S continuous ping-pong DMA, no per-call transaction
timing WS2812 / RMT pulse-width symbols locked to a clock

UART and USB CDC share ByteStream; the others are named so the boundary is a decision on record, not a gap someone fills by reaching for Bus again. Reference kernels sometimes do reach — Linux hangs a tty discipline off a uart_port, Zephyr keeps uart entirely separate from its spi/i2c drivers — and the ones that stay honest keep the stream apart from the addressed bus, which is what this does.

struct StreamErrors {
pub overrun: bool,
pub parity: bool,
pub framing: bool,
}

Line conditions a receiver can latch between reads.

A stream keeps flowing through these — they are reported, not returned as an error from [ByteStream::read], because dropping the whole read for one bad byte would lose the good ones around it.

Implements: Clone, Copy, Debug, Default, Eq, PartialEq, StructuralPartialEq

trait ByteStream: Send + Sync {
fn write(&self, data: &[u8]) -> usize
fn read(&self, buf: &mut [u8]) -> usize
fn errors(&self) -> StreamErrors
}

A byte-oriented, non-blocking stream: UART or USB CDC, not an addressed bus.

write and read move as many bytes as fit or are waiting right now and return the count — the caller loops or buffers the rest. Neither blocks, so a stream never stalls a task on a peer that is slow or silent. Line errors are polled separately via errors rather than folded into a read count.