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

## Unreleased

## 0.8.0 - 2026-08-16

- **Breaking:** `Config::default()` now disables **all** interrupts by default
(was `IER::DATA_READY`).
- Made `Uart16550::init()` enable configured interrupts only at the end. Also
documented when interrupts can still become pending.

## 0.7.0 - 2026-08-15

- **Breaking:** Changed the return type of `Uart16550::config(&self)` from
Expand Down
2 changes: 1 addition & 1 deletion Cargo.lock

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

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "uart_16550"
version = "0.7.0"
version = "0.8.0"
description = """
Simple yet highly configurable low-level driver for 16550 UART devices,
typically known and used as serial ports or COM ports. Easy integration into
Expand Down
5 changes: 2 additions & 3 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,13 +166,12 @@ impl Config {
/// configuration.
///
/// More precisely, the default configuration uses a [8-N-1] transmission
/// with a baud rate of [`BaudRate::Baud9600`]. It also activates the FIFO
/// and the [`IER::DATA_READY`] interrupt.
/// with a baud rate of [`BaudRate::Baud9600`]. It also activates the FIFO.
///
/// [8-N-1]: https://en.wikipedia.org/wiki/Serial_port#Conventional_notation
pub const DEFAULT: Self = Self {
// Properties and behavior of the UART
interrupts: IER::DATA_READY,
interrupts: IER::empty(),
frequency: CLK_FREQUENCY_HZ,
prescaler_division_factor: None,
fifo_trigger_level: Some(FifoTriggerLevel::Fourteen),
Expand Down
66 changes: 57 additions & 9 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -397,14 +397,30 @@ impl<B: Backend> Uart16550<B> {
/// the device works. Further, a call to [`Self::check_connected`] helps to
/// detect if a remote is connected.
///
/// # Interrupts
///
/// As one of its first steps, this function disables all device interrupts.
/// The interrupts selected in the [`Config`] are enabled again only at the
/// very end of the initialization sequence.
///
/// An interrupt may still become pending after the configured interrupts
/// have been enabled but before this function returns, for example because
/// data arrives asynchronously.
///
/// [`IER::THR_EMPTY`] is a special case. If enabled, it will immediately
/// trigger an interrupt because the transmitter is guaranteed to be empty
/// when the configured interrupts are enabled at the end of initialization.
///
/// **Recommendation:** Therefore, when using the device in an
/// interrupt-driven setup, it is safest to call this function from an
/// interrupt-free section.
///
/// # Caution
///
/// Callers must ensure that using this type with the underlying hardware
/// is done only in a context where such operations are valid and safe
/// (e.g., you have exclusive device access).
///
/// It is recommended to disable interrupts before calling this function.
///
/// Further, the serial config must match the expectations of the receiver
/// on the other side. Otherwise, garbage will be received.
pub fn init(&mut self, config: Config) -> Result<(), InitError> {
Expand Down Expand Up @@ -432,6 +448,12 @@ impl<B: Backend> Uart16550<B> {
check_fn(0x73)?;
}

// Clear DLAB.
// SAFETY: We operate on valid register addresses.
unsafe {
self.backend.write(offsets::LCR as u8, 0);
}

// Disable all interrupts (for now).
// SAFETY: We operate on valid register addresses.
unsafe {
Expand Down Expand Up @@ -496,13 +518,6 @@ impl<B: Backend> Uart16550<B> {
self.backend.write(offsets::MCR as u8, mcr.bits());
}

// Set interrupts.
// SAFETY: We operate on valid register addresses.
unsafe {
self.backend
.write(offsets::IER as u8, self.config.interrupts.bits());
}

// In case there is anything in THR, THR's FIFO or TSR (for
// example because the device was already initialized by another
// driver), we wait for the data to be drained. This way, we can ensure
Expand All @@ -523,6 +538,22 @@ impl<B: Backend> Uart16550<B> {
hint::spin_loop()
}
}

// Bring status bits into a clean state.
{
// Clear receiver line-status deltas / interrupt indicators.
let _ = self.lsr();

// Clear modem-status deltas.
let _ = self.msr();
}

// Set interrupts as the last step.
// SAFETY: We operate on valid register addresses.
unsafe {
self.backend
.write(offsets::IER as u8, self.config.interrupts.bits());
}
Ok(())
}

Expand Down Expand Up @@ -826,6 +857,12 @@ impl<B: Backend> Uart16550<B> {
}

/// Fetches the current value from the [`ISR`].
///
/// # Side Effects
///
/// Reading the [`ISR`] clears a pending transmitter holding register empty
/// interrupt if it is the interrupt currently indicated by the [`ISR`].
/// This does not clear the corresponding [`LSR::THR_EMPTY`] status flag.
pub fn isr(&mut self) -> ISR {
// SAFETY: We operate on valid register addresses.
let val = unsafe { self.backend.read(offsets::ISR as u8) };
Expand All @@ -847,13 +884,24 @@ impl<B: Backend> Uart16550<B> {
}

/// Fetches the current value from the [`LSR`].
///
/// # Side Effects
///
/// Reading the [`LSR`] clears a pending receiver line status interrupt.
/// It also clears the [`LSR::OVERRUN_ERROR`], [`LSR::PARITY_ERROR`],
/// [`LSR::FRAMING_ERROR`], and [`LSR::BREAK_INTERRUPT`] status flags.
pub fn lsr(&mut self) -> LSR {
// SAFETY: We operate on valid register addresses.
let val = unsafe { self.backend.read(offsets::LSR as u8) };
LSR::from_bits_retain(val)
}

/// Fetches the current value from the [`MSR`].
///
/// # Side Effects
///
/// Reading the [`MSR`] clears a pending modem status interrupt and clears
/// the modem status change indicators in bits 0 through 3.
pub fn msr(&mut self) -> MSR {
// SAFETY: We operate on valid register addresses.
let val = unsafe { self.backend.read(offsets::MSR as u8) };
Expand Down
2 changes: 1 addition & 1 deletion test/Cargo.lock

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