summaryrefslogtreecommitdiff
path: root/src/kernel
diff options
context:
space:
mode:
authordzwdz2022-04-10 16:17:57 +0200
committerdzwdz2022-04-10 16:17:57 +0200
commitec7c6eff3c10565a57ba2b2c14b3f224d9216b76 (patch)
tree2c61068f72f40403b16863776eb0bd9eacaf1198 /src/kernel
parent1677ac032bb89f87456ac5668118f0091ae5ed9f (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.c45
-rw-r--r--src/kernel/arch/i386/driver/serial.h6
-rw-r--r--src/kernel/arch/i386/interrupts/isr.c2
-rw-r--r--src/kernel/vfs/root.c19
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()) {