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