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