2 * joy-assasin.c Version 1.2
4 * Copyright (c) 1998 Vojtech Pavlik
8 * This is a module for the Linux joystick driver, supporting
9 * joysticks using FP-Gaming's Assasin 3D protocol.
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.
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.
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
27 * Should you need to contact me, the author, you can do so either by
28 * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
29 * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic
33 #include <asm/system.h>
34 #include <linux/delay.h>
35 #include <linux/errno.h>
36 #include <linux/ioport.h>
37 #include <linux/joystick.h>
38 #include <linux/kernel.h>
39 #include <linux/module.h>
40 #include <linux/string.h>
42 #define JS_AS_MAX_START 250
43 #define JS_AS_MAX_STROBE 50
44 #define JS_AS_MAX_TIME 2400
45 #define JS_AS_MAX_LENGTH 40
47 #define JS_AS_MODE_A3D 1 /* Assasin 3D */
48 #define JS_AS_MODE_PAN 2 /* Panther */
49 #define JS_AS_MODE_OEM 3 /* Panther OEM version */
50 #define JS_AS_MODE_PXL 4 /* Panther XL */
52 MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
53 MODULE_PARM(js_as
, "2-24i");
55 static int js_as
[]={-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0};
57 static int js_as_port_list
[] __initdata
= {0x201, 0};
58 static struct js_port
* js_as_port __initdata
= NULL
;
60 #include "joy-analog.h"
70 * js_as_read_packet() reads an Assasin 3D packet.
73 static int js_as_read_packet(int io
, int length
, char *data
)
80 int start
= (js_time_speed
* JS_AS_MAX_START
) >> 10;
81 int strobe
= (js_time_speed
* JS_AS_MAX_STROBE
) >> 10;
95 } while (u
== v
&& js_delta(t1
, t
) < start
);
102 if ((u
^ v
) & u
& 0x10) {
107 } while (i
< length
&& js_delta(t1
,t
) < strobe
);
109 __restore_flags(flags
);
115 * js_as_csum() computes checksum of triplet packet
118 static int js_as_csum(char *data
, int count
)
121 for (i
= 0; i
< count
- 2; i
++) csum
+= data
[i
];
122 return (csum
& 0x3f) != ((data
[count
- 2] << 3) | data
[count
- 1]);
126 * js_as_read() reads and analyzes A3D joystick data.
129 static int js_as_read(void *xinfo
, int **axes
, int **buttons
)
131 struct js_as_info
*info
= xinfo
;
132 char data
[JS_AS_MAX_LENGTH
];
134 switch (info
->mode
) {
140 if (js_as_read_packet(info
->io
, 29, data
) != 29) return -1;
141 if (data
[0] != info
->mode
) return -1;
142 if (js_as_csum(data
, 29)) return -1;
144 axes
[0][0] = ((data
[5] << 6) | (data
[6] << 3) | data
[ 7]) - ((data
[5] & 4) << 7);
145 axes
[0][1] = ((data
[8] << 6) | (data
[9] << 3) | data
[10]) - ((data
[8] & 4) << 7);
147 buttons
[0][0] = (data
[2] << 2) | (data
[3] >> 1);
149 info
->an
.axes
[0] = ((char)((data
[11] << 6) | (data
[12] << 3) | (data
[13]))) + 128;
150 info
->an
.axes
[1] = ((char)((data
[14] << 6) | (data
[15] << 3) | (data
[16]))) + 128;
151 info
->an
.axes
[2] = ((char)((data
[17] << 6) | (data
[18] << 3) | (data
[19]))) + 128;
152 info
->an
.axes
[3] = ((char)((data
[20] << 6) | (data
[21] << 3) | (data
[22]))) + 128;
154 info
->an
.buttons
= ((data
[3] << 3) | data
[4]) & 0xf;
156 js_an_decode(&info
->an
, axes
+ 1, buttons
+ 1);
162 if (js_as_read_packet(info
->io
, 33, data
) != 33) return -1;
163 if (data
[0] != info
->mode
) return -1;
164 if (js_as_csum(data
, 33)) return -1;
166 axes
[0][0] = ((char)((data
[15] << 6) | (data
[16] << 3) | (data
[17]))) + 128;
167 axes
[0][1] = ((char)((data
[18] << 6) | (data
[19] << 3) | (data
[20]))) + 128;
168 info
->an
.axes
[0] = ((char)((data
[21] << 6) | (data
[22] << 3) | (data
[23]))) + 128;
169 axes
[0][2] = ((char)((data
[24] << 6) | (data
[25] << 3) | (data
[26]))) + 128;
171 axes
[0][3] = ( data
[5] & 1) - ((data
[5] >> 2) & 1);
172 axes
[0][4] = ((data
[5] >> 1) & 1) - ((data
[6] >> 2) & 1);
173 axes
[0][5] = ((data
[4] >> 1) & 1) - ( data
[3] & 1);
174 axes
[0][6] = ((data
[4] >> 2) & 1) - ( data
[4] & 1);
176 axes
[0][7] = ((data
[ 9] << 6) | (data
[10] << 3) | data
[11]) - ((data
[ 9] & 4) << 7);
177 axes
[0][8] = ((data
[12] << 6) | (data
[13] << 3) | data
[14]) - ((data
[12] & 4) << 7);
179 buttons
[0][0] = (data
[2] << 8) | ((data
[3] & 6) << 5) | (data
[7] << 3) | data
[8];
181 if (info
->rudder
) axes
[1][0] = info
->an
.axes
[0];
192 * js_as_open() is a callback from the file open routine.
195 static int js_as_open(struct js_dev
*jd
)
202 * js_as_close() is a callback from the file release routine.
205 static int js_as_close(struct js_dev
*jd
)
212 * js_as_pxl_init_corr() initializes the correction values for
216 static void __init
js_as_pxl_init_corr(struct js_corr
**corr
, int **axes
)
220 for (i
= 0; i
< 2; i
++) {
221 corr
[0][i
].type
= JS_CORR_BROKEN
;
223 corr
[0][i
].coef
[0] = axes
[0][i
] - 4;
224 corr
[0][i
].coef
[1] = axes
[0][i
] + 4;
225 corr
[0][i
].coef
[2] = (1 << 29) / (127 - 32);
226 corr
[0][i
].coef
[3] = (1 << 29) / (127 - 32);
229 corr
[0][2].type
= JS_CORR_BROKEN
;
231 corr
[0][2].coef
[0] = 127 - 4;
232 corr
[0][2].coef
[1] = 128 + 4;
233 corr
[0][2].coef
[2] = (1 << 29) / (127 - 6);
234 corr
[0][2].coef
[3] = (1 << 29) / (127 - 6);
236 for (i
= 3; i
< 7; i
++) {
237 corr
[0][i
].type
= JS_CORR_BROKEN
;
239 corr
[0][i
].coef
[0] = 0;
240 corr
[0][i
].coef
[1] = 0;
241 corr
[0][i
].coef
[2] = (1 << 29);
242 corr
[0][i
].coef
[3] = (1 << 29);
245 for (i
= 7; i
< 9; i
++) {
246 corr
[0][i
].type
= JS_CORR_BROKEN
;
247 corr
[0][i
].prec
= -1;
248 corr
[0][i
].coef
[0] = 0;
249 corr
[0][i
].coef
[1] = 0;
250 corr
[0][i
].coef
[2] = (104 << 14);
251 corr
[0][i
].coef
[3] = (104 << 14);
256 * js_as_as_init_corr() initializes the correction values for
257 * the Panther and Assasin.
260 static void __init
js_as_as_init_corr(struct js_corr
**corr
)
264 for (i
= 0; i
< 2; i
++) {
265 corr
[0][i
].type
= JS_CORR_BROKEN
;
266 corr
[0][i
].prec
= -1;
267 corr
[0][i
].coef
[0] = 0;
268 corr
[0][i
].coef
[1] = 0;
269 corr
[0][i
].coef
[2] = (104 << 14);
270 corr
[0][i
].coef
[3] = (104 << 14);
275 * js_as_rudder_init_corr() initializes the correction values for
276 * the Panther XL connected rudder.
279 static void __init
js_as_rudder_init_corr(struct js_corr
**corr
, int **axes
)
281 corr
[1][0].type
= JS_CORR_BROKEN
;
283 corr
[1][0].coef
[0] = axes
[1][0] - (axes
[1][0] >> 3);
284 corr
[1][0].coef
[1] = axes
[1][0] + (axes
[1][0] >> 3);
285 corr
[1][0].coef
[2] = (1 << 29) / (axes
[1][0] - (axes
[1][0] >> 2) + 1);
286 corr
[1][0].coef
[3] = (1 << 29) / (axes
[1][0] - (axes
[1][0] >> 2) + 1);
290 * js_as_probe() probes for A3D joysticks.
293 static struct js_port __init
*js_as_probe(int io
, int mask0
, int mask1
, struct js_port
*port
)
295 struct js_as_info iniinfo
;
296 struct js_as_info
*info
= &iniinfo
;
298 char data
[JS_AS_MAX_LENGTH
];
303 memset(info
, 0, sizeof(struct js_as_info
));
305 if (io
< 0) return port
;
307 if (check_region(io
, 1)) return port
;
308 if (((u
= inb(io
)) & 3) == 3) return port
;
310 if (!((inb(io
) ^ u
) & ~u
& 0xf)) return port
;
312 if (js_as_read_packet(io
, 1, data
) != 1) return port
;
314 if (data
[0] && data
[0] <= 4) {
315 info
->mode
= data
[0];
317 request_region(io
, 1, "joystick (assasin)");
318 port
= js_register_port(port
, info
, 3, sizeof(struct js_as_info
), js_as_read
);
321 printk(KERN_WARNING
"joy-assasin: unknown joystick device detected "
322 "(io=%#x, id=%d), contact <vojtech@ucw.cz>\n", io
, data
[0]);
326 udelay(JS_AS_MAX_TIME
);
328 if (info
->mode
== JS_AS_MODE_PXL
) {
329 printk(KERN_INFO
"js%d: MadCatz Panther XL at %#x\n",
330 js_register_device(port
, 0, 9, 9, "MadCatz Panther XL", js_as_open
, js_as_close
),
332 js_as_read(port
->info
, port
->axes
, port
->buttons
);
333 js_as_pxl_init_corr(port
->corr
, port
->axes
);
334 if (info
->an
.axes
[0] < 254) {
335 printk(KERN_INFO
"js%d: Analog rudder on MadCatz Panther XL\n",
336 js_register_device(port
, 1, 1, 0, "Analog rudder", js_as_open
, js_as_close
));
338 port
->axes
[1][0] = info
->an
.axes
[0];
339 js_as_rudder_init_corr(port
->corr
, port
->axes
);
344 switch (info
->mode
) {
345 case JS_AS_MODE_A3D
: name
= "FP-Gaming Assasin 3D"; break;
346 case JS_AS_MODE_PAN
: name
= "MadCatz Panther"; break;
347 case JS_AS_MODE_OEM
: name
= "OEM Assasin 3D"; break;
348 default: name
= "This cannot happen"; break;
351 printk(KERN_INFO
"js%d: %s at %#x\n",
352 js_register_device(port
, 0, 2, 3, name
, js_as_open
, js_as_close
),
355 js_as_as_init_corr(port
->corr
);
357 js_as_read(port
->info
, port
->axes
, port
->buttons
);
359 for (i
= u
= 0; i
< 4; i
++) if (info
->an
.axes
[i
] < 254) u
|= 1 << i
;
361 if ((numdev
= js_an_probe_devs(&info
->an
, u
, mask0
, mask1
, port
)) <= 0)
364 for (i
= 0; i
< numdev
; i
++)
365 printk(KERN_INFO
"js%d: %s on %s\n",
366 js_register_device(port
, i
+ 1, js_an_axes(i
, &info
->an
), js_an_buttons(i
, &info
->an
),
367 js_an_name(i
, &info
->an
), js_as_open
, js_as_close
),
368 js_an_name(i
, &info
->an
), name
);
370 js_an_decode(&info
->an
, port
->axes
+ 1, port
->buttons
+ 1);
371 js_an_init_corr(&info
->an
, port
->axes
+ 1, port
->corr
+ 1, 0);
377 void __init
js_as_setup(char *str
, int *ints
)
380 for (i
= 0; i
<= ints
[0] && i
< 24; i
++) js_as
[i
] = ints
[i
+1];
385 int init_module(void)
387 int __init
js_as_init(void)
393 for (i
= 0; (js_as
[i
*3] >= 0) && i
< 8; i
++)
394 js_as_port
= js_as_probe(js_as
[i
*3], js_as
[i
*3+1], js_as
[i
*3+2], js_as_port
);
396 for (i
= 0; js_as_port_list
[i
]; i
++) js_as_port
= js_as_probe(js_as_port_list
[i
], 0, 0, js_as_port
);
398 if (js_as_port
) return 0;
401 printk(KERN_WARNING
"joy-assasin: no joysticks found\n");
408 void cleanup_module(void)
411 struct js_as_info
*info
;
413 while (js_as_port
!= NULL
) {
414 for (i
= 0; i
< js_as_port
->ndevs
; i
++)
415 if (js_as_port
->devs
[i
] != NULL
)
416 js_unregister_device(js_as_port
->devs
[i
]);
417 info
= js_as_port
->info
;
418 release_region(info
->io
, 1);
419 js_as_port
= js_unregister_port(js_as_port
);