//! Low-level driver routines for 16550a UART. #![allow(non_upper_case_globals)] use crate::{ console::consoleintr, proc::wakeup, queue::Queue, sync::mutex::Mutex, trap::InterruptBlocker, }; use core::ptr::addr_of; // The UART control registers. // Some have different meanings for read vs write. // See http://byterunner.com/16550.html /// Interrupt Enable Register const IER_RX_ENABLE: u8 = 1 << 0; const IER_TX_ENABLE: u8 = 1 << 1; const FCR_FIFO_ENABLE: u8 = 1 << 0; /// Clear the content of the two FIFOs. const FCR_FIFO_CLEAR: u8 = 3 << 1; const LCR_EIGHT_BITS: u8 = 3; /// Special mode to set baud rate const LCR_BAUD_LATCH: u8 = 1 << 7; /// Input is waiting to be read from RHR const LSR_RX_READY: u8 = 1 << 0; /// THR can accept another character to send const LSR_TX_IDLE: u8 = 1 << 5; pub static UART0: Uart = Uart::new(crate::arch::riscv::memlayout::UART0); enum Register { ReceiveHolding, TransmitHolding, InterruptEnable, FIFOControl, InterruptStatus, LineControl, LineStatus, } impl Register { pub fn as_offset(&self) -> usize { match self { Register::ReceiveHolding => 0, Register::TransmitHolding => 0, Register::InterruptEnable => 1, Register::FIFOControl => 2, Register::InterruptStatus => 2, Register::LineControl => 2, Register::LineStatus => 5, } } pub fn as_ptr(&self, base_address: usize) -> *mut u8 { (base_address + self.as_offset()) as *mut u8 } pub fn read(&self, base_address: usize) -> u8 { unsafe { self.as_ptr(base_address).read_volatile() } } pub fn write(&self, base_address: usize, value: u8) { unsafe { self.as_ptr(base_address).write_volatile(value) } } } pub struct Uart { pub base_address: usize, pub buffer: Mutex>, } impl Uart { pub const fn new(base_address: usize) -> Uart { Uart { base_address, buffer: Mutex::new(Queue::new()), } } /// Initialize the UART. pub unsafe fn initialize(&self) { // Disable interrupts. Register::InterruptEnable.write(self.base_address, 0x00); // Special mode to set baud rate. Register::LineControl.write(self.base_address, LCR_BAUD_LATCH); // LSB for baud rate of 38.4K. *(self.base_address as *mut u8) = 0x03; // MSB for baud rate of 38.4K. *((self.base_address + 1) as *mut u8) = 0x00; // Leave set-baud mode and set // word length to 8 bits, no parity. Register::LineControl.write(self.base_address, LCR_EIGHT_BITS); // Reset and enable FIFOs. Register::FIFOControl.write(self.base_address, FCR_FIFO_ENABLE | FCR_FIFO_CLEAR); // Enable transmit and receive interrupts. Register::InterruptEnable.write(self.base_address, IER_TX_ENABLE | IER_RX_ENABLE); } pub fn interrupt(&self) { // Read and process incoming data. while let Some(b) = self.read_byte() { consoleintr(b); } // Send buffered characters. self.send_buffered_bytes(); } /// Read one byte from the UART. pub fn read_byte(&self) -> Option { if Register::LineStatus.read(self.base_address) & 0x01 != 0 { // Input data is ready. Some(Register::ReceiveHolding.read(self.base_address)) } else { None } } /// Write a byte to the UART without interrupts. /// Used for kernel printing and character echoing. pub fn write_byte(&self, b: u8) { let _ = InterruptBlocker::new(); if *crate::PANICKED.lock_spinning() { loop { core::hint::spin_loop(); } } // Wait for Transmit Holding Empty to be set in LSR. while Register::LineStatus.read(self.base_address) & LSR_TX_IDLE == 0 { core::hint::spin_loop(); } Register::TransmitHolding.write(self.base_address, b); } pub fn write_slice(&self, bytes: &[u8]) { for b in bytes { self.write_byte(*b); } } /// Write a byte to the UART and buffer it. /// Should not be used in interrupts. pub fn write_byte_buffered(&self, b: u8) { let mut buf = self.buffer.lock_spinning(); if *crate::PANICKED.lock_spinning() { loop { core::hint::spin_loop(); } } // Sleep until there is space in the buffer. while buf.space_remaining() == 0 { unsafe { buf.sleep(addr_of!(*self).cast_mut().cast()); } } // Add the byte onto the end of the queue. buf.push_back(b).expect("space in the uart queue"); // Drop buf so that send_buffered_bytes() can lock it again. core::mem::drop(buf); self.send_buffered_bytes(); } pub fn write_slice_buffered(&self, bytes: &[u8]) { for b in bytes { self.write_byte_buffered(*b); } } /// If the UART is idle, and a character is /// waiting in the transmit buffer, send it. /// self.lock should be held. fn send_buffered_bytes(&self) { let mut buf = self.buffer.lock_spinning(); loop { if Register::LineStatus.read(self.base_address) & LSR_TX_IDLE == 0 { // The UART transmit holding register is full, // so we cannot give it another byte. // It will interrupt when it's ready for a new byte. return; } // Pop a byte from the front of the queue. let Some(b) = buf.pop_front() else { // The buffer is empty, we're finished sending bytes. return; }; // Maybe uartputc() is waiting for space in the buffer. unsafe { wakeup(addr_of!(*self).cast_mut().cast()); } Register::TransmitHolding.write(self.base_address, b); } } }