tg-rcore-tutorial-uart1 0.1.0-preview.1

NS16550A UART driver for S-Mode in rCore tutorial.
Documentation
//! NS16550A 串口驱动实现
//!
//! 提供寄存器定义、硬件初始化和字符输入输出操作。
//! 详细说明请参考 doc/note&sum/uart-driver-summary.md

/// 串口驱动错误类型
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum UartError {
    /// 初始化失败
    InitFailed,
    /// 发送超时
    TxTimeout,
    /// 接收超时
    RxTimeout,
}

/// 串口驱动结果别名
pub type Result<T> = core::result::Result<T, UartError>;

/// QEMU virt 机器 UART0 基地址
const UART0: usize = 0x1000_0000;

/// 寄存器偏移量(某些寄存器读写时有不同含义)
const RHR: usize = 0; // 接收保持寄存器(只读)
const THR: usize = 0; // 发送保持寄存器(只写)
const IER: usize = 1; // 中断使能寄存器
const FCR: usize = 2; // FIFO 控制寄存器(只写)
#[allow(dead_code)]
const ISR: usize = 2; // 中断状态寄存器(只读)
const LCR: usize = 3; // 线路控制寄存器
const LSR: usize = 5; // 线路状态寄存器

/// 中断使能寄存器(IER)标志位
mod ier {
    #[allow(dead_code)]
    pub const RX_ENABLE: u8 = 1 << 0;
    #[allow(dead_code)]
    pub const TX_ENABLE: u8 = 1 << 1;
}

/// FIFO 控制寄存器(FCR)标志位
mod fcr {
    pub const FIFO_ENABLE: u8 = 1 << 0;
    pub const RX_FIFO_CLEAR: u8 = 1 << 1;
    pub const TX_FIFO_CLEAR: u8 = 1 << 2;
    pub const FIFO_CLEAR: u8 = RX_FIFO_CLEAR | TX_FIFO_CLEAR;
}

/// 线路控制寄存器(LCR)标志位
mod lcr {
    pub const EIGHT_BITS: u8 = 3 << 0;
    pub const BAUD_LATCH: u8 = 1 << 7;
}

/// 线路状态寄存器(LSR)标志位
mod lsr {
    pub const RX_READY: u8 = 1 << 0;
    pub const TX_IDLE: u8 = 1 << 5;
}

/// UART 驱动结构体
#[derive(Debug, Clone, Copy)]
pub struct Uart;

impl Uart {
    /// 创建一个新的 UART 实例
    #[inline]
    pub const fn new() -> Self {
        Self
    }

    /// 初始化 UART 硬件
    ///
    /// 配置波特率 38.4K,8 数据位,启用 FIFO,禁用中断。
    /// 返回 `Ok(())` 表示成功,`Err(UartError::InitFailed)` 表示失败。
    pub fn init() -> Result<()> {
        unsafe { write_reg(IER, 0) };
        unsafe { write_reg(LCR, lcr::BAUD_LATCH) };
        unsafe { write_reg(0, 0x03) };
        unsafe { write_reg(1, 0x00) };
        unsafe { write_reg(LCR, lcr::EIGHT_BITS) };
        unsafe { write_reg(FCR, fcr::FIFO_ENABLE | fcr::FIFO_CLEAR) };
        Ok(())
    }

    /// 同步输出字符,用于 panic 处理
    ///
    /// 参考 xv6 的 `uartputc_sync` 实现,轮询发送空闲并带有超时。
    /// 在系统崩溃时确保输出,避免无限等待。
    #[inline(never)]
    pub fn put_char_sync(c: u8) -> Result<()> {
        let mut timeout = 100_000;
        while unsafe { read_reg(LSR) } & lsr::TX_IDLE == 0 {
            timeout -= 1;
            if timeout == 0 {
                return Err(UartError::TxTimeout);
            }
        }
        unsafe { write_reg(THR, c) };
        Ok(())
    }

    /// 向 UART 输出单个字符
    ///
    /// 阻塞直到发送器准备就绪,轮询 LSR 寄存器直到 TX_IDLE 标志置位。
    #[inline]
    pub fn put_char(c: u8) {
        while unsafe { read_reg(LSR) } & lsr::TX_IDLE == 0 {}
        unsafe { write_reg(THR, c) };
    }

    /// 向 UART 输出字符串
    #[inline]
    pub fn put_str(s: &str) {
        for c in s.bytes() {
            Self::put_char(c);
        }
    }

    /// 尝试从 UART 读取字符
    ///
    /// 如果有字符可用则返回 `Some(c)`,否则返回 `None`。
    #[inline]
    pub fn try_get_char() -> Option<u8> {
        if unsafe { read_reg(LSR) } & lsr::RX_READY != 0 {
            Some(unsafe { read_reg(RHR) })
        } else {
            None
        }
    }
}

/// 从 UART 寄存器读取一个字节
#[inline]
unsafe fn read_reg(offset: usize) -> u8 {
    let addr = UART0 + offset;
    unsafe { (addr as *const u8).read_volatile() }
}

/// 向 UART 寄存器写入一个字节
#[inline]
unsafe fn write_reg(offset: usize, value: u8) {
    let addr = UART0 + offset;
    unsafe { (addr as *mut u8).write_volatile(value) }
}