From 78a849a682a1d5ee7b7187b08abdc48656326a4e Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Fri, 20 Jun 2008 21:26:11 +0200 Subject: [PATCH] HID: move microsoft quirks Move them from the core code to a separate driver. Signed-off-by: Jiri Slaby Signed-off-by: Jiri Kosina --- drivers/hid/Kconfig | 8 ++ drivers/hid/Makefile | 1 + drivers/hid/hid-core.c | 6 + drivers/hid/hid-dummy.c | 3 + drivers/hid/hid-ids.h | 4 +- drivers/hid/hid-input-quirks.c | 76 ----------- drivers/hid/hid-microsoft.c | 220 ++++++++++++++++++++++++++++++++ drivers/hid/usbhid/hid-quirks.c | 33 ----- include/linux/hid.h | 2 - 9 files changed, 241 insertions(+), 112 deletions(-) create mode 100644 drivers/hid/hid-microsoft.c diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index d9d1a5671d95..8067b653f8bf 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -104,6 +104,14 @@ config HID_LOGITECH Support for some Logitech devices which breaks less or more HID specification. +config HID_MICROSOFT + tristate "Microsoft" + default m + depends on USB_HID + ---help--- + Support for some Microsoft devices which breaks less or more + HID specification. + endmenu endif # HID_SUPPORT diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 8e053eca4742..3dc2828fb719 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -14,6 +14,7 @@ endif obj-$(CONFIG_HID_APPLE) += hid-apple.o obj-$(CONFIG_HID_LOGITECH) += hid-logitech.o +obj-$(CONFIG_HID_MICROSOFT) += hid-microsoft.o obj-$(CONFIG_USB_HID) += usbhid/ obj-$(CONFIG_USB_MOUSE) += usbhid/ diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 5e62e010d805..db8fbd2f5028 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1176,8 +1176,14 @@ static const struct hid_device_id hid_blacklist[] = { { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_V150) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_EXTREME_3D) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WHEEL) }, + { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_SIDEWINDER_GV) }, + { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_NE4K) }, + { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_LK6K) }, + { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_USB) }, + { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, 0x030c) }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_BT) }, { } }; diff --git a/drivers/hid/hid-dummy.c b/drivers/hid/hid-dummy.c index b76c44efe1b8..fe64d60f9010 100644 --- a/drivers/hid/hid-dummy.c +++ b/drivers/hid/hid-dummy.c @@ -10,6 +10,9 @@ static int __init hid_dummy_init(void) #ifdef CONFIG_HID_LOGITECH_MODULE HID_COMPAT_CALL_DRIVER(logitech); #endif +#ifdef CONFIG_HID_MICROSOFT_MODULE + HID_COMPAT_CALL_DRIVER(microsoft); +#endif return -EIO; } diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 23d021bd95d0..d7e548dbe7c3 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -280,9 +280,11 @@ #define USB_VENDOR_ID_MICROSOFT 0x045e #define USB_DEVICE_ID_SIDEWINDER_GV 0x003b #define USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0 0x009d -#define USB_DEVICE_ID_DESKTOP_RECV_1028 0x00f9 #define USB_DEVICE_ID_MS_NE4K 0x00db #define USB_DEVICE_ID_MS_LK6K 0x00f9 +#define USB_DEVICE_ID_MS_PRESENTER_8K_BT 0x0701 +#define USB_DEVICE_ID_MS_PRESENTER_8K_USB 0x0713 + #define USB_VENDOR_ID_MONTEREY 0x0566 #define USB_DEVICE_ID_GENIUS_KB29E 0x3004 diff --git a/drivers/hid/hid-input-quirks.c b/drivers/hid/hid-input-quirks.c index 878e193c6d75..d1b4f093dcb3 100644 --- a/drivers/hid/hid-input-quirks.c +++ b/drivers/hid/hid-input-quirks.c @@ -102,50 +102,6 @@ static int quirk_chicony_tactical_pad(struct hid_usage *usage, return 1; } -static int quirk_microsoft_ergonomy_kb(struct hid_usage *usage, - struct hid_input *hidinput, unsigned long **bit, int *max) -{ - struct input_dev *input = hidinput->input; - - if ((usage->hid & HID_USAGE_PAGE) != HID_UP_MSVENDOR) - return 0; - - switch(usage->hid & HID_USAGE) { - case 0xfd06: map_key_clear(KEY_CHAT); break; - case 0xfd07: map_key_clear(KEY_PHONE); break; - case 0xff05: - set_bit(EV_REP, input->evbit); - map_key_clear(KEY_F13); - set_bit(KEY_F14, input->keybit); - set_bit(KEY_F15, input->keybit); - set_bit(KEY_F16, input->keybit); - set_bit(KEY_F17, input->keybit); - set_bit(KEY_F18, input->keybit); - default: - return 0; - } - return 1; -} - -static int quirk_microsoft_presenter_8k(struct hid_usage *usage, - struct hid_input *hidinput, unsigned long **bit, int *max) -{ - if ((usage->hid & HID_USAGE_PAGE) != HID_UP_MSVENDOR) - return 0; - - set_bit(EV_REP, hidinput->input->evbit); - switch(usage->hid & HID_USAGE) { - case 0xfd08: map_key_clear(KEY_FORWARD); break; - case 0xfd09: map_key_clear(KEY_BACK); break; - case 0xfd0b: map_key_clear(KEY_PLAYPAUSE); break; - case 0xfd0e: map_key_clear(KEY_CLOSE); break; - case 0xfd0f: map_key_clear(KEY_PLAY); break; - default: - return 0; - } - return 1; -} - static int quirk_petalynx_remote(struct hid_usage *usage, struct hid_input *hidinput, unsigned long **bit, int *max) { @@ -244,12 +200,6 @@ static int quirk_sunplus_wdesktop(struct hid_usage *usage, #define VENDOR_ID_GYRATION 0x0c16 #define DEVICE_ID_GYRATION_REMOTE 0x0002 -#define VENDOR_ID_MICROSOFT 0x045e -#define DEVICE_ID_MS4K 0x00db -#define DEVICE_ID_MS6K 0x00f9 -#define DEVICE_IS_MS_PRESENTER_8K_BT 0x0701 -#define DEVICE_ID_MS_PRESENTER_8K_USB 0x0713 - #define VENDOR_ID_MONTEREY 0x0566 #define DEVICE_ID_GENIUS_KB29E 0x3004 @@ -275,11 +225,6 @@ static const struct hid_input_blacklist { { VENDOR_ID_GYRATION, DEVICE_ID_GYRATION_REMOTE, quirk_gyration_remote }, - { VENDOR_ID_MICROSOFT, DEVICE_ID_MS4K, quirk_microsoft_ergonomy_kb }, - { VENDOR_ID_MICROSOFT, DEVICE_ID_MS6K, quirk_microsoft_ergonomy_kb }, - { VENDOR_ID_MICROSOFT, DEVICE_IS_MS_PRESENTER_8K_BT, quirk_microsoft_presenter_8k }, - { VENDOR_ID_MICROSOFT, DEVICE_ID_MS_PRESENTER_8K_USB, quirk_microsoft_presenter_8k }, - { VENDOR_ID_MONTEREY, DEVICE_ID_GENIUS_KB29E, quirk_cherry_genius_29e }, { VENDOR_ID_PETALYNX, DEVICE_ID_PETALYNX_MAXTER_REMOTE, quirk_petalynx_remote }, @@ -336,27 +281,6 @@ int hidinput_event_quirks(struct hid_device *hid, struct hid_field *field, struc return 1; } - /* Handling MS keyboards special buttons */ - if (hid->quirks & HID_QUIRK_MICROSOFT_KEYS && - usage->hid == (HID_UP_MSVENDOR | 0xff05)) { - int key = 0; - static int last_key = 0; - switch (value) { - case 0x01: key = KEY_F14; break; - case 0x02: key = KEY_F15; break; - case 0x04: key = KEY_F16; break; - case 0x08: key = KEY_F17; break; - case 0x10: key = KEY_F18; break; - default: break; - } - if (key) { - input_event(input, usage->type, key, 1); - last_key = key; - } else { - input_event(input, usage->type, last_key, 0); - } - } - /* handle the temporary quirky mapping to HWHEEL */ if (hid->quirks & HID_QUIRK_HWHEEL_WHEEL_INVERT && usage->type == EV_REL && usage->code == REL_HWHEEL) { diff --git a/drivers/hid/hid-microsoft.c b/drivers/hid/hid-microsoft.c new file mode 100644 index 000000000000..1fa8b813d441 --- /dev/null +++ b/drivers/hid/hid-microsoft.c @@ -0,0 +1,220 @@ +/* + * HID driver for some microsoft "special" devices + * + * Copyright (c) 1999 Andreas Gal + * Copyright (c) 2000-2005 Vojtech Pavlik + * Copyright (c) 2005 Michael Haboustak for Concept2, Inc + * Copyright (c) 2006-2007 Jiri Kosina + * Copyright (c) 2007 Paul Walmsley + * Copyright (c) 2008 Jiri Slaby + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +#include +#include +#include +#include + +#include "hid-ids.h" + +#define MS_HIDINPUT 0x01 +#define MS_ERGONOMY 0x02 +#define MS_PRESENTER 0x04 +#define MS_RDESC 0x08 +#define MS_NOGET 0x10 + +/* + * Microsoft Wireless Desktop Receiver (Model 1028) has several + * 'Usage Min/Max' where it ought to have 'Physical Min/Max' + */ +static void ms_report_fixup(struct hid_device *hdev, __u8 *rdesc, + unsigned int rsize) +{ + unsigned long quirks = (unsigned long)hid_get_drvdata(hdev); + + if ((quirks & MS_RDESC) && rsize == 571 && rdesc[284] == 0x19 && + rdesc[286] == 0x2a && rdesc[304] == 0x19 && + rdesc[306] == 0x29 && rdesc[352] == 0x1a && + rdesc[355] == 0x2a && rdesc[557] == 0x19 && + rdesc[559] == 0x29) { + dev_info(&hdev->dev, "fixing up Microsoft Wireless Receiver " + "Model 1028 report descriptor\n"); + rdesc[284] = rdesc[304] = rdesc[557] = 0x35; + rdesc[352] = 0x36; + rdesc[286] = rdesc[355] = 0x46; + rdesc[306] = rdesc[559] = 0x45; + } +} + +#define ms_map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, \ + EV_KEY, (c)) +static int ms_ergonomy_kb_quirk(struct hid_input *hi, struct hid_usage *usage, + unsigned long **bit, int *max) +{ + struct input_dev *input = hi->input; + + switch (usage->hid & HID_USAGE) { + case 0xfd06: ms_map_key_clear(KEY_CHAT); break; + case 0xfd07: ms_map_key_clear(KEY_PHONE); break; + case 0xff05: + set_bit(EV_REP, input->evbit); + ms_map_key_clear(KEY_F13); + set_bit(KEY_F14, input->keybit); + set_bit(KEY_F15, input->keybit); + set_bit(KEY_F16, input->keybit); + set_bit(KEY_F17, input->keybit); + set_bit(KEY_F18, input->keybit); + default: + return 0; + } + return 1; +} + +static int ms_presenter_8k_quirk(struct hid_input *hi, struct hid_usage *usage, + unsigned long **bit, int *max) +{ + set_bit(EV_REP, hi->input->evbit); + switch (usage->hid & HID_USAGE) { + case 0xfd08: ms_map_key_clear(KEY_FORWARD); break; + case 0xfd09: ms_map_key_clear(KEY_BACK); break; + case 0xfd0b: ms_map_key_clear(KEY_PLAYPAUSE); break; + case 0xfd0e: ms_map_key_clear(KEY_CLOSE); break; + case 0xfd0f: ms_map_key_clear(KEY_PLAY); break; + default: + return 0; + } + return 1; +} + +static int ms_input_mapping(struct hid_device *hdev, struct hid_input *hi, + struct hid_field *field, struct hid_usage *usage, + unsigned long **bit, int *max) +{ + unsigned long quirks = (unsigned long)hid_get_drvdata(hdev); + + if ((usage->hid & HID_USAGE_PAGE) != HID_UP_MSVENDOR) + return 0; + + if (quirks & MS_ERGONOMY) { + int ret = ms_ergonomy_kb_quirk(hi, usage, bit, max); + if (ret) + return ret; + } + + if ((quirks & MS_PRESENTER) && + ms_presenter_8k_quirk(hi, usage, bit, max)) + return 1; + + return 0; +} + +static int ms_event(struct hid_device *hdev, struct hid_field *field, + struct hid_usage *usage, __s32 value) +{ + unsigned long quirks = (unsigned long)hid_get_drvdata(hdev); + + if (!(hdev->claimed & HID_CLAIMED_INPUT) || !field->hidinput || + !usage->type) + return 0; + + /* Handling MS keyboards special buttons */ + if (quirks & MS_ERGONOMY && usage->hid == (HID_UP_MSVENDOR | 0xff05)) { + struct input_dev *input = field->hidinput->input; + static unsigned int last_key = 0; + unsigned int key = 0; + switch (value) { + case 0x01: key = KEY_F14; break; + case 0x02: key = KEY_F15; break; + case 0x04: key = KEY_F16; break; + case 0x08: key = KEY_F17; break; + case 0x10: key = KEY_F18; break; + } + if (key) { + input_event(input, usage->type, key, 1); + last_key = key; + } else + input_event(input, usage->type, last_key, 0); + + return 1; + } + + return 0; +} + +static int ms_probe(struct hid_device *hdev, const struct hid_device_id *id) +{ + unsigned long quirks = id->driver_data; + int ret; + + hid_set_drvdata(hdev, (void *)quirks); + + if (quirks & MS_HIDINPUT) + hdev->quirks |= HID_QUIRK_HIDINPUT; + if (quirks & MS_NOGET) + hdev->quirks |= HID_QUIRK_NOGET; + + ret = hid_parse(hdev); + if (ret) { + dev_err(&hdev->dev, "parse failed\n"); + goto err_free; + } + + ret = hid_hw_start(hdev); + if (ret) { + dev_err(&hdev->dev, "hw start failed\n"); + goto err_free; + } + + return 0; +err_free: + return ret; +} + +static const struct hid_device_id ms_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_SIDEWINDER_GV), + .driver_data = MS_HIDINPUT }, + { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_NE4K), + .driver_data = MS_ERGONOMY }, + { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_LK6K), + .driver_data = MS_ERGONOMY | MS_RDESC }, + { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_USB), + .driver_data = MS_PRESENTER }, + { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0), + .driver_data = MS_NOGET }, + + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_BT), + .driver_data = MS_PRESENTER }, + { } +}; +MODULE_DEVICE_TABLE(hid, ms_devices); + +static struct hid_driver ms_driver = { + .name = "microsoft", + .id_table = ms_devices, + .report_fixup = ms_report_fixup, + .input_mapping = ms_input_mapping, + .event = ms_event, + .probe = ms_probe, +}; + +static int ms_init(void) +{ + return hid_register_driver(&ms_driver); +} + +static void ms_exit(void) +{ + hid_unregister_driver(&ms_driver); +} + +module_init(ms_init); +module_exit(ms_exit); +MODULE_LICENSE("GPL"); + +HID_COMPAT_LOAD_DRIVER(microsoft); diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index 4c5ee9ecd40a..1614ed2efc15 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -50,14 +50,9 @@ static const struct hid_blacklist { { USB_VENDOR_ID_BELKIN, USB_DEVICE_ID_FLIP_KVM, HID_QUIRK_HIDDEV }, { USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE, HID_QUIRK_HIDDEV | HID_QUIRK_IGNORE_HIDINPUT }, - { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_SIDEWINDER_GV, HID_QUIRK_HIDINPUT }, { USB_VENDOR_ID_EZKEY, USB_DEVICE_ID_BTC_8193, HID_QUIRK_HWHEEL_WHEEL_INVERT }, - - { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_NE4K, HID_QUIRK_MICROSOFT_KEYS }, - { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_LK6K, HID_QUIRK_MICROSOFT_KEYS }, - { USB_VENDOR_ID_PANTHERLORD, USB_DEVICE_ID_PANTHERLORD_TWIN_USB_JOYSTICK, HID_QUIRK_MULTI_INPUT | HID_QUIRK_SKIP_OUTPUT_REPORTS }, { USB_VENDOR_ID_PLAYDOTCOM, USB_DEVICE_ID_PLAYDOTCOM_EMS_USBII, HID_QUIRK_MULTI_INPUT }, @@ -70,7 +65,6 @@ static const struct hid_blacklist { { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_4PORTKVMC, HID_QUIRK_NOGET }, { USB_VENDOR_ID_DMI, USB_DEVICE_ID_DMI_ENC, HID_QUIRK_NOGET }, { USB_VENDOR_ID_ELO, USB_DEVICE_ID_ELO_TS2700, HID_QUIRK_NOGET }, - { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0, HID_QUIRK_NOGET }, { USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE, HID_QUIRK_NOGET }, { USB_VENDOR_ID_SUN, USB_DEVICE_ID_RARITAN_KVM_DONGLE, HID_QUIRK_NOGET }, { USB_VENDOR_ID_TURBOX, USB_DEVICE_ID_TURBOX_KEYBOARD, HID_QUIRK_NOGET }, @@ -93,8 +87,6 @@ static const struct hid_rdesc_blacklist { { USB_VENDOR_ID_CHERRY, USB_DEVICE_ID_CHERRY_CYMOTION, HID_QUIRK_RDESC_CYMOTION }, - { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_DESKTOP_RECV_1028, HID_QUIRK_RDESC_MICROSOFT_RECV_1028 }, - { USB_VENDOR_ID_MONTEREY, USB_DEVICE_ID_GENIUS_KB29E, HID_QUIRK_RDESC_BUTTON_CONSUMER }, { USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE, HID_QUIRK_RDESC_PETALYNX }, @@ -436,28 +428,6 @@ static void usbhid_fixup_button_consumer_descriptor(unsigned char *rdesc, int rs } } -/* - * Microsoft Wireless Desktop Receiver (Model 1028) has several - * 'Usage Min/Max' where it ought to have 'Physical Min/Max' - */ -static void usbhid_fixup_microsoft_descriptor(unsigned char *rdesc, int rsize) -{ - if (rsize == 571 && rdesc[284] == 0x19 - && rdesc[286] == 0x2a - && rdesc[304] == 0x19 - && rdesc[306] == 0x29 - && rdesc[352] == 0x1a - && rdesc[355] == 0x2a - && rdesc[557] == 0x19 - && rdesc[559] == 0x29) { - printk(KERN_INFO "Fixing up Microsoft Wireless Receiver Model 1028 report descriptor\n"); - rdesc[284] = rdesc[304] = rdesc[557] = 0x35; - rdesc[352] = 0x36; - rdesc[286] = rdesc[355] = 0x46; - rdesc[306] = rdesc[559] = 0x45; - } -} - static void __usbhid_fixup_report_descriptor(__u32 quirks, char *rdesc, unsigned rsize) { if ((quirks & HID_QUIRK_RDESC_CYMOTION)) @@ -475,9 +445,6 @@ static void __usbhid_fixup_report_descriptor(__u32 quirks, char *rdesc, unsigned if (quirks & HID_QUIRK_RDESC_SAMSUNG_REMOTE) usbhid_fixup_samsung_irda_descriptor(rdesc, rsize); - if (quirks & HID_QUIRK_RDESC_MICROSOFT_RECV_1028) - usbhid_fixup_microsoft_descriptor(rdesc, rsize); - if (quirks & HID_QUIRK_RDESC_SUNPLUS_WDESKTOP) usbhid_fixup_sunplus_wdesktop(rdesc, rsize); } diff --git a/include/linux/hid.h b/include/linux/hid.h index 60e44e6b86e6..1f1edd886ef8 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -271,7 +271,6 @@ struct hid_item { #define HID_QUIRK_IGNORE_HIDINPUT 0x01000000 #define HID_QUIRK_2WHEEL_MOUSE_HACK_B8 0x02000000 #define HID_QUIRK_HWHEEL_WHEEL_INVERT 0x04000000 -#define HID_QUIRK_MICROSOFT_KEYS 0x08000000 #define HID_QUIRK_FULLSPEED_INTERVAL 0x10000000 /* @@ -283,7 +282,6 @@ struct hid_item { #define HID_QUIRK_RDESC_PETALYNX 0x00000008 #define HID_QUIRK_RDESC_BUTTON_CONSUMER 0x00000020 #define HID_QUIRK_RDESC_SAMSUNG_REMOTE 0x00000040 -#define HID_QUIRK_RDESC_MICROSOFT_RECV_1028 0x00000080 #define HID_QUIRK_RDESC_SUNPLUS_WDESKTOP 0x00000100 /* -- 2.25.1