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(clippy::must_use_candidate, clippy::upper_case_acronyms)]
10
11#[cfg(feature = "writer")]
12pub mod writer;
13
14#[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
15pub use x86::*;
16#[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
17mod x86 {
18    use super::UartAddress;
19
20    /// Address of the first COM port.
21    /// This port is VERY likely to be at this address.
22    pub const COM1: UartAddress = UartAddress::Io(0x3F8);
23    /// Address of the second COM port.
24    /// This port is likely to be at this address.
25    pub const COM2: UartAddress = UartAddress::Io(0x2F8);
26    /// Address of the third COM port.
27    /// This address is configurable on some BIOS, so it is not a very reliable port address.
28    pub const COM3: UartAddress = UartAddress::Io(0x3E8);
29    /// Address of the fourth COM port.
30    /// This address is configurable on some BIOS, so it is not a very reliable port address.
31    pub const COM4: UartAddress = UartAddress::Io(0x2E8);
32}
33
34use bitflags::bitflags;
35use core::marker::PhantomData;
36
37bitflags! {
38    #[repr(transparent)]
39    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
40    pub struct InterruptEnable: u8 {
41        /// Interrupt when received data is available.
42        const RECEIVED_DATA  = 1 << 0;
43
44        /// Interrupt when the transmitter holding register is empty.
45        const TRANSMIT_EMPTY = 1 << 1;
46
47        /// Interrupt when the receiver line status register changes.
48        const RECEIVE_STATUS = 1 << 2;
49
50        /// Interrupt when the modem status reguster changes.
51        const MODEM_STATUS   = 1 << 3;
52
53        // Bits 4-7 are reserved
54    }
55}
56
57bitflags! {
58    #[repr(transparent)]
59    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
60    pub struct InterruptIdent: u8 {
61        /// Indicates an interrupt is pending.
62        const INTERRUPT_PENDING = 1 << 0;
63
64        /// Indicates a parity error, overrun error, framing error, or break interrupt.
65        ///
66        /// Reset by reading the line status register.
67        const RECV_LINE_STATUS  = 0b011 << 1;
68
69        /// Indicates the FIFO trigger level has been reached.
70        ///
71        /// Reset when FIFO drops below the trigger level.
72        const RECV_DATA_AVAIL   = 0b010 << 1;
73
74        /// Indicates there's at least 1 character in the FIFO, but no character has
75        /// been input to the FIFO or read from it since the last 4 character entries.
76        ///
77        /// Reset when reading from the receiver buffer register.
78        const TIMEOUT           = 0b110 << 1;
79
80        /// Indicates the transmitter holding register is empty.
81        ///
82        /// Reset by writing to the transmitter holding register or reading the interrupt identification register.
83        const TX_EMPTY          = 0b001 << 1;
84
85        /// CTS, DSR, RI, or DCD.
86        ///
87        /// Reset by reading the modem status register.
88        const MODEM_STATUS      = 0b000 << 1;
89
90        // Bits 4 & 5 are 0
91        // Bits 6 & 7 are 1
92    }
93}
94
95/// Serial port speed, measured in bauds.
96#[repr(u16)]
97#[derive(Debug, Copy, Clone, Eq, PartialEq)]
98pub enum Baud {
99    B115200 = 1,
100    B57600 = 2,
101    B38400 = 3,
102    B19200 = 6,
103    B9600 = 12,
104    B4800 = 24,
105    B2400 = 48,
106    B1200 = 96,
107    B300 = 384,
108    B50 = 2304,
109}
110
111bitflags! {
112    #[repr(transparent)]
113    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
114    pub struct FifoControl: u8 {
115        /// Enables the FIFO queue operation.
116        const ENABLE     = 1 << 0;
117
118        /// Clears the receiving queue of data.
119        const CLEAR_RX   = 1 << 1;
120
121        /// Clears the transmitting queue of data.
122        const CLEAR_TX   = 1 << 2;
123
124        /// The FIFO queue will trigger an interrupt when it contains 1 byte.
125        const INT_LVL_1  = 0b00 << 5;
126
127        /// The FIFO queue will trigger an interrupt when it contains 4 bytes.
128        const INT_LVL_4  = 0b01 << 5;
129
130        /// The FIFO queue will trigger an interrupt when it contains 8 bytes.
131        const INT_LVL_8  = 0b10 << 5;
132
133        /// The FIFO queue will trigger an interrupt when it contains 14 bytes.
134        const INT_LVL_14 = 0b11 << 5;
135    }
136}
137
138bitflags! {
139    #[repr(transparent)]
140    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
141    pub struct LineControl: u8 {
142        /// Each character is composed of 5 bits.
143        const BITS_5        = 0b00;
144
145        /// Each character is composed of 6 bits.
146        const BITS_6        = 0b01;
147
148        /// Each character is composed of 7 bits.
149        const BITS_7        = 0b10;
150
151        /// Each character is composed of 8 bits.
152        const BITS_8        = 0b11;
153
154        /// Each character transmission is followed by an extra stop bit.
155        const EXTRA_STOP    = 1 << 2;
156
157        /// Enables parity checking on the receiving end of the port.
158        const PARITY_ENABLE = 1 << 3;
159
160        /// Enables using even-1s based parity; otherwise, odd-1s based parity.
161        const EVEN_PARITY   = 1 << 4;
162
163        /// If even parity is enabled, the parity bit is transmitted and checked as
164        /// logic ‘0’. If odd parity is enabled, then the parity bit is transmitted
165        /// and checked as ‘1’.
166        const STICK_PARITY  = 1 << 5;
167
168        /// Forces the serial output into a logic '0' break state (and triggers the
169        /// interrupt, if enabled).
170        const BREAK_SIGNAL  = 1 << 6;
171
172        /// Enables access to the divisor latch registers, which allow setting the baud rate.
173        const DLAB          = 1 << 7;
174    }
175}
176
177bitflags! {
178    #[repr(transparent)]
179    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
180    pub struct ModemControl: u8 {
181        /// Signals the chip is ready to transmit and receive data.
182        const TERMINAL_READY     = 1 << 0;
183
184        /// Request the other end of the port to send more data.
185        const REQUEST_TO_SEND    = 1 << 1;
186
187        /// In loopback mode, connected Ring Indicator (RI) signal input.
188        const AUXILIARY_OUTPUT_1 = 1 << 2;
189
190        /// In loopback mode, connected Data Carrier Detect (DCD) signal input.
191        const AUXILIARY_OUTPUT_2 = 1 << 3;
192
193        /// Enables loopback mode, in which transmitted bits are able to be read
194        // on the same port. This is useful for testing chip operation.
195        const LOOPBACK_MODE      = 1 << 4;
196    }
197}
198
199bitflags! {
200    #[repr(transparent)]
201    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
202    pub struct LineStatus: u8 {
203        /// Indicates that the FIFO buffer has data awaiting transmission.
204        const DATA_AVAILABLE  = 1 << 0;
205
206        /// Indicates the FIFO was full when another character was transmitted.
207        const OVERRUN_ERROR   = 1 << 1;
208
209        /// Indicates the character at the top of the FIFO has failed the parity check.
210        const PARITY_ERROR    = 1 << 2;
211
212        /// Indicates the received character at the FIFO did not have a valid stop bit.
213        const FRAMING_ERROR   = 1 << 3;
214
215        /// Indicates a break condition has been reached in the current character.
216        const BREAK_INDICATOR = 1 << 4;
217
218        /// Indicates the transmitter holding register is empty (or FIFO is being used).
219        const THR_EMPTY       = 1 << 5;
220
221        /// Indicates the transmitter holding register AND shift register are empty (there is no more data).
222        const THR_SHR_EMPTY   = 1 << 6;
223
224        /// Indicates at least one parity error, framing error, or break indicators have been received.
225        const IMPENDING_ERROR = 1 << 7;
226    }
227}
228
229bitflags! {
230    #[repr(transparent)]
231    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
232    pub struct ModemStatus: u8 {
233        /// Data Clear To Send (DCTS) indicator.
234        const CLEAR_TO_SEND_CHANGED        = 1 << 0;
235
236        /// Delta Data Set Ready (DDSR) indicator.
237        const DATA_SET_READY_CHANGED       = 1 << 1;
238
239        /// Trailing Edge of Ring Indicator (TERI) detector.
240        /// The RI line has changed from low to high state.
241        const TRAILING_EDGE_RING_INDICATOR = 1 << 2;
242
243        /// Delta Data Carrier Detect (DDCD) indicator.
244        const CARRIER_DETECT_CHANGE        = 1 << 3;
245
246        /// Complement of the CTS input, or RTS in loopback mode.
247        const CLEAR_TO_SEND                = 1 << 4;
248
249        /// Complement of the DSR input, or DTR in loopback mode.
250        const DATA_SET_READY               = 1 << 5;
251
252        /// Complement of the RI input, or AUX1 in loopback mode.
253        const RING_INDICATOR               = 1 << 6;
254
255        /// Complement of the RI input, or AUX2 in loopback mode.
256        const CARRIER_DETECT               = 1 << 7;
257    }
258}
259
260pub enum UartAddress {
261    Io(u16),
262    Mmio(*mut u8),
263}
264
265#[repr(usize)]
266#[allow(dead_code)]
267enum ReadOffset {
268    /// Receiver Holding Register
269    RHR = 0x0,
270
271    /// Interrupt Enable Register
272    IER = 0x1,
273
274    /// Interrupt Identification Register
275    IIR = 0x2,
276
277    /// Line Control Register
278    LCR = 0x3,
279
280    /// Line Status Register
281    LSR = 0x5,
282
283    /// Modem Status Register
284    MSR = 0x6,
285}
286
287#[repr(usize)]
288enum WriteOffset {
289    /// Transmitter Holding Register
290    THR = 0x0,
291
292    /// Interrupt enable Register
293    IER = 0x1,
294
295    /// Fifo Control Register
296    FCR = 0x2,
297
298    /// Line Control Register
299    LCR = 0x3,
300
301    /// Modem Control Register
302    MCR = 0x4,
303}
304
305pub trait Mode {}
306pub struct Data;
307impl Mode for Data {}
308pub struct Configure;
309impl Mode for Configure {}
310
311pub struct Uart<M: Mode>(UartAddress, PhantomData<M>);
312
313impl<M: Mode> Uart<M> {
314    fn read(&self, offset: ReadOffset) -> u8 {
315        // Safety: Constructor requires a valid base address.
316        unsafe {
317            match self.0 {
318                UartAddress::Io(port) => {
319                    let value: u8;
320
321                    #[cfg(target_arch = "x86_64")]
322                    core::arch::asm!("in al, dx", out("al") value, in("dx") port + (offset as u16), options(nostack, nomem, preserves_flags));
323
324                    #[cfg(not(target_arch = "x86_64"))]
325                    unimplemented!();
326
327                    value
328                }
329
330                UartAddress::Mmio(ptr) => ptr.add(offset as usize).read_volatile(),
331            }
332        }
333    }
334
335    fn write(&mut self, offset: WriteOffset, value: u8) {
336        // Safety: Constructor requires a valid base address.
337        unsafe {
338            match self.0 {
339                UartAddress::Io(port) => {
340                    #[cfg(target_arch = "x86_64")]
341                    core::arch::asm!("out dx, al", in("dx") port + (offset as u16), in("al") value, options(nostack, nomem, preserves_flags));
342
343                    #[cfg(not(target_arch = "x86_64"))]
344                    unimplemented!();
345                }
346
347                UartAddress::Mmio(ptr) => ptr.add(offset as usize).write_volatile(value),
348            }
349        }
350    }
351
352    pub fn write_fifo_control(&mut self, fifo_control: FifoControl) {
353        self.write(WriteOffset::FCR, fifo_control.bits());
354    }
355
356    pub fn read_line_control(&self) -> LineControl {
357        // Safety: `self.read(...)` returns a single byte, of which `LineControl` uses all bits (so no unknown bits are possible).
358        unsafe { LineControl::from_bits(self.read(ReadOffset::LCR)).unwrap_unchecked() }
359    }
360
361    pub fn write_line_control(&mut self, value: LineControl) {
362        self.write(WriteOffset::LCR, value.bits());
363    }
364
365    pub fn write_modem_control(&mut self, value: ModemControl) {
366        self.write(WriteOffset::MCR, value.bits());
367    }
368
369    pub fn read_line_status(&self) -> LineStatus {
370        LineStatus::from_bits_truncate(self.read(ReadOffset::LSR))
371    }
372
373    pub fn read_modem_status(&self) -> ModemStatus {
374        ModemStatus::from_bits_truncate(self.read(ReadOffset::MSR))
375    }
376}
377
378impl Uart<Configure> {
379    fn read_divisor_latch(&self) -> u16 {
380        // When DLAB is enabled, RHR and IER become the LSB and MSB of the
381        // divisor latch register, respectively.
382
383        let lsb = u16::from(self.read(ReadOffset::RHR));
384        let msb = u16::from(self.read(ReadOffset::IER));
385
386        (msb << 8) | lsb
387    }
388
389    fn write_divisor_latch(&mut self, value: u16) {
390        // When DLAB is enabled, THR and IER become the LSB and MSB of the
391        // divisor latch register, respectively.
392        let value_le_bytes = value.to_le_bytes();
393        self.write(WriteOffset::THR, value_le_bytes[0]);
394        self.write(WriteOffset::IER, value_le_bytes[1]);
395    }
396
397    pub fn get_baud(&self) -> Baud {
398        match self.read_divisor_latch() {
399            1 => Baud::B115200,
400            2 => Baud::B57600,
401            3 => Baud::B38400,
402            6 => Baud::B19200,
403            12 => Baud::B9600,
404            24 => Baud::B4800,
405            48 => Baud::B2400,
406            96 => Baud::B1200,
407            384 => Baud::B300,
408            2304 => Baud::B50,
409            _ => unimplemented!(),
410        }
411    }
412
413    pub fn set_baud(&mut self, baud: Baud) {
414        self.write_divisor_latch(baud as u16);
415    }
416
417    /// Disables access to the divisor latch registers.
418    pub fn into_data_mode(mut self) -> Uart<Data> {
419        let mut line_control = self.read_line_control();
420        line_control.remove(LineControl::DLAB);
421
422        self.write(WriteOffset::LCR, line_control.bits());
423
424        Uart::<Data>(self.0, PhantomData)
425    }
426}
427
428impl Uart<Data> {
429    /// ### Safety
430    ///
431    /// - Provided address must be valid for reading as a UART device.
432    /// - Provided address must not be otherwise mutably aliased.
433    pub unsafe fn new(address: UartAddress) -> Self {
434        Self(address, PhantomData)
435    }
436
437    pub fn read_byte(&self) -> u8 {
438        self.read(ReadOffset::RHR)
439    }
440
441    pub fn write_byte(&mut self, byte: u8) {
442        self.write(WriteOffset::THR, byte);
443    }
444
445    pub fn read_interrupt_enable(&self) -> InterruptEnable {
446        InterruptEnable::from_bits_truncate(self.read(ReadOffset::IER))
447    }
448
449    pub fn write_interrupt_enable(&mut self, value: InterruptEnable) {
450        self.write(WriteOffset::IER, value.bits());
451    }
452
453    /// Enables access to the divisor latch registers.
454    pub fn into_configure_mode(mut self) -> Uart<Configure> {
455        let mut line_control = self.read_line_control();
456        line_control.insert(LineControl::DLAB);
457
458        self.write(WriteOffset::LCR, line_control.bits());
459
460        Uart::<Configure>(self.0, PhantomData)
461    }
462}