diff options
| author | Richard Qin <richard.qin.x@gmail.com> | 2026-11-10 01:16:40 +0800 |
|---|---|---|
| committer | Richard Qin <richard.qin.x@gmail.com> | 2026-11-10 01:16:40 +0800 |
| commit | 4a9624da0c7cf0c27a00e1ff497d13f2e4a0d03e (patch) | |
| tree | 91b95e736fa08a071e7814872fc822c2d3fcd682 /mubios/drivers/serial.c | |
| parent | 9f45c30789b6133d4b5fb4ef07f0818a3ea4de48 (diff) | |
| download | rcc-project-4a9624da0c7cf0c27a00e1ff497d13f2e4a0d03e.tar.gz rcc-project-4a9624da0c7cf0c27a00e1ff497d13f2e4a0d03e.tar.xz | |
[mubios] Implement serial input and BIOS console key handling
Implement UART receive, transmit timeouts, configuration and status reporting for INT 14h functions 00h-03h. Initialize the BDA serial timeout and expose the driver interfaces.
Translate terminal characters and CSI/SS3 escape sequences into BIOS keyboard events for INT 16h. Add blocking reads, non-consuming status queries, enhanced key reads and keyboard buffer insertion.
Diffstat (limited to 'mubios/drivers/serial.c')
| -rw-r--r-- | mubios/drivers/serial.c | 113 |
1 files changed, 113 insertions, 0 deletions
diff --git a/mubios/drivers/serial.c b/mubios/drivers/serial.c index 3da2093..c8b9adf 100644 --- a/mubios/drivers/serial.c +++ b/mubios/drivers/serial.c @@ -30,6 +30,7 @@ #define COM_LCR_OFFSET 0x03u #define COM_MCR_OFFSET 0x04u #define COM_LSR_OFFSET 0x05u +#define COM_MSR_OFFSET 0x06u #define COM_LCR_DLAB 0x80u #define COM_LCR_8N1 0x03u @@ -44,6 +45,12 @@ #define COM_MCR_OUT2 0x08u #define COM_LSR_THRE 0x20u +#define COM_LSR_IDLE 0x40u +#define COM_LSR_DATA_READY 0x01u +#define BIOS_SERIAL_TIMEOUT 0x80u + +#define PIT_COUNTER0 0x0040u +#define PIT_COMMAND 0x0043u static u16 com1_port (u16 offset) @@ -51,6 +58,112 @@ com1_port (u16 offset) return (u16)(COM1_BASE + offset); } +/* Latching channel 0 works while the protected-mode service has IRQs off. */ +u16 +serial_timer_counter (void) +{ + u16 counter; + + io_out8 (PIT_COMMAND, 0x00u); + counter = io_in8 (PIT_COUNTER0); + counter |= (u16)((u16)io_in8 (PIT_COUNTER0) << 8); + return counter; +} + +static int +serial_wait (u16 base, u8 mask, u8 timeout, u8 *status) +{ + u16 previous; + u16 current; + u32 elapsed; + u32 duration; + + previous = serial_timer_counter (); + elapsed = 0; + /* One BDA timeout unit is one 65536-clock BIOS timer tick. */ + duration = (u32)timeout << 16; + + for (;;) + { + *status = io_in8 ((u16)(base + COM_LSR_OFFSET)); + if ((*status & mask) == mask) + return 1; + + if (elapsed >= duration) + { + *status |= BIOS_SERIAL_TIMEOUT; + return 0; + } + + current = serial_timer_counter (); + elapsed += (u16)(previous - current); + previous = current; + } +} + +u16 +serial_status (u16 base) +{ + u8 line; + u8 modem; + + line = io_in8 ((u16)(base + COM_LSR_OFFSET)); + modem = io_in8 ((u16)(base + COM_MSR_OFFSET)); + return (u16)(((u16)line << 8) | modem); +} + +u16 +serial_configure (u16 base, u8 parameters) +{ + static const u16 divisors[] + = { 1047u, 768u, 384u, 192u, 96u, 48u, 24u, 12u }; + u16 divisor; + + /* INT 14h AL: bits 7:5 select baud; bits 4:0 are the UART format. */ + divisor = divisors[parameters >> 5]; + io_out8 ((u16)(base + COM_LCR_OFFSET), COM_LCR_DLAB); + io_out8 ((u16)(base + COM_DATA_OFFSET), (u8)divisor); + io_out8 ((u16)(base + COM_IER_OFFSET), (u8)(divisor >> 8)); + io_out8 ((u16)(base + COM_LCR_OFFSET), parameters & 0x1Fu); + /* Do not reset the FIFO here: pending terminal input must survive. */ + return serial_status (base); +} + +u8 +serial_transmit (u16 base, u8 value, u8 timeout) +{ + u8 status; + + if (serial_wait (base, COM_LSR_THRE | COM_LSR_IDLE, timeout, &status)) + io_out8 ((u16)(base + COM_DATA_OFFSET), value); + + return status; +} + +u8 +serial_receive (u16 base, u8 *value, u8 timeout) +{ + u8 status; + + if (serial_wait (base, COM_LSR_DATA_READY, timeout, &status)) + *value = io_in8 ((u16)(base + COM_DATA_OFFSET)); + + return status; +} + +int +serial_try_receive (u16 base, u8 *value) +{ + u8 status; + + status = io_in8 ((u16)(base + COM_LSR_OFFSET)); + if ((status & COM_LSR_DATA_READY) == 0u) + return 0; + + *value = io_in8 ((u16)(base + COM_DATA_OFFSET)); + return 1; +} + void serial_init (void) { |
