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