mirror of
https://github.com/torvalds/linux.git
synced 2024-11-12 07:01:57 +00:00
Input: atkbd - fix HANGEUL/HANJA keys
Make atkbd report HANGEUL/HANJA keys by default and use correct scan codes for these keys (they were swapped). Also make sure their scancodes reported as EV_MSC/MSC_SCAN events. Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
This commit is contained in:
parent
b9ab58dd8e
commit
0ae051a190
@ -1075,10 +1075,12 @@ static int emulate_raw(struct vc_data *vc, unsigned int keycode,
|
||||
put_queue(vc, 0x45 | up_flag);
|
||||
return 0;
|
||||
case KEY_HANGEUL:
|
||||
if (!up_flag) put_queue(vc, 0xf1);
|
||||
if (!up_flag)
|
||||
put_queue(vc, 0xf2);
|
||||
return 0;
|
||||
case KEY_HANJA:
|
||||
if (!up_flag) put_queue(vc, 0xf2);
|
||||
if (!up_flag)
|
||||
put_queue(vc, 0xf1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -55,7 +55,7 @@ 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 = 0;
|
||||
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");
|
||||
|
||||
@ -150,8 +150,8 @@ static unsigned char atkbd_unxlate_table[128] = {
|
||||
#define ATKBD_RET_EMUL0 0xe0
|
||||
#define ATKBD_RET_EMUL1 0xe1
|
||||
#define ATKBD_RET_RELEASE 0xf0
|
||||
#define ATKBD_RET_HANGEUL 0xf1
|
||||
#define ATKBD_RET_HANJA 0xf2
|
||||
#define ATKBD_RET_HANJA 0xf1
|
||||
#define ATKBD_RET_HANGEUL 0xf2
|
||||
#define ATKBD_RET_ERR 0xff
|
||||
|
||||
#define ATKBD_KEY_UNKNOWN 0
|
||||
@ -170,6 +170,13 @@ static unsigned char atkbd_unxlate_table[128] = {
|
||||
#define ATKBD_LED_EVENT_BIT 0
|
||||
#define ATKBD_REP_EVENT_BIT 1
|
||||
|
||||
#define ATKBD_XL_ERR 0x01
|
||||
#define ATKBD_XL_BAT 0x02
|
||||
#define ATKBD_XL_ACK 0x04
|
||||
#define ATKBD_XL_NAK 0x08
|
||||
#define ATKBD_XL_HANGEUL 0x10
|
||||
#define ATKBD_XL_HANJA 0x20
|
||||
|
||||
static struct {
|
||||
unsigned char keycode;
|
||||
unsigned char set2;
|
||||
@ -211,8 +218,7 @@ struct atkbd {
|
||||
unsigned char emul;
|
||||
unsigned char resend;
|
||||
unsigned char release;
|
||||
unsigned char bat_xl;
|
||||
unsigned char err_xl;
|
||||
unsigned long xl_bit;
|
||||
unsigned int last;
|
||||
unsigned long time;
|
||||
|
||||
@ -245,17 +251,65 @@ ATKBD_DEFINE_ATTR(set);
|
||||
ATKBD_DEFINE_ATTR(softrepeat);
|
||||
ATKBD_DEFINE_ATTR(softraw);
|
||||
|
||||
static const unsigned int xl_table[] = {
|
||||
ATKBD_RET_BAT, ATKBD_RET_ERR, ATKBD_RET_ACK,
|
||||
ATKBD_RET_NAK, ATKBD_RET_HANJA, ATKBD_RET_HANGEUL,
|
||||
};
|
||||
|
||||
static void atkbd_report_key(struct input_dev *dev, struct pt_regs *regs, int code, int value)
|
||||
/*
|
||||
* Checks if we should mangle the scancode to extract 'release' bit
|
||||
* in translated mode.
|
||||
*/
|
||||
static int atkbd_need_xlate(unsigned long xl_bit, unsigned char code)
|
||||
{
|
||||
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);
|
||||
int i;
|
||||
|
||||
if (code == ATKBD_RET_EMUL0 || code == ATKBD_RET_EMUL1)
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(xl_table); i++)
|
||||
if (code == xl_table[i])
|
||||
return test_bit(i, &xl_bit);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculates new value of xl_bit so the driver can distinguish
|
||||
* between make/break pair of scancodes for select keys and PS/2
|
||||
* protocol responses.
|
||||
*/
|
||||
static void atkbd_calculate_xl_bit(struct atkbd *atkbd, unsigned char code)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(xl_table); i++) {
|
||||
if (!((code ^ xl_table[i]) & 0x7f)) {
|
||||
if (code & 0x80)
|
||||
__clear_bit(i, &atkbd->xl_bit);
|
||||
else
|
||||
__set_bit(i, &atkbd->xl_bit);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Encode the scancode, 0xe0 prefix, and high bit into a single integer,
|
||||
* keeping kernel 2.4 compatibility for set 2
|
||||
*/
|
||||
static unsigned int atkbd_compat_scancode(struct atkbd *atkbd, unsigned int code)
|
||||
{
|
||||
if (atkbd->set == 3) {
|
||||
if (atkbd->emul == 1)
|
||||
code |= 0x100;
|
||||
} else {
|
||||
code = (code & 0x7f) | ((code & 0x80) << 1);
|
||||
if (atkbd->emul == 1)
|
||||
code |= 0x80;
|
||||
}
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -267,9 +321,11 @@ static irqreturn_t atkbd_interrupt(struct serio *serio, unsigned char data,
|
||||
unsigned int flags, struct pt_regs *regs)
|
||||
{
|
||||
struct atkbd *atkbd = serio_get_drvdata(serio);
|
||||
struct input_dev *dev = atkbd->dev;
|
||||
unsigned int code = data;
|
||||
int scroll = 0, hscroll = 0, click = -1;
|
||||
int scroll = 0, hscroll = 0, click = -1, add_release_event = 0;
|
||||
int value;
|
||||
unsigned char keycode;
|
||||
|
||||
#ifdef ATKBD_DEBUG
|
||||
printk(KERN_DEBUG "atkbd.c: Received %02x flags %02x\n", data, flags);
|
||||
@ -298,25 +354,17 @@ static irqreturn_t atkbd_interrupt(struct serio *serio, unsigned char data,
|
||||
if (!atkbd->enabled)
|
||||
goto out;
|
||||
|
||||
input_event(atkbd->dev, EV_MSC, MSC_RAW, code);
|
||||
input_event(dev, EV_MSC, MSC_RAW, code);
|
||||
|
||||
if (atkbd->translated) {
|
||||
|
||||
if (atkbd->emul ||
|
||||
(code != ATKBD_RET_EMUL0 && code != ATKBD_RET_EMUL1 &&
|
||||
code != ATKBD_RET_HANGEUL && code != ATKBD_RET_HANJA &&
|
||||
(code != ATKBD_RET_ERR || atkbd->err_xl) &&
|
||||
(code != ATKBD_RET_BAT || atkbd->bat_xl))) {
|
||||
if (atkbd->emul || atkbd_need_xlate(atkbd->xl_bit, code)) {
|
||||
atkbd->release = code >> 7;
|
||||
code &= 0x7f;
|
||||
}
|
||||
|
||||
if (!atkbd->emul) {
|
||||
if ((code & 0x7f) == (ATKBD_RET_BAT & 0x7f))
|
||||
atkbd->bat_xl = !(data >> 7);
|
||||
if ((code & 0x7f) == (ATKBD_RET_ERR & 0x7f))
|
||||
atkbd->err_xl = !(data >> 7);
|
||||
}
|
||||
if (!atkbd->emul)
|
||||
atkbd_calculate_xl_bit(atkbd, data);
|
||||
}
|
||||
|
||||
switch (code) {
|
||||
@ -333,47 +381,48 @@ static irqreturn_t atkbd_interrupt(struct serio *serio, unsigned char data,
|
||||
case ATKBD_RET_RELEASE:
|
||||
atkbd->release = 1;
|
||||
goto out;
|
||||
case ATKBD_RET_ACK:
|
||||
case ATKBD_RET_NAK:
|
||||
printk(KERN_WARNING "atkbd.c: Spurious %s on %s. "
|
||||
"Some program might be trying access hardware directly.\n",
|
||||
data == ATKBD_RET_ACK ? "ACK" : "NAK", serio->phys);
|
||||
goto out;
|
||||
case ATKBD_RET_HANGEUL:
|
||||
atkbd_report_key(atkbd->dev, regs, KEY_HANGEUL, 3);
|
||||
goto out;
|
||||
case ATKBD_RET_HANJA:
|
||||
atkbd_report_key(atkbd->dev, regs, KEY_HANJA, 3);
|
||||
goto out;
|
||||
/*
|
||||
* These keys do not report release and thus need to be
|
||||
* flagged properly
|
||||
*/
|
||||
add_release_event = 1;
|
||||
break;
|
||||
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)
|
||||
code = atkbd_compat_scancode(atkbd, code);
|
||||
|
||||
if (atkbd->emul && --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);
|
||||
keycode = atkbd->keycode[code];
|
||||
|
||||
switch (atkbd->keycode[code]) {
|
||||
if (keycode != ATKBD_KEY_NULL)
|
||||
input_event(dev, EV_MSC, MSC_SCAN, code);
|
||||
|
||||
switch (keycode) {
|
||||
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",
|
||||
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",
|
||||
printk(KERN_WARNING
|
||||
"atkbd.c: Use 'setkeycodes %s%02x <keycode>' to make it known.\n",
|
||||
code & 0x80 ? "e0" : "", code & 0x7f);
|
||||
}
|
||||
input_sync(atkbd->dev);
|
||||
input_sync(dev);
|
||||
break;
|
||||
case ATKBD_SCR_1:
|
||||
scroll = 1 - atkbd->release * 2;
|
||||
@ -397,33 +446,35 @@ static irqreturn_t atkbd_interrupt(struct serio *serio, unsigned char data,
|
||||
hscroll = 1;
|
||||
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:
|
||||
if (atkbd->release) {
|
||||
value = 0;
|
||||
atkbd->last = 0;
|
||||
break;
|
||||
case 1:
|
||||
atkbd->last = code;
|
||||
atkbd->time = jiffies + msecs_to_jiffies(atkbd->dev->rep[REP_DELAY]) / 2;
|
||||
break;
|
||||
case 2:
|
||||
if (!time_after(jiffies, atkbd->time) && atkbd->last == code)
|
||||
} 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;
|
||||
break;
|
||||
atkbd->last = code;
|
||||
atkbd->time = jiffies + msecs_to_jiffies(dev->rep[REP_DELAY]) / 2;
|
||||
}
|
||||
|
||||
atkbd_report_key(atkbd->dev, regs, atkbd->keycode[code], value);
|
||||
input_regs(dev, regs);
|
||||
input_report_key(dev, keycode, value);
|
||||
input_sync(dev);
|
||||
|
||||
if (value && add_release_event) {
|
||||
input_report_key(dev, keycode, 0);
|
||||
input_sync(dev);
|
||||
}
|
||||
}
|
||||
|
||||
if (atkbd->scroll) {
|
||||
input_regs(atkbd->dev, regs);
|
||||
input_regs(dev, regs);
|
||||
if (click != -1)
|
||||
input_report_key(atkbd->dev, BTN_MIDDLE, click);
|
||||
input_report_rel(atkbd->dev, REL_WHEEL, scroll);
|
||||
input_report_rel(atkbd->dev, REL_HWHEEL, hscroll);
|
||||
input_sync(atkbd->dev);
|
||||
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;
|
||||
@ -764,6 +815,9 @@ static void atkbd_set_keycode_table(struct atkbd *atkbd)
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
|
Loading…
Reference in New Issue
Block a user