1use core::hint;
2
3use crate::{cmdline, cpu};
4
5#[repr(u32)]
6#[derive(Clone, Copy, Debug)]
7pub enum UartPort {
8 COM1 = 0x3F8,
9}
10
11#[repr(u8)]
12enum UartReg {
13 Data = 0,
14 InterruptEnable = 1,
15 InterruptAndFifoControl = 2,
16 LineControl = 3,
17 ModemControl = 4,
18 LineStatus = 5,
19 #[allow(dead_code)]
20 ModemStatus = 6,
21 #[allow(dead_code)]
22 Scratch = 7,
23}
24
25const LINE_BAUD_LATCH: u8 = 1 << 7;
27
28const MODEM_CTL_DTR: u8 = 1 << 0;
30const MODEM_CTL_RTS: u8 = 1 << 1;
32const MODEM_CTL_OUT1: u8 = 1 << 2;
34const MODEM_CTL_OUT2: u8 = 1 << 3;
36const MODEM_CTL_LOOPBACK: u8 = 1 << 4;
38
39const IE_RX_READY: u8 = 1 << 0;
40const IE_TX_READY: u8 = 1 << 1;
41
42const LINE_RX_READY: u8 = 1 << 0;
44const LINE_TX_EMPTY: u8 = 1 << 5;
46
47fn write_reg(port_addr: UartPort, reg: UartReg, val: u8) {
48 unsafe { cpu::io_out(port_addr as u16 + reg as u16, val) }
49}
50
51fn read_reg(port_addr: UartPort, reg: UartReg) -> u8 {
52 unsafe { cpu::io_in(port_addr as u16 + reg as u16) }
53}
54
55fn init_port(port_addr: UartPort) -> bool {
57 write_reg(port_addr, UartReg::InterruptEnable, 0);
59 write_reg(port_addr, UartReg::InterruptAndFifoControl, 0);
61
62 let rate = if cmdline::cmdline().uart_baud == 0 {
64 cmdline::cmdline().uart_baud
65 } else {
66 115200
67 };
68 let mut divisor = 115200 / rate;
69 if divisor == 0 {
70 divisor = 1;
71 }
72 if divisor >= 0x10000 {
73 divisor = 0xFFFF;
74 }
75
76 write_reg(port_addr, UartReg::LineControl, LINE_BAUD_LATCH);
79 write_reg(port_addr, UartReg::Data, divisor as u8);
82 write_reg(port_addr, UartReg::InterruptEnable, (divisor >> 8) as u8);
84 write_reg(port_addr, UartReg::LineControl, 0x03);
87
88 write_reg(
93 port_addr,
94 UartReg::InterruptEnable,
95 IE_RX_READY | IE_TX_READY,
96 );
97
98 write_reg(port_addr, UartReg::ModemControl, MODEM_CTL_LOOPBACK);
101 write_reg(port_addr, UartReg::Data, 0xAA);
102 while read_reg(port_addr, UartReg::LineStatus) & LINE_RX_READY == 0 {
104 hint::spin_loop();
105 }
106 let val = read_reg(port_addr, UartReg::Data);
108
109 write_reg(
111 port_addr,
112 UartReg::ModemControl,
113 MODEM_CTL_DTR | MODEM_CTL_RTS | MODEM_CTL_OUT1 | MODEM_CTL_OUT2,
114 );
115
116 val == 0xAA
118}
119
120#[derive(Clone, Debug)]
121pub struct Uart {
122 port_addr: UartPort,
123 is_enabled: bool,
124}
125
126impl Uart {
127 pub const fn new(port_addr: UartPort) -> Self {
128 Self {
129 port_addr,
130 is_enabled: false,
131 }
132 }
133
134 pub fn init(&mut self) {
135 self.is_enabled = cmdline::cmdline().uart && init_port(self.port_addr);
136 }
137
138 pub unsafe fn write_byte(&self, byte: u8) {
140 if !self.is_enabled {
141 return;
142 }
143
144 while (read_reg(self.port_addr, UartReg::LineStatus) & LINE_TX_EMPTY) == 0 {
146 hint::spin_loop();
147 }
148 write_reg(self.port_addr, UartReg::Data, byte);
150 }
151
152 pub unsafe fn try_read_byte(&self) -> Option<u8> {
154 if !self.is_enabled {
155 return None;
156 }
157
158 if (read_reg(self.port_addr, UartReg::LineStatus) & LINE_RX_READY) == 0 {
160 return None;
161 }
162 Some(read_reg(self.port_addr, UartReg::Data))
164 }
165
166 #[allow(dead_code)]
167 pub fn interrupt_num(&self) -> u8 {
168 match self.port_addr {
169 UartPort::COM1 => 4,
170 }
171 }
172}