use core::fmt::{self, Write}; use spin::Mutex; use io::{Io, Pio, ReadOnly}; pub static COM1: Mutex = Mutex::new(SerialPort::new(0x3F8)); pub static COM2: Mutex = Mutex::new(SerialPort::new(0x2F8)); pub unsafe fn init() { COM1.lock().init(); COM2.lock().init(); } bitflags! { /// Interrupt enable flags flags IntEnFlags: u8 { const RECEIVED = 1, const SENT = 1 << 1, const ERRORED = 1 << 2, const STATUS_CHANGE = 1 << 3, // 4 to 7 are unused } } bitflags! { /// Line status flags flags LineStsFlags: u8 { const INPUT_FULL = 1, // 1 to 4 unknown const OUTPUT_EMPTY = 1 << 5, // 6 and 7 unknown } } pub struct SerialPort { /// Data register, read to receive, write to send data: Pio, /// Interrupt enable int_en: Pio, /// FIFO control fifo_ctrl: Pio, /// Line control line_ctrl: Pio, /// Modem control modem_ctrl: Pio, /// Line status line_sts: ReadOnly>, /// Modem status modem_sts: ReadOnly>, } impl SerialPort { const fn new(base: u16) -> SerialPort { SerialPort { data: Pio::new(base), int_en: Pio::new(base + 1), fifo_ctrl: Pio::new(base + 2), line_ctrl: Pio::new(base + 3), modem_ctrl: Pio::new(base + 4), line_sts: ReadOnly::new(Pio::new(base + 5)), modem_sts: ReadOnly::new(Pio::new(base + 6)) } } fn int_en(&self) -> IntEnFlags { IntEnFlags::from_bits_truncate(self.int_en.read()) } fn line_sts(&self) -> LineStsFlags { LineStsFlags::from_bits_truncate(self.line_sts.read()) } fn write(&mut self, data: u8) { while ! self.line_sts().contains(OUTPUT_EMPTY) {} self.data.write(data) } fn init(&mut self) { self.int_en.write(0x00); self.line_ctrl.write(0x80); self.data.write(0x03); self.int_en.write(0x00); self.line_ctrl.write(0x03); self.fifo_ctrl.write(0xC7); self.modem_ctrl.write(0x0B); self.int_en.write(0x01); } pub fn on_receive(&mut self) { let data = self.data.read(); write!(self, "SERIAL {:X}\n", data); } } impl Write for SerialPort { fn write_str(&mut self, s: &str) -> Result<(), fmt::Error> { for byte in s.bytes() { self.write(byte); if byte == 8 { self.write(b' '); self.write(8); } } Ok(()) } } pub struct SerialConsole; impl Write for SerialConsole { fn write_str(&mut self, s: &str) -> Result<(), fmt::Error> { COM1.lock().write_str(s); Ok(()) } }