[PATCH 2/3] xpad.c: Initial support for xbox360 gamepad.

From: Jan Kratochvil
Date: Wed May 02 2007 - 11:06:02 EST


Xbox 360 gamepad is slightly different then the previous model so it has
its own version of process_packet method.

Detection of this new device relies on USB_DEVICE_INTERFACE_PROTOCOL macro.
This device got vendor specific subclass so it can't be matched with
USB_INTERFACE_INFO and we need only one interface protocol from four
availaible. It means USB_DEVICE can't be used either.

Added xpad360_btn structure with additional buttons for x360 gamepad.

Signed-off-by: Jan Kratochvil <honza@xxxxxxxx>
---
drivers/usb/input/xpad.c | 80 +++++++++++++++++++++++++++++++++++++++++++++-
1 files changed, 79 insertions(+), 1 deletions(-)

diff --git a/drivers/usb/input/xpad.c b/drivers/usb/input/xpad.c
index 2a20aa2..bac9ec2 100644
--- a/drivers/usb/input/xpad.c
+++ b/drivers/usb/input/xpad.c
@@ -8,6 +8,7 @@
* Ivan Hawkes <blackhawk@xxxxxxxxxxxxxx>
* 2005 Dominic Cerquetti <binary1230@xxxxxxxxx>
* 2006 Adam Buchbinder <adam.buchbinder@xxxxxxxxx>
+ * 2007 Jan Kratochvil <honza@xxxxxxxx>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -28,6 +29,7 @@
* - information from http://euc.jp/periphs/xbox-controller.ja.html
* - the iForce driver drivers/char/joystick/iforce.c
* - the skeleton-driver drivers/usb/usb-skeleton.c
+ * - Xbox 360 information http://www.free60.org/wiki/Gamepad
*
* Thanks to:
* - ITO Takayuki for providing essential xpad information on his website
@@ -88,6 +90,7 @@
#define XPAD_FLAGS_DPAD_TO_BUTTONS (1 << 0)
#define XPAD_FLAGS_DPAD_TO_AXES (1 << 1)
#define XPAD_FLAGS_DPAD_UNKNOWN (1 << 2)
+#define XPAD_FLAGS_XBOX360 (1 << 3)

static int dpad_to_buttons;
module_param(dpad_to_buttons, bool, S_IRUGO);
@@ -130,6 +133,7 @@ static const struct xpad_device {
{ 0x102c, 0xff0c, "Joytech Wireless Advanced Controller", XPAD_FLAGS_DPAD_TO_AXES },
{ 0x12ab, 0x8809, "Xbox DDR dancepad", XPAD_FLAGS_DPAD_TO_BUTTONS },
{ 0x1430, 0x8888, "TX6500+ Dance Pad (first generation)", XPAD_FLAGS_DPAD_TO_BUTTONS },
+ { 0x045e, 0x028e, "Microsoft X-Box 360 pad", XPAD_FLAGS_DPAD_TO_AXES | XPAD_FLAGS_XBOX360 },
{ 0xffff, 0xffff, "Chinese-made Xbox Controller", XPAD_FLAGS_DPAD_TO_AXES },
{ 0x0000, 0x0000, "Generic X-Box pad", XPAD_FLAGS_DPAD_UNKNOWN }
};
@@ -140,6 +144,12 @@ static const signed short xpad_btn[] = {
-1 /* terminating entry */
};

+static const signed short xpad360_btn[] = { /* buttons for x360 controller */
+ BTN_TL, BTN_TR, /* Button LB/RB */
+ BTN_MODE, /* The big X button */
+ -1
+};
+
/* only used if XPAD_FLAGS_DPAD_TO_BUTTONS */
static const signed short xpad_btn_pad[] = {
BTN_LEFT, BTN_RIGHT, /* d-pad left, right */
@@ -160,8 +170,12 @@ static const signed short xpad_abs_pad[] = {
-1 /* terminating entry */
};

+/* Xbox 360 has a vendor-specific (sub)class, so we cannot match it with only
+ * USB_INTERFACE_INFO, more to that this device has 4 InterfaceProtocols,
+ * but we need only one of them. */
static struct usb_device_id xpad_table [] = {
{ USB_INTERFACE_INFO('X', 'B', 0) }, /* X-Box USB-IF not approved class */
+ { USB_DEVICE_INTERFACE_PROTOCOL(0x045e, 0x028e, 1) }, /* X-Box 360 controller */
{ }
};

@@ -236,6 +250,64 @@ static void xpad_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *d
input_sync(dev);
}

