From 6e57181623da5bf5042e26a923db2de7a354dbde Mon Sep 17 00:00:00 2001 From: 82marbag <69267416+82marbag@users.noreply.github.com> Date: Sat, 26 Sep 2020 10:50:18 -0400 Subject: [PATCH] drivers: basic keyboard support Basic keyboard support with PIC interrupt Signed-off-by: Daniele Ahmed --- arch/x86/entry.S | 1 + arch/x86/traps.c | 4 ++ common/kernel.c | 8 +++ common/setup.c | 4 ++ drivers/keyboard.c | 115 +++++++++++++++++++++++++++++++++++++ drivers/pic.c | 10 ++-- include/drivers/keyboard.h | 103 +++++++++++++++++++++++++++++++++ include/string.h | 2 + 8 files changed, 242 insertions(+), 5 deletions(-) create mode 100644 drivers/keyboard.c create mode 100644 include/drivers/keyboard.h diff --git a/arch/x86/entry.S b/arch/x86/entry.S index 1038d632..6b484a6c 100644 --- a/arch/x86/entry.S +++ b/arch/x86/entry.S @@ -96,6 +96,7 @@ END_FUNC(handle_exception) interrupt_handler pit pit_interrupt_handler interrupt_handler uart uart_interrupt_handler +interrupt_handler keyboard keyboard_interrupt_handler ENTRY(usermode_call_asm) /* FIXME: Add 32-bit support */ diff --git a/arch/x86/traps.c b/arch/x86/traps.c index 237b5968..72899039 100644 --- a/arch/x86/traps.c +++ b/arch/x86/traps.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -40,6 +41,7 @@ extern void asm_interrupt_handler_uart(void); extern void asm_interrupt_handler_pit(void); +extern void asm_interrupt_handler_keyboard(void); static void ret2kern_handler(void) { /* clang-format off */ @@ -160,6 +162,8 @@ void init_traps(unsigned int cpu) { _ul(asm_interrupt_handler_uart), GATE_DPL0, GATE_PRESENT, 0); set_intr_gate(&percpu->idt[PIT_IRQ0_OFFSET], __KERN_CS, _ul(asm_interrupt_handler_pit), GATE_DPL0, GATE_PRESENT, 0); + set_intr_gate(&percpu->idt[KEYBOARD_IRQ0_OFFSET], __KERN_CS, + _ul(asm_interrupt_handler_keyboard), GATE_DPL0, GATE_PRESENT, 0); barrier(); lidt(&percpu->idt_ptr); diff --git a/common/kernel.c b/common/kernel.c index 0eb8b69e..a1eb1cee 100644 --- a/common/kernel.c +++ b/common/kernel.c @@ -23,6 +23,7 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include +#include #include #include #include @@ -40,6 +41,13 @@ int usermode_call(user_func_t fn, void *fn_arg) { PERCPU_OFFSET(user_stack)); } +static void echo_loop(void) { + while (1) { + io_delay(); + keyboard_process_keys(); + } +} + void kernel_main(void) { printk("\nKTF - Kernel Test Framework!\n\n"); diff --git a/common/setup.c b/common/setup.c index 31e0ea2a..fcb38401 100644 --- a/common/setup.c +++ b/common/setup.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -235,6 +236,9 @@ void __noreturn __text_init kernel_start(uint32_t multiboot_magic, /* Initialize Programmable Interrupt Timer */ init_pit(get_bsp_cpu_id()); + /* Initialize keyboard */ + init_keyboard(); + /* Jump from .text.init section to .text */ asm volatile("push %0; ret" ::"r"(&kernel_main)); diff --git a/drivers/keyboard.c b/drivers/keyboard.c new file mode 100644 index 00000000..e27ea004 --- /dev/null +++ b/drivers/keyboard.c @@ -0,0 +1,115 @@ +/* + * Copyright © 2020 Amazon.com, Inc. or its affiliates. + * All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include +#include +#include +#include + +static struct { + int shift, caps, ctrl, alt; + + char buf[KEY_BUF]; + unsigned curr; + unsigned init; +} keyboard_state; + +void init_keyboard(void) { + outb(PIC1_PORT_DATA, KEYBOARD_IRQ_UNMASK); /* unmask keyboard irq */ + + memset(&keyboard_state, 0, sizeof(keyboard_state)); +} + +static char keyboard_scan_to_key(char scan) { + if ((unsigned long) scan < sizeof(key_map)) { + unsigned char key = key_map[(unsigned) scan]; + + if (key >= 'a' && key <= 'z') + return key + ('A' - 'a') * (keyboard_state.caps ^ keyboard_state.shift); + else if (keyboard_state.shift && (unsigned long) scan < sizeof(key_map_up)) + return key_map_up[(unsigned) scan]; + + return key; + } + + return scan; +} + +unsigned int keyboard_process_keys(void) { + unsigned n = 0; + unsigned char key, scan; + + while (keyboard_state.curr != keyboard_state.init) { + scan = keyboard_state.buf[keyboard_state.init]; + + key = keyboard_scan_to_key(scan); + + if (isprint(key)) + printk("%c", key); + + keyboard_state.init = (keyboard_state.init + 1) % KEY_BUF; + ++n; + } + return n; +} + +void keyboard_interrupt_handler(void) { + unsigned char status; + int released; + + status = inb(KEYBOARD_PORT_CMD); + if (status & KEYBOARD_STATUS_OUT_FULL) { + char scan = inb(KEYBOARD_PORT_DATA); + + released = !!(scan & SCAN_RELEASE_MASK); + scan = scan & (SCAN_RELEASE_MASK - 1); + + switch (scan) { + case SCAN_LSHIFT: + case SCAN_RSHIFT: + keyboard_state.shift = !released; + break; + case SCAN_CAPS: + if (!released) + keyboard_state.caps = !keyboard_state.caps; + break; + case SCAN_CTRL: + keyboard_state.ctrl = !released; + break; + case SCAN_ALT: + keyboard_state.alt = !released; + break; + default: + if (!released) { + if ((long unsigned) scan < sizeof(key_map)) { + keyboard_state.buf[keyboard_state.curr] = scan; + keyboard_state.curr = (keyboard_state.curr + 1) % KEY_BUF; + } + break; + } + } + } + + outb(PIC1_PORT_CMD, PIC_EOI); +} diff --git a/drivers/pic.c b/drivers/pic.c index 61a56420..9edc069b 100644 --- a/drivers/pic.c +++ b/drivers/pic.c @@ -33,8 +33,8 @@ static inline void pic_outb(io_port_t port, unsigned char value) { void init_pic(void) { /* Cascade mode initialization sequence */ - pic_outb(PIC1_PORT_CMD, PIC_ICW1_INIT | PIC_ICW1_ICW4); - pic_outb(PIC2_PORT_CMD, PIC_ICW1_INIT | PIC_ICW1_ICW4); + pic_outb(PIC1_PORT_CMD, PIC_ICW1_INIT | PIC_ICW1_LEVEL | PIC_ICW1_ICW4); + pic_outb(PIC2_PORT_CMD, PIC_ICW1_INIT | PIC_ICW1_LEVEL | PIC_ICW1_ICW4); /* Remap PICs interrupt vectors */ pic_outb(PIC1_PORT_DATA, PIC_IRQ0_OFFSET); @@ -44,9 +44,9 @@ void init_pic(void) { outb(PIC1_PORT_DATA, PIC_CASCADE_PIC1_IRQ); outb(PIC2_PORT_DATA, PIC_CASCADE_PIC2_IRQ); - /* PIC mode: 80x86, Automatic EOI */ - outb(PIC1_PORT_DATA, PIC_ICW4_8086 | PIC_ICW4_AUTO); - outb(PIC2_PORT_DATA, PIC_ICW4_8086 | PIC_ICW4_AUTO); + /* PIC mode: 80x86 */ + outb(PIC1_PORT_DATA, PIC_ICW4_8086); + outb(PIC2_PORT_DATA, PIC_ICW4_8086); /* Mask the 8259A PICs by setting all IMR bits */ outb(PIC1_PORT_DATA, 0xFF); diff --git a/include/drivers/keyboard.h b/include/drivers/keyboard.h new file mode 100644 index 00000000..739240fd --- /dev/null +++ b/include/drivers/keyboard.h @@ -0,0 +1,103 @@ +/* + * Copyright © 2020 Amazon.com, Inc. or its affiliates. + * All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef KTF_KEYBOARD_H +#define KTF_KEYBOARD_H + +#include + +#define KEYBOARD_PORT_CMD 0x64 /* keyboard command port */ +#define KEYBOARD_PORT_DATA 0x60 /* keyboard data port */ + +#define KEYBOARD_IRQ0_OFFSET (PIC_IRQ0_OFFSET + 1) /* keyboard irq offset */ +#define KEYBOARD_IRQ_UNMASK (~2) /* mask with keyboard irq enabled, others disabled */ + +#define KEYBOARD_STATUS_OUT_FULL 1 /* bit set when the keyboard buffer is full */ + +#define KEY_BUF 80 /* size of internal input buffer */ + +#define SCAN_RELEASE_MASK 0x80 /* bit set on key release */ + +#define KEY_NULL 0x0 /* no key */ + +/* scan codes */ +#define SCAN_NULL 0x0 +#define SCAN_ESC 0x1 +#define SCAN_ENTER 0x1c +#define SCAN_CTRL 0x1d +#define SCAN_LSHIFT 0x2a +#define SCAN_RSHIFT 0x36 +#define SCAN_ALT 0x38 +#define SCAN_CAPS 0x3a +#define SCAN_F1 0x3b +#define SCAN_F2 0x3c +#define SCAN_F3 0x3d +#define SCAN_F4 0x3e +#define SCAN_F5 0x3f +#define SCAN_F6 0x40 +#define SCAN_F7 0x41 +#define SCAN_F8 0x42 +#define SCAN_F9 0x43 +#define SCAN_F10 0x44 +#define SCAN_F11 0x55 +#define SCAN_F12 0x58 +#define SCAN_NUMLOCK 0x45 +#define SCAN_SCROLLLOCK 0x46 +#define SCAN_HOME 0x47 +#define SCAN_UP 0x48 +#define SCAN_PAGEUP 0x49 +#define SCAN_LEFT 0x4b +#define SCAN_KEYPAD5 0x4c +#define SCAN_RIGHT 0x4d +#define SCAN_END 0x4f +#define SCAN_DOWN 0x50 +#define SCAN_PAGEDOWN 0x51 +#define SCAN_INS 0x52 +#define SCAN_DEL 0x53 + +void init_keyboard(void); +void keyboard_interrupt_handler(void); +unsigned int keyboard_process_keys(void); + +/* clang-format off */ +static const unsigned char key_map[] = { /* map scan code to key */ + 0xff, SCAN_ESC, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', + '\b', '\t', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', + SCAN_ENTER, SCAN_CTRL, 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`', + SCAN_LSHIFT, '\\', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', + SCAN_RSHIFT, 0x37, SCAN_ALT, ' ', SCAN_CAPS, + SCAN_F1, SCAN_F2, SCAN_F3, SCAN_F4, SCAN_F5, SCAN_F6, SCAN_F7, SCAN_F8, SCAN_F9, SCAN_F10, + SCAN_NUMLOCK, SCAN_SCROLLLOCK, SCAN_HOME, SCAN_UP, SCAN_PAGEUP, 0x4a, SCAN_LEFT, + SCAN_KEYPAD5, SCAN_RIGHT, 0x4e, SCAN_END, SCAN_DOWN, SCAN_PAGEDOWN, SCAN_INS, SCAN_DEL, + 0x54, SCAN_F11, 0x56, 0x57, SCAN_F12 +}; + +static const unsigned char key_map_up[] = { /* map scan code to upper key */ + 0xff, SCAN_ESC, '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', + '\b', '\t', 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}', + SCAN_ENTER, SCAN_CTRL, 'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '"', '~', + SCAN_LSHIFT, '|', 'Z', 'X', 'C', 'V', 'B', 'N', 'M', '<', '>', '?'}; +/* clang-format on */ + +#endif diff --git a/include/string.h b/include/string.h index 75011cca..90c32673 100644 --- a/include/string.h +++ b/include/string.h @@ -47,6 +47,8 @@ static inline int isupper(int c) { return c >= 'A' && c <= 'Z'; } static inline int isalpha(int c) { return islower(c) || isupper(c); } +static inline int isprint(char c) { return c >= 0x20 && c <= 0x7e; } + static inline size_t strlen(const char *str) { size_t len = 0;