keyboard.c
来自「linux 内核源代码」· C语言 代码 · 共 1,426 行 · 第 1/3 页
C
1,426 行
/* * linux/drivers/char/keyboard.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) * * Modified to provide 'generic' keyboard support by Hamish Macdonald * Merge with the m68k keyboard driver and split-off of the PC low-level * parts by Geert Uytterhoeven, May 1997 * * 27-05-97: Added support for the Magic SysRq Key (Martin Mares) * 30-07-98: Dead keys redone, aeb@cwi.nl. * 21-08-02: Converted to input API, major cleanup. (Vojtech Pavlik) */#include <linux/consolemap.h>#include <linux/module.h>#include <linux/sched.h>#include <linux/tty.h>#include <linux/tty_flip.h>#include <linux/mm.h>#include <linux/string.h>#include <linux/init.h>#include <linux/slab.h>#include <linux/irq.h>#include <linux/kbd_kern.h>#include <linux/kbd_diacr.h>#include <linux/vt_kern.h>#include <linux/consolemap.h>#include <linux/sysrq.h>#include <linux/input.h>#include <linux/reboot.h>#include <linux/notifier.h>extern void ctrl_alt_del(void);/* * Exported functions/variables */#define KBD_DEFMODE ((1 << VC_REPEAT) | (1 << VC_META))/* * Some laptops take the 789uiojklm,. keys as number pad when NumLock is on. * This seems a good reason to start with NumLock off. On HIL keyboards * of PARISC machines however there is no NumLock key and everyone expects the keypad * to be used for numbers. */#if defined(CONFIG_PARISC) && (defined(CONFIG_KEYBOARD_HIL) || defined(CONFIG_KEYBOARD_HIL_OLD))#define KBD_DEFLEDS (1 << VC_NUMLOCK)#else#define KBD_DEFLEDS 0#endif#define KBD_DEFLOCK 0void compute_shiftstate(void);/* * Handler Tables. */#define K_HANDLERS\ k_self, k_fn, k_spec, k_pad,\ k_dead, k_cons, k_cur, k_shift,\ k_meta, k_ascii, k_lock, k_lowercase,\ k_slock, k_dead2, k_brl, k_ignoretypedef void (k_handler_fn)(struct vc_data *vc, unsigned char value, char up_flag);static k_handler_fn K_HANDLERS;k_handler_fn *k_handler[16] = { K_HANDLERS };EXPORT_SYMBOL_GPL(k_handler);#define FN_HANDLERS\ fn_null, fn_enter, fn_show_ptregs, fn_show_mem,\ fn_show_state, fn_send_intr, fn_lastcons, fn_caps_toggle,\ fn_num, fn_hold, fn_scroll_forw, fn_scroll_back,\ fn_boot_it, fn_caps_on, fn_compose, fn_SAK,\ fn_dec_console, fn_inc_console, fn_spawn_con, fn_bare_numtypedef void (fn_handler_fn)(struct vc_data *vc);static fn_handler_fn FN_HANDLERS;static fn_handler_fn *fn_handler[] = { FN_HANDLERS };/* * Variables exported for vt_ioctl.c *//* maximum values each key_handler can handle */const int max_vals[] = { 255, ARRAY_SIZE(func_table) - 1, ARRAY_SIZE(fn_handler) - 1, NR_PAD - 1, NR_DEAD - 1, 255, 3, NR_SHIFT - 1, 255, NR_ASCII - 1, NR_LOCK - 1, 255, NR_LOCK - 1, 255, NR_BRL - 1};const int NR_TYPES = ARRAY_SIZE(max_vals);struct kbd_struct kbd_table[MAX_NR_CONSOLES];static struct kbd_struct *kbd = kbd_table;struct vt_spawn_console vt_spawn_con = { .lock = __SPIN_LOCK_UNLOCKED(vt_spawn_con.lock), .pid = NULL, .sig = 0,};/* * Variables exported for vt.c */int shift_state = 0;/* * Internal Data. */static struct input_handler kbd_handler;static unsigned long key_down[BITS_TO_LONGS(KEY_CNT)]; /* keyboard key bitmap */static unsigned char shift_down[NR_SHIFT]; /* shift state counters.. */static int dead_key_next;static int npadch = -1; /* -1 or number assembled on pad */static unsigned int diacr;static char rep; /* flag telling character repeat */static unsigned char ledstate = 0xff; /* undefined */static unsigned char ledioctl;static struct ledptr { unsigned int *addr; unsigned int mask; unsigned char valid:1;} ledptrs[3];/* Simple translation table for the SysRq keys */#ifdef CONFIG_MAGIC_SYSRQunsigned char kbd_sysrq_xlate[KEY_MAX + 1] = "\000\0331234567890-=\177\t" /* 0x00 - 0x0f */ "qwertyuiop[]\r\000as" /* 0x10 - 0x1f */ "dfghjkl;'`\000\\zxcv" /* 0x20 - 0x2f */ "bnm,./\000*\000 \000\201\202\203\204\205" /* 0x30 - 0x3f */ "\206\207\210\211\212\000\000789-456+1" /* 0x40 - 0x4f */ "230\177\000\000\213\214\000\000\000\000\000\000\000\000\000\000" /* 0x50 - 0x5f */ "\r\000/"; /* 0x60 - 0x6f */static int sysrq_down;static int sysrq_alt_use;#endifstatic int sysrq_alt;/* * Notifier list for console keyboard events */static ATOMIC_NOTIFIER_HEAD(keyboard_notifier_list);int register_keyboard_notifier(struct notifier_block *nb){ return atomic_notifier_chain_register(&keyboard_notifier_list, nb);}EXPORT_SYMBOL_GPL(register_keyboard_notifier);int unregister_keyboard_notifier(struct notifier_block *nb){ return atomic_notifier_chain_unregister(&keyboard_notifier_list, nb);}EXPORT_SYMBOL_GPL(unregister_keyboard_notifier);/* * Translation of scancodes to keycodes. We set them on only the first * keyboard in the list that accepts the scancode and keycode. * Explanation for not choosing the first attached keyboard anymore: * USB keyboards for example have two event devices: one for all "normal" * keys and one for extra function keys (like "volume up", "make coffee", * etc.). So this means that scancodes for the extra function keys won't * be valid for the first event device, but will be for the second. */int getkeycode(unsigned int scancode){ struct input_handle *handle; int keycode; int error = -ENODEV; list_for_each_entry(handle, &kbd_handler.h_list, h_node) { error = handle->dev->getkeycode(handle->dev, scancode, &keycode); if (!error) return keycode; } return error;}int setkeycode(unsigned int scancode, unsigned int keycode){ struct input_handle *handle; int error = -ENODEV; list_for_each_entry(handle, &kbd_handler.h_list, h_node) { error = handle->dev->setkeycode(handle->dev, scancode, keycode); if (!error) break; } return error;}/* * Making beeps and bells. */static void kd_nosound(unsigned long ignored){ struct input_handle *handle; list_for_each_entry(handle, &kbd_handler.h_list, h_node) { if (test_bit(EV_SND, handle->dev->evbit)) { if (test_bit(SND_TONE, handle->dev->sndbit)) input_inject_event(handle, EV_SND, SND_TONE, 0); if (test_bit(SND_BELL, handle->dev->sndbit)) input_inject_event(handle, EV_SND, SND_BELL, 0); } }}static DEFINE_TIMER(kd_mksound_timer, kd_nosound, 0, 0);void kd_mksound(unsigned int hz, unsigned int ticks){ struct list_head *node; del_timer(&kd_mksound_timer); if (hz) { list_for_each_prev(node, &kbd_handler.h_list) { struct input_handle *handle = to_handle_h(node); if (test_bit(EV_SND, handle->dev->evbit)) { if (test_bit(SND_TONE, handle->dev->sndbit)) { input_inject_event(handle, EV_SND, SND_TONE, hz); break; } if (test_bit(SND_BELL, handle->dev->sndbit)) { input_inject_event(handle, EV_SND, SND_BELL, 1); break; } } } if (ticks) mod_timer(&kd_mksound_timer, jiffies + ticks); } else kd_nosound(0);}/* * Setting the keyboard rate. */int kbd_rate(struct kbd_repeat *rep){ struct list_head *node; unsigned int d = 0; unsigned int p = 0; list_for_each(node, &kbd_handler.h_list) { struct input_handle *handle = to_handle_h(node); struct input_dev *dev = handle->dev; if (test_bit(EV_REP, dev->evbit)) { if (rep->delay > 0) input_inject_event(handle, EV_REP, REP_DELAY, rep->delay); if (rep->period > 0) input_inject_event(handle, EV_REP, REP_PERIOD, rep->period); d = dev->rep[REP_DELAY]; p = dev->rep[REP_PERIOD]; } } rep->delay = d; rep->period = p; return 0;}/* * Helper Functions. */static void put_queue(struct vc_data *vc, int ch){ struct tty_struct *tty = vc->vc_tty; if (tty) { tty_insert_flip_char(tty, ch, 0); con_schedule_flip(tty); }}static void puts_queue(struct vc_data *vc, char *cp){ struct tty_struct *tty = vc->vc_tty; if (!tty) return; while (*cp) { tty_insert_flip_char(tty, *cp, 0); cp++; } con_schedule_flip(tty);}static void applkey(struct vc_data *vc, int key, char mode){ static char buf[] = { 0x1b, 'O', 0x00, 0x00 }; buf[1] = (mode ? 'O' : '['); buf[2] = key; puts_queue(vc, buf);}/* * Many other routines do put_queue, but I think either * they produce ASCII, or they produce some user-assigned * string, and in both cases we might assume that it is * in utf-8 already. */static void to_utf8(struct vc_data *vc, uint c){ if (c < 0x80) /* 0******* */ put_queue(vc, c); else if (c < 0x800) { /* 110***** 10****** */ put_queue(vc, 0xc0 | (c >> 6)); put_queue(vc, 0x80 | (c & 0x3f)); } else if (c < 0x10000) { if (c >= 0xD800 && c < 0xE000) return; if (c == 0xFFFF) return; /* 1110**** 10****** 10****** */ put_queue(vc, 0xe0 | (c >> 12)); put_queue(vc, 0x80 | ((c >> 6) & 0x3f)); put_queue(vc, 0x80 | (c & 0x3f)); } else if (c < 0x110000) { /* 11110*** 10****** 10****** 10****** */ put_queue(vc, 0xf0 | (c >> 18)); put_queue(vc, 0x80 | ((c >> 12) & 0x3f)); put_queue(vc, 0x80 | ((c >> 6) & 0x3f)); put_queue(vc, 0x80 | (c & 0x3f)); }}/* * Called after returning from RAW mode or when changing consoles - recompute * shift_down[] and shift_state from key_down[] maybe called when keymap is * undefined, so that shiftkey release is seen */void compute_shiftstate(void){ unsigned int i, j, k, sym, val; shift_state = 0; memset(shift_down, 0, sizeof(shift_down)); for (i = 0; i < ARRAY_SIZE(key_down); i++) { if (!key_down[i]) continue; k = i * BITS_PER_LONG; for (j = 0; j < BITS_PER_LONG; j++, k++) { if (!test_bit(k, key_down)) continue; sym = U(key_maps[0][k]); if (KTYP(sym) != KT_SHIFT && KTYP(sym) != KT_SLOCK) continue; val = KVAL(sym); if (val == KVAL(K_CAPSSHIFT)) val = KVAL(K_SHIFT); shift_down[val]++; shift_state |= (1 << val); } }}/* * We have a combining character DIACR here, followed by the character CH. * If the combination occurs in the table, return the corresponding value. * Otherwise, if CH is a space or equals DIACR, return DIACR. * Otherwise, conclude that DIACR was not combining after all, * queue it and return CH. */static unsigned int handle_diacr(struct vc_data *vc, unsigned int ch){ unsigned int d = diacr; unsigned int i; diacr = 0; if ((d & ~0xff) == BRL_UC_ROW) { if ((ch & ~0xff) == BRL_UC_ROW) return d | ch; } else { for (i = 0; i < accent_table_size; i++) if (accent_table[i].diacr == d && accent_table[i].base == ch) return accent_table[i].result; } if (ch == ' ' || ch == (BRL_UC_ROW|0) || ch == d) return d; if (kbd->kbdmode == VC_UNICODE) to_utf8(vc, d); else { int c = conv_uni_to_8bit(d); if (c != -1) put_queue(vc, c); } return ch;}/* * Special function handlers */static void fn_enter(struct vc_data *vc){ if (diacr) { if (kbd->kbdmode == VC_UNICODE) to_utf8(vc, diacr); else { int c = conv_uni_to_8bit(diacr); if (c != -1) put_queue(vc, c); } diacr = 0; } put_queue(vc, 13); if (vc_kbd_mode(kbd, VC_CRLF)) put_queue(vc, 10);}static void fn_caps_toggle(struct vc_data *vc){ if (rep) return; chg_vc_kbd_led(kbd, VC_CAPSLOCK);}static void fn_caps_on(struct vc_data *vc){ if (rep) return; set_vc_kbd_led(kbd, VC_CAPSLOCK);}static void fn_show_ptregs(struct vc_data *vc){ struct pt_regs *regs = get_irq_regs(); if (regs) show_regs(regs);}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?