]> git.dujemihanovic.xyz Git - nameless-os.git/commitdiff
Add PS/2 driver
authorDuje Mihanović <duje.mihanovic@skole.hr>
Sun, 20 Mar 2022 11:36:25 +0000 (12:36 +0100)
committerDuje Mihanović <duje.mihanovic@skole.hr>
Wed, 22 Jun 2022 16:06:17 +0000 (18:06 +0200)
Very incomplete PS/2 driver which can currently reset the controller
(partially), accept keystrokes and supports the shift key.

TODO: Find out why testing a **PS/2 device** hangs

Makefile
include/arch/x86/bitflags.h [new file with mode: 0644]
include/arch/x86/input/ps2.h [new file with mode: 0644]
kernel/arch/x86/irq/sample_handler.c
kernel/drivers/input/ps2.c [new file with mode: 0644]
kernel/kernel.c

index aee8cc187a36d118823f8b1e66abedbbd4a99d87..7d10f975722546a2752b2f85013d8968bf44c375 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -6,7 +6,7 @@ export GIT_REV = $(shell git describe --long HEAD)
 
 CFLAGS = -std=gnu89 -g -Iinclude/arch/x86 -ffreestanding -DGIT_COMMIT=\"$(GIT_REV)\"
 
-KERNEL_OBJ = kernel/entry.o kernel/arch/x86/tty/tty.o kernel/drivers/irq/i8259a.o kernel/arch/x86/irq/idt.o kernel/arch/x86/irq/sample_handler.o kernel/kernel.o
+KERNEL_OBJ = kernel/entry.o kernel/arch/x86/tty/tty.o kernel/drivers/irq/i8259a.o kernel/arch/x86/irq/idt.o kernel/arch/x86/irq/sample_handler.o kernel/drivers/input/ps2.o kernel/kernel.o
 BOOTLOADER_OBJ = boot/x86/mbr boot/x86/vbr-fat32 boot/x86/stage3/LOADER.BIN
 
 default: kernel/kernel.elf bootloader
@@ -42,6 +42,10 @@ kernel/entry.o: kernel/entry.s
 kernel/arch/x86/irq/sample_handler.o: kernel/arch/x86/irq/sample_handler.c
        $(CC) $(CFLAGS) -mgeneral-regs-only -c $< -o $@
 
+kernel/drivers/input/ps2.o: kernel/drivers/input/ps2.c
+
+kernel/kernel.o: kernel/kernel.c
+
 kernel/kernel.elf: kernel/kernel.bin
        $(CC) -ffreestanding -nostdlib -o $@ -T kernel/linker.ld ${KERNEL_OBJ} -Wl,--oformat=elf32-i386
        i686-elf-objcopy --only-keep-debug kernel/kernel.elf kernel/kernel.dbg
