HID: move logitech quirks
authorJiri Slaby <jirislaby@gmail.com>
Fri, 16 May 2008 09:49:19 +0000 (11:49 +0200)
committerJiri Kosina <jkosina@suse.cz>
Tue, 14 Oct 2008 21:50:49 +0000 (23:50 +0200)
Move them from the core and input code to a separate driver.

Signed-off-by: Jiri Slaby <jslaby@suse.cz>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
drivers/hid/Kconfig
drivers/hid/Makefile
drivers/hid/hid-core.c
drivers/hid/hid-input-quirks.c
drivers/hid/hid-input.c
drivers/hid/hid-logitech.c [new file with mode: 0644]
drivers/hid/usbhid/hid-quirks.c
include/linux/hid.h

index cacf89e65af4d9f96b1785c873b6f76b384775c3..066e8c08c268624144c0f7c930247334581be35b 100644 (file)
@@ -67,4 +67,17 @@ config HIDRAW
 
 source "drivers/hid/usbhid/Kconfig"
 
+menu "Special HID drivers"
+       depends on HID
+
+config HID_LOGITECH
+       tristate "Logitech"
+       default m
+       depends on USB_HID
+       ---help---
+       Support for some Logitech devices which breaks less or more
+       HID specification.
+
+endmenu
+
 endif # HID_SUPPORT
index 275dc522c7389489583f7424fb75427d2a04867d..cae036bfe83759b25b91d5b77622ea8ae9468de6 100644 (file)
@@ -8,6 +8,8 @@ obj-$(CONFIG_HID)               += hid.o
 hid-$(CONFIG_HID_DEBUG)                += hid-debug.o
 hid-$(CONFIG_HIDRAW)           += hidraw.o
 
+obj-$(CONFIG_HID_LOGITECH)     += hid-logitech.o
+
 obj-$(CONFIG_USB_HID)          += usbhid/
 obj-$(CONFIG_USB_MOUSE)                += usbhid/
 obj-$(CONFIG_USB_KBD)          += usbhid/
index 3dacbcd7e41c404fe82137d6bcba6e7cd8ad34b9..c3ff7b13a4be9b6cea12f3eff3591f091c9fe9ae 100644 (file)
@@ -33,6 +33,8 @@
 #include <linux/hid-debug.h>
 #include <linux/hidraw.h>
 
+#include "hid-ids.h"
+
 /*
  * Version Information
  */
@@ -1128,6 +1130,20 @@ static const struct hid_device_id *hid_match_id(struct hid_device *hdev,
 }
 
 static const struct hid_device_id hid_blacklist[] = {
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER_2) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RECEIVER) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_DESKTOP) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_EDGE) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_MINI) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_KBD) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_ELITE_KBD) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_LX3) },
+       { 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) },
        { }
 };
 
index 8ec64b74d38dc187e1ad0a420819c9aa2bc63510..10451ca3a786bad7809b8817691745fafcef7340 100644 (file)
@@ -54,39 +54,6 @@ static int quirk_cherry_cymotion(struct hid_usage *usage,
        return 1;
 }
 
