From: Duje Mihanović Date: Mon, 22 Aug 2022 20:11:21 +0000 (+0200) Subject: Merge branch 'ps2-driver' into mm X-Git-Url: http://git.dujemihanovic.xyz/%7B%7B%20%24image.RelPermalink%20%7D%7D?a=commitdiff_plain;h=96b81920dce6c169f32fc4d5b724b4046ea9bfcd;hp=119dc631e1b417eec4f0eb2d8373d034b53d0f2e;p=nameless-os.git Merge branch 'ps2-driver' into mm --- diff --git a/boot/x86/stage3/Makefile b/boot/x86/stage3/Makefile index cfe2189..25b0265 100644 --- a/boot/x86/stage3/Makefile +++ b/boot/x86/stage3/Makefile @@ -3,7 +3,7 @@ STAGE3_OBJ = loader.o elf.o paging.o prekernel.o ASFLAGS_BASE = -f elf -g dwarf2 -DGIT_REVISION=\"$(GIT_REV)\" CFLAGS_BASE = -ffreestanding -nostdlib -DGIT_REVISION=\"$(GIT_REV)\" -g \ -Og -LDFLAGS_BASE = -ffreestanding -nostdlib -g -lgcc -T stage3.ld -flto +LDFLAGS_BASE = -ffreestanding -nostdlib -g -lgcc -T stage3.ld default: LOADER.BIN loader.elf diff --git a/kernel/Makefile b/kernel/Makefile index b510e80..8bb12d8 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -6,7 +6,7 @@ ASFLAGS_BASE = -f elf -g dwarf2 CFLAGS_BASE = -fgnu89-inline -ffreestanding -nostdlib -Iinclude \ -Iinclude/arch/x86 -g -DGIT_COMMIT=\"$(GIT_REV)\" \ -Og -LDFLAGS_BASE = -ffreestanding -nostdlib -lgcc -g -flto -T arch/x86/linker.ld +LDFLAGS_BASE = -ffreestanding -nostdlib -lgcc -g -T arch/x86/linker.ld kernel.elf kernel.dbg: $(KERNEL_OBJ) $(REAL_CC) $(LDFLAGS_BASE) $(LDFLAGS) $^ -o $@ diff --git a/kernel/drivers/Makefile b/kernel/drivers/Makefile index 76d86aa..c7a3619 100644 --- a/kernel/drivers/Makefile +++ b/kernel/drivers/Makefile @@ -1 +1,2 @@ +include drivers/input/Makefile include drivers/irq/Makefile diff --git a/kernel/drivers/input/Makefile b/kernel/drivers/input/Makefile new file mode 100644 index 0000000..7577b52 --- /dev/null +++ b/kernel/drivers/input/Makefile @@ -0,0 +1 @@ +KERNEL_OBJ += drivers/input/ps2.o diff --git a/kernel/drivers/input/ps2.c b/kernel/drivers/input/ps2.c new file mode 100644 index 0000000..ab4be5c --- /dev/null +++ b/kernel/drivers/input/ps2.c @@ -0,0 +1,201 @@ +#include +#include +#include +#include +#include + +static int was_released = 0, is_caps = 0; + +int ps2_keyb_handler() +{ + uint8_t scancode = inb(PS2_DATA_PORT); + if (was_released) { + was_released = 0; + return 0; + } + + if (scancode == 0xf0) { + was_released = 1; + uint8_t scancode = inb(PS2_DATA_PORT); + if (scancode == 0x12) { + is_caps = 0; + } + return 0; + } else { + if (scancode == 0x12) { + is_caps = 1; + return 0; + } + if (!is_caps) { + kprintc(scancodes[scancode], 0); + } else { + kprintc(scancodes[scancode] - ('a'-'A'), 0); + } + } + return 0; +} + +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); } + } + + inb(PS2_DATA_PORT); + ps2_input_wait(); + + if (port_2_test) { + uint8_t resp; + + do { + 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); + } while (resp == RESEND); + + 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 device test successful\n", 0); } + } + + inb(PS2_DATA_PORT); + inb(PS2_DATA_PORT); + 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_1_test) register_interrupt(33, &ps2_keyb_handler); + + 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/include/arch/x86/bitflags.h b/kernel/include/arch/x86/bitflags.h new file mode 100644 index 0000000..377f730 --- /dev/null +++ b/kernel/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/kernel/include/arch/x86/input/ps2.h b/kernel/include/arch/x86/input/ps2.h new file mode 100644 index 0000000..55858bf --- /dev/null +++ b/kernel/include/arch/x86/input/ps2.h @@ -0,0 +1,104 @@ +#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', + + /* numbers */ + [0x45] = '0', + [0x16] = '1', + [0x1e] = '2', + [0x26] = '3', + [0x25] = '4', + [0x2e] = '5', + [0x36] = '6', + [0x3d] = '7', + [0x3e] = '8', + [0x46] = '9', + + /* special */ + [0x29] = ' ' +}; + +extern int ps2_initialize(); +extern void ps2_input_wait(); +extern void ps2_output_wait(); + +#endif diff --git a/kernel/kernel/kernel.c b/kernel/kernel/kernel.c index 64e64ae..b198fcf 100644 --- a/kernel/kernel/kernel.c +++ b/kernel/kernel/kernel.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -92,6 +93,18 @@ void kmain(struct e820_map *mmap, int mmap_size) pic_init(0x20, 0x28); pic_mask_all(); asm volatile ("sti"); + kprint("Initializing PS/2...\n", VGA_LIGHT_GRAY, VGA_BLACK); + int ret = ps2_initialize(); + switch (ret) { + case -1: + kprint("No PS/2 devices found or something is faulty\n", VGA_LIGHT_GRAY, VGA_BLACK); + break; + case 0: + kprint("One PS/2 device found\n", VGA_LIGHT_GRAY, VGA_BLACK); + break; + case 1: + kprint("Two PS/2 devices found\n", VGA_LIGHT_GRAY, VGA_BLACK); + } kprint("All done\n", VGA_LIGHT_GRAY, VGA_BLACK); while(1); }