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