📄 atkbd.c
字号:
/* * AT and PS/2 keyboard driver * * Copyright (c) 1999-2002 Vojtech Pavlik *//* * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published by * the Free Software Foundation. *//* * This driver can handle standard AT keyboards and PS/2 keyboards in * Translated and Raw Set 2 and Set 3, as well as AT keyboards on dumb * input-only controllers and AT keyboards connected over a one way RS232 * converter. */#include <linux/delay.h>#include <linux/module.h>#include <linux/moduleparam.h>#include <linux/slab.h>#include <linux/interrupt.h>#include <linux/init.h>#include <linux/input.h>#include <linux/serio.h>#include <linux/workqueue.h>#include <linux/libps2.h>#define DRIVER_DESC "AT and PS/2 keyboard driver"MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");MODULE_DESCRIPTION(DRIVER_DESC);MODULE_LICENSE("GPL");static int atkbd_set = 2;module_param_named(set, atkbd_set, int, 0);MODULE_PARM_DESC(set, "Select keyboard code set (2 = default, 3 = PS/2 native)");#if defined(__i386__) || defined(__x86_64__) || defined(__hppa__)static int atkbd_reset;#elsestatic int atkbd_reset = 1;#endifmodule_param_named(reset, atkbd_reset, bool, 0);MODULE_PARM_DESC(reset, "Reset keyboard during initialization");static int atkbd_softrepeat;module_param_named(softrepeat, atkbd_softrepeat, bool, 0);MODULE_PARM_DESC(softrepeat, "Use software keyboard repeat");static int atkbd_softraw = 1;module_param_named(softraw, atkbd_softraw, bool, 0);MODULE_PARM_DESC(softraw, "Use software generated rawmode");static int atkbd_scroll;module_param_named(scroll, atkbd_scroll, bool, 0);MODULE_PARM_DESC(scroll, "Enable scroll-wheel on MS Office and similar keyboards");static int atkbd_extra;module_param_named(extra, atkbd_extra, bool, 0);MODULE_PARM_DESC(extra, "Enable extra LEDs and keys on IBM RapidAcces, EzKey and similar keyboards");__obsolete_setup("atkbd_set=");__obsolete_setup("atkbd_reset");__obsolete_setup("atkbd_softrepeat=");/* * Scancode to keycode tables. These are just the default setting, and * are loadable via an userland utility. */#if defined(__hppa__)#include "hpps2atkbd.h"#elsestatic unsigned char atkbd_set2_keycode[512] = { 0, 67, 65, 63, 61, 59, 60, 88, 0, 68, 66, 64, 62, 15, 41,117, 0, 56, 42, 93, 29, 16, 2, 0, 0, 0, 44, 31, 30, 17, 3, 0, 0, 46, 45, 32, 18, 5, 4, 95, 0, 57, 47, 33, 20, 19, 6,183, 0, 49, 48, 35, 34, 21, 7,184, 0, 0, 50, 36, 22, 8, 9,185, 0, 51, 37, 23, 24, 11, 10, 0, 0, 52, 53, 38, 39, 25, 12, 0, 0, 89, 40, 0, 26, 13, 0, 0, 58, 54, 28, 27, 0, 43, 0, 85, 0, 86, 91, 90, 92, 0, 14, 94, 0, 79,124, 75, 71,121, 0, 0, 82, 83, 80, 76, 77, 72, 1, 69, 87, 78, 81, 74, 55, 73, 70, 99, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 217,100,255, 0, 97,165, 0, 0,156, 0, 0, 0, 0, 0, 0,125, 173,114, 0,113, 0, 0, 0,126,128, 0, 0,140, 0, 0, 0,127, 159, 0,115, 0,164, 0, 0,116,158, 0,150,166, 0, 0, 0,142, 157, 0, 0, 0, 0, 0, 0, 0,155, 0, 98, 0, 0,163, 0, 0, 226, 0, 0, 0, 0, 0, 0, 0, 0,255, 96, 0, 0, 0,143, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,107, 0,105,102, 0, 0,112, 110,111,108,112,106,103, 0,119, 0,118,109, 0, 99,104,119, 0, 0, 0, 0, 65, 99,};#endifstatic unsigned char atkbd_set3_keycode[512] = { 0, 0, 0, 0, 0, 0, 0, 59, 1,138,128,129,130, 15, 41, 60, 131, 29, 42, 86, 58, 16, 2, 61,133, 56, 44, 31, 30, 17, 3, 62, 134, 46, 45, 32, 18, 5, 4, 63,135, 57, 47, 33, 20, 19, 6, 64, 136, 49, 48, 35, 34, 21, 7, 65,137,100, 50, 36, 22, 8, 9, 66, 125, 51, 37, 23, 24, 11, 10, 67,126, 52, 53, 38, 39, 25, 12, 68, 113,114, 40, 43, 26, 13, 87, 99, 97, 54, 28, 27, 43, 43, 88, 70, 108,105,119,103,111,107, 14,110, 0, 79,106, 75, 71,109,102,104, 82, 83, 80, 76, 77, 72, 69, 98, 0, 96, 81, 0, 78, 73, 55,183, 184,185,186,187, 74, 94, 92, 93, 0, 0, 0,125,126,127,112, 0, 0,139,150,163,165,115,152,150,166,140,160,154,113,114,167,168, 148,149,147,140};static unsigned char atkbd_unxlate_table[128] = { 0,118, 22, 30, 38, 37, 46, 54, 61, 62, 70, 69, 78, 85,102, 13, 21, 29, 36, 45, 44, 53, 60, 67, 68, 77, 84, 91, 90, 20, 28, 27, 35, 43, 52, 51, 59, 66, 75, 76, 82, 14, 18, 93, 26, 34, 33, 42, 50, 49, 58, 65, 73, 74, 89,124, 17, 41, 88, 5, 6, 4, 12, 3, 11, 2, 10, 1, 9,119,126,108,117,125,123,107,115,116,121,105, 114,122,112,113,127, 96, 97,120, 7, 15, 23, 31, 39, 47, 55, 63, 71, 79, 86, 94, 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 87,111, 19, 25, 57, 81, 83, 92, 95, 98, 99,100,101,103,104,106,109,110};#define ATKBD_CMD_SETLEDS 0x10ed#define ATKBD_CMD_GSCANSET 0x11f0#define ATKBD_CMD_SSCANSET 0x10f0#define ATKBD_CMD_GETID 0x02f2#define ATKBD_CMD_SETREP 0x10f3#define ATKBD_CMD_ENABLE 0x00f4#define ATKBD_CMD_RESET_DIS 0x00f5#define ATKBD_CMD_SETALL_MBR 0x00fa#define ATKBD_CMD_RESET_BAT 0x02ff#define ATKBD_CMD_RESEND 0x00fe#define ATKBD_CMD_EX_ENABLE 0x10ea#define ATKBD_CMD_EX_SETLEDS 0x20eb#define ATKBD_CMD_OK_GETID 0x02e8#define ATKBD_RET_ACK 0xfa#define ATKBD_RET_NAK 0xfe#define ATKBD_RET_BAT 0xaa#define ATKBD_RET_EMUL0 0xe0#define ATKBD_RET_EMUL1 0xe1#define ATKBD_RET_RELEASE 0xf0#define ATKBD_RET_HANGUEL 0xf1#define ATKBD_RET_HANJA 0xf2#define ATKBD_RET_ERR 0xff#define ATKBD_KEY_UNKNOWN 0#define ATKBD_KEY_NULL 255#define ATKBD_SCR_1 254#define ATKBD_SCR_2 253#define ATKBD_SCR_4 252#define ATKBD_SCR_8 251#define ATKBD_SCR_CLICK 250#define ATKBD_SPECIAL 250static unsigned char atkbd_scroll_keys[5][2] = { { ATKBD_SCR_1, 0x45 }, { ATKBD_SCR_2, 0x29 }, { ATKBD_SCR_4, 0x36 }, { ATKBD_SCR_8, 0x27 }, { ATKBD_SCR_CLICK, 0x60 },};/* * The atkbd control structure */struct atkbd { struct ps2dev ps2dev; /* Written only during init */ char name[64]; char phys[32]; struct input_dev dev; unsigned short id; unsigned char keycode[512]; unsigned char set; unsigned char translated; unsigned char extra; unsigned char write; unsigned char softrepeat; unsigned char softraw; unsigned char scroll; unsigned char enabled; /* Accessed only from interrupt */ unsigned char emul; unsigned char resend; unsigned char release; unsigned char bat_xl; unsigned int last; unsigned long time;};static ssize_t atkbd_attr_show_helper(struct device *dev, char *buf, ssize_t (*handler)(struct atkbd *, char *));static ssize_t atkbd_attr_set_helper(struct device *dev, const char *buf, size_t count, ssize_t (*handler)(struct atkbd *, const char *, size_t));#define ATKBD_DEFINE_ATTR(_name) \static ssize_t atkbd_show_##_name(struct atkbd *, char *); \static ssize_t atkbd_set_##_name(struct atkbd *, const char *, size_t); \static ssize_t atkbd_do_show_##_name(struct device *d, char *b) \{ \ return atkbd_attr_show_helper(d, b, atkbd_show_##_name); \} \static ssize_t atkbd_do_set_##_name(struct device *d, const char *b, size_t s) \{ \ return atkbd_attr_set_helper(d, b, s, atkbd_set_##_name); \} \static struct device_attribute atkbd_attr_##_name = \ __ATTR(_name, S_IWUSR | S_IRUGO, atkbd_do_show_##_name, atkbd_do_set_##_name);ATKBD_DEFINE_ATTR(extra);ATKBD_DEFINE_ATTR(scroll);ATKBD_DEFINE_ATTR(set);ATKBD_DEFINE_ATTR(softrepeat);ATKBD_DEFINE_ATTR(softraw);static void atkbd_report_key(struct input_dev *dev, struct pt_regs *regs, int code, int value){ input_regs(dev, regs); if (value == 3) { input_report_key(dev, code, 1); input_sync(dev); input_report_key(dev, code, 0); } else input_event(dev, EV_KEY, code, value); input_sync(dev);}/* * atkbd_interrupt(). Here takes place processing of data received from * the keyboard into events. */static irqreturn_t atkbd_interrupt(struct serio *serio, unsigned char data, unsigned int flags, struct pt_regs *regs){ struct atkbd *atkbd = serio->private; unsigned int code = data; int scroll = 0, click = -1; int value;#ifdef ATKBD_DEBUG printk(KERN_DEBUG "atkbd.c: Received %02x flags %02x\n", data, flags);#endif#if !defined(__i386__) && !defined (__x86_64__) if ((flags & (SERIO_FRAME | SERIO_PARITY)) && (~flags & SERIO_TIMEOUT) && !atkbd->resend && atkbd->write) { printk(KERN_WARNING "atkbd.c: frame/parity error: %02x\n", flags); serio_write(serio, ATKBD_CMD_RESEND); atkbd->resend = 1; goto out; } if (!flags && data == ATKBD_RET_ACK) atkbd->resend = 0;#endif if (unlikely(atkbd->ps2dev.flags & PS2_FLAG_ACK)) if (ps2_handle_ack(&atkbd->ps2dev, data)) goto out; if (unlikely(atkbd->ps2dev.flags & PS2_FLAG_CMD)) if (ps2_handle_response(&atkbd->ps2dev, data)) goto out; if (!atkbd->enabled) goto out; input_event(&atkbd->dev, EV_MSC, MSC_RAW, code); if (atkbd->translated) { if (atkbd->emul || !(code == ATKBD_RET_EMUL0 || code == ATKBD_RET_EMUL1 || code == ATKBD_RET_HANGUEL || code == ATKBD_RET_HANJA || code == ATKBD_RET_ERR || (code == ATKBD_RET_BAT && !atkbd->bat_xl))) { atkbd->release = code >> 7; code &= 0x7f; } if (!atkbd->emul && (code & 0x7f) == (ATKBD_RET_BAT & 0x7f)) atkbd->bat_xl = !atkbd->release; } switch (code) { case ATKBD_RET_BAT: atkbd->enabled = 0; serio_rescan(atkbd->ps2dev.serio); goto out; case ATKBD_RET_EMUL0: atkbd->emul = 1; goto out; case ATKBD_RET_EMUL1: atkbd->emul = 2; goto out; case ATKBD_RET_RELEASE: atkbd->release = 1; goto out; case ATKBD_RET_HANGUEL: atkbd_report_key(&atkbd->dev, regs, KEY_HANGUEL, 3); goto out; case ATKBD_RET_HANJA: atkbd_report_key(&atkbd->dev, regs, KEY_HANJA, 3); goto out; case ATKBD_RET_ERR: printk(KERN_DEBUG "atkbd.c: Keyboard on %s reports too many keys pressed.\n", serio->phys); goto out; } if (atkbd->set != 3) code = (code & 0x7f) | ((code & 0x80) << 1); if (atkbd->emul) { if (--atkbd->emul) goto out; code |= (atkbd->set != 3) ? 0x80 : 0x100; } if (atkbd->keycode[code] != ATKBD_KEY_NULL) input_event(&atkbd->dev, EV_MSC, MSC_SCAN, code); switch (atkbd->keycode[code]) { case ATKBD_KEY_NULL: break; case ATKBD_KEY_UNKNOWN: if (data == ATKBD_RET_ACK || data == ATKBD_RET_NAK) { printk(KERN_WARNING "atkbd.c: Spurious %s on %s. Some program, " "like XFree86, might be trying access hardware directly.\n", data == ATKBD_RET_ACK ? "ACK" : "NAK", serio->phys); } else { printk(KERN_WARNING "atkbd.c: Unknown key %s " "(%s set %d, code %#x on %s).\n", atkbd->release ? "released" : "pressed", atkbd->translated ? "translated" : "raw", atkbd->set, code, serio->phys); printk(KERN_WARNING "atkbd.c: Use 'setkeycodes %s%02x <keycode>' " "to make it known.\n", code & 0x80 ? "e0" : "", code & 0x7f); } input_sync(&atkbd->dev); break; case ATKBD_SCR_1: scroll = 1 - atkbd->release * 2; break; case ATKBD_SCR_2: scroll = 2 - atkbd->release * 4; break; case ATKBD_SCR_4: scroll = 4 - atkbd->release * 8; break; case ATKBD_SCR_8: scroll = 8 - atkbd->release * 16; break; case ATKBD_SCR_CLICK: click = !atkbd->release; break; default: value = atkbd->release ? 0 : (1 + (!atkbd->softrepeat && test_bit(atkbd->keycode[code], atkbd->dev.key))); switch (value) { /* Workaround Toshiba laptop multiple keypress */ case 0: atkbd->last = 0; break; case 1: atkbd->last = code; atkbd->time = jiffies + (atkbd->dev.rep[REP_DELAY] * HZ + 500) / 1000 / 2; break; case 2: if (!time_after(jiffies, atkbd->time) && atkbd->last == code) value = 1; break; } atkbd_report_key(&atkbd->dev, regs, atkbd->keycode[code], value); } if (scroll || click != -1) { input_regs(&atkbd->dev, regs); input_report_key(&atkbd->dev, BTN_MIDDLE, click); input_report_rel(&atkbd->dev, REL_WHEEL, scroll); input_sync(&atkbd->dev); } atkbd->release = 0;out: return IRQ_HANDLED;}/* * Event callback from the input module. Events that change the state of * the hardware are processed here. */static int atkbd_event(struct input_dev *dev, unsigned int type, unsigned int code, int value){ struct atkbd *atkbd = dev->private; const short period[32] = { 33, 37, 42, 46, 50, 54, 58, 63, 67, 75, 83, 92, 100, 109, 116, 125, 133, 149, 167, 182, 200, 217, 232, 250, 270, 303, 333, 370, 400, 435, 470, 500 }; const short delay[4] = { 250, 500, 750, 1000 }; unsigned char param[2]; int i, j; if (!atkbd->write) return -1; switch (type) { case EV_LED: param[0] = (test_bit(LED_SCROLLL, dev->led) ? 1 : 0) | (test_bit(LED_NUML, dev->led) ? 2 : 0) | (test_bit(LED_CAPSL, dev->led) ? 4 : 0); ps2_schedule_command(&atkbd->ps2dev, param, ATKBD_CMD_SETLEDS); if (atkbd->extra) { param[0] = 0; param[1] = (test_bit(LED_COMPOSE, dev->led) ? 0x01 : 0) | (test_bit(LED_SLEEP, dev->led) ? 0x02 : 0) | (test_bit(LED_SUSPEND, dev->led) ? 0x04 : 0) | (test_bit(LED_MISC, dev->led) ? 0x10 : 0) | (test_bit(LED_MUTE, dev->led) ? 0x20 : 0); ps2_schedule_command(&atkbd->ps2dev, param, ATKBD_CMD_EX_SETLEDS); } return 0; case EV_REP: if (atkbd->softrepeat) return 0; i = j = 0; while (i < 32 && period[i] < dev->rep[REP_PERIOD]) i++; while (j < 4 && delay[j] < dev->rep[REP_DELAY]) j++; dev->rep[REP_PERIOD] = period[i]; dev->rep[REP_DELAY] = delay[j]; param[0] = i | (j << 5); ps2_schedule_command(&atkbd->ps2dev, param, ATKBD_CMD_SETREP); return 0; } return -1;}/* * atkbd_enable() signals that interrupt handler is allowed to * generate input events. */static inline void atkbd_enable(struct atkbd *atkbd){ serio_pause_rx(atkbd->ps2dev.serio); atkbd->enabled = 1; serio_continue_rx(atkbd->ps2dev.serio);}/* * atkbd_disable() tells input handler that all incoming data except * for ACKs and command response should be dropped. */static inline void atkbd_disable(struct atkbd *atkbd){ serio_pause_rx(atkbd->ps2dev.serio); atkbd->enabled = 0; serio_continue_rx(atkbd->ps2dev.serio);}/* * atkbd_probe() probes for an AT keyboard on a serio port. */static int atkbd_probe(struct atkbd *atkbd){ struct ps2dev *ps2dev = &atkbd->ps2dev; unsigned char param[2];/* * Some systems, where the bit-twiddling when testing the io-lines of the * controller may confuse the keyboard need a full reset of the keyboard. On * these systems the BIOS also usually doesn't do it for us. */ if (atkbd_reset) if (ps2_command(ps2dev, NULL, ATKBD_CMD_RESET_BAT)) printk(KERN_WARNING "atkbd.c: keyboard reset failed on %s\n", ps2dev->serio->phys);/* * Then we check the keyboard ID. We should get 0xab83 under normal conditions. * Some keyboards report different values, but the first byte is always 0xab or * 0xac. Some old AT keyboards don't report anything. If a mouse is connected, this * should make sure we don't try to set the LEDs on it. */ param[0] = param[1] = 0xa5; /* initialize with invalid values */ if (ps2_command(ps2dev, param, ATKBD_CMD_GETID)) {/* * If the get ID command failed, we check if we can at least set the LEDs on * the keyboard. This should work on every keyboard out there. It also turns * the LEDs off, which we want anyway. */ param[0] = 0; if (ps2_command(ps2dev, param, ATKBD_CMD_SETLEDS)) return -1; atkbd->id = 0xabba; return 0; } if (param[0] != 0xab && param[0] != 0xac && /* Regular and NCD Sun keyboards */ param[0] != 0x2b && param[0] != 0x5d && /* Trust keyboard, raw and translated */ param[0] != 0x60 && param[0] != 0x47) /* NMB SGI keyboard, raw and translated */ return -1; atkbd->id = (param[0] << 8) | param[1]; if (atkbd->id == 0xaca1 && atkbd->translated) { printk(KERN_ERR "atkbd.c: NCD terminal keyboards are only supported on non-translating\n"); printk(KERN_ERR "atkbd.c: controllers. Use i8042.direct=1 to disable translation.\n"); return -1; } return 0;}/* * atkbd_select_set checks if a keyboard has a working Set 3 support, and * sets it into that. Unfortunately there are keyboards that can be switched * to Set 3, but don't work well in that (BTC Multimedia ...) */static int atkbd_select_set(struct atkbd *atkbd, int target_set, int allow_extra){ struct ps2dev *ps2dev = &atkbd->ps2dev; unsigned char param[2];
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -