aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/hid
diff options
context:
space:
mode:
authorSimon Wood <simon@mungewell.org>2010-09-22 07:19:42 -0400
committerJiri Kosina <jkosina@suse.cz>2010-09-22 07:25:39 -0400
commit32c88cbc3080f43c429f6456aa9cd845e37f3778 (patch)
tree18886cea811ef68ce20aea0fa975d8a3ca2b1589 /drivers/hid
parentfe2c91ee245bd81230f1d67645237a684b90be2b (diff)
HID: Add support for Logitech Speed Force Wireless gaming wheel
The following patch adds support for the Logitech Speed Force Wireless gaming wheel. Originally designed for the WII console. Details on the protocol: http://wiibrew.org/wiki/Logitech_USB_steering_wheel This patch relies on previous patch: "Don't Send Feature Reports on Interrupt Endpoint" Logitech as produce a very similar wheel for the PS2/PS3, it is expected that this patch could also support the PS2/PS3 wheel if the USB ID's are added and (if required) the HID descriptor is modified. Signed-off-by: Simon Wood <simon@mungewell.org> Signed-off-by: Jiri Kosina <jkosina@suse.cz>
Diffstat (limited to 'drivers/hid')
-rw-r--r--drivers/hid/Kconfig8
-rw-r--r--drivers/hid/Makefile3
-rw-r--r--drivers/hid/hid-core.c1
-rw-r--r--drivers/hid/hid-ids.h1
-rw-r--r--drivers/hid/hid-lg.c38
-rw-r--r--drivers/hid/hid-lg.h6
-rw-r--r--drivers/hid/hid-lg4ff.c136
7 files changed, 193 insertions, 0 deletions
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index 6369ba7f96f8..6664c573cf33 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -235,6 +235,14 @@ config LOGIG940_FF
235 Say Y here if you want to enable force feedback support for Logitech 235 Say Y here if you want to enable force feedback support for Logitech
236 Flight System G940 devices. 236 Flight System G940 devices.
237 237
238config LOGIWII_FF
239 bool "Logitech Speed Force Wireless force feedback support"
240 depends on HID_LOGITECH
241 select INPUT_FF_MEMLESS
242 help
243 Say Y here if you want to enable force feedback support for Logitech
244 Speed Force Wireless (Wii) devices.
245
238config HID_MAGICMOUSE 246config HID_MAGICMOUSE
239 tristate "Apple MagicMouse multi-touch support" 247 tristate "Apple MagicMouse multi-touch support"
240 depends on BT_HIDP 248 depends on BT_HIDP
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index 46f037f3df80..d46a455eefc1 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -21,6 +21,9 @@ endif
21ifdef CONFIG_LOGIG940_FF 21ifdef CONFIG_LOGIG940_FF
22 hid-logitech-objs += hid-lg3ff.o 22 hid-logitech-objs += hid-lg3ff.o
23endif 23endif
24ifdef CONFIG_LOGIWII_FF
25 hid-logitech-objs += hid-lg4ff.o
26endif
24 27
25obj-$(CONFIG_HID_3M_PCT) += hid-3m-pct.o 28obj-$(CONFIG_HID_3M_PCT) += hid-3m-pct.o
26obj-$(CONFIG_HID_A4TECH) += hid-a4tech.o 29obj-$(CONFIG_HID_A4TECH) += hid-a4tech.o
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index 3f7292486024..19d45473f24f 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -1335,6 +1335,7 @@ static const struct hid_device_id hid_blacklist[] = {
1335 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL) }, 1335 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL) },
1336 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2) }, 1336 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2) },
1337 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G25_WHEEL) }, 1337 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G25_WHEEL) },
1338 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WII_WHEEL) },
1338 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2) }, 1339 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2) },
1339 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACETRAVELLER) }, 1340 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACETRAVELLER) },
1340 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACENAVIGATOR) }, 1341 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACENAVIGATOR) },
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 765a4f53eb5c..6b111e1e2df1 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -350,6 +350,7 @@
350#define USB_DEVICE_ID_LOGITECH_WINGMAN_FFG 0xc293 350#define USB_DEVICE_ID_LOGITECH_WINGMAN_FFG 0xc293
351#define USB_DEVICE_ID_LOGITECH_MOMO_WHEEL 0xc295 351#define USB_DEVICE_ID_LOGITECH_MOMO_WHEEL 0xc295
352#define USB_DEVICE_ID_LOGITECH_G25_WHEEL 0xc299 352#define USB_DEVICE_ID_LOGITECH_G25_WHEEL 0xc299
353#define USB_DEVICE_ID_LOGITECH_WII_WHEEL 0xc29c
353#define USB_DEVICE_ID_LOGITECH_ELITE_KBD 0xc30a 354#define USB_DEVICE_ID_LOGITECH_ELITE_KBD 0xc30a
354#define USB_DEVICE_ID_S510_RECEIVER 0xc50c 355#define USB_DEVICE_ID_S510_RECEIVER 0xc50c
355#define USB_DEVICE_ID_S510_RECEIVER_2 0xc517 356#define USB_DEVICE_ID_S510_RECEIVER_2 0xc517
diff --git a/drivers/hid/hid-lg.c b/drivers/hid/hid-lg.c
index f6433d8050a9..8989f151e0d7 100644
--- a/drivers/hid/hid-lg.c
+++ b/drivers/hid/hid-lg.c
@@ -19,6 +19,9 @@
19#include <linux/device.h> 19#include <linux/device.h>
20#include <linux/hid.h> 20#include <linux/hid.h>
21#include <linux/module.h> 21#include <linux/module.h>
22#include <linux/random.h>
23#include <linux/sched.h>
24#include <linux/wait.h>
22 25
23#include "hid-ids.h" 26#include "hid-ids.h"
24#include "hid-lg.h" 27#include "hid-lg.h"
@@ -35,6 +38,7 @@
35#define LG_FF2 0x400 38#define LG_FF2 0x400
36#define LG_RDESC_REL_ABS 0x800 39#define LG_RDESC_REL_ABS 0x800
37#define LG_FF3 0x1000 40#define LG_FF3 0x1000
41#define LG_FF4 0x2000
38 42
39/* 43/*
40 * Certain Logitech keyboards send in report #3 keys which are far 44 * Certain Logitech keyboards send in report #3 keys which are far
@@ -60,6 +64,17 @@ static void lg_report_fixup(struct hid_device *hdev, __u8 *rdesc,
60 "report descriptor\n"); 64 "report descriptor\n");
61 rdesc[33] = rdesc[50] = 0x02; 65 rdesc[33] = rdesc[50] = 0x02;
62 } 66 }
67
68 if ((quirks & LG_FF4) && rsize >= 101 &&
69 rdesc[41] == 0x95 && rdesc[42] == 0x0B &&
70 rdesc[47] == 0x05 && rdesc[48] == 0x09) {
71 dev_info(&hdev->dev, "fixing up Logitech Speed Force Wireless "
72 "button descriptor\n");
73 rdesc[41] = 0x05;
74 rdesc[42] = 0x09;
75 rdesc[47] = 0x95;
76 rdesc[48] = 0x0B;
77 }
63} 78}
64 79
65#define lg_map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, \ 80#define lg_map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, \
@@ -285,12 +300,33 @@ static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id)
285 goto err_free; 300 goto err_free;
286 } 301 }
287 302
303 if (quirks & LG_FF4) {
304 unsigned char buf[] = { 0x00, 0xAF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
305
306 ret = hdev->hid_output_raw_report(hdev, buf, sizeof(buf), HID_FEATURE_REPORT);
307
308 if (ret >= 0) {
309 /* insert a little delay of 10 jiffies ~ 40ms */
310 wait_queue_head_t wait;
311 init_waitqueue_head (&wait);
312 wait_event_interruptible_timeout(wait, 0, 10);
313
314 /* Select random Address */
315 buf[1] = 0xB2;
316 get_random_bytes(&buf[2], 2);
317
318 ret = hdev->hid_output_raw_report(hdev, buf, sizeof(buf), HID_FEATURE_REPORT);
319 }
320 }
321
288 if (quirks & LG_FF) 322 if (quirks & LG_FF)
289 lgff_init(hdev); 323 lgff_init(hdev);
290 if (quirks & LG_FF2) 324 if (quirks & LG_FF2)
291 lg2ff_init(hdev); 325 lg2ff_init(hdev);
292 if (quirks & LG_FF3) 326 if (quirks & LG_FF3)
293 lg3ff_init(hdev); 327 lg3ff_init(hdev);
328 if (quirks & LG_FF4)
329 lg4ff_init(hdev);
294 330
295 return 0; 331 return 0;
296err_free: 332err_free:
@@ -339,6 +375,8 @@ static const struct hid_device_id lg_devices[] = {
339 .driver_data = LG_FF }, 375 .driver_data = LG_FF },
340 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G25_WHEEL), 376 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G25_WHEEL),
341 .driver_data = LG_FF }, 377 .driver_data = LG_FF },
378 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WII_WHEEL),
379 .driver_data = LG_FF4 },
342 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_FFG ), 380 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_FFG ),
343 .driver_data = LG_FF }, 381 .driver_data = LG_FF },
344 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2), 382 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2),
diff --git a/drivers/hid/hid-lg.h b/drivers/hid/hid-lg.h
index ce2ac8672624..b0100ba2ae0b 100644
--- a/drivers/hid/hid-lg.h
+++ b/drivers/hid/hid-lg.h
@@ -19,4 +19,10 @@ int lg3ff_init(struct hid_device *hdev);
19static inline int lg3ff_init(struct hid_device *hdev) { return -1; } 19static inline int lg3ff_init(struct hid_device *hdev) { return -1; }
20#endif 20#endif
21 21
22#ifdef CONFIG_LOGIWII_FF
23int lg4ff_init(struct hid_device *hdev);
24#else
25static inline int lg4ff_init(struct hid_device *hdev) { return -1; }
26#endif
27
22#endif 28#endif
diff --git a/drivers/hid/hid-lg4ff.c b/drivers/hid/hid-lg4ff.c
new file mode 100644
index 000000000000..7eef5a2ce948
--- /dev/null
+++ b/drivers/hid/hid-lg4ff.c
@@ -0,0 +1,136 @@
1/*
2 * Force feedback support for Logitech Speed Force Wireless
3 *
4 * http://wiibrew.org/wiki/Logitech_USB_steering_wheel
5 *
6 * Copyright (c) 2010 Simon Wood <simon@mungewell.org>
7 */
8
9/*
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 */
24
25
26#include <linux/input.h>
27#include <linux/usb.h>
28#include <linux/hid.h>
29
30#include "usbhid/usbhid.h"
31#include "hid-lg.h"
32
33struct lg4ff_device {
34 struct hid_report *report;
35};
36
37static const signed short ff4_wheel_ac[] = {
38 FF_CONSTANT,
39 FF_AUTOCENTER,
40 -1
41};
42
43static int hid_lg4ff_play(struct input_dev *dev, void *data,
44 struct ff_effect *effect)
45{
46 struct hid_device *hid = input_get_drvdata(dev);
47 struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
48 struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
49 int x;
50
51#define CLAMP(x) if (x < 0) x = 0; if (x > 0xff) x = 0xff
52
53 switch (effect->type) {
54 case FF_CONSTANT:
55 x = effect->u.ramp.start_level + 0x80; /* 0x80 is no force */
56 CLAMP(x);
57 report->field[0]->value[0] = 0x11; /* Slot 1 */
58 report->field[0]->value[1] = 0x10;
59 report->field[0]->value[2] = x;
60 report->field[0]->value[3] = 0x00;
61 report->field[0]->value[4] = 0x00;
62 report->field[0]->value[5] = 0x08;
63 report->field[0]->value[6] = 0x00;
64 dbg_hid("Autocenter, x=0x%02X\n", x);
65
66 usbhid_submit_report(hid, report, USB_DIR_OUT);
67 break;
68 }
69 return 0;
70}
71
72static void hid_lg4ff_set_autocenter(struct input_dev *dev, u16 magnitude)
73{
74 struct hid_device *hid = input_get_drvdata(dev);
75 struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
76 struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
77 __s32 *value = report->field[0]->value;
78
79 *value++ = 0xfe;
80 *value++ = 0x0d;
81 *value++ = 0x07;
82 *value++ = 0x07;
83 *value++ = (magnitude >> 8) & 0xff;
84 *value++ = 0x00;
85 *value = 0x00;
86
87 usbhid_submit_report(hid, report, USB_DIR_OUT);
88}
89
90
91int lg4ff_init(struct hid_device *hid)
92{
93 struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list);
94 struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
95 struct input_dev *dev = hidinput->input;
96 struct hid_report *report;
97 struct hid_field *field;
98 const signed short *ff_bits = ff4_wheel_ac;
99 int error;
100 int i;
101
102 /* Find the report to use */
103 if (list_empty(report_list)) {
104 err_hid("No output report found");
105 return -1;
106 }
107
108 /* Check that the report looks ok */
109 report = list_entry(report_list->next, struct hid_report, list);
110 if (!report) {
111 err_hid("NULL output report");
112 return -1;
113 }
114
115 field = report->field[0];
116 if (!field) {
117 err_hid("NULL field");
118 return -1;
119 }
120
121 for (i = 0; ff_bits[i] >= 0; i++)
122 set_bit(ff_bits[i], dev->ffbit);
123
124 error = input_ff_create_memless(dev, NULL, hid_lg4ff_play);
125
126 if (error)
127 return error;
128
129 if (test_bit(FF_AUTOCENTER, dev->ffbit))
130 dev->ff->set_autocenter = hid_lg4ff_set_autocenter;
131
132 dev_info(&hid->dev, "Force feedback for Logitech Speed Force Wireless by "
133 "Simon Wood <simon@mungewell.org>\n");
134 return 0;
135}
136