Palacios Public Git Repository

To checkout Palacios execute

  git clone http://v3vee.org/palacios/palacios.web/palacios.git
This will give you the master branch. You probably want the devel branch or one of the release branches. To switch to the devel branch, simply execute
  cd palacios
  git checkout --track -b devel origin/devel
The other branches are similar.


Release 1.0
[palacios.git] / geekos / src / geekos / keyboard.c
diff --git a/geekos/src/geekos/keyboard.c b/geekos/src/geekos/keyboard.c
new file mode 100644 (file)
index 0000000..501359e
--- /dev/null
@@ -0,0 +1,377 @@
+/*
+ * Keyboard driver
+ * Copyright (c) 2001,2004 David H. Hovemeyer <daveho@cs.umd.edu>
+ * $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 <peter@pcswebdesign.nl> 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 <geekos/kthread.h>
+#include <geekos/kassert.h>
+#include <geekos/screen.h>
+#include <geekos/irq.h>
+#include <geekos/io.h>
+#include <geekos/keyboard.h>
+
+
+#include <geekos/vmm_stubs.h>
+
+
+
+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;
+}