-static int quirk_logitech_ultrax_remote(struct hid_usage *usage,
-               struct hid_input *hidinput, unsigned long **bit, int *max)
-{
-       if ((usage->hid & HID_USAGE_PAGE) != HID_UP_LOGIVENDOR)
-               return 0;
-
-       set_bit(EV_REP, hidinput->input->evbit);
-       switch(usage->hid & HID_USAGE) {
-               /* Reported on Logitech Ultra X Media Remote */
-               case 0x004: map_key_clear(KEY_AGAIN);           break;
-               case 0x00d: map_key_clear(KEY_HOME);            break;
-               case 0x024: map_key_clear(KEY_SHUFFLE);         break;
-               case 0x025: map_key_clear(KEY_TV);              break;
-               case 0x026: map_key_clear(KEY_MENU);            break;
-               case 0x031: map_key_clear(KEY_AUDIO);           break;
-               case 0x032: map_key_clear(KEY_TEXT);            break;
-               case 0x033: map_key_clear(KEY_LAST);            break;
-               case 0x047: map_key_clear(KEY_MP3);             break;
-               case 0x048: map_key_clear(KEY_DVD);             break;
-               case 0x049: map_key_clear(KEY_MEDIA);           break;
-               case 0x04a: map_key_clear(KEY_VIDEO);           break;
-               case 0x04b: map_key_clear(KEY_ANGLE);           break;
-               case 0x04c: map_key_clear(KEY_LANGUAGE);        break;
-               case 0x04d: map_key_clear(KEY_SUBTITLE);        break;
-               case 0x051: map_key_clear(KEY_RED);             break;
-               case 0x052: map_key_clear(KEY_CLOSE);           break;
-
-               default:
-                       return 0;
-       }
-       return 1;
-}
-
 static int quirk_gyration_remote(struct hid_usage *usage,
                struct hid_input *hidinput, unsigned long **bit, int *max)
 {
@@ -207,58 +174,6 @@ static int quirk_petalynx_remote(struct hid_usage *usage,
        return 1;
 }
 
-static int quirk_logitech_wireless(struct hid_usage *usage,
-               struct hid_input *hidinput, unsigned long **bit, int *max)
-{
-       if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER)
-               return 0;
-
-       switch (usage->hid & HID_USAGE) {
-               case 0x1001: map_key_clear(KEY_MESSENGER);      break;
-               case 0x1003: map_key_clear(KEY_SOUND);          break;
-               case 0x1004: map_key_clear(KEY_VIDEO);          break;
-               case 0x1005: map_key_clear(KEY_AUDIO);          break;
-               case 0x100a: map_key_clear(KEY_DOCUMENTS);      break;
-               case 0x1011: map_key_clear(KEY_PREVIOUSSONG);   break;
-               case 0x1012: map_key_clear(KEY_NEXTSONG);       break;
-               case 0x1013: map_key_clear(KEY_CAMERA);         break;
-               case 0x1014: map_key_clear(KEY_MESSENGER);      break;
-               case 0x1015: map_key_clear(KEY_RECORD);         break;
-               case 0x1016: map_key_clear(KEY_PLAYER);         break;
-               case 0x1017: map_key_clear(KEY_EJECTCD);        break;
-               case 0x1018: map_key_clear(KEY_MEDIA);          break;
-               case 0x1019: map_key_clear(KEY_PROG1);          break;
-               case 0x101a: map_key_clear(KEY_PROG2);          break;
-               case 0x101b: map_key_clear(KEY_PROG3);          break;
-               case 0x101f: map_key_clear(KEY_ZOOMIN);         break;
-               case 0x1020: map_key_clear(KEY_ZOOMOUT);        break;
-               case 0x1021: map_key_clear(KEY_ZOOMRESET);      break;
-               case 0x1023: map_key_clear(KEY_CLOSE);          break;
-               case 0x1027: map_key_clear(KEY_MENU);           break;
-               /* this one is marked as 'Rotate' */
-               case 0x1028: map_key_clear(KEY_ANGLE);          break;
-               case 0x1029: map_key_clear(KEY_SHUFFLE);        break;
-               case 0x102a: map_key_clear(KEY_BACK);           break;
-               case 0x102b: map_key_clear(KEY_CYCLEWINDOWS);   break;
-               case 0x1041: map_key_clear(KEY_BATTERY);        break;
-               case 0x1042: map_key_clear(KEY_WORDPROCESSOR);  break;
-               case 0x1043: map_key_clear(KEY_SPREADSHEET);    break;
-               case 0x1044: map_key_clear(KEY_PRESENTATION);   break;
-               case 0x1045: map_key_clear(KEY_UNDO);           break;
-               case 0x1046: map_key_clear(KEY_REDO);           break;
-               case 0x1047: map_key_clear(KEY_PRINT);          break;
-               case 0x1048: map_key_clear(KEY_SAVE);           break;
-               case 0x1049: map_key_clear(KEY_PROG1);          break;
-               case 0x104a: map_key_clear(KEY_PROG2);          break;
-               case 0x104b: map_key_clear(KEY_PROG3);          break;
-               case 0x104c: map_key_clear(KEY_PROG4);          break;
-
-               default:
-                       return 0;
-       }
-       return 1;
-}
-
 static int quirk_cherry_genius_29e(struct hid_usage *usage,
                struct hid_input *hidinput, unsigned long **bit, int *max)
 {
@@ -329,12 +244,6 @@ static int quirk_sunplus_wdesktop(struct hid_usage *usage,
 #define VENDOR_ID_GYRATION                     0x0c16
 #define DEVICE_ID_GYRATION_REMOTE              0x0002
 
-#define VENDOR_ID_LOGITECH                     0x046d
-#define DEVICE_ID_LOGITECH_RECEIVER            0xc101
-#define DEVICE_ID_S510_RECEIVER                        0xc50c
-#define DEVICE_ID_S510_RECEIVER_2              0xc517
-#define DEVICE_ID_MX3000_RECEIVER              0xc513
-
 #define VENDOR_ID_MICROSOFT                    0x045e
 #define DEVICE_ID_MS4K                         0x00db
 #define DEVICE_ID_MS6K                         0x00f9
@@ -366,11 +275,6 @@ static const struct hid_input_blacklist {
 
        { VENDOR_ID_GYRATION, DEVICE_ID_GYRATION_REMOTE, quirk_gyration_remote },
 
-       { VENDOR_ID_LOGITECH, DEVICE_ID_LOGITECH_RECEIVER, quirk_logitech_ultrax_remote },
-       { VENDOR_ID_LOGITECH, DEVICE_ID_S510_RECEIVER, quirk_logitech_wireless },
-       { VENDOR_ID_LOGITECH, DEVICE_ID_S510_RECEIVER_2, quirk_logitech_wireless },
-       { VENDOR_ID_LOGITECH, DEVICE_ID_MX3000_RECEIVER, quirk_logitech_wireless },
-
        { 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 },
index be2c7a8ad254101770978731f3f3498f418cf36e..4f2bac010f5955d9def3b4d3a3bf8196cc1f253c 100644 (file)
@@ -58,19 +58,6 @@ static const unsigned char hid_keyboard[256] = {
        150,158,159,128,136,177,178,176,142,152,173,140,unk,unk,unk,unk
 };
 
-/* extended mapping for certain Logitech hardware (Logitech cordless desktop LX500) */
-#define LOGITECH_EXPANDED_KEYMAP_SIZE 80
-static int logitech_expanded_keymap[LOGITECH_EXPANDED_KEYMAP_SIZE] = {
-         0,216,  0,213,175,156,  0,  0,  0,  0,
-       144,  0,  0,  0,  0,  0,  0,  0,  0,212,
-       174,167,152,161,112,  0,  0,  0,154,  0,
-         0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-         0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-         0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-         0,  0,  0,  0,  0,183,184,185,186,187,
-       188,189,190,191,192,193,194,  0,  0,  0
-};
-
 static const struct {
        __s32 x;
        __s32 y;
@@ -437,21 +424,6 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
                                        }
                        }
 
-                       /* Special handling for Logitech Cordless Desktop */
-                       if (field->application != HID_GD_MOUSE) {
-                               if (device->quirks & HID_QUIRK_LOGITECH_EXPANDED_KEYMAP) {
-                                       int hid = usage->hid & HID_USAGE;
-                                       if (hid < LOGITECH_EXPANDED_KEYMAP_SIZE && logitech_expanded_keymap[hid] != 0)
-                                               code = logitech_expanded_keymap[hid];
-                               }
-                       } else {
-                               if (device->quirks & HID_QUIRK_LOGITECH_IGNORE_DOUBLED_WHEEL) {
-                                       int hid = usage->hid & HID_USAGE;
-                                       if (hid == 7 || hid == 8)
-                                               goto ignore;
-                               }
-                       }
-
                        map_key(code);
                        break;
 
@@ -788,18 +760,8 @@ mapped:
                || ((device->quirks & HID_QUIRK_2WHEEL_MOUSE_HACK_7) && (usage->hid == 0x00090007)))
                goto ignore;
 
-       if ((device->quirks & HID_QUIRK_BAD_RELATIVE_KEYS) &&
-               usage->type == EV_KEY && (field->flags & HID_MAIN_ITEM_RELATIVE))
-               field->flags &= ~HID_MAIN_ITEM_RELATIVE;
-
        set_bit(usage->type, input->evbit);
 
-       if (device->quirks & HID_QUIRK_DUPLICATE_USAGES &&
-                       (usage->type == EV_KEY ||
-                        usage->type == EV_REL ||
-                        usage->type == EV_ABS))
-               clear_bit(usage->code, bit);
-
        while (usage->code <= max && test_and_set_bit(usage->code, bit))
                usage->code = find_next_zero_bit(bit, max + 1, usage->code);
 
diff --git a/drivers/hid/hid-logitech.c b/drivers/hid/hid-logitech.c
new file mode 100644 (file)
index 0000000..395e42f
--- /dev/null
@@ -0,0 +1,312 @@
+/*
+ *  HID driver for some logitech "special" devices
+ *
+ *  Copyright (c) 1999 Andreas Gal
+ *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
+ *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> 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 <linux/device.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+
+#define LG_RDESC               0x001
+#define LG_BAD_RELATIVE_KEYS   0x002
+#define LG_DUPLICATE_USAGES    0x004
+#define LG_RESET_LEDS          0x008
+#define LG_EXPANDED_KEYMAP     0x010
+#define LG_IGNORE_DOUBLED_WHEEL        0x020
+#define LG_WIRELESS            0x040
+#define LG_INVERT_HWHEEL       0x080
+#define LG_NOGET               0x100
+
+/*
+ * Certain Logitech keyboards send in report #3 keys which are far
+ * above the logical maximum described in descriptor. This extends
+ * the original value of 0x28c of logical maximum to 0x104d
+ */
+static void lg_report_fixup(struct hid_device *hdev, __u8 *rdesc,
+               unsigned int rsize)
+{
+       unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);
+
+       if ((quirks & LG_RDESC) && rsize >= 90 && rdesc[83] == 0x26 &&
+                       rdesc[84] == 0x8c && rdesc[85] == 0x02) {
+               dev_info(&hdev->dev, "fixing up Logitech keyboard report "
+                               "descriptor\n");
+               rdesc[84] = rdesc[89] = 0x4d;
+               rdesc[85] = rdesc[90] = 0x10;
+       }
+}
+
+#define lg_map_key_clear(c)    hid_map_usage_clear(hi, usage, bit, max, \
+               EV_KEY, (c))
+
+static int lg_ultrax_remote_mapping(struct hid_input *hi,
+               struct hid_usage *usage, unsigned long **bit, int *max)
+{
+       if ((usage->hid & HID_USAGE_PAGE) != HID_UP_LOGIVENDOR)
+               return 0;
+
+       set_bit(EV_REP, hi->input->evbit);
+       switch (usage->hid & HID_USAGE) {
+       /* Reported on Logitech Ultra X Media Remote */
+       case 0x004: lg_map_key_clear(KEY_AGAIN);        break;
+       case 0x00d: lg_map_key_clear(KEY_HOME);         break;
+       case 0x024: lg_map_key_clear(KEY_SHUFFLE);      break;
+       case 0x025: lg_map_key_clear(KEY_TV);           break;
+       case 0x026: lg_map_key_clear(KEY_MENU);         break;
+       case 0x031: lg_map_key_clear(KEY_AUDIO);        break;
+       case 0x032: lg_map_key_clear(KEY_TEXT);         break;
+       case 0x033: lg_map_key_clear(KEY_LAST);         break;
+       case 0x047: lg_map_key_clear(KEY_MP3);          break;
+       case 0x048: lg_map_key_clear(KEY_DVD);          break;
+       case 0x049: lg_map_key_clear(KEY_MEDIA);        break;
+       case 0x04a: lg_map_key_clear(KEY_VIDEO);        break;
+       case 0x04b: lg_map_key_clear(KEY_ANGLE);        break;
+       case 0x04c: lg_map_key_clear(KEY_LANGUAGE);     break;
+       case 0x04d: lg_map_key_clear(KEY_SUBTITLE);     break;
+       case 0x051: lg_map_key_clear(KEY_RED);          break;
+       case 0x052: lg_map_key_clear(KEY_CLOSE);        break;
+
+       default:
+               return 0;
+       }
+       return 1;
+}
+
+static int lg_wireless_mapping(struct hid_input *hi, struct hid_usage *usage,
+               unsigned long **bit, int *max)
+{
+       if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER)
+               return 0;
+
+       switch (usage->hid & HID_USAGE) {
+       case 0x1001: lg_map_key_clear(KEY_MESSENGER);           break;
+       case 0x1003: lg_map_key_clear(KEY_SOUND);               break;
+       case 0x1004: lg_map_key_clear(KEY_VIDEO);               break;
+       case 0x1005: lg_map_key_clear(KEY_AUDIO);               break;
+       case 0x100a: lg_map_key_clear(KEY_DOCUMENTS);           break;
+       case 0x1011: lg_map_key_clear(KEY_PREVIOUSSONG);        break;
+       case 0x1012: lg_map_key_clear(KEY_NEXTSONG);            break;
+       case 0x1013: lg_map_key_clear(KEY_CAMERA);              break;
+       case 0x1014: lg_map_key_clear(KEY_MESSENGER);           break;
+       case 0x1015: lg_map_key_clear(KEY_RECORD);              break;
+       case 0x1016: lg_map_key_clear(KEY_PLAYER);              break;
+       case 0x1017: lg_map_key_clear(KEY_EJECTCD);             break;
+       case 0x1018: lg_map_key_clear(KEY_MEDIA);               break;
+       case 0x1019: lg_map_key_clear(KEY_PROG1);               break;
+       case 0x101a: lg_map_key_clear(KEY_PROG2);               break;
+       case 0x101b: lg_map_key_clear(KEY_PROG3);               break;
+       case 0x101f: lg_map_key_clear(KEY_ZOOMIN);              break;
+       case 0x1020: lg_map_key_clear(KEY_ZOOMOUT);             break;
+       case 0x1021: lg_map_key_clear(KEY_ZOOMRESET);           break;
+       case 0x1023: lg_map_key_clear(KEY_CLOSE);               break;
+       case 0x1027: lg_map_key_clear(KEY_MENU);                break;
+       /* this one is marked as 'Rotate' */
+       case 0x1028: lg_map_key_clear(KEY_ANGLE);               break;
+       case 0x1029: lg_map_key_clear(KEY_SHUFFLE);             break;
+       case 0x102a: lg_map_key_clear(KEY_BACK);                break;
+       case 0x102b: lg_map_key_clear(KEY_CYCLEWINDOWS);        break;
+       case 0x1041: lg_map_key_clear(KEY_BATTERY);             break;
+       case 0x1042: lg_map_key_clear(KEY_WORDPROCESSOR);       break;
+       case 0x1043: lg_map_key_clear(KEY_SPREADSHEET);         break;
+       case 0x1044: lg_map_key_clear(KEY_PRESENTATION);        break;
+       case 0x1045: lg_map_key_clear(KEY_UNDO);                break;
+       case 0x1046: lg_map_key_clear(KEY_REDO);                break;
+       case 0x1047: lg_map_key_clear(KEY_PRINT);               break;
+       case 0x1048: lg_map_key_clear(KEY_SAVE);                break;
+       case 0x1049: lg_map_key_clear(KEY_PROG1);               break;
+       case 0x104a: lg_map_key_clear(KEY_PROG2);               break;
+       case 0x104b: lg_map_key_clear(KEY_PROG3);               break;
+       case 0x104c: lg_map_key_clear(KEY_PROG4);               break;
+
+       default:
+               return 0;
+       }
+       return 1;
+}
+
+static int lg_input_mapping(struct hid_device *hdev, struct hid_input *hi,
+               struct hid_field *field, struct hid_usage *usage,
+               unsigned long **bit, int *max)
+{
+       /* extended mapping for certain Logitech hardware (Logitech cordless
+          desktop LX500) */
+       static const u8 e_keymap[] = {
+                 0,216,  0,213,175,156,  0,  0,  0,  0,
+               144,  0,  0,  0,  0,  0,  0,  0,  0,212,
+               174,167,152,161,112,  0,  0,  0,154,  0,
+                 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+                 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+                 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+                 0,  0,  0,  0,  0,183,184,185,186,187,
+               188,189,190,191,192,193,194,  0,  0,  0
+       };
+       unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);
+       unsigned int hid = usage->hid;
+
+       if (hdev->product == USB_DEVICE_ID_LOGITECH_RECEIVER &&
+                       lg_ultrax_remote_mapping(hi, usage, bit, max))
+               return 1;
+
+       if ((quirks & LG_WIRELESS) && lg_wireless_mapping(hi, usage, bit, max))
+               return 1;
+
+       if ((hid & HID_USAGE_PAGE) != HID_UP_BUTTON)
+               return 0;
+
+       hid &= HID_USAGE;
+
+       /* Special handling for Logitech Cordless Desktop */
+       if (field->application == HID_GD_MOUSE) {
+               if ((quirks & LG_IGNORE_DOUBLED_WHEEL) &&
+                               (hid == 7 || hid == 8))
+                       return -1;
+       } else {
+               if ((quirks & LG_EXPANDED_KEYMAP) &&
+                               hid < ARRAY_SIZE(e_keymap) &&
+                               e_keymap[hid] != 0) {
+                       hid_map_usage(hi, usage, bit, max, EV_KEY,
+                                       e_keymap[hid]);
+                       return 1;
+               }
+       }
+
+       return 0;
+}
+
+static int lg_input_mapped(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 ((quirks & LG_BAD_RELATIVE_KEYS) && usage->type == EV_KEY &&
+                       (field->flags & HID_MAIN_ITEM_RELATIVE))
+               field->flags &= ~HID_MAIN_ITEM_RELATIVE;
+
+       if ((quirks & LG_DUPLICATE_USAGES) && (usage->type == EV_KEY ||
+                        usage->type == EV_REL || usage->type == EV_ABS))
+               clear_bit(usage->code, *bit);
+
+       return 0;
+}
+
+static int lg_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 ((quirks & LG_INVERT_HWHEEL) && usage->code == REL_HWHEEL) {
+               input_event(field->hidinput->input, usage->type, usage->code,
+                               -value);
+               return 1;
+       }
+
+       return 0;
+}
+
+static int lg_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 & LG_RESET_LEDS)
+               hdev->quirks |= HID_QUIRK_RESET_LEDS;
+       if (quirks & LG_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 lg_devices[] = {
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER),
+               .driver_data = LG_RDESC | LG_WIRELESS },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER),
+               .driver_data = LG_RDESC | LG_WIRELESS },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER_2),
+               .driver_data = LG_RDESC | LG_WIRELESS },
+
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RECEIVER),
+               .driver_data = LG_BAD_RELATIVE_KEYS },
+
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_DESKTOP),
+               .driver_data = LG_DUPLICATE_USAGES },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_EDGE),
+               .driver_data = LG_DUPLICATE_USAGES },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_MINI),
+               .driver_data = LG_DUPLICATE_USAGES },
+
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_KBD),
+               .driver_data = LG_RESET_LEDS },
+
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_ELITE_KBD),
+               .driver_data = LG_IGNORE_DOUBLED_WHEEL | LG_EXPANDED_KEYMAP },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500),
+               .driver_data = LG_IGNORE_DOUBLED_WHEEL | LG_EXPANDED_KEYMAP },
+
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_LX3),
+               .driver_data = LG_INVERT_HWHEEL },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_V150),
+               .driver_data = LG_INVERT_HWHEEL },
+
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_EXTREME_3D),
+               .driver_data = LG_NOGET },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WHEEL),
+               .driver_data = LG_NOGET },
+       { }
+};
+MODULE_DEVICE_TABLE(hid, lg_devices);
+
+static struct hid_driver lg_driver = {
+       .name = "logitech",
+       .id_table = lg_devices,
+       .report_fixup = lg_report_fixup,
+       .input_mapping = lg_input_mapping,
+       .input_mapped = lg_input_mapped,
+       .event = lg_event,
+       .probe = lg_probe,
+};
+
+static int lg_init(void)
+{
+       return hid_register_driver(&lg_driver);
+}
+
+static void lg_exit(void)
+{
+       hid_unregister_driver(&lg_driver);
+}
+
+module_init(lg_init);
+module_exit(lg_exit);
+MODULE_LICENSE("GPL");
index 8b5ce9c9e353c86e92ef6d035d45b748a8c34a96..48fdaa5db739beb2e5e6bde766612216d5cca6f0 100644 (file)
@@ -33,8 +33,6 @@ static const struct hid_blacklist {
        { USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_X5_005D, HID_QUIRK_2WHEEL_MOUSE_HACK_B8 },
        { USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_MOUSE, HID_QUIRK_2WHEEL_MOUSE_HACK_5 },
 
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RECEIVER, HID_QUIRK_BAD_RELATIVE_KEYS },
-
        { USB_VENDOR_ID_AASHIMA, USB_DEVICE_ID_AASHIMA_GAMEPAD, HID_QUIRK_BADPAD },
        { USB_VENDOR_ID_AASHIMA, USB_DEVICE_ID_AASHIMA_PREDATOR, HID_QUIRK_BADPAD },
        { USB_VENDOR_ID_ALPS, USB_DEVICE_ID_IBM_GAMEPAD, HID_QUIRK_BADPAD },
