diff options
author | Giel de Nijs <giel@caffeinetrip.com> | 2007-11-02 09:08:02 -0400 |
---|---|---|
committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2008-01-21 01:11:06 -0500 |
commit | 554101e3e5f396b987c846332863a3fcdc87b1d6 (patch) | |
tree | 040f0734e75c2bad14fea32800df04ad51b51663 /drivers | |
parent | fb49161027e1938c34fc97d1136735e1d4209df6 (diff) |
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 b39c5b31e620..7162f79ea119 100644 --- a/drivers/input/keyboard/atkbd.c +++ b/drivers/input/keyboard/atkbd.c | |||
@@ -28,6 +28,7 @@ | |||
28 | #include <linux/workqueue.h> | 28 | #include <linux/workqueue.h> |
29 | #include <linux/libps2.h> | 29 | #include <linux/libps2.h> |
30 | #include <linux/mutex.h> | 30 | #include <linux/mutex.h> |
31 | #include <linux/dmi.h> | ||
31 | 32 | ||
32 | #define DRIVER_DESC "AT and PS/2 keyboard driver" | 33 | #define DRIVER_DESC "AT and PS/2 keyboard driver" |
33 | 34 | ||
@@ -201,6 +202,7 @@ struct atkbd { | |||
201 | 202 | ||
202 | unsigned short id; | 203 | unsigned short id; |
203 | unsigned char keycode[512]; | 204 | unsigned char keycode[512]; |
205 | DECLARE_BITMAP(force_release_mask, 512); | ||
204 | unsigned char set; | 206 | unsigned char set; |
205 | unsigned char translated; | 207 | unsigned char translated; |
206 | unsigned char extra; | 208 | unsigned char extra; |
@@ -225,6 +227,11 @@ struct atkbd { | |||
225 | unsigned long event_mask; | 227 | unsigned long event_mask; |
226 | }; | 228 | }; |
227 | 229 | ||
230 | /* | ||
231 | * System-specific ketymap fixup routine | ||
232 | */ | ||
233 | static void (*atkbd_platform_fixup)(struct atkbd *); | ||
234 | |||
228 | static ssize_t atkbd_attr_show_helper(struct device *dev, char *buf, | 235 | static ssize_t atkbd_attr_show_helper(struct device *dev, char *buf, |
229 | ssize_t (*handler)(struct atkbd *, char *)); | 236 | ssize_t (*handler)(struct atkbd *, char *)); |
230 | static ssize_t atkbd_attr_set_helper(struct device *dev, const char *buf, size_t count, | 237 | 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 | struct atkbd *atkbd = serio_get_drvdata(serio); | 356 | struct atkbd *atkbd = serio_get_drvdata(serio); |
350 | struct input_dev *dev = atkbd->dev; | 357 | struct input_dev *dev = atkbd->dev; |
351 | unsigned int code = data; | 358 | unsigned int code = data; |
352 | int scroll = 0, hscroll = 0, click = -1, add_release_event = 0; | 359 | int scroll = 0, hscroll = 0, click = -1; |
353 | int value; | 360 | int value; |
354 | unsigned char keycode; | 361 | unsigned char keycode; |
355 | 362 | ||
@@ -414,14 +421,6 @@ static irqreturn_t atkbd_interrupt(struct serio *serio, unsigned char data, | |||
414 | "Some program might be trying access hardware directly.\n", | 421 | "Some program might be trying access hardware directly.\n", |
415 | data == ATKBD_RET_ACK ? "ACK" : "NAK", serio->phys); | 422 | data == ATKBD_RET_ACK ? "ACK" : "NAK", serio->phys); |
416 | goto out; | 423 | goto out; |
417 | case ATKBD_RET_HANGEUL: | ||
418 | case ATKBD_RET_HANJA: | ||
419 | /* | ||
420 | * These keys do not report release and thus need to be | ||
421 | * flagged properly | ||
422 | */ | ||
423 | add_release_event = 1; | ||
424 | break; | ||
425 | case ATKBD_RET_ERR: | 424 | case ATKBD_RET_ERR: |
426 | atkbd->err_count++; | 425 | atkbd->err_count++; |
427 | #ifdef ATKBD_DEBUG | 426 | #ifdef ATKBD_DEBUG |
@@ -491,7 +490,7 @@ static irqreturn_t atkbd_interrupt(struct serio *serio, unsigned char data, | |||
491 | input_event(dev, EV_KEY, keycode, value); | 490 | input_event(dev, EV_KEY, keycode, value); |
492 | input_sync(dev); | 491 | input_sync(dev); |
493 | 492 | ||
494 | if (value && add_release_event) { | 493 | if (value && test_bit(code, atkbd->force_release_mask)) { |
495 | input_report_key(dev, keycode, 0); | 494 | input_report_key(dev, keycode, 0); |
496 | input_sync(dev); | 495 | input_sync(dev); |
497 | } | 496 | } |
@@ -834,6 +833,22 @@ static void atkbd_disconnect(struct serio *serio) | |||
834 | kfree(atkbd); | 833 | kfree(atkbd); |
835 | } | 834 | } |
836 | 835 | ||
836 | /* | ||
837 | * Most special keys (Fn+F?) on Dell Latitudes do not generate release | ||
838 | * events so we have to do it ourselves. | ||
839 | */ | ||
840 | static void atkbd_latitude_keymap_fixup(struct atkbd *atkbd) | ||
841 | { | ||
842 | const unsigned int forced_release_keys[] = { | ||
843 | 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8f, 0x93, | ||
844 | }; | ||
845 | int i; | ||
846 | |||
847 | if (atkbd->set == 2) | ||
848 | for (i = 0; i < ARRAY_SIZE(forced_release_keys); i++) | ||
849 | __set_bit(forced_release_keys[i], | ||
850 | atkbd->force_release_mask); | ||
851 | } | ||
837 | 852 | ||
838 | /* | 853 | /* |
839 | * atkbd_set_keycode_table() initializes keyboard's keycode table | 854 | * atkbd_set_keycode_table() initializes keyboard's keycode table |
@@ -842,17 +857,20 @@ static void atkbd_disconnect(struct serio *serio) | |||
842 | 857 | ||
843 | static void atkbd_set_keycode_table(struct atkbd *atkbd) | 858 | static void atkbd_set_keycode_table(struct atkbd *atkbd) |
844 | { | 859 | { |
860 | unsigned int scancode; | ||
845 | int i, j; | 861 | int i, j; |
846 | 862 | ||
847 | memset(atkbd->keycode, 0, sizeof(atkbd->keycode)); | 863 | memset(atkbd->keycode, 0, sizeof(atkbd->keycode)); |
864 | bitmap_zero(atkbd->force_release_mask, 512); | ||
848 | 865 | ||
849 | if (atkbd->translated) { | 866 | if (atkbd->translated) { |
850 | for (i = 0; i < 128; i++) { | 867 | for (i = 0; i < 128; i++) { |
851 | atkbd->keycode[i] = atkbd_set2_keycode[atkbd_unxlate_table[i]]; | 868 | scancode = atkbd_unxlate_table[i]; |
852 | atkbd->keycode[i | 0x80] = atkbd_set2_keycode[atkbd_unxlate_table[i] | 0x80]; | 869 | atkbd->keycode[i] = atkbd_set2_keycode[scancode]; |
870 | atkbd->keycode[i | 0x80] = atkbd_set2_keycode[scancode | 0x80]; | ||
853 | if (atkbd->scroll) | 871 | if (atkbd->scroll) |
854 | for (j = 0; j < ARRAY_SIZE(atkbd_scroll_keys); j++) | 872 | for (j = 0; j < ARRAY_SIZE(atkbd_scroll_keys); j++) |
855 | if ((atkbd_unxlate_table[i] | 0x80) == atkbd_scroll_keys[j].set2) | 873 | if ((scancode | 0x80) == atkbd_scroll_keys[j].set2) |
856 | atkbd->keycode[i | 0x80] = atkbd_scroll_keys[j].keycode; | 874 | atkbd->keycode[i | 0x80] = atkbd_scroll_keys[j].keycode; |
857 | } | 875 | } |
858 | } else if (atkbd->set == 3) { | 876 | } else if (atkbd->set == 3) { |
@@ -861,12 +879,29 @@ static void atkbd_set_keycode_table(struct atkbd *atkbd) | |||
861 | memcpy(atkbd->keycode, atkbd_set2_keycode, sizeof(atkbd->keycode)); | 879 | memcpy(atkbd->keycode, atkbd_set2_keycode, sizeof(atkbd->keycode)); |
862 | 880 | ||
863 | if (atkbd->scroll) | 881 | if (atkbd->scroll) |
864 | for (i = 0; i < ARRAY_SIZE(atkbd_scroll_keys); i++) | 882 | for (i = 0; i < ARRAY_SIZE(atkbd_scroll_keys); i++) { |
865 | atkbd->keycode[atkbd_scroll_keys[i].set2] = atkbd_scroll_keys[i].keycode; | 883 | scancode = atkbd_scroll_keys[i].set2; |
884 | atkbd->keycode[scancode] = atkbd_scroll_keys[i].keycode; | ||
885 | } | ||
866 | } | 886 | } |
867 | 887 | ||
868 | atkbd->keycode[atkbd_compat_scancode(atkbd, ATKBD_RET_HANGEUL)] = KEY_HANGUEL; | 888 | /* |
869 | atkbd->keycode[atkbd_compat_scancode(atkbd, ATKBD_RET_HANJA)] = KEY_HANJA; | 889 | * HANGEUL and HANJA keys do not send release events so we need to |
890 | * generate such events ourselves | ||
891 | */ | ||
892 | scancode = atkbd_compat_scancode(atkbd, ATKBD_RET_HANGEUL); | ||
893 | atkbd->keycode[scancode] = KEY_HANGEUL; | ||
894 | __set_bit(scancode, atkbd->force_release_mask); | ||
895 | |||
896 | scancode = atkbd_compat_scancode(atkbd, ATKBD_RET_HANJA); | ||
897 | atkbd->keycode[scancode] = KEY_HANJA; | ||
898 | __set_bit(scancode, atkbd->force_release_mask); | ||
899 | |||
900 | /* | ||
901 | * Perform additional fixups | ||
902 | */ | ||
903 | if (atkbd_platform_fixup) | ||
904 | atkbd_platform_fixup(atkbd); | ||
870 | } | 905 | } |
871 | 906 | ||
872 | /* | 907 | /* |
@@ -1401,9 +1436,29 @@ static ssize_t atkbd_show_err_count(struct atkbd *atkbd, char *buf) | |||
1401 | return sprintf(buf, "%lu\n", atkbd->err_count); | 1436 | return sprintf(buf, "%lu\n", atkbd->err_count); |
1402 | } | 1437 | } |
1403 | 1438 | ||
1439 | static int __init atkbd_setup_fixup(const struct dmi_system_id *id) | ||
1440 | { | ||
1441 | atkbd_platform_fixup = id->driver_data; | ||
1442 | return 0; | ||
1443 | } | ||
1444 | |||
1445 | static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = { | ||
1446 | { | ||
1447 | .ident = "Dell Latitude series", | ||
1448 | .matches = { | ||
1449 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), | ||
1450 | DMI_MATCH(DMI_PRODUCT_NAME, "Latitude"), | ||
1451 | }, | ||
1452 | .callback = atkbd_setup_fixup, | ||
1453 | .driver_data = atkbd_latitude_keymap_fixup, | ||
1454 | }, | ||
1455 | { } | ||
1456 | }; | ||
1404 | 1457 | ||
1405 | static int __init atkbd_init(void) | 1458 | static int __init atkbd_init(void) |
1406 | { | 1459 | { |
1460 | dmi_check_system(atkbd_dmi_quirk_table); | ||
1461 | |||
1407 | return serio_register_driver(&atkbd_drv); | 1462 | return serio_register_driver(&atkbd_drv); |
1408 | } | 1463 | } |
1409 | 1464 | ||