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 + -
显示快捷键?