diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/input/gameport/ns558.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/input/gameport/ns558.c')
-rw-r--r-- | drivers/input/gameport/ns558.c | 291 |
1 files changed, 291 insertions, 0 deletions
diff --git a/drivers/input/gameport/ns558.c b/drivers/input/gameport/ns558.c new file mode 100644 index 000000000000..7c5c6318eeb9 --- /dev/null +++ b/drivers/input/gameport/ns558.c | |||
@@ -0,0 +1,291 @@ | |||
1 | /* | ||
2 | * $Id: ns558.c,v 1.43 2002/01/24 19:23:21 vojtech Exp $ | ||
3 | * | ||
4 | * Copyright (c) 1999-2001 Vojtech Pavlik | ||
5 | * Copyright (c) 1999 Brian Gerst | ||
6 | */ | ||
7 | |||
8 | /* | ||
9 | * NS558 based standard IBM game port driver for Linux | ||
10 | */ | ||
11 | |||
12 | /* | ||
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. | ||
17 | * | ||
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. | ||
22 | * | ||
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 | ||
26 | * | ||
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, Simunkova 1594, Prague 8, 182 00 Czech Republic | ||
30 | */ | ||
31 | |||
32 | #include <asm/io.h> | ||
33 | |||
34 | #include <linux/module.h> | ||
35 | #include <linux/ioport.h> | ||
36 | #include <linux/config.h> | ||
37 | #include <linux/init.h> | ||
38 | #include <linux/delay.h> | ||
39 | #include <linux/gameport.h> | ||
40 | #include <linux/slab.h> | ||
41 | #include <linux/pnp.h> | ||
42 | |||
43 | MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); | ||
44 | MODULE_DESCRIPTION("Classic gameport (ISA/PnP) driver"); | ||
45 | MODULE_LICENSE("GPL"); | ||
46 | |||
47 | static int ns558_isa_portlist[] = { 0x201, 0x200, 0x202, 0x203, 0x204, 0x205, 0x207, 0x209, | ||
48 | 0x20b, 0x20c, 0x20e, 0x20f, 0x211, 0x219, 0x101, 0 }; | ||
49 | |||
50 | struct ns558 { | ||
51 | int type; | ||
52 | int io; | ||
53 | int size; | ||
54 | struct pnp_dev *dev; | ||
55 | struct gameport *gameport; | ||
56 | struct list_head node; | ||
57 | }; | ||
58 | |||
59 | static LIST_HEAD(ns558_list); | ||
60 | |||
61 | /* | ||
62 | * ns558_isa_probe() tries to find an isa gameport at the | ||
63 | * specified address, and also checks for mirrors. | ||
64 | * A joystick must be attached for this to work. | ||
65 | */ | ||
66 | |||
67 | static int ns558_isa_probe(int io) | ||
68 | { | ||
69 | int i, j, b; | ||
70 | unsigned char c, u, v; | ||
71 | struct ns558 *ns558; | ||
72 | struct gameport *port; | ||
73 | |||
74 | /* | ||
75 | * No one should be using this address. | ||
76 | */ | ||
77 | |||
78 | if (!request_region(io, 1, "ns558-isa")) | ||
79 | return -EBUSY; | ||
80 | |||
81 | /* | ||
82 | * We must not be able to write arbitrary values to the port. | ||
83 | * The lower two axis bits must be 1 after a write. | ||
84 | */ | ||
85 | |||
86 | c = inb(io); | ||
87 | outb(~c & ~3, io); | ||
88 | if (~(u = v = inb(io)) & 3) { | ||
89 | outb(c, io); | ||
90 | release_region(io, 1); | ||
91 | return -ENODEV; | ||
92 | } | ||
93 | /* | ||
94 | * After a trigger, there must be at least some bits changing. | ||
95 | */ | ||
96 | |||
97 | for (i = 0; i < 1000; i++) v &= inb(io); | ||
98 | |||
99 | if (u == v) { | ||
100 | outb(c, io); | ||
101 | release_region(io, 1); | ||
102 | return -ENODEV; | ||
103 | } | ||
104 | msleep(3); | ||
105 | /* | ||
106 | * After some time (4ms) the axes shouldn't change anymore. | ||
107 | */ | ||
108 | |||
109 | u = inb(io); | ||
110 | for (i = 0; i < 1000; i++) | ||
111 | if ((u ^ inb(io)) & 0xf) { | ||
112 | outb(c, io); | ||
113 | release_region(io, 1); | ||
114 | return -ENODEV; | ||
115 | } | ||
116 | /* | ||
117 | * And now find the number of mirrors of the port. | ||
118 | */ | ||
119 | |||
120 | for (i = 1; i < 5; i++) { | ||
121 | |||
122 | release_region(io & (-1 << (i - 1)), (1 << (i - 1))); | ||
123 | |||
124 | if (!request_region(io & (-1 << i), (1 << i), "ns558-isa")) | ||
125 | break; /* Don't disturb anyone */ | ||
126 | |||
127 | outb(0xff, io & (-1 << i)); | ||
128 | for (j = b = 0; j < 1000; j++) | ||
129 | if (inb(io & (-1 << i)) != inb((io & (-1 << i)) + (1 << i) - 1)) b++; | ||
130 | msleep(3); | ||
131 | |||
132 | if (b > 300) { /* We allow 30% difference */ | ||
133 | release_region(io & (-1 << i), (1 << i)); | ||
134 | break; | ||
135 | } | ||
136 | } | ||
137 | |||
138 | i--; | ||
139 | |||
140 | if (i != 4) { | ||
141 | if (!request_region(io & (-1 << i), (1 << i), "ns558-isa")) | ||
142 | return -EBUSY; | ||
143 | } | ||
144 | |||
145 | ns558 = kcalloc(1, sizeof(struct ns558), GFP_KERNEL); | ||
146 | port = gameport_allocate_port(); | ||
147 | if (!ns558 || !port) { | ||
148 | printk(KERN_ERR "ns558: Memory allocation failed.\n"); | ||
149 | release_region(io & (-1 << i), (1 << i)); | ||
150 | kfree(ns558); | ||
151 | gameport_free_port(port); | ||
152 | return -ENOMEM; | ||
153 | } | ||
154 | |||
155 | memset(ns558, 0, sizeof(struct ns558)); | ||
156 | ns558->io = io; | ||
157 | ns558->size = 1 << i; | ||
158 | ns558->gameport = port; | ||
159 | |||
160 | port->io = io; | ||
161 | gameport_set_name(port, "NS558 ISA Gameport"); | ||
162 | gameport_set_phys(port, "isa%04x/gameport0", io & (-1 << i)); | ||
163 | |||
164 | gameport_register_port(port); | ||
165 | |||
166 | list_add(&ns558->node, &ns558_list); | ||
167 | |||
168 | return 0; | ||
169 | } | ||
170 | |||
171 | #ifdef CONFIG_PNP | ||
172 | |||
173 | static struct pnp_device_id pnp_devids[] = { | ||
174 | { .id = "@P@0001", .driver_data = 0 }, /* ALS 100 */ | ||
175 | { .id = "@P@0020", .driver_data = 0 }, /* ALS 200 */ | ||
176 | { .id = "@P@1001", .driver_data = 0 }, /* ALS 100+ */ | ||
177 | { .id = "@P@2001", .driver_data = 0 }, /* ALS 120 */ | ||
178 | { .id = "ASB16fd", .driver_data = 0 }, /* AdLib NSC16 */ | ||
179 | { .id = "AZT3001", .driver_data = 0 }, /* AZT1008 */ | ||
180 | { .id = "CDC0001", .driver_data = 0 }, /* Opl3-SAx */ | ||
181 | { .id = "CSC0001", .driver_data = 0 }, /* CS4232 */ | ||
182 | { .id = "CSC000f", .driver_data = 0 }, /* CS4236 */ | ||
183 | { .id = "CSC0101", .driver_data = 0 }, /* CS4327 */ | ||
184 | { .id = "CTL7001", .driver_data = 0 }, /* SB16 */ | ||
185 | { .id = "CTL7002", .driver_data = 0 }, /* AWE64 */ | ||
186 | { .id = "CTL7005", .driver_data = 0 }, /* Vibra16 */ | ||
187 | { .id = "ENS2020", .driver_data = 0 }, /* SoundscapeVIVO */ | ||
188 | { .id = "ESS0001", .driver_data = 0 }, /* ES1869 */ | ||
189 | { .id = "ESS0005", .driver_data = 0 }, /* ES1878 */ | ||
190 | { .id = "ESS6880", .driver_data = 0 }, /* ES688 */ | ||
191 | { .id = "IBM0012", .driver_data = 0 }, /* CS4232 */ | ||
192 | { .id = "OPT0001", .driver_data = 0 }, /* OPTi Audio16 */ | ||
193 | { .id = "YMH0006", .driver_data = 0 }, /* Opl3-SA */ | ||
194 | { .id = "YMH0022", .driver_data = 0 }, /* Opl3-SAx */ | ||
195 | { .id = "PNPb02f", .driver_data = 0 }, /* Generic */ | ||
196 | { .id = "", }, | ||
197 | }; | ||
198 | |||
199 | MODULE_DEVICE_TABLE(pnp, pnp_devids); | ||
200 | |||
201 | static int ns558_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *did) | ||
202 | { | ||
203 | int ioport, iolen; | ||
204 | struct ns558 *ns558; | ||
205 | struct gameport *port; | ||
206 | |||
207 | if (!pnp_port_valid(dev, 0)) { | ||
208 | printk(KERN_WARNING "ns558: No i/o ports on a gameport? Weird\n"); | ||
209 | return -ENODEV; | ||
210 | } | ||
211 | |||
212 | ioport = pnp_port_start(dev, 0); | ||
213 | iolen = pnp_port_len(dev, 0); | ||
214 | |||
215 | if (!request_region(ioport, iolen, "ns558-pnp")) | ||
216 | return -EBUSY; | ||
217 | |||
218 | ns558 = kcalloc(1, sizeof(struct ns558), GFP_KERNEL); | ||
219 | port = gameport_allocate_port(); | ||
220 | if (!ns558 || !port) { | ||
221 | printk(KERN_ERR "ns558: Memory allocation failed\n"); | ||
222 | kfree(ns558); | ||
223 | gameport_free_port(port); | ||
224 | return -ENOMEM; | ||
225 | } | ||
226 | |||
227 | ns558->io = ioport; | ||
228 | ns558->size = iolen; | ||
229 | ns558->dev = dev; | ||
230 | ns558->gameport = port; | ||
231 | |||
232 | gameport_set_name(port, "NS558 PnP Gameport"); | ||
233 | gameport_set_phys(port, "pnp%s/gameport0", dev->dev.bus_id); | ||
234 | port->dev.parent = &dev->dev; | ||
235 | port->io = ioport; | ||
236 | |||
237 | gameport_register_port(port); | ||
238 | |||
239 | list_add_tail(&ns558->node, &ns558_list); | ||
240 | return 0; | ||
241 | } | ||
242 | |||
243 | static struct pnp_driver ns558_pnp_driver = { | ||
244 | .name = "ns558", | ||
245 | .id_table = pnp_devids, | ||
246 | .probe = ns558_pnp_probe, | ||
247 | }; | ||
248 | |||
249 | #else | ||
250 | |||
251 | static struct pnp_driver ns558_pnp_driver; | ||
252 | |||
253 | #endif | ||
254 | |||
255 | static int pnp_registered = 0; | ||
256 | |||
257 | static int __init ns558_init(void) | ||
258 | { | ||
259 | int i = 0; | ||
260 | |||
261 | /* | ||
262 | * Probe ISA ports first so that PnP gets to choose free port addresses | ||
263 | * not occupied by the ISA ports. | ||
264 | */ | ||
265 | |||
266 | while (ns558_isa_portlist[i]) | ||
267 | ns558_isa_probe(ns558_isa_portlist[i++]); | ||
268 | |||
269 | if (pnp_register_driver(&ns558_pnp_driver) >= 0) | ||
270 | pnp_registered = 1; | ||
271 | |||
272 | |||
273 | return (list_empty(&ns558_list) && !pnp_registered) ? -ENODEV : 0; | ||
274 | } | ||
275 | |||
276 | static void __exit ns558_exit(void) | ||
277 | { | ||
278 | struct ns558 *ns558; | ||
279 | |||
280 | list_for_each_entry(ns558, &ns558_list, node) { | ||
281 | gameport_unregister_port(ns558->gameport); | ||
282 | release_region(ns558->io & ~(ns558->size - 1), ns558->size); | ||
283 | kfree(ns558); | ||
284 | } | ||
285 | |||
286 | if (pnp_registered) | ||
287 | pnp_unregister_driver(&ns558_pnp_driver); | ||
288 | } | ||
289 | |||
290 | module_init(ns558_init); | ||
291 | module_exit(ns558_exit); | ||