diff options
Diffstat (limited to 'src/kernel/arch')
-rw-r--r-- | src/kernel/arch/amd64/driver/ps2.c | 10 | ||||
-rw-r--r-- | src/kernel/arch/amd64/driver/serial.c | 19 | ||||
-rw-r--r-- | src/kernel/arch/amd64/driver/util.c | 22 | ||||
-rw-r--r-- | src/kernel/arch/amd64/driver/util.h | 5 |
4 files changed, 39 insertions, 17 deletions
diff --git a/src/kernel/arch/amd64/driver/ps2.c b/src/kernel/arch/amd64/driver/ps2.c index 196b890..51aabea 100644 --- a/src/kernel/arch/amd64/driver/ps2.c +++ b/src/kernel/arch/amd64/driver/ps2.c @@ -63,10 +63,10 @@ void ps2_irq(void) { if (!(status & 1)) break; /* read while data available */ if (status & (1 << 5)) { ring_put1b((void*)&mouse_backlog, port_in8(PS2)); - postqueue_pop(&mouse_queue, accept); + postqueue_ringreadall(&mouse_queue, (void*)&mouse_backlog); } else { ring_put1b((void*)&kb_backlog, port_in8(PS2)); - postqueue_pop(&kb_queue, accept); + postqueue_ringreadall(&kb_queue, (void*)&kb_backlog); } } } @@ -107,9 +107,11 @@ static void accept(struct vfs_request *req) { ret = req_readcopy(req, data, sizeof data); vfsreq_finish_short(req, ret); } else if ((long __force)req->id == H_KB) { - read_backlog(req, (void*)&kb_backlog, &kb_queue); + postqueue_join(&kb_queue, req); + postqueue_ringreadall(&kb_queue, (void*)&kb_backlog); } else if ((long __force)req->id == H_MOUSE) { - read_backlog(req, (void*)&mouse_backlog, &mouse_queue); + postqueue_join(&mouse_queue, req); + postqueue_ringreadall(&mouse_queue, (void*)&mouse_backlog); } else panic_invalid_state(); break; default: diff --git a/src/kernel/arch/amd64/driver/serial.c b/src/kernel/arch/amd64/driver/serial.c index 2b0812b..295f9b8 100644 --- a/src/kernel/arch/amd64/driver/serial.c +++ b/src/kernel/arch/amd64/driver/serial.c @@ -3,9 +3,10 @@ #include <kernel/arch/amd64/interrupts/irq.h> #include <kernel/arch/amd64/port_io.h> #include <kernel/mem/virt.h> -#include <kernel/ring.h> #include <kernel/panic.h> #include <kernel/proc.h> +#include <kernel/ring.h> +#include <kernel/util.h> #include <stdint.h> static volatile uint8_t backlog_buf[64]; @@ -14,7 +15,7 @@ static volatile ring_t backlog = {(void*)backlog_buf, sizeof backlog_buf, 0, 0}; static const int COM1 = 0x3f8; static void accept(struct vfs_request *req); -static struct vfs_request *blocked_on = NULL; +static struct vfs_request *hung_reads = NULL; void serial_init(void) { vfs_root_register("/com1", accept); } @@ -44,7 +45,7 @@ void serial_preinit(void) { void serial_irq(void) { ring_put1b((void*)&backlog, port_in8(COM1)); - postqueue_pop(&blocked_on, accept); + postqueue_ringreadall(&hung_reads, (void*)&backlog); } @@ -58,7 +59,6 @@ void serial_write(const char *buf, size_t len) { serial_putchar(buf[i]); } - static void accept(struct vfs_request *req) { int ret; bool valid; @@ -68,15 +68,8 @@ static void accept(struct vfs_request *req) { vfsreq_finish_short(req, valid ? 0 : -1); break; case VFSOP_READ: - if (ring_used((void*)&backlog) == 0) { - postqueue_join(&blocked_on, req); - } else if (req->caller) { - ret = ring_to_virt((void*)&backlog, req->caller->pages, req->output.buf, req->output.len); - // TODO output.len can overflow here - vfsreq_finish_short(req, ret); - } else { - vfsreq_finish_short(req, -1); - } + postqueue_join(&hung_reads, req); + postqueue_ringreadall(&hung_reads, (void*)&backlog); break; case VFSOP_WRITE: if (req->caller && !req->flags) { diff --git a/src/kernel/arch/amd64/driver/util.c b/src/kernel/arch/amd64/driver/util.c index 4eeaa0d..b8895b8 100644 --- a/src/kernel/arch/amd64/driver/util.c +++ b/src/kernel/arch/amd64/driver/util.c @@ -32,3 +32,25 @@ bool postqueue_pop(struct vfs_request **queue, void (*accept)(struct vfs_request accept(req); return true; } + +void postqueue_ringreadall(struct vfs_request **queue, ring_t *r) { + struct vfs_request *req; + char tmp[64]; + size_t mlen = 0; + if (ring_used(r) == 0) return; + + /* read as much as the biggest request wants */ + for (req = *queue; req; req = req->postqueue_next) + mlen = max(mlen, req->output.len); + mlen = min(mlen, sizeof tmp); + mlen = ring_get(r, tmp, mlen); + + for (req = *queue; req; req = req->postqueue_next) { + size_t ret = min(mlen, req->output.len); + assert(req->type == VFSOP_READ); + if (req->caller) + virt_cpy_to(req->caller->pages, req->output.buf, tmp, ret); + vfsreq_finish_short(req, ret); + } + *queue = NULL; +} diff --git a/src/kernel/arch/amd64/driver/util.h b/src/kernel/arch/amd64/driver/util.h index d89992a..06ca672 100644 --- a/src/kernel/arch/amd64/driver/util.h +++ b/src/kernel/arch/amd64/driver/util.h @@ -1,4 +1,5 @@ #pragma once +#include <shared/container/ring.h> #include <stdbool.h> #include <stddef.h> @@ -14,3 +15,7 @@ int req_readcopy(struct vfs_request *req, const void *buf, size_t len); void postqueue_join(struct vfs_request **queue, struct vfs_request *req); bool postqueue_pop(struct vfs_request **queue, void (*accept)(struct vfs_request *)); + +/** If there are any pending read requests, and the ring buffer isn't empty, fulfill them + * all with a single read. */ +void postqueue_ringreadall(struct vfs_request **queue, ring_t *r); |