diff --git a/include/arch/x86/bitflags.h b/include/arch/x86/bitflags.h
new file mode 100644 (file)
index 0000000..377f730
--- /dev/null
@@ -0,0 +1,9 @@
+/* Common bit flag ops */
+#ifndef BITFLAGS_H
+#define BITFLAGS_H
+
+#define SET(flags, flag) flags |= (1 << flag)
+#define CLEAR(flags, flag) flags &= ~(1 << flag)
+#define IS_SET(flags, flag) (flags & (1 << flag) == (1 << flag))
+
+#endif
diff --git a/include/arch/x86/input/ps2.h b/include/arch/x86/input/ps2.h
new file mode 100644 (file)
index 0000000..8b90a73
--- /dev/null
@@ -0,0 +1,92 @@
+#ifndef X86_INPUT_PS2_H
+#define X86_INPUT_PS2_H
+
+/* port constants */
+#define PS2_DATA_PORT 0x60
+#define PS2_CMD_STS_PORT 0x64
+
+/* command constants */
+#define PS2_CMD_PORT_2_DISABLE 0xA7
+#define PS2_CMD_PORT_1_DISABLE 0xAD
+#define PS2_CMD_PORT_2_ENABLE 0xA8
+#define PS2_CMD_PORT_1_ENABLE 0xAE
+#define PS2_CMD_READ_CCB 0x20
+#define PS2_CMD_WRITE_CCB 0x60
+#define PS2_CMD_CONTROLLER_TEST 0xAA
+#define PS2_CMD_PORT_1_TEST 0xAB
+#define PS2_CMD_PORT_2_TEST 0xA9
+#define PS2_CMD_PORT_2_WRITE 0xD4
+
+#define PS2_DEV_RESET 0xFF
+
+#define PS2_CONTROLLER_GOOD 0x55
+#define PS2_CONTROLLER_BAD 0xFC
+
+enum PS2_CCB {
+       PORT_1_IRQ = 0,
+       PORT_2_IRQ,
+       SYSTEM_FLAG,
+       ZERO,
+       PORT_1_CLK,
+       PORT_2_CLK,
+       PORT_1_TRANSL,
+       ZERO2
+};
+
+enum PS2_STATUS {
+       OUT_STATUS = 0,
+       IN_STATUS,
+       STS_SYSTEM_FLAG,
+       CMD_DATA,
+       UNKNOWN,
+       UNKNOWN2,
+       TIMEOUT_ERROR,
+       PARITY_ERROR
+};
+
+enum PS2_SPECIAL {
+       SELF_TEST_GOOD = 0xAA,
+       SELF_TEST_BAD = 0xFC,
+       SELF_TEST_BAD_2 = 0xFD,
+       ACK = 0xFA,
+       RESEND = 0xFE
+};
+
+static const char scancodes[] = {
+       /* letters */
+       [0x15] = 'q',
+       [0x1d] = 'w',
+       [0x24] = 'e',
+       [0x2d] = 'r',
+       [0x2c] = 't',
+       [0x35] = 'y',
+       [0x3c] = 'u',
+       [0x43] = 'i',
+       [0x44] = 'o',
+       [0x4d] = 'p',
+       [0x1c] = 'a',
+       [0x1b] = 's',
+       [0x23] = 'd',
+       [0x2b] = 'f',
+       [0x34] = 'g',
+       [0x33] = 'h',
+       [0x3b] = 'j',
+       [0x42] = 'k',
+       [0x4b] = 'l',
+       [0x1a] = 'z',
+       [0x22] = 'x',
+       [0x21] = 'c',
+       [0x2a] = 'v',
+       [0x32] = 'b',
+       [0x31] = 'n',
+       [0x3a] = 'm',
+
+       /* special */
+       [0x29] = ' '
+};
+
+extern int ps2_initialize();
+extern void ps2_input_wait();
+extern void ps2_output_wait();
+
+#endif
index f4f4a4bfefad5aacf742f3c6ce2046c86eb1b85e..72a86c61bb0da2dc7fc88d814db36563374b6fde 100644 (file)
@@ -2,6 +2,10 @@
 #include <irq/i8259a.h>
 #include <io.h>
 #include <stdint.h>
+#include <input/ps2.h>
+
+int was_released = 0;
+int is_caps = 0;
 
 typedef uint32_t uword_t;
 
@@ -17,8 +21,30 @@ __attribute__((interrupt))
 void keyb_handler(struct interrupt_frame *frame)
 {
        pic_send_eoi(1);
-       kprint("Got a keyboard interrupt!\n", 0);
-       inb(0x60);
+       uint8_t scancode = inb(PS2_DATA_PORT);
+       if (was_released) {
+               was_released = 0;
+               return;
+       }
+
+       if (scancode == 0xf0) {
+               was_released = 1;
+               uint8_t scancode = inb(PS2_DATA_PORT);
+               if (scancode == 0x12) {
+                       is_caps = 0;
+               }
+               return;
+       } else {
+               if (scancode == 0x12) {
+                       is_caps = 1;
+                       return;
+               }
+               if (!is_caps) {
+                       kprintc(scancodes[scancode], 0);
+               } else {
+                       kprintc(scancodes[scancode] - ('a'-'A'), 0);
+               }
+       }
 }
 
 
