diff options
Diffstat (limited to 'drivers/input/keyboard/atkbd.c')
-rw-r--r-- | drivers/input/keyboard/atkbd.c | 91 |
1 files changed, 72 insertions, 19 deletions
diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c index b39c5b31e620..4a95adc4cc78 100644 --- a/drivers/input/keyboard/atkbd.c +++ b/drivers/input/keyboard/atkbd.c | |||
@@ -19,7 +19,6 @@ | |||
19 | 19 | ||
20 | #include <linux/delay.h> | 20 | #include <linux/delay.h> |
21 | #include <linux/module.h> | 21 | #include <linux/module.h> |
22 | #include <linux/moduleparam.h> | ||
23 | #include <linux/slab.h> | 22 | #include <linux/slab.h> |
24 | #include <linux/interrupt.h> | 23 | #include <linux/interrupt.h> |
25 | #include <linux/init.h> | 24 | #include <linux/init.h> |
@@ -28,6 +27,7 @@ | |||
28 | #include <linux/workqueue.h> | 27 | #include <linux/workqueue.h> |
29 | #include <linux/libps2.h> | 28 | #include <linux/libps2.h> |
30 | #include <linux/mutex.h> | 29 | #include <linux/mutex.h> |
30 | #include <linux/dmi.h> | ||
31 | 31 | ||
32 | #define DRIVER_DESC "AT and PS/2 keyboard driver" | 32 | #define DRIVER_DESC "AT and PS/2 keyboard driver" |
33 | 33 | ||
@@ -201,6 +201,7 @@ struct atkbd { | |||
201 | 201 | ||
202 | unsigned short id; | 202 | unsigned short id; |
203 | unsigned char keycode[512]; | 203 | unsigned char keycode[512]; |
204 | DECLARE_BITMAP(force_release_mask, 512); | ||
204 | unsigned char set; | 205 | unsigned char set; |
205 | unsigned char translated; | 206 | unsigned char translated; |
206 | unsigned char extra; | 207 | unsigned char extra; |
@@ -225,6 +226,11 @@ struct atkbd { | |||
225 | unsigned long event_mask; | 226 | unsigned long event_mask; |
226 | }; | 227 | }; |
227 | 228 | ||
229 | /* | ||
230 | * System-specific ketymap fixup routine | ||
231 | */ | ||
232 | static void (*atkbd_platform_fixup)(struct atkbd *); | ||
233 | |||
228 | static ssize_t atkbd_attr_show_helper(struct device *dev, char *buf, | 234 | static ssize_t atkbd_attr_show_helper(struct device *dev, char *buf, |
229 | ssize_t (*handler)(struct atkbd *, char *)); | 235 | ssize_t (*handler)(struct atkbd *, char *)); |
230 | static ssize_t atkbd_attr_set_helper(struct device *dev, const char *buf, size_t count, | 236 | static ssize_t atkbd_attr_set_helper(struct device *dev, const char *buf, size_t count, |
@@ -349,7 +355,7 @@ static irqreturn_t atkbd_interrupt(struct serio *serio, unsigned char data, | |||
349 | struct atkbd *atkbd = serio_get_drvdata(serio); | 355 | struct atkbd *atkbd = serio_get_drvdata(serio); |
350 | struct input_dev *dev = atkbd->dev; | 356 | struct input_dev *dev = atkbd->dev; |
351 | unsigned int code = data; | 357 | unsigned int code = data; |
352 | int scroll = 0, hscroll = 0, click = -1, add_release_event = 0; | 358 | int scroll = 0, hscroll = 0, click = -1; |
353 | int value; | 359 | int value; |
354 | unsigned char keycode; | 360 | unsigned char keycode; |
355 | 361 | ||
@@ -414,14 +420,6 @@ static irqreturn_t atkbd_interrupt(struct serio *serio, unsigned char data, | |||
414 | "Some program might be trying access hardware directly.\n", | 420 | "Some program might be trying access hardware directly.\n", |
415 | data == ATKBD_RET_ACK ? "ACK" : "NAK", serio->phys); | 421 | data == ATKBD_RET_ACK ? "ACK" : "NAK", serio->phys); |
416 | goto out; | 422 | 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: | 423 | case ATKBD_RET_ERR: |
426 | atkbd->err_count++; | 424 | atkbd->err_count++; |
427 | #ifdef ATKBD_DEBUG | 425 | #ifdef ATKBD_DEBUG |
@@ -491,7 +489,7 @@ static irqreturn_t atkbd_interrupt(struct serio *serio, unsigned char data, | |||
491 | input_event(dev, EV_KEY, keycode, value); | 489 | input_event(dev, EV_KEY, keycode, value); |
492 | input_sync(dev); | 490 | input_sync(dev); |
493 | 491 | ||
494 | if (value && add_release_event) { | 492 | if (value && test_bit(code, atkbd->force_release_mask)) { |
495 | input_report_key(dev, keycode, 0); | 493 | input_report_key(dev, keycode, 0); |
496 | input_sync(dev); | 494 | input_sync(dev); |
497 | } | 495 | } |
@@ -824,7 +822,6 @@ static void atkbd_disconnect(struct serio *serio) | |||
824 | atkbd_disable(atkbd); | 822 | atkbd_disable(atkbd); |
825 | 823 | ||
826 | /* make sure we don't have a command in flight */ | 824 | /* make sure we don't have a command in flight */ |
827 | synchronize_sched(); /* Allow atkbd_interrupt()s to complete. */ | ||
828 | flush_scheduled_work(); | 825 | flush_scheduled_work(); |
829 | 826 | ||
830 | sysfs_remove_group(&serio->dev.kobj, &atkbd_attribute_group); | 827 | sysfs_remove_group(&serio->dev.kobj, &atkbd_attribute_group); |
@@ -834,6 +831,22 @@ static void atkbd_disconnect(struct serio *serio) | |||
834 | kfree(atkbd); | 831 | kfree(atkbd); |
835 | } | 832 | } |
836 | 833 | ||
834 | /* | ||
835 | * Most special keys (Fn+F?) on Dell Latitudes do not generate release | ||
836 | * events so we have to do it ourselves. | ||
837 | */ | ||
838 | static void atkbd_latitude_keymap_fixup(struct atkbd *atkbd) | ||
839 | { | ||
840 | const unsigned int forced_release_keys[] = { | ||
841 | 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8f, 0x93, | ||
842 | }; | ||
843 | int i; | ||
844 | |||
845 | if (atkbd->set == 2) | ||
846 | for (i = 0; i < ARRAY_SIZE(forced_release_keys); i++) | ||
847 | __set_bit(forced_release_keys[i], | ||
848 | atkbd->force_release_mask); | ||
849 | } | ||
837 | 850 | ||
838 | /* | 851 | /* |
839 | * atkbd_set_keycode_table() initializes keyboard's keycode table | 852 | * atkbd_set_keycode_table() initializes keyboard's keycode table |
@@ -842,17 +855,20 @@ static void atkbd_disconnect(struct serio *serio) | |||
842 | 855 | ||
843 | static void atkbd_set_keycode_table(struct atkbd *atkbd) | 856 | static void atkbd_set_keycode_table(struct atkbd *atkbd) |
844 | { | 857 | { |
858 | unsigned int scancode; | ||
845 | int i, j; | 859 | int i, j; |
846 | 860 | ||
847 | memset(atkbd->keycode, 0, sizeof(atkbd->keycode)); | 861 | memset(atkbd->keycode, 0, sizeof(atkbd->keycode)); |
862 | bitmap_zero(atkbd->force_release_mask, 512); | ||
848 | 863 | ||
849 | if (atkbd->translated) { | 864 | if (atkbd->translated) { |
850 | for (i = 0; i < 128; i++) { | 865 | for (i = 0; i < 128; i++) { |
851 | atkbd->keycode[i] = atkbd_set2_keycode[atkbd_unxlate_table[i]]; | 866 | scancode = atkbd_unxlate_table[i]; |
852 | atkbd->keycode[i | 0x80] = atkbd_set2_keycode[atkbd_unxlate_table[i] | 0x80]; | 867 | atkbd->keycode[i] = atkbd_set2_keycode[scancode]; |
868 | atkbd->keycode[i | 0x80] = atkbd_set2_keycode[scancode | 0x80]; | ||
853 | if (atkbd->scroll) | 869 | if (atkbd->scroll) |
854 | for (j = 0; j < ARRAY_SIZE(atkbd_scroll_keys); j++) | 870 | for (j = 0; j < ARRAY_SIZE(atkbd_scroll_keys); j++) |
855 | if ((atkbd_unxlate_table[i] | 0x80) == atkbd_scroll_keys[j].set2) | 871 | if ((scancode | 0x80) == atkbd_scroll_keys[j].set2) |
856 | atkbd->keycode[i | 0x80] = atkbd_scroll_keys[j].keycode; | 872 | atkbd->keycode[i | 0x80] = atkbd_scroll_keys[j].keycode; |
857 | } | 873 | } |
858 | } else if (atkbd->set == 3) { | 874 | } else if (atkbd->set == 3) { |
@@ -861,12 +877,29 @@ static void atkbd_set_keycode_table(struct atkbd *atkbd) | |||
861 | memcpy(atkbd->keycode, atkbd_set2_keycode, sizeof(atkbd->keycode)); | 877 | memcpy(atkbd->keycode, atkbd_set2_keycode, sizeof(atkbd->keycode)); |
862 | 878 | ||
863 | if (atkbd->scroll) | 879 | if (atkbd->scroll) |
864 | for (i = 0; i < ARRAY_SIZE(atkbd_scroll_keys); i++) | 880 | for (i = 0; i < ARRAY_SIZE(atkbd_scroll_keys); i++) { |
865 | atkbd->keycode[atkbd_scroll_keys[i].set2] = atkbd_scroll_keys[i].keycode; | 881 | scancode = atkbd_scroll_keys[i].set2; |
882 | atkbd->keycode[scancode] = atkbd_scroll_keys[i].keycode; | ||
883 | } | ||
866 | } | 884 | } |
867 | 885 | ||
868 | atkbd->keycode[atkbd_compat_scancode(atkbd, ATKBD_RET_HANGEUL)] = KEY_HANGUEL; | 886 | /* |
869 | atkbd->keycode[atkbd_compat_scancode(atkbd, ATKBD_RET_HANJA)] = KEY_HANJA; | 887 | * HANGEUL and HANJA keys do not send release events so we need to |
888 | * generate such events ourselves | ||
889 | */ | ||
890 | scancode = atkbd_compat_scancode(atkbd, ATKBD_RET_HANGEUL); | ||
891 | atkbd->keycode[scancode] = KEY_HANGEUL; | ||
892 | __set_bit(scancode, atkbd->force_release_mask); | ||
893 | |||
894 | scancode = atkbd_compat_scancode(atkbd, ATKBD_RET_HANJA); | ||
895 | atkbd->keycode[scancode] = KEY_HANJA; | ||
896 | __set_bit(scancode, atkbd->force_release_mask); | ||
897 | |||
898 | /* | ||
899 | * Perform additional fixups | ||
900 | */ | ||
901 | if (atkbd_platform_fixup) | ||
902 | atkbd_platform_fixup(atkbd); | ||
870 | } | 903 | } |
871 | 904 | ||
872 | /* | 905 | /* |
@@ -1401,9 +1434,29 @@ static ssize_t atkbd_show_err_count(struct atkbd *atkbd, char *buf) | |||
1401 | return sprintf(buf, "%lu\n", atkbd->err_count); | 1434 | return sprintf(buf, "%lu\n", atkbd->err_count); |
1402 | } | 1435 | } |
1403 | 1436 | ||
1437 | static int __init atkbd_setup_fixup(const struct dmi_system_id *id) | ||
1438 | { | ||
1439 | atkbd_platform_fixup = id->driver_data; | ||
1440 | return 0; | ||
1441 | } | ||
1442 | |||
1443 | static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = { | ||
1444 | { | ||
1445 | .ident = "Dell Latitude series", | ||
1446 | .matches = { | ||
1447 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), | ||
1448 | DMI_MATCH(DMI_PRODUCT_NAME, "Latitude"), | ||
1449 | }, | ||
1450 | .callback = atkbd_setup_fixup, | ||
1451 | .driver_data = atkbd_latitude_keymap_fixup, | ||
1452 | }, | ||
1453 | { } | ||
1454 | }; | ||
1404 | 1455 | ||
1405 | static int __init atkbd_init(void) | 1456 | static int __init atkbd_init(void) |
1406 | { | 1457 | { |
1458 | dmi_check_system(atkbd_dmi_quirk_table); | ||
1459 | |||
1407 | return serio_register_driver(&atkbd_drv); | 1460 | return serio_register_driver(&atkbd_drv); |
1408 | } | 1461 | } |
1409 | 1462 | ||