axhal/platform/x86_pc/
uart16550.rs

1//! Uart 16550.
2
3use kspin::SpinNoIrq;
4use x86_64::instructions::port::{Port, PortReadOnly, PortWriteOnly};
5
6const UART_CLOCK_FACTOR: usize = 16;
7const OSC_FREQ: usize = 1_843_200;
8
9static COM1: SpinNoIrq<Uart16550> = SpinNoIrq::new(Uart16550::new(0x3f8));
10
11bitflags::bitflags! {
12    /// Line status flags
13    struct LineStsFlags: u8 {
14        const INPUT_FULL = 1;
15        // 1 to 4 unknown
16        const OUTPUT_EMPTY = 1 << 5;
17        // 6 and 7 unknown
18    }
19}
20
21struct Uart16550 {
22    data: Port<u8>,
23    int_en: PortWriteOnly<u8>,
24    fifo_ctrl: PortWriteOnly<u8>,
25    line_ctrl: PortWriteOnly<u8>,
26    modem_ctrl: PortWriteOnly<u8>,
27    line_sts: PortReadOnly<u8>,
28}
29
30impl Uart16550 {
31    const fn new(port: u16) -> Self {
32        Self {
33            data: Port::new(port),
34            int_en: PortWriteOnly::new(port + 1),
35            fifo_ctrl: PortWriteOnly::new(port + 2),
36            line_ctrl: PortWriteOnly::new(port + 3),
37            modem_ctrl: PortWriteOnly::new(port + 4),
38            line_sts: PortReadOnly::new(port + 5),
39        }
40    }
41
42    fn init(&mut self, baud_rate: usize) {
43        unsafe {
44            // Disable interrupts
45            self.int_en.write(0x00);
46
47            // Enable DLAB
48            self.line_ctrl.write(0x80);
49
50            // Set maximum speed according the input baud rate by configuring DLL and DLM
51            let divisor = OSC_FREQ / (baud_rate * UART_CLOCK_FACTOR);
52            self.data.write((divisor & 0xff) as u8);
53            self.int_en.write((divisor >> 8) as u8);
54
55            // Disable DLAB and set data word length to 8 bits
56            self.line_ctrl.write(0x03);
57
58            // Enable FIFO, clear TX/RX queues and
59            // set interrupt watermark at 14 bytes
60            self.fifo_ctrl.write(0xC7);
61
62            // Mark data terminal ready, signal request to send
63            // and enable auxilliary output #2 (used as interrupt line for CPU)
64            self.modem_ctrl.write(0x0B);
65        }
66    }
67
68    fn line_sts(&mut self) -> LineStsFlags {
69        unsafe { LineStsFlags::from_bits_truncate(self.line_sts.read()) }
70    }
71
72    fn putchar(&mut self, c: u8) {
73        while !self.line_sts().contains(LineStsFlags::OUTPUT_EMPTY) {}
74        unsafe { self.data.write(c) };
75    }
76
77    fn getchar(&mut self) -> Option<u8> {
78        if self.line_sts().contains(LineStsFlags::INPUT_FULL) {
79            unsafe { Some(self.data.read()) }
80        } else {
81            None
82        }
83    }
84}
85
86/// Writes a byte to the console.
87fn putchar(c: u8) {
88    let mut uart = COM1.lock();
89    match c {
90        b'\n' => {
91            uart.putchar(b'\r');
92            uart.putchar(b'\n');
93        }
94        c => uart.putchar(c),
95    }
96}
97
98/// Reads a byte from the console, or returns [`None`] if no input is available.
99fn getchar() -> Option<u8> {
100    COM1.lock().getchar()
101}
102
103/// Write a slice of bytes to the console.
104pub fn write_bytes(bytes: &[u8]) {
105    for c in bytes {
106        putchar(*c);
107    }
108}
109
110/// Reads bytes from the console into the given mutable slice.
111/// Returns the number of bytes read.
112pub fn read_bytes(bytes: &mut [u8]) -> usize {
113    let mut read_len = 0;
114    while read_len < bytes.len() {
115        if let Some(c) = getchar() {
116            bytes[read_len] = c;
117        } else {
118            break;
119        }
120        read_len += 1;
121    }
122    read_len
123}
124
125pub(super) fn init() {
126    COM1.lock().init(115200);
127}