[PATCH] drivers/input/joystick/interact.c ; Linux 2.6.13-1

From: roy wood
Date: Tue Sep 13 2005 - 10:36:30 EST


This patch to the Interact joystick driver adds support for the
"RaiderPro Digital" model of joystick from Interact. The patch is
made against kernel version 2.6.13-1.

I've tested this using a RaiderPro and it works perfectly. I do not
have versions of the other supported Interact controllers that this
driver supports, so I cannot attest to whether I've broken things or
not (hopefully not).

This is my first patch submission, so please don't jump on my head too
hard if I missed something in the LKML FAQ (I did read it!).

Assuming anyone's read this far, I'd appreciate direct feedback via
email on this patch, since I'm not a regular LKML subscriber. I'll
poll the archive for a week or so to keep an eye on the thread, but
direct email would guarantee I don't miss anything.

Also, apparently I need to send this directly to Linus to get this
into the tree. Anyone care to tell me the best email address to use
to do that? I promise not to foreward it to recruiters at MS. :-)



--- linux-2.6.13.1/drivers/input/joystick/interact.c 2005-09-09
22:42:58.000000000 -0400
+++ linux-2.6.13.1-Interact/drivers/input/joystick/interact.c 2005-09-12
16:07:50.000000000 -0400
@@ -5,10 +5,16 @@
*
* Based on the work of:
* Toby Deshane
+ *
+ * History:
+ * --------
+ * 2005-09-12: rrwood - Add support for RaiderPro
*/

/*
* InterAct digital gamepad/joystick driver for Linux
+ *
+ * Note that the "new" Interact web site is http://www.speed-link.com/
*/

/*
@@ -45,45 +51,66 @@
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");

-#define INTERACT_MAX_START 600 /* 400 us */
-#define INTERACT_MAX_STROBE 60 /* 40 us */
+#define INTERACT_MAX_START 600 /* 600 us */
+#define INTERACT_MAX_STROBE 60 /* 60 us */
#define INTERACT_MAX_LENGTH 32 /* 32 bits */

#define INTERACT_TYPE_HHFX 0 /* HammerHead/FX */
#define INTERACT_TYPE_PP8D 1 /* ProPad 8 */
+#define INTERACT_TYPE_RAIDERPRO 2 /* RaiderPro */

struct interact {
- struct gameport *gameport;
- struct input_dev dev;
- int bads;
- int reads;
- unsigned char type;
- unsigned char length;
- char phys[32];
+ struct gameport *gameport; /* Kernel gameport struct ptr */
+ struct input_dev dev; /* Kernel input_dev struct ptr */
+ int bads; /* Count of bad reads from joystick */
+ int reads; /* Count of total reads from joystick */
+ unsigned char type; /* Joystick model index */
+ unsigned char length; /* Number of bits in input packets */
+ char phys[32]; /* Physical device name */
};

-static short interact_abs_hhfx[] =
+
+/* I think the original purpose of setting up lists of controller
+ * axes/buttons was to provide a single location to maintain such
+ * information. Although the table-based approach certainly makes
+ * the interact_connect() code below MUCH simpler and cleaner, the
+ * interact_poll() code ends up being very hard to read, unfortunately.
+ *
+ * I was tempted to either rewrite interact_poll() in a clearer fashion,
+ * or to implement a more comprehensive table-driven decoding approach
+ * (with values for offset, masking, shifting of each value). I'm a
+ * bit leery of making such massive change though, since I don't have the
+ * controllers to test the result. Instead, I'll just add support
+ * for the RaiderPro as clearly as I can.....
+ */
+
+static short interact_abs_hhfx[] =
{ ABS_RX, ABS_RY, ABS_X, ABS_Y, ABS_HAT0X, ABS_HAT0Y, -1 };
static short interact_abs_pp8d[] =
{ ABS_X, ABS_Y, -1 };
+static short interact_abs_raiderpro[] =
+ { ABS_X, ABS_Y, ABS_THROTTLE, ABS_HAT0X, ABS_HAT0Y, -1 };

static short interact_btn_hhfx[] =
{ 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 };
static short interact_btn_pp8d[] =
{ BTN_C, BTN_TL, BTN_TR, BTN_A, BTN_B, BTN_Y, BTN_Z, BTN_X, -1 };
+static short interact_btn_raiderpro[] =
+ { BTN_TRIGGER, BTN_THUMB, BTN_BASE, BTN_BASE2, BTN_BASE3, BTN_BASE4, -1 };

