///! Ref: https://www.lammertbies.nl/comm/info/serial-uart ///! Ref: ns16550a datasheet: https://datasheetspdf.com/pdf-file/605590/NationalSemiconductor/NS16550A/1 ///! Ref: ns16450 datasheet: https://datasheetspdf.com/pdf-file/1311818/NationalSemiconductor/NS16450/1 use super::CharDevice; use crate::sync::{Condvar, UPIntrFreeCell}; use crate::task::schedule; use alloc::collections::VecDeque; use bitflags::*; use volatile::{ReadOnly, Volatile, WriteOnly}; bitflags! { /// InterruptEnableRegister pub struct IER: u8 { const RX_AVAILABLE = 1 << 0; const TX_EMPTY = 1 << 1; } /// LineStatusRegister pub struct LSR: u8 { const DATA_AVAILABLE = 1 << 0; const THR_EMPTY = 1 << 5; } /// Model Control Register pub struct MCR: u8 { const DATA_TERMINAL_READY = 1 << 0; const REQUEST_TO_SEND = 1 << 1; const AUX_OUTPUT1 = 1 << 2; const AUX_OUTPUT2 = 1 << 3; } } #[repr(C)] #[allow(dead_code)] struct ReadWithoutDLAB { /// receiver buffer register pub rbr: ReadOnly, /// interrupt enable register pub ier: Volatile, /// interrupt identification register pub iir: ReadOnly, /// line control register pub lcr: Volatile, /// model control register pub mcr: Volatile, /// line status register pub lsr: ReadOnly, /// ignore MSR _padding1: ReadOnly, /// ignore SCR _padding2: ReadOnly, } #[repr(C)] #[allow(dead_code)] struct WriteWithoutDLAB { /// transmitter holding register pub thr: WriteOnly, /// interrupt enable register pub ier: Volatile, /// ignore FCR _padding0: ReadOnly, /// line control register pub lcr: Volatile, /// modem control register pub mcr: Volatile, /// line status register pub lsr: ReadOnly, /// ignore other registers _padding1: ReadOnly, } pub struct NS16550aRaw { base_addr: usize, } impl NS16550aRaw { fn read_end(&mut self) -> &mut ReadWithoutDLAB { unsafe { &mut *(self.base_addr as *mut ReadWithoutDLAB) } } fn write_end(&mut self) -> &mut WriteWithoutDLAB { unsafe { &mut *(self.base_addr as *mut WriteWithoutDLAB) } } pub fn new(base_addr: usize) -> Self { Self { base_addr } } pub fn init(&mut self) { let read_end = self.read_end(); let mut mcr = MCR::empty(); mcr |= MCR::DATA_TERMINAL_READY; mcr |= MCR::REQUEST_TO_SEND; mcr |= MCR::AUX_OUTPUT2; read_end.mcr.write(mcr); let ier = IER::RX_AVAILABLE; read_end.ier.write(ier); } pub fn read(&mut self) -> Option { let read_end = self.read_end(); let lsr = read_end.lsr.read(); if lsr.contains(LSR::DATA_AVAILABLE) { Some(read_end.rbr.read()) } else { None } } pub fn write(&mut self, ch: u8) { let write_end = self.write_end(); loop { if write_end.lsr.read().contains(LSR::THR_EMPTY) { write_end.thr.write(ch); break; } } } } struct NS16550aInner { ns16550a: NS16550aRaw, read_buffer: VecDeque, } pub struct NS16550a { inner: UPIntrFreeCell, condvar: Condvar, } impl NS16550a { pub fn new() -> Self { let mut inner = NS16550aInner { ns16550a: NS16550aRaw::new(BASE_ADDR), read_buffer: VecDeque::new(), }; inner.ns16550a.init(); Self { inner: unsafe { UPIntrFreeCell::new(inner) }, condvar: Condvar::new(), } } } impl CharDevice for NS16550a { fn read(&self) -> u8 { loop { let mut inner = self.inner.exclusive_access(); if let Some(ch) = inner.read_buffer.pop_front() { return ch; } else { let task_cx_ptr = self.condvar.wait_no_sched(); drop(inner); schedule(task_cx_ptr); } } } fn write(&self, ch: u8) { let mut inner = self.inner.exclusive_access(); inner.ns16550a.write(ch); } fn handle_irq(&self) { let mut count = 0; self.inner.exclusive_session(|inner| { while let Some(ch) = inner.ns16550a.read() { count += 1; inner.read_buffer.push_back(ch); } }); if count > 0 { self.condvar.signal(); } } }