diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/usb/input/hid-lgff.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/usb/input/hid-lgff.c')
-rw-r--r-- | drivers/usb/input/hid-lgff.c | 528 |
1 files changed, 528 insertions, 0 deletions
diff --git a/drivers/usb/input/hid-lgff.c b/drivers/usb/input/hid-lgff.c new file mode 100644 index 000000000000..0d7404bab92f --- /dev/null +++ b/drivers/usb/input/hid-lgff.c | |||
@@ -0,0 +1,528 @@ | |||
1 | /* | ||
2 | * $$ | ||
3 | * | ||
4 | * Force feedback support for hid-compliant for some of the devices from | ||
5 | * Logitech, namely: | ||
6 | * - WingMan Cordless RumblePad | ||
7 | * - WingMan Force 3D | ||
8 | * | ||
9 | * Copyright (c) 2002-2004 Johann Deneux | ||
10 | */ | ||
11 | |||
12 | /* | ||
13 | * This program is free software; you can redistribute it and/or modify | ||
14 | * it under the terms of the GNU General Public License as published by | ||
15 | * the Free Software Foundation; either version 2 of the License, or | ||
16 | * (at your option) any later version. | ||
17 | * | ||
18 | * This program is distributed in the hope that it will be useful, | ||
19 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
20 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
21 | * GNU General Public License for more details. | ||
22 | * | ||
23 | * You should have received a copy of the GNU General Public License | ||
24 | * along with this program; if not, write to the Free Software | ||
25 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
26 | * | ||
27 | * Should you need to contact me, the author, you can do so by | ||
28 | * e-mail - mail your message to <johann.deneux@it.uu.se> | ||
29 | */ | ||
30 | |||
31 | #include <linux/input.h> | ||
32 | #include <linux/sched.h> | ||
33 | |||
34 | //#define DEBUG | ||
35 | #include <linux/usb.h> | ||
36 | |||
37 | #include <linux/circ_buf.h> | ||
38 | |||
39 | #include "hid.h" | ||
40 | #include "fixp-arith.h" | ||
41 | |||
42 | |||
43 | /* Periodicity of the update */ | ||
44 | #define PERIOD (HZ/10) | ||
45 | |||
46 | #define RUN_AT(t) (jiffies + (t)) | ||
47 | |||
48 | /* Effect status */ | ||
49 | #define EFFECT_STARTED 0 /* Effect is going to play after some time | ||
50 | (ff_replay.delay) */ | ||
51 | #define EFFECT_PLAYING 1 /* Effect is being played */ | ||
52 | #define EFFECT_USED 2 | ||
53 | |||
54 | // For lgff_device::flags | ||
55 | #define DEVICE_CLOSING 0 /* The driver is being unitialised */ | ||
56 | |||
57 | /* Check that the current process can access an effect */ | ||
58 | #define CHECK_OWNERSHIP(effect) (current->pid == 0 \ | ||
59 | || effect.owner == current->pid) | ||
60 | |||
61 | #define LGFF_CHECK_OWNERSHIP(i, l) \ | ||
62 | (i>=0 && i<LGFF_EFFECTS \ | ||
63 | && test_bit(EFFECT_USED, l->effects[i].flags) \ | ||
64 | && CHECK_OWNERSHIP(l->effects[i])) | ||
65 | |||
66 | #define LGFF_EFFECTS 8 | ||
67 | |||
68 | struct device_type { | ||
69 | u16 idVendor; | ||
70 | u16 idProduct; | ||
71 | signed short *ff; | ||
72 | }; | ||
73 | |||
74 | struct lgff_effect { | ||
75 | pid_t owner; | ||
76 | |||
77 | struct ff_effect effect; | ||
78 | |||
79 | unsigned long flags[1]; | ||
80 | unsigned int count; /* Number of times left to play */ | ||
81 | unsigned long started_at; /* When the effect started to play */ | ||
82 | }; | ||
83 | |||
84 | struct lgff_device { | ||
85 | struct hid_device* hid; | ||
86 | |||
87 | struct hid_report* constant; | ||
88 | struct hid_report* rumble; | ||
89 | struct hid_report* condition; | ||
90 | |||
91 | struct lgff_effect effects[LGFF_EFFECTS]; | ||
92 | spinlock_t lock; /* device-level lock. Having locks on | ||
93 | a per-effect basis could be nice, but | ||
94 | isn't really necessary */ | ||
95 | |||
96 | unsigned long flags[1]; /* Contains various information about the | ||
97 | state of the driver for this device */ | ||
98 | |||
99 | struct timer_list timer; | ||
100 | }; | ||
101 | |||
102 | /* Callbacks */ | ||
103 | static void hid_lgff_exit(struct hid_device* hid); | ||
104 | static int hid_lgff_event(struct hid_device *hid, struct input_dev *input, | ||
105 | unsigned int type, unsigned int code, int value); | ||
106 | static int hid_lgff_flush(struct input_dev *input, struct file *file); | ||
107 | static int hid_lgff_upload_effect(struct input_dev *input, | ||
108 | struct ff_effect *effect); | ||
109 | static int hid_lgff_erase(struct input_dev *input, int id); | ||
110 | |||
111 | /* Local functions */ | ||
112 | static void hid_lgff_input_init(struct hid_device* hid); | ||
113 | static void hid_lgff_timer(unsigned long timer_data); | ||
114 | static struct hid_report* hid_lgff_duplicate_report(struct hid_report*); | ||
115 | static void hid_lgff_delete_report(struct hid_report*); | ||
116 | |||
117 | static signed short ff_rumble[] = { | ||
118 | FF_RUMBLE, | ||
119 | -1 | ||
120 | }; | ||
121 | |||
122 | static signed short ff_joystick[] = { | ||
123 | FF_CONSTANT, | ||
124 | -1 | ||
125 | }; | ||
126 | |||
127 | static struct device_type devices[] = { | ||
128 | {0x046d, 0xc211, ff_rumble}, | ||
129 | {0x046d, 0xc219, ff_rumble}, | ||
130 | {0x046d, 0xc283, ff_joystick}, | ||
131 | {0x0000, 0x0000, ff_joystick} | ||
132 | }; | ||
133 | |||
134 | int hid_lgff_init(struct hid_device* hid) | ||
135 | { | ||
136 | struct lgff_device *private; | ||
137 | struct hid_report* report; | ||
138 | struct hid_field* field; | ||
139 | |||
140 | /* Find the report to use */ | ||
141 | if (list_empty(&hid->report_enum[HID_OUTPUT_REPORT].report_list)) { | ||
142 | err("No output report found"); | ||
143 | return -1; | ||
144 | } | ||
145 | /* Check that the report looks ok */ | ||
146 | report = (struct hid_report*)hid->report_enum[HID_OUTPUT_REPORT].report_list.next; | ||
147 | if (!report) { | ||
148 | err("NULL output report"); | ||
149 | return -1; | ||
150 | } | ||
151 | field = report->field[0]; | ||
152 | if (!field) { | ||
153 | err("NULL field"); | ||
154 | return -1; | ||
155 | } | ||
156 | |||
157 | private = kmalloc(sizeof(struct lgff_device), GFP_KERNEL); | ||
158 | if (!private) | ||
159 | return -1; | ||
160 | memset(private, 0, sizeof(struct lgff_device)); | ||
161 | hid->ff_private = private; | ||
162 | |||
163 | /* Input init */ | ||
164 | hid_lgff_input_init(hid); | ||
165 | |||
166 | |||
167 | private->constant = hid_lgff_duplicate_report(report); | ||
168 | if (!private->constant) { | ||
169 | kfree(private); | ||
170 | return -1; | ||
171 | } | ||
172 | private->constant->field[0]->value[0] = 0x51; | ||
173 | private->constant->field[0]->value[1] = 0x08; | ||
174 | private->constant->field[0]->value[2] = 0x7f; | ||
175 | private->constant->field[0]->value[3] = 0x7f; | ||
176 | |||
177 | private->rumble = hid_lgff_duplicate_report(report); | ||
178 | if (!private->rumble) { | ||
179 | hid_lgff_delete_report(private->constant); | ||
180 | kfree(private); | ||
181 | return -1; | ||
182 | } | ||
183 | private->rumble->field[0]->value[0] = 0x42; | ||
184 | |||
185 | |||
186 | private->condition = hid_lgff_duplicate_report(report); | ||
187 | if (!private->condition) { | ||
188 | hid_lgff_delete_report(private->rumble); | ||
189 | hid_lgff_delete_report(private->constant); | ||
190 | kfree(private); | ||
191 | return -1; | ||
192 | } | ||
193 | |||
194 | private->hid = hid; | ||
195 | |||
196 | spin_lock_init(&private->lock); | ||
197 | init_timer(&private->timer); | ||
198 | private->timer.data = (unsigned long)private; | ||
199 | private->timer.function = hid_lgff_timer; | ||
200 | |||
201 | /* Event and exit callbacks */ | ||
202 | hid->ff_exit = hid_lgff_exit; | ||
203 | hid->ff_event = hid_lgff_event; | ||
204 | |||
205 | /* Start the update task */ | ||
206 | private->timer.expires = RUN_AT(PERIOD); | ||
207 | add_timer(&private->timer); /*TODO: only run the timer when at least | ||
208 | one effect is playing */ | ||
209 | |||
210 | printk(KERN_INFO "Force feedback for Logitech force feedback devices by Johann Deneux <johann.deneux@it.uu.se>\n"); | ||
211 | |||
212 | return 0; | ||
213 | } | ||
214 | |||
215 | static struct hid_report* hid_lgff_duplicate_report(struct hid_report* report) | ||
216 | { | ||
217 | struct hid_report* ret; | ||
218 | |||
219 | ret = kmalloc(sizeof(struct lgff_device), GFP_KERNEL); | ||
220 | if (!ret) | ||
221 | return NULL; | ||
222 | *ret = *report; | ||
223 | |||
224 | ret->field[0] = kmalloc(sizeof(struct hid_field), GFP_KERNEL); | ||
225 | if (!ret->field[0]) { | ||
226 | kfree(ret); | ||
227 | return NULL; | ||
228 | } | ||
229 | *ret->field[0] = *report->field[0]; | ||
230 | |||
231 | ret->field[0]->value = kmalloc(sizeof(s32[8]), GFP_KERNEL); | ||
232 | if (!ret->field[0]->value) { | ||
233 | kfree(ret->field[0]); | ||
234 | kfree(ret); | ||
235 | return NULL; | ||
236 | } | ||
237 | memset(ret->field[0]->value, 0, sizeof(s32[8])); | ||
238 | |||
239 | return ret; | ||
240 | } | ||
241 | |||
242 | static void hid_lgff_delete_report(struct hid_report* report) | ||
243 | { | ||
244 | if (report) { | ||
245 | kfree(report->field[0]->value); | ||
246 | kfree(report->field[0]); | ||
247 | kfree(report); | ||
248 | } | ||
249 | } | ||
250 | |||
251 | static void hid_lgff_input_init(struct hid_device* hid) | ||
252 | { | ||
253 | struct device_type* dev = devices; | ||
254 | signed short* ff; | ||
255 | u16 idVendor = le16_to_cpu(hid->dev->descriptor.idVendor); | ||
256 | u16 idProduct = le16_to_cpu(hid->dev->descriptor.idProduct); | ||
257 | struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list); | ||
258 | |||
259 | while (dev->idVendor && (idVendor != dev->idVendor || idProduct != dev->idProduct)) | ||
260 | dev++; | ||
261 | |||
262 | ff = dev->ff; | ||
263 | |||
264 | while (*ff >= 0) { | ||
265 | set_bit(*ff, hidinput->input.ffbit); | ||
266 | ++ff; | ||
267 | } | ||
268 | |||
269 | hidinput->input.upload_effect = hid_lgff_upload_effect; | ||
270 | hidinput->input.flush = hid_lgff_flush; | ||
271 | |||
272 | set_bit(EV_FF, hidinput->input.evbit); | ||
273 | hidinput->input.ff_effects_max = LGFF_EFFECTS; | ||
274 | } | ||
275 | |||
276 | static void hid_lgff_exit(struct hid_device* hid) | ||
277 | { | ||
278 | struct lgff_device *lgff = hid->ff_private; | ||
279 | |||
280 | set_bit(DEVICE_CLOSING, lgff->flags); | ||
281 | del_timer_sync(&lgff->timer); | ||
282 | |||
283 | hid_lgff_delete_report(lgff->condition); | ||
284 | hid_lgff_delete_report(lgff->rumble); | ||
285 | hid_lgff_delete_report(lgff->constant); | ||
286 | |||
287 | kfree(lgff); | ||
288 | } | ||
289 | |||
290 | static int hid_lgff_event(struct hid_device *hid, struct input_dev* input, | ||
291 | unsigned int type, unsigned int code, int value) | ||
292 | { | ||
293 | struct lgff_device *lgff = hid->ff_private; | ||
294 | struct lgff_effect *effect = lgff->effects + code; | ||
295 | unsigned long flags; | ||
296 | |||
297 | if (type != EV_FF) return -EINVAL; | ||
298 | if (!LGFF_CHECK_OWNERSHIP(code, lgff)) return -EACCES; | ||
299 | if (value < 0) return -EINVAL; | ||
300 | |||
301 | spin_lock_irqsave(&lgff->lock, flags); | ||
302 | |||
303 | if (value > 0) { | ||
304 | if (test_bit(EFFECT_STARTED, effect->flags)) { | ||
305 | spin_unlock_irqrestore(&lgff->lock, flags); | ||
306 | return -EBUSY; | ||
307 | } | ||
308 | if (test_bit(EFFECT_PLAYING, effect->flags)) { | ||
309 | spin_unlock_irqrestore(&lgff->lock, flags); | ||
310 | return -EBUSY; | ||
311 | } | ||
312 | |||
313 | effect->count = value; | ||
314 | |||
315 | if (effect->effect.replay.delay) { | ||
316 | set_bit(EFFECT_STARTED, effect->flags); | ||
317 | } else { | ||
318 | set_bit(EFFECT_PLAYING, effect->flags); | ||
319 | } | ||
320 | effect->started_at = jiffies; | ||
321 | } | ||
322 | else { /* value == 0 */ | ||
323 | clear_bit(EFFECT_STARTED, effect->flags); | ||
324 | clear_bit(EFFECT_PLAYING, effect->flags); | ||
325 | } | ||
326 | |||
327 | spin_unlock_irqrestore(&lgff->lock, flags); | ||
328 | |||
329 | return 0; | ||
330 | |||
331 | } | ||
332 | |||
333 | /* Erase all effects this process owns */ | ||
334 | static int hid_lgff_flush(struct input_dev *dev, struct file *file) | ||
335 | { | ||
336 | struct hid_device *hid = dev->private; | ||
337 | struct lgff_device *lgff = hid->ff_private; | ||
338 | int i; | ||
339 | |||
340 | for (i=0; i<dev->ff_effects_max; ++i) { | ||
341 | |||
342 | /*NOTE: no need to lock here. The only times EFFECT_USED is | ||
343 | modified is when effects are uploaded or when an effect is | ||
344 | erased. But a process cannot close its dev/input/eventX fd | ||
345 | and perform ioctls on the same fd all at the same time */ | ||
346 | if ( current->pid == lgff->effects[i].owner | ||
347 | && test_bit(EFFECT_USED, lgff->effects[i].flags)) { | ||
348 | |||
349 | if (hid_lgff_erase(dev, i)) | ||
350 | warn("erase effect %d failed", i); | ||
351 | } | ||
352 | |||
353 | } | ||
354 | |||
355 | return 0; | ||
356 | } | ||
357 | |||
358 | static int hid_lgff_erase(struct input_dev *dev, int id) | ||
359 | { | ||
360 | struct hid_device *hid = dev->private; | ||
361 | struct lgff_device *lgff = hid->ff_private; | ||
362 | unsigned long flags; | ||
363 | |||
364 | if (!LGFF_CHECK_OWNERSHIP(id, lgff)) return -EACCES; | ||
365 | |||
366 | spin_lock_irqsave(&lgff->lock, flags); | ||
367 | lgff->effects[id].flags[0] = 0; | ||
368 | spin_unlock_irqrestore(&lgff->lock, flags); | ||
369 | |||
370 | return 0; | ||
371 | } | ||
372 | |||
373 | static int hid_lgff_upload_effect(struct input_dev* input, | ||
374 | struct ff_effect* effect) | ||
375 | { | ||
376 | struct hid_device *hid = input->private; | ||
377 | struct lgff_device *lgff = hid->ff_private; | ||
378 | struct lgff_effect new; | ||
379 | int id; | ||
380 | unsigned long flags; | ||
381 | |||
382 | dbg("ioctl rumble"); | ||
383 | |||
384 | if (!test_bit(effect->type, input->ffbit)) return -EINVAL; | ||
385 | |||
386 | spin_lock_irqsave(&lgff->lock, flags); | ||
387 | |||
388 | if (effect->id == -1) { | ||
389 | int i; | ||
390 | |||
391 | for (i=0; i<LGFF_EFFECTS && test_bit(EFFECT_USED, lgff->effects[i].flags); ++i); | ||
392 | if (i >= LGFF_EFFECTS) { | ||
393 | spin_unlock_irqrestore(&lgff->lock, flags); | ||
394 | return -ENOSPC; | ||
395 | } | ||
396 | |||
397 | effect->id = i; | ||
398 | lgff->effects[i].owner = current->pid; | ||
399 | lgff->effects[i].flags[0] = 0; | ||
400 | set_bit(EFFECT_USED, lgff->effects[i].flags); | ||
401 | } | ||
402 | else if (!LGFF_CHECK_OWNERSHIP(effect->id, lgff)) { | ||
403 | spin_unlock_irqrestore(&lgff->lock, flags); | ||
404 | return -EACCES; | ||
405 | } | ||
406 | |||
407 | id = effect->id; | ||
408 | new = lgff->effects[id]; | ||
409 | |||
410 | new.effect = *effect; | ||
411 | |||
412 | if (test_bit(EFFECT_STARTED, lgff->effects[id].flags) | ||
413 | || test_bit(EFFECT_STARTED, lgff->effects[id].flags)) { | ||
414 | |||
415 | /* Changing replay parameters is not allowed (for the time | ||
416 | being) */ | ||
417 | if (new.effect.replay.delay != lgff->effects[id].effect.replay.delay | ||
418 | || new.effect.replay.length != lgff->effects[id].effect.replay.length) { | ||
419 | spin_unlock_irqrestore(&lgff->lock, flags); | ||
420 | return -ENOSYS; | ||
421 | } | ||
422 | |||
423 | lgff->effects[id] = new; | ||
424 | |||
425 | } else { | ||
426 | lgff->effects[id] = new; | ||
427 | } | ||
428 | |||
429 | spin_unlock_irqrestore(&lgff->lock, flags); | ||
430 | return 0; | ||
431 | } | ||
432 | |||
433 | static void hid_lgff_timer(unsigned long timer_data) | ||
434 | { | ||
435 | struct lgff_device *lgff = (struct lgff_device*)timer_data; | ||
436 | struct hid_device *hid = lgff->hid; | ||
437 | unsigned long flags; | ||
438 | int x = 0x7f, y = 0x7f; // Coordinates of constant effects | ||
439 | unsigned int left = 0, right = 0; // Rumbling | ||
440 | int i; | ||
441 | |||
442 | spin_lock_irqsave(&lgff->lock, flags); | ||
443 | |||
444 | for (i=0; i<LGFF_EFFECTS; ++i) { | ||
445 | struct lgff_effect* effect = lgff->effects +i; | ||
446 | |||
447 | if (test_bit(EFFECT_PLAYING, effect->flags)) { | ||
448 | |||
449 | switch (effect->effect.type) { | ||
450 | case FF_CONSTANT: { | ||
451 | //TODO: handle envelopes | ||
452 | int degrees = effect->effect.direction * 360 >> 16; | ||
453 | x += fixp_mult(fixp_sin(degrees), | ||
454 | fixp_new16(effect->effect.u.constant.level)); | ||
455 | y += fixp_mult(-fixp_cos(degrees), | ||
456 | fixp_new16(effect->effect.u.constant.level)); | ||
457 | } break; | ||
458 | case FF_RUMBLE: | ||
459 | right += effect->effect.u.rumble.strong_magnitude; | ||
460 | left += effect->effect.u.rumble.weak_magnitude; | ||
461 | break; | ||
462 | }; | ||
463 | |||
464 | /* One run of the effect is finished playing */ | ||
465 | if (time_after(jiffies, | ||
466 | effect->started_at | ||
467 | + effect->effect.replay.delay*HZ/1000 | ||
468 | + effect->effect.replay.length*HZ/1000)) { | ||
469 | dbg("Finished playing once %d", i); | ||
470 | if (--effect->count <= 0) { | ||
471 | dbg("Stopped %d", i); | ||
472 | clear_bit(EFFECT_PLAYING, effect->flags); | ||
473 | } | ||
474 | else { | ||
475 | dbg("Start again %d", i); | ||
476 | if (effect->effect.replay.length != 0) { | ||
477 | clear_bit(EFFECT_PLAYING, effect->flags); | ||
478 | set_bit(EFFECT_STARTED, effect->flags); | ||
479 | } | ||
480 | effect->started_at = jiffies; | ||
481 | } | ||
482 | } | ||
483 | |||
484 | } else if (test_bit(EFFECT_STARTED, lgff->effects[i].flags)) { | ||
485 | /* Check if we should start playing the effect */ | ||
486 | if (time_after(jiffies, | ||
487 | lgff->effects[i].started_at | ||
488 | + lgff->effects[i].effect.replay.delay*HZ/1000)) { | ||
489 | dbg("Now playing %d", i); | ||
490 | clear_bit(EFFECT_STARTED, lgff->effects[i].flags); | ||
491 | set_bit(EFFECT_PLAYING, lgff->effects[i].flags); | ||
492 | } | ||
493 | } | ||
494 | } | ||
495 | |||
496 | #define CLAMP(x) if (x < 0) x = 0; if (x > 0xff) x = 0xff | ||
497 | |||
498 | // Clamp values | ||
499 | CLAMP(x); | ||
500 | CLAMP(y); | ||
501 | CLAMP(left); | ||
502 | CLAMP(right); | ||
503 | |||
504 | #undef CLAMP | ||
505 | |||
506 | if (x != lgff->constant->field[0]->value[2] | ||
507 | || y != lgff->constant->field[0]->value[3]) { | ||
508 | lgff->constant->field[0]->value[2] = x; | ||
509 | lgff->constant->field[0]->value[3] = y; | ||
510 | dbg("(x,y)=(%04x, %04x)", x, y); | ||
511 | hid_submit_report(hid, lgff->constant, USB_DIR_OUT); | ||
512 | } | ||
513 | |||
514 | if (left != lgff->rumble->field[0]->value[2] | ||
515 | || right != lgff->rumble->field[0]->value[3]) { | ||
516 | lgff->rumble->field[0]->value[2] = left; | ||
517 | lgff->rumble->field[0]->value[3] = right; | ||
518 | dbg("(left,right)=(%04x, %04x)", left, right); | ||
519 | hid_submit_report(hid, lgff->rumble, USB_DIR_OUT); | ||
520 | } | ||
521 | |||
522 | if (!test_bit(DEVICE_CLOSING, lgff->flags)) { | ||
523 | lgff->timer.expires = RUN_AT(PERIOD); | ||
524 | add_timer(&lgff->timer); | ||
525 | } | ||
526 | |||
527 | spin_unlock_irqrestore(&lgff->lock, flags); | ||
528 | } | ||