summaryrefslogtreecommitdiffstats
path: root/drivers/char/pc_keyb.c
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1997-06-01 03:16:17 +0000
committerRalf Baechle <ralf@linux-mips.org>1997-06-01 03:16:17 +0000
commitd8d9b8f76f22b7a16a83e261e64f89ee611f49df (patch)
tree3067bc130b80d52808e6390c9fc7fc087ec1e33c /drivers/char/pc_keyb.c
parent19c9bba94152148523ba0f7ef7cffe3d45656b11 (diff)
Initial revision
Diffstat (limited to 'drivers/char/pc_keyb.c')
-rw-r--r--drivers/char/pc_keyb.c647
1 files changed, 647 insertions, 0 deletions
diff --git a/drivers/char/pc_keyb.c b/drivers/char/pc_keyb.c
new file mode 100644
index 000000000..88dcb1986
--- /dev/null
+++ b/drivers/char/pc_keyb.c
@@ -0,0 +1,647 @@
+/*
+ * linux/drivers/char/pc_keyb.c
+ *
+ * Written for linux by Johan Myreen as a translation from
+ * the assembly version by Linus (with diacriticals added)
+ *
+ * Some additional features added by Christoph Niemann (ChN), March 1993
+ *
+ * Loadable keymaps by Risto Kankkunen, May 1993
+ *
+ * Diacriticals redone & other small changes, aeb@cwi.nl, June 1993
+ * Added decr/incr_console, dynamic keymaps, Unicode support,
+ * dynamic function/string keys, led setting, Sept 1994
+ * `Sticky' modifier keys, 951006.
+ * 11-11-96: SAK should now work in the raw mode (Martin Mares)
+ *
+ * Separation of the PC low-level part by Geert Uytterhoeven, May 1997
+ */
+
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/mm.h>
+#include <linux/signal.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/config.h>
+
+#include <asm/bitops.h>
+
+/*
+ * Define these here, include/asm-mips/keyboard.h depends on them.
+ *
+ * keyboard controller registers
+ */
+#define KBD_STATUS_REG (unsigned int) 0x64
+#define KBD_CNTL_REG (unsigned int) 0x64
+#define KBD_DATA_REG (unsigned int) 0x60
+/*
+ * controller commands
+ */
+#define KBD_READ_MODE (unsigned int) 0x20
+#define KBD_WRITE_MODE (unsigned int) 0x60
+#define KBD_SELF_TEST (unsigned int) 0xAA
+#define KBD_SELF_TEST2 (unsigned int) 0xAB
+#define KBD_CNTL_ENABLE (unsigned int) 0xAE
+/*
+ * keyboard commands
+ */
+#define KBD_ENABLE (unsigned int) 0xF4
+#define KBD_DISABLE (unsigned int) 0xF5
+#define KBD_RESET (unsigned int) 0xFF
+/*
+ * keyboard replies
+ */
+#define KBD_ACK (unsigned int) 0xFA
+#define KBD_POR (unsigned int) 0xAA
+/*
+ * status register bits
+ */
+#define KBD_OBF (unsigned int) 0x01
+#define KBD_IBF (unsigned int) 0x02
+#define KBD_GTO (unsigned int) 0x40
+#define KBD_PERR (unsigned int) 0x80
+/*
+ * keyboard controller mode register bits
+ */
+#define KBD_EKI (unsigned int) 0x01
+#define KBD_SYS (unsigned int) 0x04
+#define KBD_DMS (unsigned int) 0x20
+#define KBD_KCC (unsigned int) 0x40
+
+#include <asm/keyboard.h>
+
+/*
+ * On non-x86 hardware we do a full keyboard controller
+ * initialization, in case the bootup software hasn't done
+ * it. On a x86, the BIOS will already have initialized the
+ * keyboard.
+ */
+#ifdef INIT_KBD
+int initialize_kbd(void);
+#endif
+
+#include <asm/io.h>
+#include <asm/system.h>
+
+unsigned char kbd_read_mask = 0x01; /* modified by psaux.c */
+
+/* used only by send_data - set by keyboard_interrupt */
+static volatile unsigned char reply_expected = 0;
+static volatile unsigned char acknowledge = 0;
+static volatile unsigned char resend = 0;
+
+/* pt_regs - set by keyboard_interrupt(), used by show_ptregs() */
+static inline void kb_wait(void)
+{
+ int i;
+
+ for (i=0; i<0x100000; i++)
+ if ((kbd_inb_p(0x64) & 0x02) == 0)
+ return;
+ printk(KERN_WARNING "Keyboard timed out\n");
+}
+
+extern struct pt_regs *pt_regs;
+
+extern void handle_scancode(unsigned char scancode);
+
+
+/*
+ * Translation of escaped scancodes to keycodes.
+ * This is now user-settable.
+ * The keycodes 1-88,96-111,119 are fairly standard, and
+ * should probably not be changed - changing might confuse X.
+ * X also interprets scancode 0x5d (KEY_Begin).
+ *
+ * For 1-88 keycode equals scancode.
+ */
+
+#define E0_KPENTER 96
+#define E0_RCTRL 97
+#define E0_KPSLASH 98
+#define E0_PRSCR 99
+#define E0_RALT 100
+#define E0_BREAK 101 /* (control-pause) */
+#define E0_HOME 102
+#define E0_UP 103
+#define E0_PGUP 104
+#define E0_LEFT 105
+#define E0_RIGHT 106
+#define E0_END 107
+#define E0_DOWN 108
+#define E0_PGDN 109
+#define E0_INS 110
+#define E0_DEL 111
+
+#define E1_PAUSE 119
+
+/*
+ * The keycodes below are randomly located in 89-95,112-118,120-127.
+ * They could be thrown away (and all occurrences below replaced by 0),
+ * but that would force many users to use the `setkeycodes' utility, where
+ * they needed not before. It does not matter that there are duplicates, as
+ * long as no duplication occurs for any single keyboard.
+ */
+#define SC_LIM 89
+
+#define FOCUS_PF1 85 /* actual code! */
+#define FOCUS_PF2 89
+#define FOCUS_PF3 90
+#define FOCUS_PF4 91
+#define FOCUS_PF5 92
+#define FOCUS_PF6 93
+#define FOCUS_PF7 94
+#define FOCUS_PF8 95
+#define FOCUS_PF9 120
+#define FOCUS_PF10 121
+#define FOCUS_PF11 122
+#define FOCUS_PF12 123
+
+#define JAP_86 124
+/* tfj@olivia.ping.dk:
+ * The four keys are located over the numeric keypad, and are
+ * labelled A1-A4. It's an rc930 keyboard, from
+ * Regnecentralen/RC International, Now ICL.
+ * Scancodes: 59, 5a, 5b, 5c.
+ */
+#define RGN1 124
+#define RGN2 125
+#define RGN3 126
+#define RGN4 127
+
+static unsigned char high_keys[128 - SC_LIM] = {
+ RGN1, RGN2, RGN3, RGN4, 0, 0, 0, /* 0x59-0x5f */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x60-0x67 */
+ 0, 0, 0, 0, 0, FOCUS_PF11, 0, FOCUS_PF12, /* 0x68-0x6f */
+ 0, 0, 0, FOCUS_PF2, FOCUS_PF9, 0, 0, FOCUS_PF3, /* 0x70-0x77 */
+ FOCUS_PF4, FOCUS_PF5, FOCUS_PF6, FOCUS_PF7, /* 0x78-0x7b */
+ FOCUS_PF8, JAP_86, FOCUS_PF10, 0 /* 0x7c-0x7f */
+};
+
+/* BTC */
+#define E0_MACRO 112
+/* LK450 */
+#define E0_F13 113
+#define E0_F14 114
+#define E0_HELP 115
+#define E0_DO 116
+#define E0_F17 117
+#define E0_KPMINPLUS 118
+/*
+ * My OmniKey generates e0 4c for the "OMNI" key and the
+ * right alt key does nada. [kkoller@nyx10.cs.du.edu]
+ */
+#define E0_OK 124
+/*
+ * New microsoft keyboard is rumoured to have
+ * e0 5b (left window button), e0 5c (right window button),
+ * e0 5d (menu button). [or: LBANNER, RBANNER, RMENU]
+ * [or: Windows_L, Windows_R, TaskMan]
+ */
+#define E0_MSLW 125
+#define E0_MSRW 126
+#define E0_MSTM 127
+
+static unsigned char e0_keys[128] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00-0x07 */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x08-0x0f */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10-0x17 */
+ 0, 0, 0, 0, E0_KPENTER, E0_RCTRL, 0, 0, /* 0x18-0x1f */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x20-0x27 */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x28-0x2f */
+ 0, 0, 0, 0, 0, E0_KPSLASH, 0, E0_PRSCR, /* 0x30-0x37 */
+ E0_RALT, 0, 0, 0, 0, E0_F13, E0_F14, E0_HELP, /* 0x38-0x3f */
+ E0_DO, E0_F17, 0, 0, 0, 0, E0_BREAK, E0_HOME, /* 0x40-0x47 */
+ E0_UP, E0_PGUP, 0, E0_LEFT, E0_OK, E0_RIGHT, E0_KPMINPLUS, E0_END,/* 0x48-0x4f */
+ E0_DOWN, E0_PGDN, E0_INS, E0_DEL, 0, 0, 0, 0, /* 0x50-0x57 */
+ 0, 0, 0, E0_MSLW, E0_MSRW, E0_MSTM, 0, 0, /* 0x58-0x5f */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x60-0x67 */
+ 0, 0, 0, 0, 0, 0, 0, E0_MACRO, /* 0x68-0x6f */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x70-0x77 */
+ 0, 0, 0, 0, 0, 0, 0, 0 /* 0x78-0x7f */
+};
+
+static unsigned int prev_scancode = 0; /* remember E0, E1 */
+
+int pckbd_setkeycode(unsigned int scancode, unsigned int keycode)
+{
+ if (scancode < SC_LIM || scancode > 255 || keycode > 127)
+ return -EINVAL;
+ if (scancode < 128)
+ high_keys[scancode - SC_LIM] = keycode;
+ else
+ e0_keys[scancode - 128] = keycode;
+ return 0;
+}
+
+int pckbd_getkeycode(unsigned int scancode)
+{
+ return
+ (scancode < SC_LIM || scancode > 255) ? -EINVAL :
+ (scancode < 128) ? high_keys[scancode - SC_LIM] :
+ e0_keys[scancode - 128];
+}
+
+#if DISABLE_KBD_DURING_INTERRUPTS
+static inline void send_cmd(unsigned char c)
+{
+ kb_wait();
+ kbd_outb(c,0x64);
+}
+
+#define disable_keyboard() do { send_cmd(0xAD); kb_wait(); } while (0)
+#define enable_keyboard() send_cmd(0xAE)
+#else
+#define disable_keyboard() /* nothing */
+#define enable_keyboard() /* nothing */
+#endif
+
+static int do_acknowledge(unsigned char scancode)
+{
+ if (reply_expected) {
+ /* 0xfa, 0xfe only mean "acknowledge", "resend" for most keyboards */
+ /* but they are the key-up scancodes for PF6, PF10 on a FOCUS 9000 */
+ reply_expected = 0;
+ if (scancode == 0xfa) {
+ acknowledge = 1;
+ return 0;
+ } else if (scancode == 0xfe) {
+ resend = 1;
+ return 0;
+ }
+ /* strange ... */
+ reply_expected = 1;
+#if 0
+ printk(KERN_DEBUG "keyboard reply expected - got %02x\n",
+ scancode);
+#endif
+ }
+ if (scancode == 0) {
+#ifdef KBD_REPORT_ERR
+ printk(KERN_INFO "keyboard buffer overflow\n");
+#endif
+ prev_scancode = 0;
+ return 0;
+ }
+ return 1;
+}
+
+int pckbd_pretranslate(unsigned char scancode, char raw_mode)
+{
+ if (scancode == 0xff) {
+ /* in scancode mode 1, my ESC key generates 0xff */
+ /* the calculator keys on a FOCUS 9000 generate 0xff */
+#ifndef KBD_IS_FOCUS_9000
+#ifdef KBD_REPORT_ERR
+ if (!raw_mode)
+ printk(KERN_DEBUG "keyboard error\n");
+#endif
+#endif
+ prev_scancode = 0;
+ return 0;
+ }
+
+ if (scancode == 0xe0 || scancode == 0xe1) {
+ prev_scancode = scancode;
+ return 0;
+ }
+ return 1;
+}
+
+int pckbd_translate(unsigned char scancode, unsigned char *keycode,
+ char raw_mode)
+{
+ if (prev_scancode) {
+ /*
+ * usually it will be 0xe0, but a Pause key generates
+ * e1 1d 45 e1 9d c5 when pressed, and nothing when released
+ */
+ if (prev_scancode != 0xe0) {
+ if (prev_scancode == 0xe1 && scancode == 0x1d) {
+ prev_scancode = 0x100;
+ return 0;
+ } else if (prev_scancode == 0x100 && scancode == 0x45) {
+ *keycode = E1_PAUSE;
+ prev_scancode = 0;
+ } else {
+#ifdef KBD_REPORT_UNKN
+ if (!raw_mode)
+ printk(KERN_INFO "keyboard: unknown e1 escape sequence\n");
+#endif
+ prev_scancode = 0;
+ return 0;
+ }
+ } else {
+ prev_scancode = 0;
+ /*
+ * The keyboard maintains its own internal caps lock and
+ * num lock statuses. In caps lock mode E0 AA precedes make
+ * code and E0 2A follows break code. In num lock mode,
+ * E0 2A precedes make code and E0 AA follows break code.
+ * We do our own book-keeping, so we will just ignore these.
+ */
+ /*
+ * For my keyboard there is no caps lock mode, but there are
+ * both Shift-L and Shift-R modes. The former mode generates
+ * E0 2A / E0 AA pairs, the latter E0 B6 / E0 36 pairs.
+ * So, we should also ignore the latter. - aeb@cwi.nl
+ */
+ if (scancode == 0x2a || scancode == 0x36)
+ return 0;
+
+ if (e0_keys[scancode])
+ *keycode = e0_keys[scancode];
+ else {
+#ifdef KBD_REPORT_UNKN
+ if (!raw_mode)
+ printk(KERN_INFO "keyboard: unknown scancode e0 %02x\n",
+ scancode);
+#endif
+ return 0;
+ }
+ }
+ } else if (scancode >= SC_LIM) {
+ /* This happens with the FOCUS 9000 keyboard
+ Its keys PF1..PF12 are reported to generate
+ 55 73 77 78 79 7a 7b 7c 74 7e 6d 6f
+ Moreover, unless repeated, they do not generate
+ key-down events, so we have to zero up_flag below */
+ /* Also, Japanese 86/106 keyboards are reported to
+ generate 0x73 and 0x7d for \ - and \ | respectively. */
+ /* Also, some Brazilian keyboard is reported to produce
+ 0x73 and 0x7e for \ ? and KP-dot, respectively. */
+
+ *keycode = high_keys[scancode - SC_LIM];
+
+ if (!*keycode) {
+ if (!raw_mode) {
+#ifdef KBD_REPORT_UNKN
+ printk(KERN_INFO "keyboard: unrecognized scancode (%02x)"
+ " - ignored\n", scancode);
+#endif
+ }
+ return 0;
+ }
+ } else
+ *keycode = scancode;
+ return 1;
+}
+
+char pckbd_unexpected_up(unsigned char keycode)
+{
+ /* unexpected, but this can happen: maybe this was a key release for a
+ FOCUS 9000 PF key; if we want to see it, we have to clear up_flag */
+ if (keycode >= SC_LIM || keycode == 85)
+ return 0;
+ else
+ return 0200;
+}
+
+static void keyboard_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ unsigned char status;
+
+ pt_regs = regs;
+ disable_keyboard();
+
+ status = kbd_inb_p(0x64);
+ do {
+ unsigned char scancode;
+
+ /* mouse data? */
+ if (status & kbd_read_mask & 0x20)
+ break;
+
+ scancode = kbd_inb(0x60);
+ if ((status & 0x01) && do_acknowledge(scancode))
+ handle_scancode(scancode);
+
+ status = kbd_inb(0x64);
+ } while (status & 0x01);
+
+ mark_bh(KEYBOARD_BH);
+ enable_keyboard();
+}
+
+/*
+ * send_data sends a character to the keyboard and waits
+ * for an acknowledge, possibly retrying if asked to. Returns
+ * the success status.
+ */
+static int send_data(unsigned char data)
+{
+ int retries = 3;
+ int i;
+
+ do {
+ kb_wait();
+ acknowledge = 0;
+ resend = 0;
+ reply_expected = 1;
+ kbd_outb_p(data, 0x60);
+ for(i=0; i<0x200000; i++) {
+ kbd_inb_p(0x64); /* just as a delay */
+ if (acknowledge)
+ return 1;
+ if (resend)
+ break;
+ }
+ if (!resend)
+ return 0;
+ } while (retries-- > 0);
+ return 0;
+}
+
+void pckbd_leds(unsigned char leds)
+{
+ if (!send_data(0xed) || !send_data(leds))
+ send_data(0xf4); /* re-enable kbd if any errors */
+}
+
+__initfunc(void pckbd_init_hw(void))
+{
+ request_irq(KEYBOARD_IRQ, keyboard_interrupt, 0, "keyboard", NULL);
+ keyboard_setup();
+#ifdef INIT_KBD
+ initialize_kbd();
+#endif
+}
+
+#ifdef INIT_KBD
+
+/*
+ * controller commands
+ */
+#define KBD_READ_MODE (unsigned int) 0x20
+#define KBD_WRITE_MODE (unsigned int) 0x60
+#define KBD_SELF_TEST (unsigned int) 0xAA
+#define KBD_SELF_TEST2 (unsigned int) 0xAB
+#define KBD_CNTL_ENABLE (unsigned int) 0xAE
+/*
+ * keyboard commands
+ */
+#define KBD_ENABLE (unsigned int) 0xF4
+#define KBD_DISABLE (unsigned int) 0xF5
+#define KBD_RESET (unsigned int) 0xFF
+/*
+ * keyboard replies
+ */
+#define KBD_ACK (unsigned int) 0xFA
+#define KBD_POR (unsigned int) 0xAA
+/*
+ * status register bits
+ */
+#define KBD_OBF (unsigned int) 0x01
+#define KBD_IBF (unsigned int) 0x02
+#define KBD_GTO (unsigned int) 0x40
+#define KBD_PERR (unsigned int) 0x80
+/*
+ * keyboard controller mode register bits
+ */
+#define KBD_EKI (unsigned int) 0x01
+#define KBD_SYS (unsigned int) 0x04
+#define KBD_DMS (unsigned int) 0x20
+#define KBD_KCC (unsigned int) 0x40
+
+#define TIMEOUT_CONST 500000
+
+static int kbd_wait_for_input(void)
+{
+ int n;
+ int status, data;
+
+ n = TIMEOUT_CONST;
+ do {
+ status = kbd_inb(KBD_STATUS_REG);
+ /*
+ * Wait for input data to become available. This bit will
+ * then be cleared by the following read of the DATA
+ * register.
+ */
+
+ if (!(status & KBD_OBF))
+ continue;
+
+ data = kbd_inb(KBD_DATA_REG);
+
+ /*
+ * Check to see if a timeout error has occurred. This means
+ * that transmission was started but did not complete in the
+ * normal time cycle. PERR is set when a parity error occurred
+ * in the last transmission.
+ */
+ if (status & (KBD_GTO | KBD_PERR)) {
+ continue;
+ }
+ return (data & 0xff);
+ } while (--n);
+ return (-1); /* timed-out if fell through to here... */
+}
+
+static void kbd_write(int address, int data)
+{
+ int status;
+
+ do {
+ status = kbd_inb(KBD_STATUS_REG); /* spin until input buffer empty*/
+ } while (status & KBD_IBF);
+ kbd_outb(data, address); /* write out the data*/
+}
+
+__initfunc(int initialize_kbd(void))
+{
+ unsigned long flags;
+
+ save_flags(flags); cli();
+
+ /* Flush any pending input. */
+ while (kbd_wait_for_input() != -1)
+ continue;
+
+ /*
+ * Test the keyboard interface.
+ * This seems to be the only way to get it going.
+ * If the test is successful a x55 is placed in the input buffer.
+ */
+ kbd_write(KBD_CNTL_REG, KBD_SELF_TEST);
+ if (kbd_wait_for_input() != 0x55) {
+ printk(KERN_WARNING "initialize_kbd: "
+ "keyboard failed self test.\n");
+ restore_flags(flags);
+ return(-1);
+ }
+
+ /*
+ * Perform a keyboard interface test. This causes the controller
+ * to test the keyboard clock and data lines. The results of the
+ * test are placed in the input buffer.
+ */
+ kbd_write(KBD_CNTL_REG, KBD_SELF_TEST2);
+ if (kbd_wait_for_input() != 0x00) {
+ printk(KERN_WARNING "initialize_kbd: "
+ "keyboard failed self test 2.\n");
+ restore_flags(flags);
+ return(-1);
+ }
+
+ /* Enable the keyboard by allowing the keyboard clock to run. */
+ kbd_write(KBD_CNTL_REG, KBD_CNTL_ENABLE);
+
+ /*
+ * Reset keyboard. If the read times out
+ * then the assumption is that no keyboard is
+ * plugged into the machine.
+ * This defaults the keyboard to scan-code set 2.
+ */
+ kbd_write(KBD_DATA_REG, KBD_RESET);
+ if (kbd_wait_for_input() != KBD_ACK) {
+ printk(KERN_WARNING "initialize_kbd: "
+ "reset kbd failed, no ACK.\n");
+ restore_flags(flags);
+ return(-1);
+ }
+
+ if (kbd_wait_for_input() != KBD_POR) {
+ printk(KERN_WARNING "initialize_kbd: "
+ "reset kbd failed, not POR.\n");
+ restore_flags(flags);
+ return(-1);
+ }
+
+ /*
+ * now do a DEFAULTS_DISABLE always
+ */
+ kbd_write(KBD_DATA_REG, KBD_DISABLE);
+ if (kbd_wait_for_input() != KBD_ACK) {
+ printk(KERN_WARNING "initialize_kbd: "
+ "disable kbd failed, no ACK.\n");
+ restore_flags(flags);
+ return(-1);
+ }
+
+ /*
+ * Enable keyboard interrupt, operate in "sys" mode,
+ * enable keyboard (by clearing the disable keyboard bit),
+ * disable mouse, do conversion of keycodes.
+ */
+ kbd_write(KBD_CNTL_REG, KBD_WRITE_MODE);
+ kbd_write(KBD_DATA_REG, KBD_EKI|KBD_SYS|KBD_DMS|KBD_KCC);
+
+ /*
+ * now ENABLE the keyboard to set it scanning...
+ */
+ kbd_write(KBD_DATA_REG, KBD_ENABLE);
+ if (kbd_wait_for_input() != KBD_ACK) {
+ printk(KERN_WARNING "initialize_kbd: "
+ "keyboard enable failed.\n");
+ restore_flags(flags);
+ return(-1);
+ }
+
+ restore_flags(flags);
+
+ return (1);
+}
+#endif /* INIT_KBD */