1 /* 2 * Copyright (c) 1999-2001 Vojtech Pavlik 3 */ 4 5 /* 6 * Creative Labs Blaster GamePad Cobra 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/kernel.h> 30 #include <linux/module.h> 31 #include <linux/slab.h> 32 #include <linux/init.h> 33 #include <linux/gameport.h> 34 #include <linux/input.h> 35 #include <linux/jiffies.h> 36 37 #define DRIVER_DESC "Creative Labs Blaster GamePad Cobra driver" 38 39 MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); 40 MODULE_DESCRIPTION(DRIVER_DESC); 41 MODULE_LICENSE("GPL"); 42 43 #define COBRA_MAX_STROBE 45 /* 45 us max wait for first strobe */ 44 #define COBRA_LENGTH 36 45 46 static int cobra_btn[] = { BTN_START, BTN_SELECT, BTN_TL, BTN_TR, BTN_X, BTN_Y, BTN_Z, BTN_A, BTN_B, BTN_C, BTN_TL2, BTN_TR2, 0 }; 47 48 struct cobra { 49 struct gameport *gameport; 50 struct input_dev *dev[2]; 51 int reads; 52 int bads; 53 unsigned char exists; 54 char phys[2][32]; 55 }; 56 57 static unsigned char cobra_read_packet(struct gameport *gameport, unsigned int *data) 58 { 59 unsigned long flags; 60 unsigned char u, v, w; 61 __u64 buf[2]; 62 int r[2], t[2]; 63 int i, j, ret; 64 65 int strobe = gameport_time(gameport, COBRA_MAX_STROBE); 66 67 for (i = 0; i < 2; i++) { 68 r[i] = buf[i] = 0; 69 t[i] = COBRA_MAX_STROBE; 70 } 71 72 local_irq_save(flags); 73 74 u = gameport_read(gameport); 75 76 do { 77 t[0]--; t[1]--; 78 v = gameport_read(gameport); 79 for (i = 0, w = u ^ v; i < 2 && w; i++, w >>= 2) 80 if (w & 0x30) { 81 if ((w & 0x30) < 0x30 && r[i] < COBRA_LENGTH && t[i] > 0) { 82 buf[i] |= (__u64)((w >> 5) & 1) << r[i]++; 83 t[i] = strobe; 84 u = v; 85 } else t[i] = 0; 86 } 87 } while (t[0] > 0 || t[1] > 0); 88 89 local_irq_restore(flags); 90 91 ret = 0; 92 93 for (i = 0; i < 2; i++) { 94 95 if (r[i] != COBRA_LENGTH) continue; 96 97 for (j = 0; j < COBRA_LENGTH && (buf[i] & 0x04104107f) ^ 0x041041040; j++) 98 buf[i] = (buf[i] >> 1) | ((__u64)(buf[i] & 1) << (COBRA_LENGTH - 1)); 99 100 if (j < COBRA_LENGTH) ret |= (1 << i); 101 102 data[i] = ((buf[i] >> 7) & 0x000001f) | ((buf[i] >> 8) & 0x00003e0) 103 | ((buf[i] >> 9) & 0x0007c00) | ((buf[i] >> 10) & 0x00f8000) 104 | ((buf[i] >> 11) & 0x1f00000); 105 106 } 107 108 return ret; 109 } 110 111 static void cobra_poll(struct gameport *gameport) 112 { 113 struct cobra *cobra = gameport_get_drvdata(gameport); 114 struct input_dev *dev; 115 unsigned int data[2]; 116 int i, j, r; 117 118 cobra->reads++; 119 120 if ((r = cobra_read_packet(gameport, data)) != cobra->exists) { 121 cobra->bads++; 122 return; 123 } 124 125 for (i = 0; i < 2; i++) 126 if (cobra->exists & r & (1 << i)) { 127 128 dev = cobra->dev[i]; 129 130 input_report_abs(dev, ABS_X, ((data[i] >> 4) & 1) - ((data[i] >> 3) & 1)); 131 input_report_abs(dev, ABS_Y, ((data[i] >> 2) & 1) - ((data[i] >> 1) & 1)); 132 133 for (j = 0; cobra_btn[j]; j++) 134 input_report_key(dev, cobra_btn[j], data[i] & (0x20 << j)); 135 136 input_sync(dev); 137 138 } 139 } 140 141 static int cobra_open(struct input_dev *dev) 142 { 143 struct cobra *cobra = input_get_drvdata(dev); 144 145 gameport_start_polling(cobra->gameport); 146 return 0; 147 } 148 149 static void cobra_close(struct input_dev *dev) 150 { 151 struct cobra *cobra = input_get_drvdata(dev); 152 153 gameport_stop_polling(cobra->gameport); 154 } 155 156 static int cobra_connect(struct gameport *gameport, struct gameport_driver *drv) 157 { 158 struct cobra *cobra; 159 struct input_dev *input_dev; 160 unsigned int data[2]; 161 int i, j; 162 int err; 163 164 cobra = kzalloc(sizeof(struct cobra), GFP_KERNEL); 165 if (!cobra) 166 return -ENOMEM; 167 168 cobra->gameport = gameport; 169 170 gameport_set_drvdata(gameport, cobra); 171 172 err = gameport_open(gameport, drv, GAMEPORT_MODE_RAW); 173 if (err) 174 goto fail1; 175 176 cobra->exists = cobra_read_packet(gameport, data); 177 178 for (i = 0; i < 2; i++) 179 if ((cobra->exists >> i) & data[i] & 1) { 180 printk(KERN_WARNING "cobra.c: Device %d on %s has the Ext bit set. ID is: %d" 181 " Contact vojtech@ucw.cz\n", i, gameport->phys, (data[i] >> 2) & 7); 182 cobra->exists &= ~(1 << i); 183 } 184 185 if (!cobra->exists) { 186 err = -ENODEV; 187 goto fail2; 188 } 189 190 gameport_set_poll_handler(gameport, cobra_poll); 191 gameport_set_poll_interval(gameport, 20); 192 193 for (i = 0; i < 2; i++) { 194 if (~(cobra->exists >> i) & 1) 195 continue; 196 197 cobra->dev[i] = input_dev = input_allocate_device(); 198 if (!input_dev) { 199 err = -ENOMEM; 200 goto fail3; 201 } 202 203 snprintf(cobra->phys[i], sizeof(cobra->phys[i]), 204 "%s/input%d", gameport->phys, i); 205 206 input_dev->name = "Creative Labs Blaster GamePad Cobra"; 207 input_dev->phys = cobra->phys[i]; 208 input_dev->id.bustype = BUS_GAMEPORT; 209 input_dev->id.vendor = GAMEPORT_ID_VENDOR_CREATIVE; 210 input_dev->id.product = 0x0008; 211 input_dev->id.version = 0x0100; 212 input_dev->dev.parent = &gameport->dev; 213 214 input_set_drvdata(input_dev, cobra); 215 216 input_dev->open = cobra_open; 217 input_dev->close = cobra_close; 218 219 input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); 220 input_set_abs_params(input_dev, ABS_X, -1, 1, 0, 0); 221 input_set_abs_params(input_dev, ABS_Y, -1, 1, 0, 0); 222 for (j = 0; cobra_btn[j]; j++) 223 set_bit(cobra_btn[j], input_dev->keybit); 224 225 err = input_register_device(cobra->dev[i]); 226 if (err) 227 goto fail4; 228 } 229 230 return 0; 231 232 fail4: input_free_device(cobra->dev[i]); 233 fail3: while (--i >= 0) 234 if (cobra->dev[i]) 235 input_unregister_device(cobra->dev[i]); 236 fail2: gameport_close(gameport); 237 fail1: gameport_set_drvdata(gameport, NULL); 238 kfree(cobra); 239 return err; 240 } 241 242 static void cobra_disconnect(struct gameport *gameport) 243 { 244 struct cobra *cobra = gameport_get_drvdata(gameport); 245 int i; 246 247 for (i = 0; i < 2; i++) 248 if ((cobra->exists >> i) & 1) 249 input_unregister_device(cobra->dev[i]); 250 gameport_close(gameport); 251 gameport_set_drvdata(gameport, NULL); 252 kfree(cobra); 253 } 254 255 static struct gameport_driver cobra_drv = { 256 .driver = { 257 .name = "cobra", 258 }, 259 .description = DRIVER_DESC, 260 .connect = cobra_connect, 261 .disconnect = cobra_disconnect, 262 }; 263 264 module_gameport_driver(cobra_drv); 265