Skip to content

Commit 8893849

Browse files
committed
doc: improve examples and wording + build on non-x86
- dedicated example for x86 port IO and MMIO - also builds on non-x86 systems To test doc cross-build, you may use RUSTDOCFLAGS="--cfg docsrs" cargo +nightly doc --open --no-deps --document-private-items --target aarch64-unknown-linux-gnu -Zbuild-std
1 parent 2b86ba7 commit 8893849

4 files changed

Lines changed: 107 additions & 33 deletions

File tree

README.md

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,24 @@ layer.
4646

4747
# Overview
4848

49-
Use `Uart16550Tty` for a quick start. For more fine-grained low-level
50-
control, please have a look at `Uart16550` instead.
49+
Use `Uart16550Tty` for a quick start to write to a terminal via your serial
50+
connection. For more fine-grained low-level control, please have a look at
51+
`Uart16550` instead.
5152

52-
# Example (Minimalistic)
53+
# Example (Minimal - x86 Port IO)
54+
55+
```rust
56+
use uart_16550::{Config, Uart16550Tty};
57+
use core::fmt::Write;
58+
59+
fn main() {
60+
// SAFETY: The port is valid and we have exclusive access.
61+
let mut uart = unsafe { Uart16550Tty::new_port(0x3f8, Config::default()).expect("should initialize device") };
62+
uart.write_str("hello world\nhow's it going?");
63+
}
64+
```
65+
66+
# Example (Minimal - MMIO)
5367

5468
```rust
5569
use uart_16550::{Config, Uart16550Tty};
@@ -58,7 +72,6 @@ use core::fmt::Write;
5872
fn main() {
5973
// SAFETY: The address is valid and we have exclusive access.
6074
let mut uart = unsafe { Uart16550Tty::new_mmio(0x1000 as *mut _, 4, Config::default()).expect("should initialize device") };
61-
// ^ or `new_port(0x3f8, Config::default())`
6275
uart.write_str("hello world\nhow's it going?");
6376
}
6477
```
@@ -74,6 +87,7 @@ fn main() {
7487
// ^ or `new_port(0x3f8)`
7588
uart.init(Config::default()).expect("should init device successfully");
7689
uart.test_loopback().expect("should have working loopback mode");
90+
// Note: Might fail on real hardware with some null-modem cables
7791
uart.check_connected().expect("should have physically connected receiver");
7892
uart.send_bytes_exact(b"hello world!");
7993
}

src/backend/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@
88
//! - [`MmioBackend`]
99
1010
mod mmio;
11-
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
11+
#[cfg(any(target_arch = "x86", target_arch = "x86_64", doc))]
1212
mod pio;
1313

1414
pub use mmio::{MmioAddress, MmioBackend};
15-
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
15+
#[cfg(any(target_arch = "x86", target_arch = "x86_64", doc))]
1616
pub use pio::{PioBackend, PortIoAddress};
1717

1818
use crate::spec::NUM_REGISTERS;

src/lib.rs

Lines changed: 58 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -50,24 +50,41 @@
5050
//!
5151
//! # Overview
5252
//!
53-
//! Use [`Uart16550Tty`] for a quick start. For more fine-grained low-level
54-
//! control, please have a look at [`Uart16550`] instead.
53+
//! Use [`Uart16550Tty`] for a quick start to write to a terminal via your
54+
//! serial connection. For more fine-grained low-level control, please have a
55+
//! look at [`Uart16550`] instead. The following examples show usage of the
56+
//! latter.
5557
//!
56-
//! # Example (Minimalistic)
58+
//! # Example (Minimal - x86 Port IO)
59+
//!
60+
#![cfg_attr(
61+
any(target_arch = "x86", target_arch = "x86_64"),
62+
doc = "```rust,no_run"
63+
)]
64+
#![cfg_attr(
65+
not(any(target_arch = "x86", target_arch = "x86_64")),
66+
doc = "```rust,ignore"
67+
)]
68+
//! use uart_16550::{Config, Uart16550};
69+
//!
70+
//! // SAFETY: The port is valid and we have exclusive access.
71+
//! let mut uart = unsafe { Uart16550::new_port(0x3f8).unwrap() };
72+
//! uart.init(Config::default()).expect("should init device successfully");
73+
//! uart.send_bytes_exact(b"hello world!");
74+
//! ```
75+
//!
76+
//! # Example (Minimal - MMIO)
5777
//!
5878
//! ```rust,no_run
59-
//! use uart_16550::{Config, Uart16550Tty};
60-
//! use core::fmt::Write;
79+
//! use uart_16550::{Config, Uart16550};
6180
//!
6281
//! // SAFETY: The address is valid and we have exclusive access.
63-
//! let mut uart = unsafe { Uart16550Tty::new_mmio(0x1000 as *mut _, 4, Config::default()).expect("should initialize device") };
64-
//! // ^ or `new_port(0x3f8, Config::default())`
65-
//! uart.write_str("hello world\nhow's it going?");
82+
//! let mut uart = unsafe { Uart16550::new_mmio(0x1000 as *mut _, 4).unwrap() };
83+
//! uart.init(Config::default()).expect("should init device successfully");
84+
//! uart.send_bytes_exact(b"hello world!");
6685
//! ```
6786
//!
68-
//! See [`Uart16550Tty`] for more details.
69-
//!
70-
//! # Example (More low-level control)
87+
//! # Example (Recommended)
7188
//!
7289
//! ```rust,no_run
7390
//! use uart_16550::{Config, Uart16550};
@@ -77,6 +94,7 @@
7794
//! // ^ or `new_port(0x3f8)`
7895
//! uart.init(Config::default()).expect("should init device successfully");
7996
//! uart.test_loopback().expect("should have working loopback mode");
97+
//! // Note: Might fail on real hardware with some null-modem cables
8098
//! uart.check_connected().expect("should have physically connected receiver");
8199
//! uart.send_bytes_exact(b"hello world!");
82100
//! ```
@@ -146,7 +164,7 @@ pub use crate::error::*;
146164
pub use crate::tty::*;
147165

