Import 2.4.0-test2pre6
[davej-history.git] / drivers / char / joystick / joy-assassin.c
blob469c6c8cee51df575ad306b8cd8b5dcdac6cf70a
1 /*
2 * joy-assassin.c Version 1.2
4 * Copyright (c) 1998-1999 Vojtech Pavlik
6 * Sponsored by SuSE
7 */
9 /*
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
34 #include <asm/io.h>
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"
64 struct js_as_info {
65 int io;
66 char mode;
67 char rudder;
68 struct js_an_info an;
72 * js_as_read_packet() reads an Assassin 3D packet.
75 static int js_as_read_packet(int io, int length, char *data)
77 unsigned char u, v;
78 int i;
79 unsigned int t, p;
80 unsigned long flags;
82 i = 0;
84 __save_flags(flags);
85 __cli();
87 outb(0xff,io);
88 v = inb(io);
89 t = p = JS_AS_MAX_START;
91 while (t > 0 && i < length) {
92 t--;
93 u = v; v = inb(io);
94 if (~v & u & 0x10) {
95 data[i++] = v >> 5;
96 p = t = (p - t) << 3;
100 __restore_flags(flags);
102 return i;
106 * js_as_csum() computes checksum of triplet packet
109 static int js_as_csum(char *data, int count)
111 int i, csum = 0;
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) {
127 case JS_AS_MODE_A3D:
128 case JS_AS_MODE_OEM:
129 case JS_AS_MODE_PAN:
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);
149 return 0;
151 case JS_AS_MODE_PXL:
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];
174 return 0;
176 return -1;
180 * js_as_pxl_init_corr() initializes the correction values for
181 * the Panther XL.
184 static void __init js_as_pxl_init_corr(struct js_corr **corr, int **axes)
186 int i;
188 for (i = 0; i < 2; i++) {
189 corr[0][i].type = JS_CORR_BROKEN;
190 corr[0][i].prec = 0;
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;
198 corr[0][2].prec = 0;
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;
206 corr[0][i].prec = 0;
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)
230 int i;
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;
250 corr[1][0].prec = 0;
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;
265 char *name;
266 char data[JS_AS_MAX_LENGTH];
267 unsigned char u;
268 int i;
269 int numdev;
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);
279 printk("%d\n", i);
281 if (!i) return port;
282 if (js_as_csum(data, i)) return port;
284 if (data[0] && data[0] <= 4) {
285 info->mode = data[0];
286 info->io = io;
287 request_region(io, 1, "joystick (assassin)");
288 port = js_register_port(port, info, 3, sizeof(struct js_as_info), js_as_read);
289 info = port->info;
290 } else {
291 printk(KERN_WARNING "joy-assassin: unknown joystick device detected "
292 "(io=%#x, id=%d), contact <vojtech@suse.cz>\n", io, data[0]);
293 return port;
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),
301 info->io);
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));
307 info->rudder = 1;
308 port->axes[1][0] = info->an.axes[0];
309 js_as_rudder_init_corr(port->corr, port->axes);
311 return port;
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),
323 name, info->io);
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)
332 return port;
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);
343 return port;
346 #ifndef MODULE
347 int __init js_as_setup(SETUP_PARAM)
349 int i;
350 SETUP_PARSE(24);
351 for (i = 0; i <= ints[0] && i < 24; i++) js_as[i] = ints[i+1];
352 return 1;
354 __setup("js_as=", js_as_setup);
355 #endif
357 #ifdef MODULE
358 int init_module(void)
359 #else
360 int __init js_as_init(void)
361 #endif
363 int i;
365 if (js_as[0] >= 0) {
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);
368 } else {
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;
373 #ifdef MODULE
374 printk(KERN_WARNING "joy-assassin: no joysticks found\n");
375 #endif
377 return -ENODEV;
380 #ifdef MODULE
381 void cleanup_module(void)
383 int i;
384 struct js_as_info *info;
386 while (js_as_port) {
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);
396 #endif