altos: Add PS/2 keyboard driver
authorKeith Packard <keithp@keithp.com>
Mon, 20 Feb 2017 20:19:42 +0000 (12:19 -0800)
committerKeith Packard <keithp@keithp.com>
Mon, 20 Feb 2017 20:34:02 +0000 (12:34 -0800)
Interrupt driven, includes standard US keymap.

Signed-off-by: Keith Packard <keithp@keithp.com>
src/drivers/ao_ps2.c [new file with mode: 0644]
src/drivers/ao_ps2.h [new file with mode: 0644]

diff --git a/src/drivers/ao_ps2.c b/src/drivers/ao_ps2.c
new file mode 100644 (file)
index 0000000..29eecea
--- /dev/null
@@ -0,0 +1,419 @@
+/*
+ * Copyright © 2016 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+#include "ao.h"
+#include "ao_ps2.h"
+#include "ao_exti.h"
+
+static struct ao_fifo  ao_ps2_rx_fifo;
+
+static uint16_t                ao_ps2_tx;
+static uint8_t         ao_ps2_tx_count;
+
+static AO_TICK_TYPE    ao_ps2_tick;
+static uint16_t                ao_ps2_value;
+static uint8_t         ao_ps2_count;
+
+uint8_t                        ao_ps2_stdin;
+
+uint8_t                        ao_ps2_scancode_set;
+
+#define AO_PS2_CLOCK_MODE(pull) ((pull) | AO_EXTI_MODE_FALLING | AO_EXTI_PRIORITY_MED)
+
+static void
+ao_ps2_isr(void);
+
+static uint8_t
+_ao_ps2_parity(uint8_t value)
+{
+       uint8_t parity = 1;
+       uint8_t b;
+
+       for (b = 0; b < 8; b++) {
+               parity ^= (value & 1);
+               value >>= 1;
+       }
+       return parity;
+}
+
+static int
+_ao_ps2_poll(void)
+{
+       uint8_t u;
+       if (ao_fifo_empty(ao_ps2_rx_fifo)) {
+               return AO_READ_AGAIN;
+       }
+       ao_fifo_remove(ao_ps2_rx_fifo, u);
+
+       return (int) u;
+}
+
+uint8_t
+ao_ps2_get(void)
+{
+       int c;
+       ao_arch_block_interrupts();
+       while ((c = _ao_ps2_poll()) == AO_READ_AGAIN)
+               ao_sleep(&ao_ps2_rx_fifo);
+       ao_arch_release_interrupts();
+       return (uint8_t) c;
+}
+
+
+int
+ao_ps2_poll(void)
+{
+       int     c;
+       ao_arch_block_interrupts();
+       c = _ao_ps2_poll();
+       ao_arch_release_interrupts();
+       return (uint8_t) c;
+}
+
+void
+ao_ps2_put(uint8_t c)
+{
+       ao_arch_block_interrupts();
+       ao_ps2_tx = ((uint16_t) c) | (_ao_ps2_parity(c) << 8) | (3 << 9);
+       ao_ps2_tx_count = 11;
+       ao_exti_disable(AO_PS2_CLOCK_PORT, AO_PS2_CLOCK_BIT);
+       ao_arch_release_interrupts();
+
+       /* pull the clock pin down */
+       ao_enable_output(AO_PS2_CLOCK_PORT, AO_PS2_CLOCK_BIT, AO_PS2_CLOCK_PIN, 0);
+       ao_delay(0);
+
+       /* pull the data pin down for the start bit */
+       ao_enable_output(AO_PS2_DATA_PORT, AO_PS2_DATA_BIT, AO_PS2_DATA_PIN, 0);
+       ao_delay(0);
+
+       /* switch back to input mode for the interrupt to work */
+       ao_exti_setup(AO_PS2_CLOCK_PORT, AO_PS2_CLOCK_BIT,
+                     AO_PS2_CLOCK_MODE(AO_EXTI_MODE_PULL_UP),
+                     ao_ps2_isr);
+       ao_exti_enable(AO_PS2_CLOCK_PORT, AO_PS2_CLOCK_BIT);
+
+       /* wait for the bits to drain */
+       while (ao_ps2_tx_count)
+               ao_sleep(&ao_ps2_tx_count);
+
+}
+
+static uint8_t ao_ps2_down[128 / 8];
+
+static void
+ao_ps2_set_down(uint8_t code, uint8_t value)
+{
+       uint8_t shift = (code & 0x07);
+       uint8_t byte = code >> 3;
+
+       ao_ps2_down[byte] = (ao_ps2_down[byte] & ~(1 << shift)) | (value << shift);
+}
+
+uint8_t
+ao_ps2_is_down(uint8_t code)
+{
+       uint8_t shift = (code & 0x07);
+       uint8_t byte = code >> 3;
+
+       return (ao_ps2_down[byte] >> shift) & 1;
+}
+
+static void
+_ao_ps2_set_leds(void)
+{
+       uint8_t led = 0;
+       if (ao_ps2_is_down(AO_PS2_CAPS_LOCK))
+               led |= AO_PS2_SET_LEDS_CAPS;
+       if (ao_ps2_is_down(AO_PS2_NUM_LOCK))
+               led |= AO_PS2_SET_LEDS_NUM;
+       if (ao_ps2_is_down(AO_PS2_SCROLL_LOCK))
+               led |= AO_PS2_SET_LEDS_SCROLL;
+       ao_arch_release_interrupts();
+       ao_ps2_put(AO_PS2_SET_LEDS);
+       while (ao_ps2_get() != 0xfa);
+       ao_ps2_put(led);
+       ao_arch_block_interrupts();
+}
+
+static uint8_t
+ao_ps2_is_lock(uint8_t code) {
+       switch (code) {
+       case AO_PS2_CAPS_LOCK:
+       case AO_PS2_NUM_LOCK:
+       case AO_PS2_SCROLL_LOCK:
+               return 1;
+       }
+       return 0;
+}
+
+static void
+_ao_ps2_set_scancode_set(uint8_t set)
+{
+       ao_ps2_scancode_set = set;
+       ao_arch_release_interrupts();
+       ao_ps2_put(AO_PS2_SET_SCAN_CODE_SET);
+       while (ao_ps2_get() != 0xfa);
+       ao_ps2_put(set);
+       ao_ps2_put(AO_PS2_SET_KEY_TYPEMATIC_MAKE_BREAK);
+       while (ao_ps2_get() != 0xfa);
+       ao_arch_block_interrupts();
+}
+
+static int
+_ao_ps2_poll_key(void)
+{
+       int     c;
+       uint8_t set_led = 0;
+       static uint8_t  saw_break;
+
+       c = _ao_ps2_poll();
+       if (c < 0) {
+               if (ao_ps2_scancode_set != 3) {
+                       _ao_ps2_set_scancode_set(3);
+               }
+               return c;
+       }
+
+       if (c == AO_PS2_BREAK) {
+               saw_break = 1;
+               return AO_READ_AGAIN;
+       }
+       if (c & 0x80)
+               return AO_READ_AGAIN;
+
+       if (ao_ps2_is_lock(c)) {
+               if (saw_break) {
+                       saw_break = 0;
+                       return AO_READ_AGAIN;
+               }
+               if (ao_ps2_is_down(c))
+                       saw_break = 1;
+               set_led = 1;
+       }
+       if (saw_break) {
+               saw_break = 0;
+               ao_ps2_set_down(c, 0);
+               c |= 0x80;
+       } else
+               ao_ps2_set_down(c, 1);
+       if (set_led)
+               _ao_ps2_set_leds();
+
+       if (ao_ps2_scancode_set != 3)
+               _ao_ps2_set_scancode_set(3);
+
+       return c;
+}
+
+int
+ao_ps2_poll_key(void)
+{
+       int     c;
+       ao_arch_block_interrupts();
+       c = _ao_ps2_poll_key();
+       ao_arch_release_interrupts();
+       return c;
+}
+
+uint8_t
+ao_ps2_get_key(void)
+{
+       int     c;
+       ao_arch_block_interrupts();
+       while ((c = _ao_ps2_poll_key()) == AO_READ_AGAIN)
+               ao_sleep(&ao_ps2_rx_fifo);
+       ao_arch_release_interrupts();
+       return (uint8_t) c;
+}
+
+static const uint8_t   ao_ps2_asciimap[128][2] = {
+       [AO_PS2_A] = { 'a', 'A' },
+       [AO_PS2_B] = { 'b', 'B' },
+       [AO_PS2_C] = { 'c', 'C' },
+       [AO_PS2_D] = { 'd', 'D' },
+       [AO_PS2_E] = { 'e', 'E' },
+       [AO_PS2_F] = { 'f', 'F' },
+       [AO_PS2_G] = { 'g', 'G' },
+       [AO_PS2_H] = { 'h', 'H' },
+       [AO_PS2_I] = { 'i', 'I' },
+       [AO_PS2_J] = { 'j', 'J' },
+       [AO_PS2_K] = { 'k', 'K' },
+       [AO_PS2_L] = { 'l', 'L' },
+       [AO_PS2_M] = { 'm', 'M' },
+       [AO_PS2_N] = { 'n', 'N' },
+       [AO_PS2_O] = { 'o', 'O' },
+       [AO_PS2_P] = { 'p', 'P' },
+       [AO_PS2_Q] = { 'q', 'Q' },
+       [AO_PS2_R] = { 'r', 'R' },
+       [AO_PS2_S] = { 's', 'S' },
+       [AO_PS2_T] = { 't', 'T' },
+       [AO_PS2_U] = { 'u', 'U' },
+       [AO_PS2_V] = { 'v', 'V' },
+       [AO_PS2_W] = { 'w', 'W' },
+       [AO_PS2_X] = { 'x', 'X' },
+       [AO_PS2_Y] = { 'y', 'Y' },
+       [AO_PS2_Z] = { 'z', 'Z' },
+
+       [AO_PS2_0] = { '0', ')' },
+       [AO_PS2_1] = { '1', '!' },
+       [AO_PS2_2] = { '2', '@' },
+       [AO_PS2_3] = { '3', '#' },
+       [AO_PS2_4] = { '4', '$' },
+       [AO_PS2_5] = { '5', '%' },
+       [AO_PS2_6] = { '6', '^' },
+       [AO_PS2_7] = { '7', '&' },
+       [AO_PS2_8] = { '8', '*' },
+       [AO_PS2_9] = { '9', '(' },
+
+       [AO_PS2_GRAVE] = { '`', '~' },
+       [AO_PS2_HYPHEN] = { '-', '_' },
+       [AO_PS2_EQUAL] = { '=', '+' },
+       [AO_PS2_BACKSLASH] = { '\\', '|' },
+       [AO_PS2_BACKSPACE] = { '\010', '\010' },
+       [AO_PS2_SPACE] = { ' ', ' ' },
+       [AO_PS2_TAB] = { '\t', '\t' },
+
+       [AO_PS2_ENTER] = { '\r', '\r' },
+       [AO_PS2_ESC] = { '\033', '\033' },
+
+       [AO_PS2_OPEN_SQ] = { '[', '{' },
+       [AO_PS2_DELETE] = { '\177', '\177' },
+
+       [AO_PS2_KP_TIMES] = { '*', '*' },
+       [AO_PS2_KP_PLUS] = { '+', '+' },
+       [AO_PS2_KP_ENTER] = { '\r', '\r' },
+       [AO_PS2_KP_DECIMAL] = { '.', '.' },
+       [AO_PS2_KP_0] = { '0', '0' },
+       [AO_PS2_KP_1] = { '1', '1' },
+       [AO_PS2_KP_2] = { '2', '2' },
+       [AO_PS2_KP_3] = { '3', '3' },
+       [AO_PS2_KP_4] = { '4', '4' },
+       [AO_PS2_KP_5] = { '5', '5' },
+       [AO_PS2_KP_6] = { '6', '6' },
+       [AO_PS2_KP_7] = { '7', '7' },
+       [AO_PS2_KP_8] = { '8', '8' },
+       [AO_PS2_KP_9] = { '9', '9' },
+       [AO_PS2_CLOSE_SQ] = { ']', '}' },
+       [AO_PS2_SEMICOLON] = { ';', ':' },
+       [AO_PS2_ACUTE] = { '\'', '"' },
+       [AO_PS2_COMMA] = { ',', '<' },
+       [AO_PS2_PERIOD] = { '.', '>' },
+       [AO_PS2_SLASH] = { '/', '?' },
+};
+
+int
+ao_ps2_ascii(uint8_t key)
+{
+       uint8_t col;
+       char a;
+
+       /* Skip key releases */
+       if (key & 0x80)
+               return AO_READ_AGAIN;
+
+       col = 0;
+       if (ao_ps2_is_down(AO_PS2_L_SHIFT) || ao_ps2_is_down(AO_PS2_R_SHIFT))
+               col = 1;
+
+       /* caps lock */
+       a = ao_ps2_asciimap[key][0];
+       if (!a)
+               return AO_READ_AGAIN;
+
+       if ('a' <= a && a <= 'z')
+               if (ao_ps2_is_down(AO_PS2_CAPS_LOCK))
+                       col ^= 1;
+       a = ao_ps2_asciimap[key][col];
+       if ('@' <= a && a <= 0x7f && (ao_ps2_is_down(AO_PS2_L_CTRL) || ao_ps2_is_down(AO_PS2_R_CTRL)))
+               a &= 0x1f;
+       return a;
+}
+
+int
+_ao_ps2_pollchar(void)
+{
+       int     key;
+
+       key = _ao_ps2_poll_key();
+       if (key < 0)
+               return key;
+       return ao_ps2_ascii(key);
+}
+
+char
+ao_ps2_getchar(void)
+{
+       int     c;
+       ao_arch_block_interrupts();
+       while ((c = _ao_ps2_pollchar()) == AO_READ_AGAIN)
+               ao_sleep(&ao_ps2_rx_fifo);
+       ao_arch_release_interrupts();
+       return (char) c;
+}
+
+static void
+ao_ps2_isr(void)
+{
+       uint8_t bit;
+
+       if (ao_ps2_tx_count) {
+               ao_gpio_set(AO_PS2_DATA_PORT, AO_PS2_DATA_BIT, AO_PS2_DATA_PIN, ao_ps2_tx&1);
+               ao_ps2_tx >>= 1;
+               ao_ps2_tx_count--;
+               if (!ao_ps2_tx_count) {
+                       ao_enable_input(AO_PS2_DATA_PORT, AO_PS2_DATA_BIT, AO_EXTI_MODE_PULL_UP);
+                       ao_wakeup(&ao_ps2_tx_count);
+               }
+               return;
+       }
+       /* reset if its been a while */
+       if ((ao_tick_count - ao_ps2_tick) > AO_MS_TO_TICKS(100))
+               ao_ps2_count = 0;
+       ao_ps2_tick = ao_tick_count;
+
+       bit = ao_gpio_get(AO_PS2_DATA_PORT, AO_PS2_DATA_BIT, AO_PS2_DATA_PIN);
+       if (ao_ps2_count == 0) {
+               /* check for start bit, ignore if not zero */
+               if (bit)
+                       return;
+               ao_ps2_value = 0;
+       } else if (ao_ps2_count < 9) {
+               ao_ps2_value |= (bit << (ao_ps2_count - 1));
+       } else if (ao_ps2_count == 10) {
+               ao_fifo_insert(ao_ps2_rx_fifo, ao_ps2_value);
+               ao_wakeup(&ao_ps2_rx_fifo);
+               if (ao_ps2_stdin)
+                       ao_wakeup(&ao_stdin_ready);
+               ao_ps2_count = 0;
+               return;
+       }
+       ao_ps2_count++;
+}
+
+void
+ao_ps2_init(void)
+{
+       ao_enable_input(AO_PS2_DATA_PORT, AO_PS2_DATA_BIT,
+                       AO_EXTI_MODE_PULL_UP);
+
+       ao_enable_port(AO_PS2_CLOCK_PORT);
+
+       ao_exti_setup(AO_PS2_CLOCK_PORT, AO_PS2_CLOCK_BIT,
+                     AO_PS2_CLOCK_MODE(AO_EXTI_MODE_PULL_UP),
+                     ao_ps2_isr);
+       ao_exti_enable(AO_PS2_CLOCK_PORT, AO_PS2_CLOCK_BIT);
+
+       ao_ps2_scancode_set = 2;
+}
diff --git a/src/drivers/ao_ps2.h b/src/drivers/ao_ps2.h
new file mode 100644 (file)
index 0000000..f1f05ee
--- /dev/null
@@ -0,0 +1,220 @@
+/*
+ * Copyright © 2016 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+#ifndef _AO_PS2_H_
+#define _AO_PS2_H_
+
+extern uint8_t                 ao_ps2_stdin;
+
+int
+ao_ps2_poll(void);
+
+uint8_t
+ao_ps2_get(void);
+
+void
+ao_ps2_put(uint8_t b);
+
+uint8_t
+ao_ps2_is_down(uint8_t code);
+
+int
+ao_ps2_poll_key(void);
+
+uint8_t
+ao_ps2_get_key(void);
+
+int
+ao_ps2_ascii(uint8_t key);
+
+int
+_ao_ps2_pollchar(void);
+
+char
+ao_ps2_getchar(void);
+
+void
+ao_ps2_init(void);
+
+/* From http://computer-engineering.org/ps2keyboard/ */
+
+/* Device responds with ACK and then resets */
+#define AO_PS2_RESET                           0xff
+
+/* Device retransmits last byte */
+#define AO_PS2_RESEND                          0xfe
+
+/* Setting key report only works in mode 3 */
+
+/* Disable break and typematic for specified mode 3 keys. Terminate with invalid key */
+#define AO_PS2_SET_KEY_MAKE                    0xfd
+
+/* Disable typematic for keys */
+#define AO_PS2_SET_KEY_MAKE_BREAK              0xfc
+
+/* Disable break code for keys */
+#define AO_PS2_SET_KEY_TYPEMATIC               0xfb
+
+/* Enable make, break and typematic */
+#define AO_PS2_SET_KEY_TYPEMATIC_MAKE_BREAK    0xfa
+
+/* Disable break and typematic for all */
+#define AO_PS2_SET_ALL_MAKE                    0xf9
+
+/* Disable typematic for all */
+#define AO_PS2_SET_ALL_MAKE_BREAK              0xf8
+
+/* Disable break for all */
+#define AO_PS2_SET_ALL_TYPEMATIC               0xf7
+
+/* Set keyboard to default (repeat, report and scan code set 2) */
+#define AO_PS2_SET_DEFAULT                     0xf6
+
+/* Disable and reset to default */
+#define AO_PS2_DISABLE                         0xf5
+
+/* Enable */
+#define AO_PS2_ENABLE                          0xf4
+
+/* Set repeat rate. Bytes 5-6 are the start delay, bits 0-4 are the rate */
+#define AO_PS2_SET_REPEAT_RATE                 0xf3
+
+/* Read keyboard id. Returns two bytes */
+#define AO_PS2_GETID                           0xf2
+
+/* Set scan code (1, 2, or 3) */
+#define AO_PS2_SET_SCAN_CODE_SET               0xf0
+
+/* Echo. Keyboard replies with Echo */
+#define AO_PS2_ECHO                            0xee
+
+/* Set LEDs */
+#define AO_PS2_SET_LEDS                                0xed
+# define AO_PS2_SET_LEDS_SCROLL                        0x01
+# define AO_PS2_SET_LEDS_NUM                   0x02
+# define AO_PS2_SET_LEDS_CAPS                  0x04
+
+#define AO_PS2_BREAK                           0xf0
+#define AO_PS2_ACK                             0xfa
+#define AO_PS2_ERROR                           0xfc
+#define AO_PS2_NAK                             0xfe
+
+/* Scan code set 3 */
+
+#define AO_PS2_A               0x1c
+#define AO_PS2_B               0x32
+#define AO_PS2_C               0x21
+#define AO_PS2_D               0x23
+#define AO_PS2_E               0x24
+#define AO_PS2_F               0x2b
+#define AO_PS2_G               0x34
+#define AO_PS2_H               0x33
+#define AO_PS2_I               0x43
+#define AO_PS2_J               0x3b
+#define AO_PS2_K               0x42
+#define AO_PS2_L               0x4b
+#define AO_PS2_M               0x3a
+#define AO_PS2_N               0x31
+#define AO_PS2_O               0x44
+#define AO_PS2_P               0x4d
+#define AO_PS2_Q               0x15
+#define AO_PS2_R               0x2d
+#define AO_PS2_S               0x1b
+#define AO_PS2_T               0x2c
+#define AO_PS2_U               0x3c
+#define AO_PS2_V               0x2a
+#define AO_PS2_W               0x1d
+#define AO_PS2_X               0x22
+#define AO_PS2_Y               0x35
+#define AO_PS2_Z               0x1a
+#define AO_PS2_0               0x45
+#define AO_PS2_1               0x16
+#define AO_PS2_2               0x1e
+#define AO_PS2_3               0x26
+#define AO_PS2_4               0x25
+#define AO_PS2_5               0x2e
+#define AO_PS2_6               0x36
+#define AO_PS2_7               0x3d
+#define AO_PS2_8               0x3e
+#define AO_PS2_9               0x46
+#define AO_PS2_GRAVE           0x0e
+#define AO_PS2_HYPHEN          0x4e
+#define AO_PS2_EQUAL           0x55
+#define AO_PS2_BACKSLASH       0x5c
+#define AO_PS2_BACKSPACE       0x66
+#define AO_PS2_SPACE           0x29
+#define AO_PS2_TAB             0x0d
+#define AO_PS2_CAPS_LOCK       0x14
+#define AO_PS2_L_SHIFT         0x12
+#define AO_PS2_L_CTRL          0x11
+#define AO_PS2_L_WIN           0x8b
+#define AO_PS2_L_ALT           0x19
+#define AO_PS2_R_SHIFT         0x59
+#define AO_PS2_R_CTRL          0x58
+#define AO_PS2_R_WIN           0x8c
+#define AO_PS2_R_ALT           0x39
+#define AO_PS2_APPS            0x8d
+#define AO_PS2_ENTER           0x5a
+#define AO_PS2_ESC             0x08
+#define AO_PS2_F1              0x07
+#define AO_PS2_F2              0x0f
+#define AO_PS2_F3              0x17
+#define AO_PS2_F4              0x1f
+#define AO_PS2_F5              0x27
+#define AO_PS2_F6              0x2f
+#define AO_PS2_F7              0x37
+#define AO_PS2_F8              0x3f
+#define AO_PS2_F9              0x47
+#define AO_PS2_F10             0x4f
+#define AO_PS2_F11             0x56
+#define AO_PS2_F12             0x5e
+#define AO_PS2_PRNT_SCRN       0x57
+#define AO_PS2_SCROLL_LOCK     0x5f
+#define AO_PS2_PAUSE           0x62
+#define AO_PS2_OPEN_SQ         0x54
+#define AO_PS2_INSERT          0x67
+#define AO_PS2_HOME            0x6e
+#define AO_PS2_PG_UP           0x6f
+#define AO_PS2_DELETE          0x64
+#define AO_PS2_END             0x65
+#define AO_PS2_PG_DN           0x6d
+#define AO_PS2_UP              0x63
+#define AO_PS2_LEFT            0x61
+#define AO_PS2_DOWN            0x60
+#define AO_PS2_RIGHT           0x6a
+#define AO_PS2_NUM_LOCK                0x76
+#define AO_PS2_KP_TIMES                0x7e
+#define AO_PS2_KP_PLUS         0x7c
+#define AO_PS2_KP_ENTER                0x79
+#define AO_PS2_KP_DECIMAL      0x71
+#define AO_PS2_KP_0            0x70
+#define AO_PS2_KP_1            0x69
+#define AO_PS2_KP_2            0x72
+#define AO_PS2_KP_3            0x7a
+#define AO_PS2_KP_4            0x6b
+#define AO_PS2_KP_5            0x73
+#define AO_PS2_KP_6            0x74
+#define AO_PS2_KP_7            0x6c
+#define AO_PS2_KP_8            0x75
+#define AO_PS2_KP_9            0x7d
+#define AO_PS2_CLOSE_SQ                0x5b
+#define AO_PS2_SEMICOLON       0x4c
+#define AO_PS2_ACUTE           0x52
+#define AO_PS2_COMMA           0x41
+#define AO_PS2_PERIOD          0x49
+#define AO_PS2_SLASH           0x4a
+
+#define AO_PS2_RELEASE_FLAG    0x80
+
+#endif /* _AO_PS2_H_ */