@@ -48,10 +46,6 @@ static const struct hid_blacklist {
        { USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RUMBLEPAD, HID_QUIRK_BADPAD },
        { USB_VENDOR_ID_TOPMAX, USB_DEVICE_ID_TOPMAX_COBRAPAD, HID_QUIRK_BADPAD },
 
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_DESKTOP, HID_QUIRK_DUPLICATE_USAGES },
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_EDGE, HID_QUIRK_DUPLICATE_USAGES },
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_MINI, HID_QUIRK_DUPLICATE_USAGES },
-
        { USB_VENDOR_ID_AFATECH, USB_DEVICE_ID_AFATECH_AF9016, HID_QUIRK_FULLSPEED_INTERVAL },
 
        { USB_VENDOR_ID_BELKIN, USB_DEVICE_ID_FLIP_KVM, HID_QUIRK_HIDDEV },
@@ -196,11 +190,6 @@ static const struct hid_blacklist {
        { USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICKIT1, HID_QUIRK_IGNORE },
        { USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICKIT2, HID_QUIRK_IGNORE },
 
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_ELITE_KBD, HID_QUIRK_LOGITECH_IGNORE_DOUBLED_WHEEL | HID_QUIRK_LOGITECH_EXPANDED_KEYMAP },
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500, HID_QUIRK_LOGITECH_IGNORE_DOUBLED_WHEEL | HID_QUIRK_LOGITECH_EXPANDED_KEYMAP },
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_LX3, HID_QUIRK_INVERT_HWHEEL },
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_V150, HID_QUIRK_INVERT_HWHEEL },
-
        { 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 },
 
