linux/drivers/input/joystick/interact.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 *  Copyright (c) 2001 Vojtech Pavlik
   4 *
   5 *  Based on the work of:
   6 *      Toby Deshane
   7 */
   8
   9/*
  10 * InterAct digital gamepad/joystick driver for Linux
  11 */
  12
  13/*
  14 */
  15
  16#include <linux/kernel.h>
  17#include <linux/slab.h>
  18#include <linux/module.h>
  19#include <linux/delay.h>
  20#include <linux/gameport.h>
  21#include <linux/input.h>
  22#include <linux/jiffies.h>
  23
  24#define DRIVER_DESC     "InterAct digital joystick driver"
  25
  26MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
  27MODULE_DESCRIPTION(DRIVER_DESC);
  28MODULE_LICENSE("GPL");
  29
  30#define INTERACT_MAX_START      600     /* 400 us */
  31#define INTERACT_MAX_STROBE     60      /* 40 us */
  32#define INTERACT_MAX_LENGTH     32      /* 32 bits */
  33
  34#define INTERACT_TYPE_HHFX      0       /* HammerHead/FX */
  35#define INTERACT_TYPE_PP8D      1       /* ProPad 8 */
  36
  37struct interact {
  38        struct gameport *gameport;
  39        struct input_dev *dev;
  40        int bads;
  41        int reads;
  42        unsigned char type;
  43        unsigned char length;
  44        char phys[32];
  45};
  46
  47static short interact_abs_hhfx[] =
  48        { ABS_RX, ABS_RY, ABS_X, ABS_Y, ABS_HAT0X, ABS_HAT0Y, -1 };
  49static short interact_abs_pp8d[] =
  50        { ABS_X, ABS_Y, -1 };
  51
  52static short interact_btn_hhfx[] =
  53        { BTN_TR, BTN_X, BTN_Y, BTN_Z, BTN_A, BTN_B, BTN_C, BTN_TL, BTN_TL2, BTN_TR2, BTN_MODE, BTN_SELECT, -1 };
  54static short interact_btn_pp8d[] =
  55        { BTN_C, BTN_TL, BTN_TR, BTN_A, BTN_B, BTN_Y, BTN_Z, BTN_X, -1 };
  56
  57struct interact_type {
  58        int id;
  59        short *abs;
  60        short *btn;
  61        char *name;
  62        unsigned char length;
  63        unsigned char b8;
  64};
  65
  66static struct interact_type interact_type[] = {
  67        { 0x6202, interact_abs_hhfx, interact_btn_hhfx, "InterAct HammerHead/FX",    32, 4 },
  68        { 0x53f8, interact_abs_pp8d, interact_btn_pp8d, "InterAct ProPad 8 Digital", 16, 0 },
  69        { 0 }};
  70
  71/*
  72 * interact_read_packet() reads and InterAct joystick data.
  73 */
  74
  75static int interact_read_packet(struct gameport *gameport, int length, u32 *data)
  76{
  77        unsigned long flags;
  78        unsigned char u, v;
  79        unsigned int t, s;
  80        int i;
  81
  82        i = 0;
  83        data[0] = data[1] = data[2] = 0;
  84        t = gameport_time(gameport, INTERACT_MAX_START);
  85        s = gameport_time(gameport, INTERACT_MAX_STROBE);
  86
  87        local_irq_save(flags);
  88        gameport_trigger(gameport);
  89        v = gameport_read(gameport);
  90
  91        while (t > 0 && i < length) {
  92                t--;
  93                u = v; v = gameport_read(gameport);
  94                if (v & ~u & 0x40) {
  95                        data[0] = (data[0] << 1) | ((v >> 4) & 1);
  96                        data[1] = (data[1] << 1) | ((v >> 5) & 1);
  97                        data[2] = (data[2] << 1) | ((v >> 7) & 1);
  98                        i++;
  99                        t = s;
 100                }
 101        }
 102
 103        local_irq_restore(flags);
 104
 105        return i;
 106}
 107
 108/*
 109 * interact_poll() reads and analyzes InterAct joystick data.
 110 */
 111
 112static void interact_poll(struct gameport *gameport)
 113{
 114        struct interact *interact = gameport_get_drvdata(gameport);
 115        struct input_dev *dev = interact->dev;
 116        u32 data[3];
 117        int i;
 118
 119        interact->reads++;
 120
 121        if (interact_read_packet(interact->gameport, interact->length, data) < interact->length) {
 122                interact->bads++;
 123        } else {
 124
 125                for (i = 0; i < 3; i++)
 126                        data[i] <<= INTERACT_MAX_LENGTH - interact->length;
 127
 128                switch (interact->type) {
 129
 130                        case INTERACT_TYPE_HHFX:
 131
 132                                for (i = 0; i < 4; i++)
 133                                        input_report_abs(dev, interact_abs_hhfx[i], (data[i & 1] >> ((i >> 1) << 3)) & 0xff);
 134
 135                                for (i = 0; i < 2; i++)
 136                                        input_report_abs(dev, ABS_HAT0Y - i,
 137                                                ((data[1] >> ((i << 1) + 17)) & 1)  - ((data[1] >> ((i << 1) + 16)) & 1));
 138
 139                                for (i = 0; i < 8; i++)
 140                                        input_report_key(dev, interact_btn_hhfx[i], (data[0] >> (i + 16)) & 1);
 141
 142                                for (i = 0; i < 4; i++)
 143                                        input_report_key(dev, interact_btn_hhfx[i + 8], (data[1] >> (i + 20)) & 1);
 144
 145                                break;
 146
 147                        case INTERACT_TYPE_PP8D:
 148
 149                                for (i = 0; i < 2; i++)
 150                                        input_report_abs(dev, interact_abs_pp8d[i],
 151                                                ((data[0] >> ((i << 1) + 20)) & 1)  - ((data[0] >> ((i << 1) + 21)) & 1));
 152
 153                                for (i = 0; i < 8; i++)
 154                                        input_report_key(dev, interact_btn_pp8d[i], (data[1] >> (i + 16)) & 1);
 155
 156                                break;
 157                }
 158        }
 159
 160        input_sync(dev);
 161}
 162
 163/*
 164 * interact_open() is a callback from the input open routine.
 165 */
 166
 167static int interact_open(struct input_dev *dev)
 168{
 169        struct interact *interact = input_get_drvdata(dev);
 170
 171        gameport_start_polling(interact->gameport);
 172        return 0;
 173}
 174
 175/*
 176 * interact_close() is a callback from the input close routine.
 177 */
 178
 179static void interact_close(struct input_dev *dev)
 180{
 181        struct interact *interact = input_get_drvdata(dev);
 182
 183        gameport_stop_polling(interact->gameport);
 184}
 185
 186/*
 187 * interact_connect() probes for InterAct joysticks.
 188 */
 189
 190static int interact_connect(struct gameport *gameport, struct gameport_driver *drv)
 191{
 192        struct interact *interact;
 193        struct input_dev *input_dev;
 194        __u32 data[3];
 195        int i, t;
 196        int err;
 197
 198        interact = kzalloc(sizeof(struct interact), GFP_KERNEL);
 199        input_dev = input_allocate_device();
 200        if (!interact || !input_dev) {
 201                err = -ENOMEM;
 202                goto fail1;
 203        }
 204
 205        interact->gameport = gameport;
 206        interact->dev = input_dev;
 207
 208        gameport_set_drvdata(gameport, interact);
 209
 210        err = gameport_open(gameport, drv, GAMEPORT_MODE_RAW);
 211        if (err)
 212                goto fail1;
 213
 214        i = interact_read_packet(gameport, INTERACT_MAX_LENGTH * 2, data);
 215
 216        if (i != 32 || (data[0] >> 24) != 0x0c || (data[1] >> 24) != 0x02) {
 217                err = -ENODEV;
 218                goto fail2;
 219        }
 220
 221        for (i = 0; interact_type[i].length; i++)
 222                if (interact_type[i].id == (data[2] >> 16))
 223                        break;
 224
 225        if (!interact_type[i].length) {
 226                printk(KERN_WARNING "interact.c: Unknown joystick on %s. [len %d d0 %08x d1 %08x i2 %08x]\n",
 227                        gameport->phys, i, data[0], data[1], data[2]);
 228                err = -ENODEV;
 229                goto fail2;
 230        }
 231
 232        gameport_set_poll_handler(gameport, interact_poll);
 233        gameport_set_poll_interval(gameport, 20);
 234
 235        snprintf(interact->phys, sizeof(interact->phys), "%s/input0", gameport->phys);
 236
 237        interact->type = i;
 238        interact->length = interact_type[i].length;
 239
 240        input_dev->name = interact_type[i].name;
 241        input_dev->phys = interact->phys;
 242        input_dev->id.bustype = BUS_GAMEPORT;
 243        input_dev->id.vendor = GAMEPORT_ID_VENDOR_INTERACT;
 244        input_dev->id.product = interact_type[i].id;
 245        input_dev->id.version = 0x0100;
 246        input_dev->dev.parent = &gameport->dev;
 247
 248        input_set_drvdata(input_dev, interact);
 249
 250        input_dev->open = interact_open;
 251        input_dev->close = interact_close;
 252
 253        input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
 254
 255        for (i = 0; (t = interact_type[interact->type].abs[i]) >= 0; i++) {
 256                if (i < interact_type[interact->type].b8)
 257                        input_set_abs_params(input_dev, t, 0, 255, 0, 0);
 258                else
 259                        input_set_abs_params(input_dev, t, -1, 1, 0, 0);
 260        }
 261
 262        for (i = 0; (t = interact_type[interact->type].btn[i]) >= 0; i++)
 263                __set_bit(t, input_dev->keybit);
 264
 265        err = input_register_device(interact->dev);
 266        if (err)
 267                goto fail2;
 268
 269        return 0;
 270
 271fail2:  gameport_close(gameport);
 272fail1:  gameport_set_drvdata(gameport, NULL);
 273        input_free_device(input_dev);
 274        kfree(interact);
 275        return err;
 276}
 277
 278static void interact_disconnect(struct gameport *gameport)
 279{
 280        struct interact *interact = gameport_get_drvdata(gameport);
 281
 282        input_unregister_device(interact->dev);
 283        gameport_close(gameport);
 284        gameport_set_drvdata(gameport, NULL);
 285        kfree(interact);
 286}
 287
 288static struct gameport_driver interact_drv = {
 289        .driver         = {
 290                .name   = "interact",
 291        },
 292        .description    = DRIVER_DESC,
 293        .connect        = interact_connect,
 294        .disconnect     = interact_disconnect,
 295};
 296
 297module_gameport_driver(interact_drv);
 298