//! Early Serial Driver (16550 UART on COM1 0x3F8) use core::fmt::{self, Write}; use core::sync::atomic::{AtomicBool, Ordering}; const COM1: u16 = 0x3F8; static SERIAL_INITIALIZED: AtomicBool = AtomicBool::new(false); #[inline] unsafe fn outb(port: u16, val: u8) { core::arch::asm!("out dx, al", in("dx") port, in("al") val, options(nomem, nostack, preserves_flags)); } #[inline] unsafe fn inb(port: u16) -> u8 { let mut val: u8; core::arch::asm!("in al, dx", in("dx") port, out("al") val, options(nomem, nostack, preserves_flags)); val } pub struct SerialPort; impl SerialPort { pub fn init() { unsafe { outb(COM1 + 1, 0x00); // Disable all interrupts outb(COM1 + 3, 0x80); // Enable DLAB (set baud rate divisor) outb(COM1 + 0, 0x01); // Set divisor to 1 (lo byte) 115200 baud outb(COM1 + 1, 0x00); // (hi byte) outb(COM1 + 3, 0x03); // 8 bits, no parity, one stop bit outb(COM1 + 2, 0xC7); // Enable FIFO, clear them, with 14-byte threshold outb(COM1 + 4, 0x0B); // IRQs enabled, RTS/DSR set } SERIAL_INITIALIZED.store(true, Ordering::Release); } pub fn write_byte(&self, byte: u8) { unsafe { // Wait for transmit buffer empty while (inb(COM1 + 5) & 0x20) == 0 { core::hint::spin_loop(); } outb(COM1, byte); } } pub fn write_str(&self, s: &str) { for b in s.bytes() { if b == b'\n' { self.write_byte(b'\r'); } self.write_byte(b); } } } impl Write for SerialPort { fn write_str(&mut self, s: &str) -> fmt::Result { SerialPort.write_str(s); Ok(()) } } pub fn _kprint(args: fmt::Arguments) { let mut port = SerialPort; let _ = port.write_fmt(args); } #[macro_export] macro_rules! kprint { ($($arg:tt)*) => { $crate::drivers::serial::_kprint(format_args!($($arg)*)) }; } #[macro_export] macro_rules! kprintln { () => ($crate::kprint!("\n")); ($($arg:tt)*) => ({ $crate::kprint!("{}\n", format_args!($($arg)*)); }); }