uart_16550/
mmio.rs

1use core::{
2    fmt,
3    sync::atomic::{AtomicPtr, Ordering},
4};
5
6use crate::{LineStsFlags, WouldBlockError};
7
8/// A memory-mapped UART.
9#[derive(Debug)]
10pub struct MmioSerialPort {
11    data: AtomicPtr<u8>,
12    int_en: AtomicPtr<u8>,
13    fifo_ctrl: AtomicPtr<u8>,
14    line_ctrl: AtomicPtr<u8>,
15    modem_ctrl: AtomicPtr<u8>,
16    line_sts: AtomicPtr<u8>,
17}
18
19impl MmioSerialPort {
20    /// Creates a new UART interface on the given memory mapped address.
21    ///
22    /// This function is unsafe because the caller must ensure that the given base address
23    /// really points to a serial port device.
24    #[rustversion::attr(since(1.61), const)]
25    pub unsafe fn new(base: usize) -> Self {
26        Self::new_with_stride(base, 1)
27    }
28
29    /// Creates a new UART interface on the given memory mapped address with a given
30    /// register stride.
31    ///
32    /// This function is unsafe because the caller must ensure that the given base address
33    /// really points to a serial port device.
34    #[rustversion::attr(since(1.61), const)]
35    pub unsafe fn new_with_stride(base: usize, stride: usize) -> Self {
36        let base_pointer = base as *mut u8;
37        Self {
38            data: AtomicPtr::new(base_pointer),
39            int_en: AtomicPtr::new(base_pointer.add(1 * stride)),
40            fifo_ctrl: AtomicPtr::new(base_pointer.add(2 * stride)),
41            line_ctrl: AtomicPtr::new(base_pointer.add(3 * stride)),
42            modem_ctrl: AtomicPtr::new(base_pointer.add(4 * stride)),
43            line_sts: AtomicPtr::new(base_pointer.add(5 * stride)),
44        }
45    }
46
47    /// Initializes the memory-mapped UART.
48    ///
49    /// The default configuration of [38400/8-N-1](https://en.wikipedia.org/wiki/8-N-1) is used.
50    pub fn init(&mut self) {
51        let self_int_en = self.int_en.load(Ordering::Relaxed);
52        let self_line_ctrl = self.line_ctrl.load(Ordering::Relaxed);
53        let self_data = self.data.load(Ordering::Relaxed);
54        let self_fifo_ctrl = self.fifo_ctrl.load(Ordering::Relaxed);
55        let self_modem_ctrl = self.modem_ctrl.load(Ordering::Relaxed);
56        unsafe {
57            // Disable interrupts
58            self_int_en.write(0x00);
59
60            // Enable DLAB
61            self_line_ctrl.write(0x80);
62
63            // Set maximum speed to 38400 bps by configuring DLL and DLM
64            self_data.write(0x03);
65            self_int_en.write(0x00);
66
67            // Disable DLAB and set data word length to 8 bits
68            self_line_ctrl.write(0x03);
69
70            // Enable FIFO, clear TX/RX queues and
71            // set interrupt watermark at 14 bytes
72            self_fifo_ctrl.write(0xC7);
73
74            // Mark data terminal ready, signal request to send
75            // and enable auxilliary output #2 (used as interrupt line for CPU)
76            self_modem_ctrl.write(0x0B);
77
78            // Enable interrupts
79            self_int_en.write(0x01);
80        }
81    }
82
83    fn line_sts(&mut self) -> LineStsFlags {
84        unsafe { LineStsFlags::from_bits_truncate(*self.line_sts.load(Ordering::Relaxed)) }
85    }
86
87    /// Sends a byte on the serial port.
88    pub fn send(&mut self, data: u8) {
89        match data {
90            8 | 0x7F => {
91                self.send_raw(8);
92                self.send_raw(b' ');
93                self.send_raw(8);
94            }
95            data => {
96                self.send_raw(data);
97            }
98        }
99    }
100
101    /// Sends a raw byte on the serial port, intended for binary data.
102    pub fn send_raw(&mut self, data: u8) {
103        retry_until_ok!(self.try_send_raw(data))
104    }
105
106    /// Tries to send a raw byte on the serial port, intended for binary data.
107    pub fn try_send_raw(&mut self, data: u8) -> Result<(), WouldBlockError> {
108        if self.line_sts().contains(LineStsFlags::OUTPUT_EMPTY) {
109            let self_data = self.data.load(Ordering::Relaxed);
110            unsafe {
111                self_data.write(data);
112            }
113            Ok(())
114        } else {
115            Err(WouldBlockError)
116        }
117    }
118
119    /// Receives a byte on the serial port.
120    pub fn receive(&mut self) -> u8 {
121        retry_until_ok!(self.try_receive())
122    }
123
124    /// Tries to receive a byte on the serial port.
125    pub fn try_receive(&mut self) -> Result<u8, WouldBlockError> {
126        if self.line_sts().contains(LineStsFlags::INPUT_FULL) {
127            let self_data = self.data.load(Ordering::Relaxed);
128            let data = unsafe { self_data.read() };
129            Ok(data)
130        } else {
131            Err(WouldBlockError)
132        }
133    }
134}
135
136impl fmt::Write for MmioSerialPort {
137    fn write_str(&mut self, s: &str) -> fmt::Result {
138        for byte in s.bytes() {
139            self.send(byte);
140        }
141        Ok(())
142    }
143}