1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30#include <linux/input.h>
31#include <linux/slab.h>
32#include <linux/usb.h>
33#include <linux/hid.h>
34
35#include "hid-ids.h"
36
37#ifdef CONFIG_DRAGONRISE_FF
38#include "usbhid/usbhid.h"
39
40struct drff_device {
41 struct hid_report *report;
42};
43
44static int drff_play(struct input_dev *dev, void *data,
45 struct ff_effect *effect)
46{
47 struct hid_device *hid = input_get_drvdata(dev);
48 struct drff_device *drff = data;
49 int strong, weak;
50
51 strong = effect->u.rumble.strong_magnitude;
52 weak = effect->u.rumble.weak_magnitude;
53
54 dbg_hid("called with 0x%04x 0x%04x", strong, weak);
55
56 if (strong || weak) {
57 strong = strong * 0xff / 0xffff;
58 weak = weak * 0xff / 0xffff;
59
60
61
62
63 if (weak == 0x0a)
64 weak = 0x0b;
65
66 drff->report->field[0]->value[0] = 0x51;
67 drff->report->field[0]->value[1] = 0x00;
68 drff->report->field[0]->value[2] = weak;
69 drff->report->field[0]->value[4] = strong;
70 usbhid_submit_report(hid, drff->report, USB_DIR_OUT);
71
72 drff->report->field[0]->value[0] = 0xfa;
73 drff->report->field[0]->value[1] = 0xfe;
74 } else {
75 drff->report->field[0]->value[0] = 0xf3;
76 drff->report->field[0]->value[1] = 0x00;
77 }
78
79 drff->report->field[0]->value[2] = 0x00;
80 drff->report->field[0]->value[4] = 0x00;
81 dbg_hid("running with 0x%02x 0x%02x", strong, weak);
82 usbhid_submit_report(hid, drff->report, USB_DIR_OUT);
83
84 return 0;
85}
86
87static int drff_init(struct hid_device *hid)
88{
89 struct drff_device *drff;
90 struct hid_report *report;
91 struct hid_input *hidinput = list_first_entry(&hid->inputs,
92 struct hid_input, list);
93 struct list_head *report_list =
94 &hid->report_enum[HID_OUTPUT_REPORT].report_list;
95 struct input_dev *dev = hidinput->input;
96 int error;
97
98 if (list_empty(report_list)) {
99 hid_err(hid, "no output reports found\n");
100 return -ENODEV;
101 }
102
103 report = list_first_entry(report_list, struct hid_report, list);
104 if (report->maxfield < 1) {
105 hid_err(hid, "no fields in the report\n");
106 return -ENODEV;
107 }
108
109 if (report->field[0]->report_count < 7) {
110 hid_err(hid, "not enough values in the field\n");
111 return -ENODEV;
112 }
113
114 drff = kzalloc(sizeof(struct drff_device), GFP_KERNEL);
115 if (!drff)
116 return -ENOMEM;
117
118 set_bit(FF_RUMBLE, dev->ffbit);
119
120 error = input_ff_create_memless(dev, drff, drff_play);
121 if (error) {
122 kfree(drff);
123 return error;
124 }
125
126 drff->report = report;
127 drff->report->field[0]->value[0] = 0xf3;
128 drff->report->field[0]->value[1] = 0x00;
129 drff->report->field[0]->value[2] = 0x00;
130 drff->report->field[0]->value[3] = 0x00;
131 drff->report->field[0]->value[4] = 0x00;
132 drff->report->field[0]->value[5] = 0x00;
133 drff->report->field[0]->value[6] = 0x00;
134 usbhid_submit_report(hid, drff->report, USB_DIR_OUT);
135
136 hid_info(hid, "Force Feedback for DragonRise Inc. "
137 "game controllers by Richard Walmsley <richwalm@gmail.com>\n");
138
139 return 0;
140}
141#else
142static inline int drff_init(struct hid_device *hid)
143{
144 return 0;
145}
146#endif
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206#define PID0011_RDESC_ORIG_SIZE 101
207
208
209static __u8 pid0011_rdesc_fixed[] = {
210 0x05, 0x01,
211 0x09, 0x04,
212 0xA1, 0x01,
213 0xA1, 0x02,
214 0x14,
215 0x75, 0x08,
216 0x95, 0x03,
217 0x81, 0x01,
218 0x26, 0xFF, 0x00,
219 0x95, 0x02,
220 0x09, 0x30,
221 0x09, 0x31,
222 0x81, 0x02,
223 0x75, 0x01,
224 0x95, 0x04,
225 0x81, 0x01,
226 0x25, 0x01,
227 0x95, 0x0A,
228 0x05, 0x09,
229 0x19, 0x01,
230 0x29, 0x0A,
231 0x81, 0x02,
232 0x95, 0x0A,
233 0x81, 0x01,
234 0xC0,
235 0xC0
236};
237
238static __u8 *dr_report_fixup(struct hid_device *hdev, __u8 *rdesc,
239 unsigned int *rsize)
240{
241 switch (hdev->product) {
242 case 0x0011:
243 if (*rsize == PID0011_RDESC_ORIG_SIZE) {
244 rdesc = pid0011_rdesc_fixed;
245 *rsize = sizeof(pid0011_rdesc_fixed);
246 }
247 break;
248 }
249 return rdesc;
250}
251
252static int dr_probe(struct hid_device *hdev, const struct hid_device_id *id)
253{
254 int ret;
255
256 dev_dbg(&hdev->dev, "DragonRise Inc. HID hardware probe...");
257
258 ret = hid_parse(hdev);
259 if (ret) {
260 hid_err(hdev, "parse failed\n");
261 goto err;
262 }
263
264 ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF);
265 if (ret) {
266 hid_err(hdev, "hw start failed\n");
267 goto err;
268 }
269
270 switch (hdev->product) {
271 case 0x0006:
272 ret = drff_init(hdev);
273 if (ret) {
274 dev_err(&hdev->dev, "force feedback init failed\n");
275 hid_hw_stop(hdev);
276 goto err;
277 }
278 break;
279 }
280
281 return 0;
282err:
283 return ret;
284}
285
286static const struct hid_device_id dr_devices[] = {
287 { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, 0x0006), },
288 { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, 0x0011), },
289 { }
290};
291MODULE_DEVICE_TABLE(hid, dr_devices);
292
293static struct hid_driver dr_driver = {
294 .name = "dragonrise",
295 .id_table = dr_devices,
296 .report_fixup = dr_report_fixup,
297 .probe = dr_probe,
298};
299
300static int __init dr_init(void)
301{
302 return hid_register_driver(&dr_driver);
303}
304
305static void __exit dr_exit(void)
306{
307 hid_unregister_driver(&dr_driver);
308}
309
310module_init(dr_init);
311module_exit(dr_exit);
312MODULE_LICENSE("GPL");
313