diff options
-rw-r--r-- | drivers/hid/hid-lg4ff.c | 89 |
1 files changed, 89 insertions, 0 deletions
diff --git a/drivers/hid/hid-lg4ff.c b/drivers/hid/hid-lg4ff.c index 1fcb2da32785..8eb938d7aa02 100644 --- a/drivers/hid/hid-lg4ff.c +++ b/drivers/hid/hid-lg4ff.c | |||
@@ -31,6 +31,17 @@ | |||
31 | #include "hid-lg.h" | 31 | #include "hid-lg.h" |
32 | #include "hid-ids.h" | 32 | #include "hid-ids.h" |
33 | 33 | ||
34 | #define DFGT_REV_MAJ 0x13 | ||
35 | #define DFGT_REV_MIN 0x22 | ||
36 | #define DFP_REV_MAJ 0x11 | ||
37 | #define DFP_REV_MIN 0x06 | ||
38 | #define FFEX_REV_MAJ 0x21 | ||
39 | #define FFEX_REV_MIN 0x00 | ||
40 | #define G25_REV_MAJ 0x12 | ||
41 | #define G25_REV_MIN 0x22 | ||
42 | #define G27_REV_MAJ 0x12 | ||
43 | #define G27_REV_MIN 0x38 | ||
44 | |||
34 | static const signed short lg4ff_wheel_effects[] = { | 45 | static const signed short lg4ff_wheel_effects[] = { |
35 | FF_CONSTANT, | 46 | FF_CONSTANT, |
36 | FF_AUTOCENTER, | 47 | FF_AUTOCENTER, |
@@ -55,6 +66,46 @@ static const struct lg4ff_wheel lg4ff_devices[] = { | |||
55 | {USB_DEVICE_ID_LOGITECH_WII_WHEEL, lg4ff_wheel_effects, 40, 270} | 66 | {USB_DEVICE_ID_LOGITECH_WII_WHEEL, lg4ff_wheel_effects, 40, 270} |
56 | }; | 67 | }; |
57 | 68 | ||
69 | struct lg4ff_native_cmd { | ||
70 | const __u8 cmd_num; /* Number of commands to send */ | ||
71 | const __u8 cmd[]; | ||
72 | }; | ||
73 | |||
74 | struct lg4ff_usb_revision { | ||
75 | const __u16 rev_maj; | ||
76 | const __u16 rev_min; | ||
77 | const struct lg4ff_native_cmd *command; | ||
78 | }; | ||
79 | |||
80 | static const struct lg4ff_native_cmd native_dfp = { | ||
81 | 1, | ||
82 | {0xf8, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00} | ||
83 | }; | ||
84 | |||
85 | static const struct lg4ff_native_cmd native_dfgt = { | ||
86 | 2, | ||
87 | {0xf8, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, /* 1st command */ | ||
88 | 0xf8, 0x09, 0x03, 0x01, 0x00, 0x00, 0x00} /* 2nd command */ | ||
89 | }; | ||
90 | |||
91 | static const struct lg4ff_native_cmd native_g25 = { | ||
92 | 1, | ||
93 | {0xf8, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00} | ||
94 | }; | ||
95 | |||
96 | static const struct lg4ff_native_cmd native_g27 = { | ||
97 | 2, | ||
98 | {0xf8, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, /* 1st command */ | ||
99 | 0xf8, 0x09, 0x04, 0x01, 0x00, 0x00, 0x00} /* 2nd command */ | ||
100 | }; | ||
101 | |||
102 | static const struct lg4ff_usb_revision lg4ff_revs[] = { | ||
103 | {DFGT_REV_MAJ, DFGT_REV_MIN, &native_dfgt}, /* Driving Force GT */ | ||
104 | {DFP_REV_MAJ, DFP_REV_MIN, &native_dfp}, /* Driving Force Pro */ | ||
105 | {G25_REV_MAJ, G25_REV_MIN, &native_g25}, /* G25 */ | ||
106 | {G27_REV_MAJ, G27_REV_MIN, &native_g27}, /* G27 */ | ||
107 | }; | ||
108 | |||
58 | static int hid_lg4ff_play(struct input_dev *dev, void *data, | 109 | static int hid_lg4ff_play(struct input_dev *dev, void *data, |
59 | struct ff_effect *effect) | 110 | struct ff_effect *effect) |
60 | { | 111 | { |
@@ -100,6 +151,20 @@ static void hid_lg4ff_set_autocenter(struct input_dev *dev, u16 magnitude) | |||
100 | usbhid_submit_report(hid, report, USB_DIR_OUT); | 151 | usbhid_submit_report(hid, report, USB_DIR_OUT); |
101 | } | 152 | } |
102 | 153 | ||
154 | static void hid_lg4ff_switch_native(struct hid_device *hid, const struct lg4ff_native_cmd *cmd) | ||
155 | { | ||
156 | struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; | ||
157 | struct hid_report *report = list_entry(report_list->next, struct hid_report, list); | ||
158 | __u8 i, j; | ||
159 | |||
160 | j = 0; | ||
161 | while (j < 7*cmd->cmd_num) { | ||
162 | for (i = 0; i < 7; i++) | ||
163 | report->field[0]->value[i] = cmd->cmd[j++]; | ||
164 | |||
165 | usbhid_submit_report(hid, report, USB_DIR_OUT); | ||
166 | } | ||
167 | } | ||
103 | 168 | ||
104 | int lg4ff_init(struct hid_device *hid) | 169 | int lg4ff_init(struct hid_device *hid) |
105 | { | 170 | { |
@@ -108,7 +173,9 @@ int lg4ff_init(struct hid_device *hid) | |||
108 | struct input_dev *dev = hidinput->input; | 173 | struct input_dev *dev = hidinput->input; |
109 | struct hid_report *report; | 174 | struct hid_report *report; |
110 | struct hid_field *field; | 175 | struct hid_field *field; |
176 | struct usb_device_descriptor *udesc = 0; | ||
111 | int error, i, j; | 177 | int error, i, j; |
178 | __u16 bcdDevice, rev_maj, rev_min; | ||
112 | 179 | ||
113 | /* Find the report to use */ | 180 | /* Find the report to use */ |
114 | if (list_empty(report_list)) { | 181 | if (list_empty(report_list)) { |
@@ -143,6 +210,28 @@ int lg4ff_init(struct hid_device *hid) | |||
143 | return -1; | 210 | return -1; |
144 | } | 211 | } |
145 | 212 | ||
213 | /* Attempt to switch wheel to native mode when applicable */ | ||
214 | udesc = &(hid_to_usb_dev(hid)->descriptor); | ||
215 | if (!udesc) { | ||
216 | hid_err(hid, "NULL USB device descriptor\n"); | ||
217 | return -1; | ||
218 | } | ||
219 | bcdDevice = le16_to_cpu(udesc->bcdDevice); | ||
220 | rev_maj = bcdDevice >> 8; | ||
221 | rev_min = bcdDevice & 0xff; | ||
222 | |||
223 | if (lg4ff_devices[i].product_id == USB_DEVICE_ID_LOGITECH_WHEEL) { | ||
224 | dbg_hid("Generic wheel detected, can it do native?\n"); | ||
225 | dbg_hid("USB revision: %2x.%02x\n", rev_maj, rev_min); | ||
226 | |||
227 | for (j = 0; j < ARRAY_SIZE(lg4ff_revs); j++) { | ||
228 | if (lg4ff_revs[j].rev_maj == rev_maj && lg4ff_revs[j].rev_min == rev_min) { | ||
229 | hid_lg4ff_switch_native(hid, lg4ff_revs[j].command); | ||
230 | hid_info(hid, "Switched to native mode\n"); | ||
231 | } | ||
232 | } | ||
233 | } | ||
234 | |||
146 | /* Set supported force feedback capabilities */ | 235 | /* Set supported force feedback capabilities */ |
147 | for (j = 0; lg4ff_devices[i].ff_effects[j] >= 0; j++) | 236 | for (j = 0; lg4ff_devices[i].ff_effects[j] >= 0; j++) |
148 | set_bit(lg4ff_devices[i].ff_effects[j], dev->ffbit); | 237 | set_bit(lg4ff_devices[i].ff_effects[j], dev->ffbit); |