/* * Keyboard driver * Copyright (c) 2001,2004 David H. Hovemeyer * $Revision: 1.1 $ * * 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 /* ---------------------------------------------------------------------- * 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 status, scanCode; unsigned flag = 0; bool release = false, shift; Keycode keycode; Begin_IRQ(state); status = In_Byte(KB_CMD); IO_Delay(); if ((status & KB_OUTPUT_FULL) != 0) { /* There is a byte available */ scanCode = In_Byte(KB_DATA); 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]; /* 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! */ goto done; 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; /* 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; } 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; }