📄 keyboard_driver.c
字号:
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;
case ATKBD_SCR_LEFT:
hscroll = -1;
break;
case ATKBD_SCR_RIGHT:
hscroll = 1;
break;
default:
if (atkbd->release) {
value = 0;
atkbd->last = 0;
} else if (!atkbd->softrepeat && test_bit(keycode, dev->key)) {
/* Workaround Toshiba laptop multiple keypress */
value = time_before(jiffies, atkbd->time) && atkbd->last == code ? 1 : 2;
} else {
value = 1;
atkbd->last = code;
atkbd->time = jiffies + msecs_to_jiffies(dev->rep[REP_DELAY]) / 2;
}
input_regs(dev, regs);
input_event(dev, EV_KEY, keycode, value);
input_sync(dev);
if (value && add_release_event) {
input_report_key(dev, keycode, 0);
input_sync(dev);
}
}
if (atkbd->scroll) {
input_regs(dev, regs);
if (click != -1)
input_report_key(dev, BTN_MIDDLE, click);
input_report_rel(dev, REL_WHEEL, scroll);
input_report_rel(dev, REL_HWHEEL, hscroll);
input_sync(dev);
}
atkbd->release = 0;
out:
return IRQ_HANDLED;
}
static int atkbd_set_repeat_rate(struct atkbd *atkbd)
{
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 };
struct input_dev *dev = atkbd->dev;
unsigned char param;
int i = 0, j = 0;
while (i < ARRAY_SIZE(period) - 1 && period[i] < dev->rep[REP_PERIOD])
i++;
dev->rep[REP_PERIOD] = period[i];
while (j < ARRAY_SIZE(delay) - 1 && delay[j] < dev->rep[REP_DELAY])
j++;
dev->rep[REP_DELAY] = delay[j];
param = i | (j << 5);
return ps2_command(&atkbd->ps2dev, ¶m, ATKBD_CMD_SETREP);
}
static int atkbd_set_leds(struct atkbd *atkbd)
{
struct input_dev *dev = atkbd->dev;
unsigned char param[2];
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);
if (ps2_command(&atkbd->ps2dev, param, ATKBD_CMD_SETLEDS))
return -1;
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);
if (ps2_command(&atkbd->ps2dev, param, ATKBD_CMD_EX_SETLEDS))
return -1;
}
return 0;
}
/*
* atkbd_event_work() is used to complete processing of events that
* can not be processed by input_event() which is often called from
* interrupt context.
*/
static void atkbd_event_work(void *data)
{
struct atkbd *atkbd = data;
mutex_lock(&atkbd->event_mutex);
if (test_and_clear_bit(ATKBD_LED_EVENT_BIT, &atkbd->event_mask))
atkbd_set_leds(atkbd);
if (test_and_clear_bit(ATKBD_REP_EVENT_BIT, &atkbd->event_mask))
atkbd_set_repeat_rate(atkbd);
mutex_unlock(&atkbd->event_mutex);
}
/*
* Event callback from the input module. Events that change the state of
* the hardware are processed here. If action can not be performed in
* interrupt context it is offloaded to atkbd_event_work.
*/
static int atkbd_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
{
struct atkbd *atkbd = dev->private;
if (!atkbd->write)
return -1;
switch (type) {
case EV_LED:
set_bit(ATKBD_LED_EVENT_BIT, &atkbd->event_mask);
wmb();
schedule_work(&atkbd->event_work);
return 0;
case EV_REP:
if (!atkbd->softrepeat) {
set_bit(ATKBD_REP_EVENT_BIT, &atkbd->event_mask);
wmb();
schedule_work(&atkbd->event_work);
}
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.
*/
//hwm
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];
atkbd->extra = 0;
/*
* For known special keyboards we can go ahead and set the correct set.
* We check for NCD PS/2 Sun, NorthGate OmniKey 101 and
* IBM RapidAccess / IBM EzButton / Chicony KBP-8993 keyboards.
*/
if (atkbd->translated)
return 2;
if (atkbd->id == 0xaca1) {
param[0] = 3;
ps2_command(ps2dev, param, ATKBD_CMD_SSCANSET);
return 3;
}
if (allow_extra) {
param[0] = 0x71;
if (!ps2_command(ps2dev, param, ATKBD_CMD_EX_ENABLE)) {
atkbd->extra = 1;
return 2;
}
}
if (target_set != 3)
return 2;
if (!ps2_command(ps2dev, param, ATKBD_CMD_OK_GETID)) {
atkbd->id = param[0] << 8 | param[1];
return 2;
}
param[0] = 3;
if (ps2_command(ps2dev, param, ATKBD_CMD_SSCANSET))
return 2;
param[0] = 0;
if (ps2_command(ps2dev, param, ATKBD_CMD_GSCANSET))
return 2;
if (param[0] != 3) {
param[0] = 2;
if (ps2_command(ps2dev, param, ATKBD_CMD_SSCANSET))
return 2;
}
ps2_command(ps2dev, param, ATKBD_CMD_SETALL_MBR);
return 3;
}
static int atkbd_activate(struct atkbd *atkbd)
{
struct ps2dev *ps2dev = &atkbd->ps2dev;
unsigned char param[1];
/*
* Set the LEDs to a defined state.
*/
param[0] = 0;
if (ps2_command(ps2dev, param, ATKBD_CMD_SETLEDS))
return -1;
/*
* Set autorepeat to fastest possible.
*/
param[0] = 0;
if (ps2_command(ps2dev, param, ATKBD_CMD_SETREP))
return -1;
/*
* Enable the keyboard to receive keystrokes.
*/
if (ps2_command(ps2dev, NULL, ATKBD_CMD_ENABLE)) {
printk(KERN_ERR "atkbd.c: Failed to enable keyboard on %s\n",
ps2dev->serio->phys);
return -1;
}
return 0;
}
/*
* atkbd_cleanup() restores the keyboard state so that BIOS is happy after a
* reboot.
*/
static void atkbd_cleanup(struct serio *serio)
{
struct atkbd *atkbd = serio_get_drvdata(serio);
ps2_command(&atkbd->ps2dev, NULL, ATKBD_CMD_RESET_BAT);
}
/*
* atkbd_disconnect() closes and frees.
*/
static void atkbd_disconnect(struct serio *serio)
{
struct atkbd *atkbd = serio_get_drvdata(serio);
atkbd_disable(atkbd);
/* make sure we don't have a command in flight */
synchronize_sched(); /* Allow atkbd_interrupt()s to complete. */
flush_scheduled_work();
device_remove_file(&serio->dev, &atkbd_attr_extra);
device_remove_file(&serio->dev, &atkbd_attr_scroll);
device_remove_file(&serio->dev, &atkbd_attr_set);
device_remove_file(&serio->dev, &atkbd_attr_softrepeat);
device_remove_file(&serio->dev, &atkbd_attr_softraw);
input_unregister_device(atkbd->dev);
serio_close(serio);
serio_set_drvdata(serio, NULL);
kfree(atkbd);
}
/*
* atkbd_set_keycode_table() initializes keyboard's keycode table
* according to the selected scancode set
*/
static void atkbd_set_keycode_table(struct atkbd *atkbd)
{
int i, j;
memset(atkbd->keycode, 0, sizeof(atkbd->keycode));
if (atkbd->translated) {
for (i = 0; i < 128; i++) {
atkbd->keycode[i] = atkbd_set2_keycode[atkbd_unxlate_table[i]];
atkbd->keycode[i | 0x80] = atkbd_set2_keycode[atkbd_unxlate_table[i] | 0x80];
if (atkbd->scroll)
for (j = 0; j < ARRAY_SIZE(atkbd_scroll_keys); j++)
if ((atkbd_unxlate_table[i] | 0x80) == atkbd_scroll_keys[j].set2)
atkbd->keycode[i | 0x80] = atkbd_scroll_keys[j].keycode;
}
} else if (atkbd->set == 3) {
memcpy(atkbd->keycode, atkbd_set3_keycode, sizeof(atkbd->keycode));
} else {
memcpy(atkbd->keycode, atkbd_set2_keycode, sizeof(atkbd->keycode));
if (atkbd->scroll)
for (i = 0; i < ARRAY_SIZE(atkbd_scroll_keys); i++)
atkbd->keycode[atkbd_scroll_keys[i].set2] = atkbd_scroll_keys[i].keycode;
}
atkbd->keycode[atkbd_compat_scancode(atkbd, ATKBD_RET_HANGEUL)] = KEY_HANGUEL;
atkbd->keycode[atkbd_compat_scancode(atkbd, ATKBD_RET_HANJA)] = KEY_HANJA;
}
/*
* atkbd_set_device_attrs() sets up keyboard's input device structure
*/
static void atkbd_set_device_attrs(struct atkbd *atkbd)
{
struct input_dev *input_dev = atkbd->dev;
int i;
if (atkbd->extra)
snprintf(atkbd->name, sizeof(atkbd->name),
"AT Set 2 Extra keyboard");
else
snprintf(atkbd->name, sizeof(atkbd->name),
"AT %s Set %d keyboard",
atkbd->translated ? "Translated" : "Raw", atkbd->set);
snprintf(atkbd->phys, sizeof(atkbd->phys),
"%s/input0", atkbd->ps2dev.serio->phys);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -