use spinlock::SpinNoIrq;
use x86_64::instructions::port::{Port, PortReadOnly, PortWriteOnly};
const UART_CLOCK_FACTOR: usize = 16;
const OSC_FREQ: usize = 1_843_200;
static COM1: SpinNoIrq<Uart16550> = SpinNoIrq::new(Uart16550::new(0x3f8));
bitflags::bitflags! {
struct LineStsFlags: u8 {
const INPUT_FULL = 1;
const OUTPUT_EMPTY = 1 << 5;
}
}
struct Uart16550 {
data: Port<u8>,
int_en: PortWriteOnly<u8>,
fifo_ctrl: PortWriteOnly<u8>,
line_ctrl: PortWriteOnly<u8>,
modem_ctrl: PortWriteOnly<u8>,
line_sts: PortReadOnly<u8>,
}
impl Uart16550 {
const fn new(port: u16) -> Self {
Self {
data: Port::new(port),
int_en: PortWriteOnly::new(port + 1),
fifo_ctrl: PortWriteOnly::new(port + 2),
line_ctrl: PortWriteOnly::new(port + 3),
modem_ctrl: PortWriteOnly::new(port + 4),
line_sts: PortReadOnly::new(port + 5),
}
}
fn init(&mut self, baud_rate: usize) {
unsafe {
self.int_en.write(0x00);
self.line_ctrl.write(0x80);
let divisor = OSC_FREQ / (baud_rate * UART_CLOCK_FACTOR);
self.data.write((divisor & 0xff) as u8);
self.int_en.write((divisor >> 8) as u8);
self.line_ctrl.write(0x03);
self.fifo_ctrl.write(0xC7);
self.modem_ctrl.write(0x0B);
}
}
fn line_sts(&mut self) -> LineStsFlags {
unsafe { LineStsFlags::from_bits_truncate(self.line_sts.read()) }
}
fn putchar(&mut self, c: u8) {
while !self.line_sts().contains(LineStsFlags::OUTPUT_EMPTY) {}
unsafe { self.data.write(c) };
}
fn getchar(&mut self) -> Option<u8> {
if self.line_sts().contains(LineStsFlags::INPUT_FULL) {
unsafe { Some(self.data.read()) }
} else {
None
}
}
}
pub fn putchar(c: u8) {
let mut uart = COM1.lock();
match c {
b'\n' => {
uart.putchar(b'\r');
uart.putchar(b'\n');
}
c => uart.putchar(c),
}
}
pub fn getchar() -> Option<u8> {
COM1.lock().getchar()
}
pub fn init_early() {
COM1.lock().init(115200);
}