1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
/* Copyright (c) [2023] [Syswonder Community]
* [Rukos] is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
* http://license.coscl.org.cn/MulanPSL2
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/
//! Uart 16550.
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! {
/// Line status flags
struct LineStsFlags: u8 {
const INPUT_FULL = 1;
// 1 to 4 unknown
const OUTPUT_EMPTY = 1 << 5;
// 6 and 7 unknown
}
}
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 {
// Disable interrupts
self.int_en.write(0x00);
// Enable DLAB
self.line_ctrl.write(0x80);
// Set maximum speed according the input baud rate by configuring DLL and DLM
let divisor = OSC_FREQ / (baud_rate * UART_CLOCK_FACTOR);
self.data.write((divisor & 0xff) as u8);
self.int_en.write((divisor >> 8) as u8);
// Disable DLAB and set data word length to 8 bits
self.line_ctrl.write(0x03);
// Enable FIFO, clear TX/RX queues and
// set interrupt watermark at 14 bytes
self.fifo_ctrl.write(0xC7);
// Mark data terminal ready, signal request to send
// and enable auxilliary output #2 (used as interrupt line for CPU)
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
}
}
}
/// Writes a byte to the console.
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),
}
}
/// Reads a byte from the console, or returns [`None`] if no input is available.
pub fn getchar() -> Option<u8> {
COM1.lock().getchar()
}
pub(super) fn init() {
COM1.lock().init(115200);
}