summaryrefslogtreecommitdiff
path: root/src/drivers/uart.cc
diff options
context:
space:
mode:
authorRichard Qin <richard.qin.x@gmail.com>2026-01-01 15:04:47 +0800
committerRichard Qin <richard.qin.x@gmail.com>2026-01-01 15:04:47 +0800
commit2dde56422dcfb94affee2baffc8d240ad16ad4b2 (patch)
treeaeee8a0dad24ceaca22585f773411e386c8019be /src/drivers/uart.cc
parent0a6815b7143a8ffd0e09777b5fbd12098d8884c2 (diff)
downloadlume-2dde56422dcfb94affee2baffc8d240ad16ad4b2.tar.gz
lume-2dde56422dcfb94affee2baffc8d240ad16ad4b2.tar.xz
dev(device): implement VirtIO
See dev log for detail
Diffstat (limited to 'src/drivers/uart.cc')
-rw-r--r--src/drivers/uart.cc226
1 files changed, 165 insertions, 61 deletions
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 &reg(uint32_t offset)
+ // Circular buffer structure
+ struct
{
- return *reinterpret_cast<volatile uint8_t *>(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