Orion
Barry Keyboard/Mouse drivers + POSIX names for structs 1628fcf (3 years, 2 months ago)
diff --git a/drivers/keyboard/keyboard.c b/drivers/keyboard/keyboard.c
new file mode 100644
index 0000000..5ef3c6f
--- /dev/null
+++ b/drivers/keyboard/keyboard.c
@@ -0,0 +1,108 @@
+/*
+ * This file implements a keyboard driver. It generates the /dev/kbd device
+ * file, which can be read and returns a structure of the keyboard data. This
+ * driver produces raw keyboard data, not parsed text.
+ */
+
+#include <sys/kbd.h>
+#include "../drivers.h"
+#include "../../task/task.h"
+#include "../../proc/proc.h"
+#include "../../io.h"
+
+void tty_keypress(unsigned char key);
+size_t kbd_read(File *file, char *buf, size_t size, off_t offset);
+
+FileOps kbdFileOps = {
+ .read = kbd_read,
+};
+
+Driver kbdDriver = {
+ .major = 10,
+ .ops = &kbdFileOps,
+ .next = NULL,
+};
+
+TaskQueue keyWait;
+
+/* Ring buffer for keys */
+struct KeyBuffer {
+ int start, end;
+ char key[1024];
+} keyBuf;
+
+/* Add a key to the key buffer */
+static void
+add_to_buf(char key)
+{
+ /* No more space in buffer */
+ int start = (keyBuf.start + sizeof(keyBuf.key) - 1)
+ % sizeof(keyBuf.key);
+ if (keyBuf.end == start)
+ return;
+
+ keyBuf.key[keyBuf.end] = key;
+ keyBuf.end++;
+ keyBuf.end %= sizeof(keyBuf.key);
+}
+
+/* Get a key from the key buffer */
+static char
+get_from_buf(void)
+{
+ /* Nothing to read */
+ if (keyBuf.start == keyBuf.end)
+ return 0x00;
+
+ char key = keyBuf.key[keyBuf.start];
+ keyBuf.start++;
+ keyBuf.start %= sizeof(keyBuf.key);
+ return key;
+}
+
+/* Handle the keyboard interrupt */
+static void
+keyboard_handler(InterruptFrame *frame)
+{
+ unsigned char key;
+ int i;
+ for (i = 0; i < 1000; i++) {
+ if ((inb(0x64) & 1) == 0)
+ continue;
+ key = inb(0x60);
+ break;
+ }
+ if (i == 1000)
+ return;
+ add_to_buf(key);
+ tty_keypress(key);
+
+ for (Task *tmp = keyWait.start; tmp; tmp = tmp->next)
+ unblock_task(pop_from_queue(&keyWait));
+}
+
+/* Initialise the keyboard */
+void
+init_kbd(void)
+{
+ register_driver(&kbdDriver);
+ mknod("/dev/kbd", S_IFCHR | 0666, MKDEV(kbdDriver.major, 0));
+ register_interrupt(1, keyboard_handler);
+}
+
+size_t
+kbd_read(File *file, char *buf, size_t size, off_t offset)
+{
+ char c;
+ size_t count = 0;
+ while (count < size) {
+ c = get_from_buf();
+ if (!c) {
+ add_to_queue(&keyWait, current);
+ block_task(WAITING_FOR_IO);
+ }
+ *(buf + count) = c;
+ count++;
+ }
+ return count;
+}