File: /usr/src/linux/drivers/char/joystick/lightning.c

1     /*
2      * $Id: lightning.c,v 1.13 2001/04/26 10:24:46 vojtech Exp $
3      *
4      *  Copyright (c) 1998-2001 Vojtech Pavlik
5      *
6      *  Sponsored by SuSE
7      */
8     
9     /*
10      * PDPI Lightning 4 gamecard driver for Linux.
11      */
12     
13     /*
14      * This program is free software; you can redistribute it and/or modify
15      * it under the terms of the GNU General Public License as published by
16      * the Free Software Foundation; either version 2 of the License, or 
17      * (at your option) any later version.
18      * 
19      * This program is distributed in the hope that it will be useful,
20      * but WITHOUT ANY WARRANTY; without even the implied warranty of
21      * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22      * GNU General Public License for more details.
23      * 
24      * You should have received a copy of the GNU General Public License
25      * along with this program; if not, write to the Free Software
26      * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27      * 
28      * Should you need to contact me, the author, you can do so either by
29      * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail:
30      * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic
31      */
32     
33     #include <asm/io.h>
34     #include <linux/delay.h>
35     #include <linux/errno.h>
36     #include <linux/ioport.h>
37     #include <linux/kernel.h>
38     #include <linux/module.h>
39     #include <linux/init.h>
40     #include <linux/gameport.h>
41     #include <linux/slab.h>
42     
43     #define L4_PORT			0x201
44     #define L4_SELECT_ANALOG	0xa4
45     #define L4_SELECT_DIGITAL	0xa5
46     #define L4_SELECT_SECONDARY	0xa6
47     #define L4_CMD_ID		0x80
48     #define L4_CMD_GETCAL		0x92
49     #define L4_CMD_SETCAL		0x93
50     #define L4_ID			0x04
51     #define L4_BUSY			0x01
52     #define L4_TIMEOUT		80	/* 80 us */
53     
54     MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
55     MODULE_LICENSE("GPL");
56     
57     struct l4 {
58     	struct gameport gameport;
59     	unsigned char port;
60     } *l4_port[8];
61     
62     /*
63      * l4_wait_ready() waits for the L4 to become ready.
64      */
65     
66     static int l4_wait_ready(void)
67     {
68     	unsigned int t;
69     	t = L4_TIMEOUT;
70     	while ((inb(L4_PORT) & L4_BUSY) && t > 0) t--;
71     	return -(t<=0);
72     }
73     
74     /*
75      * l4_cooked_read() reads data from the Lightning 4.
76      */
77     
78     static int l4_cooked_read(struct gameport *gameport, int *axes, int *buttons)
79     {
80     	struct l4 *l4 = gameport->private;
81     	unsigned char status;
82     	int i, result = -1;
83     
84     	outb(L4_SELECT_ANALOG, L4_PORT);
85     	outb(L4_SELECT_DIGITAL + (l4->port >> 2), L4_PORT);
86     
87     	if (inb(L4_PORT) & L4_BUSY) goto fail;
88     	outb(l4->port & 3, L4_PORT);
89     
90     	if (l4_wait_ready()) goto fail;
91     	status = inb(L4_PORT);
92     
93     	for (i = 0; i < 4; i++)
94     		if (status & (1 << i)) {
95     			if (l4_wait_ready()) goto fail;
96     			axes[i] = inb(L4_PORT);
97     			if (axes[i] > 252) axes[i] = -1;
98     		}
99     
100     	if (status & 0x10) {
101     		if (l4_wait_ready()) goto fail;
102     		*buttons = inb(L4_PORT) & 0x0f;
103     	}
104     
105     	result = 0;
106     
107     fail:	outb(L4_SELECT_ANALOG, L4_PORT);	
108     	return result;
109     }
110     
111     static int l4_open(struct gameport *gameport, int mode)
112     {
113     	struct l4 *l4 = gameport->private;
114             if (l4->port != 0 && mode != GAMEPORT_MODE_COOKED)
115     		return -1;
116     	outb(L4_SELECT_ANALOG, L4_PORT);
117     	return 0;
118     }
119     
120     /*
121      * l4_getcal() reads the L4 with calibration values.
122      */
123     
124     static int l4_getcal(int port, int *cal)
125     {
126     	int i, result = -1;
127     	
128     	outb(L4_SELECT_ANALOG, L4_PORT);
129     	outb(L4_SELECT_DIGITAL + (port >> 2), L4_PORT);
130     
131     	if (inb(L4_PORT) & L4_BUSY) goto fail;
132     	outb(L4_CMD_GETCAL, L4_PORT);
133     
134     	if (l4_wait_ready()) goto fail;
135     	if (inb(L4_PORT) != L4_SELECT_DIGITAL + (port >> 2)) goto fail;
136     
137     	if (l4_wait_ready()) goto fail;
138             outb(port & 3, L4_PORT);
139     
140     	for (i = 0; i < 4; i++) {
141     		if (l4_wait_ready()) goto fail;
142     		cal[i] = inb(L4_PORT);
143     	}
144     
145     	result = 0;
146     
147     fail:	outb(L4_SELECT_ANALOG, L4_PORT);
148     	return result;
149     }
150     
151     /*
152      * l4_setcal() programs the L4 with calibration values.
153      */
154     
155     static int l4_setcal(int port, int *cal)
156     {
157     	int i, result = -1;
158     
159     	outb(L4_SELECT_ANALOG, L4_PORT);
160     	outb(L4_SELECT_DIGITAL + (port >> 2), L4_PORT);
161     
162     	if (inb(L4_PORT) & L4_BUSY) goto fail;
163     	outb(L4_CMD_SETCAL, L4_PORT);
164     
165     	if (l4_wait_ready()) goto fail;
166     	if (inb(L4_PORT) != L4_SELECT_DIGITAL + (port >> 2)) goto fail;
167     
168     	if (l4_wait_ready()) goto fail;
169             outb(port & 3, L4_PORT);
170     
171     	for (i = 0; i < 4; i++) {
172     		if (l4_wait_ready()) goto fail;
173     		outb(cal[i], L4_PORT);
174     	}
175     
176     	result = 0;
177     
178     fail:	outb(L4_SELECT_ANALOG, L4_PORT);
179     	return result;
180     }
181     
182     /*
183      * l4_calibrate() calibrates the L4 for the attached device, so
184      * that the device's resistance fits into the L4's 8-bit range.
185      */
186     
187     static int l4_calibrate(struct gameport *gameport, int *axes, int *max)
188     {
189     	int i, t;
190     	int cal[4];
191     	struct l4 *l4 = gameport->private;
192     
193     	if (l4_getcal(l4->port, cal))
194     		return -1;
195     
196     	for (i = 0; i < 4; i++) {
197     		t = (max[i] * cal[i]) / 200;
198     		t = (t < 1) ? 1 : ((t > 255) ? 255 : t);
199     		axes[i] = (axes[i] < 0) ? -1 : (axes[i] * cal[i]) / t;
200     		axes[i] = (axes[i] > 252) ? 252 : axes[i];
201     		cal[i] = t;
202     	}
203     
204     	if (l4_setcal(l4->port, cal))
205     		return -1;
206     
207     	return 0;
208     }
209     	
210     int __init l4_init(void)
211     {
212     	int cal[4] = {255,255,255,255};
213     	int i, j, rev, cards = 0;
214     	struct gameport *gameport;
215     	struct l4 *l4;
216     
217     	if (!request_region(L4_PORT, 1, "lightning"))
218     		return -1;
219     
220     	for (i = 0; i < 2; i++) {
221     
222     		outb(L4_SELECT_ANALOG, L4_PORT);
223     		outb(L4_SELECT_DIGITAL + i, L4_PORT);
224     
225     		if (inb(L4_PORT) & L4_BUSY) continue;
226     		outb(L4_CMD_ID, L4_PORT);
227     
228     		if (l4_wait_ready()) continue;
229     		if (inb(L4_PORT) != L4_SELECT_DIGITAL + i) continue;
230     
231     		if (l4_wait_ready()) continue;
232     		if (inb(L4_PORT) != L4_ID) continue;
233     
234     		if (l4_wait_ready()) continue;
235     		rev = inb(L4_PORT);
236     
237     		if (!rev) continue;
238     
239     		if (!(l4_port[i * 4] = kmalloc(sizeof(struct l4) * 4, GFP_KERNEL))) {
240     			printk(KERN_ERR "lightning: Out of memory allocating ports.\n");
241     			continue;
242     		}
243     		memset(l4_port[i * 4], 0, sizeof(struct l4) * 4);
244     
245     		for (j = 0; j < 4; j++) {
246     
247     			l4 = l4_port[i * 4 + j] = l4_port[i * 4] + j;
248     			l4->port = i * 4 + j;
249     
250     			gameport = &l4->gameport;
251     			gameport->private = l4;
252     			gameport->open = l4_open;
253     			gameport->cooked_read = l4_cooked_read;
254     			gameport->calibrate = l4_calibrate;
255     
256     			if (!i && !j)
257     				gameport->io = L4_PORT;
258     
259     			if (rev > 0x28)		/* on 2.9+ the setcal command works correctly */
260     				l4_setcal(l4->port, cal);
261     			
262     			gameport_register_port(gameport);
263     		}
264     
265     		printk(KERN_INFO "gameport%d,%d,%d,%d: PDPI Lightning 4 %s card v%d.%d at %#x\n",
266     			l4_port[i * 4 + 0]->gameport.number, l4_port[i * 4 + 1]->gameport.number, 
267     			l4_port[i * 4 + 2]->gameport.number, l4_port[i * 4 + 3]->gameport.number, 
268     			i ? "secondary" : "primary", rev >> 4, rev, L4_PORT);
269     
270     		cards++;
271     	}
272     
273     	outb(L4_SELECT_ANALOG, L4_PORT);
274     
275     	if (!cards) {
276     		release_region(L4_PORT, 1);
277     		return -1;
278     	}
279     
280     	return 0;
281     }
282     
283     void __init l4_exit(void)
284     {
285     	int i;
286     	int cal[4] = {59, 59, 59, 59};
287     
288     	for (i = 0; i < 8; i++)
289     		if (l4_port[i]) {
290     			l4_setcal(l4_port[i]->port, cal);
291     			gameport_unregister_port(&l4_port[i]->gameport);
292     		}
293     	outb(L4_SELECT_ANALOG, L4_PORT);
294     	release_region(L4_PORT, 1);
295     }
296     
297     module_init(l4_init);
298     module_exit(l4_exit);
299