struct interact_type {
- int id;
- short *abs;
- short *btn;
- char *name;
- unsigned char length;
- unsigned char b8;
+ int id; /* Numeric device ID read during configuration */
+ short *abs; /* Pointer to list of axes identifiers */
+ short *btn; /* Pointer to list of button identifiers */
+ char *name; /* Pointer to device model descriptor string */
+ unsigned char length; /* Number of bits in input data packet */
+ unsigned char b8; /* Count of analog (non-hat) axes in *abs list */
};

static struct interact_type interact_type[] = {
{ 0x6202, interact_abs_hhfx, interact_btn_hhfx, "InterAct
HammerHead/FX", 32, 4 },
{ 0x53f8, interact_abs_pp8d, interact_btn_pp8d, "InterAct ProPad 8
Digital", 16, 0 },
+ { 0x51F8, interact_abs_raiderpro, interact_btn_raiderpro, "InterAct
RaiderPro Digital", 32, 3 },
{ 0 }};

/*
@@ -137,44 +164,70 @@
interact->reads++;

if (interact_read_packet(interact->gameport, interact->length, data)
< interact->length) {
+ /* Couldn't read a full packet, so update the bad-count,
+ * queue another read, and get out */
interact->bads++;
- } else {
+ input_sync(dev);
+ return;
+ }
+
+/* During development and debugging, it's nice to see all the read data */
+/* printk("d0:%08X d1:%08X d2:%08X\n", data[0], data[1], data[2]); */

+
+ if (INTERACT_MAX_LENGTH - interact->length > 0) {
+ /* If data packets are less than max length, shift them
+ * for easier processing below (ProPad goofiness) */
for (i = 0; i < 3; i++)
data[i] <<= INTERACT_MAX_LENGTH - interact->length;
+ }

- switch (interact->type) {
-
- case INTERACT_TYPE_HHFX:
-
- for (i = 0; i < 4; i++)
- input_report_abs(dev, interact_abs_hhfx[i], (data[i & 1] >> ((i
>> 1) << 3)) & 0xff);
-
- for (i = 0; i < 2; i++)
- input_report_abs(dev, ABS_HAT0Y - i,
- ((data[1] >> ((i << 1) + 17)) & 1) - ((data[1] >> ((i << 1) +
16)) & 1));
-
- for (i = 0; i < 8; i++)
- input_report_key(dev, interact_btn_hhfx[i], (data[0] >> (i + 16)) & 1);
-
- for (i = 0; i < 4; i++)
- input_report_key(dev, interact_btn_hhfx[i + 8], (data[1] >> (i +
20)) & 1);
-
- break;
-
- case INTERACT_TYPE_PP8D:
+
+ /* Unpack the data we read and pass along the info to the input layer */
+
+ if (interact->type == INTERACT_TYPE_HHFX) {
+ for (i = 0; i < 4; i++)
+ input_report_abs(dev, interact_abs_hhfx[i], (data[i & 1] >> ((i >>
1) << 3)) & 0xff);

- for (i = 0; i < 2; i++)
- input_report_abs(dev, interact_abs_pp8d[i],
- ((data[0] >> ((i << 1) + 20)) & 1) - ((data[0] >> ((i << 1) +
21)) & 1));
+ for (i = 0; i < 2; i++)
+ input_report_abs(dev, ABS_HAT0Y - i, ((data[1] >> ((i << 1) + 17))
& 1) - ((data[1] >> ((i << 1) + 16)) & 1));

- for (i = 0; i < 8; i++)
- input_report_key(dev, interact_btn_pp8d[i], (data[1] >> (i + 16)) & 1);
+ for (i = 0; i < 8; i++)
+ input_report_key(dev, interact_btn_hhfx[i], (data[0] >> (i + 16)) & 1);

- break;
- }
+ for (i = 0; i < 4; i++)
+ input_report_key(dev, interact_btn_hhfx[i + 8], (data[1] >> (i + 20)) & 1);
+ }
+ else if (interact->type == INTERACT_TYPE_PP8D) {
+ for (i = 0; i < 2; i++)
+ input_report_abs(dev, interact_abs_pp8d[i], ((data[0] >> ((i << 1)
+ 20)) & 1) - ((data[0] >> ((i << 1) + 21)) & 1));
+
+ for (i = 0; i < 8; i++)
+ input_report_key(dev, interact_btn_pp8d[i], (data[1] >> (i + 16)) & 1);
+
+ }
+ else if (interact->type == INTERACT_TYPE_RAIDERPRO) {
+ int hatRight = (data[0] >> 20) & 0x01;
+ int hatLeft = (data[0] >> 21) & 0x01;
+ int hatDown = (data[0] >> 22) & 0x01;
+ int hatUp = (data[0] >> 23) & 0x01;
+
+ input_report_abs(dev, ABS_HAT0X, hatRight - hatLeft);
+ input_report_abs(dev, ABS_HAT0Y, hatUp - hatDown);
+
+ input_report_abs(dev, ABS_X, (data[0] >> 8) & 0xff);
+ input_report_abs(dev, ABS_Y, (data[1] >> 8) & 0xff);
+ input_report_abs(dev, ABS_THROTTLE, data[1] & 0xff);
+
+ input_report_key(dev, BTN_BASE4, (data[1] >> 16) & 1);
+ input_report_key(dev, BTN_BASE2, (data[1] >> 19) & 1);
+ input_report_key(dev, BTN_BASE3, (data[1] >> 20) & 1);
+ input_report_key(dev, BTN_THUMB, (data[1] >> 21) & 1);
+ input_report_key(dev, BTN_BASE, (data[1] >> 22) & 1);
+ input_report_key(dev, BTN_TRIGGER, (data[1] >> 23) & 1);
}

+ /* Queue another read */
input_sync(dev);
}

@@ -235,7 +288,7 @@
break;

if (!interact_type[i].length) {
- printk(KERN_WARNING "interact.c: Unknown joystick on %s. [len %d d0
%08x d1 %08x i2 %08x]\n",
+ printk(KERN_WARNING "interact.c: Unknown joystick on %s. [len %d d0
%08x d1 %08x d2 %08x]\n",
gameport->phys, i, data[0], data[1], data[2]);
err = -ENODEV;
goto fail2;
@@ -262,6 +315,10 @@

interact->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);

