1use spin::Once;
6use x86_64::instructions::port::ReadWriteAccess;
7
8use crate::{
9 boot::EarlyCmdline,
10 console::uart_ns16650a::{Ns16550aAccess, Ns16550aRegister, Ns16550aUart},
11 io::{IoPort, reserve_io_port_range},
12 sync::{LocalIrqDisabled, SpinLock},
13};
14
15pub static SERIAL_PORT: Once<SpinLock<Ns16550aUart<SerialAccess>, LocalIrqDisabled>> = Once::new();
17
18#[derive(Debug)]
20pub struct SerialAccess {
21 data: IoPort<u8, ReadWriteAccess>,
22 int_en: IoPort<u8, ReadWriteAccess>,
23 fifo_ctrl: IoPort<u8, ReadWriteAccess>,
24 line_ctrl: IoPort<u8, ReadWriteAccess>,
25 modem_ctrl: IoPort<u8, ReadWriteAccess>,
26 line_stat: IoPort<u8, ReadWriteAccess>,
27 modem_stat: IoPort<u8, ReadWriteAccess>,
28}
29
30impl SerialAccess {
31 const unsafe fn new(port: u16) -> Self {
36 unsafe {
38 Self {
39 data: IoPort::new(port),
40 int_en: IoPort::new(port + 1),
41 fifo_ctrl: IoPort::new(port + 2),
42 line_ctrl: IoPort::new(port + 3),
43 modem_ctrl: IoPort::new(port + 4),
44 line_stat: IoPort::new(port + 5),
45 modem_stat: IoPort::new(port + 6),
46 }
47 }
48 }
49
50 fn probe(&mut self) -> bool {
54 const IER_ALL_INTR: u8 = 0x0F;
61
62 let saved_ier = self.read(Ns16550aRegister::IntEnOrDivisorHi);
63
64 let is_ok1 = {
65 self.write(Ns16550aRegister::IntEnOrDivisorHi, 0x00);
66 self.read(Ns16550aRegister::IntEnOrDivisorHi) & IER_ALL_INTR == 0
67 };
68 let is_ok2 = {
69 self.write(Ns16550aRegister::IntEnOrDivisorHi, IER_ALL_INTR);
70 self.read(Ns16550aRegister::IntEnOrDivisorHi) & IER_ALL_INTR == IER_ALL_INTR
71 };
72
73 self.write(Ns16550aRegister::IntEnOrDivisorHi, saved_ier);
74
75 is_ok1 && is_ok2
76 }
77}
78
79impl Ns16550aAccess for SerialAccess {
80 fn read(&self, reg: Ns16550aRegister) -> u8 {
81 match reg {
82 Ns16550aRegister::DataOrDivisorLo => self.data.read(),
83 Ns16550aRegister::IntEnOrDivisorHi => self.int_en.read(),
84 Ns16550aRegister::FifoCtrl => self.fifo_ctrl.read(),
85 Ns16550aRegister::LineCtrl => self.line_ctrl.read(),
86 Ns16550aRegister::ModemCtrl => self.modem_ctrl.read(),
87 Ns16550aRegister::LineStat => self.line_stat.read(),
88 Ns16550aRegister::ModemStat => self.modem_stat.read(),
89 }
90 }
91
92 fn write(&mut self, reg: Ns16550aRegister, val: u8) {
93 match reg {
94 Ns16550aRegister::DataOrDivisorLo => self.data.write(val),
95 Ns16550aRegister::IntEnOrDivisorHi => self.int_en.write(val),
96 Ns16550aRegister::FifoCtrl => self.fifo_ctrl.write(val),
97 Ns16550aRegister::LineCtrl => self.line_ctrl.write(val),
98 Ns16550aRegister::ModemCtrl => self.modem_ctrl.write(val),
99 Ns16550aRegister::LineStat => self.line_stat.write(val),
100 Ns16550aRegister::ModemStat => self.modem_stat.write(val),
101 }
102 }
103}
104
105pub(crate) unsafe fn init(early_cmdline: &EarlyCmdline) {
111 if !early_cmdline.has_early_console {
112 return;
113 }
114
115 let mut access = unsafe { SerialAccess::new(0x3F8) };
121 if !access.probe() {
122 core::mem::forget(access);
126 return;
127 }
128
129 let mut serial = Ns16550aUart::new(access);
130 serial.init();
131
132 SERIAL_PORT.call_once(|| SpinLock::new(serial));
133}
134reserve_io_port_range!(0x3F8..0x400);