+/*
+ * xpad360_process_packet
+ *
+ * Completes a request by converting the data into events for the
+ * input subsystem. It is version for xbox 360 controller
+ *
+ * The used report descriptor was taken from:
+ * http://www.free60.org/wiki/Gamepad
+ */
+
+static void xpad360_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *data)
+{
+ struct input_dev *dev = xpad->dev;
+
+ /* digital pad */
+ if (xpad->flags & XPAD_FLAGS_DPAD_TO_AXES) {
+ input_report_abs(dev, ABS_HAT0X, !!(data[2] & 0x01) - !!((data[2] & 0x08) >> 3));
+ input_report_abs(dev, ABS_HAT0Y, !!((data[2] & 0x02) >> 1) - !!((data[2] & 0x04) >> 2));
+ } else if ( xpad->flags & XPAD_FLAGS_DPAD_TO_BUTTONS ) {
+ /* dpad as buttons (right, left, down, up) */
+ input_report_key(dev, BTN_RIGHT, (data[2] & 0x01));
+ input_report_key(dev, BTN_LEFT, (data[2] & 0x08) >> 3);
+ input_report_key(dev, BTN_0, (data[2] & 0x02) >> 1);
+ input_report_key(dev, BTN_1, (data[2] & 0x04) >> 2);
+ }
+
+ /* start/back buttons */
+ input_report_key(dev, BTN_START, (data[2] & 0x10) >> 4);
+ input_report_key(dev, BTN_BACK, (data[2] & 0x20) >> 5);
+
+ /* stick press left/right */
+ input_report_key(dev, BTN_THUMBL, data[2] & 0x40);
+ input_report_key(dev, BTN_THUMBR, data[2] & 0x80);
+
+ /* buttons A,B,X,Y,TL,TR and MODE */
+ input_report_key(dev, BTN_A, (data[3] & 0x10) >> 4);
+ input_report_key(dev, BTN_B, (data[3] & 0x20) >> 5);
+ input_report_key(dev, BTN_X, (data[3] & 0x40) >> 6);
+ input_report_key(dev, BTN_Y, (data[3] & 0x80) >> 7);
+ input_report_key(dev, BTN_TL, data[3] & 0x01 );
+ input_report_key(dev, BTN_TR, (data[3] & 0x02) >> 1);
+ input_report_key(dev, BTN_MODE, (data[3] & 0x04) >> 2);
+
+ /* left stick */
+ input_report_abs(dev, ABS_X, (__s16) (((__s16)data[7] << 8) | (__s16)data[6]));
+ input_report_abs(dev, ABS_Y, ~(__s16) (((__s16)data[9] << 8) | (__s16)data[8]));
+
+ /* right stick */
+ input_report_abs(dev, ABS_RY, ~(__s16) (((__s16)data[13] << 8) | (__s16)data[12]));
+ input_report_abs(dev, ABS_RX, (__s16) (((__s16)data[11] << 8) | (__s16)data[10]));
+
+ /* triggers left/right */
+ input_report_abs(dev, ABS_Z, data[4]);
+ input_report_abs(dev, ABS_RZ, data[5]);
+
+ input_sync(dev);
+}
+
static void xpad_irq_in(struct urb *urb)
{
struct usb_xpad *xpad = urb->context;
@@ -256,7 +328,10 @@ static void xpad_irq_in(struct urb *urb)
goto exit;
}

- xpad_process_packet(xpad, 0, xpad->idata);
+ if (xpad->flags & XPAD_FLAGS_XBOX360)
+ xpad360_process_packet(xpad, 0, xpad->idata);
+ else
+ xpad_process_packet(xpad, 0, xpad->idata);

exit:
retval = usb_submit_urb (urb, GFP_ATOMIC);
@@ -360,6 +435,9 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id
/* set up buttons */
for (i = 0; xpad_btn[i] >= 0; i++)
set_bit(xpad_btn[i], input_dev->keybit);
+ if (xpad->flags & XPAD_FLAGS_XBOX360)
+ for (i = 0; xpad360_btn[i] >= 0; i++)
+ set_bit(xpad360_btn[i], input_dev->keybit);
if (xpad->flags & XPAD_FLAGS_DPAD_TO_BUTTONS)
for (i = 0; xpad_btn_pad[i] >= 0; i++)
set_bit(xpad_btn_pad[i], input_dev->keybit);
--
1.5.0.6


-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/