2 * joy-lightning.c Version 1.2
4 * Copyright (c) 1998-1999 Vojtech Pavlik
10 * This is a module for the Linux joystick driver, supporting
11 * PDPI Lightning 4 gamecards and analog joysticks connected
16 * This program is free software; you can redistribute it and/or modify
17 * it under the terms of the GNU General Public License as published by
18 * the Free Software Foundation; either version 2 of the License, or
19 * (at your option) any later version.
21 * This program is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU General Public License for more details.
26 * You should have received a copy of the GNU General Public License
27 * along with this program; if not, write to the Free Software
28 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
30 * Should you need to contact me, the author, you can do so either by
31 * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail:
32 * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic
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_L4_PORT 0x201
46 #define JS_L4_SELECT_ANALOG 0xa4
47 #define JS_L4_SELECT_DIGITAL 0xa5
48 #define JS_L4_SELECT_SECONDARY 0xa6
49 #define JS_L4_CMD_ID 0x80
50 #define JS_L4_CMD_GETCAL 0x92
51 #define JS_L4_CMD_SETCAL 0x93
53 #define JS_L4_BUSY 0x01
54 #define JS_L4_TIMEOUT 80 /* 80 us */
56 static struct js_port
* __initdata js_l4_port
= NULL
;
58 MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
59 MODULE_PARM(js_l4
, "2-24i");
61 static int __initdata js_l4
[] = { -1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0 };
63 #include "joy-analog.h"
71 * js_l4_wait_ready() waits for the L4 to become ready.
74 static int js_l4_wait_ready(void)
78 while ((inb(JS_L4_PORT
) & JS_L4_BUSY
) && t
> 0) t
--;
83 * js_l4_read() reads data from the Lightning 4.
86 static int js_l4_read(void *xinfo
, int **axes
, int **buttons
)
88 struct js_l4_info
*info
= xinfo
;
92 outb(JS_L4_SELECT_ANALOG
, JS_L4_PORT
);
93 outb(JS_L4_SELECT_DIGITAL
+ (info
->port
>> 2), JS_L4_PORT
);
95 if (inb(JS_L4_PORT
) & JS_L4_BUSY
) return -1;
96 outb(info
->port
& 3, JS_L4_PORT
);
98 if (js_l4_wait_ready()) return -1;
99 status
= inb(JS_L4_PORT
);
101 for (i
= 0; i
< 4; i
++)
102 if (status
& (1 << i
)) {
103 if (js_l4_wait_ready()) return -1;
104 info
->an
.axes
[i
] = inb(JS_L4_PORT
);
108 if (js_l4_wait_ready()) return -1;
109 info
->an
.buttons
= inb(JS_L4_PORT
);
112 js_an_decode(&info
->an
, axes
, buttons
);
118 * js_l4_getcal() reads the L4 with calibration values.
121 static int js_l4_getcal(int port
, int *cal
)
125 outb(JS_L4_SELECT_ANALOG
, JS_L4_PORT
);
126 outb(JS_L4_SELECT_DIGITAL
+ (port
>> 2), JS_L4_PORT
);
128 if (inb(JS_L4_PORT
) & JS_L4_BUSY
) return -1;
129 outb(JS_L4_CMD_GETCAL
, JS_L4_PORT
);
131 if (js_l4_wait_ready()) return -1;
132 if (inb(JS_L4_PORT
) != JS_L4_SELECT_DIGITAL
+ (port
>> 2)) return -1;
134 if (js_l4_wait_ready()) return -1;
135 outb(port
& 3, JS_L4_PORT
);
137 for (i
= 0; i
< 4; i
++) {
138 if (js_l4_wait_ready()) return -1;
139 cal
[i
] = inb(JS_L4_PORT
);
146 * js_l4_setcal() programs the L4 with calibration values.
149 static int js_l4_setcal(int port
, int *cal
)
153 outb(JS_L4_SELECT_ANALOG
, JS_L4_PORT
);
154 outb(JS_L4_SELECT_DIGITAL
+ (port
>> 2), JS_L4_PORT
);
156 if (inb(JS_L4_PORT
) & JS_L4_BUSY
) return -1;
157 outb(JS_L4_CMD_SETCAL
, JS_L4_PORT
);
159 if (js_l4_wait_ready()) return -1;
160 if (inb(JS_L4_PORT
) != JS_L4_SELECT_DIGITAL
+ (port
>> 2)) return -1;
162 if (js_l4_wait_ready()) return -1;
163 outb(port
& 3, JS_L4_PORT
);
165 for (i
= 0; i
< 4; i
++) {
166 if (js_l4_wait_ready()) return -1;
167 outb(cal
[i
], JS_L4_PORT
);
174 * js_l4_calibrate() calibrates the L4 for the attached device, so
175 * that the device's resistance fits into the L4's 8-bit range.
178 static void js_l4_calibrate(struct js_l4_info
*info
)
185 js_l4_getcal(info
->port
, cal
);
187 for (i
= 0; i
< 4; i
++)
188 axes
[i
] = info
->an
.axes
[i
];
190 if ((info
->an
.extensions
& JS_AN_BUTTON_PXY_X
) && !(info
->an
.extensions
& JS_AN_BUTTON_PXY_U
))
191 axes
[2] >>= 1; /* Pad button X */
193 if ((info
->an
.extensions
& JS_AN_BUTTON_PXY_Y
) && !(info
->an
.extensions
& JS_AN_BUTTON_PXY_V
))
194 axes
[3] >>= 1; /* Pad button Y */
196 if (info
->an
.extensions
& JS_AN_HAT_FCS
)
197 axes
[3] >>= 1; /* FCS hat */
199 if (((info
->an
.mask
[0] & 0xb) == 0xb) || ((info
->an
.mask
[1] & 0xb) == 0xb))
200 axes
[3] = (axes
[0] + axes
[1]) >> 1; /* Throttle */
202 for (i
= 0; i
< 4; i
++) {
203 t
= (axes
[i
] * cal
[i
]) / 100;
204 if (t
> 255) t
= 255;
205 info
->an
.axes
[i
] = (info
->an
.axes
[i
] * cal
[i
]) / t
;
209 js_l4_setcal(info
->port
, cal
);
213 * js_l4_probe() probes for joysticks on the L4 cards.
216 static struct js_port __init
*js_l4_probe(unsigned char *cards
, int l4port
, int mask0
, int mask1
, struct js_port
*port
)
218 struct js_l4_info iniinfo
;
219 struct js_l4_info
*info
= &iniinfo
;
220 int cal
[4] = {255,255,255,255};
224 if (l4port
< 0) return port
;
225 if (!cards
[(l4port
>> 2)]) return port
;
227 memset(info
, 0, sizeof(struct js_l4_info
));
230 if (cards
[l4port
>> 2] > 0x28) js_l4_setcal(info
->port
, cal
);
231 if (js_l4_read(info
, NULL
, NULL
)) return port
;
233 for (i
= u
= 0; i
< 4; i
++) if (info
->an
.axes
[i
] < 253) u
|= 1 << i
;
235 if ((numdev
= js_an_probe_devs(&info
->an
, u
, mask0
, mask1
, port
)) <= 0)
238 port
= js_register_port(port
, info
, numdev
, sizeof(struct js_l4_info
), js_l4_read
);
242 for (i
= 0; i
< numdev
; i
++)
243 printk(KERN_INFO
"js%d: %s on L4 port %d\n",
244 js_register_device(port
, i
, js_an_axes(i
, &info
->an
), js_an_buttons(i
, &info
->an
),
245 js_an_name(i
, &info
->an
), THIS_MODULE
, NULL
, NULL
),
246 js_an_name(i
, &info
->an
), info
->port
);
248 js_l4_calibrate(info
);
249 js_l4_read(info
, port
->axes
, port
->buttons
);
250 js_an_init_corr(&info
->an
, port
->axes
, port
->corr
, 0);
256 * js_l4_card_probe() probes for presence of the L4 card(s).
259 static void __init
js_l4_card_probe(unsigned char *cards
)
262 unsigned char rev
= 0;
264 if (check_region(JS_L4_PORT
, 1)) return;
266 for (i
= 0; i
< 2; i
++) {
268 outb(JS_L4_SELECT_ANALOG
, JS_L4_PORT
);
269 outb(JS_L4_SELECT_DIGITAL
+ i
, JS_L4_PORT
); /* Select card 0-1 */
271 if (inb(JS_L4_PORT
) & JS_L4_BUSY
) continue;
272 outb(JS_L4_CMD_ID
, JS_L4_PORT
); /* Get card ID & rev */
274 if (js_l4_wait_ready()) continue;
275 if (inb(JS_L4_PORT
) != JS_L4_SELECT_DIGITAL
+ i
) continue;
277 if (js_l4_wait_ready()) continue;
278 if (inb(JS_L4_PORT
) != JS_L4_ID
) continue;
280 if (js_l4_wait_ready()) continue;
281 rev
= inb(JS_L4_PORT
);
285 printk(KERN_INFO
"js: PDPI Lightning 4 %s card (ports %d-%d) firmware v%d.%d at %#x\n",
286 i
? "secondary" : "primary", (i
<< 2), (i
<< 2) + 3, rev
>> 4, rev
& 0xf, JS_L4_PORT
);
292 int __init
js_l4_setup(SETUP_PARAM
)
296 for (i
= 0; i
<= ints
[0] && i
< 24; i
++) js_l4
[i
] = ints
[i
+1];
299 __setup("js_l4=", js_l4_setup
);
303 int init_module(void)
305 int __init
js_l4_init(void)
309 unsigned char cards
[2] = {0, 0};
311 js_l4_card_probe(cards
);
314 for (i
= 0; (js_l4
[i
*3] >= 0) && i
< 8; i
++)
315 js_l4_port
= js_l4_probe(cards
, js_l4
[i
*3], js_l4
[i
*3+1], js_l4
[i
*3+2], js_l4_port
);
317 for (i
= 0; i
< 8; i
++)
318 js_l4_port
= js_l4_probe(cards
, i
, 0, 0, js_l4_port
);
323 printk(KERN_WARNING
"joy-lightning: no joysticks found\n");
328 request_region(JS_L4_PORT
, 1, "joystick (lightning)");
334 void cleanup_module(void)
337 int cal
[4] = {59, 59, 59, 59};
338 struct js_l4_info
*info
;
341 for (i
= 0; i
< js_l4_port
->ndevs
; i
++)
342 if (js_l4_port
->devs
[i
])
343 js_unregister_device(js_l4_port
->devs
[i
]);
344 info
= js_l4_port
->info
;
345 js_l4_setcal(info
->port
, cal
);
346 js_l4_port
= js_unregister_port(js_l4_port
);
348 outb(JS_L4_SELECT_ANALOG
, JS_L4_PORT
);
349 release_region(JS_L4_PORT
, 1);