Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  *  Copyright (c) 1999-2001 Vojtech Pavlik
0004  */
0005 
0006 /*
0007  * Creative Labs Blaster GamePad Cobra driver for Linux
0008  */
0009 
0010 /*
0011  */
0012 
0013 #include <linux/kernel.h>
0014 #include <linux/module.h>
0015 #include <linux/slab.h>
0016 #include <linux/gameport.h>
0017 #include <linux/input.h>
0018 #include <linux/jiffies.h>
0019 
0020 #define DRIVER_DESC "Creative Labs Blaster GamePad Cobra driver"
0021 
0022 MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
0023 MODULE_DESCRIPTION(DRIVER_DESC);
0024 MODULE_LICENSE("GPL");
0025 
0026 #define COBRA_MAX_STROBE    45  /* 45 us max wait for first strobe */
0027 #define COBRA_LENGTH        36
0028 
0029 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 };
0030 
0031 struct cobra {
0032     struct gameport *gameport;
0033     struct input_dev *dev[2];
0034     int reads;
0035     int bads;
0036     unsigned char exists;
0037     char phys[2][32];
0038 };
0039 
0040 static unsigned char cobra_read_packet(struct gameport *gameport, unsigned int *data)
0041 {
0042     unsigned long flags;
0043     unsigned char u, v, w;
0044     __u64 buf[2];
0045     int r[2], t[2];
0046     int i, j, ret;
0047 
0048     int strobe = gameport_time(gameport, COBRA_MAX_STROBE);
0049 
0050     for (i = 0; i < 2; i++) {
0051         r[i] = buf[i] = 0;
0052         t[i] = COBRA_MAX_STROBE;
0053     }
0054 
0055     local_irq_save(flags);
0056 
0057     u = gameport_read(gameport);
0058 
0059     do {
0060         t[0]--; t[1]--;
0061         v = gameport_read(gameport);
0062         for (i = 0, w = u ^ v; i < 2 && w; i++, w >>= 2)
0063             if (w & 0x30) {
0064                 if ((w & 0x30) < 0x30 && r[i] < COBRA_LENGTH && t[i] > 0) {
0065                     buf[i] |= (__u64)((w >> 5) & 1) << r[i]++;
0066                     t[i] = strobe;
0067                     u = v;
0068                 } else t[i] = 0;
0069             }
0070     } while (t[0] > 0 || t[1] > 0);
0071 
0072     local_irq_restore(flags);
0073 
0074     ret = 0;
0075 
0076     for (i = 0; i < 2; i++) {
0077 
0078         if (r[i] != COBRA_LENGTH) continue;
0079 
0080         for (j = 0; j < COBRA_LENGTH && (buf[i] & 0x04104107f) ^ 0x041041040; j++)
0081             buf[i] = (buf[i] >> 1) | ((__u64)(buf[i] & 1) << (COBRA_LENGTH - 1));
0082 
0083         if (j < COBRA_LENGTH) ret |= (1 << i);
0084 
0085         data[i] = ((buf[i] >>  7) & 0x000001f) | ((buf[i] >>  8) & 0x00003e0)
0086             | ((buf[i] >>  9) & 0x0007c00) | ((buf[i] >> 10) & 0x00f8000)
0087             | ((buf[i] >> 11) & 0x1f00000);
0088 
0089     }
0090 
0091     return ret;
0092 }
0093 
0094 static void cobra_poll(struct gameport *gameport)
0095 {
0096     struct cobra *cobra = gameport_get_drvdata(gameport);
0097     struct input_dev *dev;
0098     unsigned int data[2];
0099     int i, j, r;
0100 
0101     cobra->reads++;
0102 
0103     if ((r = cobra_read_packet(gameport, data)) != cobra->exists) {
0104         cobra->bads++;
0105         return;
0106     }
0107 
0108     for (i = 0; i < 2; i++)
0109         if (cobra->exists & r & (1 << i)) {
0110 
0111             dev = cobra->dev[i];
0112 
0113             input_report_abs(dev, ABS_X, ((data[i] >> 4) & 1) - ((data[i] >> 3) & 1));
0114             input_report_abs(dev, ABS_Y, ((data[i] >> 2) & 1) - ((data[i] >> 1) & 1));
0115 
0116             for (j = 0; cobra_btn[j]; j++)
0117                 input_report_key(dev, cobra_btn[j], data[i] & (0x20 << j));
0118 
0119             input_sync(dev);
0120 
0121         }
0122 }
0123 
0124 static int cobra_open(struct input_dev *dev)
0125 {
0126     struct cobra *cobra = input_get_drvdata(dev);
0127 
0128     gameport_start_polling(cobra->gameport);
0129     return 0;
0130 }
0131 
0132 static void cobra_close(struct input_dev *dev)
0133 {
0134     struct cobra *cobra = input_get_drvdata(dev);
0135 
0136     gameport_stop_polling(cobra->gameport);
0137 }
0138 
0139 static int cobra_connect(struct gameport *gameport, struct gameport_driver *drv)
0140 {
0141     struct cobra *cobra;
0142     struct input_dev *input_dev;
0143     unsigned int data[2];
0144     int i, j;
0145     int err;
0146 
0147     cobra = kzalloc(sizeof(struct cobra), GFP_KERNEL);
0148     if (!cobra)
0149         return -ENOMEM;
0150 
0151     cobra->gameport = gameport;
0152 
0153     gameport_set_drvdata(gameport, cobra);
0154 
0155     err = gameport_open(gameport, drv, GAMEPORT_MODE_RAW);
0156     if (err)
0157         goto fail1;
0158 
0159     cobra->exists = cobra_read_packet(gameport, data);
0160 
0161     for (i = 0; i < 2; i++)
0162         if ((cobra->exists >> i) & data[i] & 1) {
0163             printk(KERN_WARNING "cobra.c: Device %d on %s has the Ext bit set. ID is: %d"
0164                 " Contact vojtech@ucw.cz\n", i, gameport->phys, (data[i] >> 2) & 7);
0165             cobra->exists &= ~(1 << i);
0166         }
0167 
0168     if (!cobra->exists) {
0169         err = -ENODEV;
0170         goto fail2;
0171     }
0172 
0173     gameport_set_poll_handler(gameport, cobra_poll);
0174     gameport_set_poll_interval(gameport, 20);
0175 
0176     for (i = 0; i < 2; i++) {
0177         if (~(cobra->exists >> i) & 1)
0178             continue;
0179 
0180         cobra->dev[i] = input_dev = input_allocate_device();
0181         if (!input_dev) {
0182             err = -ENOMEM;
0183             goto fail3;
0184         }
0185 
0186         snprintf(cobra->phys[i], sizeof(cobra->phys[i]),
0187              "%s/input%d", gameport->phys, i);
0188 
0189         input_dev->name = "Creative Labs Blaster GamePad Cobra";
0190         input_dev->phys = cobra->phys[i];
0191         input_dev->id.bustype = BUS_GAMEPORT;
0192         input_dev->id.vendor = GAMEPORT_ID_VENDOR_CREATIVE;
0193         input_dev->id.product = 0x0008;
0194         input_dev->id.version = 0x0100;
0195         input_dev->dev.parent = &gameport->dev;
0196 
0197         input_set_drvdata(input_dev, cobra);
0198 
0199         input_dev->open = cobra_open;
0200         input_dev->close = cobra_close;
0201 
0202         input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
0203         input_set_abs_params(input_dev, ABS_X, -1, 1, 0, 0);
0204         input_set_abs_params(input_dev, ABS_Y, -1, 1, 0, 0);
0205         for (j = 0; cobra_btn[j]; j++)
0206             set_bit(cobra_btn[j], input_dev->keybit);
0207 
0208         err = input_register_device(cobra->dev[i]);
0209         if (err)
0210             goto fail4;
0211     }
0212 
0213     return 0;
0214 
0215  fail4: input_free_device(cobra->dev[i]);
0216  fail3: while (--i >= 0)
0217         if (cobra->dev[i])
0218             input_unregister_device(cobra->dev[i]);
0219  fail2: gameport_close(gameport);
0220  fail1: gameport_set_drvdata(gameport, NULL);
0221     kfree(cobra);
0222     return err;
0223 }
0224 
0225 static void cobra_disconnect(struct gameport *gameport)
0226 {
0227     struct cobra *cobra = gameport_get_drvdata(gameport);
0228     int i;
0229 
0230     for (i = 0; i < 2; i++)
0231         if ((cobra->exists >> i) & 1)
0232             input_unregister_device(cobra->dev[i]);
0233     gameport_close(gameport);
0234     gameport_set_drvdata(gameport, NULL);
0235     kfree(cobra);
0236 }
0237 
0238 static struct gameport_driver cobra_drv = {
0239     .driver     = {
0240         .name   = "cobra",
0241     },
0242     .description    = DRIVER_DESC,
0243     .connect    = cobra_connect,
0244     .disconnect = cobra_disconnect,
0245 };
0246 
0247 module_gameport_driver(cobra_drv);