148166
use crate::backend::{Backend, MmioAddress, MmioBackend};
149-
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
167+
#[cfg(any(target_arch = "x86", target_arch = "x86_64", doc))]
150168
use crate::backend::{PioBackend, PortIoAddress};
151169
use crate::spec::registers::{DLL, DLM, FCR, IER, ISR, LCR, LSR, MCR, MSR, SPR, offsets};
152170
use crate::spec::{FIFO_SIZE, NUM_REGISTERS, calc_baud_rate, calc_divisor};
@@ -171,16 +189,34 @@ mod tty;
171189
/// on the underlying hardware.
172190
///
173191
/// This type is generic over x86 port I/O and MMIO via the corresponding
174-
/// constructors (`Uart16550::new_port()` and [`Uart16550::new_mmio()`].
192+
/// constructors [`Uart16550::new_port()`] and [`Uart16550::new_mmio()`].
193+
/// The following examples show usage of the latter.
194+
///
195+
/// # Example (Minimal - x86 Port IO)
196+
///
197+
#[cfg_attr(
198+
any(target_arch = "x86", target_arch = "x86_64"),
199+
doc = "```rust,no_run"
200+
)]
201+
#[cfg_attr(
202+
not(any(target_arch = "x86", target_arch = "x86_64")),
203+
doc = "```rust,ignore"
204+
)]
205+
/// use uart_16550::{Config, Uart16550};
175206
///
176-
/// # Example (Minimal)
207+
/// // SAFETY: The port is valid and we have exclusive access.
208+
/// let mut uart = unsafe { Uart16550::new_port(0x3f8).unwrap() };
209+
/// uart.init(Config::default()).expect("should init device successfully");
210+
/// uart.send_bytes_exact(b"hello world!");
211+
/// ```
212+
///
213+
/// # Example (Minimal - MMIO)
177214
///
178215
/// ```rust,no_run
179216
/// use uart_16550::{Config, Uart16550};
180217
///
181218
/// // SAFETY: The address is valid and we have exclusive access.
182219
/// let mut uart = unsafe { Uart16550::new_mmio(0x1000 as *mut _, 4).unwrap() };
183-
/// // ^ or `new_port(0x3f8)`
184220
/// uart.init(Config::default()).expect("should init device successfully");
185221
/// uart.send_bytes_exact(b"hello world!");
186222
/// ```
@@ -195,6 +231,7 @@ mod tty;
195231
/// // ^ or `new_port(0x3f8)`
196232
/// uart.init(Config::default()).expect("should init device successfully");
197233
/// uart.test_loopback().expect("should have working loopback mode");
234+
/// // Note: Might fail on real hardware with some null-modem cables
198235
/// uart.check_connected().expect("should have physically connected receiver");
199236
/// uart.send_bytes_exact(b"hello world!");
200237
/// ```
@@ -221,15 +258,16 @@ mod tty;
221258
/// - [`Uart16550::send_bytes_exact`]: Transmit all bytes, looping until the
222259
/// entire buffer has been written.
223260
/// - [`Uart16550::receive_bytes_exact`]: Receive bytes until the provided
224-
/// buffer is completely filled.
261+
/// buffer is filled.
225262
///
226263
/// These methods spin until completion.
227264
///
228265
/// # MMIO and Port I/O
229266
///
230267
/// Uart 16550 devices are typically mapped via port I/O on x86 and via MMIO on
231-
/// other platforms. The constructors `new_port()` and `new_mmio()` create an
232-
/// instance of a device with the corresponding backend.
268+
/// other platforms. The constructors [`Uart16550::new_port()`] and
269+
/// [`Uart16550::new_mmio()`] create an instance of a device with the
270+
/// corresponding backend.
233271
///
234272
/// # Hints for Usage on Real Hardware
235273
///
@@ -248,7 +286,7 @@ pub struct Uart16550<B: Backend> {
248286
config: Config,
249287
}
250288

