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
51impl Ns16550aAccess for SerialAccess {
52 fn read(&self, reg: Ns16550aRegister) -> u8 {
53 match reg {
54 Ns16550aRegister::DataOrDivisorLo => self.data.read(),
55 Ns16550aRegister::IntEnOrDivisorHi => self.int_en.read(),
56 Ns16550aRegister::FifoCtrl => self.fifo_ctrl.read(),
57 Ns16550aRegister::LineCtrl => self.line_ctrl.read(),
58 Ns16550aRegister::ModemCtrl => self.modem_ctrl.read(),
59 Ns16550aRegister::LineStat => self.line_stat.read(),
60 Ns16550aRegister::ModemStat => self.modem_stat.read(),
61 }
62 }
63
64 fn write(&mut self, reg: Ns16550aRegister, val: u8) {
65 match reg {
66 Ns16550aRegister::DataOrDivisorLo => self.data.write(val),
67 Ns16550aRegister::IntEnOrDivisorHi => self.int_en.write(val),
68 Ns16550aRegister::FifoCtrl => self.fifo_ctrl.write(val),
69 Ns16550aRegister::LineCtrl => self.line_ctrl.write(val),
70 Ns16550aRegister::ModemCtrl => self.modem_ctrl.write(val),
71 Ns16550aRegister::LineStat => self.line_stat.write(val),
72 Ns16550aRegister::ModemStat => self.modem_stat.write(val),
73 }
74 }
75}
76
77pub(crate) fn init(early_cmdline: &EarlyCmdline) {
79 if !early_cmdline.has_early_console {
80 return;
81 }
82
83 SERIAL_PORT.call_once(|| {
85 let access = unsafe { SerialAccess::new(0x3F8) };
92 let mut serial = Ns16550aUart::new(access);
93 serial.init();
94 SpinLock::new(serial)
95 });
96}
97reserve_io_port_range!(0x3F8..0x400);