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