+ /* This is the one place it's nice to have tables of the axes/buttons,
+ * since it makes it so easy to report the controller characteristics
+ * to the kernel's input layer */
+
for (i = 0; (t = interact_type[interact->type].abs[i]) >= 0; i++) {
set_bit(t, interact->dev.absbit);
if (i < interact_type[interact->type].b8) {
--- linux-2.6.13.1/drivers/input/joystick/interact.c 2005-09-09 22:42:58.000000000 -0400
+++ linux-2.6.13.1-Interact/drivers/input/joystick/interact.c 2005-09-12 16:07:50.000000000 -0400
@@ -5,10 +5,16 @@
*
* Based on the work of:
* Toby Deshane
+ *
+ * History:
+ * --------
+ * 2005-09-12: rrwood - Add support for RaiderPro
*/

/*
* InterAct digital gamepad/joystick driver for Linux
+ *
+ * Note that the "new" Interact web site is http://www.speed-link.com/
*/

/*
@@ -45,45 +51,66 @@
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");

-#define INTERACT_MAX_START 600 /* 400 us */
-#define INTERACT_MAX_STROBE 60 /* 40 us */
+#define INTERACT_MAX_START 600 /* 600 us */
+#define INTERACT_MAX_STROBE 60 /* 60 us */
#define INTERACT_MAX_LENGTH 32 /* 32 bits */

#define INTERACT_TYPE_HHFX 0 /* HammerHead/FX */
#define INTERACT_TYPE_PP8D 1 /* ProPad 8 */
+#define INTERACT_TYPE_RAIDERPRO 2 /* RaiderPro */

struct interact {
- struct gameport *gameport;
- struct input_dev dev;
- int bads;
- int reads;
- unsigned char type;
- unsigned char length;
- char phys[32];
+ struct gameport *gameport; /* Kernel gameport struct ptr */
+ struct input_dev dev; /* Kernel input_dev struct ptr */
+ int bads; /* Count of bad reads from joystick */
+ int reads; /* Count of total reads from joystick */
+ unsigned char type; /* Joystick model index */
+ unsigned char length; /* Number of bits in input packets */
+ char phys[32]; /* Physical device name */
};

-static short interact_abs_hhfx[] =
+
+/* I think the original purpose of setting up lists of controller
+ * axes/buttons was to provide a single location to maintain such
+ * information. Although the table-based approach certainly makes
+ * the interact_connect() code below MUCH simpler and cleaner, the
+ * interact_poll() code ends up being very hard to read, unfortunately.
+ *
+ * I was tempted to either rewrite interact_poll() in a clearer fashion,
+ * or to implement a more comprehensive table-driven decoding approach
+ * (with values for offset, masking, shifting of each value). I'm a
+ * bit leery of making such massive change though, since I don't have the
+ * controllers to test the result. Instead, I'll just add support
+ * for the RaiderPro as clearly as I can.....
+ */
+
+static short interact_abs_hhfx[] =
{ ABS_RX, ABS_RY, ABS_X, ABS_Y, ABS_HAT0X, ABS_HAT0Y, -1 };
static short interact_abs_pp8d[] =
{ ABS_X, ABS_Y, -1 };
+static short interact_abs_raiderpro[] =
+ { ABS_X, ABS_Y, ABS_THROTTLE, ABS_HAT0X, ABS_HAT0Y, -1 };

static short interact_btn_hhfx[] =
{ 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 };
static short interact_btn_pp8d[] =
{ BTN_C, BTN_TL, BTN_TR, BTN_A, BTN_B, BTN_Y, BTN_Z, BTN_X, -1 };
+static short interact_btn_raiderpro[] =
+ { BTN_TRIGGER, BTN_THUMB, BTN_BASE, BTN_BASE2, BTN_BASE3, BTN_BASE4, -1 };

struct interact_type {
- int id;
- short *abs;
- short *btn;
- char *name;
- unsigned char length;
- unsigned char b8;
+ int id; /* Numeric device ID read during configuration */
+ short *abs; /* Pointer to list of axes identifiers */
+ short *btn; /* Pointer to list of button identifiers */
+ char *name; /* Pointer to device model descriptor string */
+ unsigned char length; /* Number of bits in input data packet */
+ unsigned char b8; /* Count of analog (non-hat) axes in *abs list */
};

static struct interact_type interact_type[] = {
{ 0x6202, interact_abs_hhfx, interact_btn_hhfx, "InterAct HammerHead/FX", 32, 4 },
{ 0x53f8, interact_abs_pp8d, interact_btn_pp8d, "InterAct ProPad 8 Digital", 16, 0 },
+ { 0x51F8, interact_abs_raiderpro, interact_btn_raiderpro, "InterAct RaiderPro Digital", 32, 3 },
{ 0 }};

/*
@@ -137,44 +164,70 @@
interact->reads++;

if (interact_read_packet(interact->gameport, interact->length, data) < interact->length) {
+ /* Couldn't read a full packet, so update the bad-count,
+ * queue another read, and get out */
interact->bads++;
- } else {
+ input_sync(dev);
+ return;
+ }
+
+/* During development and debugging, it's nice to see all the read data */
+/* printk("d0:%08X d1:%08X d2:%08X\n", data[0], data[1], data[2]); */

+
+ if (INTERACT_MAX_LENGTH - interact->length > 0) {
+ /* If data packets are less than max length, shift them
+ * for easier processing below (ProPad goofiness) */
for (i = 0; i < 3; i++)
data[i] <<= INTERACT_MAX_LENGTH - interact->length;
+ }

- switch (interact->type) {
-
- case INTERACT_TYPE_HHFX:
-
- for (i = 0; i < 4; i++)
- input_report_abs(dev, interact_abs_hhfx[i], (data[i & 1] >> ((i >> 1) << 3)) & 0xff);
-
- for (i = 0; i < 2; i++)
- input_report_abs(dev, ABS_HAT0Y - i,
- ((data[1] >> ((i << 1) + 17)) & 1) - ((data[1] >> ((i << 1) + 16)) & 1));
-
- for (i = 0; i < 8; i++)
- input_report_key(dev, interact_btn_hhfx[i], (data[0] >> (i + 16)) & 1);
-
- for (i = 0; i < 4; i++)
- input_report_key(dev, interact_btn_hhfx[i + 8], (data[1] >> (i + 20)) & 1);
-
- break;
-
- case INTERACT_TYPE_PP8D:
+
+ /* Unpack the data we read and pass along the info to the input layer */
+
+ if (interact->type == INTERACT_TYPE_HHFX) {
+ for (i = 0; i < 4; i++)
+ input_report_abs(dev, interact_abs_hhfx[i], (data[i & 1] >> ((i >> 1) << 3)) & 0xff);

- for (i = 0; i < 2; i++)
- input_report_abs(dev, interact_abs_pp8d[i],
- ((data[0] >> ((i << 1) + 20)) & 1) - ((data[0] >> ((i << 1) + 21)) & 1));
+ for (i = 0; i < 2; i++)
+ input_report_abs(dev, ABS_HAT0Y - i, ((data[1] >> ((i << 1) + 17)) & 1) - ((data[1] >> ((i << 1) + 16)) & 1));

- for (i = 0; i < 8; i++)
- input_report_key(dev, interact_btn_pp8d[i], (data[1] >> (i + 16)) & 1);
+ for (i = 0; i < 8; i++)
+ input_report_key(dev, interact_btn_hhfx[i], (data[0] >> (i + 16)) & 1);

- break;
- }
+ for (i = 0; i < 4; i++)
+ input_report_key(dev, interact_btn_hhfx[i + 8], (data[1] >> (i + 20)) & 1);
+ }
+ else if (interact->type == INTERACT_TYPE_PP8D) {
+ for (i = 0; i < 2; i++)
+ input_report_abs(dev, interact_abs_pp8d[i], ((data[0] >> ((i << 1) + 20)) & 1) - ((data[0] >> ((i << 1) + 21)) & 1));
+
+ for (i = 0; i < 8; i++)
+ input_report_key(dev, interact_btn_pp8d[i], (data[1] >> (i + 16)) & 1);
+
+ }
+ else if (interact->type == INTERACT_TYPE_RAIDERPRO) {
+ int hatRight = (data[0] >> 20) & 0x01;
+ int hatLeft = (data[0] >> 21) & 0x01;
+ int hatDown = (data[0] >> 22) & 0x01;
+ int hatUp = (data[0] >> 23) & 0x01;
+
+ input_report_abs(dev, ABS_HAT0X, hatRight - hatLeft);
+ input_report_abs(dev, ABS_HAT0Y, hatUp - hatDown);
+
+ input_report_abs(dev, ABS_X, (data[0] >> 8) & 0xff);
+ input_report_abs(dev, ABS_Y, (data[1] >> 8) & 0xff);
+ input_report_abs(dev, ABS_THROTTLE, data[1] & 0xff);
+
+ input_report_key(dev, BTN_BASE4, (data[1] >> 16) & 1);
+ input_report_key(dev, BTN_BASE2, (data[1] >> 19) & 1);
+ input_report_key(dev, BTN_BASE3, (data[1] >> 20) & 1);
+ input_report_key(dev, BTN_THUMB, (data[1] >> 21) & 1);
+ input_report_key(dev, BTN_BASE, (data[1] >> 22) & 1);
+ input_report_key(dev, BTN_TRIGGER, (data[1] >> 23) & 1);
}

+ /* Queue another read */
input_sync(dev);
}

@@ -235,7 +288,7 @@
break;

if (!interact_type[i].length) {
- printk(KERN_WARNING "interact.c: Unknown joystick on %s. [len %d d0 %08x d1 %08x i2 %08x]\n",
+ printk(KERN_WARNING "interact.c: Unknown joystick on %s. [len %d d0 %08x d1 %08x d2 %08x]\n",
gameport->phys, i, data[0], data[1], data[2]);
err = -ENODEV;
goto fail2;
@@ -262,6 +315,10 @@

interact->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);

+ /* This is the one place it's nice to have tables of the axes/buttons,
+ * since it makes it so easy to report the controller characteristics
+ * to the kernel's input layer */
+
for (i = 0; (t = interact_type[interact->type].abs[i]) >= 0; i++) {
set_bit(t, interact->dev.absbit);
if (i < interact_type[interact->type].b8) {