HID: move logitech FF processing
authorJiri Slaby <jirislaby@gmail.com>
Fri, 4 Jul 2008 21:06:45 +0000 (23:06 +0200)
committerJiri Kosina <jkosina@suse.cz>
Tue, 14 Oct 2008 21:50:56 +0000 (23:50 +0200)
Merge the logitech force feedback processing directly into logitech
driver from the usbhid core.

Signed-off-by: Jiri Slaby <jirislaby@gmail.com>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
16 files changed:
drivers/hid/Kconfig
drivers/hid/Makefile
drivers/hid/hid-core.c
drivers/hid/hid-ids.h
drivers/hid/hid-lg.c [new file with mode: 0644]
drivers/hid/hid-lg.h [new file with mode: 0644]
drivers/hid/hid-lg2ff.c [new file with mode: 0644]
drivers/hid/hid-lgff.c [new file with mode: 0644]
drivers/hid/hid-logitech.c [deleted file]
drivers/hid/usbhid/Kconfig
drivers/hid/usbhid/Makefile
drivers/hid/usbhid/hid-core.c
drivers/hid/usbhid/hid-ff.c
drivers/hid/usbhid/hid-lg2ff.c [deleted file]
drivers/hid/usbhid/hid-lgff.c [deleted file]
include/linux/hid.h

index 1ab067ee7e6c9161a180941ba5eeb28c16c9ad55..4319af320adf1760a74fa85e7c76c697e6be82a7 100644 (file)
@@ -160,6 +160,30 @@ config HID_LOGITECH
        Support for some Logitech devices which breaks less or more
        HID specification.
 
+config LOGITECH_FF
+       bool "Logitech force feedback"
+       depends on HID_LOGITECH
+       select INPUT_FF_MEMLESS
+       help
+         Say Y here if you have one of these devices:
+         - Logitech WingMan Cordless RumblePad
+         - Logitech WingMan Cordless RumblePad 2
+         - Logitech WingMan Force 3D
+         - Logitech Formula Force EX
+         - Logitech MOMO Force wheel
+
+         and if you want to enable force feedback for them.
+         Note: if you say N here, this device will still be supported, but without
+         force feedback.
+
+config LOGIRUMBLEPAD2_FF
+       bool "Logitech Rumblepad 2 force feedback"
+       depends on HID_LOGITECH
+       select INPUT_FF_MEMLESS
+       help
+         Say Y here if you want to enable force feedback support for Logitech
+         Rumblepad 2 devices.
+
 config HID_MICROSOFT
        tristate "Microsoft"
        default m
index 4a756c6e3a95e7bfb2836d31dfe280bf3e5b5e2d..300ee00913bc17dd369f7b03b1321be6ca21bb5f 100644 (file)
@@ -12,6 +12,14 @@ ifdef CONFIG_HID_COMPAT
 obj-m                          += hid-dummy.o
 endif
 
+hid-logitech-objs              := hid-lg.o
+ifdef CONFIG_LOGITECH_FF
+       hid-logitech-objs       += hid-lgff.o
+endif
+ifdef CONFIG_LOGIRUMBLEPAD2_FF
+       hid-logitech-objs       += hid-lg2ff.o
+endif
+
 obj-$(CONFIG_HID_A4TECH)       += hid-a4tech.o
 obj-$(CONFIG_HID_APPLE)                += hid-apple.o
 obj-$(CONFIG_HID_BELKIN)       += hid-belkin.o
index 699547ce257f4d23d9ee04397b6b2af5ff4f4090..6a730df336b4563047aaf9e6e14e02815ad226d0 100644 (file)
@@ -1262,6 +1262,13 @@ 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_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2_2) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_F3D) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FORCE3D_PRO) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2) },
        { 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) },
index 678f7c3f341dea52ed52afcf337f16a0c9cacc0b..e2fd52ca68b77e071ddb2a144729d5109c622bfc 100644 (file)
 #define USB_DEVICE_ID_LOGITECH_RECEIVER        0xc101
 #define USB_DEVICE_ID_LOGITECH_HARMONY_FIRST  0xc110
 #define USB_DEVICE_ID_LOGITECH_HARMONY_LAST 0xc14f
+#define USB_DEVICE_ID_LOGITECH_RUMBLEPAD       0xc211
 #define USB_DEVICE_ID_LOGITECH_EXTREME_3D      0xc215
