From: Duje Mihanović Date: Sun, 20 Mar 2022 11:36:25 +0000 (+0100) Subject: Add PS/2 driver X-Git-Url: http://git.dujemihanovic.xyz/%7B%7B%20%24style.RelPermalink%20%7D%7D?a=commitdiff_plain;h=8518c4b121776aba483f253333e4b0ddae54309d;p=nameless-os.git Add PS/2 driver 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 --- diff --git a/Makefile b/Makefile index aee8cc1..7d10f97 100644 --- 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 index 0000000..377f730 --- /dev/null +++ b/include/arch/x86/bitflags.h @@ -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 index 0000000..8b90a73 --- /dev/null +++ b/include/arch/x86/input/ps2.h @@ -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 diff --git a/kernel/arch/x86/irq/sample_handler.c b/kernel/arch/x86/irq/sample_handler.c index f4f4a4b..72a86c6 100644 --- a/kernel/arch/x86/irq/sample_handler.c +++ b/kernel/arch/x86/irq/sample_handler.c @@ -2,6 +2,10 @@ #include #include #include +#include + +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 index 0000000..0507024 --- /dev/null +++ b/kernel/drivers/input/ps2.c @@ -0,0 +1,164 @@ +#include +#include +#include +#include + +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; +} diff --git a/kernel/kernel.c b/kernel/kernel.c index 4c6efd8..3f6c256 100644 --- a/kernel/kernel.c +++ b/kernel/kernel.c @@ -1,8 +1,9 @@ +#include #include #include #include #include -#include +#include 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);