X-Git-Url: http://v3vee.org/palacios/gitweb/gitweb.cgi?p=palacios.git;a=blobdiff_plain;f=geekos%2Fsrc%2Fgeekos%2Fkeyboard.c;fp=geekos%2Fsrc%2Fgeekos%2Fkeyboard.c;h=501359e2a510912e25ef4e65df675bf16409c0ac;hp=0000000000000000000000000000000000000000;hb=ddc16b0737cf58f7aa90a69c6652cdf4090aec51;hpb=626595465a2c6987606a6bc697df65130ad8c2d3 diff --git a/geekos/src/geekos/keyboard.c b/geekos/src/geekos/keyboard.c new file mode 100644 index 0000000..501359e --- /dev/null +++ b/geekos/src/geekos/keyboard.c @@ -0,0 +1,377 @@ +/* + * Keyboard driver + * Copyright (c) 2001,2004 David H. Hovemeyer + * $Revision: 1.8 $ + * + * This is free software. You are permitted to use, + * redistribute, and modify it as specified in the file "COPYING". + */ + +/* + * Information sources: + * - Chapter 8 of _The Undocumented PC_, 2nd ed, by Frank van Gilluwe, + * ISBN 0-201-47950-8. + * - Pages 400-409 of _The Programmers PC Sourcebook_, by Thom Hogan, + * ISBN 1-55615-118-7. + */ + +/* + * Credits: + * - Peter Gnodde added support for + * the CTRL and ALT modifiers + */ + +/* + * TODO list: + * - Right now we're assuming an 83-key keyboard. + * Should add support for 101+ keyboards. + * - Should toggle keyboard LEDs. + */ + +#include +#include +#include +#include +#include +#include + + +#include + + + +static enum {TARGET_GEEKOS, TARGET_VMM} target = TARGET_VMM; + + + + +/* ---------------------------------------------------------------------- + * Private data and functions + * ---------------------------------------------------------------------- */ + +/* + * Current shift state. + */ +#define LEFT_SHIFT 0x01 +#define RIGHT_SHIFT 0x02 +#define LEFT_CTRL 0x04 +#define RIGHT_CTRL 0x08 +#define LEFT_ALT 0x10 +#define RIGHT_ALT 0x20 +#define SHIFT_MASK (LEFT_SHIFT | RIGHT_SHIFT) +#define CTRL_MASK (LEFT_CTRL | RIGHT_CTRL) +#define ALT_MASK (LEFT_ALT | RIGHT_ALT) +static unsigned s_shiftState = 0; + +/* + * Queue for keycodes, in case they arrive faster than consumer + * can deal with them. + */ +#define QUEUE_SIZE 256 +#define QUEUE_MASK 0xff +#define NEXT(index) (((index) + 1) & QUEUE_MASK) +static Keycode s_queue[QUEUE_SIZE]; +static int s_queueHead, s_queueTail; + +/* + * Wait queue for thread(s) waiting for keyboard events. + */ +static struct Thread_Queue s_waitQueue; + +/* + * Translate from scan code to key code, when shift is not pressed. + */ +static const Keycode s_scanTableNoShift[] = { + KEY_UNKNOWN, ASCII_ESC, '1', '2', /* 0x00 - 0x03 */ + '3', '4', '5', '6', /* 0x04 - 0x07 */ + '7', '8', '9', '0', /* 0x08 - 0x0B */ + '-', '=', ASCII_BS, '\t', /* 0x0C - 0x0F */ + 'q', 'w', 'e', 'r', /* 0x10 - 0x13 */ + 't', 'y', 'u', 'i', /* 0x14 - 0x17 */ + 'o', 'p', '[', ']', /* 0x18 - 0x1B */ + '\r', KEY_LCTRL, 'a', 's', /* 0x1C - 0x1F */ + 'd', 'f', 'g', 'h', /* 0x20 - 0x23 */ + 'j', 'k', 'l', ';', /* 0x24 - 0x27 */ + '\'', '`', KEY_LSHIFT, '\\', /* 0x28 - 0x2B */ + 'z', 'x', 'c', 'v', /* 0x2C - 0x2F */ + 'b', 'n', 'm', ',', /* 0x30 - 0x33 */ + '.', '/', KEY_RSHIFT, KEY_PRINTSCRN, /* 0x34 - 0x37 */ + KEY_LALT, ' ', KEY_CAPSLOCK, KEY_F1, /* 0x38 - 0x3B */ + KEY_F2, KEY_F3, KEY_F4, KEY_F5, /* 0x3C - 0x3F */ + KEY_F6, KEY_F7, KEY_F8, KEY_F9, /* 0x40 - 0x43 */ + KEY_F10, KEY_NUMLOCK, KEY_SCRLOCK, KEY_KPHOME, /* 0x44 - 0x47 */ + KEY_KPUP, KEY_KPPGUP, KEY_KPMINUS, KEY_KPLEFT, /* 0x48 - 0x4B */ + KEY_KPCENTER, KEY_KPRIGHT, KEY_KPPLUS, KEY_KPEND, /* 0x4C - 0x4F */ + KEY_KPDOWN, KEY_KPPGDN, KEY_KPINSERT, KEY_KPDEL, /* 0x50 - 0x53 */ + KEY_SYSREQ, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, /* 0x54 - 0x57 */ +}; +#define SCAN_TABLE_SIZE (sizeof(s_scanTableNoShift) / sizeof(Keycode)) + +/* + * Translate from scan code to key code, when shift *is* pressed. + * Keep this in sync with the unshifted table above! + * They must be the same size. + */ +static const Keycode s_scanTableWithShift[] = { + KEY_UNKNOWN, ASCII_ESC, '!', '@', /* 0x00 - 0x03 */ + '#', '$', '%', '^', /* 0x04 - 0x07 */ + '&', '*', '(', ')', /* 0x08 - 0x0B */ + '_', '+', ASCII_BS, '\t', /* 0x0C - 0x0F */ + 'Q', 'W', 'E', 'R', /* 0x10 - 0x13 */ + 'T', 'Y', 'U', 'I', /* 0x14 - 0x17 */ + 'O', 'P', '{', '}', /* 0x18 - 0x1B */ + '\r', KEY_LCTRL, 'A', 'S', /* 0x1C - 0x1F */ + 'D', 'F', 'G', 'H', /* 0x20 - 0x23 */ + 'J', 'K', 'L', ':', /* 0x24 - 0x27 */ + '"', '~', KEY_LSHIFT, '|', /* 0x28 - 0x2B */ + 'Z', 'X', 'C', 'V', /* 0x2C - 0x2F */ + 'B', 'N', 'M', '<', /* 0x30 - 0x33 */ + '>', '?', KEY_RSHIFT, KEY_PRINTSCRN, /* 0x34 - 0x37 */ + KEY_LALT, ' ', KEY_CAPSLOCK, KEY_F1, /* 0x38 - 0x3B */ + KEY_F2, KEY_F3, KEY_F4, KEY_F5, /* 0x3C - 0x3F */ + KEY_F6, KEY_F7, KEY_F8, KEY_F9, /* 0x40 - 0x43 */ + KEY_F10, KEY_NUMLOCK, KEY_SCRLOCK, KEY_KPHOME, /* 0x44 - 0x47 */ + KEY_KPUP, KEY_KPPGUP, KEY_KPMINUS, KEY_KPLEFT, /* 0x48 - 0x4B */ + KEY_KPCENTER, KEY_KPRIGHT, KEY_KPPLUS, KEY_KPEND, /* 0x4C - 0x4F */ + KEY_KPDOWN, KEY_KPPGDN, KEY_KPINSERT, KEY_KPDEL, /* 0x50 - 0x53 */ + KEY_SYSREQ, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, /* 0x54 - 0x57 */ +}; + +static __inline__ bool Is_Queue_Empty(void) +{ + return s_queueHead == s_queueTail; +} + +static __inline__ bool Is_Queue_Full(void) +{ + return NEXT(s_queueTail) == s_queueHead; +} + +static __inline__ void Enqueue_Keycode(Keycode keycode) +{ + if (!Is_Queue_Full()) { + s_queue[ s_queueTail ] = keycode; + s_queueTail = NEXT(s_queueTail); + } +} + +static __inline__ Keycode Dequeue_Keycode(void) +{ + Keycode result; + KASSERT(!Is_Queue_Empty()); + result = s_queue[ s_queueHead ]; + s_queueHead = NEXT(s_queueHead); + return result; +} + +/* + * Handler for keyboard interrupts. + */ +static void Keyboard_Interrupt_Handler(struct Interrupt_State* state) +{ + uchar_t raw_status, raw_scancode; + + uchar_t status, scanCode; + unsigned flag = 0; + bool release = false, shift; + Keycode keycode; + bool flagchange; + + Begin_IRQ(state); + + //Print("Keyboard\n"); + + + status = In_Byte(KB_CMD); + + raw_status=status; + + IO_Delay(); + + if ((status & KB_OUTPUT_FULL) != 0) { + /* There is a byte available */ + scanCode = In_Byte(KB_DATA); + raw_scancode=scanCode; + //Print("Keyboard: status=0x%x, scancode=0x%x\n", raw_status, raw_scancode); + IO_Delay(); + /* + * Print("code=%x%s\n", scanCode, (scanCode&0x80) ? " [release]" : ""); + */ + + if (scanCode & KB_KEY_RELEASE) { + release = true; + scanCode &= ~(KB_KEY_RELEASE); + } + + if (scanCode >= SCAN_TABLE_SIZE) { + Print("Unknown scan code: %x\n", scanCode); + goto done; + } + + /* Process the key */ + shift = ((s_shiftState & SHIFT_MASK) != 0); + keycode = shift ? s_scanTableWithShift[scanCode] : s_scanTableNoShift[scanCode]; + + flagchange=false; + + /* Update shift, control and alt state */ + switch (keycode) { + case KEY_LSHIFT: + flag = LEFT_SHIFT; + break; + case KEY_RSHIFT: + flag = RIGHT_SHIFT; + break; + case KEY_LCTRL: + flag = LEFT_CTRL; + break; + case KEY_RCTRL: + flag = RIGHT_CTRL; + break; + case KEY_LALT: + flag = LEFT_ALT; + break; + case KEY_RALT: + flag = RIGHT_ALT; + break; + default: + goto noflagchange; + } + + if (release) + s_shiftState &= ~(flag); + else + s_shiftState |= flag; + + /* + * Shift, control and alt keys don't have to be + * queued, flags will be set! + */ + // huh? + flagchange=true; + goto skip_flagchange; + +noflagchange: + /* Format the new keycode */ + if (shift) + keycode |= KEY_SHIFT_FLAG; + if ((s_shiftState & CTRL_MASK) != 0) + keycode |= KEY_CTRL_FLAG; + if ((s_shiftState & ALT_MASK) != 0) + keycode |= KEY_ALT_FLAG; + if (release) + keycode |= KEY_RELEASE_FLAG; + + +skip_flagchange: + + if (target == TARGET_GEEKOS) { + if (raw_scancode==0xc4) { // F10 release + Print("Switching keyboard to VMM\n"); + target=TARGET_VMM; + } else { + if (flagchange) { + goto done; + } + /* Put the keycode in the buffer */ + Enqueue_Keycode(keycode); + + /* Wake up event consumers */ + Wake_Up(&s_waitQueue); + + /* + * Pick a new thread upon return from interrupt + * (hopefully the one waiting for the keyboard event) + */ + g_needReschedule = true; + } + } else if (target == TARGET_VMM) { + + if (raw_scancode == 0xc4) { // F10 release + Print("Switching keyboard to GeekOS\n"); + target = TARGET_GEEKOS; + } else { + send_key_to_vmm(raw_status, raw_scancode); + } + + } + } + + done: + End_IRQ(state); + +} + +/* ---------------------------------------------------------------------- + * Public functions + * ---------------------------------------------------------------------- */ + +void Init_Keyboard(void) +{ + ushort_t irqMask; + + Print("Initializing keyboard...\n"); + + /* Start out with no shift keys enabled. */ + s_shiftState = 0; + + /* Buffer is initially empty. */ + s_queueHead = s_queueTail = 0; + + /* Install interrupt handler */ + Install_IRQ(KB_IRQ, Keyboard_Interrupt_Handler); + + /* Enable IRQ1 (keyboard) */ + irqMask = Get_IRQ_Mask(); + irqMask &= ~(1 << KB_IRQ); + Set_IRQ_Mask(irqMask); +} + +/* + * Poll for a key event. + * Returns true if a key is available, + * false if not. If a key event is available, + * it will be stored in the location pointed to + * by keycode. + */ +bool Read_Key(Keycode* keycode) +{ + bool result, iflag; + + iflag = Begin_Int_Atomic(); + + result = !Is_Queue_Empty(); + if (result) { + *keycode = Dequeue_Keycode(); + } + + End_Int_Atomic(iflag); + + return result; +} + +/* + * Wait for a keycode to arrive. + * Uses the keyboard wait queue to sleep until + * a keycode arrives. + */ +Keycode Wait_For_Key(void) +{ + bool gotKey, iflag; + Keycode keycode = KEY_UNKNOWN; + + iflag = Begin_Int_Atomic(); + + do { + gotKey = !Is_Queue_Empty(); + if (gotKey) + keycode = Dequeue_Keycode(); + else + Wait(&s_waitQueue); + } + while (!gotKey); + + End_Int_Atomic(iflag); + + return keycode; +}