Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (c) 1998-2005 Vojtech Pavlik |
| 3 | */ |
| 4 | |
| 5 | /* |
| 6 | * Microsoft SideWinder joystick family 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 | * Should you need to contact me, the author, you can do so either by |
| 25 | * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: |
| 26 | * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic |
| 27 | */ |
| 28 | |
| 29 | #include <linux/delay.h> |
| 30 | #include <linux/kernel.h> |
| 31 | #include <linux/module.h> |
| 32 | #include <linux/slab.h> |
| 33 | #include <linux/init.h> |
| 34 | #include <linux/input.h> |
| 35 | #include <linux/gameport.h> |
Tim Schmielau | 4e57b68 | 2005-10-30 15:03:48 -0800 | [diff] [blame] | 36 | #include <linux/jiffies.h> |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 37 | |
| 38 | #define DRIVER_DESC "Microsoft SideWinder joystick family driver" |
| 39 | |
| 40 | MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); |
| 41 | MODULE_DESCRIPTION(DRIVER_DESC); |
| 42 | MODULE_LICENSE("GPL"); |
| 43 | |
| 44 | /* |
| 45 | * These are really magic values. Changing them can make a problem go away, |
| 46 | * as well as break everything. |
| 47 | */ |
| 48 | |
| 49 | #undef SW_DEBUG |
| 50 | #undef SW_DEBUG_DATA |
| 51 | |
| 52 | #define SW_START 600 /* The time we wait for the first bit [600 us] */ |
| 53 | #define SW_STROBE 60 /* Max time per bit [60 us] */ |
| 54 | #define SW_TIMEOUT 6 /* Wait for everything to settle [6 ms] */ |
| 55 | #define SW_KICK 45 /* Wait after A0 fall till kick [45 us] */ |
| 56 | #define SW_END 8 /* Number of bits before end of packet to kick */ |
| 57 | #define SW_FAIL 16 /* Number of packet read errors to fail and reinitialize */ |
| 58 | #define SW_BAD 2 /* Number of packet read errors to switch off 3d Pro optimization */ |
| 59 | #define SW_OK 64 /* Number of packet read successes to switch optimization back on */ |
| 60 | #define SW_LENGTH 512 /* Max number of bits in a packet */ |
| 61 | |
| 62 | #ifdef SW_DEBUG |
| 63 | #define dbg(format, arg...) printk(KERN_DEBUG __FILE__ ": " format "\n" , ## arg) |
| 64 | #else |
| 65 | #define dbg(format, arg...) do {} while (0) |
| 66 | #endif |
| 67 | |
| 68 | /* |
| 69 | * SideWinder joystick types ... |
| 70 | */ |
| 71 | |
| 72 | #define SW_ID_3DP 0 |
| 73 | #define SW_ID_GP 1 |
| 74 | #define SW_ID_PP 2 |
| 75 | #define SW_ID_FFP 3 |
| 76 | #define SW_ID_FSP 4 |
| 77 | #define SW_ID_FFW 5 |
| 78 | |
| 79 | /* |
| 80 | * Names, buttons, axes ... |
| 81 | */ |
| 82 | |
| 83 | static char *sw_name[] = { "3D Pro", "GamePad", "Precision Pro", "Force Feedback Pro", "FreeStyle Pro", |
| 84 | "Force Feedback Wheel" }; |
| 85 | |
| 86 | static char sw_abs[][7] = { |
| 87 | { ABS_X, ABS_Y, ABS_RZ, ABS_THROTTLE, ABS_HAT0X, ABS_HAT0Y }, |
| 88 | { ABS_X, ABS_Y }, |
| 89 | { ABS_X, ABS_Y, ABS_RZ, ABS_THROTTLE, ABS_HAT0X, ABS_HAT0Y }, |
| 90 | { ABS_X, ABS_Y, ABS_RZ, ABS_THROTTLE, ABS_HAT0X, ABS_HAT0Y }, |
| 91 | { ABS_X, ABS_Y, ABS_THROTTLE, ABS_HAT0X, ABS_HAT0Y }, |
| 92 | { ABS_RX, ABS_RUDDER, ABS_THROTTLE }}; |
| 93 | |
| 94 | static char sw_bit[][7] = { |
| 95 | { 10, 10, 9, 10, 1, 1 }, |
| 96 | { 1, 1 }, |
| 97 | { 10, 10, 6, 7, 1, 1 }, |
| 98 | { 10, 10, 6, 7, 1, 1 }, |
| 99 | { 10, 10, 6, 1, 1 }, |
| 100 | { 10, 7, 7, 1, 1 }}; |
| 101 | |
| 102 | static short sw_btn[][12] = { |
| 103 | { BTN_TRIGGER, BTN_TOP, BTN_THUMB, BTN_THUMB2, BTN_BASE, BTN_BASE2, BTN_BASE3, BTN_BASE4, BTN_MODE }, |
| 104 | { BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_TL, BTN_TR, BTN_START, BTN_MODE }, |
| 105 | { BTN_TRIGGER, BTN_THUMB, BTN_TOP, BTN_TOP2, BTN_BASE, BTN_BASE2, BTN_BASE3, BTN_BASE4, BTN_SELECT }, |
| 106 | { BTN_TRIGGER, BTN_THUMB, BTN_TOP, BTN_TOP2, BTN_BASE, BTN_BASE2, BTN_BASE3, BTN_BASE4, BTN_SELECT }, |
| 107 | { BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_TL, BTN_TR, BTN_START, BTN_MODE, BTN_SELECT }, |
| 108 | { BTN_TRIGGER, BTN_TOP, BTN_THUMB, BTN_THUMB2, BTN_BASE, BTN_BASE2, BTN_BASE3, BTN_BASE4 }}; |
| 109 | |
| 110 | static struct { |
| 111 | int x; |
| 112 | int y; |
| 113 | } sw_hat_to_axis[] = {{ 0, 0}, { 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}}; |
| 114 | |
| 115 | struct sw { |
| 116 | struct gameport *gameport; |
Dmitry Torokhov | 17dd3f0 | 2005-09-15 02:01:52 -0500 | [diff] [blame] | 117 | struct input_dev *dev[4]; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 118 | char name[64]; |
| 119 | char phys[4][32]; |
| 120 | int length; |
| 121 | int type; |
| 122 | int bits; |
| 123 | int number; |
| 124 | int fail; |
| 125 | int ok; |
| 126 | int reads; |
| 127 | int bads; |
| 128 | }; |
| 129 | |
| 130 | /* |
| 131 | * sw_read_packet() is a function which reads either a data packet, or an |
| 132 | * identification packet from a SideWinder joystick. The protocol is very, |
| 133 | * very, very braindamaged. Microsoft patented it in US patent #5628686. |
| 134 | */ |
| 135 | |
| 136 | static int sw_read_packet(struct gameport *gameport, unsigned char *buf, int length, int id) |
| 137 | { |
| 138 | unsigned long flags; |
| 139 | int timeout, bitout, sched, i, kick, start, strobe; |
| 140 | unsigned char pending, u, v; |
| 141 | |
| 142 | i = -id; /* Don't care about data, only want ID */ |
| 143 | timeout = id ? gameport_time(gameport, SW_TIMEOUT * 1000) : 0; /* Set up global timeout for ID packet */ |
| 144 | kick = id ? gameport_time(gameport, SW_KICK) : 0; /* Set up kick timeout for ID packet */ |
| 145 | start = gameport_time(gameport, SW_START); |
| 146 | strobe = gameport_time(gameport, SW_STROBE); |
| 147 | bitout = start; |
| 148 | pending = 0; |
| 149 | sched = 0; |
| 150 | |
| 151 | local_irq_save(flags); /* Quiet, please */ |
| 152 | |
| 153 | gameport_trigger(gameport); /* Trigger */ |
| 154 | v = gameport_read(gameport); |
| 155 | |
| 156 | do { |
| 157 | bitout--; |
| 158 | u = v; |
| 159 | v = gameport_read(gameport); |
| 160 | } while (!(~v & u & 0x10) && (bitout > 0)); /* Wait for first falling edge on clock */ |
| 161 | |
| 162 | if (bitout > 0) |
| 163 | bitout = strobe; /* Extend time if not timed out */ |
| 164 | |
| 165 | while ((timeout > 0 || bitout > 0) && (i < length)) { |
| 166 | |
| 167 | timeout--; |
| 168 | bitout--; /* Decrement timers */ |
| 169 | sched--; |
| 170 | |
| 171 | u = v; |
| 172 | v = gameport_read(gameport); |
| 173 | |
| 174 | if ((~u & v & 0x10) && (bitout > 0)) { /* Rising edge on clock - data bit */ |
| 175 | if (i >= 0) /* Want this data */ |
| 176 | buf[i] = v >> 5; /* Store it */ |
| 177 | i++; /* Advance index */ |
| 178 | bitout = strobe; /* Extend timeout for next bit */ |
| 179 | } |
| 180 | |
| 181 | if (kick && (~v & u & 0x01)) { /* Falling edge on axis 0 */ |
| 182 | sched = kick; /* Schedule second trigger */ |
| 183 | kick = 0; /* Don't schedule next time on falling edge */ |
| 184 | pending = 1; /* Mark schedule */ |
| 185 | } |
| 186 | |
| 187 | if (pending && sched < 0 && (i > -SW_END)) { /* Second trigger time */ |
| 188 | gameport_trigger(gameport); /* Trigger */ |
| 189 | bitout = start; /* Long bit timeout */ |
| 190 | pending = 0; /* Unmark schedule */ |
| 191 | timeout = 0; /* Switch from global to bit timeouts */ |
| 192 | } |
| 193 | } |
| 194 | |
| 195 | local_irq_restore(flags); /* Done - relax */ |
| 196 | |
| 197 | #ifdef SW_DEBUG_DATA |
| 198 | { |
| 199 | int j; |
| 200 | printk(KERN_DEBUG "sidewinder.c: Read %d triplets. [", i); |
| 201 | for (j = 0; j < i; j++) printk("%d", buf[j]); |
| 202 | printk("]\n"); |
| 203 | } |
| 204 | #endif |
| 205 | |
| 206 | return i; |
| 207 | } |
| 208 | |
| 209 | /* |
| 210 | * sw_get_bits() and GB() compose bits from the triplet buffer into a __u64. |
| 211 | * Parameter 'pos' is bit number inside packet where to start at, 'num' is number |
| 212 | * of bits to be read, 'shift' is offset in the resulting __u64 to start at, bits |
| 213 | * is number of bits per triplet. |
| 214 | */ |
| 215 | |
| 216 | #define GB(pos,num) sw_get_bits(buf, pos, num, sw->bits) |
| 217 | |
| 218 | static __u64 sw_get_bits(unsigned char *buf, int pos, int num, char bits) |
| 219 | { |
| 220 | __u64 data = 0; |
| 221 | int tri = pos % bits; /* Start position */ |
| 222 | int i = pos / bits; |
| 223 | int bit = 0; |
| 224 | |
| 225 | while (num--) { |
| 226 | data |= (__u64)((buf[i] >> tri++) & 1) << bit++; /* Transfer bit */ |
| 227 | if (tri == bits) { |
| 228 | i++; /* Next triplet */ |
| 229 | tri = 0; |
| 230 | } |
| 231 | } |
| 232 | |
| 233 | return data; |
| 234 | } |
| 235 | |
| 236 | /* |
| 237 | * sw_init_digital() initializes a SideWinder 3D Pro joystick |
| 238 | * into digital mode. |
| 239 | */ |
| 240 | |
| 241 | static void sw_init_digital(struct gameport *gameport) |
| 242 | { |
| 243 | int seq[] = { 140, 140+725, 140+300, 0 }; |
| 244 | unsigned long flags; |
| 245 | int i, t; |
| 246 | |
| 247 | local_irq_save(flags); |
| 248 | |
| 249 | i = 0; |
| 250 | do { |
| 251 | gameport_trigger(gameport); /* Trigger */ |
| 252 | t = gameport_time(gameport, SW_TIMEOUT * 1000); |
| 253 | while ((gameport_read(gameport) & 1) && t) t--; /* Wait for axis to fall back to 0 */ |
| 254 | udelay(seq[i]); /* Delay magic time */ |
| 255 | } while (seq[++i]); |
| 256 | |
| 257 | gameport_trigger(gameport); /* Last trigger */ |
| 258 | |
| 259 | local_irq_restore(flags); |
| 260 | } |
| 261 | |
| 262 | /* |
| 263 | * sw_parity() computes parity of __u64 |
| 264 | */ |
| 265 | |
| 266 | static int sw_parity(__u64 t) |
| 267 | { |
| 268 | int x = t ^ (t >> 32); |
| 269 | |
| 270 | x ^= x >> 16; |
| 271 | x ^= x >> 8; |
| 272 | x ^= x >> 4; |
| 273 | x ^= x >> 2; |
| 274 | x ^= x >> 1; |
| 275 | return x & 1; |
| 276 | } |
| 277 | |
| 278 | /* |
| 279 | * sw_ccheck() checks synchronization bits and computes checksum of nibbles. |
| 280 | */ |
| 281 | |
| 282 | static int sw_check(__u64 t) |
| 283 | { |
| 284 | unsigned char sum = 0; |
| 285 | |
| 286 | if ((t & 0x8080808080808080ULL) ^ 0x80) /* Sync */ |
| 287 | return -1; |
| 288 | |
| 289 | while (t) { /* Sum */ |
| 290 | sum += t & 0xf; |
| 291 | t >>= 4; |
| 292 | } |
| 293 | |
| 294 | return sum & 0xf; |
| 295 | } |
| 296 | |
| 297 | /* |
| 298 | * sw_parse() analyzes SideWinder joystick data, and writes the results into |
| 299 | * the axes and buttons arrays. |
| 300 | */ |
| 301 | |
| 302 | static int sw_parse(unsigned char *buf, struct sw *sw) |
| 303 | { |
| 304 | int hat, i, j; |
Dmitry Torokhov | 17dd3f0 | 2005-09-15 02:01:52 -0500 | [diff] [blame] | 305 | struct input_dev *dev; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 306 | |
| 307 | switch (sw->type) { |
| 308 | |
| 309 | case SW_ID_3DP: |
| 310 | |
| 311 | if (sw_check(GB(0,64)) || (hat = (GB(6,1) << 3) | GB(60,3)) > 8) |
| 312 | return -1; |
| 313 | |
Dmitry Torokhov | 17dd3f0 | 2005-09-15 02:01:52 -0500 | [diff] [blame] | 314 | dev = sw->dev[0]; |
| 315 | |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 316 | input_report_abs(dev, ABS_X, (GB( 3,3) << 7) | GB(16,7)); |
| 317 | input_report_abs(dev, ABS_Y, (GB( 0,3) << 7) | GB(24,7)); |
| 318 | input_report_abs(dev, ABS_RZ, (GB(35,2) << 7) | GB(40,7)); |
| 319 | input_report_abs(dev, ABS_THROTTLE, (GB(32,3) << 7) | GB(48,7)); |
| 320 | |
| 321 | input_report_abs(dev, ABS_HAT0X, sw_hat_to_axis[hat].x); |
| 322 | input_report_abs(dev, ABS_HAT0Y, sw_hat_to_axis[hat].y); |
| 323 | |
| 324 | for (j = 0; j < 7; j++) |
| 325 | input_report_key(dev, sw_btn[SW_ID_3DP][j], !GB(j+8,1)); |
| 326 | |
| 327 | input_report_key(dev, BTN_BASE4, !GB(38,1)); |
| 328 | input_report_key(dev, BTN_BASE5, !GB(37,1)); |
| 329 | |
| 330 | input_sync(dev); |
| 331 | |
| 332 | return 0; |
| 333 | |
| 334 | case SW_ID_GP: |
| 335 | |
| 336 | for (i = 0; i < sw->number; i ++) { |
| 337 | |
| 338 | if (sw_parity(GB(i*15,15))) |
| 339 | return -1; |
| 340 | |
Dmitry Torokhov | 17dd3f0 | 2005-09-15 02:01:52 -0500 | [diff] [blame] | 341 | input_report_abs(sw->dev[i], ABS_X, GB(i*15+3,1) - GB(i*15+2,1)); |
| 342 | input_report_abs(sw->dev[i], ABS_Y, GB(i*15+0,1) - GB(i*15+1,1)); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 343 | |
| 344 | for (j = 0; j < 10; j++) |
Dmitry Torokhov | 17dd3f0 | 2005-09-15 02:01:52 -0500 | [diff] [blame] | 345 | input_report_key(sw->dev[i], sw_btn[SW_ID_GP][j], !GB(i*15+j+4,1)); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 346 | |
Dmitry Torokhov | 17dd3f0 | 2005-09-15 02:01:52 -0500 | [diff] [blame] | 347 | input_sync(sw->dev[i]); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 348 | } |
| 349 | |
| 350 | return 0; |
| 351 | |
| 352 | case SW_ID_PP: |
| 353 | case SW_ID_FFP: |
| 354 | |
| 355 | if (!sw_parity(GB(0,48)) || (hat = GB(42,4)) > 8) |
| 356 | return -1; |
| 357 | |
Dmitry Torokhov | 17dd3f0 | 2005-09-15 02:01:52 -0500 | [diff] [blame] | 358 | dev = sw->dev[0]; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 359 | input_report_abs(dev, ABS_X, GB( 9,10)); |
| 360 | input_report_abs(dev, ABS_Y, GB(19,10)); |
| 361 | input_report_abs(dev, ABS_RZ, GB(36, 6)); |
| 362 | input_report_abs(dev, ABS_THROTTLE, GB(29, 7)); |
| 363 | |
| 364 | input_report_abs(dev, ABS_HAT0X, sw_hat_to_axis[hat].x); |
| 365 | input_report_abs(dev, ABS_HAT0Y, sw_hat_to_axis[hat].y); |
| 366 | |
| 367 | for (j = 0; j < 9; j++) |
| 368 | input_report_key(dev, sw_btn[SW_ID_PP][j], !GB(j,1)); |
| 369 | |
| 370 | input_sync(dev); |
| 371 | |
| 372 | return 0; |
| 373 | |
| 374 | case SW_ID_FSP: |
| 375 | |
| 376 | if (!sw_parity(GB(0,43)) || (hat = GB(28,4)) > 8) |
| 377 | return -1; |
| 378 | |
Dmitry Torokhov | 17dd3f0 | 2005-09-15 02:01:52 -0500 | [diff] [blame] | 379 | dev = sw->dev[0]; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 380 | input_report_abs(dev, ABS_X, GB( 0,10)); |
| 381 | input_report_abs(dev, ABS_Y, GB(16,10)); |
| 382 | input_report_abs(dev, ABS_THROTTLE, GB(32, 6)); |
| 383 | |
| 384 | input_report_abs(dev, ABS_HAT0X, sw_hat_to_axis[hat].x); |
| 385 | input_report_abs(dev, ABS_HAT0Y, sw_hat_to_axis[hat].y); |
| 386 | |
| 387 | for (j = 0; j < 6; j++) |
| 388 | input_report_key(dev, sw_btn[SW_ID_FSP][j], !GB(j+10,1)); |
| 389 | |
| 390 | input_report_key(dev, BTN_TR, !GB(26,1)); |
| 391 | input_report_key(dev, BTN_START, !GB(27,1)); |
| 392 | input_report_key(dev, BTN_MODE, !GB(38,1)); |
| 393 | input_report_key(dev, BTN_SELECT, !GB(39,1)); |
| 394 | |
| 395 | input_sync(dev); |
| 396 | |
| 397 | return 0; |
| 398 | |
| 399 | case SW_ID_FFW: |
| 400 | |
| 401 | if (!sw_parity(GB(0,33))) |
| 402 | return -1; |
| 403 | |
Dmitry Torokhov | 17dd3f0 | 2005-09-15 02:01:52 -0500 | [diff] [blame] | 404 | dev = sw->dev[0]; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 405 | input_report_abs(dev, ABS_RX, GB( 0,10)); |
| 406 | input_report_abs(dev, ABS_RUDDER, GB(10, 6)); |
| 407 | input_report_abs(dev, ABS_THROTTLE, GB(16, 6)); |
| 408 | |
| 409 | for (j = 0; j < 8; j++) |
| 410 | input_report_key(dev, sw_btn[SW_ID_FFW][j], !GB(j+22,1)); |
| 411 | |
| 412 | input_sync(dev); |
| 413 | |
| 414 | return 0; |
| 415 | } |
| 416 | |
| 417 | return -1; |
| 418 | } |
| 419 | |
| 420 | /* |
| 421 | * sw_read() reads SideWinder joystick data, and reinitializes |
| 422 | * the joystick in case of persistent problems. This is the function that is |
| 423 | * called from the generic code to poll the joystick. |
| 424 | */ |
| 425 | |
| 426 | static int sw_read(struct sw *sw) |
| 427 | { |
| 428 | unsigned char buf[SW_LENGTH]; |
| 429 | int i; |
| 430 | |
| 431 | i = sw_read_packet(sw->gameport, buf, sw->length, 0); |
| 432 | |
| 433 | if (sw->type == SW_ID_3DP && sw->length == 66 && i != 66) { /* Broken packet, try to fix */ |
| 434 | |
| 435 | if (i == 64 && !sw_check(sw_get_bits(buf,0,64,1))) { /* Last init failed, 1 bit mode */ |
| 436 | printk(KERN_WARNING "sidewinder.c: Joystick in wrong mode on %s" |
| 437 | " - going to reinitialize.\n", sw->gameport->phys); |
| 438 | sw->fail = SW_FAIL; /* Reinitialize */ |
| 439 | i = 128; /* Bogus value */ |
| 440 | } |
| 441 | |
| 442 | if (i < 66 && GB(0,64) == GB(i*3-66,64)) /* 1 == 3 */ |
| 443 | i = 66; /* Everything is fine */ |
| 444 | |
| 445 | if (i < 66 && GB(0,64) == GB(66,64)) /* 1 == 2 */ |
| 446 | i = 66; /* Everything is fine */ |
| 447 | |
| 448 | if (i < 66 && GB(i*3-132,64) == GB(i*3-66,64)) { /* 2 == 3 */ |
| 449 | memmove(buf, buf + i - 22, 22); /* Move data */ |
| 450 | i = 66; /* Carry on */ |
| 451 | } |
| 452 | } |
| 453 | |
| 454 | if (i == sw->length && !sw_parse(buf, sw)) { /* Parse data */ |
| 455 | |
| 456 | sw->fail = 0; |
| 457 | sw->ok++; |
| 458 | |
| 459 | if (sw->type == SW_ID_3DP && sw->length == 66 /* Many packets OK */ |
| 460 | && sw->ok > SW_OK) { |
| 461 | |
| 462 | printk(KERN_INFO "sidewinder.c: No more trouble on %s" |
| 463 | " - enabling optimization again.\n", sw->gameport->phys); |
| 464 | sw->length = 22; |
| 465 | } |
| 466 | |
| 467 | return 0; |
| 468 | } |
| 469 | |
| 470 | sw->ok = 0; |
| 471 | sw->fail++; |
| 472 | |
| 473 | if (sw->type == SW_ID_3DP && sw->length == 22 && sw->fail > SW_BAD) { /* Consecutive bad packets */ |
| 474 | |
| 475 | printk(KERN_INFO "sidewinder.c: Many bit errors on %s" |
| 476 | " - disabling optimization.\n", sw->gameport->phys); |
| 477 | sw->length = 66; |
| 478 | } |
| 479 | |
| 480 | if (sw->fail < SW_FAIL) |
| 481 | return -1; /* Not enough, don't reinitialize yet */ |
| 482 | |
| 483 | printk(KERN_WARNING "sidewinder.c: Too many bit errors on %s" |
| 484 | " - reinitializing joystick.\n", sw->gameport->phys); |
| 485 | |
| 486 | if (!i && sw->type == SW_ID_3DP) { /* 3D Pro can be in analog mode */ |
| 487 | mdelay(3 * SW_TIMEOUT); |
| 488 | sw_init_digital(sw->gameport); |
| 489 | } |
| 490 | |
| 491 | mdelay(SW_TIMEOUT); |
| 492 | i = sw_read_packet(sw->gameport, buf, SW_LENGTH, 0); /* Read normal data packet */ |
| 493 | mdelay(SW_TIMEOUT); |
| 494 | sw_read_packet(sw->gameport, buf, SW_LENGTH, i); /* Read ID packet, this initializes the stick */ |
| 495 | |
| 496 | sw->fail = SW_FAIL; |
| 497 | |
| 498 | return -1; |
| 499 | } |
| 500 | |
| 501 | static void sw_poll(struct gameport *gameport) |
| 502 | { |
| 503 | struct sw *sw = gameport_get_drvdata(gameport); |
| 504 | |
| 505 | sw->reads++; |
| 506 | if (sw_read(sw)) |
| 507 | sw->bads++; |
| 508 | } |
| 509 | |
| 510 | static int sw_open(struct input_dev *dev) |
| 511 | { |
Dmitry Torokhov | 8715c1c | 2007-04-12 01:34:14 -0400 | [diff] [blame] | 512 | struct sw *sw = input_get_drvdata(dev); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 513 | |
| 514 | gameport_start_polling(sw->gameport); |
| 515 | return 0; |
| 516 | } |
| 517 | |
| 518 | static void sw_close(struct input_dev *dev) |
| 519 | { |
Dmitry Torokhov | 8715c1c | 2007-04-12 01:34:14 -0400 | [diff] [blame] | 520 | struct sw *sw = input_get_drvdata(dev); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 521 | |
| 522 | gameport_stop_polling(sw->gameport); |
| 523 | } |
| 524 | |
| 525 | /* |
| 526 | * sw_print_packet() prints the contents of a SideWinder packet. |
| 527 | */ |
| 528 | |
| 529 | static void sw_print_packet(char *name, int length, unsigned char *buf, char bits) |
| 530 | { |
| 531 | int i; |
| 532 | |
| 533 | printk(KERN_INFO "sidewinder.c: %s packet, %d bits. [", name, length); |
| 534 | for (i = (((length + 3) >> 2) - 1); i >= 0; i--) |
| 535 | printk("%x", (int)sw_get_bits(buf, i << 2, 4, bits)); |
| 536 | printk("]\n"); |
| 537 | } |
| 538 | |
| 539 | /* |
| 540 | * sw_3dp_id() translates the 3DP id into a human legible string. |
| 541 | * Unfortunately I don't know how to do this for the other SW types. |
| 542 | */ |
| 543 | |
Dmitry Torokhov | 10ca4c0 | 2006-06-26 01:45:48 -0400 | [diff] [blame] | 544 | static void sw_3dp_id(unsigned char *buf, char *comment, size_t size) |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 545 | { |
| 546 | int i; |
| 547 | char pnp[8], rev[9]; |
| 548 | |
| 549 | for (i = 0; i < 7; i++) /* ASCII PnP ID */ |
| 550 | pnp[i] = sw_get_bits(buf, 24+8*i, 8, 1); |
| 551 | |
| 552 | for (i = 0; i < 8; i++) /* ASCII firmware revision */ |
| 553 | rev[i] = sw_get_bits(buf, 88+8*i, 8, 1); |
| 554 | |
| 555 | pnp[7] = rev[8] = 0; |
| 556 | |
Dmitry Torokhov | 10ca4c0 | 2006-06-26 01:45:48 -0400 | [diff] [blame] | 557 | snprintf(comment, size, " [PnP %d.%02d id %s rev %s]", |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 558 | (int) ((sw_get_bits(buf, 8, 6, 1) << 6) | /* Two 6-bit values */ |
| 559 | sw_get_bits(buf, 16, 6, 1)) / 100, |
| 560 | (int) ((sw_get_bits(buf, 8, 6, 1) << 6) | |
| 561 | sw_get_bits(buf, 16, 6, 1)) % 100, |
| 562 | pnp, rev); |
| 563 | } |
| 564 | |
| 565 | /* |
| 566 | * sw_guess_mode() checks the upper two button bits for toggling - |
| 567 | * indication of that the joystick is in 3-bit mode. This is documented |
| 568 | * behavior for 3DP ID packet, and for example the FSP does this in |
| 569 | * normal packets instead. Fun ... |
| 570 | */ |
| 571 | |
| 572 | static int sw_guess_mode(unsigned char *buf, int len) |
| 573 | { |
| 574 | int i; |
| 575 | unsigned char xor = 0; |
| 576 | |
| 577 | for (i = 1; i < len; i++) |
| 578 | xor |= (buf[i - 1] ^ buf[i]) & 6; |
| 579 | |
| 580 | return !!xor * 2 + 1; |
| 581 | } |
| 582 | |
| 583 | /* |
| 584 | * sw_connect() probes for SideWinder type joysticks. |
| 585 | */ |
| 586 | |
| 587 | static int sw_connect(struct gameport *gameport, struct gameport_driver *drv) |
| 588 | { |
| 589 | struct sw *sw; |
Dmitry Torokhov | 17dd3f0 | 2005-09-15 02:01:52 -0500 | [diff] [blame] | 590 | struct input_dev *input_dev; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 591 | int i, j, k, l; |
Jesper Juhl | 7363cfc | 2006-05-29 23:28:05 -0400 | [diff] [blame] | 592 | int err = 0; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 593 | unsigned char *buf = NULL; /* [SW_LENGTH] */ |
| 594 | unsigned char *idbuf = NULL; /* [SW_LENGTH] */ |
| 595 | unsigned char m = 1; |
| 596 | char comment[40]; |
| 597 | |
| 598 | comment[0] = 0; |
| 599 | |
Pekka Enberg | a97e148 | 2005-09-06 15:18:33 -0700 | [diff] [blame] | 600 | sw = kzalloc(sizeof(struct sw), GFP_KERNEL); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 601 | buf = kmalloc(SW_LENGTH, GFP_KERNEL); |
| 602 | idbuf = kmalloc(SW_LENGTH, GFP_KERNEL); |
| 603 | if (!sw || !buf || !idbuf) { |
| 604 | err = -ENOMEM; |
| 605 | goto fail1; |
| 606 | } |
| 607 | |
| 608 | sw->gameport = gameport; |
| 609 | |
| 610 | gameport_set_drvdata(gameport, sw); |
| 611 | |
| 612 | err = gameport_open(gameport, drv, GAMEPORT_MODE_RAW); |
| 613 | if (err) |
| 614 | goto fail1; |
| 615 | |
| 616 | dbg("Init 0: Opened %s, io %#x, speed %d", |
| 617 | gameport->phys, gameport->io, gameport->speed); |
| 618 | |
| 619 | i = sw_read_packet(gameport, buf, SW_LENGTH, 0); /* Read normal packet */ |
| 620 | msleep(SW_TIMEOUT); |
| 621 | dbg("Init 1: Mode %d. Length %d.", m , i); |
| 622 | |
| 623 | if (!i) { /* No data. 3d Pro analog mode? */ |
| 624 | sw_init_digital(gameport); /* Switch to digital */ |
| 625 | msleep(SW_TIMEOUT); |
| 626 | i = sw_read_packet(gameport, buf, SW_LENGTH, 0); /* Retry reading packet */ |
| 627 | msleep(SW_TIMEOUT); |
| 628 | dbg("Init 1b: Length %d.", i); |
| 629 | if (!i) { /* No data -> FAIL */ |
| 630 | err = -ENODEV; |
| 631 | goto fail2; |
| 632 | } |
| 633 | } |
| 634 | |
| 635 | j = sw_read_packet(gameport, idbuf, SW_LENGTH, i); /* Read ID. This initializes the stick */ |
| 636 | m |= sw_guess_mode(idbuf, j); /* ID packet should carry mode info [3DP] */ |
| 637 | dbg("Init 2: Mode %d. ID Length %d.", m, j); |
| 638 | |
| 639 | if (j <= 0) { /* Read ID failed. Happens in 1-bit mode on PP */ |
| 640 | msleep(SW_TIMEOUT); |
| 641 | i = sw_read_packet(gameport, buf, SW_LENGTH, 0); /* Retry reading packet */ |
| 642 | m |= sw_guess_mode(buf, i); |
| 643 | dbg("Init 2b: Mode %d. Length %d.", m, i); |
| 644 | if (!i) { |
| 645 | err = -ENODEV; |
| 646 | goto fail2; |
| 647 | } |
| 648 | msleep(SW_TIMEOUT); |
| 649 | j = sw_read_packet(gameport, idbuf, SW_LENGTH, i); /* Retry reading ID */ |
| 650 | dbg("Init 2c: ID Length %d.", j); |
| 651 | } |
| 652 | |
| 653 | sw->type = -1; |
| 654 | k = SW_FAIL; /* Try SW_FAIL times */ |
| 655 | l = 0; |
| 656 | |
| 657 | do { |
| 658 | k--; |
| 659 | msleep(SW_TIMEOUT); |
| 660 | i = sw_read_packet(gameport, buf, SW_LENGTH, 0); /* Read data packet */ |
| 661 | dbg("Init 3: Mode %d. Length %d. Last %d. Tries %d.", m, i, l, k); |
| 662 | |
| 663 | if (i > l) { /* Longer? As we can only lose bits, it makes */ |
| 664 | /* no sense to try detection for a packet shorter */ |
| 665 | l = i; /* than the previous one */ |
| 666 | |
| 667 | sw->number = 1; |
| 668 | sw->gameport = gameport; |
| 669 | sw->length = i; |
| 670 | sw->bits = m; |
| 671 | |
| 672 | dbg("Init 3a: Case %d.\n", i * m); |
| 673 | |
| 674 | switch (i * m) { |
| 675 | case 60: |
| 676 | sw->number++; |
| 677 | case 45: /* Ambiguous packet length */ |
| 678 | if (j <= 40) { /* ID length less or eq 40 -> FSP */ |
| 679 | case 43: |
| 680 | sw->type = SW_ID_FSP; |
| 681 | break; |
| 682 | } |
| 683 | sw->number++; |
| 684 | case 30: |
| 685 | sw->number++; |
| 686 | case 15: |
| 687 | sw->type = SW_ID_GP; |
| 688 | break; |
| 689 | case 33: |
| 690 | case 31: |
| 691 | sw->type = SW_ID_FFW; |
| 692 | break; |
| 693 | case 48: /* Ambiguous */ |
| 694 | if (j == 14) { /* ID length 14*3 -> FFP */ |
| 695 | sw->type = SW_ID_FFP; |
| 696 | sprintf(comment, " [AC %s]", sw_get_bits(idbuf,38,1,3) ? "off" : "on"); |
| 697 | } else |
Dmitry Torokhov | 10ca4c0 | 2006-06-26 01:45:48 -0400 | [diff] [blame] | 698 | sw->type = SW_ID_PP; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 699 | break; |
| 700 | case 66: |
| 701 | sw->bits = 3; |
| 702 | case 198: |
| 703 | sw->length = 22; |
| 704 | case 64: |
| 705 | sw->type = SW_ID_3DP; |
Dmitry Torokhov | 10ca4c0 | 2006-06-26 01:45:48 -0400 | [diff] [blame] | 706 | if (j == 160) |
| 707 | sw_3dp_id(idbuf, comment, sizeof(comment)); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 708 | break; |
| 709 | } |
| 710 | } |
| 711 | |
| 712 | } while (k && sw->type == -1); |
| 713 | |
| 714 | if (sw->type == -1) { |
| 715 | printk(KERN_WARNING "sidewinder.c: unknown joystick device detected " |
| 716 | "on %s, contact <vojtech@ucw.cz>\n", gameport->phys); |
| 717 | sw_print_packet("ID", j * 3, idbuf, 3); |
| 718 | sw_print_packet("Data", i * m, buf, m); |
| 719 | err = -ENODEV; |
| 720 | goto fail2; |
| 721 | } |
| 722 | |
| 723 | #ifdef SW_DEBUG |
| 724 | sw_print_packet("ID", j * 3, idbuf, 3); |
| 725 | sw_print_packet("Data", i * m, buf, m); |
| 726 | #endif |
| 727 | |
| 728 | gameport_set_poll_handler(gameport, sw_poll); |
| 729 | gameport_set_poll_interval(gameport, 20); |
| 730 | |
| 731 | k = i; |
| 732 | l = j; |
| 733 | |
| 734 | for (i = 0; i < sw->number; i++) { |
| 735 | int bits, code; |
| 736 | |
Dmitry Torokhov | 10ca4c0 | 2006-06-26 01:45:48 -0400 | [diff] [blame] | 737 | snprintf(sw->name, sizeof(sw->name), |
| 738 | "Microsoft SideWinder %s", sw_name[sw->type]); |
| 739 | snprintf(sw->phys[i], sizeof(sw->phys[i]), |
| 740 | "%s/input%d", gameport->phys, i); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 741 | |
Zinx Verituse | 847fd5f | 2006-01-29 21:51:51 -0500 | [diff] [blame] | 742 | sw->dev[i] = input_dev = input_allocate_device(); |
Dmitry Torokhov | 17dd3f0 | 2005-09-15 02:01:52 -0500 | [diff] [blame] | 743 | if (!input_dev) { |
| 744 | err = -ENOMEM; |
| 745 | goto fail3; |
| 746 | } |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 747 | |
Dmitry Torokhov | 17dd3f0 | 2005-09-15 02:01:52 -0500 | [diff] [blame] | 748 | input_dev->name = sw->name; |
| 749 | input_dev->phys = sw->phys[i]; |
| 750 | input_dev->id.bustype = BUS_GAMEPORT; |
| 751 | input_dev->id.vendor = GAMEPORT_ID_VENDOR_MICROSOFT; |
| 752 | input_dev->id.product = sw->type; |
| 753 | input_dev->id.version = 0x0100; |
Dmitry Torokhov | 935e658 | 2007-04-12 01:35:26 -0400 | [diff] [blame] | 754 | input_dev->dev.parent = &gameport->dev; |
Dmitry Torokhov | 8715c1c | 2007-04-12 01:34:14 -0400 | [diff] [blame] | 755 | |
| 756 | input_set_drvdata(input_dev, sw); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 757 | |
Dmitry Torokhov | 17dd3f0 | 2005-09-15 02:01:52 -0500 | [diff] [blame] | 758 | input_dev->open = sw_open; |
| 759 | input_dev->close = sw_close; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 760 | |
Jiri Slaby | 7b19ada | 2007-10-18 23:40:32 -0700 | [diff] [blame] | 761 | input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 762 | |
| 763 | for (j = 0; (bits = sw_bit[sw->type][j]); j++) { |
Daniel Mack | 987a6c0 | 2010-08-02 20:15:17 -0700 | [diff] [blame] | 764 | int min, max, fuzz, flat; |
| 765 | |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 766 | code = sw_abs[sw->type][j]; |
Daniel Mack | 987a6c0 | 2010-08-02 20:15:17 -0700 | [diff] [blame] | 767 | min = bits == 1 ? -1 : 0; |
| 768 | max = (1 << bits) - 1; |
| 769 | fuzz = (bits >> 1) >= 2 ? 1 << ((bits >> 1) - 2) : 0; |
| 770 | flat = code == ABS_THROTTLE || bits < 5 ? |
| 771 | 0 : 1 << (bits - 5); |
| 772 | |
| 773 | input_set_abs_params(input_dev, code, |
| 774 | min, max, fuzz, flat); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 775 | } |
| 776 | |
| 777 | for (j = 0; (code = sw_btn[sw->type][j]); j++) |
Daniel Mack | 987a6c0 | 2010-08-02 20:15:17 -0700 | [diff] [blame] | 778 | __set_bit(code, input_dev->keybit); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 779 | |
Dmitry Torokhov | 17dd3f0 | 2005-09-15 02:01:52 -0500 | [diff] [blame] | 780 | dbg("%s%s [%d-bit id %d data %d]\n", sw->name, comment, m, l, k); |
| 781 | |
Dmitry Torokhov | 07cf779 | 2006-01-29 21:51:56 -0500 | [diff] [blame] | 782 | err = input_register_device(sw->dev[i]); |
| 783 | if (err) |
| 784 | goto fail4; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 785 | } |
| 786 | |
Jesper Juhl | 7363cfc | 2006-05-29 23:28:05 -0400 | [diff] [blame] | 787 | out: kfree(buf); |
| 788 | kfree(idbuf); |
| 789 | |
| 790 | return err; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 791 | |
Dmitry Torokhov | 07cf779 | 2006-01-29 21:51:56 -0500 | [diff] [blame] | 792 | fail4: input_free_device(sw->dev[i]); |
| 793 | fail3: while (--i >= 0) |
Dmitry Torokhov | 17dd3f0 | 2005-09-15 02:01:52 -0500 | [diff] [blame] | 794 | input_unregister_device(sw->dev[i]); |
| 795 | fail2: gameport_close(gameport); |
| 796 | fail1: gameport_set_drvdata(gameport, NULL); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 797 | kfree(sw); |
Jesper Juhl | 7363cfc | 2006-05-29 23:28:05 -0400 | [diff] [blame] | 798 | goto out; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 799 | } |
| 800 | |
| 801 | static void sw_disconnect(struct gameport *gameport) |
| 802 | { |
| 803 | struct sw *sw = gameport_get_drvdata(gameport); |
| 804 | int i; |
| 805 | |
| 806 | for (i = 0; i < sw->number; i++) |
Dmitry Torokhov | 17dd3f0 | 2005-09-15 02:01:52 -0500 | [diff] [blame] | 807 | input_unregister_device(sw->dev[i]); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 808 | gameport_close(gameport); |
| 809 | gameport_set_drvdata(gameport, NULL); |
| 810 | kfree(sw); |
| 811 | } |
| 812 | |
| 813 | static struct gameport_driver sw_drv = { |
| 814 | .driver = { |
| 815 | .name = "sidewinder", |
Dmitry Torokhov | 07cf779 | 2006-01-29 21:51:56 -0500 | [diff] [blame] | 816 | .owner = THIS_MODULE, |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 817 | }, |
| 818 | .description = DRIVER_DESC, |
| 819 | .connect = sw_connect, |
| 820 | .disconnect = sw_disconnect, |
| 821 | }; |
| 822 | |
| 823 | static int __init sw_init(void) |
| 824 | { |
Dmitry Torokhov | 2547203 | 2008-06-06 01:33:37 -0400 | [diff] [blame] | 825 | return gameport_register_driver(&sw_drv); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 826 | } |
| 827 | |
| 828 | static void __exit sw_exit(void) |
| 829 | { |
| 830 | gameport_unregister_driver(&sw_drv); |
| 831 | } |
| 832 | |
| 833 | module_init(sw_init); |
| 834 | module_exit(sw_exit); |