diff options
author | dzwdz | 2022-04-10 16:17:57 +0200 |
---|---|---|
committer | dzwdz | 2022-04-10 16:17:57 +0200 |
commit | ec7c6eff3c10565a57ba2b2c14b3f224d9216b76 (patch) | |
tree | 2c61068f72f40403b16863776eb0bd9eacaf1198 /src/kernel | |
parent | 1677ac032bb89f87456ac5668118f0091ae5ed9f (diff) |
kernel/i386: rewrite the serial driver to wait for IRQ
proper multiprocess support!
Diffstat (limited to 'src/kernel')
-rw-r--r-- | src/kernel/arch/i386/driver/serial.c | 45 | ||||
-rw-r--r-- | src/kernel/arch/i386/driver/serial.h | 6 | ||||
-rw-r--r-- | src/kernel/arch/i386/interrupts/isr.c | 2 | ||||
-rw-r--r-- | src/kernel/vfs/root.c | 19 |
4 files changed, 49 insertions, 23 deletions
diff --git a/src/kernel/arch/i386/driver/serial.c b/src/kernel/arch/i386/driver/serial.c index 14bb8fd..56a2f62 100644 --- a/src/kernel/arch/i386/driver/serial.c +++ b/src/kernel/arch/i386/driver/serial.c @@ -2,10 +2,17 @@ #include <kernel/arch/i386/interrupts/irq.h> #include <kernel/arch/i386/port_io.h> #include <kernel/panic.h> +#include <shared/mem.h> #include <stdint.h> + +#define BACKLOG_CAPACITY 64 +static volatile uint8_t backlog[BACKLOG_CAPACITY] = {}; +static volatile size_t backlog_size = 0; + static const int COM1 = 0x3f8; + static void serial_selftest(void) { char b = 0x69; port_out8(COM1 + 4, 0b00011110); // enable loopback mode @@ -29,29 +36,37 @@ void serial_init(void) { port_out8(COM1 + 4, 0b00001111); // enable everything in the MCR } -static void serial_putchar(char c) { - while ((port_in8(COM1 + 5) & 0x20) == 0); // wait for THRE - port_out8(COM1, c); + +bool serial_ready(void) { + return backlog_size > 0; } -bool serial_poll_read(char *c) { - if ((port_in8(COM1 + 5) & 0x01) == 0) // needs DR - return false; - *c = port_in8(COM1); - return true; +void serial_irq(void) { + if (backlog_size >= BACKLOG_CAPACITY) return; + backlog[backlog_size++] = port_in8(COM1); } size_t serial_read(char *buf, size_t len) { - irq_interrupt_flag(true); - for (size_t i = 0; i < len; i++) { - while (!serial_poll_read(&buf[i])) - asm("hlt" ::: "memory"); - } - irq_interrupt_flag(false); - // TODO root driver assumes no partial reads + // copied from ps2, maybe could be made into a shared function? TODO FIFO lib + if (backlog_size <= len) + len = backlog_size; + backlog_size -= len; /* guaranteed to never be < 0 */ + memcpy(buf, (void*)backlog, len); + + /* move rest of buffer back on partial reads */ + // TODO assumes that memcpy()ing into an overlapping buffer is fine, outside spec + if (backlog_size > 0) + memcpy((void*)backlog, (void*)backlog + len, backlog_size); + return len; } + +static void serial_putchar(char c) { + while ((port_in8(COM1 + 5) & 0x20) == 0); // wait for THRE + port_out8(COM1, c); +} + void serial_write(const char *buf, size_t len) { for (size_t i = 0; i < len; i++) serial_putchar(buf[i]); diff --git a/src/kernel/arch/i386/driver/serial.h b/src/kernel/arch/i386/driver/serial.h index be738d1..c2b93ce 100644 --- a/src/kernel/arch/i386/driver/serial.h +++ b/src/kernel/arch/i386/driver/serial.h @@ -3,7 +3,9 @@ #include <stddef.h> void serial_init(void); + +bool serial_ready(void); +void serial_irq(void); size_t serial_read(char *buf, size_t len); -void serial_write(const char *buf, size_t len); -bool serial_poll_read(char *c); +void serial_write(const char *buf, size_t len); diff --git a/src/kernel/arch/i386/interrupts/isr.c b/src/kernel/arch/i386/interrupts/isr.c index e3bb6d6..5bba43d 100644 --- a/src/kernel/arch/i386/interrupts/isr.c +++ b/src/kernel/arch/i386/interrupts/isr.c @@ -1,4 +1,5 @@ #include <kernel/arch/i386/driver/ps2.h> +#include <kernel/arch/i386/driver/serial.h> #include <kernel/arch/i386/interrupts/irq.h> #include <kernel/arch/i386/interrupts/isr.h> #include <kernel/arch/i386/port_io.h> @@ -25,6 +26,7 @@ void isr_stage3(int interrupt) { return; case 0x24: // COM1 irq + serial_irq(); irq_eoi(1); return; diff --git a/src/kernel/vfs/root.c b/src/kernel/vfs/root.c index 89c2b3e..b5f7a8a 100644 --- a/src/kernel/vfs/root.c +++ b/src/kernel/vfs/root.c @@ -97,12 +97,19 @@ static int handle(struct vfs_request *req, bool *ready) { return req->output.len; } case HANDLE_COM1: { - struct virt_iter iter; - virt_iter_new(&iter, req->output.buf, req->output.len, - req->caller->pages, true, false); - while (virt_iter_next(&iter)) - serial_read(iter.frag, iter.frag_len); - return iter.prior; + // yet another bit of code shared between serial and ps2. + // i really should think how i could unite both of those + if (!serial_ready()) { + *ready = false; + req->caller->state = PS_WAITS4IRQ; + req->caller->waits4irq.req = *req; + req->caller->waits4irq.ready = serial_ready; + return -1; + } + uint8_t buf[16]; + size_t len = serial_read(buf, sizeof buf); + virt_cpy_to(req->caller->pages, req->output.buf, buf, len); + return len; } case HANDLE_PS2: { if (!ps2_ready()) { |