251-
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
289+
#[cfg(any(target_arch = "x86", target_arch = "x86_64", doc))]
252290
impl Uart16550<PioBackend> {
253291
/// Creates a new [`Uart16550`] backed by x86 port I/O.
254292
///
@@ -955,7 +993,7 @@ mod tests {
955993
#[test]
956994
fn constructors() {
957995
// SAFETY: We just test the constructor but do not access the device.
958-
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
996+
#[cfg(any(target_arch = "x86", target_arch = "x86_64", doc))]
959997
unsafe {
960998
assert2::assert!(let Ok(_) = Uart16550::new_port(0x3f8));
961999
assert2::assert!(let Ok(_) = Uart16550::new_port(u16::MAX - NUM_REGISTERS as u16));

src/tty.rs

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,13 @@
1212
//! See [`Uart16550Tty`].
1313
1414
use crate::backend::{Backend, MmioAddress, MmioBackend, RegisterAddress};
15-
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
15+
#[cfg(any(target_arch = "x86", target_arch = "x86_64", doc))]
1616
use crate::backend::{PioBackend, PortIoAddress};
1717
use crate::{Config, InitError, InvalidAddressError, LoopbackError, Uart16550};
1818
use core::error::Error;
1919
use core::fmt::{self, Display, Formatter};
2020

21-
/// Errors that [`Uart16550Tty::new_port`] and [`Uart16550Tty::new_mmio`] may
21+
/// Errors that [`Uart16550Tty::new_port()`] and [`Uart16550Tty::new_mmio()`] may
2222
/// return.
2323
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2424
pub enum Uart16550TtyError<A: RegisterAddress> {
@@ -62,22 +62,44 @@ impl<A: RegisterAddress + 'static> Error for Uart16550TtyError<A> {
6262
/// Ideal for quickly observing debug output during VM development and testing.
6363
/// It implements [`fmt::Write`] allowing the use of `write!()`.
6464
///
65-
/// # Example
65+
/// Access to the underlying UART device is provided through via
66+
/// [`Uart16550Tty::inner()`] and [`Uart16550Tty::inner_mut()`].
67+
///
68+
/// # Example (x86 Port IO)
69+
///
70+
#[cfg_attr(
71+
any(target_arch = "x86", target_arch = "x86_64"),
72+
doc = "```rust,no_run"
73+
)]
74+
#[cfg_attr(
75+
not(any(target_arch = "x86", target_arch = "x86_64")),
76+
doc = "```rust,ignore"
77+
)]
78+
/// use uart_16550::{Config, Uart16550Tty};
79+
/// use core::fmt::Write;
80+
///
81+
/// // SAFETY: The port is valid and we have exclusive access.
82+
/// let mut uart = unsafe { Uart16550Tty::new_port(0x3f8, Config::default()).expect("should initialize device") };
83+
/// uart.write_str("hello world\nhow's it going?");
84+
/// ```
85+
///
86+
/// # Example (MMIO)
87+
///
6688
/// ```rust,no_run
6789
/// use uart_16550::{Config, Uart16550Tty};
6890
/// use core::fmt::Write;
6991
///
7092
/// // SAFETY: The address is valid and we have exclusive access.
7193
/// let mut uart = unsafe { Uart16550Tty::new_mmio(0x1000 as *mut _, 4, Config::default()).expect("should initialize device") };
72-
/// // ^ or `new_port(0x3f8, Config::default())`
7394
/// uart.write_str("hello world\nhow's it going?");
7495
/// ```
7596
///
7697
/// # MMIO and Port I/O
7798
///
7899
/// Uart 16550 devices are typically mapped via port I/O on x86 and via MMIO on
79-
/// other platforms. The constructors `new_port()` and `new_mmio()` create an
80-
/// instance of a device with the corresponding backend.
100+
/// other platforms. The constructors [`Uart16550Tty::new_port()`] and
101+
/// [`Uart16550Tty::new_mmio()`] create an instance of a device with the
102+
/// corresponding backend.
81103
///
82104
/// # Hints for Usage on Real Hardware
83105
///
@@ -89,7 +111,7 @@ impl<A: RegisterAddress + 'static> Error for Uart16550TtyError<A> {
89111
#[derive(Debug)]
90112
pub struct Uart16550Tty<B: Backend>(Uart16550<B>);
91113

92-
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
114+
#[cfg(any(target_arch = "x86", target_arch = "x86_64", doc))]
93115
impl Uart16550Tty<PioBackend> {
94116
/// Creates a new [`Uart16550Tty`] backed by x86 port I/O.
95117
///

0 commit comments

Comments
 (0)