diff options
author | Gary Stein <LordCnidarian@gmail.com> | 2010-01-12 18:25:58 -0500 |
---|---|---|
committer | Jiri Kosina <jkosina@suse.cz> | 2010-01-12 18:26:31 -0500 |
commit | 74f292ca8c7a2b9370f80d97a49e48174f4c7635 (patch) | |
tree | a42e4260f003aff9ec76e1b80fd09e027cb79e57 | |
parent | d67dec5b2cc208215de21dc7806945bf6a6e85d0 (diff) |
HID: add driver for the Logitech Flight System G940
Implements a new USB-HID for Force Feedback based on the normal
Logitech Force Feedback code and FF-Memless.
Currently only supports the FF_CONSTANT effect although the joystick
appears to support additional non-standard ones.
Signed-off-by: Gary Stein <LordCnidarian@gmail.com>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
-rw-r--r-- | drivers/hid/Kconfig | 8 | ||||
-rw-r--r-- | drivers/hid/Makefile | 3 | ||||
-rw-r--r-- | drivers/hid/hid-core.c | 1 | ||||
-rw-r--r-- | drivers/hid/hid-ids.h | 1 | ||||
-rw-r--r-- | drivers/hid/hid-lg.c | 7 | ||||
-rw-r--r-- | drivers/hid/hid-lg.h | 6 | ||||
-rw-r--r-- | drivers/hid/hid-lg3ff.c | 176 | ||||
-rw-r--r-- | drivers/hid/hid-lgff.c | 1 |
8 files changed, 202 insertions, 1 deletions
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 5f73774164d8..317049b80c35 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig | |||
@@ -189,6 +189,14 @@ config LOGIRUMBLEPAD2_FF | |||
189 | Say Y here if you want to enable force feedback support for Logitech | 189 | Say Y here if you want to enable force feedback support for Logitech |
190 | Rumblepad 2 devices. | 190 | Rumblepad 2 devices. |
191 | 191 | ||
192 | config LOGIG940_FF | ||
193 | bool "Logitech Flight System G940 force feedback support" | ||
194 | depends on HID_LOGITECH | ||
195 | select INPUT_FF_MEMLESS | ||
196 | help | ||
197 | Say Y here if you want to enable force feedback support for Logitech | ||
198 | Flight System G940 devices. | ||
199 | |||
192 | config HID_MICROSOFT | 200 | config HID_MICROSOFT |
193 | tristate "Microsoft" if EMBEDDED | 201 | tristate "Microsoft" if EMBEDDED |
194 | depends on USB_HID | 202 | depends on USB_HID |
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index f8dc5bac79bd..ce6c8da28486 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile | |||
@@ -18,6 +18,9 @@ endif | |||
18 | ifdef CONFIG_LOGIRUMBLEPAD2_FF | 18 | ifdef CONFIG_LOGIRUMBLEPAD2_FF |
19 | hid-logitech-objs += hid-lg2ff.o | 19 | hid-logitech-objs += hid-lg2ff.o |
20 | endif | 20 | endif |
21 | ifdef CONFIG_LOGIG940_FF | ||
22 | hid-logitech-objs += hid-lg3ff.o | ||
23 | endif | ||
21 | 24 | ||
22 | obj-$(CONFIG_HID_3M_PCT) += hid-3m-pct.o | 25 | obj-$(CONFIG_HID_3M_PCT) += hid-3m-pct.o |
23 | obj-$(CONFIG_HID_A4TECH) += hid-a4tech.o | 26 | obj-$(CONFIG_HID_A4TECH) += hid-a4tech.o |
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index a4b496c68259..a0c0c49dec09 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c | |||
@@ -1323,6 +1323,7 @@ static const struct hid_device_id hid_blacklist[] = { | |||
1323 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_F3D) }, | 1323 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_F3D) }, |
1324 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_FFG ) }, | 1324 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_FFG ) }, |
1325 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FORCE3D_PRO) }, | 1325 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FORCE3D_PRO) }, |
1326 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FLIGHT_SYSTEM_G940) }, | ||
1326 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL) }, | 1327 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL) }, |
1327 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2) }, | 1328 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2) }, |
1328 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G25_WHEEL) }, | 1329 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G25_WHEEL) }, |
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 07e056b0e188..c7c9cbf8132c 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h | |||
@@ -300,6 +300,7 @@ | |||
300 | #define USB_DEVICE_ID_LOGITECH_RUMBLEPAD2_2 0xc219 | 300 | #define USB_DEVICE_ID_LOGITECH_RUMBLEPAD2_2 0xc219 |
301 | #define USB_DEVICE_ID_LOGITECH_WINGMAN_F3D 0xc283 | 301 | #define USB_DEVICE_ID_LOGITECH_WINGMAN_F3D 0xc283 |
302 | #define USB_DEVICE_ID_LOGITECH_FORCE3D_PRO 0xc286 | 302 | #define USB_DEVICE_ID_LOGITECH_FORCE3D_PRO 0xc286 |
303 | #define USB_DEVICE_ID_LOGITECH_FLIGHT_SYSTEM_G940 0xc287 | ||
303 | #define USB_DEVICE_ID_LOGITECH_WHEEL 0xc294 | 304 | #define USB_DEVICE_ID_LOGITECH_WHEEL 0xc294 |
304 | #define USB_DEVICE_ID_LOGITECH_WINGMAN_FFG 0xc293 | 305 | #define USB_DEVICE_ID_LOGITECH_WINGMAN_FFG 0xc293 |
305 | #define USB_DEVICE_ID_LOGITECH_MOMO_WHEEL 0xc295 | 306 | #define USB_DEVICE_ID_LOGITECH_MOMO_WHEEL 0xc295 |
diff --git a/drivers/hid/hid-lg.c b/drivers/hid/hid-lg.c index 9fcd3d017ab3..3677c9037a11 100644 --- a/drivers/hid/hid-lg.c +++ b/drivers/hid/hid-lg.c | |||
@@ -34,6 +34,7 @@ | |||
34 | #define LG_FF 0x200 | 34 | #define LG_FF 0x200 |
35 | #define LG_FF2 0x400 | 35 | #define LG_FF2 0x400 |
36 | #define LG_RDESC_REL_ABS 0x800 | 36 | #define LG_RDESC_REL_ABS 0x800 |
37 | #define LG_FF3 0x1000 | ||
37 | 38 | ||
38 | /* | 39 | /* |
39 | * Certain Logitech keyboards send in report #3 keys which are far | 40 | * Certain Logitech keyboards send in report #3 keys which are far |
@@ -266,7 +267,7 @@ static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id) | |||
266 | goto err_free; | 267 | goto err_free; |
267 | } | 268 | } |
268 | 269 | ||
269 | if (quirks & (LG_FF | LG_FF2)) | 270 | if (quirks & (LG_FF | LG_FF2 | LG_FF3)) |
270 | connect_mask &= ~HID_CONNECT_FF; | 271 | connect_mask &= ~HID_CONNECT_FF; |
271 | 272 | ||
272 | ret = hid_hw_start(hdev, connect_mask); | 273 | ret = hid_hw_start(hdev, connect_mask); |
@@ -279,6 +280,8 @@ static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id) | |||
279 | lgff_init(hdev); | 280 | lgff_init(hdev); |
280 | if (quirks & LG_FF2) | 281 | if (quirks & LG_FF2) |
281 | lg2ff_init(hdev); | 282 | lg2ff_init(hdev); |
283 | if (quirks & LG_FF3) | ||
284 | lg3ff_init(hdev); | ||
282 | 285 | ||
283 | return 0; | 286 | return 0; |
284 | err_free: | 287 | err_free: |
@@ -331,6 +334,8 @@ static const struct hid_device_id lg_devices[] = { | |||
331 | .driver_data = LG_FF }, | 334 | .driver_data = LG_FF }, |
332 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2), | 335 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2), |
333 | .driver_data = LG_FF2 }, | 336 | .driver_data = LG_FF2 }, |
337 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FLIGHT_SYSTEM_G940), | ||
338 | .driver_data = LG_FF3 }, | ||
334 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACENAVIGATOR), | 339 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACENAVIGATOR), |
335 | .driver_data = LG_RDESC_REL_ABS }, | 340 | .driver_data = LG_RDESC_REL_ABS }, |
336 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACETRAVELLER), | 341 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACETRAVELLER), |
diff --git a/drivers/hid/hid-lg.h b/drivers/hid/hid-lg.h index 27ae750ca878..8069c3e8b64a 100644 --- a/drivers/hid/hid-lg.h +++ b/drivers/hid/hid-lg.h | |||
@@ -15,4 +15,10 @@ int lg2ff_init(struct hid_device *hdev); | |||
15 | static inline int lg2ff_init(struct hid_device *hdev) { return -1; } | 15 | static inline int lg2ff_init(struct hid_device *hdev) { return -1; } |
16 | #endif | 16 | #endif |
17 | 17 | ||
18 | #ifdef CONFIG_LOGIG940_FF | ||
19 | int lg3ff_init(struct hid_device *hdev); | ||
20 | #else | ||
21 | static inline int lg3ff_init(struct hid_device *hdev) { return -1; } | ||
22 | #endif | ||
23 | |||
18 | #endif | 24 | #endif |
diff --git a/drivers/hid/hid-lg3ff.c b/drivers/hid/hid-lg3ff.c new file mode 100644 index 000000000000..4002832ee4af --- /dev/null +++ b/drivers/hid/hid-lg3ff.c | |||
@@ -0,0 +1,176 @@ | |||
1 | /* | ||
2 | * Force feedback support for Logitech Flight System G940 | ||
3 | * | ||
4 | * Copyright (c) 2009 Gary Stein <LordCnidarian@gmail.com> | ||
5 | */ | ||
6 | |||
7 | /* | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
21 | */ | ||
22 | |||
23 | |||
24 | #include <linux/input.h> | ||
25 | #include <linux/usb.h> | ||
26 | #include <linux/hid.h> | ||
27 | |||
28 | #include "usbhid/usbhid.h" | ||
29 | #include "hid-lg.h" | ||
30 | |||
31 | /* | ||
32 | * G940 Theory of Operation (from experimentation) | ||
33 | * | ||
34 | * There are 63 fields (only 3 of them currently used) | ||
35 | * 0 - seems to be command field | ||
36 | * 1 - 30 deal with the x axis | ||
37 | * 31 -60 deal with the y axis | ||
38 | * | ||
39 | * Field 1 is x axis constant force | ||
40 | * Field 31 is y axis constant force | ||
41 | * | ||
42 | * other interesting fields 1,2,3,4 on x axis | ||
43 | * (same for 31,32,33,34 on y axis) | ||
44 | * | ||
45 | * 0 0 127 127 makes the joystick autocenter hard | ||
46 | * | ||
47 | * 127 0 127 127 makes the joystick loose on the right, | ||
48 | * but stops all movemnt left | ||
49 | * | ||
50 | * -127 0 -127 -127 makes the joystick loose on the left, | ||
51 | * but stops all movement right | ||
52 | * | ||
53 | * 0 0 -127 -127 makes the joystick rattle very hard | ||
54 | * | ||
55 | * I'm sure these are effects that I don't know enough about them | ||
56 | */ | ||
57 | |||
58 | struct lg3ff_device { | ||
59 | struct hid_report *report; | ||
60 | }; | ||
61 | |||
62 | static int hid_lg3ff_play(struct input_dev *dev, void *data, | ||
63 | struct ff_effect *effect) | ||
64 | { | ||
65 | struct hid_device *hid = input_get_drvdata(dev); | ||
66 | struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; | ||
67 | struct hid_report *report = list_entry(report_list->next, struct hid_report, list); | ||
68 | int x, y; | ||
69 | |||
70 | /* | ||
71 | * Maxusage should always be 63 (maximum fields) | ||
72 | * likely a better way to ensure this data is clean | ||
73 | */ | ||
74 | memset(report->field[0]->value, 0, sizeof(__s32)*report->field[0]->maxusage); | ||
75 | |||
76 | switch (effect->type) { | ||
77 | case FF_CONSTANT: | ||
78 | /* | ||
79 | * Already clamped in ff_memless | ||
80 | * 0 is center (different then other logitech) | ||
81 | */ | ||
82 | x = effect->u.ramp.start_level; | ||
83 | y = effect->u.ramp.end_level; | ||
84 | |||
85 | /* send command byte */ | ||
86 | report->field[0]->value[0] = 0x51; | ||
87 | |||
88 | /* | ||
89 | * Sign backwards from other Force3d pro | ||
90 | * which get recast here in two's complement 8 bits | ||
91 | */ | ||
92 | report->field[0]->value[1] = (unsigned char)(-x); | ||
93 | report->field[0]->value[31] = (unsigned char)(-y); | ||
94 | |||
95 | usbhid_submit_report(hid, report, USB_DIR_OUT); | ||
96 | break; | ||
97 | } | ||
98 | return 0; | ||
99 | } | ||
100 | static void hid_lg3ff_set_autocenter(struct input_dev *dev, u16 magnitude) | ||
101 | { | ||
102 | struct hid_device *hid = input_get_drvdata(dev); | ||
103 | struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; | ||
104 | struct hid_report *report = list_entry(report_list->next, struct hid_report, list); | ||
105 | |||
106 | /* | ||
107 | * Auto Centering probed from device | ||
108 | * NOTE: deadman's switch on G940 must be covered | ||
109 | * for effects to work | ||
110 | */ | ||
111 | report->field[0]->value[0] = 0x51; | ||
112 | report->field[0]->value[1] = 0x00; | ||
113 | report->field[0]->value[2] = 0x00; | ||
114 | report->field[0]->value[3] = 0x7F; | ||
115 | report->field[0]->value[4] = 0x7F; | ||
116 | report->field[0]->value[31] = 0x00; | ||
117 | report->field[0]->value[32] = 0x00; | ||
118 | report->field[0]->value[33] = 0x7F; | ||
119 | report->field[0]->value[34] = 0x7F; | ||
120 | |||
121 | usbhid_submit_report(hid, report, USB_DIR_OUT); | ||
122 | } | ||
123 | |||
124 | |||
125 | static const signed short ff3_joystick_ac[] = { | ||
126 | FF_CONSTANT, | ||
127 | FF_AUTOCENTER, | ||
128 | -1 | ||
129 | }; | ||
130 | |||
131 | int lg3ff_init(struct hid_device *hid) | ||
132 | { | ||
133 | struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list); | ||
134 | struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; | ||
135 | struct input_dev *dev = hidinput->input; | ||
136 | struct hid_report *report; | ||
137 | struct hid_field *field; | ||
138 | const signed short *ff_bits = ff3_joystick_ac; | ||
139 | int error; | ||
140 | int i; | ||
141 | |||
142 | /* Find the report to use */ | ||
143 | if (list_empty(report_list)) { | ||
144 | err_hid("No output report found"); | ||
145 | return -1; | ||
146 | } | ||
147 | |||
148 | /* Check that the report looks ok */ | ||
149 | report = list_entry(report_list->next, struct hid_report, list); | ||
150 | if (!report) { | ||
151 | err_hid("NULL output report"); | ||
152 | return -1; | ||
153 | } | ||
154 | |||
155 | field = report->field[0]; | ||
156 | if (!field) { | ||
157 | err_hid("NULL field"); | ||
158 | return -1; | ||
159 | } | ||
160 | |||
161 | /* Assume single fixed device G940 */ | ||
162 | for (i = 0; ff_bits[i] >= 0; i++) | ||
163 | set_bit(ff_bits[i], dev->ffbit); | ||
164 | |||
165 | error = input_ff_create_memless(dev, NULL, hid_lg3ff_play); | ||
166 | if (error) | ||
167 | return error; | ||
168 | |||
169 | if (test_bit(FF_AUTOCENTER, dev->ffbit)) | ||
170 | dev->ff->set_autocenter = hid_lg3ff_set_autocenter; | ||
171 | |||
172 | dev_info(&hid->dev, "Force feedback for Logitech Flight System G940 by " | ||
173 | "Gary Stein <LordCnidarian@gmail.com>\n"); | ||
174 | return 0; | ||
175 | } | ||
176 | |||
diff --git a/drivers/hid/hid-lgff.c b/drivers/hid/hid-lgff.c index 987abebe0829..61142b76a9b1 100644 --- a/drivers/hid/hid-lgff.c +++ b/drivers/hid/hid-lgff.c | |||
@@ -67,6 +67,7 @@ static const struct dev_type devices[] = { | |||
67 | { 0x046d, 0xc219, ff_rumble }, | 67 | { 0x046d, 0xc219, ff_rumble }, |
68 | { 0x046d, 0xc283, ff_joystick }, | 68 | { 0x046d, 0xc283, ff_joystick }, |
69 | { 0x046d, 0xc286, ff_joystick_ac }, | 69 | { 0x046d, 0xc286, ff_joystick_ac }, |
70 | { 0x046d, 0xc287, ff_joystick_ac }, | ||
70 | { 0x046d, 0xc293, ff_joystick }, | 71 | { 0x046d, 0xc293, ff_joystick }, |
71 | { 0x046d, 0xc294, ff_wheel }, | 72 | { 0x046d, 0xc294, ff_wheel }, |
72 | { 0x046d, 0xc295, ff_joystick }, | 73 | { 0x046d, 0xc295, ff_joystick }, |