+#define USB_DEVICE_ID_LOGITECH_RUMBLEPAD2      0xc218
+#define USB_DEVICE_ID_LOGITECH_RUMBLEPAD2_2    0xc219
+#define USB_DEVICE_ID_LOGITECH_WINGMAN_F3D     0xc283
+#define USB_DEVICE_ID_LOGITECH_FORCE3D_PRO     0xc286
 #define USB_DEVICE_ID_LOGITECH_WHEEL   0xc294
+#define USB_DEVICE_ID_LOGITECH_MOMO_WHEEL      0xc295
 #define USB_DEVICE_ID_LOGITECH_ELITE_KBD       0xc30a
 #define USB_DEVICE_ID_LOGITECH_KBD     0xc311
 #define USB_DEVICE_ID_S510_RECEIVER    0xc50c
 #define USB_DEVICE_ID_DINOVO_DESKTOP   0xc704
 #define USB_DEVICE_ID_DINOVO_EDGE      0xc714
 #define USB_DEVICE_ID_DINOVO_MINI      0xc71f
+#define USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2     0xca03
 
 #define USB_VENDOR_ID_MCC              0x09db
 #define USB_DEVICE_ID_MCC_PMD1024LS    0x0076
diff --git a/drivers/hid/hid-lg.c b/drivers/hid/hid-lg.c
new file mode 100644 (file)
index 0000000..406d8c8
--- /dev/null
@@ -0,0 +1,342 @@
+/*
+ *  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"
+#include "hid-lg.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
+#define LG_FF                  0x200
+#define LG_FF2                 0x400
+
+/*
+ * 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;
+       unsigned int connect_mask = HID_CONNECT_DEFAULT;
+       int ret;
+
+       hid_set_drvdata(hdev, (void *)quirks);
+
+       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;
+       }
+
+       if (quirks & (LG_FF | LG_FF2))
+               connect_mask &= ~HID_CONNECT_FF;
+
+       ret = hid_hw_start(hdev, connect_mask);
+       if (ret) {
+               dev_err(&hdev->dev, "hw start failed\n");
+               goto err_free;
+       }
+
+       if (quirks & LG_RESET_LEDS)
+               usbhid_set_leds(hdev);
+
+       if (quirks & LG_FF)
+               lgff_init(hdev);
+       if (quirks & LG_FF2)
+               lg2ff_init(hdev);
+
+       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 | LG_FF },
+
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD),
+               .driver_data = LG_FF },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2_2),
+               .driver_data = LG_FF },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_F3D),
+               .driver_data = LG_FF },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FORCE3D_PRO),
+               .driver_data = LG_FF },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL),
+               .driver_data = LG_FF },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2),
+               .driver_data = LG_FF },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2),
+               .driver_data = LG_FF2 },
+       { }
+};
+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");
+
+HID_COMPAT_LOAD_DRIVER(logitech);
diff --git a/drivers/hid/hid-lg.h b/drivers/hid/hid-lg.h
new file mode 100644 (file)
index 0000000..27ae750
--- /dev/null
@@ -0,0 +1,18 @@
+#ifndef __HID_LG_H
+#define __HID_LG_H
+
+#include <linux/autoconf.h>
+
+#ifdef CONFIG_LOGITECH_FF
+int lgff_init(struct hid_device *hdev);
+#else
+static inline int lgff_init(struct hid_device *hdev) { return -1; }
+#endif
+
+#ifdef CONFIG_LOGIRUMBLEPAD2_FF
+int lg2ff_init(struct hid_device *hdev);
+#else
+static inline int lg2ff_init(struct hid_device *hdev) { return -1; }
+#endif
+
+#endif
diff --git a/drivers/hid/hid-lg2ff.c b/drivers/hid/hid-lg2ff.c
new file mode 100644 (file)
index 0000000..b2e9a67
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ *  Force feedback support for Logitech Rumblepad 2
+ *
+ *  Copyright (c) 2008 Anssi Hannula <anssi.hannula@gmail.com>
+ */
+
+/*
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#include <linux/input.h>
+#include <linux/usb.h>
+#include <linux/hid.h>
+
+#include "usbhid/usbhid.h"
+#include "hid-lg.h"
+
+struct lg2ff_device {
+       struct hid_report *report;
+};
+
+static int play_effect(struct input_dev *dev, void *data,
+                        struct ff_effect *effect)
+{
+       struct hid_device *hid = input_get_drvdata(dev);
+       struct lg2ff_device *lg2ff = data;
+       int weak, strong;
+
+       strong = effect->u.rumble.strong_magnitude;
+       weak = effect->u.rumble.weak_magnitude;
+
+       if (weak || strong) {
+               weak = weak * 0xff / 0xffff;
+               strong = strong * 0xff / 0xffff;
+
+               lg2ff->report->field[0]->value[0] = 0x51;
+               lg2ff->report->field[0]->value[2] = weak;
+               lg2ff->report->field[0]->value[4] = strong;
+       } else {
+               lg2ff->report->field[0]->value[0] = 0xf3;
+               lg2ff->report->field[0]->value[2] = 0x00;
+               lg2ff->report->field[0]->value[4] = 0x00;
+       }
+
+       usbhid_submit_report(hid, lg2ff->report, USB_DIR_OUT);
+       return 0;
+}
+
+int lg2ff_init(struct hid_device *hid)
+{
+       struct lg2ff_device *lg2ff;
+       struct hid_report *report;
+       struct hid_input *hidinput = list_entry(hid->inputs.next,
+                                               struct hid_input, list);
+       struct list_head *report_list =
+                       &hid->report_enum[HID_OUTPUT_REPORT].report_list;
+       struct input_dev *dev = hidinput->input;
+       int error;
+
+       if (list_empty(report_list)) {
+               printk(KERN_ERR "hid-lg2ff: no output report found\n");
+               return -ENODEV;
+       }
+
+       report = list_entry(report_list->next, struct hid_report, list);
+
+       if (report->maxfield < 1) {
+               printk(KERN_ERR "hid-lg2ff: output report is empty\n");
+               return -ENODEV;
+       }
+       if (report->field[0]->report_count < 7) {
+               printk(KERN_ERR "hid-lg2ff: not enough values in the field\n");
+               return -ENODEV;
+       }
+
+       lg2ff = kmalloc(sizeof(struct lg2ff_device), GFP_KERNEL);
+       if (!lg2ff)
+               return -ENOMEM;
+
+       set_bit(FF_RUMBLE, dev->ffbit);
+
+       error = input_ff_create_memless(dev, lg2ff, play_effect);
+       if (error) {
+               kfree(lg2ff);
+               return error;
+       }
+
+       lg2ff->report = report;
+       report->field[0]->value[0] = 0xf3;
+       report->field[0]->value[1] = 0x00;
+       report->field[0]->value[2] = 0x00;
+       report->field[0]->value[3] = 0x00;
+       report->field[0]->value[4] = 0x00;
+       report->field[0]->value[5] = 0x00;
+       report->field[0]->value[6] = 0x00;
+
+       usbhid_submit_report(hid, report, USB_DIR_OUT);
+
+       printk(KERN_INFO "Force feedback for Logitech Rumblepad 2 by "
+              "Anssi Hannula <anssi.hannula@gmail.com>\n");
+
+       return 0;
+}
diff --git a/drivers/hid/hid-lgff.c b/drivers/hid/hid-lgff.c
new file mode 100644 (file)
index 0000000..e379c16
--- /dev/null
@@ -0,0 +1,153 @@
+/*
+ * Force feedback support for hid-compliant for some of the devices from
+ * Logitech, namely:
+ * - WingMan Cordless RumblePad
+ * - WingMan Force 3D
+ *
+ *  Copyright (c) 2002-2004 Johann Deneux
+ *  Copyright (c) 2006 Anssi Hannula <anssi.hannula@gmail.com>
+ */
+
+/*
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so by
+ * e-mail - mail your message to <johann.deneux@it.uu.se>
+ */
+
+#include <linux/input.h>
+#include <linux/usb.h>
+#include <linux/hid.h>
+
+#include "usbhid/usbhid.h"
+#include "hid-lg.h"
+
+struct dev_type {
+       u16 idVendor;
+       u16 idProduct;
+       const signed short *ff;
+};
+
+static const signed short ff_rumble[] = {
+       FF_RUMBLE,
+       -1
+};
+
+static const signed short ff_joystick[] = {
+       FF_CONSTANT,
+       -1
+};
+
+static const struct dev_type devices[] = {
+       { 0x046d, 0xc211, ff_rumble },
+       { 0x046d, 0xc219, ff_rumble },
+       { 0x046d, 0xc283, ff_joystick },
+       { 0x046d, 0xc286, ff_joystick },
+       { 0x046d, 0xc294, ff_joystick },
+       { 0x046d, 0xc295, ff_joystick },
+       { 0x046d, 0xca03, ff_joystick },
+};
+
+static int hid_lgff_play(struct input_dev *dev, void *data, struct ff_effect *effect)
+{
+       struct hid_device *hid = input_get_drvdata(dev);
+       struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
+       struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
+       int x, y;
+       unsigned int left, right;
+
+#define CLAMP(x) if (x < 0) x = 0; if (x > 0xff) x = 0xff
+
+       switch (effect->type) {
+       case FF_CONSTANT:
+               x = effect->u.ramp.start_level + 0x7f;  /* 0x7f is center */
+               y = effect->u.ramp.end_level + 0x7f;
+               CLAMP(x);
+               CLAMP(y);
+               report->field[0]->value[0] = 0x51;
+               report->field[0]->value[1] = 0x08;
+               report->field[0]->value[2] = x;
+               report->field[0]->value[3] = y;
+               dbg_hid("(x, y)=(%04x, %04x)\n", x, y);
+               usbhid_submit_report(hid, report, USB_DIR_OUT);
+               break;
+
+       case FF_RUMBLE:
+               right = effect->u.rumble.strong_magnitude;
+               left = effect->u.rumble.weak_magnitude;
+               right = right * 0xff / 0xffff;
+               left = left * 0xff / 0xffff;
+               CLAMP(left);
+               CLAMP(right);
+               report->field[0]->value[0] = 0x42;
+               report->field[0]->value[1] = 0x00;
+               report->field[0]->value[2] = left;
+               report->field[0]->value[3] = right;
+               dbg_hid("(left, right)=(%04x, %04x)\n", left, right);
+               usbhid_submit_report(hid, report, USB_DIR_OUT);
+               break;
+       }
+       return 0;
+}
+
+int lgff_init(struct hid_device* hid)
+{
+       struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list);
+       struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
+       struct input_dev *dev = hidinput->input;
+       struct hid_report *report;
+       struct hid_field *field;
+       const signed short *ff_bits = ff_joystick;
+       int error;
+       int i;
+
+       /* Find the report to use */
+       if (list_empty(report_list)) {
+               err_hid("No output report found");
+               return -1;
+       }
+
+       /* Check that the report looks ok */
+       report = list_entry(report_list->next, struct hid_report, list);
+       if (!report) {
+               err_hid("NULL output report");
+               return -1;
+       }
+
+       field = report->field[0];
+       if (!field) {
+               err_hid("NULL field");
+               return -1;
+       }
+
+       for (i = 0; i < ARRAY_SIZE(devices); i++) {
+               if (dev->id.vendor == devices[i].idVendor &&
+                   dev->id.product == devices[i].idProduct) {
+                       ff_bits = devices[i].ff;
+                       break;
+               }
+       }
+
+       for (i = 0; ff_bits[i] >= 0; i++)
+               set_bit(ff_bits[i], dev->ffbit);
+
+       error = input_ff_create_memless(dev, NULL, hid_lgff_play);
+       if (error)
+               return error;
+
+       printk(KERN_INFO "Force feedback for Logitech force feedback devices by Johann Deneux <johann.deneux@it.uu.se>\n");
+
+       return 0;
+}
diff --git a/drivers/hid/hid-logitech.c b/drivers/hid/hid-logitech.c
deleted file mode 100644 (file)
index df27f9a..0000000
+++ /dev/null
@@ -1,315 +0,0 @@
-/*
- *  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_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, HID_CONNECT_DEFAULT);
-       if (ret) {
-               dev_err(&hdev->dev, "hw start failed\n");
-               goto err_free;
-       }
-
-       if (quirks & LG_RESET_LEDS)
-               usbhid_set_leds(hdev);
-
-       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");
-
-HID_COMPAT_LOAD_DRIVER(logitech);
index 177bfa1a3e5b5a81da70106d623db04b04863f09..0e7e2e6d975efe73ca8fe741e353b5d6ec29843d 100644 (file)
@@ -44,30 +44,6 @@ config HID_PID
          feedback for it. Microsoft Sidewinder Force Feedback 2 is one of such
          devices.
 
-config LOGITECH_FF
-       bool "Logitech devices support"
-       depends on HID_FF
-       select INPUT_FF_MEMLESS if USB_HID
-       help
-         Say Y here if you have one of these devices:
-         - Logitech WingMan Cordless RumblePad
-         - Logitech WingMan Cordless RumblePad 2
-         - Logitech WingMan Force 3D
-         - Logitech Formula Force EX
-         - Logitech MOMO Force wheel
-
-         and if you want to enable force feedback for them.
-         Note: if you say N here, this device will still be supported, but without
-         force feedback.
-
-config LOGIRUMBLEPAD2_FF
-       bool "Logitech Rumblepad 2 support"
-       depends on HID_FF
-       select INPUT_FF_MEMLESS if USB_HID
-       help
-         Say Y here if you want to enable force feedback support for Logitech
-         Rumblepad 2 devices.
-
 config PANTHERLORD_FF
        bool "PantherLord/GreenAsia based device support"
        depends on HID_FF
index 00a7b70901925276e63237b023580c94ec21cec8..224c62dc6a32725cabca4ab0f62c9539d87a37f6 100644 (file)
@@ -13,12 +13,6 @@ endif
 ifeq ($(CONFIG_HID_PID),y)
        usbhid-objs     += hid-pidff.o
 endif
-ifeq ($(CONFIG_LOGITECH_FF),y)
-       usbhid-objs     += hid-lgff.o
-endif
-ifeq ($(CONFIG_LOGIRUMBLEPAD2_FF),y)
-       usbhid-objs     += hid-lg2ff.o
-endif
 ifeq ($(CONFIG_PANTHERLORD_FF),y)
        usbhid-objs     += hid-plff.o
 endif
index 402ace7512718028ab19ea0f3f7434b86bface59..4ec10aa618dbebc9cfadfd6fbd5da32750b27f96 100644 (file)
@@ -444,6 +444,7 @@ void usbhid_submit_report(struct hid_device *hid, struct hid_report *report, uns
 
        spin_unlock_irqrestore(&usbhid->ctrllock, flags);
 }
+EXPORT_SYMBOL_GPL(usbhid_submit_report);
 
 static int usb_hidinput_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
 {
index 1d0dac52f1664607afe7130f348e7ba5d3cfa23e..8891f60d3beb3defd609e62946be6e07c93f1d8c 100644 (file)
@@ -50,18 +50,6 @@ struct hid_ff_initializer {
  * be a PID device
  */
 static struct hid_ff_initializer inits[] = {
-#ifdef CONFIG_LOGITECH_FF
-       { 0x46d, 0xc211, hid_lgff_init }, /* Logitech Cordless rumble pad */
-       { 0x46d, 0xc219, hid_lgff_init }, /* Logitech Cordless rumble pad 2 */
-       { 0x46d, 0xc283, hid_lgff_init }, /* Logitech Wingman Force 3d */
-       { 0x46d, 0xc286, hid_lgff_init }, /* Logitech Force 3D Pro Joystick */
-       { 0x46d, 0xc294, hid_lgff_init }, /* Logitech Formula Force EX */
-       { 0x46d, 0xc295, hid_lgff_init }, /* Logitech MOMO force wheel */
-       { 0x46d, 0xca03, hid_lgff_init }, /* Logitech MOMO force wheel */
-#endif
-#ifdef CONFIG_LOGIRUMBLEPAD2_FF
-       { 0x46d, 0xc218, hid_lg2ff_init }, /* Logitech Rumblepad 2 */
-#endif
 #ifdef CONFIG_PANTHERLORD_FF
        { 0x810, 0x0001, hid_plff_init }, /* "Twin USB Joystick" */
        { 0xe8f, 0x0003, hid_plff_init }, /* "GreenAsia Inc.    USB Joystick     " */
diff --git a/drivers/hid/usbhid/hid-lg2ff.c b/drivers/hid/usbhid/hid-lg2ff.c
deleted file mode 100644 (file)
index d469bd0..0000000
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- *  Force feedback support for Logitech Rumblepad 2
- *
- *  Copyright (c) 2008 Anssi Hannula <anssi.hannula@gmail.com>
- */
-
-/*
- * 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.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-
-#include <linux/input.h>
-#include <linux/usb.h>
-#include <linux/hid.h>
-#include "usbhid.h"
-
-struct lg2ff_device {
-       struct hid_report *report;
-};
-
-static int play_effect(struct input_dev *dev, void *data,
-                        struct ff_effect *effect)
-{
-       struct hid_device *hid = input_get_drvdata(dev);
-       struct lg2ff_device *lg2ff = data;
-       int weak, strong;
-
-       strong = effect->u.rumble.strong_magnitude;
-       weak = effect->u.rumble.weak_magnitude;
-
-       if (weak || strong) {
-               weak = weak * 0xff / 0xffff;
-               strong = strong * 0xff / 0xffff;
-
-               lg2ff->report->field[0]->value[0] = 0x51;
-               lg2ff->report->field[0]->value[2] = weak;
-               lg2ff->report->field[0]->value[4] = strong;
-       } else {
-               lg2ff->report->field[0]->value[0] = 0xf3;
-               lg2ff->report->field[0]->value[2] = 0x00;
-               lg2ff->report->field[0]->value[4] = 0x00;
-       }
-
-       usbhid_submit_report(hid, lg2ff->report, USB_DIR_OUT);
-       return 0;
-}
-
-int hid_lg2ff_init(struct hid_device *hid)
-{
-       struct lg2ff_device *lg2ff;
-       struct hid_report *report;
-       struct hid_input *hidinput = list_entry(hid->inputs.next,
-                                               struct hid_input, list);
-       struct list_head *report_list =
-                       &hid->report_enum[HID_OUTPUT_REPORT].report_list;
-       struct input_dev *dev = hidinput->input;
-       int error;
-
-       if (list_empty(report_list)) {
-               printk(KERN_ERR "hid-lg2ff: no output report found\n");
-               return -ENODEV;
-       }
-
-       report = list_entry(report_list->next, struct hid_report, list);
-
-       if (report->maxfield < 1) {
-               printk(KERN_ERR "hid-lg2ff: output report is empty\n");
-               return -ENODEV;
-       }
-       if (report->field[0]->report_count < 7) {
-               printk(KERN_ERR "hid-lg2ff: not enough values in the field\n");
-               return -ENODEV;
-       }
-
-       lg2ff = kmalloc(sizeof(struct lg2ff_device), GFP_KERNEL);
-       if (!lg2ff)
-               return -ENOMEM;
-
-       set_bit(FF_RUMBLE, dev->ffbit);
-
-       error = input_ff_create_memless(dev, lg2ff, play_effect);
-       if (error) {
-               kfree(lg2ff);
-               return error;
-       }
-
-       lg2ff->report = report;
-       report->field[0]->value[0] = 0xf3;
-       report->field[0]->value[1] = 0x00;
-       report->field[0]->value[2] = 0x00;
-       report->field[0]->value[3] = 0x00;
-       report->field[0]->value[4] = 0x00;
-       report->field[0]->value[5] = 0x00;
-       report->field[0]->value[6] = 0x00;
-
-       usbhid_submit_report(hid, report, USB_DIR_OUT);
-
-       printk(KERN_INFO "Force feedback for Logitech Rumblepad 2 by "
-              "Anssi Hannula <anssi.hannula@gmail.com>\n");
-
-       return 0;
-}
diff --git a/drivers/hid/usbhid/hid-lgff.c b/drivers/hid/usbhid/hid-lgff.c
deleted file mode 100644 (file)
index 4b7ab6a..0000000
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * Force feedback support for hid-compliant for some of the devices from
- * Logitech, namely:
- * - WingMan Cordless RumblePad
- * - WingMan Force 3D
- *
- *  Copyright (c) 2002-2004 Johann Deneux
- *  Copyright (c) 2006 Anssi Hannula <anssi.hannula@gmail.com>
- */
-
-/*
- * 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.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- * Should you need to contact me, the author, you can do so by
- * e-mail - mail your message to <johann.deneux@it.uu.se>
- */
-
-#include <linux/input.h>
-#include <linux/usb.h>
-#include <linux/hid.h>
-#include "usbhid.h"
-
-struct dev_type {
-       u16 idVendor;
-       u16 idProduct;
-       const signed short *ff;
-};
-
-static const signed short ff_rumble[] = {
-       FF_RUMBLE,
-       -1
-};
-
-static const signed short ff_joystick[] = {
-       FF_CONSTANT,
-       -1
-};
-
-static const struct dev_type devices[] = {
-       { 0x046d, 0xc211, ff_rumble },
-       { 0x046d, 0xc219, ff_rumble },
-       { 0x046d, 0xc283, ff_joystick },
-       { 0x046d, 0xc286, ff_joystick },
-       { 0x046d, 0xc294, ff_joystick },
-       { 0x046d, 0xc295, ff_joystick },
-       { 0x046d, 0xca03, ff_joystick },
-};
-
-static int hid_lgff_play(struct input_dev *dev, void *data, struct ff_effect *effect)
-{
-       struct hid_device *hid = input_get_drvdata(dev);
-       struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
-       struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
-       int x, y;
-       unsigned int left, right;
-
-#define CLAMP(x) if (x < 0) x = 0; if (x > 0xff) x = 0xff
-
-       switch (effect->type) {
-       case FF_CONSTANT:
-               x = effect->u.ramp.start_level + 0x7f;  /* 0x7f is center */
-               y = effect->u.ramp.end_level + 0x7f;
-               CLAMP(x);
-               CLAMP(y);
-               report->field[0]->value[0] = 0x51;
-               report->field[0]->value[1] = 0x08;
-               report->field[0]->value[2] = x;
-               report->field[0]->value[3] = y;
-               dbg_hid("(x, y)=(%04x, %04x)\n", x, y);
-               usbhid_submit_report(hid, report, USB_DIR_OUT);
-               break;
-
-       case FF_RUMBLE:
-               right = effect->u.rumble.strong_magnitude;
-               left = effect->u.rumble.weak_magnitude;
-               right = right * 0xff / 0xffff;
-               left = left * 0xff / 0xffff;
-               CLAMP(left);
-               CLAMP(right);
-               report->field[0]->value[0] = 0x42;
-               report->field[0]->value[1] = 0x00;
-               report->field[0]->value[2] = left;
-               report->field[0]->value[3] = right;
-               dbg_hid("(left, right)=(%04x, %04x)\n", left, right);
-               usbhid_submit_report(hid, report, USB_DIR_OUT);
-               break;
-       }
-       return 0;
-}
-
-int hid_lgff_init(struct hid_device* hid)
-{
-       struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list);
-       struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
-       struct input_dev *dev = hidinput->input;
-       struct hid_report *report;
-       struct hid_field *field;
-       const signed short *ff_bits = ff_joystick;
-       int error;
-       int i;
-
-       /* Find the report to use */
-       if (list_empty(report_list)) {
-               err_hid("No output report found");
-               return -1;
-       }
-
-       /* Check that the report looks ok */
-       report = list_entry(report_list->next, struct hid_report, list);
-       if (!report) {
-               err_hid("NULL output report");
-               return -1;
-       }
-
-       field = report->field[0];
-       if (!field) {
-               err_hid("NULL field");
-               return -1;
-       }
-
-       for (i = 0; i < ARRAY_SIZE(devices); i++) {
-               if (dev->id.vendor == devices[i].idVendor &&
-                   dev->id.product == devices[i].idProduct) {
-                       ff_bits = devices[i].ff;
-                       break;
-               }
-       }
-
-       for (i = 0; ff_bits[i] >= 0; i++)
-               set_bit(ff_bits[i], dev->ffbit);
-
-       error = input_ff_create_memless(dev, NULL, hid_lgff_play);
-       if (error)
-               return error;
-
-       printk(KERN_INFO "Force feedback for Logitech force feedback devices by Johann Deneux <johann.deneux@it.uu.se>\n");
-
-       return 0;
-}
index b0f03fa2ed1925fe3ba71b74df666fc2e9ba880d..15ee33e0463e5cf7a8fbf7114f2c4534a57c5ac9 100644 (file)
@@ -760,8 +760,6 @@ void usbhid_set_leds(struct hid_device *hid);
 #ifdef CONFIG_HID_FF
 int hid_ff_init(struct hid_device *hid);
 
-int hid_lgff_init(struct hid_device *hid);
-int hid_lg2ff_init(struct hid_device *hid);
 int hid_plff_init(struct hid_device *hid);
 int hid_tmff_init(struct hid_device *hid);
 int hid_zpff_init(struct hid_device *hid);