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