Improved support for the xbox 360 gamepad. Support for setting the LEDs
on the controller upon initialization, fine tuning of the axes and
triggers, and removal of unused buttons from the driver are all
included. The behavior of the original xbox gamepad should not be
affected.
Signed-off-by: Matthew Nicholson <
matt@...>
---
> The drivers maps the left and right stick, up and right directions to
> positive values, this may not be how other drivers handle this. I was
> not able to confirm if this is the case or not, but at least one game I
> have encountered expected the Y axis to be inverted although it makes
> more sense the way it is now.
Here is another stab at the patch this time with the kernel directory
hierarchy correct.
--
Matthew A. Nicholson
matt-land.com
diff -ruN a/drivers/usb/input/xpad.c b/drivers/usb/input/xpad.c
--- a/drivers/usb/input/xpad.c 2006-05-01 22:42:29.000000000 -0500
+++ b/drivers/usb/input/xpad.c 2006-05-02 00:36:58.000000000 -0500
@@ -1,4 +1,4 @@
-/*
+/* vim: set noet sw=8:
* Xbox input device driver for Linux - v0.1.6
*
* Copyright (c) 2002 - 2004 Marko Friedemann <
mfr@...>
@@ -11,6 +11,7 @@
* Franz Lehner <
franz@...>,
* Ivan Hawkes <
blackhawk@...>
* Edgar Hucek <
hostmaster@...>
+ * Matthew Nicholson <
matt@...>
*
*
* This program is free software; you can redistribute it and/or
@@ -30,6 +31,7 @@
*
* This driver is based on:
* - information from
http://euc.jp/periphs/xbox-controller.en.html+ * - information from
http://www.free60.org/wiki/Gamepad * - the iForce driver drivers/char/joystick/iforce.c
* - the skeleton-driver drivers/usb/usb-skeleton.c
*
@@ -110,6 +112,13 @@
BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, /* analogue buttons */
BTN_START, BTN_BACK, BTN_THUMBL, BTN_THUMBR, /* start/back/sticks */
BTN_0, BTN_1, BTN_2, BTN_3, /* d-pad as buttons */
+ -1 /* terminating entry */
+};
+
+static const signed short x360_btn[] = {
+ BTN_A, BTN_B, BTN_X, BTN_Y, /* face buttons */
+ BTN_START, BTN_BACK, BTN_THUMBL, BTN_THUMBR, /* start/back/sticks */
+ BTN_0, BTN_1, BTN_2, BTN_3, /* d-pad as buttons */
BTN_TL, BTN_TR, /* Button LB/RB */
BTN_MODE, /* The big X */
-1 /* terminating entry */
@@ -133,6 +142,13 @@
-1 /* terminating entry */
};
+static const signed short x360_abs[] = {
+ ABS_X, ABS_Y, /* left stick */
+ ABS_RX, ABS_RY, /* right stick */
+ ABS_Z, ABS_RZ, /* triggers left/right */
+ -1 /* terminating entry */
+};
+
static struct usb_device_id xpad_table [] = {
{ USB_INTERFACE_INFO('X', 'B', 0) }, /* Xbox USB-IF not approved class */
{ USB_INTERFACE_INFO( 3 , 0 , 0) }, /* for Joytech Advanced Controller */
@@ -166,6 +182,12 @@
printk("\n");
}
+ if (xpad->is360) {
+ /* make sure this is an input report message */
+ if (data[0] != 0x00)
+ return;
+ }
+
/* digital pad (button mode) bits (3 2 1 0) (right left down up) */
input_report_key(dev, BTN_0, (data[2] & 0x01));
input_report_key(dev, BTN_1, (data[2] & 0x08) >> 3);
@@ -184,8 +206,8 @@
if(xpad->is360) {
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] & 0x80) >> 7);
- input_report_key(dev, BTN_Y, (data[3] & 0x40) >> 6);
+ 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);
@@ -199,11 +221,12 @@
if (xpad->isMat)
return;
- /* left stick (Y axis needs to be flipped) */
+ /* left stick */
if(xpad->is360) {
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) | data[8]));
+ input_report_abs(dev, ABS_Y, (__s16)(((__s16)data[9] << 8) | data[8]));
} else {
+ /* Y axis needs to be flipped */
input_report_abs(dev, ABS_X, (__s16)(((__s16)data[13] << 8) | (__s16)data[12]));
input_report_abs(dev, ABS_Y, ~(__s16)(((__s16)data[15] << 8) | data[14]));
}
@@ -287,6 +310,39 @@
}
/**
+ * xpad_irq_out
+ *
+ * Completion handler for interrupt in transfers (led and rumble output).
+ */
+static void xpad_irq_out(struct urb *urb, struct pt_regs *regs)
+{
+ int retval;
+
+ switch (urb->status) {
+ case 0:
+ /* success */
+ break;
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ /* this urb is terminated, clean up */
+ dbg("%s - urb shutting down with status: %d",
+ __FUNCTION__, urb->status);
+ return;
+ default:
+ dbg("%s - nonzero urb status received: %d",
+ __FUNCTION__, urb->status);
+ goto exit;
+ }
+
+exit:
+ retval = usb_submit_urb(urb, GFP_ATOMIC);
+ if (retval)
+ err("%s - usb_submit_urb failed with result %d",
+ __FUNCTION__, retval);
+}
+
+/**
* xpad_open
*
* Called when a an application opens the device.
@@ -339,7 +395,9 @@
struct usb_xpad *xpad;
struct input_dev *input_dev;
struct usb_endpoint_descriptor *ep_irq_in;
+ struct usb_endpoint_descriptor *ep_irq_out;
int i;
+ int status;
int probedDevNum = -1; /* this takes the index into the known devices
array for the recognized device */
@@ -366,16 +424,32 @@
SLAB_ATOMIC, &xpad->idata_dma);
if (!xpad->idata)
goto fail1;
+
+ xpad->isMat = xpad_device[probedDevNum].isMat;
+ xpad->is360 = xpad_device[probedDevNum].is360;
+
+ /* allocate buffer for led output packets */
+ if (xpad->is360) {
+ xpad->odata_led = usb_buffer_alloc(udev, X360_PKT_LEN_LED,
+ SLAB_ATOMIC, &xpad->odata_led_dma);
+ if (!xpad->odata_led)
+ goto fail2;
+ }
/* setup input interrupt pipe (button and axis state) */
xpad->irq_in = usb_alloc_urb(0, GFP_KERNEL);
if (!xpad->irq_in)
- goto fail2;
+ goto fail3;
+
+ if (xpad->is360) {
+ /* setup output interrupt pipe (led and rumble control) */
+ xpad->irq_led_out = usb_alloc_urb(0, GFP_KERNEL);
+ if (!xpad->irq_led_out)
+ goto fail4;
+ }
xpad->udev = udev;
xpad->dev = input_dev;
- xpad->isMat = xpad_device[probedDevNum].isMat;
- xpad->is360 = xpad_device[probedDevNum].is360;
usb_make_path(udev, xpad->phys, sizeof(xpad->phys));
strlcat(xpad->phys, "/input0", sizeof(xpad->phys));
@@ -399,46 +473,71 @@
set_bit(xpad_mat_btn[i], input_dev->keybit);
} else {
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
+ if (xpad->is360) {
+ for (i = 0; x360_btn[i] >= 0; ++i)
+ set_bit(x360_btn[i], xpad->dev->keybit);
+
+ for (i = 0; x360_abs[i] >= 0; ++i) {
+ signed short t = x360_abs[i];
+
+ set_bit(t, xpad->dev->absbit);
+
+ switch (t) {
+ case ABS_X:
+ case ABS_Y:
+ case ABS_RX:
+ case ABS_RY: /* the two sticks */
+ input_set_abs_params(input_dev, t,
+ -32768, 32767, 16, 5000);
+ break;
+ case ABS_Z: /* left trigger */
+ case ABS_RZ: /* right trigger */
+ input_set_abs_params(input_dev, t,
+ -255, 255, 0, 0);
+ break;
+ }
+ }
- for (i = 0; xpad_btn[i] >= 0; ++i)
- set_bit(xpad_btn[i], input_dev->keybit);
-
- for (i = 0; xpad_abs[i] >= 0; ++i) {
-
- signed short t = xpad_abs[i];
-
- set_bit(t, input_dev->absbit);
-
- switch (t) {
- case ABS_X:
- case ABS_Y:
- case ABS_RX:
- case ABS_RY: /* the two sticks */
- input_set_abs_params(input_dev, t,
- -32768, 32767, 16, 12000);
- break;
- case ABS_Z: /* left trigger */
- case ABS_RZ: /* right trigger */
- case ABS_HAT1X: /* analogue button A */
- case ABS_HAT1Y: /* analogue button B */
- case ABS_HAT2X: /* analogue button C */
- case ABS_HAT2Y: /* analogue button X */
- case ABS_HAT3X: /* analogue button Y */
- case ABS_HAT3Y: /* analogue button Z */
- input_set_abs_params(input_dev, t,
- 0, 255, 0, 0);
- break;
- case ABS_HAT0X:
- case ABS_HAT0Y: /* the d-pad */
- input_set_abs_params(input_dev, t,
- -1, 1, 0, 0);
- break;
+ } else {
+ for (i = 0; xpad_btn[i] >= 0; ++i)
+ set_bit(xpad_btn[i], input_dev->keybit);
+
+ for (i = 0; xpad_abs[i] >= 0; ++i) {
+
+ signed short t = xpad_abs[i];
+
+ set_bit(t, input_dev->absbit);
+
+ switch (t) {
+ case ABS_X:
+ case ABS_Y:
+ case ABS_RX:
+ case ABS_RY: /* the two sticks */
+ input_set_abs_params(input_dev, t,
+ -32768, 32767, 16, 12000);
+ break;
+ case ABS_Z: /* left trigger */
+ case ABS_RZ: /* right trigger */
+ case ABS_HAT1X: /* analogue button A */
+ case ABS_HAT1Y: /* analogue button B */
+ case ABS_HAT2X: /* analogue button C */
+ case ABS_HAT2Y: /* analogue button X */
+ case ABS_HAT3X: /* analogue button Y */
+ case ABS_HAT3Y: /* analogue button Z */
+ input_set_abs_params(input_dev, t,
+ 0, 255, 0, 0);
+ break;
+ case ABS_HAT0X:
+ case ABS_HAT0Y: /* the d-pad */
+ input_set_abs_params(input_dev, t,
+ -1, 1, 0, 0);
+ break;
+ }
}
- }
- if (!xpad->is360)
if (xpad_rumble_probe(udev, xpad, ifnum) != 0)
err("could not init rumble");
+ }
}
/* init input URB for USB INT transfer from device */
@@ -449,12 +548,36 @@
xpad, ep_irq_in->bInterval);
xpad->irq_in->transfer_dma = xpad->idata_dma;
xpad->irq_in->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+ if (xpad->is360) {
+ /* init output URB for USB INT transfer to device */
+ ep_irq_out = &intf->cur_altsetting->endpoint[1].desc;
+ usb_fill_int_urb(xpad->irq_led_out, udev,
+ usb_sndintpipe(udev, ep_irq_out->bEndpointAddress),
+ xpad->odata_led, X360_PKT_LEN_LED,
+ xpad_irq_out, xpad, ep_irq_out->bInterval);
+ xpad->irq_led_out->transfer_dma = xpad->odata_led_dma;
+ xpad->irq_led_out->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+ /* set xpad 360 leds */
+ xpad->odata_led[0] = 0x01; /* message type */
+ xpad->odata_led[1] = 0x03; /* message length */
+ /* xpad->odata_led[2] = 0x00; /* use 0x00 to turn the leds off */
+ xpad->odata_led[2] = 0x06; /* use 0x06 to turn on the '1' */
+
+ if ((status = usb_submit_urb(xpad->irq_led_out, GFP_KERNEL))) {
+ err("sending led output urb failed: %d", status);
+ }
+ }
input_register_device(xpad->dev);
usb_set_intfdata(intf, xpad);
return 0;
+fail4: usb_free_urb(xpad->irq_in);
+fail3: if (xpad->is360)
+ usb_buffer_free(udev, X360_PKT_LEN_LED, xpad->odata_led, xpad->odata_led_dma);
fail2: usb_buffer_free(udev, XPAD_PKT_LEN, xpad->idata, xpad->idata_dma);
fail1: input_free_device(input_dev);
kfree(xpad);
@@ -474,6 +597,10 @@
usb_set_intfdata(intf, NULL);
if (xpad) {
usb_kill_urb(xpad->irq_in);
+
+ if (xpad->is360)
+ usb_kill_urb(xpad->irq_led_out);
+
if(!xpad->is360) {
xpad_rumble_close(xpad);
}
@@ -483,6 +610,12 @@
usb_buffer_free(interface_to_usbdev(intf), XPAD_PKT_LEN,
xpad->idata, xpad->idata_dma);
+
+ if (xpad->is360) {
+ usb_free_urb(xpad->irq_led_out);
+ usb_buffer_free(interface_to_usbdev(intf), X360_PKT_LEN_LED,
+ xpad->odata_led, xpad->odata_led_dma);
+ }
if(!xpad->is360) {
xpad_rumble_disconnect(xpad);
diff -ruN a/drivers/usb/input/xpad.h b/drivers/usb/input/xpad.h
--- a/drivers/usb/input/xpad.h 2006-05-01 22:42:14.000000000 -0500
+++ b/drivers/usb/input/xpad.h 2006-05-01 23:17:56.000000000 -0500
@@ -1,4 +1,4 @@
-/*
+/* vim: set noet sw=8:
* Xbox Controller driver for Linux - v0.1.5
*
* header file containing ioctl definitions
@@ -55,8 +55,11 @@
#define XPAD_MAX_DEVICES 4
#define XPAD_PKT_LEN 32 /* input packet size */
#define XPAD_PKT_LEN_FF 6 /* output packet size - rumble */
+#define X360_PKT_LEN_FF 8 /* output packet size - rumble */
+#define X360_PKT_LEN_LED 3 /* output packet size - led */
#define XPAD_TX_BUFSIZE XPAD_PKT_LEN_FF * 8 /* max. 8 requests */
+#define X360_TX_BUFSIZE X360_PKT_LEN_FF * 8 /* max. 8 requests */
/************************* the device struct **************************/
struct usb_xpad {
@@ -67,6 +70,14 @@
unsigned char *idata; /* input data */
dma_addr_t idata_dma;
+ struct urb *irq_led_out; /* urb for int. transfer of led data */
+ unsigned char *odata_led; /* output data buffer (led) */
+ dma_addr_t odata_led_dma;
+
+ struct urb *irq_ff_out; /* urb for int. transfer of force feedback data */
+ unsigned char *odata_ff; /* output data buffer (force feedback) */
+ dma_addr_t odata_ff_dma;
+
char phys[65]; /* physical input dev path */
unsigned char offsetset_compensation;