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