aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichal Malý <madcatxster@gmail.com>2011-08-04 10:16:09 -0400
committerJiri Kosina <jkosina@suse.cz>2011-08-04 10:45:55 -0400
commit7362cd2286d2364cca6738b583668f64254fe04b (patch)
tree471b56ba9b7560387603fe9c9f6ddbd4d1ee9cdb
parentf5fc87905ea075a0b14878086fd4fe38be128844 (diff)
HID: lg4ff - Move handling of Logitech wheels to lg4ff driver
This is the first out of five patches me and Simon Wood (CC'd) have been working on. It separates the handling of Logite from the generic lgff driver and adds additional features specific for the Logitech wheels, namely - Native mode support for Driving Force GT, Driving Force Pro, G25 and G27 wheels Every Logitech wheel reports itself as generic Logitech Driving Force wheel (VID 046d, PID c294). This is done to ensu wheel will work on every USB HID-aware system even when no Logitech driver is available. It however limits the capabilit wheel - range is limited to 200 degrees, G25/G27 don't report the clutch pedal and there is only one combined axis for t brake. The switch to native mode is done via hardware-specific command which is different for each wheel. When the wheel receives such command, it simulates reconnect and reports to the OS with its actual PID. - Adjustable wheel range DFGT, DFP, G25 and G27 have variable range of the steering wheel. The range is limited by applying a maximum constant when the wheel is turned beyond the allowed range. The limit as also set by a hardware-specific command. There is a comm command for DFGT, G25 and G27 and another one for DFP. It is probably possible to use the DFP command to limit the range other Logitech wheels too, but this is not supported by the official Logitech driver for Windows. The patch adds a sysfs interface which allows for the range to be set from userspace. - Fixed autocentering command All Logitech wheels support FF_AUTOCENTER effect. The original implementation in the lgff driver didn't work well with patch fixes it. According to USB communication sniffs the Formula Force EX (pretty much rebranded original Driving Force accept the generic autocentering command, this issue is also addressed by the patch There are still some features this patch doesn't cover, but since some of them will most likely require modifications of memless driver we have decided not to include them yet. As first we decided to move the handling of Logitech wheels from hid-lgff driver to hid-lg4ff driver (originally used fo At also adds PID of Logitech Driving Force GT. Signed-off-by: Michal Malý <madcatxster@gmail.com> Signed-off-by: Simon Wood <simon@mungewell.org> Signed-off-by: Jiri Kosina <jkosina@suse.cz>
-rw-r--r--drivers/hid/hid-ids.h1
-rw-r--r--drivers/hid/hid-lg.c19
-rw-r--r--drivers/hid/hid-lg4ff.c70
-rw-r--r--drivers/hid/hid-lgff.c13
4 files changed, 60 insertions, 43 deletions
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index db63ccf21cc..ea2c338814f 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -436,6 +436,7 @@
436#define USB_DEVICE_ID_LOGITECH_MOMO_WHEEL 0xc295 436#define USB_DEVICE_ID_LOGITECH_MOMO_WHEEL 0xc295
437#define USB_DEVICE_ID_LOGITECH_DFP_WHEEL 0xc298 437#define USB_DEVICE_ID_LOGITECH_DFP_WHEEL 0xc298
438#define USB_DEVICE_ID_LOGITECH_G25_WHEEL 0xc299 438#define USB_DEVICE_ID_LOGITECH_G25_WHEEL 0xc299
439#define USB_DEVICE_ID_LOGITECH_DFGT_WHEEL 0xc29a
439#define USB_DEVICE_ID_LOGITECH_G27_WHEEL 0xc29b 440#define USB_DEVICE_ID_LOGITECH_G27_WHEEL 0xc29b
440#define USB_DEVICE_ID_LOGITECH_WII_WHEEL 0xc29c 441#define USB_DEVICE_ID_LOGITECH_WII_WHEEL 0xc29c
441#define USB_DEVICE_ID_LOGITECH_ELITE_KBD 0xc30a 442#define USB_DEVICE_ID_LOGITECH_ELITE_KBD 0xc30a
diff --git a/drivers/hid/hid-lg.c b/drivers/hid/hid-lg.c
index a7f916e8fc3..e0ae4a09425 100644
--- a/drivers/hid/hid-lg.c
+++ b/drivers/hid/hid-lg.c
@@ -363,7 +363,7 @@ static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id)
363 goto err_free; 363 goto err_free;
364 } 364 }
365 365
366 if (quirks & (LG_FF | LG_FF2 | LG_FF3)) 366 if (quirks & (LG_FF | LG_FF2 | LG_FF3 | LG_FF4))
367 connect_mask &= ~HID_CONNECT_FF; 367 connect_mask &= ~HID_CONNECT_FF;
368 368
369 ret = hid_hw_start(hdev, connect_mask); 369 ret = hid_hw_start(hdev, connect_mask);
@@ -372,7 +372,8 @@ static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id)
372 goto err_free; 372 goto err_free;
373 } 373 }
374 374
375 if (quirks & LG_FF4) { 375 /* Setup wireless link with Logitech Wii wheel */
376 if(hdev->product == USB_DEVICE_ID_LOGITECH_WII_WHEEL) {
376 unsigned char buf[] = { 0x00, 0xAF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; 377 unsigned char buf[] = { 0x00, 0xAF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
377 378
378 ret = hdev->hid_output_raw_report(hdev, buf, sizeof(buf), HID_FEATURE_REPORT); 379 ret = hdev->hid_output_raw_report(hdev, buf, sizeof(buf), HID_FEATURE_REPORT);
@@ -431,7 +432,7 @@ static const struct hid_device_id lg_devices[] = {
431 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_EXTREME_3D), 432 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_EXTREME_3D),
432 .driver_data = LG_NOGET }, 433 .driver_data = LG_NOGET },
433 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WHEEL), 434 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WHEEL),
434 .driver_data = LG_NOGET | LG_FF }, 435 .driver_data = LG_NOGET | LG_FF4 },
435 436
436 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD_CORD), 437 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD_CORD),
437 .driver_data = LG_FF2 }, 438 .driver_data = LG_FF2 },
@@ -444,15 +445,17 @@ static const struct hid_device_id lg_devices[] = {
444 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FORCE3D_PRO), 445 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FORCE3D_PRO),
445 .driver_data = LG_FF }, 446 .driver_data = LG_FF },
446 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL), 447 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL),
447 .driver_data = LG_FF }, 448 .driver_data = LG_FF4 },
448 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2), 449 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2),
449 .driver_data = LG_FF }, 450 .driver_data = LG_FF4 },
450 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G25_WHEEL), 451 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G25_WHEEL),
451 .driver_data = LG_FF }, 452 .driver_data = LG_FF4 },
453 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DFGT_WHEEL),
454 .driver_data = LG_FF4 },
452 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G27_WHEEL), 455 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G27_WHEEL),
453 .driver_data = LG_FF }, 456 .driver_data = LG_FF4 },
454 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DFP_WHEEL), 457 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DFP_WHEEL),
455 .driver_data = LG_NOGET | LG_FF }, 458 .driver_data = LG_NOGET | LG_FF4 },
456 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WII_WHEEL), 459 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WII_WHEEL),
457 .driver_data = LG_FF4 }, 460 .driver_data = LG_FF4 },
458 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_FFG ), 461 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_FFG ),
diff --git a/drivers/hid/hid-lg4ff.c b/drivers/hid/hid-lg4ff.c
index fa550c8e1d1..1fcb2da3278 100644
--- a/drivers/hid/hid-lg4ff.c
+++ b/drivers/hid/hid-lg4ff.c
@@ -29,17 +29,32 @@
29 29
30#include "usbhid/usbhid.h" 30#include "usbhid/usbhid.h"
31#include "hid-lg.h" 31#include "hid-lg.h"
32#include "hid-ids.h"
32 33
33struct lg4ff_device { 34static const signed short lg4ff_wheel_effects[] = {
34 struct hid_report *report;
35};
36
37static const signed short ff4_wheel_ac[] = {
38 FF_CONSTANT, 35 FF_CONSTANT,
39 FF_AUTOCENTER, 36 FF_AUTOCENTER,
40 -1 37 -1
41}; 38};
42 39
40struct lg4ff_wheel {
41 const __u32 product_id;
42 const signed short *ff_effects;
43 const __u16 min_range;
44 const __u16 max_range;
45};
46
47static const struct lg4ff_wheel lg4ff_devices[] = {
48 {USB_DEVICE_ID_LOGITECH_WHEEL, lg4ff_wheel_effects, 40, 270},
49 {USB_DEVICE_ID_LOGITECH_MOMO_WHEEL, lg4ff_wheel_effects, 40, 270},
50 {USB_DEVICE_ID_LOGITECH_DFP_WHEEL, lg4ff_wheel_effects, 40, 900},
51 {USB_DEVICE_ID_LOGITECH_G25_WHEEL, lg4ff_wheel_effects, 40, 900},
52 {USB_DEVICE_ID_LOGITECH_DFGT_WHEEL, lg4ff_wheel_effects, 40, 900},
53 {USB_DEVICE_ID_LOGITECH_G27_WHEEL, lg4ff_wheel_effects, 40, 900},
54 {USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2, lg4ff_wheel_effects, 40, 270},
55 {USB_DEVICE_ID_LOGITECH_WII_WHEEL, lg4ff_wheel_effects, 40, 270}
56};
57
43static int hid_lg4ff_play(struct input_dev *dev, void *data, 58static int hid_lg4ff_play(struct input_dev *dev, void *data,
44 struct ff_effect *effect) 59 struct ff_effect *effect)
45{ 60{
@@ -55,13 +70,12 @@ static int hid_lg4ff_play(struct input_dev *dev, void *data,
55 x = effect->u.ramp.start_level + 0x80; /* 0x80 is no force */ 70 x = effect->u.ramp.start_level + 0x80; /* 0x80 is no force */
56 CLAMP(x); 71 CLAMP(x);
57 report->field[0]->value[0] = 0x11; /* Slot 1 */ 72 report->field[0]->value[0] = 0x11; /* Slot 1 */
58 report->field[0]->value[1] = 0x10; 73 report->field[0]->value[1] = 0x08;
59 report->field[0]->value[2] = x; 74 report->field[0]->value[2] = x;
60 report->field[0]->value[3] = 0x00; 75 report->field[0]->value[3] = 0x80;
61 report->field[0]->value[4] = 0x00; 76 report->field[0]->value[4] = 0x00;
62 report->field[0]->value[5] = 0x08; 77 report->field[0]->value[5] = 0x00;
63 report->field[0]->value[6] = 0x00; 78 report->field[0]->value[6] = 0x00;
64 dbg_hid("Autocenter, x=0x%02X\n", x);
65 79
66 usbhid_submit_report(hid, report, USB_DIR_OUT); 80 usbhid_submit_report(hid, report, USB_DIR_OUT);
67 break; 81 break;
@@ -74,15 +88,14 @@ static void hid_lg4ff_set_autocenter(struct input_dev *dev, u16 magnitude)
74 struct hid_device *hid = input_get_drvdata(dev); 88 struct hid_device *hid = input_get_drvdata(dev);
75 struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; 89 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); 90 struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
77 __s32 *value = report->field[0]->value;
78 91
79 *value++ = 0xfe; 92 report->field[0]->value[0] = 0xfe;
80 *value++ = 0x0d; 93 report->field[0]->value[1] = 0x0d;
81 *value++ = 0x07; 94 report->field[0]->value[2] = magnitude >> 13;
82 *value++ = 0x07; 95 report->field[0]->value[3] = magnitude >> 13;
83 *value++ = (magnitude >> 8) & 0xff; 96 report->field[0]->value[4] = magnitude >> 8;
84 *value++ = 0x00; 97 report->field[0]->value[5] = 0x00;
85 *value = 0x00; 98 report->field[0]->value[6] = 0x00;
86 99
87 usbhid_submit_report(hid, report, USB_DIR_OUT); 100 usbhid_submit_report(hid, report, USB_DIR_OUT);
88} 101}
@@ -95,9 +108,7 @@ int lg4ff_init(struct hid_device *hid)
95 struct input_dev *dev = hidinput->input; 108 struct input_dev *dev = hidinput->input;
96 struct hid_report *report; 109 struct hid_report *report;
97 struct hid_field *field; 110 struct hid_field *field;
98 const signed short *ff_bits = ff4_wheel_ac; 111 int error, i, j;
99 int error;
100 int i;
101 112
102 /* Find the report to use */ 113 /* Find the report to use */
103 if (list_empty(report_list)) { 114 if (list_empty(report_list)) {
@@ -117,9 +128,24 @@ int lg4ff_init(struct hid_device *hid)
117 hid_err(hid, "NULL field\n"); 128 hid_err(hid, "NULL field\n");
118 return -1; 129 return -1;
119 } 130 }
131
132 /* Check what wheel has been connected */
133 for (i = 0; i < ARRAY_SIZE(lg4ff_devices); i++) {
134 if (hid->product == lg4ff_devices[i].product_id) {
135 dbg_hid("Found compatible device, product ID %04X\n", lg4ff_devices[i].product_id);
136 break;
137 }
138 }
139
140 if (i == ARRAY_SIZE(lg4ff_devices)) {
141 hid_err(hid, "Device is not supported by lg4ff driver. If you think it should be, consider reporting a bug to"
142 "LKML, Simon Wood <simon@mungewell.org> or Michal Maly <madcatxster@gmail.com>\n");
143 return -1;
144 }
120 145
121 for (i = 0; ff_bits[i] >= 0; i++) 146 /* Set supported force feedback capabilities */
122 set_bit(ff_bits[i], dev->ffbit); 147 for (j = 0; lg4ff_devices[i].ff_effects[j] >= 0; j++)
148 set_bit(lg4ff_devices[i].ff_effects[j], dev->ffbit);
123 149
124 error = input_ff_create_memless(dev, NULL, hid_lg4ff_play); 150 error = input_ff_create_memless(dev, NULL, hid_lg4ff_play);
125 151
diff --git a/drivers/hid/hid-lgff.c b/drivers/hid/hid-lgff.c
index 088f8504929..27bc54f92f4 100644
--- a/drivers/hid/hid-lgff.c
+++ b/drivers/hid/hid-lgff.c
@@ -58,12 +58,6 @@ static const signed short ff_joystick_ac[] = {
58 -1 58 -1
59}; 59};
60 60
61static const signed short ff_wheel[] = {
62 FF_CONSTANT,
63 FF_AUTOCENTER,
64 -1
65};
66
67static const struct dev_type devices[] = { 61static const struct dev_type devices[] = {
68 { 0x046d, 0xc211, ff_rumble }, 62 { 0x046d, 0xc211, ff_rumble },
69 { 0x046d, 0xc219, ff_rumble }, 63 { 0x046d, 0xc219, ff_rumble },
@@ -71,14 +65,7 @@ static const struct dev_type devices[] = {
71 { 0x046d, 0xc286, ff_joystick_ac }, 65 { 0x046d, 0xc286, ff_joystick_ac },
72 { 0x046d, 0xc287, ff_joystick_ac }, 66 { 0x046d, 0xc287, ff_joystick_ac },
73 { 0x046d, 0xc293, ff_joystick }, 67 { 0x046d, 0xc293, ff_joystick },
74 { 0x046d, 0xc294, ff_wheel },
75 { 0x046d, 0xc298, ff_wheel },
76 { 0x046d, 0xc299, ff_wheel },
77 { 0x046d, 0xc29b, ff_wheel },
78 { 0x046d, 0xc295, ff_joystick }, 68 { 0x046d, 0xc295, ff_joystick },
79 { 0x046d, 0xc298, ff_wheel },
80 { 0x046d, 0xc299, ff_wheel },
81 { 0x046d, 0xca03, ff_wheel },
82}; 69};
83 70
84static int hid_lgff_play(struct input_dev *dev, void *data, struct ff_effect *effect) 71static int hid_lgff_play(struct input_dev *dev, void *data, struct ff_effect *effect)