@@ -218,8 +207,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_LOGITECH, USB_DEVICE_ID_LOGITECH_EXTREME_3D, HID_QUIRK_NOGET },
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WHEEL, 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 },
@@ -259,7 +246,6 @@ static const struct hid_blacklist {
        { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE },
 
        { USB_VENDOR_ID_DELL, USB_DEVICE_ID_DELL_W7658, HID_QUIRK_RESET_LEDS },
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_KBD, HID_QUIRK_RESET_LEDS },
        { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY, HID_QUIRK_IGNORE },
        { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_2, HID_QUIRK_IGNORE },
        { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_3, HID_QUIRK_IGNORE },
@@ -339,9 +325,6 @@ static const struct hid_rdesc_blacklist {
 
        { USB_VENDOR_ID_CHERRY, USB_DEVICE_ID_CHERRY_CYMOTION, HID_QUIRK_RDESC_CYMOTION },
 
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER, HID_QUIRK_RDESC_LOGITECH },
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER, HID_QUIRK_RDESC_LOGITECH },
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER_2, HID_QUIRK_RDESC_LOGITECH },
        { 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 },
@@ -611,23 +594,6 @@ static void usbhid_fixup_cymotion_descriptor(char *rdesc, int rsize)
        }
 }
 
-
-/*
- * Certain Logitech keyboards send in report #3 keys which are far
- * above the logical maximum described in descriptor. This extends
- * the original value of 0x28c of logical maximum to 0x104d
- */
-static void usbhid_fixup_logitech_descriptor(unsigned char *rdesc, int rsize)
-{
-       if (rsize >= 90 && rdesc[83] == 0x26
-                       && rdesc[84] == 0x8c
-                       && rdesc[85] == 0x02) {
-               printk(KERN_INFO "Fixing up Logitech keyboard report descriptor\n");
-               rdesc[84] = rdesc[89] = 0x4d;
-               rdesc[85] = rdesc[90] = 0x10;
-       }
-}
-
 static void usbhid_fixup_sunplus_wdesktop(unsigned char *rdesc, int rsize)
 {
        if (rsize >= 107 && rdesc[104] == 0x26
@@ -753,9 +719,6 @@ static void __usbhid_fixup_report_descriptor(__u32 quirks, char *rdesc, unsigned
        if ((quirks & HID_QUIRK_RDESC_CYMOTION))
                usbhid_fixup_cymotion_descriptor(rdesc, rsize);
 
-       if (quirks & HID_QUIRK_RDESC_LOGITECH)
-               usbhid_fixup_logitech_descriptor(rdesc, rsize);
-
        if (quirks & HID_QUIRK_RDESC_SWAPPED_MIN_MAX)
                usbhid_fixup_cypress_descriptor(rdesc, rsize);
 
index 986c0e7ea66abc8c4ae3e2eb0d5c6eed092a4eaf..e9c4154ba3366b411bdfd2843b246dc34623c218 100644 (file)
@@ -270,15 +270,11 @@ struct hid_item {
 #define HID_QUIRK_APPLE_FN_ON                  0x00001000
 #define HID_QUIRK_INVERT_HWHEEL                        0x00002000
 #define HID_QUIRK_APPLE_ISO_KEYBOARD           0x00004000
-#define HID_QUIRK_BAD_RELATIVE_KEYS            0x00008000
 #define HID_QUIRK_SKIP_OUTPUT_REPORTS          0x00010000
 #define HID_QUIRK_IGNORE_MOUSE                 0x00020000
 #define HID_QUIRK_SONY_PS3_CONTROLLER          0x00040000
-#define HID_QUIRK_DUPLICATE_USAGES             0x00080000
 #define HID_QUIRK_RESET_LEDS                   0x00100000
 #define HID_QUIRK_HIDINPUT                     0x00200000
-#define HID_QUIRK_LOGITECH_IGNORE_DOUBLED_WHEEL        0x00400000
-#define HID_QUIRK_LOGITECH_EXPANDED_KEYMAP     0x00800000
 #define HID_QUIRK_IGNORE_HIDINPUT              0x01000000
 #define HID_QUIRK_2WHEEL_MOUSE_HACK_B8         0x02000000
 #define HID_QUIRK_HWHEEL_WHEEL_INVERT          0x04000000
@@ -291,7 +287,6 @@ struct hid_item {
  */
 
 #define HID_QUIRK_RDESC_CYMOTION               0x00000001
-#define HID_QUIRK_RDESC_LOGITECH               0x00000002
 #define HID_QUIRK_RDESC_SWAPPED_MIN_MAX                0x00000004
 #define HID_QUIRK_RDESC_PETALYNX               0x00000008
 #define HID_QUIRK_RDESC_MACBOOK_JIS            0x00000010