Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  *  Copyright (c) 2001 Vojtech Pavlik
0004  *
0005  *  Based on the work of:
0006  *  Toby Deshane
0007  */
0008 
0009 /*
0010  * InterAct digital gamepad/joystick driver for Linux
0011  */
0012 
0013 /*
0014  */
0015 
0016 #include <linux/kernel.h>
0017 #include <linux/slab.h>
0018 #include <linux/module.h>
0019 #include <linux/delay.h>
0020 #include <linux/gameport.h>
0021 #include <linux/input.h>
0022 #include <linux/jiffies.h>
0023 
0024 #define DRIVER_DESC "InterAct digital joystick driver"
0025 
0026 MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
0027 MODULE_DESCRIPTION(DRIVER_DESC);
0028 MODULE_LICENSE("GPL");
0029 
0030 #define INTERACT_MAX_START  600 /* 400 us */
0031 #define INTERACT_MAX_STROBE 60  /* 40 us */
0032 #define INTERACT_MAX_LENGTH 32  /* 32 bits */
0033 
0034 #define INTERACT_TYPE_HHFX  0   /* HammerHead/FX */
0035 #define INTERACT_TYPE_PP8D  1   /* ProPad 8 */
0036 
0037 struct interact {
0038     struct gameport *gameport;
0039     struct input_dev *dev;
0040     int bads;
0041     int reads;
0042     unsigned char type;
0043     unsigned char length;
0044     char phys[32];
0045 };
0046 
0047 static short interact_abs_hhfx[] =
0048     { ABS_RX, ABS_RY, ABS_X, ABS_Y, ABS_HAT0X, ABS_HAT0Y, -1 };
0049 static short interact_abs_pp8d[] =
0050     { ABS_X, ABS_Y, -1 };
0051 
0052 static short interact_btn_hhfx[] =
0053     { 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 };
0054 static short interact_btn_pp8d[] =
0055     { BTN_C, BTN_TL, BTN_TR, BTN_A, BTN_B, BTN_Y, BTN_Z, BTN_X, -1 };
0056 
0057 struct interact_type {
0058     int id;
0059     short *abs;
0060     short *btn;
0061     char *name;
0062     unsigned char length;
0063     unsigned char b8;
0064 };
0065 
0066 static struct interact_type interact_type[] = {
0067     { 0x6202, interact_abs_hhfx, interact_btn_hhfx, "InterAct HammerHead/FX",    32, 4 },
0068     { 0x53f8, interact_abs_pp8d, interact_btn_pp8d, "InterAct ProPad 8 Digital", 16, 0 },
0069     { 0 }};
0070 
0071 /*
0072  * interact_read_packet() reads and InterAct joystick data.
0073  */
0074 
0075 static int interact_read_packet(struct gameport *gameport, int length, u32 *data)
0076 {
0077     unsigned long flags;
0078     unsigned char u, v;
0079     unsigned int t, s;
0080     int i;
0081 
0082     i = 0;
0083     data[0] = data[1] = data[2] = 0;
0084     t = gameport_time(gameport, INTERACT_MAX_START);
0085     s = gameport_time(gameport, INTERACT_MAX_STROBE);
0086 
0087     local_irq_save(flags);
0088     gameport_trigger(gameport);
0089     v = gameport_read(gameport);
0090 
0091     while (t > 0 && i < length) {
0092         t--;
0093         u = v; v = gameport_read(gameport);
0094         if (v & ~u & 0x40) {
0095             data[0] = (data[0] << 1) | ((v >> 4) & 1);
0096             data[1] = (data[1] << 1) | ((v >> 5) & 1);
0097             data[2] = (data[2] << 1) | ((v >> 7) & 1);
0098             i++;
0099             t = s;
0100         }
0101     }
0102 
0103     local_irq_restore(flags);
0104 
0105     return i;
0106 }
0107 
0108 /*
0109  * interact_poll() reads and analyzes InterAct joystick data.
0110  */
0111 
0112 static void interact_poll(struct gameport *gameport)
0113 {
0114     struct interact *interact = gameport_get_drvdata(gameport);
0115     struct input_dev *dev = interact->dev;
0116     u32 data[3];
0117     int i;
0118 
0119     interact->reads++;
0120 
0121     if (interact_read_packet(interact->gameport, interact->length, data) < interact->length) {
0122         interact->bads++;
0123     } else {
0124 
0125         for (i = 0; i < 3; i++)
0126             data[i] <<= INTERACT_MAX_LENGTH - interact->length;
0127 
0128         switch (interact->type) {
0129 
0130             case INTERACT_TYPE_HHFX:
0131 
0132                 for (i = 0; i < 4; i++)
0133                     input_report_abs(dev, interact_abs_hhfx[i], (data[i & 1] >> ((i >> 1) << 3)) & 0xff);
0134 
0135                 for (i = 0; i < 2; i++)
0136                     input_report_abs(dev, ABS_HAT0Y - i,
0137                         ((data[1] >> ((i << 1) + 17)) & 1)  - ((data[1] >> ((i << 1) + 16)) & 1));
0138 
0139                 for (i = 0; i < 8; i++)
0140                     input_report_key(dev, interact_btn_hhfx[i], (data[0] >> (i + 16)) & 1);
0141 
0142                 for (i = 0; i < 4; i++)
0143                     input_report_key(dev, interact_btn_hhfx[i + 8], (data[1] >> (i + 20)) & 1);
0144 
0145                 break;
0146 
0147             case INTERACT_TYPE_PP8D:
0148 
0149                 for (i = 0; i < 2; i++)
0150                     input_report_abs(dev, interact_abs_pp8d[i],
0151                         ((data[0] >> ((i << 1) + 20)) & 1)  - ((data[0] >> ((i << 1) + 21)) & 1));
0152 
0153                 for (i = 0; i < 8; i++)
0154                     input_report_key(dev, interact_btn_pp8d[i], (data[1] >> (i + 16)) & 1);
0155 
0156                 break;
0157         }
0158     }
0159 
0160     input_sync(dev);
0161 }
0162 
0163 /*
0164  * interact_open() is a callback from the input open routine.
0165  */
0166 
0167 static int interact_open(struct input_dev *dev)
0168 {
0169     struct interact *interact = input_get_drvdata(dev);
0170 
0171     gameport_start_polling(interact->gameport);
0172     return 0;
0173 }
0174 
0175 /*
0176  * interact_close() is a callback from the input close routine.
0177  */
0178 
0179 static void interact_close(struct input_dev *dev)
0180 {
0181     struct interact *interact = input_get_drvdata(dev);
0182 
0183     gameport_stop_polling(interact->gameport);
0184 }
0185 
0186 /*
0187  * interact_connect() probes for InterAct joysticks.
0188  */
0189 
0190 static int interact_connect(struct gameport *gameport, struct gameport_driver *drv)
0191 {
0192     struct interact *interact;
0193     struct input_dev *input_dev;
0194     __u32 data[3];
0195     int i, t;
0196     int err;
0197 
0198     interact = kzalloc(sizeof(struct interact), GFP_KERNEL);
0199     input_dev = input_allocate_device();
0200     if (!interact || !input_dev) {
0201         err = -ENOMEM;
0202         goto fail1;
0203     }
0204 
0205     interact->gameport = gameport;
0206     interact->dev = input_dev;
0207 
0208     gameport_set_drvdata(gameport, interact);
0209 
0210     err = gameport_open(gameport, drv, GAMEPORT_MODE_RAW);
0211     if (err)
0212         goto fail1;
0213 
0214     i = interact_read_packet(gameport, INTERACT_MAX_LENGTH * 2, data);
0215 
0216     if (i != 32 || (data[0] >> 24) != 0x0c || (data[1] >> 24) != 0x02) {
0217         err = -ENODEV;
0218         goto fail2;
0219     }
0220 
0221     for (i = 0; interact_type[i].length; i++)
0222         if (interact_type[i].id == (data[2] >> 16))
0223             break;
0224 
0225     if (!interact_type[i].length) {
0226         printk(KERN_WARNING "interact.c: Unknown joystick on %s. [len %d d0 %08x d1 %08x i2 %08x]\n",
0227             gameport->phys, i, data[0], data[1], data[2]);
0228         err = -ENODEV;
0229         goto fail2;
0230     }
0231 
0232     gameport_set_poll_handler(gameport, interact_poll);
0233     gameport_set_poll_interval(gameport, 20);
0234 
0235     snprintf(interact->phys, sizeof(interact->phys), "%s/input0", gameport->phys);
0236 
0237     interact->type = i;
0238     interact->length = interact_type[i].length;
0239 
0240     input_dev->name = interact_type[i].name;
0241     input_dev->phys = interact->phys;
0242     input_dev->id.bustype = BUS_GAMEPORT;
0243     input_dev->id.vendor = GAMEPORT_ID_VENDOR_INTERACT;
0244     input_dev->id.product = interact_type[i].id;
0245     input_dev->id.version = 0x0100;
0246     input_dev->dev.parent = &gameport->dev;
0247 
0248     input_set_drvdata(input_dev, interact);
0249 
0250     input_dev->open = interact_open;
0251     input_dev->close = interact_close;
0252 
0253     input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
0254 
0255     for (i = 0; (t = interact_type[interact->type].abs[i]) >= 0; i++) {
0256         if (i < interact_type[interact->type].b8)
0257             input_set_abs_params(input_dev, t, 0, 255, 0, 0);
0258         else
0259             input_set_abs_params(input_dev, t, -1, 1, 0, 0);
0260     }
0261 
0262     for (i = 0; (t = interact_type[interact->type].btn[i]) >= 0; i++)
0263         __set_bit(t, input_dev->keybit);
0264 
0265     err = input_register_device(interact->dev);
0266     if (err)
0267         goto fail2;
0268 
0269     return 0;
0270 
0271 fail2:  gameport_close(gameport);
0272 fail1:  gameport_set_drvdata(gameport, NULL);
0273     input_free_device(input_dev);
0274     kfree(interact);
0275     return err;
0276 }
0277 
0278 static void interact_disconnect(struct gameport *gameport)
0279 {
0280     struct interact *interact = gameport_get_drvdata(gameport);
0281 
0282     input_unregister_device(interact->dev);
0283     gameport_close(gameport);
0284     gameport_set_drvdata(gameport, NULL);
0285     kfree(interact);
0286 }
0287 
0288 static struct gameport_driver interact_drv = {
0289     .driver     = {
0290         .name   = "interact",
0291     },
0292     .description    = DRIVER_DESC,
0293     .connect    = interact_connect,
0294     .disconnect = interact_disconnect,
0295 };
0296 
0297 module_gameport_driver(interact_drv);