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