diff options
Diffstat (limited to 'lume-legacy/src/drivers/uart.cc')
| -rw-r--r-- | lume-legacy/src/drivers/uart.cc | 204 |
1 files changed, 204 insertions, 0 deletions
diff --git a/lume-legacy/src/drivers/uart.cc b/lume-legacy/src/drivers/uart.cc new file mode 100644 index 0000000..397a402 --- /dev/null +++ b/lume-legacy/src/drivers/uart.cc @@ -0,0 +1,204 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2026 Richard QIn + */ +#include "drivers/uart.h" +#include "kernel/riscv.h" +#include "kernel/spinlock.h" +#include "kernel/proc.h" +#include "kernel/mm.h" +#include "lib/string.h" + +namespace Drivers +{ + struct + { + Spinlock lock; + char buf[128]; // Line buffer + uint32 r; // Read pointer (position taken by the user) + uint32 w; // Write pointer (position confirmed by Enter) + uint32 e; // Editing cursor (current typing position) + } cons; + + // Circular buffer structure + struct + { + 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() + { + uart_rx.lock.init("uart_rx"); + cons.lock.init("console"); + uart_tx_lock.init("uart_tx"); + uart_rx.r = 0; + uart_rx.w = 0; + + // Disable interrupts and configure + *(volatile uint8 *)(uart_base() + IER) = 0x00; + + // Enter baud rate setting mode (LCR_DLAB) = 0x80; + *(volatile uint8 *)(uart_base() + LCR) = 0x80; + *(volatile uint8 *)(uart_base() + 0) = 0x03; // DLL: 38.4K baud + *(volatile uint8 *)(uart_base() + 1) = 0x00; // DLM + + // Exit baud rate setting, set 8-bit word length, no parity + *(volatile uint8 *)(uart_base() + LCR) = 0x03; + + // Reset and enable FIFO + *(volatile uint8 *)(uart_base() + FCR) = 0x07; + + // Enable Receive Data Available Interrupt + *(volatile uint8 *)(uart_base() + IER) = 0x01; + } + + void uart_putc(char c) + { + uart_tx_lock.acquire(); + while ((*(volatile uint8 *)(uart_base() + LSR) & LSR_TX_IDLE) == 0) + ; + *(volatile uint8 *)(uart_base() + THR) = c; + uart_tx_lock.release(); + } + + void uart_puts(const char *s) + { + while (*s) + uart_putc(*s++); + } + + void uart_put_int(int val) + { + char buf[16]; + int i = 0; + if (val == 0) + { + uart_puts("0"); + return; + } + if (val < 0) + { + uart_puts("-"); + val = -val; + } + while (val > 0) + { + buf[i++] = (val % 10) + '0'; + val /= 10; + } + while (i > 0) + { + uart_putc(buf[--i]); + } + } + + void print_hex(uint64 val) + { + char hex[] = "0123456789ABCDEF"; + uart_puts("0x"); + for (int i = 60; i >= 0; i -= 4) + { + uart_putc(hex[(val >> i) & 0xF]); + } + } + + void uart_intr() + { + uart_rx.lock.acquire(); + + while (1) + { + if ((*(volatile uint8 *)(uart_base() + LSR) & LSR_RX_READY) == 0) + break; + + char c = *(volatile uint8 *)(uart_base() + 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; + + cons.lock.acquire(); + + while (target_idx < len) + { + while (cons.r < cons.w) + { + char c = cons.buf[cons.r++ % sizeof(cons.buf)]; + cons.lock.release(); + + if (VM::copyout(p->pagetable, target + target_idx, &c, 1) < 0) + return -1; + + target_idx++; + if (c == '\n') + return target_idx; + if (target_idx == len) + return target_idx; + + cons.lock.acquire(); + } + + uart_rx.lock.acquire(); + while (uart_rx.r == uart_rx.w) + { + if (p->xstate) + { + uart_rx.lock.release(); + cons.lock.release(); + return -1; + } + cons.lock.release(); + ProcManager::sleep(&uart_rx, &uart_rx.lock); + cons.lock.acquire(); + } + + char c = uart_rx.buf[uart_rx.r++ % UART_RX_BUF_SIZE]; + uart_rx.lock.release(); + + if (c == 127 || c == '\b') + { + if (cons.e != cons.w) + { + cons.e--; + uart_putc('\b'); + uart_putc(' '); + uart_putc('\b'); + } + } + else + { + if (c == '\r') + c = '\n'; + + uart_putc(c); + + cons.buf[cons.e++ % sizeof(cons.buf)] = c; + + if (c == '\n' || cons.e == cons.w + sizeof(cons.buf)) + { + cons.w = cons.e; + ProcManager::wakeup(&cons); + } + } + } + + cons.lock.release(); + return target_idx; + } +}
\ No newline at end of file |
