2 * joy-assassin.c Version 1.2
4 * Copyright (c) 1998-1999 Vojtech Pavlik
10 * This is a module for the Linux joystick driver, supporting
11 * joysticks using FP-Gaming's Assassin 3D protocol.
15 * This program is free software; you can redistribute it and/or modify
16 * it under the terms of the GNU General Public License as published by
17 * the Free Software Foundation; either version 2 of the License, or
18 * (at your option) any later version.
20 * This program is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
25 * You should have received a copy of the GNU General Public License
26 * along with this program; if not, write to the Free Software
27 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
29 * Should you need to contact me, the author, you can do so either by
30 * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail:
31 * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic
35 #include <asm/system.h>
36 #include <linux/delay.h>
37 #include <linux/errno.h>
38 #include <linux/ioport.h>
39 #include <linux/joystick.h>
40 #include <linux/kernel.h>
41 #include <linux/module.h>
42 #include <linux/string.h>
43 #include <linux/init.h>
45 #define JS_AS_MAX_START 1000
46 #define JS_AS_DELAY_READ 3000
47 #define JS_AS_MAX_LENGTH 40
49 #define JS_AS_MODE_A3D 1 /* Assassin 3D */
50 #define JS_AS_MODE_PAN 2 /* Panther */
51 #define JS_AS_MODE_OEM 3 /* Panther OEM version */
52 #define JS_AS_MODE_PXL 4 /* Panther XL */
54 MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
55 MODULE_PARM(js_as
, "2-24i");
57 static int __initdata 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 };
59 static int js_as_port_list
[] __initdata
= {0x201, 0};
60 static struct js_port
* js_as_port __initdata
= NULL
;
62 #include "joy-analog.h"
72 * js_as_read_packet() reads an Assassin 3D packet.
75 static int js_as_read_packet(int io
, int length
, char *data
)
89 t
= p
= JS_AS_MAX_START
;
91 while (t
> 0 && i
< length
) {
100 __restore_flags(flags
);
106 * js_as_csum() computes checksum of triplet packet
109 static int js_as_csum(char *data
, int count
)
112 for (i
= 0; i
< count
- 2; i
++) csum
+= data
[i
];
113 return (csum
& 0x3f) != ((data
[count
- 2] << 3) | data
[count
- 1]);
117 * js_as_read() reads and analyzes A3D joystick data.
120 static int js_as_read(void *xinfo
, int **axes
, int **buttons
)
122 struct js_as_info
*info
= xinfo
;
123 char data
[JS_AS_MAX_LENGTH
];
125 switch (info
->mode
) {
131 if (js_as_read_packet(info
->io
, 29, data
) != 29) return -1;
132 if (data
[0] != info
->mode
) return -1;
133 if (js_as_csum(data
, 29)) return -1;
135 axes
[0][0] = ((data
[5] << 6) | (data
[6] << 3) | data
[ 7]) - ((data
[5] & 4) << 7);
136 axes
[0][1] = ((data
[8] << 6) | (data
[9] << 3) | data
[10]) - ((data
[8] & 4) << 7);
138 buttons
[0][0] = (data
[2] << 2) | (data
[3] >> 1);
140 info
->an
.axes
[0] = ((char)((data
[11] << 6) | (data
[12] << 3) | (data
[13]))) + 128;
141 info
->an
.axes
[1] = ((char)((data
[14] << 6) | (data
[15] << 3) | (data
[16]))) + 128;
142 info
->an
.axes
[2] = ((char)((data
[17] << 6) | (data
[18] << 3) | (data
[19]))) + 128;
143 info
->an
.axes
[3] = ((char)((data
[20] << 6) | (data
[21] << 3) | (data
[22]))) + 128;
145 info
->an
.buttons
= ((data
[3] << 3) | data
[4]) & 0xf;
147 js_an_decode(&info
->an
, axes
+ 1, buttons
+ 1);
153 if (js_as_read_packet(info
->io
, 33, data
) != 33) return -1;
154 if (data
[0] != info
->mode
) return -1;
155 if (js_as_csum(data
, 33)) return -1;
157 axes
[0][0] = ((char)((data
[15] << 6) | (data
[16] << 3) | (data
[17]))) + 128;
158 axes
[0][1] = ((char)((data
[18] << 6) | (data
[19] << 3) | (data
[20]))) + 128;
159 info
->an
.axes
[0] = ((char)((data
[21] << 6) | (data
[22] << 3) | (data
[23]))) + 128;
160 axes
[0][2] = ((char)((data
[24] << 6) | (data
[25] << 3) | (data
[26]))) + 128;
162 axes
[0][3] = ( data
[5] & 1) - ((data
[5] >> 2) & 1);
163 axes
[0][4] = ((data
[5] >> 1) & 1) - ((data
[6] >> 2) & 1);
164 axes
[0][5] = ((data
[4] >> 1) & 1) - ( data
[3] & 1);
165 axes
[0][6] = ((data
[4] >> 2) & 1) - ( data
[4] & 1);
167 axes
[0][7] = ((data
[ 9] << 6) | (data
[10] << 3) | data
[11]) - ((data
[ 9] & 4) << 7);
168 axes
[0][8] = ((data
[12] << 6) | (data
[13] << 3) | data
[14]) - ((data
[12] & 4) << 7);
170 buttons
[0][0] = (data
[2] << 8) | ((data
[3] & 6) << 5) | (data
[7] << 3) | data
[8];
172 if (info
->rudder
) axes
[1][0] = info
->an
.axes
[0];
180 * js_as_pxl_init_corr() initializes the correction values for
184 static void __init
js_as_pxl_init_corr(struct js_corr
**corr
, int **axes
)
188 for (i
= 0; i
< 2; i
++) {
189 corr
[0][i
].type
= JS_CORR_BROKEN
;
191 corr
[0][i
].coef
[0] = axes
[0][i
] - 4;
192 corr
[0][i
].coef
[1] = axes
[0][i
] + 4;
193 corr
[0][i
].coef
[2] = (1 << 29) / (127 - 32);
194 corr
[0][i
].coef
[3] = (1 << 29) / (127 - 32);
197 corr
[0][2].type
= JS_CORR_BROKEN
;
199 corr
[0][2].coef
[0] = 127 - 4;
200 corr
[0][2].coef
[1] = 128 + 4;
201 corr
[0][2].coef
[2] = (1 << 29) / (127 - 6);
202 corr
[0][2].coef
[3] = (1 << 29) / (127 - 6);
204 for (i
= 3; i
< 7; i
++) {
205 corr
[0][i
].type
= JS_CORR_BROKEN
;
207 corr
[0][i
].coef
[0] = 0;
208 corr
[0][i
].coef
[1] = 0;
209 corr
[0][i
].coef
[2] = (1 << 29);
210 corr
[0][i
].coef
[3] = (1 << 29);
213 for (i
= 7; i
< 9; i
++) {
214 corr
[0][i
].type
= JS_CORR_BROKEN
;
215 corr
[0][i
].prec
= -1;
216 corr
[0][i
].coef
[0] = 0;
217 corr
[0][i
].coef
[1] = 0;
218 corr
[0][i
].coef
[2] = (104 << 14);
219 corr
[0][i
].coef
[3] = (104 << 14);
224 * js_as_as_init_corr() initializes the correction values for
225 * the Panther and Assassin.
228 static void __init
js_as_as_init_corr(struct js_corr
**corr
)
232 for (i
= 0; i
< 2; i
++) {
233 corr
[0][i
].type
= JS_CORR_BROKEN
;
234 corr
[0][i
].prec
= -1;
235 corr
[0][i
].coef
[0] = 0;
236 corr
[0][i
].coef
[1] = 0;
237 corr
[0][i
].coef
[2] = (104 << 14);
238 corr
[0][i
].coef
[3] = (104 << 14);
243 * js_as_rudder_init_corr() initializes the correction values for
244 * the Panther XL connected rudder.
247 static void __init
js_as_rudder_init_corr(struct js_corr
**corr
, int **axes
)
249 corr
[1][0].type
= JS_CORR_BROKEN
;
251 corr
[1][0].coef
[0] = axes
[1][0] - (axes
[1][0] >> 3);
252 corr
[1][0].coef
[1] = axes
[1][0] + (axes
[1][0] >> 3);
253 corr
[1][0].coef
[2] = (1 << 29) / (axes
[1][0] - (axes
[1][0] >> 2) + 1);
254 corr
[1][0].coef
[3] = (1 << 29) / (axes
[1][0] - (axes
[1][0] >> 2) + 1);
258 * js_as_probe() probes for A3D joysticks.
261 static struct js_port __init
*js_as_probe(int io
, int mask0
, int mask1
, struct js_port
*port
)
263 struct js_as_info iniinfo
;
264 struct js_as_info
*info
= &iniinfo
;
266 char data
[JS_AS_MAX_LENGTH
];
271 memset(info
, 0, sizeof(struct js_as_info
));
273 if (io
< 0) return port
;
275 if (check_region(io
, 1)) return port
;
277 i
= js_as_read_packet(io
, JS_AS_MAX_LENGTH
, data
);
282 if (js_as_csum(data
, i
)) return port
;
284 if (data
[0] && data
[0] <= 4) {
285 info
->mode
= data
[0];
287 request_region(io
, 1, "joystick (assassin)");
288 port
= js_register_port(port
, info
, 3, sizeof(struct js_as_info
), js_as_read
);
291 printk(KERN_WARNING
"joy-assassin: unknown joystick device detected "
292 "(io=%#x, id=%d), contact <vojtech@suse.cz>\n", io
, data
[0]);
296 udelay(JS_AS_DELAY_READ
);
298 if (info
->mode
== JS_AS_MODE_PXL
) {
299 printk(KERN_INFO
"js%d: MadCatz Panther XL at %#x\n",
300 js_register_device(port
, 0, 9, 9, "MadCatz Panther XL", THIS_MODULE
, NULL
, NULL
),
302 js_as_read(port
->info
, port
->axes
, port
->buttons
);
303 js_as_pxl_init_corr(port
->corr
, port
->axes
);
304 if (info
->an
.axes
[0] < 254) {
305 printk(KERN_INFO
"js%d: Analog rudder on MadCatz Panther XL\n",
306 js_register_device(port
, 1, 1, 0, "Analog rudder", THIS_MODULE
, NULL
, NULL
));
308 port
->axes
[1][0] = info
->an
.axes
[0];
309 js_as_rudder_init_corr(port
->corr
, port
->axes
);
314 switch (info
->mode
) {
315 case JS_AS_MODE_A3D
: name
= "FP-Gaming Assassin 3D"; break;
316 case JS_AS_MODE_PAN
: name
= "MadCatz Panther"; break;
317 case JS_AS_MODE_OEM
: name
= "OEM Assassin 3D"; break;
318 default: name
= "This cannot happen"; break;
321 printk(KERN_INFO
"js%d: %s at %#x\n",
322 js_register_device(port
, 0, 2, 3, name
, THIS_MODULE
, NULL
, NULL
),
325 js_as_as_init_corr(port
->corr
);
327 js_as_read(port
->info
, port
->axes
, port
->buttons
);
329 for (i
= u
= 0; i
< 4; i
++) if (info
->an
.axes
[i
] < 254) u
|= 1 << i
;
331 if ((numdev
= js_an_probe_devs(&info
->an
, u
, mask0
, mask1
, port
)) <= 0)
334 for (i
= 0; i
< numdev
; i
++)
335 printk(KERN_INFO
"js%d: %s on %s\n",
336 js_register_device(port
, i
+ 1, js_an_axes(i
, &info
->an
), js_an_buttons(i
, &info
->an
),
337 js_an_name(i
, &info
->an
), THIS_MODULE
, NULL
, NULL
),
338 js_an_name(i
, &info
->an
), name
);
340 js_an_decode(&info
->an
, port
->axes
+ 1, port
->buttons
+ 1);
341 js_an_init_corr(&info
->an
, port
->axes
+ 1, port
->corr
+ 1, 0);
347 int __init
js_as_setup(SETUP_PARAM
)
351 for (i
= 0; i
<= ints
[0] && i
< 24; i
++) js_as
[i
] = ints
[i
+1];
354 __setup("js_as=", js_as_setup
);
358 int init_module(void)
360 int __init
js_as_init(void)
366 for (i
= 0; (js_as
[i
*3] >= 0) && i
< 8; i
++)
367 js_as_port
= js_as_probe(js_as
[i
*3], js_as
[i
*3+1], js_as
[i
*3+2], js_as_port
);
369 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
);
371 if (js_as_port
) return 0;
374 printk(KERN_WARNING
"joy-assassin: no joysticks found\n");
381 void cleanup_module(void)
384 struct js_as_info
*info
;
387 for (i
= 0; i
< js_as_port
->ndevs
; i
++)
388 if (js_as_port
->devs
[i
])
389 js_unregister_device(js_as_port
->devs
[i
]);
390 info
= js_as_port
->info
;
391 release_region(info
->io
, 1);
392 js_as_port
= js_unregister_port(js_as_port
);