From 7eae4edb86fc65b2e99bf81757aeb00392d8a60d Mon Sep 17 00:00:00 2001 From: Andreas Baumann Date: Thu, 20 Jul 2017 09:31:30 +0200 Subject: added a virtual keyboard driver interface, made the PS/2 keyboard a specialization of it --- src/Makefile | 12 +- src/drivers/hdi/keyboard.c | 44 +++++ src/drivers/hdi/keyboard.h | 55 +++++++ src/drivers/hdi/mouse.h | 2 - src/drivers/hdi/ps2/keyboard.c | 331 -------------------------------------- src/drivers/hdi/ps2/keyboard.h | 69 -------- src/drivers/hdi/ps2/ps2keyboard.c | 331 ++++++++++++++++++++++++++++++++++++++ src/drivers/hdi/ps2/ps2keyboard.h | 33 ++++ src/kernel/kernel.c | 6 +- 9 files changed, 474 insertions(+), 409 deletions(-) create mode 100644 src/drivers/hdi/keyboard.c create mode 100644 src/drivers/hdi/keyboard.h delete mode 100644 src/drivers/hdi/ps2/keyboard.c delete mode 100644 src/drivers/hdi/ps2/keyboard.h create mode 100644 src/drivers/hdi/ps2/ps2keyboard.c create mode 100644 src/drivers/hdi/ps2/ps2keyboard.h diff --git a/src/Makefile b/src/Makefile index 5b67063..1696e6c 100644 --- a/src/Makefile +++ b/src/Makefile @@ -36,7 +36,7 @@ kernel.bin: kernel.elf kernel.sym: kernel.elf $(OBJCOPY) --only-keep-debug kernel.elf kernel.sym -kernel.elf: kernel/entry.o kernel/kernel.o kernel/kernel_asm.o kernel/console.o kernel/vgatext.o kernel/serial.o kernel/memorymanagement.o kernel/tasks.o hardware/port.o hardware/port_asm.o hardware/interrupts.o hardware/interrupts_asm.o hardware/pci.o drivers/driver.o drivers/hdi/mouse.o drivers/hdi/ps2/keyboard.o drivers/hdi/ps2/ps2mouse.o drivers/video/vga.o drivers/video/vga_font.o drivers/net/rtl8139.o gui/widget.o gui/composite_widget.o gui/window.o gui/desktop.o gui/text_widget.o libc/string.o libc/stdlib.o libc/stdio.o libc/setjmp.o +kernel.elf: kernel/entry.o kernel/kernel.o kernel/kernel_asm.o kernel/console.o kernel/vgatext.o kernel/serial.o kernel/memorymanagement.o kernel/tasks.o hardware/port.o hardware/port_asm.o hardware/interrupts.o hardware/interrupts_asm.o hardware/pci.o drivers/driver.o drivers/hdi/mouse.o drivers/hdi/keyboard.o drivers/hdi/ps2/ps2keyboard.o drivers/hdi/ps2/ps2mouse.o drivers/video/vga.o drivers/video/vga_font.o drivers/net/rtl8139.o gui/widget.o gui/composite_widget.o gui/window.o gui/desktop.o gui/text_widget.o libc/string.o libc/stdlib.o libc/stdio.o libc/setjmp.o $(LD) -o kernel.elf -N -n -Ttext 0x8800 -e kernel_entry --oformat elf32-i386 \ kernel/entry.o \ kernel/kernel.o kernel/kernel_asm.o \ @@ -46,7 +46,8 @@ kernel.elf: kernel/entry.o kernel/kernel.o kernel/kernel_asm.o kernel/console.o hardware/interrupts.o hardware/interrupts_asm.o \ hardware/pci.o \ drivers/driver.o drivers/hdi/mouse.o \ - drivers/hdi/ps2/keyboard.o drivers/hdi/ps2/ps2mouse.o \ + drivers/hdi/keyboard.o \ + drivers/hdi/ps2/ps2keyboard.o drivers/hdi/ps2/ps2mouse.o \ drivers/video/vga.o drivers/video/vga_font.o \ drivers/net/rtl8139.o \ libc/string.o libc/stdlib.o libc/stdio.o libc/setjmp.o \ @@ -101,8 +102,11 @@ drivers/driver.o: drivers/driver.c drivers/driver.h drivers/hdi/mouse.o: drivers/hdi/mouse.c drivers/hdi/mouse.h $(CC) $(CFLAGS) -c -o drivers/hdi/mouse.o drivers/hdi/mouse.c -drivers/hdi/ps2/keyboard.o: drivers/hdi/ps2/keyboard.c drivers/hdi/ps2/keyboard.h - $(CC) $(CFLAGS) -c -o drivers/hdi/ps2/keyboard.o drivers/hdi/ps2/keyboard.c +drivers/hdi/keyboard.o: drivers/hdi/keyboard.c drivers/hdi/keyboard.h + $(CC) $(CFLAGS) -c -o drivers/hdi/keyboard.o drivers/hdi/keyboard.c + +drivers/hdi/ps2/ps2keyboard.o: drivers/hdi/ps2/ps2keyboard.c drivers/hdi/ps2/ps2keyboard.h + $(CC) $(CFLAGS) -c -o drivers/hdi/ps2/ps2keyboard.o drivers/hdi/ps2/ps2keyboard.c drivers/hdi/ps2/ps2mouse.o: drivers/hdi/ps2/ps2mouse.c drivers/hdi/ps2/ps2mouse.h $(CC) $(CFLAGS) -c -o drivers/hdi/ps2/ps2mouse.o drivers/hdi/ps2/ps2mouse.c diff --git a/src/drivers/hdi/keyboard.c b/src/drivers/hdi/keyboard.c new file mode 100644 index 0000000..dd7ff65 --- /dev/null +++ b/src/drivers/hdi/keyboard.c @@ -0,0 +1,44 @@ +#include "keyboard.h" + +#include "string.h" +#include "kernel.h" + +static keyboard_vtable_t const keyboard_vtable = { + { + keyboard_activate, + keyboard_deactivate, + keyboard_deinit, + keyboard_print_info + } +}; + +void keyboard_init( keyboard_t *keyboard, keyboard_event_handler_t handler, interrupt_t *interrupt, void *context ) +{ + memset( keyboard, 0, sizeof( keyboard_t ) ); + + driver_init( (driver_t *)keyboard, DRIVER_TYPE_KEYBOARD, interrupt, context ); + + keyboard->handler = handler; + + ((driver_t *)keyboard)->vtable = (driver_vtable_t *)&keyboard_vtable; +} + +void keyboard_deinit( void *obj ) +{ + // nothing to be done +} + +void keyboard_activate( void *obj ) +{ + kernel_panic( "Activating generic keyboard driver should not be called directly." ); +} + +void keyboard_deactivate( void *obj ) +{ + kernel_panic( "Deactivating generic keyboard driver should not be called directly." ); +} + +void keyboard_print_info( void *obj ) +{ + kernel_panic( "Printing info of generic keyboard driver should not be called directly." ); +} diff --git a/src/drivers/hdi/keyboard.h b/src/drivers/hdi/keyboard.h new file mode 100644 index 0000000..aab7383 --- /dev/null +++ b/src/drivers/hdi/keyboard.h @@ -0,0 +1,55 @@ +#ifndef KEYBOARD_H +#define KEYBOARD_H + +#include "driver.h" + +typedef enum { + KEYBOARD_EVENT_TYPE_KEY_PRESSED, + KEYBOARD_EVENT_TYPE_KEY_RELEASED +} keyboard_event_type_t; + +typedef enum { + KEYBOARD_MODIFIER_SHIFT = 0x01, + KEYBOARD_MODIFIER_CTRL = 0x02, + KEYBOARD_MODIFIER_ALT = 0x04 +} keyboard_modifier_mask_t; + +typedef enum { + KEYBOARD_KEY_UNKNOWN, + KEYBOARD_KEY_ASCII, + KEYBOARD_KEY_ESC, + KEYBOARD_KEY_TAB, + KEYBOARD_KEY_INS, + KEYBAORD_KEY_DEL, + KEYBAORD_KEY_BACKSPACE, + KEYBAORD_CURSOR_LEFT, + KEYBOARD_CURSOR_RIGHT, + KEYBOARD_CURSOR_UP, + KEYBOARD_CURSOR_DOWN +} keyboard_key_t; + +typedef struct { + keyboard_event_type_t type; + keyboard_modifier_mask_t modifiers; + keyboard_key_t key; + char ascii_key; +} keyboard_event_t; + +typedef void (*keyboard_event_handler_t)( keyboard_event_t *event, void *context ); + +typedef struct { + driver_t base; + keyboard_event_handler_t handler; +} keyboard_t; + +typedef struct { + driver_vtable_t base; +} keyboard_vtable_t; + +void keyboard_init( keyboard_t *keyboard, keyboard_event_handler_t handler, interrupt_t *interrupt, void *context ); +void keyboard_activate( void *obj ); +void keyboard_deactivate( void *obj ); +void keyboard_deinit( void *obj ); +void keyboard_print_info( void *obj ); + +#endif // KEYBOARD_H diff --git a/src/drivers/hdi/mouse.h b/src/drivers/hdi/mouse.h index 90417f3..5b3d08f 100644 --- a/src/drivers/hdi/mouse.h +++ b/src/drivers/hdi/mouse.h @@ -50,6 +50,4 @@ void mouse_print_info( void *obj ); void mouse_set_resolution( void *obj, const uint32_t x, const uint32_t y ); void mouse_set_position( void *obj, const uint32_t x, const uint32_t y ); -uint32_t mouse_handle_interrupt( interrupt_handler_t *handler, uint32_t esp ); - #endif // MOUSE_H diff --git a/src/drivers/hdi/ps2/keyboard.c b/src/drivers/hdi/ps2/keyboard.c deleted file mode 100644 index 9c5a246..0000000 --- a/src/drivers/hdi/ps2/keyboard.c +++ /dev/null @@ -1,331 +0,0 @@ -#include "keyboard.h" -#include "stdio.h" - -// status register on command port (read) -#define STATUS_REG_OUTPUT_BUF_FULL 0x01 -#define STATUS_REG_INPUT_BUF_FULL 0x02 - -// generic PS/2 commands -#define COMMAND_GET_STATE 0x20 -#define COMMAND_SET_STATE 0x60 -#define COMMAND_DISABLE_PORT1 0xAD -#define COMMAND_ENABLE_PORT1 0xAE - -// status control register flags -#define STATUS_CONTROL_CONFIG_ENABLE_IRQ_PORT1 0x01 -#define STATUS_CONTROL_CONFIG_CLOCK_PORT1 0x10 - -// keyboard specific commands, PSAUX1 -#define KBD_COMMAND_SET_LEDS 0xED -#define KBD_SET_SCANCODE 0xF0 -#define KBD_ACTIVATE 0xF4 - -// keyboard LED status codes -#define KBD_LED_SCROLL_LOCK_ON 0x01 -#define KBD_LED_NUM_LOCK_ON 0x02 -#define KBD_LED_CAPS_LOCK_ON 0x04 -#define KBD_LED_ALL_OFF 0x00 -#define KBD_LED_ALL_ON KBD_LED_SCROLL_LOCK_ON | KBD_LED_NUM_LOCK_ON | KBD_LED_CAPS_LOCK_ON - -// keyboard scan codes -#define KBD_SCANCODE_1 0x01 -#define KBD_SCANCODE_2 0x02 -#define KBD_SCANCODE_3 0x03 - -#undef DEBUG - -static uint8_t read_data( keyboard_t *keyboard ) -{ - while( ( port8_read( &keyboard->command_port ) & STATUS_REG_OUTPUT_BUF_FULL ) == 0 ) { } - return port8_read( &keyboard->data_port ); -} - -static void send_command( keyboard_t *keyboard, uint8_t command ) -{ - while( port8_read( &keyboard->command_port ) & STATUS_REG_INPUT_BUF_FULL ) { } - port8_write( &keyboard->command_port, command ); -} - -static void write_data( keyboard_t *keyboard, uint8_t data ) -{ - while( port8_read( &keyboard->command_port ) & STATUS_REG_INPUT_BUF_FULL ) { } - port8_write( &keyboard->data_port, data ); -} - -/* -static void read_ack( keyboard_t *keyboard ) -{ - uint8_t data = read_data( keyboard ); - if( data == 0xFA ) { - return; - } else { - kernel_warn( "ERR PS/2 keyboard: acknoledgment failed 0x%X\n", data ); - } -} -*/ - -static keyboard_vtable_t const keyboard_vtable = { - { - keyboard_activate, - keyboard_deactivate, - keyboard_deinit, - keyboard_print_info - } -}; - -void keyboard_init( keyboard_t *keyboard, keyboard_event_handler_t handler, interrupt_t *interrupt, void *context ) -{ - memset( keyboard, 0, sizeof( keyboard_t ) ); - - driver_init( (driver_t *)keyboard, DRIVER_TYPE_KEYBOARD, interrupt, context ); - - port8_init( &keyboard->command_port, 0x64 ); - port8_init( &keyboard->data_port, 0x60 ); - - keyboard->shift = false; - keyboard->handler = handler; - - ((driver_t *)keyboard)->vtable = (driver_vtable_t *)&keyboard_vtable; -} - -void keyboard_activate( void *obj ) -{ - puts( "Activating driver for PS/2 keyboard.." ); - - driver_t *driver = obj; - keyboard_t *keyboard = obj; - - // first switch off port 1 - send_command( keyboard, COMMAND_DISABLE_PORT1 ); - - // consume character pressed before executing the kernel - while( port8_read( &keyboard->command_port ) & STATUS_REG_OUTPUT_BUF_FULL ) { - port8_read( &keyboard->data_port ); - } - - // enable interrupts for port 1 - send_command( keyboard, COMMAND_GET_STATE ); - uint8_t status = read_data( keyboard ); - status |= STATUS_CONTROL_CONFIG_ENABLE_IRQ_PORT1; - status &= ~STATUS_CONTROL_CONFIG_CLOCK_PORT1; - send_command( keyboard, COMMAND_SET_STATE ); - write_data( keyboard, status ); - - // disable all LEDs on keyboard - //~ send_command( keyboard, KBD_COMMAND_SET_LEDS ); - //~ write_data( keyboard, KBD_LED_ALL_ON ); - - // set scan code - //~ send_command( keyboard, KBD_SET_SCANCODE ); - //~ write_data( keyboard, KBD_SCANCODE_2 ); - - // activate keyboard - //send_command( keyboard, KBD_ACTIVATE ); - //read_ack( keyboard ); - - interrupt_handler_init( &keyboard->interrupt_handler, IRQ_BASE + 0x01, driver->interrupt, keyboard_handle_interrupt, obj ); - interrupts_register_interrupt_handler( keyboard->interrupt_handler ); - - // enable port 1 - send_command( keyboard, COMMAND_ENABLE_PORT1 ); -} - -void keyboard_deactivate( void *obj ) -{ - puts( "Deactivating driver for PS/2 keyboard.." ); - - keyboard_t *keyboard = obj; - - send_command( keyboard, COMMAND_DISABLE_PORT1 ); -} - -void keyboard_deinit( void *obj ) -{ - // nothing to do -} - -typedef enum { - KEYCODE_UNKNOWN, - KEYCODE_ASCII_CHAR, - KEYCODE_ESC -} keycode_type_t; - -typedef struct { - keycode_type_t type; - char c; -} keycode_t; - -typedef enum { - SCANCODE_SET_NORMAL = 0, - SCANCODE_SET_SHIFT = 1, - SCANCODE_SET_E0 = 2, - SCANCODE_SET_E1 = 3 -} scancode_set_t; - -// TODO: here come the translations to other keyboard layouts, -// assumming a QWERTY US keyboard here.. -static char scancode_map[4][128] = { - { - 0, 0, '1', '2', '3', '4', '5', '6', - '7', '8', '9', '0', '-', '=', '\b', 0, - 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', - 'o', 'p', '[', ']', '\n', 0, 'a', 's', - 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', - '\'', '`', 0, '\\', 'z', 'x', 'c', 'v', - 'b', 'n', 'm', ',', '.', '/', 0, 0, - 0, ' ', 0, 0, 0, 0, 0, 0, - 0, 0, 0 , 0 , 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0 , 0 , 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0 , 0 , 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0 , 0 , 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0 - }, - { - 0, 0, '!', '@', '#', '$', '%', '^', - '&', '*', '(', ')', '_', '+', 0, 0, - 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', - 'O', 'P', '{', '}', '\n', 0, 'A', 'S', - 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', - '"', '~', 0, '|', 'Z', 'X', 'C', 'V', - 'B', 'N', 'M', '<', '>', '?', 0, 0, - 0, ' ', 0, 0, 0, 0, 0, 0, - 0, 0, 0 , 0 , 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0 , 0 , 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0 , 0 , 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0 , 0 , 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0 - }, - { - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0 - }, - { - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0 - } -}; - -#define SCAN_CODE_ESC 0x01 - -static keycode_t scancode_to_keycode( scancode_set_t set, uint8_t scan_code ) -{ - keycode_t key_code; - key_code.type = KEYCODE_UNKNOWN; - - key_code.c = scancode_map[set][scan_code]; - if( key_code.c > 0 ) { - key_code.type = KEYCODE_ASCII_CHAR; - } - - if( scan_code == SCAN_CODE_ESC ) { - key_code.type = KEYCODE_ESC; - } - - return key_code; -} - -#define SCAN_CODE_BREAK_CODE 0x80 -#define SCAN_CODE_LEFT_SHIFT 0x2A -#define SCAN_CODE_RIGHT_SHIFT 0x36 - -uint32_t keyboard_handle_interrupt( interrupt_handler_t *handler, uint32_t esp ) -{ - keyboard_t *keyboard = (keyboard_t *)handler->driver; - - // we can only wake up on IRQ1 because we don't send commands - // after initialization - uint8_t scan_code = port8_read( &keyboard->data_port ); - - // we get break code when the high bit is set, so the - // the key is released - bool break_code = false; - if( scan_code & SCAN_CODE_BREAK_CODE ) { - break_code = true; - } - - scan_code &= ~SCAN_CODE_BREAK_CODE; - - if( scan_code == SCAN_CODE_LEFT_SHIFT || - scan_code == SCAN_CODE_RIGHT_SHIFT ) { - if( break_code ) { - keyboard->shift = false; - } else { - keyboard->shift = true; - } - } - - scancode_set_t code_set = SCANCODE_SET_NORMAL; - if( keyboard->shift ) { - code_set = SCANCODE_SET_SHIFT; - } - - keycode_t key_code = scancode_to_keycode( code_set, scan_code ); - -#ifdef DEBUG - printf( "KBD SCAN:0x%X S:%d B:%d S:%d T:0x%X '%c' 0x%X\n", - scan_code, code_set, break_code, keyboard->shift, - key_code.type, key_code.c, key_code.c ); -#endif - - keyboard_event_t event; - if( break_code ) { - event.type = KEYBOARD_EVENT_TYPE_KEY_RELEASED; - } else { - event.type = KEYBOARD_EVENT_TYPE_KEY_PRESSED; - } - event.modifiers = 0; - if( keyboard->shift ) { - event.modifiers |= KEYBOARD_MODIFIER_SHIFT; - } - // TODO: for control, alt, meta, etc. - event.key = KEYBOARD_KEY_UNKNOWN; - if( key_code.type == KEYCODE_ASCII_CHAR ) { - event.key = KEYBOARD_KEY_ASCII; - event.ascii_key = key_code.c; - } else if( key_code.type == KEYCODE_ESC ) { - event.key = KEYBOARD_KEY_ESC; - } - - keyboard->handler( &event, keyboard->base.context ); - - return esp; -} - -void keyboard_print_info( void *obj ) -{ - puts( "PS/2 keyboard driver" ); -} diff --git a/src/drivers/hdi/ps2/keyboard.h b/src/drivers/hdi/ps2/keyboard.h deleted file mode 100644 index a0e06ef..0000000 --- a/src/drivers/hdi/ps2/keyboard.h +++ /dev/null @@ -1,69 +0,0 @@ -#ifndef KEYBOARD_H -#define KEYBOARD_H - -#include - -#include "string.h" - -#include "interrupts.h" -#include "port.h" - -#include "driver.h" - -typedef enum { - KEYBOARD_EVENT_TYPE_KEY_PRESSED, - KEYBOARD_EVENT_TYPE_KEY_RELEASED -} keyboard_event_type_t; - -typedef enum { - KEYBOARD_MODIFIER_SHIFT = 0x01, - KEYBOARD_MODIFIER_CTRL = 0x02, - KEYBOARD_MODIFIER_ALT = 0x04 -} keyboard_modifier_mask_t; - -typedef enum { - KEYBOARD_KEY_UNKNOWN, - KEYBOARD_KEY_ASCII, - KEYBOARD_KEY_ESC, - KEYBOARD_KEY_TAB, - KEYBOARD_KEY_INS, - KEYBAORD_KEY_DEL, - KEYBAORD_KEY_BACKSPACE, - KEYBAORD_CURSOR_LEFT, - KEYBOARD_CURSOR_RIGHT, - KEYBOARD_CURSOR_UP, - KEYBOARD_CURSOR_DOWN -} keyboard_key_t; - -typedef struct { - keyboard_event_type_t type; - keyboard_modifier_mask_t modifiers; - keyboard_key_t key; - char ascii_key; -} keyboard_event_t; - -typedef void (*keyboard_event_handler_t)( keyboard_event_t *event, void *context ); - -typedef struct { - driver_t base; - interrupt_t *interrupts; - port8_t command_port; - port8_t data_port; - bool shift; - keyboard_event_handler_t handler; - interrupt_handler_t interrupt_handler; -} keyboard_t; - -typedef struct { - driver_vtable_t base; -} keyboard_vtable_t; - -void keyboard_init( keyboard_t *keyboard, keyboard_event_handler_t handler, interrupt_t *interrupt, void *context ); -void keyboard_activate( void *obj ); -void keyboard_deactivate( void *obj ); -void keyboard_deinit( void *obj ); -void keyboard_print_info( void *obj ); - -uint32_t keyboard_handle_interrupt( interrupt_handler_t *handler, uint32_t esp ); - -#endif // KEYBOARD_H diff --git a/src/drivers/hdi/ps2/ps2keyboard.c b/src/drivers/hdi/ps2/ps2keyboard.c new file mode 100644 index 0000000..4151c56 --- /dev/null +++ b/src/drivers/hdi/ps2/ps2keyboard.c @@ -0,0 +1,331 @@ +#include "ps2keyboard.h" +#include "stdio.h" + +// status register on command port (read) +#define STATUS_REG_OUTPUT_BUF_FULL 0x01 +#define STATUS_REG_INPUT_BUF_FULL 0x02 + +// generic PS/2 commands +#define COMMAND_GET_STATE 0x20 +#define COMMAND_SET_STATE 0x60 +#define COMMAND_DISABLE_PORT1 0xAD +#define COMMAND_ENABLE_PORT1 0xAE + +// status control register flags +#define STATUS_CONTROL_CONFIG_ENABLE_IRQ_PORT1 0x01 +#define STATUS_CONTROL_CONFIG_CLOCK_PORT1 0x10 + +// keyboard specific commands, PSAUX1 +#define KBD_COMMAND_SET_LEDS 0xED +#define KBD_SET_SCANCODE 0xF0 +#define KBD_ACTIVATE 0xF4 + +// keyboard LED status codes +#define KBD_LED_SCROLL_LOCK_ON 0x01 +#define KBD_LED_NUM_LOCK_ON 0x02 +#define KBD_LED_CAPS_LOCK_ON 0x04 +#define KBD_LED_ALL_OFF 0x00 +#define KBD_LED_ALL_ON KBD_LED_SCROLL_LOCK_ON | KBD_LED_NUM_LOCK_ON | KBD_LED_CAPS_LOCK_ON + +// keyboard scan codes +#define KBD_SCANCODE_1 0x01 +#define KBD_SCANCODE_2 0x02 +#define KBD_SCANCODE_3 0x03 + +#undef DEBUG + +static uint8_t read_data( ps2keyboard_t *ps2keyboard ) +{ + while( ( port8_read( &ps2keyboard->command_port ) & STATUS_REG_OUTPUT_BUF_FULL ) == 0 ) { } + return port8_read( &ps2keyboard->data_port ); +} + +static void send_command( ps2keyboard_t *ps2keyboard, uint8_t command ) +{ + while( port8_read( &ps2keyboard->command_port ) & STATUS_REG_INPUT_BUF_FULL ) { } + port8_write( &ps2keyboard->command_port, command ); +} + +static void write_data( ps2keyboard_t *ps2keyboard, uint8_t data ) +{ + while( port8_read( &ps2keyboard->command_port ) & STATUS_REG_INPUT_BUF_FULL ) { } + port8_write( &ps2keyboard->data_port, data ); +} + +/* +static void read_ack( ps2keyboard_t *ps2keyboard ) +{ + uint8_t data = read_data( ps2keyboard ); + if( data == 0xFA ) { + return; + } else { + kernel_warn( "ERR PS/2 keyboard: acknoledgment failed 0x%X\n", data ); + } +} +*/ + +static keyboard_vtable_t const ps2keyboard_vtable = { + { + ps2keyboard_activate, + ps2keyboard_deactivate, + ps2keyboard_deinit, + ps2keyboard_print_info + } +}; + +void ps2keyboard_init( ps2keyboard_t *ps2keyboard, keyboard_event_handler_t handler, interrupt_t *interrupt, void *context ) +{ + memset( ps2keyboard, 0, sizeof( ps2keyboard_t ) ); + + driver_init( (driver_t *)ps2keyboard, DRIVER_TYPE_KEYBOARD, interrupt, context ); + + port8_init( &ps2keyboard->command_port, 0x64 ); + port8_init( &ps2keyboard->data_port, 0x60 ); + + ps2keyboard->shift = false; + ps2keyboard->base.handler = handler; + + ((driver_t *)ps2keyboard)->vtable = (driver_vtable_t *)&ps2keyboard_vtable; +} + +void ps2keyboard_activate( void *obj ) +{ + puts( "Activating driver for PS/2 keyboard.." ); + + driver_t *driver = obj; + ps2keyboard_t *ps2keyboard = obj; + + // first switch off port 1 + send_command( ps2keyboard, COMMAND_DISABLE_PORT1 ); + + // consume character pressed before executing the kernel + while( port8_read( &ps2keyboard->command_port ) & STATUS_REG_OUTPUT_BUF_FULL ) { + port8_read( &ps2keyboard->data_port ); + } + + // enable interrupts for port 1 + send_command( ps2keyboard, COMMAND_GET_STATE ); + uint8_t status = read_data( ps2keyboard ); + status |= STATUS_CONTROL_CONFIG_ENABLE_IRQ_PORT1; + status &= ~STATUS_CONTROL_CONFIG_CLOCK_PORT1; + send_command( ps2keyboard, COMMAND_SET_STATE ); + write_data( ps2keyboard, status ); + + // disable all LEDs on keyboard + //~ send_command( ps2keyboard, KBD_COMMAND_SET_LEDS ); + //~ write_data( ps2keyboard, KBD_LED_ALL_ON ); + + // set scan code + //~ send_command( ps2keyboard, KBD_SET_SCANCODE ); + //~ write_data( ps2keyboard, KBD_SCANCODE_2 ); + + // activate keyboard + //send_command( ps2keyboard, KBD_ACTIVATE ); + //read_ack( ps2keyboard ); + + interrupt_handler_init( &ps2keyboard->interrupt_handler, IRQ_BASE + 0x01, driver->interrupt, ps2keyboard_handle_interrupt, obj ); + interrupts_register_interrupt_handler( ps2keyboard->interrupt_handler ); + + // enable port 1 + send_command( ps2keyboard, COMMAND_ENABLE_PORT1 ); +} + +void ps2keyboard_deactivate( void *obj ) +{ + puts( "Deactivating driver for PS/2 keyboard.." ); + + ps2keyboard_t *ps2keyboard = obj; + + send_command( ps2keyboard, COMMAND_DISABLE_PORT1 ); +} + +void ps2keyboard_deinit( void *obj ) +{ + // nothing to do +} + +typedef enum { + KEYCODE_UNKNOWN, + KEYCODE_ASCII_CHAR, + KEYCODE_ESC +} keycode_type_t; + +typedef struct { + keycode_type_t type; + char c; +} keycode_t; + +typedef enum { + SCANCODE_SET_NORMAL = 0, + SCANCODE_SET_SHIFT = 1, + SCANCODE_SET_E0 = 2, + SCANCODE_SET_E1 = 3 +} scancode_set_t; + +// TODO: here come the translations to other keyboard layouts, +// assumming a QWERTY US keyboard here.. +static char scancode_map[4][128] = { + { + 0, 0, '1', '2', '3', '4', '5', '6', + '7', '8', '9', '0', '-', '=', '\b', 0, + 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', + 'o', 'p', '[', ']', '\n', 0, 'a', 's', + 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', + '\'', '`', 0, '\\', 'z', 'x', 'c', 'v', + 'b', 'n', 'm', ',', '.', '/', 0, 0, + 0, ' ', 0, 0, 0, 0, 0, 0, + 0, 0, 0 , 0 , 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0 , 0 , 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0 , 0 , 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0 , 0 , 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 + }, + { + 0, 0, '!', '@', '#', '$', '%', '^', + '&', '*', '(', ')', '_', '+', 0, 0, + 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', + 'O', 'P', '{', '}', '\n', 0, 'A', 'S', + 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', + '"', '~', 0, '|', 'Z', 'X', 'C', 'V', + 'B', 'N', 'M', '<', '>', '?', 0, 0, + 0, ' ', 0, 0, 0, 0, 0, 0, + 0, 0, 0 , 0 , 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0 , 0 , 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0 , 0 , 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0 , 0 , 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 + }, + { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 + }, + { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 + } +}; + +#define SCAN_CODE_ESC 0x01 + +static keycode_t scancode_to_keycode( scancode_set_t set, uint8_t scan_code ) +{ + keycode_t key_code; + key_code.type = KEYCODE_UNKNOWN; + + key_code.c = scancode_map[set][scan_code]; + if( key_code.c > 0 ) { + key_code.type = KEYCODE_ASCII_CHAR; + } + + if( scan_code == SCAN_CODE_ESC ) { + key_code.type = KEYCODE_ESC; + } + + return key_code; +} + +#define SCAN_CODE_BREAK_CODE 0x80 +#define SCAN_CODE_LEFT_SHIFT 0x2A +#define SCAN_CODE_RIGHT_SHIFT 0x36 + +uint32_t ps2keyboard_handle_interrupt( interrupt_handler_t *handler, uint32_t esp ) +{ + ps2keyboard_t *ps2keyboard = (ps2keyboard_t *)handler->driver; + + // we can only wake up on IRQ1 because we don't send commands + // after initialization + uint8_t scan_code = port8_read( &ps2keyboard->data_port ); + + // we get break code when the high bit is set, so the + // the key is released + bool break_code = false; + if( scan_code & SCAN_CODE_BREAK_CODE ) { + break_code = true; + } + + scan_code &= ~SCAN_CODE_BREAK_CODE; + + if( scan_code == SCAN_CODE_LEFT_SHIFT || + scan_code == SCAN_CODE_RIGHT_SHIFT ) { + if( break_code ) { + ps2keyboard->shift = false; + } else { + ps2keyboard->shift = true; + } + } + + scancode_set_t code_set = SCANCODE_SET_NORMAL; + if( ps2keyboard->shift ) { + code_set = SCANCODE_SET_SHIFT; + } + + keycode_t key_code = scancode_to_keycode( code_set, scan_code ); + +#ifdef DEBUG + printf( "KBD SCAN:0x%X S:%d B:%d S:%d T:0x%X '%c' 0x%X\n", + scan_code, code_set, break_code, ps2keyboard->shift, + key_code.type, key_code.c, key_code.c ); +#endif + + keyboard_event_t event; + if( break_code ) { + event.type = KEYBOARD_EVENT_TYPE_KEY_RELEASED; + } else { + event.type = KEYBOARD_EVENT_TYPE_KEY_PRESSED; + } + event.modifiers = 0; + if( ps2keyboard->shift ) { + event.modifiers |= KEYBOARD_MODIFIER_SHIFT; + } + // TODO: for control, alt, meta, etc. + event.key = KEYBOARD_KEY_UNKNOWN; + if( key_code.type == KEYCODE_ASCII_CHAR ) { + event.key = KEYBOARD_KEY_ASCII; + event.ascii_key = key_code.c; + } else if( key_code.type == KEYCODE_ESC ) { + event.key = KEYBOARD_KEY_ESC; + } + + ps2keyboard->base.handler( &event, ps2keyboard->base.base.context ); + + return esp; +} + +void ps2keyboard_print_info( void *obj ) +{ + puts( "PS/2 keyboard driver" ); +} diff --git a/src/drivers/hdi/ps2/ps2keyboard.h b/src/drivers/hdi/ps2/ps2keyboard.h new file mode 100644 index 0000000..a5b66ca --- /dev/null +++ b/src/drivers/hdi/ps2/ps2keyboard.h @@ -0,0 +1,33 @@ +#ifndef PS2KEYBOARD_H +#define PS2KEYBOARD_H + +#include + +#include "string.h" + +#include "interrupts.h" +#include "port.h" + +#include "keyboard.h" + +typedef struct { + keyboard_t base; + port8_t command_port; + port8_t data_port; + bool shift; + interrupt_handler_t interrupt_handler; +} ps2keyboard_t; + +typedef struct { + keyboard_vtable_t base; +} ps2keyboard_vtable_t; + +void ps2keyboard_init( ps2keyboard_t *ps2keyboard, keyboard_event_handler_t handler, interrupt_t *interrupt, void *context ); +void ps2keyboard_activate( void *obj ); +void ps2keyboard_deactivate( void *obj ); +void ps2keyboard_deinit( void *obj ); +void ps2keyboard_print_info( void *obj ); + +uint32_t ps2keyboard_handle_interrupt( interrupt_handler_t *handler, uint32_t esp ); + +#endif // PS2KEYBOARD_H diff --git a/src/kernel/kernel.c b/src/kernel/kernel.c index 9bb1054..91d9988 100644 --- a/src/kernel/kernel.c +++ b/src/kernel/kernel.c @@ -11,7 +11,7 @@ #include "stdio.h" #include "interrupts.h" #include "driver.h" -#include "keyboard.h" +#include "ps2keyboard.h" #include "ps2mouse.h" #include "pci.h" #include "memorymanagement.h" @@ -129,8 +129,8 @@ void kernel_main( void ) // hard-wired drivers - global_context.keyboard = (keyboard_t *)malloc( sizeof( keyboard_t ) ); - keyboard_init( global_context.keyboard, &handle_keyboard_event, &interrupt, (void *)&global_context ); + global_context.keyboard = (keyboard_t *)malloc( sizeof( ps2keyboard_t ) ); + ps2keyboard_init( (ps2keyboard_t *)global_context.keyboard, &handle_keyboard_event, &interrupt, (void *)&global_context ); driver_manager_add_driver( &global_context.driver_manager, (driver_t *)global_context.keyboard ); global_context.mouse = (mouse_t *)malloc( sizeof( ps2mouse_t ) ); -- cgit v1.2.3-54-g00ecf