uart/
lib.rs

1#![no_std]
2#![forbid(clippy::inline_asm_x86_att_syntax)]
3#![deny(
4    clippy::semicolon_if_nothing_returned,
5    clippy::debug_assert_with_mut_call,
6    clippy::float_arithmetic
7)]
8#![warn(clippy::cargo, clippy::pedantic, clippy::undocumented_unsafe_blocks)]
9#![allow(
10    clippy::cast_lossless,
11    clippy::enum_glob_use,
12    clippy::inline_always,
13    clippy::items_after_statements,
14    clippy::must_use_candidate,
15    clippy::unreadable_literal,
16    clippy::wildcard_imports
17)]
18
19#[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
20pub use x86::*;
21#[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
22mod x86 {
23    use super::UartAddress;
24
25    /// Address of the first COM port.
26    /// This port is VERY likely to be at this address.
27    pub const COM1: UartAddress = UartAddress::Io(0x3F8);
28    /// Address of the second COM port.
29    /// This port is likely to be at this address.
30    pub const COM2: UartAddress = UartAddress::Io(0x2F8);
31    /// Address of the third COM port.
32    /// This address is configurable on some BIOS, so it is not a very reliable port address.
33    pub const COM3: UartAddress = UartAddress::Io(0x3E8);
34    /// Address of the fourth COM port.
35    /// This address is configurable on some BIOS, so it is not a very reliable port address.
36    pub const COM4: UartAddress = UartAddress::Io(0x2E8);
37}
38
39use bit_field::BitField;
40use bitflags::bitflags;
41use core::marker::PhantomData;
42
43bitflags! {
44    #[repr(transparent)]
45    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
46    pub struct InterruptEnable : u8 {
47        /// Interrupt when received data is available.
48        const RECEIVED_DATA = 1 << 0;
49        /// Interrupt when the transmit holding register is empty.
50        const TRANSMIT_EMPTY = 1 << 1;
51        /// Interrupt when the receiver line status register changes.
52        const RECEIVE_STATUS = 1 << 2;
53        /// Interrupt when the modem status reguster changes.
54        const MODEM_STATUS = 1 << 3;
55        /// This bit is UART 16750 -specific.
56        const SLEEP_MODE = 1 << 4;
57        /// This bit is UART 16750 -specific.
58        const LOW_POWER = 1 << 5;
59        // Bit 6 reserved
60        // Bit 7 reserved
61    }
62}
63
64#[repr(u8)]
65#[derive(Debug, Clone, Copy, PartialEq, Eq)]
66pub enum FifoSize {
67    Four = 0b01,
68    Eight = 0b10,
69    Fourteen = 0b11,
70}
71
72#[repr(u8)]
73#[derive(Debug, Clone, Copy, PartialEq, Eq)]
74pub enum DataBits {
75    Five = 0b00,
76    Six = 0b01,
77    Seven = 0b10,
78    Eight = 0b11,
79}
80
81#[repr(u8)]
82#[derive(Debug, Clone, Copy, PartialEq, Eq)]
83pub enum ParityMode {
84    None = 0b000,
85    Odd = 0b001,
86    Even = 0b011,
87    High = 0b101,
88    Low = 0b111,
89}
90
91/// Serial port speed, measured in bauds.
92#[repr(u16)]
93#[derive(Debug, Copy, Clone, Eq, PartialEq)]
94pub enum Baud {
95    B115200 = 1,
96    B57600 = 2,
97    B38400 = 3,
98    B19200 = 6,
99    B9600 = 12,
100    B4800 = 24,
101    B2400 = 48,
102    B1200 = 96,
103    B300 = 384,
104    B50 = 2304,
105}
106
107#[repr(C, packed)]
108pub struct LineControl {
109    pub bits: DataBits,
110    pub parity: ParityMode,
111    pub extra_stop: bool,
112    pub break_signal: bool,
113}
114
115impl LineControl {
116    #[inline]
117    pub const fn empty() -> Self {
118        Self {
119            bits: DataBits::Five,
120            parity: ParityMode::None,
121            extra_stop: false,
122            break_signal: false,
123        }
124    }
125
126    #[inline]
127    pub fn as_u8(self) -> u8 {
128        *0.set_bits(0..2, self.bits as u8)
129            .set_bit(2, self.extra_stop)
130            .set_bits(3..6, self.parity as u8)
131            .set_bit(6, self.break_signal)
132    }
133}
134
135bitflags! {
136    #[repr(transparent)]
137    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
138    pub struct ModemControl : u8 {
139        const TERMINAL_READY = 1 << 0;
140        const REQUEST_TO_SEND = 1 << 1;
141        const AUXILIARY_OUTPUT_1 = 1 << 2;
142        const AUXILIARY_OUTPUT_2 = 1 << 3;
143        const LOOPBACK_MODE = 1 << 4;
144    }
145}
146
147bitflags! {
148    #[repr(transparent)]
149    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
150    pub struct LineStatus : u8 {
151        const DATA_AVAILABLE = 1 << 0;
152        const OVERRUN_ERROR = 1 << 1;
153        const PARITY_ERROR = 1 << 2;
154        const FRAMING_ERROR = 1 << 3;
155        const BREAK_INDICATOR = 1 << 4;
156        const TRANSMIT_EMPTY = 1 << 5;
157        const TRANSMIT_EMPTY_IDLE = 1 << 6;
158        const IMPENDING_ERROR = 1 << 7;
159    }
160}
161
162bitflags! {
163    #[repr(transparent)]
164    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
165    pub struct ModemStatus : u8 {
166        const CLEAR_TO_SEND_CHANGED = 1 << 0;
167        const DATA_SET_READY_CHANGED= 1 << 1;
168        const TRAILING_EDGE_RING_INDICATOR = 1 << 2;
169        const CARRIER_DETECT_CHANGE = 1 << 3;
170        const CLEAR_TO_SEND = 1 << 4;
171        const DATA_SET_READY = 1 << 5;
172        const RING_INDICATOR = 1 << 6;
173        const CARRIER_DETECT = 1 << 7;
174    }
175}
176
177pub enum UartAddress {
178    Io(u16),
179    Mmio(*mut u8),
180}
181
182#[repr(usize)]
183#[allow(dead_code)]
184enum ReadOffset {
185    Data0 = 0x0,
186    Data1 = 0x1,
187    InterruptIdent = 0x2,
188    LineControl = 0x3,
189    ModemControl = 0x4,
190    LineStatus = 0x5,
191    ModemStatus = 0x6,
192}
193
194#[repr(usize)]
195enum WriteOffset {
196    Data0 = 0x0,
197    Data1 = 0x1,
198    FifoControl = 0x2,
199    LineControl = 0x3,
200    ModemControl = 0x4,
201}
202
203pub trait Mode {}
204pub struct Data;
205impl Mode for Data {}
206pub struct Configure;
207impl Mode for Configure {}
208
209pub struct Uart<M: Mode>(UartAddress, PhantomData<M>);
210
211impl<M: Mode> Uart<M> {
212    #[inline]
213    fn read(&self, offset: ReadOffset) -> u8 {
214        // Safety: Constructor for `Uart` requires a valid base address.
215        unsafe {
216            match self.0 {
217                UartAddress::Io(port) => {
218                    let value: u8;
219
220                    #[cfg(target_arch = "x86_64")]
221                    core::arch::asm!("in al, dx", out("al") value, in("dx") port + (offset as u16), options(nostack, nomem, preserves_flags));
222                    #[cfg(not(target_arch = "x86_64"))]
223                    unimplemented!();
224
225                    value
226                }
227
228                UartAddress::Mmio(ptr) => ptr.add(offset as usize).read_volatile(),
229            }
230        }
231    }
232
233    #[inline]
234    fn write(&mut self, offset: WriteOffset, value: u8) {
235        // Safety: Constructor for `Uart` requires a valid base address.
236        unsafe {
237            match self.0 {
238                UartAddress::Io(port) => {
239                    #[cfg(target_arch = "x86_64")]
240                    core::arch::asm!("out dx, al", in("dx") port + (offset as u16), in("al") value, options(nostack, nomem, preserves_flags));
241                    #[cfg(not(target_arch = "x86_64"))]
242                    unimplemented!();
243                }
244
245                UartAddress::Mmio(ptr) => ptr.add(offset as usize).write_volatile(value),
246            }
247        }
248    }
249
250    // TODO safety
251    #[inline]
252    pub fn disable_fifo(&mut self) {
253        self.write(WriteOffset::FifoControl, 0x0);
254    }
255
256    // TODO safety
257    #[inline]
258    pub fn enable_fifo(
259        &mut self,
260        clear_rx: bool,
261        clear_tx: bool,
262        dma_mode_1: bool,
263        size: FifoSize,
264        /* todo enable_64_byte_buffer */
265    ) {
266        let mut control_bits = 1u8;
267        control_bits.set_bit(1, clear_rx);
268        control_bits.set_bit(2, clear_tx);
269        control_bits.set_bit(3, dma_mode_1);
270        control_bits.set_bits(6..8, size as u8);
271
272        self.write(WriteOffset::FifoControl, control_bits);
273    }
274
275    #[inline]
276    pub fn read_line_control(&self) -> LineControl {
277        let line_control_raw = self.read(ReadOffset::LineControl);
278        LineControl {
279            bits: match line_control_raw.get_bits(0..2) {
280                0b00 => DataBits::Five,
281                0b01 => DataBits::Six,
282                0b10 => DataBits::Seven,
283                0b11 => DataBits::Eight,
284                _ => unimplemented!(),
285            },
286            parity: match line_control_raw.get_bits(3..6) {
287                0b000 => ParityMode::None,
288                0b001 => ParityMode::Odd,
289                0b011 => ParityMode::Even,
290                0b101 | 0b111 => ParityMode::High,
291                _ => unimplemented!(),
292            },
293            extra_stop: line_control_raw.get_bit(2),
294            break_signal: line_control_raw.get_bit(6),
295        }
296    }
297
298    #[inline]
299    pub fn write_line_control(&mut self, value: LineControl) {
300        self.write(WriteOffset::LineControl, value.as_u8());
301    }
302
303    #[inline]
304    pub fn read_modem_control(&self) -> ModemControl {
305        ModemControl::from_bits_truncate(self.read(ReadOffset::ModemControl))
306    }
307
308    #[inline]
309    pub fn write_model_control(&mut self, value: ModemControl) {
310        self.write(WriteOffset::ModemControl, value.bits());
311    }
312
313    #[inline]
314    pub fn read_line_status(&self) -> LineStatus {
315        LineStatus::from_bits_truncate(self.read(ReadOffset::LineStatus))
316    }
317
318    #[inline]
319    pub fn read_modem_status(&self) -> ModemStatus {
320        ModemStatus::from_bits_truncate(self.read(ReadOffset::ModemStatus))
321    }
322}
323
324impl Uart<Configure> {
325    #[inline]
326    fn read_divisor_latch(&self) -> u16 {
327        ((self.read(ReadOffset::Data1) as u16) << 8) | (self.read(ReadOffset::Data0) as u16)
328    }
329
330    #[inline]
331    fn write_divisor_latch(&mut self, value: u16) {
332        let value_le_bytes = value.to_le_bytes();
333        self.write(WriteOffset::Data0, value_le_bytes[0]);
334        self.write(WriteOffset::Data1, value_le_bytes[1]);
335    }
336
337    pub fn get_baud(&self) -> Baud {
338        match self.read_divisor_latch() {
339            1 => Baud::B115200,
340            2 => Baud::B57600,
341            3 => Baud::B38400,
342            6 => Baud::B19200,
343            12 => Baud::B9600,
344            24 => Baud::B4800,
345            48 => Baud::B2400,
346            96 => Baud::B1200,
347            384 => Baud::B300,
348            2304 => Baud::B50,
349            _ => unimplemented!(),
350        }
351    }
352
353    pub fn set_baud(&mut self, baud: Baud) {
354        self.write_divisor_latch(baud as u16);
355    }
356
357    pub fn data_mode(mut self) -> Uart<Data> {
358        // enable DLAB
359        self.write(WriteOffset::LineControl, self.read_line_control().as_u8());
360
361        Uart::<Data>(self.0, PhantomData)
362    }
363}
364
365impl Uart<Data> {
366    /// ### Safety
367    ///
368    /// - Provided address must be valid for reading as a UART device.
369    /// - Provided address must not be otherwise mutably aliased.
370    pub unsafe fn new(address: UartAddress) -> Self {
371        Self(address, PhantomData)
372    }
373
374    #[inline]
375    pub fn read_data(&self) -> u8 {
376        self.read(ReadOffset::Data0)
377    }
378
379    #[inline]
380    pub fn write_data(&mut self, data: u8) {
381        self.write(WriteOffset::Data0, data);
382    }
383
384    #[inline]
385    pub fn read_interrupt_enable(&self) -> InterruptEnable {
386        InterruptEnable::from_bits_truncate(self.read(ReadOffset::Data1))
387    }
388
389    #[inline]
390    pub fn write_interrupt_enable(&mut self, value: InterruptEnable) {
391        self.write(WriteOffset::Data1, value.bits());
392    }
393
394    #[inline]
395    pub fn configure_mode(mut self) -> Uart<Configure> {
396        // enable DLAB
397        self.write(
398            WriteOffset::LineControl,
399            self.read_line_control().as_u8() | (1 << 7),
400        );
401
402        Uart::<Configure>(self.0, PhantomData)
403    }
404}
405
406pub const UART_FIFO_QUEUE_LEN: usize = 14;
407
408pub struct UartWriter {
409    uart: Uart<Data>,
410    queue_accumulator: usize,
411}
412
413impl UartWriter {
414    pub fn new(mut uart: Uart<Data>) -> Option<Self> {
415        // Bring UART to a known state.
416        uart.write_line_control(LineControl::empty());
417        uart.write_interrupt_enable(InterruptEnable::empty());
418
419        // Configure the baud rate (tx/rx speed).
420        let mut uart = uart.configure_mode();
421        uart.set_baud(Baud::B115200);
422        let mut uart = uart.data_mode();
423
424        // Configure total UART state.
425        uart.write_line_control(LineControl {
426            bits: DataBits::Eight,
427            parity: ParityMode::None,
428            extra_stop: false,
429            break_signal: false,
430        });
431        uart.enable_fifo(true, true, false, FifoSize::Fourteen);
432
433        // Test the UART to ensure it's functioning correctly.
434        uart.write_model_control(
435            ModemControl::REQUEST_TO_SEND
436                | ModemControl::AUXILIARY_OUTPUT_1
437                | ModemControl::AUXILIARY_OUTPUT_2
438                | ModemControl::LOOPBACK_MODE,
439        );
440
441        const TEST_VALUE: u8 = 0x1F;
442        uart.write_data(TEST_VALUE);
443        if uart.read_data().ne(&TEST_VALUE) {
444            return None;
445        }
446
447        // Configure modem control for actual UART usage.
448        uart.write_model_control(
449            ModemControl::TERMINAL_READY
450                | ModemControl::REQUEST_TO_SEND
451                | ModemControl::AUXILIARY_OUTPUT_1
452                | ModemControl::AUXILIARY_OUTPUT_2,
453        );
454
455        Some(Self {
456            uart,
457            queue_accumulator: 0,
458        })
459    }
460
461    fn queue_index(&self) -> usize {
462        self.queue_accumulator % UART_FIFO_QUEUE_LEN
463    }
464
465    fn write_byte(&mut self, byte: u8) {
466        if self.queue_index() == UART_FIFO_QUEUE_LEN {
467            while !self
468                .uart
469                .read_line_status()
470                .contains(LineStatus::TRANSMIT_EMPTY_IDLE)
471            {
472                core::hint::spin_loop();
473            }
474        } else {
475            while !self
476                .uart
477                .read_line_status()
478                .contains(LineStatus::TRANSMIT_EMPTY)
479            {
480                core::hint::spin_loop();
481            }
482        }
483
484        self.uart.write_data(byte);
485        self.queue_accumulator += 1;
486    }
487}
488
489impl core::fmt::Write for UartWriter {
490    fn write_str(&mut self, s: &str) -> core::fmt::Result {
491        s.chars().try_for_each(|c| self.write_char(c))
492    }
493
494    fn write_char(&mut self, c: char) -> core::fmt::Result {
495        self.write_byte(u8::try_from(c).unwrap_or(b'?'));
496
497        Ok(())
498    }
499}