diff --git a/kernel/drivers/input/ps2.c b/kernel/drivers/input/ps2.c
new file mode 100644 (file)
index 0000000..0507024
--- /dev/null
@@ -0,0 +1,164 @@
+#include <tty.h>
+#include <io.h>
+#include <input/ps2.h>
+#include <bitflags.h>
+
+int ps2_initialize()
+{
+       uint8_t ccb, is_2channel, port_1_test, port_2_test;
+       
+       kprint("ps2: Begin initializing PS/2 controller\n", 0);
+       outb(PS2_CMD_STS_PORT, PS2_CMD_PORT_2_DISABLE);
+       ps2_input_wait();
+       outb(PS2_CMD_STS_PORT, PS2_CMD_PORT_1_DISABLE);
+       ps2_input_wait();
+       inb(PS2_DATA_PORT);
+
+       outb(PS2_CMD_STS_PORT, PS2_CMD_READ_CCB);
+       ps2_output_wait();
+       ccb = inb(PS2_DATA_PORT);
+
+       CLEAR(ccb, 0);
+       CLEAR(ccb, 1);
+       CLEAR(ccb, 6);
+       
+       ps2_input_wait();
+       outb(PS2_CMD_STS_PORT, PS2_CMD_WRITE_CCB);
+       ps2_input_wait();
+       outb(PS2_DATA_PORT, ccb);
+
+       ps2_input_wait();
+       outb(PS2_CMD_STS_PORT, PS2_CMD_CONTROLLER_TEST);
+       ps2_output_wait();
+       if (inb(PS2_DATA_PORT) != PS2_CONTROLLER_GOOD) {
+               kprint("ps2: Controller self test failed, exiting!\n", 0);
+               return -1;
+       };
+       
+       ps2_input_wait();
+       outb(PS2_CMD_STS_PORT, PS2_CMD_PORT_2_ENABLE);
+       ps2_input_wait();
+       outb(PS2_CMD_STS_PORT, PS2_CMD_READ_CCB);
+       ps2_output_wait();
+       ccb = inb(PS2_DATA_PORT);
+
+       if (ccb & PORT_2_CLK != PORT_2_CLK) {
+               is_2channel = 0;
+               kprint("ps2: Controller is single-channel\n", 0);
+       } else {
+               is_2channel = 1;
+               kprint("ps2: Controller is dual-channel\n", 0);
+               ps2_input_wait();
+               outb(PS2_CMD_STS_PORT, PS2_CMD_PORT_2_DISABLE);
+       }
+
+       ps2_input_wait();
+       outb(PS2_CMD_STS_PORT, PS2_CMD_PORT_1_TEST);
+       ps2_output_wait();
+       
+       if (inb(PS2_DATA_PORT) != 0) {
+               port_1_test = 0;
+               kprint("ps2: Port 1 test failed!\n", 0);
+               if (!is_2channel) {
+                       kprint("ps2: No functional port, exiting!\n", 0);
+                       return -1;
+               }
+       } else port_1_test = 1;
+
+       ps2_input_wait();
+       if (is_2channel) {
+               outb(PS2_CMD_STS_PORT, PS2_CMD_PORT_2_TEST);
+               ps2_output_wait();
+
+               if (inb(PS2_DATA_PORT) != 0) {
+                       port_2_test = 0;
+                       kprint("ps2: Port 2 test failed!", 0);
+                       if (!port_1_test) {
+                               kprint("ps2: No functional port, exiting!\n", 0);
+                               return -1;
+                       }
+               } else port_2_test = 1;
+       }
+
+       if (port_1_test) { ps2_input_wait(); outb(PS2_CMD_STS_PORT, PS2_CMD_PORT_1_ENABLE); }
+       if (port_2_test) { ps2_input_wait(); outb(PS2_CMD_STS_PORT, PS2_CMD_PORT_2_ENABLE); }
+
+       int dev_1_test, dev_2_test;
+
+/*
+       if (port_1_test) {
+               uint8_t resp;
+               
+               do {
+                       ps2_input_wait();
+                       outb(PS2_DATA_PORT, PS2_DEV_RESET);
+                       ps2_output_wait();
+                       resp = inb(PS2_DATA_PORT);
+               } while (resp == RESEND);
+
+               if (resp == SELF_TEST_BAD || resp == SELF_TEST_BAD_2) {
+                       dev_1_test = 0;
+                       kprint("ps2: Port 1 device self test failed!\n", 0);
+                       if (!port_2_test) {
+                               kprint("ps2: No functioning devices, exiting!\n", 0);
+                               return -1;
+                       }
+               } else { dev_1_test = 1; kprint("ps2: Port 1 device test successful\n", 0); }
+       }
+
+       if (port_2_test) {
+               int resp = RESEND;
+
+               while (resp == RESEND) {
+                       ps2_input_wait();
+                       outb(PS2_CMD_STS_PORT, PS2_CMD_PORT_2_WRITE);
+                       ps2_input_wait();
+                       outb(PS2_DATA_PORT, PS2_DEV_RESET);
+                       ps2_output_wait();
+                       resp = inb(PS2_DATA_PORT);
+               }
+
+               if (resp == SELF_TEST_BAD || resp == SELF_TEST_BAD_2) {
+                       dev_2_test = 0;
+                       kprint("ps2: Port 2 device self test failed!\n", 0);
+                       if (!dev_1_test) {
+                               kprint("ps2: No functioning devices, exiting!\n", 0);
+                               return -1;
+                       }
+               } else { dev_2_test = 1; kprint("ps2: Port 2 test successful\n", 0); }
+       }
+*/
+
+       ps2_input_wait();
+       outb(PS2_CMD_STS_PORT, PS2_CMD_READ_CCB);
+       ps2_output_wait();
+       ccb = inb(PS2_DATA_PORT);
+
+       SET(ccb, PORT_1_IRQ);
+
+       ps2_input_wait();
+       outb(PS2_CMD_STS_PORT, PS2_CMD_WRITE_CCB);
+       ps2_input_wait();
+       outb(PS2_DATA_PORT, ccb);
+
+       if (dev_2_test && dev_1_test) return 1;
+       else return 0;
+}
+
+void ps2_output_wait()
+{
+       uint8_t status;
+       status = inb(PS2_CMD_STS_PORT);
+
+       while (!IS_SET(status, OUT_STATUS)) status = inb(PS2_CMD_STS_PORT);
+       return;
+}
+
+void ps2_input_wait()
+{
+       uint8_t status;
+       status = inb(PS2_CMD_STS_PORT);
+
+       while (IS_SET(status, IN_STATUS)) status = inb(PS2_CMD_STS_PORT);
+       return;
+}
index 4c6efd8692da90d7ab15cf782846b3194e9483f7..3f6c256d8029f2a06429da169381e0b401dea300 100644 (file)
@@ -1,8 +1,9 @@
+#include <stdint.h>
 #include <tty.h>
 #include <io.h>
 #include <irq/idt.h>
 #include <irq/i8259a.h>
-#include <stdint.h>
+#include <input/ps2.h>
 
 extern void double_fault(struct abort_frame *frame);
 extern void keyb_handler(struct interrupt_frame *frame);
@@ -11,10 +12,22 @@ struct idtr idtr __attribute__((aligned(0x10)));
 
 void kmain(void)
 {
+       int ps2_success;
        screen_clear();
        kprint("Welcome to Nameless OS!\nRunning revision: ", 0);
        kprint(GIT_COMMIT, 0);
        kprint("\n", 0);
+       ps2_success = ps2_initialize();
+       switch (ps2_success) {
+               case -1:
+                       kprint("No PS/2 devices found or working, we will have no input\n", 0);
+                       break;
+               case 0:
+                       kprint("Found one working PS/2 device\n", 0);
+                       break;
+               case 1:
+                       kprint("Found two working PS/2 devices\n", 0);
+       }
        kprint("Preparing IDT...\n", 0);
        idt_set_descriptor(idt, 0x8, 0x8, (uint32_t) double_fault, IDT_TRAP_GATE, 0x0);
        idt_set_descriptor(idt, 0x21, 0x8, (uint32_t) keyb_handler, IDT_INTERRUPT_GATE, 0x0);