/* Pilote pour le controleur de clavier Intel 8042 */ /* Copyright (C) 2008 Emmanuel Varoquaux This file is part of XOS. 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 3 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. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "keymap.h" #include "vc.h" #include "vc_struct.h" #include "tty.h" #include "video_driver.h" #include "tty_queue.h" #include #include #include #include /* commandes du 8042 */ #define CT_PULSE_PORTS 0xf0 /* commandes du 8048 */ #define KB_WRITE_LEDS 0xed /* statut du 8048 */ #define KB_ACK 0xfa #define KB_RESEND 0xfe /* Traduction des scancodes en keycodes */ /* Les scancodes de 1 a 88 sont egaux a leur keycode. */ /* keycodes standards */ #define E0_KPENTER 96 #define E0_RCTRL 97 #define E0_KPSLASH 98 #define E0_PRSCR 99 #define E0_RALT 100 #define E0_BREAK 101 #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 /* Touches Windows. * e0 5b est le boutton "fenetre" gauche * e0 5c est le boutton "fenetre" droit * e0 5d est le bouton "menu" */ #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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, E0_KPENTER, E0_RCTRL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, E0_KPSLASH, 0, E0_PRSCR, E0_RALT, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, E0_BREAK, E0_HOME, E0_UP, E0_PGUP, 0, E0_LEFT, 0, E0_RIGHT, 0, E0_END, E0_DOWN, E0_PGDN, E0_INS, E0_DEL, 0, 0, 0, 0, 0, 0, 0, E0_MSLW, E0_MSRW, E0_MSTM, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; /* Traduction des keycodes en caracteres */ /* types de touches */ #define KT_LATIN 0 #define KT_FN 1 #define KT_SPEC 2 #define KT_PAD 3 #define KT_DEAD 4 #define KT_CONS 5 #define KT_CUR 6 #define KT_SHIFT 7 #define KT_META 8 #define KT_ASCII 9 #define KT_LOCK 10 #define KT_LETTER 11 /* symbole affecte par CapsLock */ #define KT_SLOCK 12 /* touches d'edition */ #define K_FIND 20 #define K_INSERT 21 #define K_REMOVE 22 #define K_SELECT 23 #define K_PGUP 24 #define K_PGDN 25 /* pave numerique */ #define K_P0 0 #define K_P1 1 #define K_P2 2 #define K_P3 3 #define K_P4 4 #define K_P5 5 #define K_P6 6 #define K_P7 7 #define K_P8 8 #define K_P9 9 #define K_PPLUS 10 #define K_PMINUS 11 #define K_PSTAR 12 #define K_PSLASH 13 #define K_PENTER 14 #define K_PDOT 16 /* fleches */ #define K_DOWN 0 #define K_LEFT 1 #define K_RIGHT 2 #define K_UP 3 /* touches de controle */ #define K_SHIFT 0 #define K_CTRL 2 #define K_ALT 2 #define K_ALTGR 1 #define K_SHIFTL 4 #define K_SHIFTR 5 #define K_CTRLL 6 #define K_CTRLR 7 #define K_CAPSSHIFT 8 static struct vc_struct dummy_con; /* Variables d'etat */ static int shift_down[9]; static int dead_key_next; static unsigned char diacr; static int repeating; static unsigned short prev_scancode; static unsigned char prev_keycode; static struct vc_struct *con; static int char_put; #define put_queue(c) ({ \ tty_put(&con->tty, (c)); \ char_put = 1; \ }) #define puts_queue(s) ({ \ const char *tmp = (s); \ while (*tmp) \ put_queue(*tmp++); \ }) /* dummy_con */ static int dummy() { return 0; } static const struct tty_vtbl_struct dummy_con_tty_vtbl = { .do_output = vc_tty_do_output, .do_stop = vc_tty_do_stop, .do_start = vc_tty_do_start, .do_ioctl = vc_tty_do_ioctl }; static const struct vc_vtbl_struct dummy_con_vc_vtbl = { .do_stopped_changed = (void (*)())dummy }; static const struct video_driver_struct dummy_video_driver = { .get_lines = (unsigned short (*)())dummy, .get_columns = (unsigned short (*)())dummy, .get_cursor_type = (unsigned char (*)())dummy, .set_cursor_type = (void (*)(unsigned char))dummy, .get_cursor_pos = (unsigned short (*)())dummy, .set_cursor_pos = (void (*)(unsigned short))dummy, .set_attr = (void (*)(const struct attr_mode_struct *))dummy, .putc = (void (*)(unsigned short, unsigned char))dummy, .clear = (void (*)(unsigned short, unsigned short))dummy, .scroll = (void (*)())dummy, .history_scroll = (void (*)(short))dummy, .history_cancel = (void (*)())dummy }; static void dummy_con_init() { dummy_con.tty.vptr = &dummy_con_tty_vtbl; dummy_con.vptr = &dummy_con_vc_vtbl; vc_init(&dummy_con, 0, 0); dummy_con.vt.do_kb_reset = (void (*)())dummy; dummy_con.vt.driver = &dummy_video_driver; dummy_con.vt.do_beep = (void (*)())dummy; dummy_con.lockstate = 0; } /* Routines du 8042 */ static unsigned char read_status() { return inb(0x64); } /* Attend que le tampon de sortie du controleur ne soit pas vide. */ static inline int wait_input_full() { int i; for (i = 0; i < 0x10000; i++) if (read_status() & 0x01) return 1; return 0; } /* Attend que le tampon d'entree du controleur soit vide. */ static inline int wait_output_empty() { int i; for (i = 0; i < 0x10000; i++) if (!(read_status() & 0x02)) return 1; return 0; } static void write_command(unsigned char command) { wait_output_empty(); outb(command, 0x64); } static unsigned char read_data() { if (!wait_input_full()) return 0; return inb(0x60); } static void write_data(unsigned char data) { wait_output_empty(); outb(data, 0x60); } static int send_data(unsigned char data) { int try; for (try = 0; try < 3; try++) { write_data(data); switch (read_data()) { case KB_ACK: return 1; case KB_RESEND: continue; default: return 0; } } return 0; } /* Commandes de bas niveau */ static void set_leds(unsigned char leds) { if (!send_data(KB_WRITE_LEDS)) return; send_data(leds); } static void reset_cpu() { write_command(CT_PULSE_PORTS | 0xe); /* pulse reset line */ } /* Console */ /* Synchronise la console avec le pilote. */ static void synchronize() { unsigned int i; con->modifiers = 0; for (i = 0; i < sizeof shift_down / sizeof *shift_down; i++) if (shift_down[i]) con->modifiers |= 1 << i; set_leds(con->lockstate); } /* Utilitaires */ static void applkey(unsigned char value) { put_queue('\033'); put_queue('['); put_queue(value); } static unsigned char handle_diacr(unsigned char c) { unsigned char d; unsigned int i; d = diacr; diacr = 0; for (i = 0; i < accent_table_size; i++) if (accent_table[i].diacr == d && accent_table[i].base == c) return accent_table[i].result; if (c == ' ' || c == d) return d; put_queue(d); return c; } static void __attribute__ ((noreturn)) reboot() { reset_cpu(); die(); } /* Conversion des scancodes en keycodes */ static int translate(unsigned char scancode, unsigned char *keycode) { unsigned char up_flag; /* Scancodes etendus */ if (scancode == 0xe0 || scancode == 0xe1) { prev_scancode = scancode; return 0; } /* On ignore 0xfa (acknowledge), 0xfe (resend), 0x00 (keyboard buffer overflow) et 0xff (keyboard error) */ if (scancode == 0xfa || scancode == 0xfe) return 0; if (scancode == 0x00 || scancode == 0xff) { prev_scancode = 0; return 0; } up_flag = scancode & 0x80; scancode &= 0x7f; if (prev_scancode) { /* La touche Pause genere e1 1d 45 e1 9d c5 quand elle est pressee, et rien quand elle est relachee */ 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 { prev_scancode = 0; return 0; } } else { prev_scancode = 0; /* On ignore les "fake shifts" */ if (scancode == 0x2a || scancode == 0x36) return 0; if (e0_keys[scancode]) *keycode = e0_keys[scancode]; else return 0; } } else if (scancode >= 89) return 0; else *keycode = scancode; *keycode |= up_flag; repeating = *keycode == prev_keycode; prev_keycode = *keycode; return 1; } /* Gestionnaires des fonctions speciales */ static void fn_enter() { if (diacr) { put_queue(diacr); diacr = 0; } put_queue('\r'); } static void fn_caps_toggle() { if (repeating) return; con->lockstate ^= CAPS_LOCK; set_leds(con->lockstate); } static void fn_hold() { if (repeating) return; con->lockstate ^= SCROLL_LOCK; set_leds(con->lockstate); con->lockstate & SCROLL_LOCK ? tty_stop(&con->tty) : tty_start(&con->tty); } static void fn_num() { if (repeating) return; con->lockstate ^= NUM_LOCK; set_leds(con->lockstate); } static void fn_history_pgdn() { vc_history_pgdn(con); } static void fn_history_pgup() { vc_history_pgup(con); } static void fn_history_down() { vc_history_down(con); } static void fn_history_up() { vc_history_up(con); } static void __attribute__ ((noreturn)) fn_reboot() { reboot(); } static void fn_compose() { dead_key_next = 1; } static void fn_null() {} static void (*fn_handler[])() = { fn_null, fn_enter, fn_null, fn_null, fn_null, fn_null, fn_null, fn_caps_toggle, fn_num, fn_hold, fn_history_pgdn, fn_history_pgup, fn_reboot, fn_null, fn_compose, fn_null, fn_null, fn_null, fn_null, fn_num, fn_history_down, fn_history_up }; /* Gestionnaires des touches speciales */ static void k_self(unsigned char value, int up_flag) { if (up_flag) return; if (diacr) value = handle_diacr(value); if (dead_key_next) { dead_key_next = 0; diacr = value; return; } put_queue(value); } static void k_fn(unsigned char value, int up_flag) { if (up_flag) return; if (func_table[value]) puts_queue(func_table[value]); } static void k_spec(unsigned char value, int up_flag) { if (up_flag) return; if (value >= sizeof fn_handler / sizeof *fn_handler) return; fn_handler[value](); } static void k_cur(unsigned char value, int up_flag) { static const char *cur_chars = "BDCA"; if (up_flag) return; applkey(cur_chars[value]); } static void k_shift(unsigned char value, int up_flag) { if (repeating) return; if (value == K_CAPSSHIFT) { value = K_SHIFT; con->lockstate &= ~CAPS_LOCK; set_leds(con->lockstate); } if (up_flag) { if (shift_down[value]) shift_down[value]--; } else shift_down[value]++; if (shift_down[value]) con->modifiers |= (1 << value); else con->modifiers &= ~(1 << value); } static void k_meta(unsigned char value, int up_flag) { if (up_flag) return; put_queue('\033'); put_queue(value); } static void k_dead(unsigned char value, int up_flag) { if (up_flag) return; diacr = diacr ? handle_diacr(value) : value; } static void k_ignore(unsigned char value, int up_flag) {} static void k_pad(unsigned char value, int up_flag) { static const char *pad_chars = "0123456789+-*/\015,.?()#"; if (up_flag) return; if (!(con->lockstate & NUM_LOCK)) switch (value) { case K_P0: return k_fn(K_INSERT, 0); case K_P1: return k_fn(K_SELECT, 0); case K_P2: return k_cur(K_DOWN, 0); case K_P3: return k_fn(K_PGDN, 0); case K_P4: return k_cur(K_LEFT, 0); case K_P5: return applkey('G'); case K_P6: return k_cur(K_RIGHT, 0); case K_P7: return k_fn(K_FIND, 0); case K_P8: return k_cur(K_UP, 0); case K_P9: return k_fn(K_PGUP, 0); case K_PDOT: return k_fn(K_REMOVE, 0); } put_queue(pad_chars[value]); } static void (*const k_handler[16])(unsigned char value, int up_flag) = { k_self, k_fn, k_spec, k_pad, k_ignore, k_ignore, k_cur, k_shift, k_meta, k_ignore, k_ignore, k_ignore, k_ignore, k_dead, k_ignore, k_ignore }; /* Traitement des keycodes */ static void process_keycode(unsigned char keycode) { unsigned char up_flag; const unsigned short *key_map; unsigned short keysym; unsigned char type; up_flag = keycode & 0x80; keycode &= 0x7f; if (con->modifiers >= sizeof key_maps / sizeof *key_maps) return; key_map = key_maps[con->modifiers]; if (!key_map) return; keysym = key_map[keycode]; type = keysym >> 8; if (type < 0xf0) return; type -= 0xf0; if (type == KT_LETTER) { if (con->lockstate & CAPS_LOCK) { key_map = key_maps[con->modifiers ^ SHIFT]; if (key_map) keysym = key_map[keycode]; } type = KT_LATIN; } char_put = 0; (*k_handler[type])(keysym & 0xff, up_flag); if (char_put) { vc_history_cancel(con); tty_process_input_queue(&con->tty); } } /* Fonctions exportees */ void kb_init() { dummy_con_init(); memset(shift_down, 0, sizeof shift_down / sizeof *shift_down); dead_key_next = 0; diacr = 0; repeating = 0; prev_scancode = 0; prev_keycode = 0; con = &dummy_con; synchronize(); } void kb_print_info() { printk("Keyboard controller: Intel 8042\n"); printk("Keymap: " KEYMAP_NAME "\n"); } void kb_set_con(struct vc_struct *vc) { int intr; disable_intr(intr); /* pas d'interruption clavier pendant la synchronisation */ con = vc; synchronize(); restore_intr(intr); } void kb_reset() { int intr; disable_intr(intr); /* pas d'interruption clavier pendant la reinitialisation */ con->lockstate = 0; synchronize(); restore_intr(intr); } void __attribute__ ((noreturn)) kb_reboot() { cli(); reboot(); } void do_keyboard_interrupt() { unsigned char scancode, keycode; if (!(scancode = read_data())) return; if (translate(scancode, &keycode)) process_keycode(keycode); }