diff options
Diffstat (limited to 'drivers/input/joystick/a3d.c')
-rw-r--r-- | drivers/input/joystick/a3d.c | 417 |
1 files changed, 417 insertions, 0 deletions
diff --git a/drivers/input/joystick/a3d.c b/drivers/input/joystick/a3d.c new file mode 100644 index 000000000000..ad39fe4bf35f --- /dev/null +++ b/drivers/input/joystick/a3d.c | |||
@@ -0,0 +1,417 @@ | |||
1 | /* | ||
2 | * $Id: a3d.c,v 1.21 2002/01/22 20:11:50 vojtech Exp $ | ||
3 | * | ||
4 | * Copyright (c) 1998-2001 Vojtech Pavlik | ||
5 | */ | ||
6 | |||
7 | /* | ||
8 | * FP-Gaming Assasin 3D joystick driver for Linux | ||
9 | */ | ||
10 | |||
11 | /* | ||
12 | * This program is free software; you can redistribute it and/or modify | ||
13 | * it under the terms of the GNU General Public License as published by | ||
14 | * the Free Software Foundation; either version 2 of the License, or | ||
15 | * (at your option) any later version. | ||
16 | * | ||
17 | * This program is distributed in the hope that it will be useful, | ||
18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
20 | * GNU General Public License for more details. | ||
21 | * | ||
22 | * You should have received a copy of the GNU General Public License | ||
23 | * along with this program; if not, write to the Free Software | ||
24 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
25 | * | ||
26 | * Should you need to contact me, the author, you can do so either by | ||
27 | * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: | ||
28 | * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic | ||
29 | */ | ||
30 | |||
31 | #include <linux/kernel.h> | ||
32 | #include <linux/module.h> | ||
33 | #include <linux/slab.h> | ||
34 | #include <linux/init.h> | ||
35 | #include <linux/gameport.h> | ||
36 | #include <linux/input.h> | ||
37 | |||
38 | #define DRIVER_DESC "FP-Gaming Assasin 3D joystick driver" | ||
39 | |||
40 | MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); | ||
41 | MODULE_DESCRIPTION(DRIVER_DESC); | ||
42 | MODULE_LICENSE("GPL"); | ||
43 | |||
44 | #define A3D_MAX_START 600 /* 600 us */ | ||
45 | #define A3D_MAX_STROBE 80 /* 80 us */ | ||
46 | #define A3D_MAX_LENGTH 40 /* 40*3 bits */ | ||
47 | |||
48 | #define A3D_MODE_A3D 1 /* Assassin 3D */ | ||
49 | #define A3D_MODE_PAN 2 /* Panther */ | ||
50 | #define A3D_MODE_OEM 3 /* Panther OEM version */ | ||
51 | #define A3D_MODE_PXL 4 /* Panther XL */ | ||
52 | |||
53 | static char *a3d_names[] = { NULL, "FP-Gaming Assassin 3D", "MadCatz Panther", "OEM Panther", | ||
54 | "MadCatz Panther XL", "MadCatz Panther XL w/ rudder" }; | ||
55 | |||
56 | struct a3d { | ||
57 | struct gameport *gameport; | ||
58 | struct gameport *adc; | ||
59 | struct input_dev dev; | ||
60 | int axes[4]; | ||
61 | int buttons; | ||
62 | int mode; | ||
63 | int length; | ||
64 | int reads; | ||
65 | int bads; | ||
66 | char phys[32]; | ||
67 | }; | ||
68 | |||
69 | /* | ||
70 | * a3d_read_packet() reads an Assassin 3D packet. | ||
71 | */ | ||
72 | |||
73 | static int a3d_read_packet(struct gameport *gameport, int length, char *data) | ||
74 | { | ||
75 | unsigned long flags; | ||
76 | unsigned char u, v; | ||
77 | unsigned int t, s; | ||
78 | int i; | ||
79 | |||
80 | i = 0; | ||
81 | t = gameport_time(gameport, A3D_MAX_START); | ||
82 | s = gameport_time(gameport, A3D_MAX_STROBE); | ||
83 | |||
84 | local_irq_save(flags); | ||
85 | gameport_trigger(gameport); | ||
86 | v = gameport_read(gameport); | ||
87 | |||
88 | while (t > 0 && i < length) { | ||
89 | t--; | ||
90 | u = v; v = gameport_read(gameport); | ||
91 | if (~v & u & 0x10) { | ||
92 | data[i++] = v >> 5; | ||
93 | t = s; | ||
94 | } | ||
95 | } | ||
96 | |||
97 | local_irq_restore(flags); | ||
98 | |||
99 | return i; | ||
100 | } | ||
101 | |||
102 | /* | ||
103 | * a3d_csum() computes checksum of triplet packet | ||
104 | */ | ||
105 | |||
106 | static int a3d_csum(char *data, int count) | ||
107 | { | ||
108 | int i, csum = 0; | ||
109 | |||
110 | for (i = 0; i < count - 2; i++) | ||
111 | csum += data[i]; | ||
112 | return (csum & 0x3f) != ((data[count - 2] << 3) | data[count - 1]); | ||
113 | } | ||
114 | |||
115 | static void a3d_read(struct a3d *a3d, unsigned char *data) | ||
116 | { | ||
117 | struct input_dev *dev = &a3d->dev; | ||
118 | |||
119 | switch (a3d->mode) { | ||
120 | |||
121 | case A3D_MODE_A3D: | ||
122 | case A3D_MODE_OEM: | ||
123 | case A3D_MODE_PAN: | ||
124 | |||
125 | input_report_rel(dev, REL_X, ((data[5] << 6) | (data[6] << 3) | data[ 7]) - ((data[5] & 4) << 7)); | ||
126 | input_report_rel(dev, REL_Y, ((data[8] << 6) | (data[9] << 3) | data[10]) - ((data[8] & 4) << 7)); | ||
127 | |||
128 | input_report_key(dev, BTN_RIGHT, data[2] & 1); | ||
129 | input_report_key(dev, BTN_LEFT, data[3] & 2); | ||
130 | input_report_key(dev, BTN_MIDDLE, data[3] & 4); | ||
131 | |||
132 | input_sync(dev); | ||
133 | |||
134 | a3d->axes[0] = ((signed char)((data[11] << 6) | (data[12] << 3) | (data[13]))) + 128; | ||
135 | a3d->axes[1] = ((signed char)((data[14] << 6) | (data[15] << 3) | (data[16]))) + 128; | ||
136 | a3d->axes[2] = ((signed char)((data[17] << 6) | (data[18] << 3) | (data[19]))) + 128; | ||
137 | a3d->axes[3] = ((signed char)((data[20] << 6) | (data[21] << 3) | (data[22]))) + 128; | ||
138 | |||
139 | a3d->buttons = ((data[3] << 3) | data[4]) & 0xf; | ||
140 | |||
141 | break; | ||
142 | |||
143 | case A3D_MODE_PXL: | ||
144 | |||
145 | input_report_rel(dev, REL_X, ((data[ 9] << 6) | (data[10] << 3) | data[11]) - ((data[ 9] & 4) << 7)); | ||
146 | input_report_rel(dev, REL_Y, ((data[12] << 6) | (data[13] << 3) | data[14]) - ((data[12] & 4) << 7)); | ||
147 | |||
148 | input_report_key(dev, BTN_RIGHT, data[2] & 1); | ||
149 | input_report_key(dev, BTN_LEFT, data[3] & 2); | ||
150 | input_report_key(dev, BTN_MIDDLE, data[3] & 4); | ||
151 | input_report_key(dev, BTN_SIDE, data[7] & 2); | ||
152 | input_report_key(dev, BTN_EXTRA, data[7] & 4); | ||
153 | |||
154 | input_report_abs(dev, ABS_X, ((signed char)((data[15] << 6) | (data[16] << 3) | (data[17]))) + 128); | ||
155 | input_report_abs(dev, ABS_Y, ((signed char)((data[18] << 6) | (data[19] << 3) | (data[20]))) + 128); | ||
156 | input_report_abs(dev, ABS_RUDDER, ((signed char)((data[21] << 6) | (data[22] << 3) | (data[23]))) + 128); | ||
157 | input_report_abs(dev, ABS_THROTTLE, ((signed char)((data[24] << 6) | (data[25] << 3) | (data[26]))) + 128); | ||
158 | |||
159 | input_report_abs(dev, ABS_HAT0X, ( data[5] & 1) - ((data[5] >> 2) & 1)); | ||
160 | input_report_abs(dev, ABS_HAT0Y, ((data[5] >> 1) & 1) - ((data[6] >> 2) & 1)); | ||
161 | input_report_abs(dev, ABS_HAT1X, ((data[4] >> 1) & 1) - ( data[3] & 1)); | ||
162 | input_report_abs(dev, ABS_HAT1Y, ((data[4] >> 2) & 1) - ( data[4] & 1)); | ||
163 | |||
164 | input_report_key(dev, BTN_TRIGGER, data[8] & 1); | ||
165 | input_report_key(dev, BTN_THUMB, data[8] & 2); | ||
166 | input_report_key(dev, BTN_TOP, data[8] & 4); | ||
167 | input_report_key(dev, BTN_PINKIE, data[7] & 1); | ||
168 | |||
169 | input_sync(dev); | ||
170 | |||
171 | break; | ||
172 | } | ||
173 | } | ||
174 | |||
175 | |||
176 | /* | ||
177 | * a3d_poll() reads and analyzes A3D joystick data. | ||
178 | */ | ||
179 | |||
180 | static void a3d_poll(struct gameport *gameport) | ||
181 | { | ||
182 | struct a3d *a3d = gameport_get_drvdata(gameport); | ||
183 | unsigned char data[A3D_MAX_LENGTH]; | ||
184 | |||
185 | a3d->reads++; | ||
186 | if (a3d_read_packet(a3d->gameport, a3d->length, data) != a3d->length || | ||
187 | data[0] != a3d->mode || a3d_csum(data, a3d->length)) | ||
188 | a3d->bads++; | ||
189 | else | ||
190 | a3d_read(a3d, data); | ||
191 | } | ||
192 | |||
193 | /* | ||
194 | * a3d_adc_cooked_read() copies the acis and button data to the | ||
195 | * callers arrays. It could do the read itself, but the caller could | ||
196 | * call this more than 50 times a second, which would use too much CPU. | ||
197 | */ | ||
198 | |||
199 | static int a3d_adc_cooked_read(struct gameport *gameport, int *axes, int *buttons) | ||
200 | { | ||
201 | struct a3d *a3d = gameport->port_data; | ||
202 | int i; | ||
203 | |||
204 | for (i = 0; i < 4; i++) | ||
205 | axes[i] = (a3d->axes[i] < 254) ? a3d->axes[i] : -1; | ||
206 | *buttons = a3d->buttons; | ||
207 | return 0; | ||
208 | } | ||
209 | |||
210 | /* | ||
211 | * a3d_adc_open() is the gameport open routine. It refuses to serve | ||
212 | * any but cooked data. | ||
213 | */ | ||
214 | |||
215 | static int a3d_adc_open(struct gameport *gameport, int mode) | ||
216 | { | ||
217 | struct a3d *a3d = gameport->port_data; | ||
218 | |||
219 | if (mode != GAMEPORT_MODE_COOKED) | ||
220 | return -1; | ||
221 | |||
222 | gameport_start_polling(a3d->gameport); | ||
223 | return 0; | ||
224 | } | ||
225 | |||
226 | /* | ||
227 | * a3d_adc_close() is a callback from the input close routine. | ||
228 | */ | ||
229 | |||
230 | static void a3d_adc_close(struct gameport *gameport) | ||
231 | { | ||
232 | struct a3d *a3d = gameport->port_data; | ||
233 | |||
234 | gameport_stop_polling(a3d->gameport); | ||
235 | } | ||
236 | |||
237 | /* | ||
238 | * a3d_open() is a callback from the input open routine. | ||
239 | */ | ||
240 | |||
241 | static int a3d_open(struct input_dev *dev) | ||
242 | { | ||
243 | struct a3d *a3d = dev->private; | ||
244 | |||
245 | gameport_start_polling(a3d->gameport); | ||
246 | return 0; | ||
247 | } | ||
248 | |||
249 | /* | ||
250 | * a3d_close() is a callback from the input close routine. | ||
251 | */ | ||
252 | |||
253 | static void a3d_close(struct input_dev *dev) | ||
254 | { | ||
255 | struct a3d *a3d = dev->private; | ||
256 | |||
257 | gameport_stop_polling(a3d->gameport); | ||
258 | } | ||
259 | |||
260 | /* | ||
261 | * a3d_connect() probes for A3D joysticks. | ||
262 | */ | ||
263 | |||
264 | static int a3d_connect(struct gameport *gameport, struct gameport_driver *drv) | ||
265 | { | ||
266 | struct a3d *a3d; | ||
267 | struct gameport *adc; | ||
268 | unsigned char data[A3D_MAX_LENGTH]; | ||
269 | int i; | ||
270 | int err; | ||
271 | |||
272 | if (!(a3d = kcalloc(1, sizeof(struct a3d), GFP_KERNEL))) | ||
273 | return -ENOMEM; | ||
274 | |||
275 | a3d->gameport = gameport; | ||
276 | |||
277 | gameport_set_drvdata(gameport, a3d); | ||
278 | |||
279 | err = gameport_open(gameport, drv, GAMEPORT_MODE_RAW); | ||
280 | if (err) | ||
281 | goto fail1; | ||
282 | |||
283 | i = a3d_read_packet(gameport, A3D_MAX_LENGTH, data); | ||
284 | |||
285 | if (!i || a3d_csum(data, i)) { | ||
286 | err = -ENODEV; | ||
287 | goto fail2; | ||
288 | } | ||
289 | |||
290 | a3d->mode = data[0]; | ||
291 | |||
292 | if (!a3d->mode || a3d->mode > 5) { | ||
293 | printk(KERN_WARNING "a3d.c: Unknown A3D device detected " | ||
294 | "(%s, id=%d), contact <vojtech@ucw.cz>\n", gameport->phys, a3d->mode); | ||
295 | err = -ENODEV; | ||
296 | goto fail2; | ||
297 | } | ||
298 | |||
299 | gameport_set_poll_handler(gameport, a3d_poll); | ||
300 | gameport_set_poll_interval(gameport, 20); | ||
301 | |||
302 | sprintf(a3d->phys, "%s/input0", gameport->phys); | ||
303 | |||
304 | if (a3d->mode == A3D_MODE_PXL) { | ||
305 | |||
306 | int axes[] = { ABS_X, ABS_Y, ABS_THROTTLE, ABS_RUDDER }; | ||
307 | |||
308 | a3d->length = 33; | ||
309 | |||
310 | init_input_dev(&a3d->dev); | ||
311 | |||
312 | a3d->dev.evbit[0] |= BIT(EV_ABS) | BIT(EV_KEY) | BIT(EV_REL); | ||
313 | a3d->dev.relbit[0] |= BIT(REL_X) | BIT(REL_Y); | ||
314 | a3d->dev.absbit[0] |= BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_THROTTLE) | BIT(ABS_RUDDER) | ||
315 | | BIT(ABS_HAT0X) | BIT(ABS_HAT0Y) | BIT(ABS_HAT1X) | BIT(ABS_HAT1Y); | ||
316 | |||
317 | a3d->dev.keybit[LONG(BTN_MOUSE)] |= BIT(BTN_RIGHT) | BIT(BTN_LEFT) | BIT(BTN_MIDDLE) | ||
318 | | BIT(BTN_SIDE) | BIT(BTN_EXTRA); | ||
319 | |||
320 | a3d->dev.keybit[LONG(BTN_JOYSTICK)] |= BIT(BTN_TRIGGER) | BIT(BTN_THUMB) | BIT(BTN_TOP) | BIT(BTN_PINKIE); | ||
321 | |||
322 | a3d_read(a3d, data); | ||
323 | |||
324 | for (i = 0; i < 4; i++) { | ||
325 | if (i < 2) | ||
326 | input_set_abs_params(&a3d->dev, axes[i], 48, a3d->dev.abs[axes[i]] * 2 - 48, 0, 8); | ||
327 | else | ||
328 | input_set_abs_params(&a3d->dev, axes[i], 2, 253, 0, 0); | ||
329 | input_set_abs_params(&a3d->dev, ABS_HAT0X + i, -1, 1, 0, 0); | ||
330 | } | ||
331 | |||
332 | } else { | ||
333 | a3d->length = 29; | ||
334 | |||
335 | init_input_dev(&a3d->dev); | ||
336 | |||
337 | a3d->dev.evbit[0] |= BIT(EV_KEY) | BIT(EV_REL); | ||
338 | a3d->dev.relbit[0] |= BIT(REL_X) | BIT(REL_Y); | ||
339 | a3d->dev.keybit[LONG(BTN_MOUSE)] |= BIT(BTN_RIGHT) | BIT(BTN_LEFT) | BIT(BTN_MIDDLE); | ||
340 | |||
341 | a3d_read(a3d, data); | ||
342 | |||
343 | if (!(a3d->adc = adc = gameport_allocate_port())) | ||
344 | printk(KERN_ERR "a3d: Not enough memory for ADC port\n"); | ||
345 | else { | ||
346 | adc->port_data = a3d; | ||
347 | adc->open = a3d_adc_open; | ||
348 | adc->close = a3d_adc_close; | ||
349 | adc->cooked_read = a3d_adc_cooked_read; | ||
350 | adc->fuzz = 1; | ||
351 | |||
352 | gameport_set_name(adc, a3d_names[a3d->mode]); | ||
353 | gameport_set_phys(adc, "%s/gameport0", gameport->phys); | ||
354 | adc->dev.parent = &gameport->dev; | ||
355 | |||
356 | gameport_register_port(adc); | ||
357 | } | ||
358 | } | ||
359 | |||
360 | a3d->dev.private = a3d; | ||
361 | a3d->dev.open = a3d_open; | ||
362 | a3d->dev.close = a3d_close; | ||
363 | |||
364 | a3d->dev.name = a3d_names[a3d->mode]; | ||
365 | a3d->dev.phys = a3d->phys; | ||
366 | a3d->dev.id.bustype = BUS_GAMEPORT; | ||
367 | a3d->dev.id.vendor = GAMEPORT_ID_VENDOR_MADCATZ; | ||
368 | a3d->dev.id.product = a3d->mode; | ||
369 | a3d->dev.id.version = 0x0100; | ||
370 | |||
371 | input_register_device(&a3d->dev); | ||
372 | printk(KERN_INFO "input: %s on %s\n", a3d_names[a3d->mode], a3d->phys); | ||
373 | |||
374 | return 0; | ||
375 | |||
376 | fail2: gameport_close(gameport); | ||
377 | fail1: gameport_set_drvdata(gameport, NULL); | ||
378 | kfree(a3d); | ||
379 | return err; | ||
380 | } | ||
381 | |||
382 | static void a3d_disconnect(struct gameport *gameport) | ||
383 | { | ||
384 | struct a3d *a3d = gameport_get_drvdata(gameport); | ||
385 | |||
386 | input_unregister_device(&a3d->dev); | ||
387 | if (a3d->adc) { | ||
388 | gameport_unregister_port(a3d->adc); | ||
389 | a3d->adc = NULL; | ||
390 | } | ||
391 | gameport_close(gameport); | ||
392 | gameport_set_drvdata(gameport, NULL); | ||
393 | kfree(a3d); | ||
394 | } | ||
395 | |||
396 | static struct gameport_driver a3d_drv = { | ||
397 | .driver = { | ||
398 | .name = "adc", | ||
399 | }, | ||
400 | .description = DRIVER_DESC, | ||
401 | .connect = a3d_connect, | ||
402 | .disconnect = a3d_disconnect, | ||
403 | }; | ||
404 | |||
405 | static int __init a3d_init(void) | ||
406 | { | ||
407 | gameport_register_driver(&a3d_drv); | ||
408 | return 0; | ||
409 | } | ||
410 | |||
411 | static void __exit a3d_exit(void) | ||
412 | { | ||
413 | gameport_unregister_driver(&a3d_drv); | ||
414 | } | ||
415 | |||
416 | module_init(a3d_init); | ||
417 | module_exit(a3d_exit); | ||