diff options
author | Giel de Nijs <giel@caffeinetrip.com> | 2007-11-02 13:08:02 (GMT) |
---|---|---|
committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2008-01-21 06:11:06 (GMT) |
commit | 554101e3e5f396b987c846332863a3fcdc87b1d6 (patch) | |
tree | 040f0734e75c2bad14fea32800df04ad51b51663 /drivers | |
parent | fb49161027e1938c34fc97d1136735e1d4209df6 (diff) | |
download | linux-fsl-qoriq-554101e3e5f396b987c846332863a3fcdc87b1d6.tar.xz |
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: Giel de Nijs <giel@caffeinetrip.com>
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/input/keyboard/atkbd.c | 89 |
1 files changed, 72 insertions, 17 deletions
diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c index b39c5b3..7162f79 100644 --- a/drivers/input/keyboard/atkbd.c +++ b/drivers/input/keyboard/atkbd.c @@ -28,6 +28,7 @@ #include <linux/workqueue.h> #include <linux/libps2.h> #include <linux/mutex.h> +#include <linux/dmi.h> #define DRIVER_DESC "AT and PS/2 keyboard driver" @@ -201,6 +202,7 @@ struct atkbd { unsigned short id; unsigned char keycode[512]; + DECLARE_BITMAP(force_release_mask, 512); unsigned char set; unsigned char translated; unsigned char extra; @@ -225,6 +227,11 @@ struct atkbd { 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, ssize_t (*handler)(struct atkbd *, char *)); 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, struct atkbd *atkbd = serio_get_drvdata(serio); struct input_dev *dev = atkbd->dev; unsigned int code = data; - int scroll = 0, hscroll = 0, click = -1, add_release_event = 0; + int scroll = 0, hscroll = 0, click = -1; int value; unsigned char keycode; @@ -414,14 +421,6 @@ static irqreturn_t atkbd_interrupt(struct serio *serio, unsigned char data, "Some program might be trying access hardware directly.\n", data == ATKBD_RET_ACK ? "ACK" : "NAK", serio->phys); 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: atkbd->err_count++; #ifdef ATKBD_DEBUG @@ -491,7 +490,7 @@ static irqreturn_t atkbd_interrupt(struct serio *serio, unsigned char data, input_event(dev, EV_KEY, keycode, value); input_sync(dev); - if (value && add_release_event) { + if (value && test_bit(code, atkbd->force_release_mask)) { input_report_key(dev, keycode, 0); input_sync(dev); } @@ -834,6 +833,22 @@ static void atkbd_disconnect(struct serio *serio) 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 @@ -842,17 +857,20 @@ static void atkbd_disconnect(struct serio *serio) static void atkbd_set_keycode_table(struct atkbd *atkbd) { + unsigned int scancode; int i, j; memset(atkbd->keycode, 0, sizeof(atkbd->keycode)); + bitmap_zero(atkbd->force_release_mask, 512); 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]; + scancode = atkbd_unxlate_table[i]; + atkbd->keycode[i] = atkbd_set2_keycode[scancode]; + atkbd->keycode[i | 0x80] = atkbd_set2_keycode[scancode | 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) + if ((scancode | 0x80) == atkbd_scroll_keys[j].set2) atkbd->keycode[i | 0x80] = atkbd_scroll_keys[j].keycode; } } else if (atkbd->set == 3) { @@ -861,12 +879,29 @@ static void atkbd_set_keycode_table(struct atkbd *atkbd) 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; + for (i = 0; i < ARRAY_SIZE(atkbd_scroll_keys); i++) { + 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) 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) { + dmi_check_system(atkbd_dmi_quirk_table); + return serio_register_driver(&atkbd_drv); } |