summaryrefslogtreecommitdiff
path: root/src/drivers/uart.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/drivers/uart.cc')
-rw-r--r--src/drivers/uart.cc221
1 files changed, 175 insertions, 46 deletions
diff --git a/src/drivers/uart.cc b/src/drivers/uart.cc
index 367f442..4064b16 100644
--- a/src/drivers/uart.cc
+++ b/src/drivers/uart.cc
@@ -1,77 +1,206 @@
#include "drivers/uart.h"
-#include "common/types.h"
-#include "kernel/config.h"
+#include "kernel/riscv.h"
+#include "kernel/spinlock.h"
+#include "kernel/proc.h"
+#include "kernel/mm.h"
+#include "lib/string.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
-
-namespace Drivers{
- inline volatile uint8_t &reg(uint32_t offset)
+namespace Drivers
+{
+ // 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;
+
+ // Disable interrupts and configure
+ *(volatile uint8 *)(UART0 + IER) = 0x00;
- reg(UART_IER) = 0x00;
+ // 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
- reg(UART_LCR) = 0x80;
- reg(0x00) = 0x03;
- reg(0x01) = 0x00;
+ // Exit baud rate setting, set 8-bit word length, no parity
+ *(volatile uint8 *)(UART0 + LCR) = 0x03;
- reg(UART_LCR) = 0x03;
+ // Reset and enable FIFO
+ *(volatile uint8 *)(UART0 + FCR) = 0x07;
- reg(UART_FCR) = 0x07; // Enable FIFO, clear TX/RX queues
+ // Enable Receive Data Available Interrupt
+ *(volatile uint8 *)(UART0 + IER) = 0x01;
}
void uart_putc(char c)
{
- if (c == '\n')
+ 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 val)
+ {
+ char buf[16];
+ int i = 0;
+ if (val == 0)
{
- uart_putc('\r');
+ uart_puts("0");
+ return;
}
-
- while ((reg(UART_LSR) & UART_LSR_TX_EMPTY) == 0)
+ if (val < 0)
{
-
+ uart_puts("-");
+ val = -val;
}
+ while (val > 0)
+ {
+ buf[i++] = (val % 10) + '0';
+ val /= 10;
+ }
+ while (i > 0)
+ {
+ uart_putc(buf[--i]);
+ }
+ }
- reg(UART_THR) = c;
+ 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_puts(const char *s)
+ void uart_intr()
{
- while (*s)
+ uart_rx.lock.acquire();
+
+ while (1)
{
- uart_putc(*s++);
+ 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();
}
- void print_hex(uint64_t val)
+ // Modern console read: Line buffering + Editing Keys + Echo
+ int console_read(uint64 target, int len)
{
- char buf[20];
- const char *digits = "0123456789ABCDEF";
- buf[0] = '0';
- buf[1] = 'x';
- for (int i = 0; i < 16; i++)
+ struct Proc *p = myproc();
+ int target_idx = 0;
+
+ uart_rx.lock.acquire();
+
+ while (target_idx < len)
{
- buf[17 - i] = digits[val & 0xF];
- val >>= 4;
+ 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();
}
- buf[18] = 0;
- Drivers::uart_puts(buf);
+
+ uart_rx.lock.release();
+ return target_idx;
}
} \ No newline at end of file