Commit 554101e3 authored by Giel de Nijs's avatar Giel de Nijs Committed by Dmitry Torokhov

Input: atkbd - properly handle special keys on Dell Latitudes

Most of Fn+F? special keys on (at least) the Dell Latitude laptops don't
generate a hardware key release event so the driver has to generate one.
Signed-off-by: default avatarGiel de Nijs <giel@caffeinetrip.com>
Signed-off-by: default avatarDmitry Torokhov <dtor@mail.ru>
parent fb491610
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include <linux/libps2.h> #include <linux/libps2.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/dmi.h>
#define DRIVER_DESC "AT and PS/2 keyboard driver" #define DRIVER_DESC "AT and PS/2 keyboard driver"
...@@ -201,6 +202,7 @@ struct atkbd { ...@@ -201,6 +202,7 @@ struct atkbd {
unsigned short id; unsigned short id;
unsigned char keycode[512]; unsigned char keycode[512];
DECLARE_BITMAP(force_release_mask, 512);
unsigned char set; unsigned char set;
unsigned char translated; unsigned char translated;
unsigned char extra; unsigned char extra;
...@@ -225,6 +227,11 @@ struct atkbd { ...@@ -225,6 +227,11 @@ struct atkbd {
unsigned long event_mask; unsigned long event_mask;
}; };
/*
* System-specific ketymap fixup routine
*/
static void (*atkbd_platform_fixup)(struct atkbd *);
static ssize_t atkbd_attr_show_helper(struct device *dev, char *buf, static ssize_t atkbd_attr_show_helper(struct device *dev, char *buf,
ssize_t (*handler)(struct atkbd *, char *)); ssize_t (*handler)(struct atkbd *, char *));
static ssize_t atkbd_attr_set_helper(struct device *dev, const char *buf, size_t count, static ssize_t atkbd_attr_set_helper(struct device *dev, const char *buf, size_t count,
...@@ -349,7 +356,7 @@ static irqreturn_t atkbd_interrupt(struct serio *serio, unsigned char data, ...@@ -349,7 +356,7 @@ static irqreturn_t atkbd_interrupt(struct serio *serio, unsigned char data,
struct atkbd *atkbd = serio_get_drvdata(serio); struct atkbd *atkbd = serio_get_drvdata(serio);
struct input_dev *dev = atkbd->dev; struct input_dev *dev = atkbd->dev;
unsigned int code = data; unsigned int code = data;
int scroll = 0, hscroll = 0, click = -1, add_release_event = 0; int scroll = 0, hscroll = 0, click = -1;
int value; int value;
unsigned char keycode; unsigned char keycode;
...@@ -414,14 +421,6 @@ static irqreturn_t atkbd_interrupt(struct serio *serio, unsigned char data, ...@@ -414,14 +421,6 @@ static irqreturn_t atkbd_interrupt(struct serio *serio, unsigned char data,
"Some program might be trying access hardware directly.\n", "Some program might be trying access hardware directly.\n",
data == ATKBD_RET_ACK ? "ACK" : "NAK", serio->phys); data == ATKBD_RET_ACK ? "ACK" : "NAK", serio->phys);
goto out; goto out;
case ATKBD_RET_HANGEUL:
case ATKBD_RET_HANJA:
/*
* These keys do not report release and thus need to be
* flagged properly
*/
add_release_event = 1;
break;
case ATKBD_RET_ERR: case ATKBD_RET_ERR:
atkbd->err_count++; atkbd->err_count++;
#ifdef ATKBD_DEBUG #ifdef ATKBD_DEBUG
...@@ -491,7 +490,7 @@ static irqreturn_t atkbd_interrupt(struct serio *serio, unsigned char data, ...@@ -491,7 +490,7 @@ static irqreturn_t atkbd_interrupt(struct serio *serio, unsigned char data,
input_event(dev, EV_KEY, keycode, value); input_event(dev, EV_KEY, keycode, value);
input_sync(dev); input_sync(dev);
if (value && add_release_event) { if (value && test_bit(code, atkbd->force_release_mask)) {
input_report_key(dev, keycode, 0); input_report_key(dev, keycode, 0);
input_sync(dev); input_sync(dev);
} }
...@@ -834,6 +833,22 @@ static void atkbd_disconnect(struct serio *serio) ...@@ -834,6 +833,22 @@ static void atkbd_disconnect(struct serio *serio)
kfree(atkbd); kfree(atkbd);
} }
/*
* Most special keys (Fn+F?) on Dell Latitudes do not generate release
* events so we have to do it ourselves.
*/
static void atkbd_latitude_keymap_fixup(struct atkbd *atkbd)
{
const unsigned int forced_release_keys[] = {
0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8f, 0x93,
};
int i;
if (atkbd->set == 2)
for (i = 0; i < ARRAY_SIZE(forced_release_keys); i++)
__set_bit(forced_release_keys[i],
atkbd->force_release_mask);
}
/* /*
* atkbd_set_keycode_table() initializes keyboard's keycode table * atkbd_set_keycode_table() initializes keyboard's keycode table
...@@ -842,17 +857,20 @@ static void atkbd_disconnect(struct serio *serio) ...@@ -842,17 +857,20 @@ static void atkbd_disconnect(struct serio *serio)
static void atkbd_set_keycode_table(struct atkbd *atkbd) static void atkbd_set_keycode_table(struct atkbd *atkbd)
{ {
unsigned int scancode;
int i, j; int i, j;
memset(atkbd->keycode, 0, sizeof(atkbd->keycode)); memset(atkbd->keycode, 0, sizeof(atkbd->keycode));
bitmap_zero(atkbd->force_release_mask, 512);
if (atkbd->translated) { if (atkbd->translated) {
for (i = 0; i < 128; i++) { for (i = 0; i < 128; i++) {
atkbd->keycode[i] = atkbd_set2_keycode[atkbd_unxlate_table[i]]; scancode = atkbd_unxlate_table[i];
atkbd->keycode[i | 0x80] = atkbd_set2_keycode[atkbd_unxlate_table[i] | 0x80]; atkbd->keycode[i] = atkbd_set2_keycode[scancode];
atkbd->keycode[i | 0x80] = atkbd_set2_keycode[scancode | 0x80];
if (atkbd->scroll) if (atkbd->scroll)
for (j = 0; j < ARRAY_SIZE(atkbd_scroll_keys); j++) for (j = 0; j < ARRAY_SIZE(atkbd_scroll_keys); j++)
if ((atkbd_unxlate_table[i] | 0x80) == atkbd_scroll_keys[j].set2) if ((scancode | 0x80) == atkbd_scroll_keys[j].set2)
atkbd->keycode[i | 0x80] = atkbd_scroll_keys[j].keycode; atkbd->keycode[i | 0x80] = atkbd_scroll_keys[j].keycode;
} }
} else if (atkbd->set == 3) { } else if (atkbd->set == 3) {
...@@ -861,12 +879,29 @@ static void atkbd_set_keycode_table(struct atkbd *atkbd) ...@@ -861,12 +879,29 @@ static void atkbd_set_keycode_table(struct atkbd *atkbd)
memcpy(atkbd->keycode, atkbd_set2_keycode, sizeof(atkbd->keycode)); memcpy(atkbd->keycode, atkbd_set2_keycode, sizeof(atkbd->keycode));
if (atkbd->scroll) if (atkbd->scroll)
for (i = 0; i < ARRAY_SIZE(atkbd_scroll_keys); i++) for (i = 0; i < ARRAY_SIZE(atkbd_scroll_keys); i++) {
atkbd->keycode[atkbd_scroll_keys[i].set2] = atkbd_scroll_keys[i].keycode; scancode = atkbd_scroll_keys[i].set2;
atkbd->keycode[scancode] = 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; * HANGEUL and HANJA keys do not send release events so we need to
* generate such events ourselves
*/
scancode = atkbd_compat_scancode(atkbd, ATKBD_RET_HANGEUL);
atkbd->keycode[scancode] = KEY_HANGEUL;
__set_bit(scancode, atkbd->force_release_mask);
scancode = atkbd_compat_scancode(atkbd, ATKBD_RET_HANJA);
atkbd->keycode[scancode] = KEY_HANJA;
__set_bit(scancode, atkbd->force_release_mask);
/*
* Perform additional fixups
*/
if (atkbd_platform_fixup)
atkbd_platform_fixup(atkbd);
} }
/* /*
...@@ -1401,9 +1436,29 @@ static ssize_t atkbd_show_err_count(struct atkbd *atkbd, char *buf) ...@@ -1401,9 +1436,29 @@ static ssize_t atkbd_show_err_count(struct atkbd *atkbd, char *buf)
return sprintf(buf, "%lu\n", atkbd->err_count); return sprintf(buf, "%lu\n", atkbd->err_count);
} }
static int __init atkbd_setup_fixup(const struct dmi_system_id *id)
{
atkbd_platform_fixup = id->driver_data;
return 0;
}
static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = {
{
.ident = "Dell Latitude series",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
DMI_MATCH(DMI_PRODUCT_NAME, "Latitude"),
},
.callback = atkbd_setup_fixup,
.driver_data = atkbd_latitude_keymap_fixup,
},
{ }
};
static int __init atkbd_init(void) static int __init atkbd_init(void)
{ {
dmi_check_system(atkbd_dmi_quirk_table);
return serio_register_driver(&atkbd_drv); return serio_register_driver(&atkbd_drv);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment