1/* 2 * $Id: ns558.c,v 1.1.1.1 2008/10/15 03:26:32 james26_jang Exp $ 3 * 4 * Copyright (c) 1999-2001 Vojtech Pavlik 5 * Copyright (c) 1999 Brian Gerst 6 * 7 * Sponsored by SuSE 8 */ 9 10/* 11 * NS558 based standard IBM game port driver for Linux 12 */ 13 14/* 15 * This program is free software; you can redistribute it and/or modify 16 * it under the terms of the GNU General Public License as published by 17 * the Free Software Foundation; either version 2 of the License, or 18 * (at your option) any later version. 19 * 20 * This program is distributed in the hope that it will be useful, 21 * but WITHOUT ANY WARRANTY; without even the implied warranty of 22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23 * GNU General Public License for more details. 24 * 25 * You should have received a copy of the GNU General Public License 26 * along with this program; if not, write to the Free Software 27 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 28 * 29 * Should you need to contact me, the author, you can do so either by 30 * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: 31 * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic 32 */ 33 34#include <asm/io.h> 35 36#include <linux/module.h> 37#include <linux/ioport.h> 38#include <linux/config.h> 39#include <linux/init.h> 40#include <linux/gameport.h> 41#include <linux/slab.h> 42#include <linux/isapnp.h> 43 44MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); 45MODULE_LICENSE("GPL"); 46 47#define NS558_ISA 1 48#define NS558_PNP 2 49 50static int ns558_isa_portlist[] = { 0x200, 0x201, 0x202, 0x203, 0x204, 0x205, 0x207, 0x209, 51 0x20b, 0x20c, 0x20e, 0x20f, 0x211, 0x219, 0x101, 0 }; 52 53struct ns558 { 54 int type; 55 int size; 56 struct pci_dev *dev; 57 struct ns558 *next; 58 struct gameport gameport; 59}; 60 61static struct ns558 *ns558; 62 63/* 64 * ns558_isa_probe() tries to find an isa gameport at the 65 * specified address, and also checks for mirrors. 66 * A joystick must be attached for this to work. 67 */ 68 69static struct ns558* ns558_isa_probe(int io, struct ns558 *next) 70{ 71 int i, j, b; 72 unsigned char c, u, v; 73 struct ns558 *port; 74 75/* 76 * No one should be using this address. 77 */ 78 79 if (check_region(io, 1)) 80 return next; 81 82/* 83 * We must not be able to write arbitrary values to the port. 84 * The lower two axis bits must be 1 after a write. 85 */ 86 87 c = inb(io); 88 outb(~c & ~3, io); 89 if (~(u = v = inb(io)) & 3) { 90 outb(c, io); 91 return next; 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 return next; 102 } 103 wait_ms(3); 104/* 105 * After some time (4ms) the axes shouldn't change anymore. 106 */ 107 108 u = inb(io); 109 for (i = 0; i < 1000; i++) 110 if ((u ^ inb(io)) & 0xf) { 111 outb(c, io); 112 return next; 113 } 114/* 115 * And now find the number of mirrors of the port. 116 */ 117 118 for (i = 1; i < 5; i++) { 119 120 if (check_region(io & (-1 << i), (1 << i))) /* Don't disturb anyone */ 121 break; 122 123 outb(0xff, io & (-1 << i)); 124 for (j = b = 0; j < 1000; j++) 125 if (inb(io & (-1 << i)) != inb((io & (-1 << i)) + (1 << i) - 1)) b++; 126 wait_ms(3); 127 128 if (b > 300) /* We allow 30% difference */ 129 break; 130 } 131 132 i--; 133 134 if (!(port = kmalloc(sizeof(struct ns558), GFP_KERNEL))) { 135 printk(KERN_ERR "ns558: Memory allocation failed.\n"); 136 return next; 137 } 138 memset(port, 0, sizeof(struct ns558)); 139 140 port->next = next; 141 port->type = NS558_ISA; 142 port->size = (1 << i); 143 port->gameport.io = io & (-1 << i); 144 145 request_region(port->gameport.io, (1 << i), "ns558-isa"); 146 147 gameport_register_port(&port->gameport); 148 149 printk(KERN_INFO "gameport%d: NS558 ISA at %#x", port->gameport.number, port->gameport.io); 150 if (port->size > 1) printk(" size %d", port->size); 151 printk(" speed %d kHz\n", port->gameport.speed); 152 153 return port; 154} 155 156#if defined(CONFIG_ISAPNP) || (defined(CONFIG_ISAPNP_MODULE) && defined(MODULE)) 157#define NSS558_ISAPNP 158#endif 159 160#ifdef NSS558_ISAPNP 161 162static struct isapnp_device_id pnp_devids[] = { 163 { ISAPNP_ANY_ID, ISAPNP_ANY_ID, ISAPNP_VENDOR('@','P','@'), ISAPNP_DEVICE(0x0001), 0 }, 164 { ISAPNP_ANY_ID, ISAPNP_ANY_ID, ISAPNP_VENDOR('@','P','@'), ISAPNP_DEVICE(0x2001), 0 }, 165 { ISAPNP_ANY_ID, ISAPNP_ANY_ID, ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x7001), 0 }, 166 { ISAPNP_ANY_ID, ISAPNP_ANY_ID, ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x7002), 0 }, 167 { ISAPNP_ANY_ID, ISAPNP_ANY_ID, ISAPNP_VENDOR('C','S','C'), ISAPNP_DEVICE(0x0010), 0 }, 168 { ISAPNP_ANY_ID, ISAPNP_ANY_ID, ISAPNP_VENDOR('C','S','C'), ISAPNP_DEVICE(0x0110), 0 }, 169 { ISAPNP_ANY_ID, ISAPNP_ANY_ID, ISAPNP_VENDOR('C','S','C'), ISAPNP_DEVICE(0x0b35), 0 }, 170 { ISAPNP_ANY_ID, ISAPNP_ANY_ID, ISAPNP_VENDOR('C','S','C'), ISAPNP_DEVICE(0x0010), 0 }, 171 { ISAPNP_ANY_ID, ISAPNP_ANY_ID, ISAPNP_VENDOR('C','S','C'), ISAPNP_DEVICE(0x0110), 0 }, 172 { ISAPNP_ANY_ID, ISAPNP_ANY_ID, ISAPNP_VENDOR('P','N','P'), ISAPNP_DEVICE(0xb02f), 0 }, 173 { 0, }, 174}; 175 176MODULE_DEVICE_TABLE(isapnp, pnp_devids); 177 178static struct ns558* ns558_pnp_probe(struct pci_dev *dev, struct ns558 *next) 179{ 180 int ioport, iolen; 181 struct ns558 *port; 182 183 if (dev->prepare && dev->prepare(dev) < 0) 184 return next; 185 186 if (!(dev->resource[0].flags & IORESOURCE_IO)) { 187 printk(KERN_WARNING "ns558: No i/o ports on a gameport? Weird\n"); 188 return next; 189 } 190 191 if (dev->activate && dev->activate(dev) < 0) { 192 printk(KERN_ERR "ns558: PnP resource allocation failed\n"); 193 return next; 194 } 195 196 ioport = pci_resource_start(dev, 0); 197 iolen = pci_resource_len(dev, 0); 198 199 if (!request_region(ioport, iolen, "ns558-pnp")) 200 goto deactivate; 201 202 if (!(port = kmalloc(sizeof(struct ns558), GFP_KERNEL))) { 203 printk(KERN_ERR "ns558: Memory allocation failed.\n"); 204 goto deactivate; 205 } 206 memset(port, 0, sizeof(struct ns558)); 207 208 port->next = next; 209 port->type = NS558_PNP; 210 port->gameport.io = ioport; 211 port->size = iolen; 212 port->dev = dev; 213 214 gameport_register_port(&port->gameport); 215 216 printk(KERN_INFO "gameport%d: NS558 PnP at %#x", port->gameport.number, port->gameport.io); 217 if (iolen > 1) printk(" size %d", iolen); 218 printk(" speed %d kHz\n", port->gameport.speed); 219 220 return port; 221 222deactivate: 223 if (dev->deactivate) 224 dev->deactivate(dev); 225 return next; 226} 227#endif 228 229int __init ns558_init(void) 230{ 231 int i = 0; 232#ifdef NSS558_ISAPNP 233 struct isapnp_device_id *devid; 234 struct pci_dev *dev = NULL; 235#endif 236 237/* 238 * Probe for ISA ports. 239 */ 240 241 while (ns558_isa_portlist[i]) 242 ns558 = ns558_isa_probe(ns558_isa_portlist[i++], ns558); 243 244/* 245 * Probe for PnP ports. 246 */ 247 248#ifdef NSS558_ISAPNP 249 for (devid = pnp_devids; devid->vendor; devid++) { 250 while ((dev = isapnp_find_dev(NULL, devid->vendor, devid->function, dev))) { 251 ns558 = ns558_pnp_probe(dev, ns558); 252 } 253 } 254#endif 255 256 return ns558 ? 0 : -ENODEV; 257} 258 259void __exit ns558_exit(void) 260{ 261 struct ns558 *next, *port = ns558; 262 263 while (port) { 264 gameport_unregister_port(&port->gameport); 265 switch (port->type) { 266 267#ifdef NSS558_ISAPNP 268 case NS558_PNP: 269 if (port->dev->deactivate) 270 port->dev->deactivate(port->dev); 271 /* fall through */ 272#endif 273 274 case NS558_ISA: 275 release_region(port->gameport.io, port->size); 276 break; 277 278 default: 279 break; 280 } 281 282 next = port->next; 283 kfree(port); 284 port = next; 285 } 286} 287 288module_init(ns558_init); 289module_exit(ns558_exit); 290