* add p cc
[mascara-docs.git] / i386 / linux / linux-2.3.21 / drivers / char / joystick / joy-sidewinder.c
blob3fb0d8cef481f7e1d87afa0b3b5334779811a7a4
1 /*
2 * joy-sidewinder.c Version 1.2
4 * Copyright (c) 1998 Vojtech Pavlik
5 */
7 /*
8 * This is a module for the Linux joystick driver, supporting
9 * Microsoft SideWinder digital joystick family.
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
32 #include <asm/io.h>
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_SW_MAX_START 250
43 #define JS_SW_MIN_STROBE 25
44 #define JS_SW_EXT_STROBE 45
45 #define JS_SW_MIN_TIME 1500
46 #define JS_SW_MAX_TIME 4000
48 #define JS_SW_MAX_LENGTH 72
50 #define JS_SW_MODE_3DP 1
51 #define JS_SW_MODE_PP 2
52 #define JS_SW_MODE_GP 3
54 static int js_sw_port_list[] __initdata = {0x201, 0};
55 static struct js_port* js_sw_port __initdata = NULL;
57 static struct {
58 int x;
59 int y;
60 } js_sw_hat_to_axis[] = {{ 0, 0}, { 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}};
62 struct js_sw_info {
63 int io;
64 unsigned char mode;
65 unsigned char number;
66 unsigned char optimize;
70 * js_sw_init_digital() switches a SideWinder into digital mode.
73 static void __init js_sw_init_digital(int io)
75 unsigned int t;
76 unsigned int timeout = (js_time_speed * JS_SW_MAX_TIME) >> 10;
77 int delays[] = {140, 140+726, 140+300, 0};
78 int i = 0;
79 unsigned long flags;
81 __save_flags(flags);
82 __cli();
83 do {
84 outb(0xff,io);
85 t = js_get_time();
86 while ((inb(io) & 1) && (js_delta(js_get_time(),t) < timeout));
87 udelay(delays[i]);
88 } while (delays[i++]);
89 __restore_flags(flags);
91 for (i = 0; i < 4; i++) {
92 udelay(300);
93 outb(0xff, io);
96 return;
100 * js_sw_read_packet() reads a SideWinder packet.
103 static int js_sw_read_packet(int io, int l1, int l2, int strobe, __u64 *data)
105 static unsigned char buf[JS_SW_MAX_LENGTH];
106 unsigned char u, v;
107 int i;
108 unsigned long flags;
109 unsigned int t, t1;
111 int length = l1 < l2 ? l2 : l1;
112 int start = (js_time_speed * JS_SW_MAX_START) >> 10;
113 strobe = (js_time_speed * strobe) >> 10;
115 i = 0;
117 __save_flags(flags);
118 __cli();
119 outb(0xff,io);
121 v = inb(io);
122 t = js_get_time();
124 do {
125 u = v;
126 v = inb(io);
127 t1 = js_get_time();
128 } while (!((u ^ v) & u & 0x10) && js_delta(t1, t) < start);
130 t = t1;
132 do {
133 v = inb(io);
134 t1 = js_get_time();
135 if ((u ^ v) & v & 0x10) {
136 buf[i++] = v >> 5;
137 t = t1;
139 u = v;
140 } while (i < length && js_delta(t1,t) < strobe);
142 __restore_flags(flags);
144 *data = 0;
146 if (i == l1) {
147 t = i > 64 ? 64 : i;
148 for (i = 0; i < t; i++)
149 *data |= (__u64) (buf[i] & 1) << i;
150 return t;
152 if (i == l2) {
153 t = i > 22 ? 22 : i;
154 for (i = 0; i < t; i++)
155 *data |= (__u64) buf[i] << (3 * i);
156 return t * 3;
159 return i;
163 * js_sw_parity computes parity of __u64
166 static int js_sw_parity(__u64 t)
168 t ^= t >> 32;
169 t ^= t >> 16;
170 t ^= t >> 8;
171 t ^= t >> 4;
172 t ^= t >> 2;
173 t ^= t >> 1;
174 return t & 1;
178 * js_sw_csum() computes checksum of nibbles in __u64
181 static int js_sw_csum(__u64 t)
183 char sum = 0;
184 while (t) {
185 sum += t & 0xf;
186 t >>= 4;
188 return sum & 0xf;
192 * js_sw_read() reads and analyzes SideWinder joystick data.
195 static int js_sw_read(void *xinfo, int **axes, int **buttons)
197 struct js_sw_info *info = xinfo;
198 __u64 data;
199 int hat, i;
201 switch (info->mode) {
203 case JS_SW_MODE_3DP:
205 if (info->optimize) {
206 i = js_sw_read_packet(info->io, -1, 22, JS_SW_EXT_STROBE, &data);
207 } else {
208 i = js_sw_read_packet(info->io, 64, 66, JS_SW_EXT_STROBE, &data);
209 if (i == 198) info->optimize = 1;
212 if (i < 60) {
213 js_sw_init_digital(info->io);
214 info->optimize = 0;
215 return -1;
218 if (((data & 0x8080808080808080ULL) ^ 0x80) || js_sw_csum(data) ||
219 (hat = ((data >> 3) & 0x08) | ((data >> 60) & 0x07)) > 8) {
220 info->optimize = 0;
221 return -1;
223 axes[0][0] = ((data << 4) & 0x380) | ((data >> 16) & 0x07f);
224 axes[0][1] = ((data << 7) & 0x380) | ((data >> 24) & 0x07f);
225 axes[0][2] = ((data >> 28) & 0x180) | ((data >> 40) & 0x07f);
226 axes[0][3] = ((data >> 25) & 0x380) | ((data >> 48) & 0x07f);
227 axes[0][4] = js_sw_hat_to_axis[hat].x;
228 axes[0][5] = js_sw_hat_to_axis[hat].y;
229 buttons[0][0] = ((~data >> 31) & 0x80) | ((~data >> 8) & 0x7f);
231 return 0;
233 case JS_SW_MODE_PP:
235 if (js_sw_read_packet(info->io, 48, 16, JS_SW_EXT_STROBE, &data) != 48) return -1;
236 if (!js_sw_parity(data) || (hat = (data >> 42) & 0xf) > 8) return -1;
238 axes[0][0] = (data >> 9) & 0x3ff;
239 axes[0][1] = (data >> 19) & 0x3ff;
240 axes[0][2] = (data >> 29) & 0x07f;
241 axes[0][3] = (data >> 36) & 0x03f;
242 axes[0][4] = js_sw_hat_to_axis[hat].x;
243 axes[0][5] = js_sw_hat_to_axis[hat].y;
244 buttons[0][0] = ~data & 0x1ff;
246 return 0;
248 case JS_SW_MODE_GP:
250 if (js_sw_read_packet(info->io, 15 * info->number, 5 * info->number,
251 JS_SW_EXT_STROBE, &data) != 15 * info->number) return -1;
252 if (js_sw_parity(data)) return -1;
254 for (i = 0; i < info->number; i++) {
255 axes[i][0] = ((data >> 3) & 1) - ((data >> 2) & 1);
256 axes[i][1] = ( data & 1) - ((data >> 1) & 1);
257 buttons[i][0] = (~data >> 4) & 0x3ff;
258 data >>= 15;
261 return 0;
263 default:
264 return -1;
269 * js_sw_open() is a callback from the file open routine.
272 static int js_sw_open(struct js_dev *jd)
274 MOD_INC_USE_COUNT;
275 return 0;
279 * js_sw_close() is a callback from the file release routine.
282 static int js_sw_close(struct js_dev *jd)
284 MOD_DEC_USE_COUNT;
285 return 0;
289 * js_sw_init_corr() initializes the correction values for
290 * SideWinders.
293 static void __init js_sw_init_corr(int num_axes, int mode, int number, struct js_corr **corr)
295 int i, j;
297 for (i = 0; i < number; i++) {
299 for (j = 0; j < num_axes; j++) {
300 corr[i][j].type = JS_CORR_BROKEN;
301 corr[i][j].prec = 8;
302 corr[i][j].coef[0] = 511 - 32;
303 corr[i][j].coef[1] = 512 + 32;
304 corr[i][j].coef[2] = (1 << 29) / (511 - 32);
305 corr[i][j].coef[3] = (1 << 29) / (511 - 32);
308 switch (mode) {
310 case JS_SW_MODE_3DP:
312 corr[i][2].type = JS_CORR_BROKEN;
313 corr[i][2].prec = 4;
314 corr[i][2].coef[0] = 255 - 16;
315 corr[i][2].coef[1] = 256 + 16;
316 corr[i][2].coef[2] = (1 << 29) / (255 - 16);
317 corr[i][2].coef[3] = (1 << 29) / (255 - 16);
319 j = 4;
321 break;
323 case JS_SW_MODE_PP:
325 corr[i][2].type = JS_CORR_BROKEN;
326 corr[i][2].prec = 1;
327 corr[i][2].coef[0] = 63 - 4;
328 corr[i][2].coef[1] = 64 + 4;
329 corr[i][2].coef[2] = (1 << 29) / (63 - 4);
330 corr[i][2].coef[3] = (1 << 29) / (63 - 4);
332 corr[i][3].type = JS_CORR_BROKEN;
333 corr[i][3].prec = 0;
334 corr[i][3].coef[0] = 31 - 2;
335 corr[i][3].coef[1] = 32 + 2;
336 corr[i][3].coef[2] = (1 << 29) / (31 - 2);
337 corr[i][3].coef[3] = (1 << 29) / (31 - 2);
339 j = 4;
341 break;
343 default:
345 j = 0;
349 for (; j < num_axes; j++) {
350 corr[i][j].type = JS_CORR_BROKEN;
351 corr[i][j].prec = 0;
352 corr[i][j].coef[0] = 0;
353 corr[i][j].coef[1] = 0;
354 corr[i][j].coef[2] = (1 << 29);
355 corr[i][j].coef[3] = (1 << 29);
361 * js_sw_probe() probes for SideWinder type joysticks.
364 static struct js_port __init *js_sw_probe(int io, struct js_port *port)
366 struct js_sw_info info;
367 char *name;
368 int i, j, axes, buttons;
369 __u64 data;
370 unsigned char u;
373 if (check_region(io, 1)) return port;
374 if (((u = inb(io)) & 3) == 3) return port;
375 outb(0xff,io);
376 if (!((inb(io) ^ u) & ~u & 0xf)) return port;
378 i = js_sw_read_packet(io, JS_SW_MAX_LENGTH, -1, JS_SW_EXT_STROBE, &data);
380 if (!i) {
381 udelay(JS_SW_MIN_TIME);
382 js_sw_init_digital(io);
383 udelay(JS_SW_MAX_TIME);
384 i = js_sw_read_packet(io, JS_SW_MAX_LENGTH, -1, JS_SW_EXT_STROBE, &data);
387 switch (i) {
388 case 0:
389 return port;
390 case 5:
391 case 10:
392 case 15:
393 case 20:
394 case 30:
395 case 45:
396 case 60:
397 info.mode = JS_SW_MODE_GP;
398 outb(0xff,io); /* Kick into 3-bit mode */
399 udelay(JS_SW_MAX_TIME);
400 i = js_sw_read_packet(io, 60, -1, JS_SW_EXT_STROBE, &data); /* Get total length */
401 udelay(JS_SW_MIN_TIME);
402 j = js_sw_read_packet(io, 15, -1, JS_SW_MIN_STROBE, &data); /* Get subpacket length */
403 if (!i || !j) {
404 printk(KERN_WARNING "joy-sidewinder: SideWinder GamePad detected (%d,%d),"
405 " but not idenfitied.\n", i, j);
406 return port;
408 info.number = i / j;
409 axes = 2; buttons = 10; name = "SideWinder GamePad";
410 break;
411 case 16:
412 case 48:
413 info.mode = JS_SW_MODE_PP; info.number = 1;
414 axes = 6; buttons = 9; name = "SideWinder Precision Pro";
415 break;
416 case 64:
417 case 66:
418 info.mode = JS_SW_MODE_3DP; info.number = 1; info.optimize = 0;
419 axes = 6; buttons = 8; name = "SideWinder 3D Pro";
420 break;
421 case 72:
422 return port;
423 default:
424 printk(KERN_WARNING "joy-sidewinder: unknown joystick device detected "
425 "(io=%#x, count=%d, data=0x%08x%08x), contact <vojtech@ucw.cz>\n",
426 io, i, (int)(data >> 32), (int)(data & 0xffffffff));
427 return port;
430 info.io = io;
432 request_region(io, 1, "joystick (sidewinder)");
433 port = js_register_port(port, &info, info.number, sizeof(struct js_sw_info), js_sw_read);
434 for (i = 0; i < info.number; i++)
435 printk(KERN_INFO "js%d: %s at %#x\n",
436 js_register_device(port, i, axes, buttons, name, js_sw_open, js_sw_close), name, io);
437 js_sw_init_corr(axes, info.mode, info.number, port->corr);
439 return port;
442 #ifdef MODULE
443 int init_module(void)
444 #else
445 int __init js_sw_init(void)
446 #endif
448 int *p;
450 for (p = js_sw_port_list; *p; p++) js_sw_port = js_sw_probe(*p, js_sw_port);
451 if (js_sw_port) return 0;
453 #ifdef MODULE
454 printk(KERN_WARNING "joy-sidewinder: no joysticks found\n");
455 #endif
457 return -ENODEV;
460 #ifdef MODULE
461 void cleanup_module(void)
463 int i;
464 struct js_sw_info *info;
466 while (js_sw_port != NULL) {
467 for (i = 0; i < js_sw_port->ndevs; i++)
468 if (js_sw_port->devs[i] != NULL)
469 js_unregister_device(js_sw_port->devs[i]);
470 info = js_sw_port->info;
471 release_region(info->io, 1);
472 js_sw_port = js_unregister_port(js_sw_port);
476 #endif