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