From 2dde56422dcfb94affee2baffc8d240ad16ad4b2 Mon Sep 17 00:00:00 2001 From: Richard Qin Date: Thu, 1 Jan 2026 15:04:47 +0800 Subject: dev(device): implement VirtIO See dev log for detail --- src/drivers/uart.cc | 226 ++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 165 insertions(+), 61 deletions(-) (limited to 'src/drivers/uart.cc') diff --git a/src/drivers/uart.cc b/src/drivers/uart.cc index fe051f8..9475d49 100644 --- a/src/drivers/uart.cc +++ b/src/drivers/uart.cc @@ -1,102 +1,206 @@ #include "drivers/uart.h" -#include "common/types.h" -#include "kernel/config.h" - -// #define UART0_BASE 0x10000000L - -#define UART_RBR 0x00 // Receiver Buffer Register (Read) -#define UART_THR 0x00 // Transmitter Holding Register (Write) -#define UART_IER 0x01 // Interrupt Enable Register -#define UART_FCR 0x02 // FIFO Control Register -#define UART_LCR 0x03 // Line Control Register -#define UART_LSR 0x05 // Line Status Register - -#define UART_LSR_RX_READY 0x01 -#define UART_LSR_TX_EMPTY 0x20 +#include "kernel/riscv.h" +#include "kernel/spinlock.h" +#include "kernel/proc.h" +#include "kernel/mm.h" +#include "lib/string.h" namespace Drivers { - - inline volatile uint8_t ®(uint32_t offset) + // Circular buffer structure + struct { - return *reinterpret_cast(g_uart_base + offset); - } + Spinlock lock; + char buf[UART_RX_BUF_SIZE]; + uint32 r; // read index + uint32 w; // write index + } uart_rx; + + Spinlock uart_tx_lock; void uart_init() { - if (g_uart_base == 0) - return; + uart_rx.lock.init("uart_rx"); + uart_tx_lock.init("uart_tx"); + uart_rx.r = 0; + uart_rx.w = 0; - reg(UART_IER) = 0x00; // Disable interrupt - reg(UART_LCR) = 0x80; // Set baud rate (LATCH bit) - reg(0x00) = 0x03; // LSB - reg(0x01) = 0x00; // MSB - reg(UART_LCR) = 0x03; // 8 bits, no parity, one stop bit - reg(UART_FCR) = 0x07; // Enable FIFO, clear TX/RX queues - } + // Disable interrupts and configure + *(volatile uint8 *)(UART0 + IER) = 0x00; - void uart_putc(char c) - { - if (g_uart_base == 0) - return; + // Enter baud rate setting mode (LCR_DLAB) = 0x80; + *(volatile uint8 *)(UART0 + LCR) = 0x80; + *(volatile uint8 *)(UART0 + 0) = 0x03; // DLL: 38.4K baud + *(volatile uint8 *)(UART0 + 1) = 0x00; // DLM - if (c == '\n') - { - uart_putc('\r'); - } + // Exit baud rate setting, set 8-bit word length, no parity + *(volatile uint8 *)(UART0 + LCR) = 0x03; - // Waiting for the send buffer to be empty - while ((reg(UART_LSR) & UART_LSR_TX_EMPTY) == 0) - ; + // Reset and enable FIFO + *(volatile uint8 *)(UART0 + FCR) = 0x07; - reg(UART_THR) = c; + // Enable Receive Data Available Interrupt + *(volatile uint8 *)(UART0 + IER) = 0x01; } + void uart_putc(char c) + { + uart_tx_lock.acquire(); + while ((*(volatile uint8 *)(UART0 + LSR) & LSR_TX_IDLE) == 0) + ; + *(volatile uint8 *)(UART0 + THR) = c; + uart_tx_lock.release(); + } void uart_puts(const char *s) { while (*s) - { uart_putc(*s++); - } } - void uart_put_int(int value) + void uart_put_int(int val) { - if (value == 0) + char buf[16]; + int i = 0; + if (val == 0) { - uart_putc('0'); + uart_puts("0"); return; } - - if (value < 0) + if (val < 0) { - uart_putc('-'); - value = -value; + uart_puts("-"); + val = -val; } - - char buffer[16]; - int i = 0; - while (value > 0) + while (val > 0) { - buffer[i++] = (value % 10) + '0'; - value /= 10; + buf[i++] = (val % 10) + '0'; + val /= 10; } - while (i > 0) { - uart_putc(buffer[--i]); + uart_putc(buf[--i]); } } - void print_hex(uint64 value) + void print_hex(uint64 val) { + char hex[] = "0123456789ABCDEF"; uart_puts("0x"); - char hex[] = "0123456789abcdef"; - for (int i = 15; i >= 0; i--) + for (int i = 60; i >= 0; i -= 4) { - int nibble = (value >> (i * 4)) & 0xF; - uart_putc(hex[nibble]); + uart_putc(hex[(val >> i) & 0xF]); } } + + void uart_intr() + { + uart_rx.lock.acquire(); + + while (1) + { + if ((*(volatile uint8 *)(UART0 + LSR) & LSR_RX_READY) == 0) + break; + + char c = *(volatile uint8 *)(UART0 + RHR); + + uart_rx.buf[uart_rx.w % UART_RX_BUF_SIZE] = c; + uart_rx.w++; + + ProcManager::wakeup(&uart_rx); + } + + uart_rx.lock.release(); + } + + // Modern console read: Line buffering + Editing Keys + Echo + int console_read(uint64 target, int len) + { + struct Proc *p = myproc(); + int target_idx = 0; + + uart_rx.lock.acquire(); + + while (target_idx < len) + { + while (uart_rx.r == uart_rx.w) + { + if (p->xstate != 0) + { + uart_rx.lock.release(); + return -1; + } + ProcManager::sleep(&uart_rx, &uart_rx.lock); + } + + char c = uart_rx.buf[uart_rx.r % UART_RX_BUF_SIZE]; + uart_rx.r++; + + uart_rx.lock.release(); + + // 1. Handle Backspace (127 or \b) + if (c == 127 || c == '\b') + { + if (target_idx > 0) + { + // Erase character from screen: Backspace, Space, Backspace + uart_putc('\b'); + uart_putc(' '); + uart_putc('\b'); + target_idx--; + } + uart_rx.lock.acquire(); + continue; + } + + // 2. Handle Ctrl+U (Kill Line) + if (c == 0x15) + { + while (target_idx > 0) + { + uart_putc('\b'); + uart_putc(' '); + uart_putc('\b'); + target_idx--; + } + uart_rx.lock.acquire(); + continue; + } + + // 3. Handle Ctrl+L (Clear Screen) + if (c == 0x0C) + { + uart_puts(ANSI_CLEAR); + uart_puts("$ "); + uart_rx.lock.acquire(); + continue; + } + + // 4. Handle Return (CR -> LF) + if (c == '\r') + { + c = '\n'; + } + + // Echo back to user + uart_putc(c); + + if (VM::copyout(p->pagetable, target + target_idx, &c, 1) < 0) + { + return -1; + } + + target_idx++; + + if (c == '\n') + { + return target_idx; + } + + uart_rx.lock.acquire(); + } + + uart_rx.lock.release(); + return target_idx; + } } \ No newline at end of file -- cgit v1.2.3