[PATCH] (final rfc revision) input: xpad.c - Xbox 360 wireless support and sysfs interface

From: Mike Murphy
Date: Tue Feb 24 2009 - 23:46:25 EST


Greetings,

Attached is what I hope to be the final RFC version of the Xbox driver
patch. I have made the following changes:

1. Removed check for CONFIG_SYSFS (rely on dummy implementations of
the sysfs subsystem whenever sysfs is not enabled). Thanks Frederic
Weisbecker.

2. Fixed backwards ordering of sscanf return check. Thanks Frederic
Weisbecker and Greg K-H.

3. Checked code and made style adjustments to pass automated checks in
scripts/checkpatch.pl. There is one reported error in the xpad.h file,
as the following multi-line preprocessor macros cannot be wrapped in a
do-while loop (lines 279-288):

#define XPAD_XBOX360_VENDOR_PROTOCOL(vend,pr) \
.match_flags = USB_DEVICE_ID_MATCH_VENDOR | \
USB_DEVICE_ID_MATCH_INT_INFO, \
.idVendor = (vend), \
.bInterfaceClass = USB_CLASS_VENDOR_SPEC, \
.bInterfaceSubClass = 93, \
.bInterfaceProtocol = (pr)
#define XPAD_XBOX360_VENDOR(vend) \
{ XPAD_XBOX360_VENDOR_PROTOCOL(vend, 1) }, \
{ XPAD_XBOX360_VENDOR_PROTOCOL(vend, 129) }

I have managed to remove all other errors and warnings, but I'm not
sure all the changes actually enhance code readability.

4. The current full version of the source for the header and C file,
as well as a userspace support file to prevent X11 picking the game
pad up as a pointer input, are available at:
http://cirg.cs.clemson.edu/~mamurph/pub/xpad/

I was _NOT_ able to test the driver with any wired Xbox or Xbox 360
input devices. It turns out that the devices we thought were wired
(Xbox 360 wireless controller connected via a cable) actually
communicate with the receiver wirelessly, and the USB cable merely
provides power. Thus, I would appreciate it if anyone with wired
hardware is able to test this driver and report any bugs.

Assuming there are no more substantive changes, I plan to sign off on
the "final" version of this revision sometime over the weekend. Prior
to this final version, I will make the following "minor" changes:

1. Change default modes on read-write sysfs attributes from 0666 to
0644. Userspace can handle assigning appropriate permissions while
operational. The modes are 0666 for now to permit easier testing.

2. Document the sysfs interface in
Documentation/ABI/testing/game_device-sysfs-interface (perhaps this
needs a better name?)

3. Update Documentation/input/xpad.txt

Thanks,
Mike
--
Mike Murphy
Ph.D. Candidate and NSF Graduate Research Fellow
Clemson University School of Computing
120 McAdams Hall
Clemson, SC 29634-0974 USA
Tel: +1 864.656.2838 Fax: +1 864.656.0145
http://cirg.cs.clemson.edu/~mamurph
diff -uNr origdrv/drivers/input/joystick/xpad.c newdrv/drivers/input/joystick/xpad.c
--- origdrv/drivers/input/joystick/xpad.c 2009-02-14 22:39:20.000000000 -0500
+++ newdrv/drivers/input/joystick/xpad.c 2009-02-23 23:00:58.000000000 -0500
@@ -1,5 +1,8 @@
/*
- * X-Box gamepad driver
+ * Xbox gamepad driver with Xbox 360 wired/wireless support
+ *
+ * Last Modified: 23 February 2009
+ * Mike Murphy <mamurph@xxxxxxxxxxxxxx>
*
* Copyright (c) 2002 Marko Friedemann <mfr@xxxxxxxxxxxxxxx>
* 2004 Oliver Schwartz <Oliver.Schwartz@xxxxxx>,
@@ -9,6 +12,8 @@
* 2005 Dominic Cerquetti <binary1230@xxxxxxxxx>
* 2006 Adam Buchbinder <adam.buchbinder@xxxxxxxxx>
* 2007 Jan Kratochvil <honza@xxxxxxxx>
+ * 2009 Clemson University
+ * (contact: Mike Murphy <mamurph@xxxxxxxxxxxxxx>)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -24,224 +29,313 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
+ * Please see xbox.h for the ChangeLog.
+ */
+
+
+
+#include "xpad.h"
+
+
+/* The dead zone and stick limit both affect the behavior of the corresponding
+ * analog stick, since the output values reported for the stick inputs will
+ * be scaled onto [0,32767]. It is thus necessary to ensure that the dead zone
+ * is never larger than the stick limit. In fact, a minimal amount of stick
+ * travel space (1024) is maintained between the two values. In practice,
+ * however, the stick limit should always be much greater than the dead zone.
+ */
+
+static void set_dead_zone(unsigned int new_size, unsigned int *dz,
+ unsigned int stick_limit)
+{
+ if ((new_size + 1024) >= stick_limit)
+ new_size = (stick_limit > 1024) ? stick_limit - 1024 : 0;
+ *dz = new_size;
+}
+
+static void set_stick_limit(unsigned int new_size, unsigned int *sl,
+ unsigned int dead_zone)
+{
+ if (new_size < (dead_zone + 1024))
+ new_size = dead_zone + 1024;
+ if (new_size > 32767)
+ new_size = 32767;
+ *sl = new_size;
+}
+
+
+/****************************************************************************/
+/*
+ * SysFs interface functions
*
- * This driver is based on:
- * - 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
- * - Vojtech Pavlik - iforce driver / input subsystem
- * - Greg Kroah-Hartman - usb-skeleton driver
- * - XBOX Linux project - extra USB id's
- *
- * TODO:
- * - fine tune axes (especially trigger axes)
- * - fix "analog" buttons (reported as digital now)
- * - get rumble working
- * - need USB IDs for other dance pads
- *
- * History:
- *
- * 2002-06-27 - 0.0.1 : first version, just said "XBOX HID controller"
- *
- * 2002-07-02 - 0.0.2 : basic working version
- * - all axes and 9 of the 10 buttons work (german InterAct device)
- * - the black button does not work
- *
- * 2002-07-14 - 0.0.3 : rework by Vojtech Pavlik
- * - indentation fixes
- * - usb + input init sequence fixes
- *
- * 2002-07-16 - 0.0.4 : minor changes, merge with Vojtech's v0.0.3
- * - verified the lack of HID and report descriptors
- * - verified that ALL buttons WORK
- * - fixed d-pad to axes mapping
- *
- * 2002-07-17 - 0.0.5 : simplified d-pad handling
- *
- * 2004-10-02 - 0.0.6 : DDR pad support
- * - borrowed from the XBOX linux kernel
- * - USB id's for commonly used dance pads are present
- * - dance pads will map D-PAD to buttons, not axes
- * - pass the module paramater 'dpad_to_buttons' to force
- * the D-PAD to map to buttons if your pad is not detected
- *
- * Later changes can be tracked in SCM.
- */
-
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/stat.h>
-#include <linux/module.h>
-#include <linux/usb/input.h>
-
-#define DRIVER_AUTHOR "Marko Friedemann <mfr@xxxxxxxxxxxxxxx>"
-#define DRIVER_DESC "X-Box pad driver"
-
-#define XPAD_PKT_LEN 32
-
-/* xbox d-pads should map to buttons, as is required for DDR pads
- but we map them to axes when possible to simplify things */
-#define MAP_DPAD_TO_BUTTONS 0
-#define MAP_DPAD_TO_AXES 1
-#define MAP_DPAD_UNKNOWN 2
-
-#define XTYPE_XBOX 0
-#define XTYPE_XBOX360 1
-#define XTYPE_XBOX360W 2
-#define XTYPE_UNKNOWN 3
-
-static int dpad_to_buttons;
-module_param(dpad_to_buttons, bool, S_IRUGO);
-MODULE_PARM_DESC(dpad_to_buttons, "Map D-PAD to buttons rather than axes for unknown pads");
-
-static const struct xpad_device {
- u16 idVendor;
- u16 idProduct;
- char *name;
- u8 dpad_mapping;
- u8 xtype;
-} xpad_device[] = {
- { 0x045e, 0x0202, "Microsoft X-Box pad v1 (US)", MAP_DPAD_TO_AXES, XTYPE_XBOX },
- { 0x045e, 0x0289, "Microsoft X-Box pad v2 (US)", MAP_DPAD_TO_AXES, XTYPE_XBOX },
- { 0x045e, 0x0285, "Microsoft X-Box pad (Japan)", MAP_DPAD_TO_AXES, XTYPE_XBOX },
- { 0x045e, 0x0287, "Microsoft Xbox Controller S", MAP_DPAD_TO_AXES, XTYPE_XBOX },
- { 0x045e, 0x0719, "Xbox 360 Wireless Receiver", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360W },
- { 0x0c12, 0x8809, "RedOctane Xbox Dance Pad", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX },
- { 0x044f, 0x0f07, "Thrustmaster, Inc. Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX },
- { 0x046d, 0xc242, "Logitech Chillstream Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX360 },
- { 0x046d, 0xca84, "Logitech Xbox Cordless Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX },
- { 0x046d, 0xca88, "Logitech Compact Controller for Xbox", MAP_DPAD_TO_AXES, XTYPE_XBOX },
- { 0x05fd, 0x1007, "Mad Catz Controller (unverified)", MAP_DPAD_TO_AXES, XTYPE_XBOX },
- { 0x05fd, 0x107a, "InterAct 'PowerPad Pro' X-Box pad (Germany)", MAP_DPAD_TO_AXES, XTYPE_XBOX },
- { 0x0738, 0x4516, "Mad Catz Control Pad", MAP_DPAD_TO_AXES, XTYPE_XBOX },
- { 0x0738, 0x4522, "Mad Catz LumiCON", MAP_DPAD_TO_AXES, XTYPE_XBOX },
- { 0x0738, 0x4526, "Mad Catz Control Pad Pro", MAP_DPAD_TO_AXES, XTYPE_XBOX },
- { 0x0738, 0x4536, "Mad Catz MicroCON", MAP_DPAD_TO_AXES, XTYPE_XBOX },
- { 0x0738, 0x4540, "Mad Catz Beat Pad", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX },
- { 0x0738, 0x4556, "Mad Catz Lynx Wireless Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX },
- { 0x0738, 0x4716, "Mad Catz Wired Xbox 360 Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX360 },
- { 0x0738, 0x6040, "Mad Catz Beat Pad Pro", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX },
- { 0x0c12, 0x8802, "Zeroplus Xbox Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX },
- { 0x0c12, 0x880a, "Pelican Eclipse PL-2023", MAP_DPAD_TO_AXES, XTYPE_XBOX },
- { 0x0c12, 0x8810, "Zeroplus Xbox Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX },
- { 0x0c12, 0x9902, "HAMA VibraX - *FAULTY HARDWARE*", MAP_DPAD_TO_AXES, XTYPE_XBOX },
- { 0x0e4c, 0x1097, "Radica Gamester Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX },
- { 0x0e4c, 0x2390, "Radica Games Jtech Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX },
- { 0x0e6f, 0x0003, "Logic3 Freebird wireless Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX },
- { 0x0e6f, 0x0005, "Eclipse wireless Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX },
- { 0x0e6f, 0x0006, "Edge wireless Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX },
- { 0x0e6f, 0x0006, "Pelican 'TSZ' Wired Xbox 360 Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX360 },
- { 0x0e8f, 0x0201, "SmartJoy Frag Xpad/PS2 adaptor", MAP_DPAD_TO_AXES, XTYPE_XBOX },
- { 0x0f30, 0x0202, "Joytech Advanced Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX },
- { 0x0f30, 0x8888, "BigBen XBMiniPad Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX },
- { 0x102c, 0xff0c, "Joytech Wireless Advanced Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX },
- { 0x12ab, 0x8809, "Xbox DDR dancepad", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX },
- { 0x1430, 0x4748, "RedOctane Guitar Hero X-plorer", MAP_DPAD_TO_AXES, XTYPE_XBOX360 },
- { 0x1430, 0x8888, "TX6500+ Dance Pad (first generation)", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX },
- { 0x045e, 0x028e, "Microsoft X-Box 360 pad", MAP_DPAD_TO_AXES, XTYPE_XBOX360 },
- { 0xffff, 0xffff, "Chinese-made Xbox Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX },
- { 0x0000, 0x0000, "Generic X-Box pad", MAP_DPAD_UNKNOWN, XTYPE_UNKNOWN }
-};
-
-/* buttons shared with xbox and xbox360 */
-static const signed short xpad_common_btn[] = {
- BTN_A, BTN_B, BTN_X, BTN_Y, /* "analog" buttons */
- BTN_START, BTN_BACK, BTN_THUMBL, BTN_THUMBR, /* start/back/sticks */
- -1 /* terminating entry */
-};
-
-/* original xbox controllers only */
-static const signed short xpad_btn[] = {
- BTN_C, BTN_Z, /* "analog" buttons */
- -1 /* terminating entry */
-};
-
-/* only used if MAP_DPAD_TO_BUTTONS */
-static const signed short xpad_btn_pad[] = {
- BTN_LEFT, BTN_RIGHT, /* d-pad left, right */
- BTN_0, BTN_1, /* d-pad up, down (XXX names??) */
- -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
-};
-
-static const signed short xpad_abs[] = {
- ABS_X, ABS_Y, /* left stick */
- ABS_RX, ABS_RY, /* right stick */
- ABS_Z, ABS_RZ, /* triggers left/right */
- -1 /* terminating entry */
-};
-
-/* only used if MAP_DPAD_TO_AXES */
-static const signed short xpad_abs_pad[] = {
- ABS_HAT0X, ABS_HAT0Y, /* d-pad axes */
- -1 /* terminating entry */
-};
-
-/* Xbox 360 has a vendor-specific class, so we cannot match it with only
- * USB_INTERFACE_INFO (also specifically refused by USB subsystem), so we
- * match against vendor id as well. Wired Xbox 360 devices have protocol 1,
- * wireless controllers have protocol 129. */
-#define XPAD_XBOX360_VENDOR_PROTOCOL(vend,pr) \
- .match_flags = USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_INT_INFO, \
- .idVendor = (vend), \
- .bInterfaceClass = USB_CLASS_VENDOR_SPEC, \
- .bInterfaceSubClass = 93, \
- .bInterfaceProtocol = (pr)
-#define XPAD_XBOX360_VENDOR(vend) \
- { XPAD_XBOX360_VENDOR_PROTOCOL(vend,1) }, \
- { XPAD_XBOX360_VENDOR_PROTOCOL(vend,129) }
-
-static struct usb_device_id xpad_table [] = {
- { USB_INTERFACE_INFO('X', 'B', 0) }, /* X-Box USB-IF not approved class */
- XPAD_XBOX360_VENDOR(0x045e), /* Microsoft X-Box 360 controllers */
- XPAD_XBOX360_VENDOR(0x046d), /* Logitech X-Box 360 style controllers */
- XPAD_XBOX360_VENDOR(0x0738), /* Mad Catz X-Box 360 controllers */
- XPAD_XBOX360_VENDOR(0x0e6f), /* 0x0e6f X-Box 360 controllers */
- XPAD_XBOX360_VENDOR(0x1430), /* RedOctane X-Box 360 controllers */
- { }
-};
-
-MODULE_DEVICE_TABLE (usb, xpad_table);
-
-struct usb_xpad {
- struct input_dev *dev; /* input device interface */
- struct usb_device *udev; /* usb device */
-
- int pad_present;
-
- struct urb *irq_in; /* urb for interrupt in report */
- unsigned char *idata; /* input data */
- dma_addr_t idata_dma;
+ * We use common functions, where possible, to implement the show/store
+ * routines. This design saves on code and reduces the burden of adding to or
+ * changing the interface.
+ */

- struct urb *bulk_out;
- unsigned char *bdata;

-#if defined(CONFIG_JOYSTICK_XPAD_FF) || defined(CONFIG_JOYSTICK_XPAD_LEDS)
- struct urb *irq_out; /* urb for interrupt out report */
- unsigned char *odata; /* output data */
- dma_addr_t odata_dma;
- struct mutex odata_mutex;
-#endif
+static ssize_t xpad_show_uint(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct usb_xpad *xpad = to_xpad(dev);
+ unsigned int value;
+ if (attr == &dev_attr_left_dead_zone)
+ value = xpad->left_dead_zone;
+ else if (attr == &dev_attr_right_dead_zone)
+ value = xpad->right_dead_zone;
+ else if (attr == &dev_attr_left_stick_limit)
+ value = xpad->left_stick_limit;
+ else if (attr == &dev_attr_right_stick_limit)
+ value = xpad->right_stick_limit;
+ else
+ return -EIO;
+ return snprintf(buf, PAGE_SIZE, "%u\n", value);
+}

-#if defined(CONFIG_JOYSTICK_XPAD_LEDS)
- struct xpad_led *led;
-#endif

- char phys[64]; /* physical device path */
+static ssize_t xpad_store_uint(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct usb_xpad *xpad = to_xpad(dev);
+ unsigned int new_value;
+ if (sscanf(buf, "%u", &new_value) != 1)
+ return -EIO;
+
+ if (attr == &dev_attr_left_dead_zone)
+ set_dead_zone(new_value, &xpad->left_dead_zone,
+ xpad->left_stick_limit);
+ else if (attr == &dev_attr_right_dead_zone)
+ set_dead_zone(new_value, &xpad->right_dead_zone,
+ xpad->right_stick_limit);
+ else if (attr == &dev_attr_left_stick_limit)
+ set_stick_limit(new_value, &xpad->left_stick_limit,
+ xpad->left_dead_zone);
+ else if (attr == &dev_attr_right_stick_limit)
+ set_stick_limit(new_value, &xpad->right_stick_limit,
+ xpad->right_dead_zone);
+ else
+ return -EIO;
+ return strnlen(buf, PAGE_SIZE);
+}
+
+
+static ssize_t xpad_store_rumble_enable(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct usb_xpad *xpad = to_xpad(dev);
+ int newrumble = xpad->rumble_enable;
+ if (1 == sscanf(buf, "%d", &newrumble))
+ xpad->rumble_enable = (newrumble) ? 1 : 0;
+ return count;
+}
+
+
+/* read-only attributes share a common store function that returns an error */
+static ssize_t xpad_store_ro(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ return -EIO;
+}
+
+
+static ssize_t xpad_show_int(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct usb_xpad *xpad = to_xpad(dev);
+ int value;
+ if (attr == &dev_attr_rumble_enable)
+ value = xpad->rumble_enable;
+ else if (attr == &dev_attr_controller_number)
+ value = xpad->controller_number;
+ else if (attr == &dev_attr_controller_present)
+ value = xpad->controller_present;
+ else if (attr == &dev_attr_controller_type)
+ value = xpad->controller_type;
+ else
+ return -EIO;
+ return snprintf(buf, PAGE_SIZE, "%d\n", value);
+}
+
+
+static ssize_t xpad_show_id(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct usb_xpad *xpad = to_xpad(dev);
+ return snprintf(buf, PAGE_SIZE, "%s\n", xpad->controller_unique_id);
+}
+
+
+/* end of sysfs interface */
+/*****************************************************************************/
+
+/* Input section */
+
+/* xpad_init_controller
+ *
+ * Performs controller setup based on controller type.
+ *
+ * NOTE: xpad->controller_data->controller_type needs to be set BEFORE
+ * calling this function!
+ */
+
+static void xpad_init_controller(struct usb_xpad *xpad)
+{
+ set_stick_limit(XSTICK_LIMIT_DEFAULT, &xpad->left_stick_limit,
+ xpad->left_dead_zone);
+ set_stick_limit(XSTICK_LIMIT_DEFAULT, &xpad->right_stick_limit,
+ xpad->right_dead_zone);
+ set_dead_zone(XDEAD_ZONE_DEFAULT, &xpad->left_dead_zone,
+ xpad->left_stick_limit);
+ set_dead_zone(XDEAD_ZONE_DEFAULT, &xpad->right_dead_zone,
+ xpad->right_stick_limit);
+
+ if (xpad->controller_type == XCONTROLLER_TYPE_GUITAR)
+ xpad->rumble_enable = 0;
+ else if (xpad->controller_type == XCONTROLLER_TYPE_DANCE_PAD)
+ xpad->rumble_enable = 0;
+ else
+ xpad->rumble_enable = 1;
+}
+
+
+/*
+ * xpad_work_controller
+ *
+ * Submits command to set pad number on LED display of wireless 360
+ * controllers. The shared workqueue is used for this purpose, so that
+ * the interrupt handler is kept short.
+ */
+
+static void xpad_work_controller(struct work_struct *w)
+{
+ struct usb_xpad *xpad = container_of(w, struct usb_xpad, work);
+ xpad_send_led_command(xpad, xpad->controller_number + 1);
+ xpad->work_pending = 0;
+}
+
+
+/*
+ * xpad_process_sticks
+ *
+ * Handles stick input, accounting for dead zones and square axes. Based
+ * on the original handlers for the Xbox and Xbox 360 in
+ * xpad_process_packet and xpad360_process_packet, but unified to avoid
+ * duplication.
+ *
+ * Whenever a dead zone is used, each axis is scaled so that moving the
+ * stick slightly out of the dead zone range results in a low axis
+ * value in jstest(1), while moving the stick to the maximum position
+ * along any axis still results in 32767.
+ *
+ * In order to provide the ability to map inputs to a square axis (used
+ * by older games), the left_stick_limit and right_stick_limit can be
+ * set. These limits specify at what point in the raw input coordinates
+ * an axis is reported to be at maximum value (32767 or -32767).
+ *
+ * Both the dead zone and stick limit algorithms are implemented
+ * together as a coordinate transformation from "effective coordinates"
+ * onto the output coordinates (which have absolute values from 0 to
+ * 32767 and are positive or negative based on direction). Effective
+ * coordinates are defined as those input values that are greater than
+ * the dead zone but less than the stick limit on the axis in question.
+ *
+ * DANGER: All denominator values in division operations MUST be checked
+ * for non-zero condition. Dividing by zero inside the kernel can cause
+ * a system deadlock.
+ */
+
+static void xpad_process_sticks(struct usb_xpad *xpad, unsigned char *data)
+{
+ struct input_dev *dev = xpad->dev;
+ int coords[4]; /* x, y, rx, ry */
+ int x_offset, y_offset, rx_offset, ry_offset;
+ int c;
+ int range;
+ int abs_magnitude, adjusted_magnitude, difference, scale_fraction;
+ int dead_zone[2], stick_limit[2];
+
+ dead_zone[0] = xpad->left_dead_zone;
+ dead_zone[1] = xpad->right_dead_zone;
+ stick_limit[0] = xpad->left_stick_limit;
+ stick_limit[1] = xpad->right_stick_limit;
+
+ if (xpad->xtype == XTYPE_XBOX) {
+ x_offset = 12;
+ y_offset = 14;
+ rx_offset = 16;
+ ry_offset = 18;
+ } else {
+ x_offset = 6;
+ y_offset = 8;
+ rx_offset = 10;
+ ry_offset = 12;
+ }
+
+ coords[0] = (__s16) le16_to_cpup((__le16 *)(data + x_offset));
+ coords[1] = ~(__s16) le16_to_cpup((__le16 *)(data + y_offset));
+ coords[2] = (__s16) le16_to_cpup((__le16 *)(data + rx_offset));
+ coords[3] = ~(__s16) le16_to_cpup((__le16 *)(data + ry_offset));
+
+ /* Adjustment for dead zone and square axis */
+ for (c = 0; c < 4; c++) {
+ abs_magnitude = (int) coords[c];
+ if (abs_magnitude < 0)
+ abs_magnitude = -abs_magnitude;
+ adjusted_magnitude = abs_magnitude;
+
+ range = (stick_limit[c/2] - dead_zone[c/2]);
+
+ if (abs_magnitude >= stick_limit[c/2]) {
+ adjusted_magnitude = 32767;
+ } else if (abs_magnitude <= dead_zone[c/2]) {
+ adjusted_magnitude = 0;
+ } else if (range > 0) {
+ difference = 32767 - range;
+ if (difference) {
+ /* DIVISION: difference non-zero */
+ scale_fraction = range / difference;
+ adjusted_magnitude =
+ abs_magnitude - dead_zone[c/2];
+
+ /* Approximate floating-point division with a
+ * "catch-up" scaling algorithm that adds back
+ * to the adjusted_magnitude based on distance
+ * from the origin (0 in adjusted coordinates).
+ * If the range / difference is at least 1,
+ * then 1 needs to be added to the adjusted
+ * magnitude for every scale_fraction units
+ * from the origin. If the range / difference
+ * is less than 1 (0 in integer division),
+ * then divide the difference by the range to
+ * obtain the number of units to add per unit
+ * from the adjusted origin.
+ */
+ if (scale_fraction) {
+ /* DIVISION: scale_fraction non-zero */
+ adjusted_magnitude +=
+ adjusted_magnitude
+ / scale_fraction;
+ } else {
+ /* DIVISION: range non-zero */
+ scale_fraction = difference / range;
+ adjusted_magnitude +=
+ adjusted_magnitude
+ * scale_fraction;
+ }
+ if (adjusted_magnitude > 32767)
+ adjusted_magnitude = 32767;
+ }
+ }
+ coords[c] = (coords[c] < 0) ?
+ -adjusted_magnitude : adjusted_magnitude;
+ }
+
+ input_report_abs(dev, ABS_X, (__s16) coords[0]);
+ input_report_abs(dev, ABS_Y, (__s16) coords[1]);
+ input_report_abs(dev, ABS_RX, (__s16) coords[2]);
+ input_report_abs(dev, ABS_RY, (__s16) coords[3]);
+}

- int dpad_mapping; /* map d-pad to buttons or to axes */
- int xtype; /* type of xbox device */
-};

/*
* xpad_process_packet
@@ -253,21 +347,13 @@
* http://euc.jp/periphs/xbox-controller.ja.html
*/

-static void xpad_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *data)
+static void xpad_process_packet(struct usb_xpad *xpad, u16 cmd,
+ unsigned char *data)
{
struct input_dev *dev = xpad->dev;

- /* left stick */
- input_report_abs(dev, ABS_X,
- (__s16) le16_to_cpup((__le16 *)(data + 12)));
- input_report_abs(dev, ABS_Y,
- ~(__s16) le16_to_cpup((__le16 *)(data + 14)));
-
- /* right stick */
- input_report_abs(dev, ABS_RX,
- (__s16) le16_to_cpup((__le16 *)(data + 16)));
- input_report_abs(dev, ABS_RY,
- ~(__s16) le16_to_cpup((__le16 *)(data + 18)));
+ /* left and right sticks */
+ xpad_process_sticks(xpad, data);

/* triggers left/right */
input_report_abs(dev, ABS_Z, data[10]);
@@ -305,6 +391,7 @@
input_sync(dev);
}

+
/*
* xpad360_process_packet
*
@@ -316,7 +403,7 @@
*/

static void xpad360_process_packet(struct usb_xpad *xpad,
- u16 cmd, unsigned char *data)
+ u16 cmd, unsigned char *data)
{
struct input_dev *dev = xpad->dev;

@@ -351,17 +438,8 @@
input_report_key(dev, BTN_TR, data[3] & 0x02);
input_report_key(dev, BTN_MODE, data[3] & 0x04);

- /* left stick */
- input_report_abs(dev, ABS_X,
- (__s16) le16_to_cpup((__le16 *)(data + 6)));
- input_report_abs(dev, ABS_Y,
- ~(__s16) le16_to_cpup((__le16 *)(data + 8)));
-
- /* right stick */
- input_report_abs(dev, ABS_RX,
- (__s16) le16_to_cpup((__le16 *)(data + 10)));
- input_report_abs(dev, ABS_RY,
- ~(__s16) le16_to_cpup((__le16 *)(data + 12)));
+ /* left and right sticks */
+ xpad_process_sticks(xpad, data);

/* triggers left/right */
input_report_abs(dev, ABS_Z, data[4]);
@@ -370,6 +448,35 @@
input_sync(dev);
}

+
+static void xpad360w_identify_controller(struct usb_xpad *xpad,
+ unsigned char *data)
+{
+ u32 id;
+ int i;
+
+ snprintf(xpad->controller_unique_id, 17,
+ "%02x%02x%02x%02x%02x%02x%02x%02x",
+ data[8], data[9], data[10], data[11], data[12], data[13],
+ data[14], data[15]);
+
+ /* Identify controller type */
+ id = (u32) *(data + 22);
+ xpad->controller_type = XCONTROLLER_TYPE_OTHER;
+ for (i = 0; w360_id[i].id_bytes; i++) {
+ if (id == w360_id[i].id_bytes) {
+ xpad->controller_type =
+ w360_id[i].controller_type;
+ break;
+ }
+ }
+
+ if (id == XCONTROLLER_TYPE_OTHER)
+ printk(KERN_INFO
+ "xpad: unknown wireless controller type: %08x\n", id);
+}
+
+
/*
* xpad360w_process_packet
*
@@ -379,30 +486,64 @@
* Byte.Bit
* 00.1 - Status change: The controller or headset has connected/disconnected
* Bits 01.7 and 01.6 are valid
+ * 01.f - Some kind of unique identifier message (see above)
* 01.7 - Controller present
* 01.6 - Headset present
* 01.1 - Pad state (Bytes 4+) valid
*
*/

-static void xpad360w_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *data)
+static void xpad360w_process_packet(struct usb_xpad *xpad, u16 cmd,
+ unsigned char *data)
{
+ int padnum = 0;
+
/* Presence change */
if (data[0] & 0x08) {
+ padnum = xpad->controller_number;
if (data[1] & 0x80) {
- xpad->pad_present = 1;
- usb_submit_urb(xpad->bulk_out, GFP_ATOMIC);
- } else
- xpad->pad_present = 0;
+ /* ignore duplicates */
+ if (!xpad->controller_present) {
+ xpad->controller_present = 1;
+
+ if (xpad->work_pending)
+ PREPARE_WORK(&xpad->work,
+ &xpad_work_controller);
+ else
+ INIT_WORK(&xpad->work,
+ &xpad_work_controller);
+ xpad->work_pending = 1;
+ schedule_work(&xpad->work);
+ kobject_uevent(&xpad->dev->dev.kobj,
+ KOBJ_ONLINE);
+ }
+ } else {
+ kobject_uevent(&xpad->dev->dev.kobj, KOBJ_OFFLINE);
+ xpad->controller_present = 0;
+ xpad->controller_unique_id[0] = '\0';
+ xpad->controller_type = XCONTROLLER_TYPE_NONE;
+ /* We do NOT flush the shared workqueue here, because
+ * this function is called from an interrupt handler.
+ * If the controller has disconnected from the receiver,
+ * the worst that will happen from the work task running
+ * is that a packet will be transmitted from the
+ * receiver to a non-listening controller
+ */
+ }
}

- /* Valid pad data */
- if (!(data[1] & 0x1))
- return;
-
- xpad360_process_packet(xpad, cmd, &data[4]);
+ /* Process packets according to type */
+ if (data[1] == 0x0f) {
+ if (!xpad->controller_unique_id[0]) {
+ xpad360w_identify_controller(xpad, data);
+ xpad_init_controller(xpad);
+ }
+ } else if (data[1] & 0x1) {
+ xpad360_process_packet(xpad, cmd, &data[4]);
+ }
}

+
static void xpad_irq_in(struct urb *urb)
{
struct usb_xpad *xpad = urb->context;
@@ -439,30 +580,22 @@
}

exit:
- retval = usb_submit_urb (urb, GFP_ATOMIC);
+ retval = usb_submit_urb(urb, GFP_ATOMIC);
if (retval)
- err ("%s - usb_submit_urb failed with result %d",
+ err("%s - usb_submit_urb failed with result %d",
__func__, retval);
}

-static void xpad_bulk_out(struct urb *urb)
-{
- 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", __func__, urb->status);
- break;
- default:
- dbg("%s - nonzero urb status received: %d", __func__, urb->status);
- }
-}
+
+/* end input section */
+
+/*****************************************************************************/
+/* IRQ output section: present in object code only if the force feedback or
+ * LED interface is enabled.
+ */

#if defined(CONFIG_JOYSTICK_XPAD_FF) || defined(CONFIG_JOYSTICK_XPAD_LEDS)
+
static void xpad_irq_out(struct urb *urb)
{
int retval, status;
@@ -470,20 +603,20 @@
status = urb->status;

switch (status) {
- case 0:
+ case 0:
/* success */
break;
- case -ECONNRESET:
- case -ENOENT:
- case -ESHUTDOWN:
- /* this urb is terminated, clean up */
- dbg("%s - urb shutting down with status: %d",
- __func__, status);
- return;
- default:
- dbg("%s - nonzero urb status received: %d",
- __func__, status);
- goto exit;
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ /* this urb is terminated, clean up */
+ dbg("%s - urb shutting down with status: %d",
+ __func__, status);
+ return;
+ default:
+ dbg("%s - nonzero urb status received: %d",
+ __func__, status);
+ goto exit;
}

exit:
@@ -493,12 +626,13 @@
__func__, retval);
}

+
static int xpad_init_output(struct usb_interface *intf, struct usb_xpad *xpad)
{
struct usb_endpoint_descriptor *ep_irq_out;
int error = -ENOMEM;

- if (xpad->xtype != XTYPE_XBOX360)
+ if ((xpad->xtype != XTYPE_XBOX360) && (xpad->xtype != XTYPE_XBOX360W))
return 0;

xpad->odata = usb_buffer_alloc(xpad->udev, XPAD_PKT_LEN,
@@ -514,7 +648,8 @@

ep_irq_out = &intf->cur_altsetting->endpoint[1].desc;
usb_fill_int_urb(xpad->irq_out, xpad->udev,
- usb_sndintpipe(xpad->udev, ep_irq_out->bEndpointAddress),
+ usb_sndintpipe(xpad->udev,
+ ep_irq_out->bEndpointAddress),
xpad->odata, XPAD_PKT_LEN,
xpad_irq_out, xpad, ep_irq_out->bInterval);
xpad->irq_out->transfer_dma = xpad->odata_dma;
@@ -526,45 +661,82 @@
fail1: return error;
}

+
static void xpad_stop_output(struct usb_xpad *xpad)
{
- if (xpad->xtype == XTYPE_XBOX360)
+ if ((xpad->xtype == XTYPE_XBOX360) || (xpad->xtype == XTYPE_XBOX360W))
usb_kill_urb(xpad->irq_out);
}

+
static void xpad_deinit_output(struct usb_xpad *xpad)
{
- if (xpad->xtype == XTYPE_XBOX360) {
+ if ((xpad->xtype == XTYPE_XBOX360) || (xpad->xtype == XTYPE_XBOX360W)) {
usb_free_urb(xpad->irq_out);
usb_buffer_free(xpad->udev, XPAD_PKT_LEN,
xpad->odata, xpad->odata_dma);
}
}
+
#else
-static int xpad_init_output(struct usb_interface *intf, struct usb_xpad *xpad) { return 0; }
+/* Dummy implementations for xpad_probe and xpad_disconnect */
+static int xpad_init_output(struct usb_interface *intf, struct usb_xpad *xpad)
+ { return 0; }
static void xpad_deinit_output(struct usb_xpad *xpad) {}
static void xpad_stop_output(struct usb_xpad *xpad) {}
#endif

-#ifdef CONFIG_JOYSTICK_XPAD_FF
+/* end output section */
+
+/*****************************************************************************/
+
+/* Force feedback (rumble effect) section, depends on CONFIG_JOYSTICK_XPAD_FF */
+
+#if defined(CONFIG_JOYSTICK_XPAD_FF)
+
+/* Rumble support for wireless controllers follows protocol description
+ * from xboxdrv userspace driver:
+ * http://pingus.seul.org/~grumbel/xboxdrv/
+ */
static int xpad_play_effect(struct input_dev *dev, void *data,
struct ff_effect *effect)
{
struct usb_xpad *xpad = input_get_drvdata(dev);

+ if (!xpad->rumble_enable)
+ return 0;
+
if (effect->type == FF_RUMBLE) {
__u16 strong = effect->u.rumble.strong_magnitude;
__u16 weak = effect->u.rumble.weak_magnitude;
- xpad->odata[0] = 0x00;
- xpad->odata[1] = 0x08;
- xpad->odata[2] = 0x00;
- xpad->odata[3] = strong / 256;
- xpad->odata[4] = weak / 256;
- xpad->odata[5] = 0x00;
- xpad->odata[6] = 0x00;
- xpad->odata[7] = 0x00;
- xpad->irq_out->transfer_buffer_length = 8;
+ mutex_lock(&xpad->odata_mutex);
+ if (xpad->xtype == XTYPE_XBOX360W) {
+ xpad->odata[0] = 0x00;
+ xpad->odata[1] = 0x01;
+ xpad->odata[2] = 0x0f;
+ xpad->odata[3] = 0xc0;
+ xpad->odata[4] = 0x00;
+ xpad->odata[5] = strong / 256;
+ xpad->odata[6] = weak / 256;
+ xpad->odata[7] = 0x00;
+ xpad->odata[8] = 0x00;
+ xpad->odata[9] = 0x00;
+ xpad->odata[10] = 0x00;
+ xpad->odata[11] = 0x00;
+ xpad->irq_out->transfer_buffer_length = 12;
+ } else {
+ xpad->odata[0] = 0x00;
+ xpad->odata[1] = 0x08;
+ xpad->odata[2] = 0x00;
+ xpad->odata[3] = strong / 256;
+ xpad->odata[4] = weak / 256;
+ xpad->odata[5] = 0x00;
+ xpad->odata[6] = 0x00;
+ xpad->odata[7] = 0x00;
+ xpad->irq_out->transfer_buffer_length = 8;
+ }
usb_submit_urb(xpad->irq_out, GFP_KERNEL);
+ mutex_unlock(&xpad->odata_mutex);
}

return 0;
@@ -572,7 +744,7 @@

static int xpad_init_ff(struct usb_xpad *xpad)
{
- if (xpad->xtype != XTYPE_XBOX360)
+ if ((xpad->xtype != XTYPE_XBOX360) && (xpad->xtype != XTYPE_XBOX360W))
return 0;

input_set_capability(xpad->dev, EV_FF, FF_RUMBLE);
@@ -581,26 +753,49 @@
}

#else
+/* dummy implementation for xpad_probe */
static int xpad_init_ff(struct usb_xpad *xpad) { return 0; }
#endif

+
+/* end force feedback section */
+
+/*****************************************************************************/
+
+/* LED handling section: provides support for the ring of LEDs on the 360
+ * controllers. */
+
#if defined(CONFIG_JOYSTICK_XPAD_LEDS)
-#include <linux/leds.h>

-struct xpad_led {
- char name[16];
- struct led_classdev led_cdev;
- struct usb_xpad *xpad;
-};

+/* XBox 360 wireless controller follows protocol from xboxdrv userspace
+ * driver:
+ * http://pingus.seul.org/~grumbel/xboxdrv/
+ */
static void xpad_send_led_command(struct usb_xpad *xpad, int command)
{
if (command >= 0 && command < 14) {
mutex_lock(&xpad->odata_mutex);
- xpad->odata[0] = 0x01;
- xpad->odata[1] = 0x03;
- xpad->odata[2] = command;
- xpad->irq_out->transfer_buffer_length = 3;
+ if (xpad->xtype == XTYPE_XBOX360W) {
+ xpad->odata[0] = 0x00;
+ xpad->odata[1] = 0x00;
+ xpad->odata[2] = 0x08;
+ xpad->odata[3] = 0x40 + (command % 0x0e);
+ xpad->odata[4] = 0x00;
+ xpad->odata[5] = 0x00;
+ xpad->odata[6] = 0x00;
+ xpad->odata[7] = 0x00;
+ xpad->odata[8] = 0x00;
+ xpad->odata[9] = 0x00;
+ xpad->odata[10] = 0x00;
+ xpad->odata[11] = 0x00;
+ xpad->irq_out->transfer_buffer_length = 12;
+ } else {
+ xpad->odata[0] = 0x01;
+ xpad->odata[1] = 0x03;
+ xpad->odata[2] = command;
+ xpad->irq_out->transfer_buffer_length = 3;
+ }
usb_submit_urb(xpad->irq_out, GFP_KERNEL);
mutex_unlock(&xpad->odata_mutex);
}
@@ -615,6 +810,7 @@
xpad_send_led_command(xpad_led->xpad, value);
}

+
static int xpad_led_probe(struct usb_xpad *xpad)
{
static atomic_t led_seq = ATOMIC_INIT(0);
@@ -623,7 +819,7 @@
struct led_classdev *led_cdev;
int error;

- if (xpad->xtype != XTYPE_XBOX360)
+ if ((xpad->xtype != XTYPE_XBOX360) && (xpad->xtype != XTYPE_XBOX360W))
return 0;

xpad->led = led = kzalloc(sizeof(struct xpad_led), GFP_KERNEL);
@@ -664,17 +860,23 @@
}
}
#else
+/* dummies for xpad_probe and xpad_disconnect */
static int xpad_led_probe(struct usb_xpad *xpad) { return 0; }
static void xpad_led_disconnect(struct usb_xpad *xpad) { }
#endif

+/* end LED section */
+
+/*****************************************************************************/
+
+/* Module and device functions */

static int xpad_open(struct input_dev *dev)
{
struct usb_xpad *xpad = input_get_drvdata(dev);

/* URB was submitted in probe */
- if(xpad->xtype == XTYPE_XBOX360W)
+ if (xpad->xtype == XTYPE_XBOX360W)
return 0;

xpad->irq_in->dev = xpad->udev;
@@ -688,7 +890,7 @@
{
struct usb_xpad *xpad = input_get_drvdata(dev);

- if(xpad->xtype != XTYPE_XBOX360W)
+ if (xpad->xtype != XTYPE_XBOX360W)
usb_kill_urb(xpad->irq_in);
xpad_stop_output(xpad);
}
@@ -715,18 +917,22 @@
}
}

-static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id)
+static int xpad_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
{
struct usb_device *udev = interface_to_usbdev(intf);
struct usb_xpad *xpad;
struct input_dev *input_dev;
struct usb_endpoint_descriptor *ep_irq_in;
+ int controller_type;
int i;
int error = -ENOMEM;

for (i = 0; xpad_device[i].idVendor; i++) {
- if ((le16_to_cpu(udev->descriptor.idVendor) == xpad_device[i].idVendor) &&
- (le16_to_cpu(udev->descriptor.idProduct) == xpad_device[i].idProduct))
+ if ((le16_to_cpu(udev->descriptor.idVendor) ==
+ xpad_device[i].idVendor) &&
+ (le16_to_cpu(udev->descriptor.idProduct) ==
+ xpad_device[i].idProduct))
break;
}

@@ -747,11 +953,14 @@
xpad->udev = udev;
xpad->dpad_mapping = xpad_device[i].dpad_mapping;
xpad->xtype = xpad_device[i].xtype;
+ controller_type = xpad_device[i].controller_type;
if (xpad->dpad_mapping == MAP_DPAD_UNKNOWN)
xpad->dpad_mapping = !dpad_to_buttons;
if (xpad->xtype == XTYPE_UNKNOWN) {
- if (intf->cur_altsetting->desc.bInterfaceClass == USB_CLASS_VENDOR_SPEC) {
- if (intf->cur_altsetting->desc.bInterfaceProtocol == 129)
+ if (intf->cur_altsetting->desc.bInterfaceClass ==
+ USB_CLASS_VENDOR_SPEC) {
+ if (intf->cur_altsetting->desc.bInterfaceProtocol ==
+ 129)
xpad->xtype = XTYPE_XBOX360W;
else
xpad->xtype = XTYPE_XBOX360;
@@ -783,16 +992,18 @@
else
for (i = 0; xpad_btn[i] >= 0; i++)
set_bit(xpad_btn[i], input_dev->keybit);
- if (xpad->dpad_mapping == MAP_DPAD_TO_BUTTONS)
+ if (xpad->dpad_mapping == MAP_DPAD_TO_BUTTONS) {
for (i = 0; xpad_btn_pad[i] >= 0; i++)
set_bit(xpad_btn_pad[i], input_dev->keybit);
+ }

/* set up axes */
for (i = 0; xpad_abs[i] >= 0; i++)
xpad_set_up_abs(input_dev, xpad_abs[i]);
- if (xpad->dpad_mapping == MAP_DPAD_TO_AXES)
+ if (xpad->dpad_mapping == MAP_DPAD_TO_AXES) {
for (i = 0; xpad_abs_pad[i] >= 0; i++)
- xpad_set_up_abs(input_dev, xpad_abs_pad[i]);
+ xpad_set_up_abs(input_dev, xpad_abs_pad[i]);
+ }

error = xpad_init_output(intf, xpad);
if (error)
@@ -820,6 +1031,10 @@

usb_set_intfdata(intf, xpad);

+ xpad->controller_type = controller_type;
+ if (controller_type != XCONTROLLER_TYPE_NONE)
+ xpad_init_controller(xpad);
+
/*
* Submit the int URB immediatly rather than waiting for open
* because we get status messages from the device whether
@@ -828,48 +1043,38 @@
* we're waiting for.
*/
if (xpad->xtype == XTYPE_XBOX360W) {
+ xpad->controller_present = 0;
+ xpad->controller_unique_id[0] = '\0';
+ xpad->controller_number =
+ (intf->cur_altsetting->desc.bInterfaceNumber / 2) + 1;
xpad->irq_in->dev = xpad->udev;
error = usb_submit_urb(xpad->irq_in, GFP_KERNEL);
if (error)
- goto fail4;
-
- /*
- * Setup the message to set the LEDs on the
- * controller when it shows up
- */
- xpad->bulk_out = usb_alloc_urb(0, GFP_KERNEL);
- if(!xpad->bulk_out)
goto fail5;
+ } else {
+ xpad->controller_present = 1;
+ strncpy(xpad->controller_unique_id, "wired", 17);
+ xpad->controller_number = 0;
+ }

- xpad->bdata = kzalloc(XPAD_PKT_LEN, GFP_KERNEL);
- if(!xpad->bdata)
- goto fail6;
-
- xpad->bdata[2] = 0x08;
- switch (intf->cur_altsetting->desc.bInterfaceNumber) {
- case 0:
- xpad->bdata[3] = 0x42;
- break;
- case 2:
- xpad->bdata[3] = 0x43;
- break;
- case 4:
- xpad->bdata[3] = 0x44;
- break;
- case 6:
- xpad->bdata[3] = 0x45;
- }
-
- ep_irq_in = &intf->cur_altsetting->endpoint[1].desc;
- usb_fill_bulk_urb(xpad->bulk_out, udev,
- usb_sndbulkpipe(udev, ep_irq_in->bEndpointAddress),
- xpad->bdata, XPAD_PKT_LEN, xpad_bulk_out, xpad);
+ /* Set up device attributes */
+ xpad->sysfs_ok = 1;
+ error = sysfs_create_group(&input_dev->dev.kobj,
+ &xpad_default_attr_group);
+ if (error) {
+ /* Driver will work without the sysfs interface, but parameters
+ * will not be adjustable, so this failure is a warning. */
+ printk(KERN_WARNING
+ "xpad: sysfs_create_group failed with error %d\n",
+ error);
+ xpad->sysfs_ok = 0;
}

return 0;

- fail6: usb_free_urb(xpad->bulk_out);
- fail5: usb_kill_urb(xpad->irq_in);
+ fail5: usb_set_intfdata(intf, NULL);
+ input_unregister_device(xpad->dev);
+ xpad_led_disconnect(xpad);
fail4: usb_free_urb(xpad->irq_in);
fail3: xpad_deinit_output(xpad);
fail2: usb_buffer_free(udev, XPAD_PKT_LEN, xpad->idata, xpad->idata_dma);
@@ -881,18 +1086,25 @@

static void xpad_disconnect(struct usb_interface *intf)
{
- struct usb_xpad *xpad = usb_get_intfdata (intf);
+ struct usb_xpad *xpad = usb_get_intfdata(intf);

usb_set_intfdata(intf, NULL);
if (xpad) {
+ /* If a work task remains, get rid of it by flushing the
+ * shared work queue.
+ */
+ if (xpad->work_pending)
+ flush_scheduled_work();
+
+ if (xpad->sysfs_ok)
+ sysfs_remove_group(&xpad->dev->dev.kobj,
+ &xpad_default_attr_group);
+
xpad_led_disconnect(xpad);
input_unregister_device(xpad->dev);
xpad_deinit_output(xpad);
- if (xpad->xtype == XTYPE_XBOX360W) {
- usb_kill_urb(xpad->bulk_out);
- usb_free_urb(xpad->bulk_out);
+ if (xpad->xtype == XTYPE_XBOX360W)
usb_kill_urb(xpad->irq_in);
- }
usb_free_urb(xpad->irq_in);
usb_buffer_free(xpad->udev, XPAD_PKT_LEN,
xpad->idata, xpad->idata_dma);
@@ -900,12 +1112,7 @@
}
}

-static struct usb_driver xpad_driver = {
- .name = "xpad",
- .probe = xpad_probe,
- .disconnect = xpad_disconnect,
- .id_table = xpad_table,
-};
+

static int __init usb_xpad_init(void)
{
@@ -920,9 +1127,4 @@
usb_deregister(&xpad_driver);
}

-module_init(usb_xpad_init);
-module_exit(usb_xpad_exit);

-MODULE_AUTHOR(DRIVER_AUTHOR);
-MODULE_DESCRIPTION(DRIVER_DESC);
-MODULE_LICENSE("GPL");
diff -uNr origdrv/drivers/input/joystick/xpad.h newdrv/drivers/input/joystick/xpad.h
--- origdrv/drivers/input/joystick/xpad.h 1969-12-31 19:00:00.000000000 -0500
+++ newdrv/drivers/input/joystick/xpad.h 2009-02-23 23:09:31.000000000 -0500
@@ -0,0 +1,591 @@
+/*
+ * Xbox gamepad driver with Xbox 360 wired/wireless support
+ *
+ * Last Modified: 23 February 2009
+ * Mike Murphy <mamurph@xxxxxxxxxxxxxx>
+ *
+ * Copyright (c) 2002 Marko Friedemann <mfr@xxxxxxxxxxxxxxx>
+ * 2004 Oliver Schwartz <Oliver.Schwartz@xxxxxx>,
+ * Steven Toth <steve@xxxxxxxxxxxxxxxx>,
+ * Franz Lehner <franz@xxxxxxx>,
+ * Ivan Hawkes <blackhawk@xxxxxxxxxxxxxx>
+ * 2005 Dominic Cerquetti <binary1230@xxxxxxxxx>
+ * 2006 Adam Buchbinder <adam.buchbinder@xxxxxxxxx>
+ * 2007 Jan Kratochvil <honza@xxxxxxxx>
+ * 2009 Clemson University
+ * (contact: Mike Murphy <mamurph@xxxxxxxxxxxxxx>)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * This driver is based on:
+ * - 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
+ * - xboxdrv docs http://pingus.seul.org/~grumbel/xboxdrv/
+ *
+ * Thanks to:
+ * - ITO Takayuki for providing essential xpad information on his website
+ * - Vojtech Pavlik - iforce driver / input subsystem
+ * - Greg Kroah-Hartman - usb-skeleton driver
+ * - XBOX Linux project - extra USB id's
+ *
+ * TODO:
+ * - fix "analog" buttons (reported as digital now)
+ * - need USB IDs for other dance pads
+ *
+ * Driver history is located at the bottom of this file.
+ */
+
+#ifndef _XPAD_H
+#define _XPAD_H
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/stat.h>
+#include <linux/module.h>
+#include <linux/usb/input.h>
+#include <linux/workqueue.h>
+
+#include <linux/kobject.h>
+#include <linux/sysfs.h>
+#include <linux/string.h>
+
+#if defined(CONFIG_JOYSTICK_XPAD_LEDS)
+#include <linux/leds.h>
+
+struct xpad_led {
+ char name[16];
+ struct led_classdev led_cdev;
+ struct usb_xpad *xpad;
+};
+#endif
+
+
+#define DRIVER_AUTHOR "Marko Friedemann <mfr@xxxxxxxxxxxxxxx>"
+#define DRIVER_DESC "Xbox/360 pad driver"
+
+#define XPAD_PKT_LEN 32
+
+
+/* xbox d-pads should map to buttons, as is required for DDR pads
+ but we map them to axes when possible to simplify things */
+#define MAP_DPAD_TO_BUTTONS 0
+#define MAP_DPAD_TO_AXES 1
+#define MAP_DPAD_UNKNOWN 2
+
+/* Type of controller *interface* (original, wired 360, wireless 360) */
+#define XTYPE_XBOX 0
+#define XTYPE_XBOX360 1
+#define XTYPE_XBOX360W 2
+#define XTYPE_UNKNOWN 3
+
+/* Type of controller (e.g. pad, guitar, other input device) */
+#define XCONTROLLER_TYPE_NONE 0
+#define XCONTROLLER_TYPE_PAD 1
+#define XCONTROLLER_TYPE_GUITAR 2
+#define XCONTROLLER_TYPE_DANCE_PAD 3
+#define XCONTROLLER_TYPE_OTHER 255
+
+
+/* The Xbox 360 controllers have sensitive sticks that often do not center
+ * exactly. A dead zone causes stick events below a certain threshhold to be
+ * reported as zero.
+ *
+ * The default dead zone size is 8192, which was obtained by testing a
+ * wireless 360 controller with jstest(1) and consulting gaming forums for
+ * a recommended dead zone for this controller. The consensus opinion was
+ * 0.25 (on a scale from 0 to 1), which corresponds to 8192 (out of 32767).
+ */
+#define XDEAD_ZONE_DEFAULT 8192
+
+/* Default limit for the sticks is the maximum axis value (32767), which will
+ * cause the sticks to have a radial axis as designed in the hardware. To
+ * enable square axis support, set the stick limits to 23170 or lower at run
+ * time via the sysfs interface. */
+#define XSTICK_LIMIT_DEFAULT 32767
+
+/* Rumble normally enabled */
+#define XRUMBLE_DEFAULT 1
+
+
+/* This module parameter is something of a relic, but it remains for
+ * compatibility. Importantly, the option to map the D-PAD buttons applies
+ * only to controller *interfaces* (i.e. vendor and product codes) not
+ * explicitly present in xpad_device[]. */
+static int dpad_to_buttons;
+module_param(dpad_to_buttons, bool, S_IRUGO);
+MODULE_PARM_DESC(dpad_to_buttons,
+ "Map D-PAD to buttons rather than axes for unknown pads");
+
+
+/* Table of various device interfaces recognized by this driver. Each supported
+ * device has a directional pad mapping, interface type, and controller type.
+ * Note that wireless 360 devices have XCONTROLLER_TYPE_NONE, as the actual
+ * type of the gaming controller is not known until the controller binds
+ * wirelessly with the receiver
+ */
+static const struct xpad_device {
+ u16 idVendor;
+ u16 idProduct;
+ char *name;
+ u8 dpad_mapping;
+ u8 xtype;
+ u8 controller_type;
+} xpad_device[] = {
+ { 0x045e, 0x0202, "Microsoft X-Box pad v1 (US)", MAP_DPAD_TO_AXES,
+ XTYPE_XBOX, XCONTROLLER_TYPE_PAD },
+ { 0x045e, 0x0289, "Microsoft X-Box pad v2 (US)", MAP_DPAD_TO_AXES,
+ XTYPE_XBOX, XCONTROLLER_TYPE_PAD },
+ { 0x045e, 0x0285, "Microsoft X-Box pad (Japan)", MAP_DPAD_TO_AXES,
+ XTYPE_XBOX, XCONTROLLER_TYPE_PAD },
+ { 0x045e, 0x0287, "Microsoft Xbox Controller S", MAP_DPAD_TO_AXES,
+ XTYPE_XBOX, XCONTROLLER_TYPE_PAD },
+ { 0x045e, 0x0719, "Xbox 360 Wireless Receiver", MAP_DPAD_TO_BUTTONS,
+ XTYPE_XBOX360W, XCONTROLLER_TYPE_NONE },
+ { 0x0c12, 0x8809, "RedOctane Xbox Dance Pad", MAP_DPAD_TO_BUTTONS,
+ XTYPE_XBOX, XCONTROLLER_TYPE_DANCE_PAD },
+ { 0x044f, 0x0f07, "Thrustmaster, Inc. Controller", MAP_DPAD_TO_AXES,
+ XTYPE_XBOX, XCONTROLLER_TYPE_PAD },
+ { 0x046d, 0xc242, "Logitech Chillstream Controller", MAP_DPAD_TO_AXES,
+ XTYPE_XBOX360, XCONTROLLER_TYPE_PAD },
+ { 0x046d, 0xca84, "Logitech Xbox Cordless Controller", MAP_DPAD_TO_AXES,
+ XTYPE_XBOX, XCONTROLLER_TYPE_PAD },
+ { 0x046d, 0xca88, "Logitech Compact Controller for Xbox",
+ MAP_DPAD_TO_AXES, XTYPE_XBOX, XCONTROLLER_TYPE_PAD },
+ { 0x05fd, 0x1007, "Mad Catz Controller (unverified)", MAP_DPAD_TO_AXES,
+ XTYPE_XBOX, XCONTROLLER_TYPE_PAD },
+ { 0x05fd, 0x107a, "InterAct 'PowerPad Pro' X-Box pad (Germany)",
+ MAP_DPAD_TO_AXES, XTYPE_XBOX, XCONTROLLER_TYPE_PAD },
+ { 0x0738, 0x4516, "Mad Catz Control Pad", MAP_DPAD_TO_AXES, XTYPE_XBOX,
+ XCONTROLLER_TYPE_PAD },
+ { 0x0738, 0x4522, "Mad Catz LumiCON", MAP_DPAD_TO_AXES, XTYPE_XBOX,
+ XCONTROLLER_TYPE_PAD },
+ { 0x0738, 0x4526, "Mad Catz Control Pad Pro", MAP_DPAD_TO_AXES,
+ XTYPE_XBOX, XCONTROLLER_TYPE_PAD },
+ { 0x0738, 0x4536, "Mad Catz MicroCON", MAP_DPAD_TO_AXES, XTYPE_XBOX,
+ XCONTROLLER_TYPE_PAD },
+ { 0x0738, 0x4540, "Mad Catz Beat Pad", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX,
+ XCONTROLLER_TYPE_DANCE_PAD },
+ { 0x0738, 0x4556, "Mad Catz Lynx Wireless Controller", MAP_DPAD_TO_AXES,
+ XTYPE_XBOX, XCONTROLLER_TYPE_PAD },
+ { 0x0738, 0x4716, "Mad Catz Wired Xbox 360 Controller",
+ MAP_DPAD_TO_AXES, XTYPE_XBOX360, XCONTROLLER_TYPE_PAD },
+ { 0x0738, 0x6040, "Mad Catz Beat Pad Pro", MAP_DPAD_TO_BUTTONS,
+ XTYPE_XBOX, XCONTROLLER_TYPE_DANCE_PAD },
+ { 0x0c12, 0x8802, "Zeroplus Xbox Controller", MAP_DPAD_TO_AXES,
+ XTYPE_XBOX, XCONTROLLER_TYPE_PAD },
+ { 0x0c12, 0x880a, "Pelican Eclipse PL-2023", MAP_DPAD_TO_AXES,
+ XTYPE_XBOX, XCONTROLLER_TYPE_PAD },
+ { 0x0c12, 0x8810, "Zeroplus Xbox Controller", MAP_DPAD_TO_AXES,
+ XTYPE_XBOX, XCONTROLLER_TYPE_PAD },
+ { 0x0c12, 0x9902, "HAMA VibraX - *FAULTY HARDWARE*", MAP_DPAD_TO_AXES,
+ XTYPE_XBOX, XCONTROLLER_TYPE_PAD },
+ { 0x0e4c, 0x1097, "Radica Gamester Controller", MAP_DPAD_TO_AXES,
+ XTYPE_XBOX, XCONTROLLER_TYPE_PAD },
+ { 0x0e4c, 0x2390, "Radica Games Jtech Controller", MAP_DPAD_TO_AXES,
+ XTYPE_XBOX, XCONTROLLER_TYPE_PAD },
+ { 0x0e6f, 0x0003, "Logic3 Freebird wireless Controller",
+ MAP_DPAD_TO_AXES, XTYPE_XBOX, XCONTROLLER_TYPE_PAD },
+ { 0x0e6f, 0x0005, "Eclipse wireless Controller", MAP_DPAD_TO_AXES,
+ XTYPE_XBOX, XCONTROLLER_TYPE_PAD },
+ { 0x0e6f, 0x0006, "Edge wireless Controller", MAP_DPAD_TO_AXES,
+ XTYPE_XBOX, XCONTROLLER_TYPE_PAD },
+ { 0x0e6f, 0x0006, "Pelican 'TSZ' Wired Xbox 360 Controller",
+ MAP_DPAD_TO_AXES, XTYPE_XBOX360, XCONTROLLER_TYPE_PAD },
+ { 0x0e8f, 0x0201, "SmartJoy Frag Xpad/PS2 adaptor", MAP_DPAD_TO_AXES,
+ XTYPE_XBOX, XCONTROLLER_TYPE_PAD },
+ { 0x0f30, 0x0202, "Joytech Advanced Controller", MAP_DPAD_TO_AXES,
+ XTYPE_XBOX, XCONTROLLER_TYPE_PAD },
+ { 0x0f30, 0x8888, "BigBen XBMiniPad Controller", MAP_DPAD_TO_AXES,
+ XTYPE_XBOX, XCONTROLLER_TYPE_PAD },
+ { 0x102c, 0xff0c, "Joytech Wireless Advanced Controller",
+ MAP_DPAD_TO_AXES, XTYPE_XBOX, XCONTROLLER_TYPE_PAD },
+ { 0x12ab, 0x8809, "Xbox DDR dancepad", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX,
+ XCONTROLLER_TYPE_DANCE_PAD },
+ { 0x1430, 0x4748, "RedOctane Guitar Hero X-plorer", MAP_DPAD_TO_AXES,
+ XTYPE_XBOX360, XCONTROLLER_TYPE_GUITAR },
+ { 0x1430, 0x8888, "TX6500+ Dance Pad (first generation)",
+ MAP_DPAD_TO_BUTTONS, XTYPE_XBOX, XCONTROLLER_TYPE_DANCE_PAD },
+ { 0x045e, 0x028e, "Microsoft X-Box 360 pad", MAP_DPAD_TO_AXES,
+ XTYPE_XBOX360, XCONTROLLER_TYPE_PAD },
+ { 0xffff, 0xffff, "Chinese-made Xbox Controller", MAP_DPAD_TO_AXES,
+ XTYPE_XBOX, XCONTROLLER_TYPE_PAD },
+ { 0x0000, 0x0000, "Generic X-Box pad", MAP_DPAD_UNKNOWN, XTYPE_UNKNOWN,
+ XCONTROLLER_TYPE_PAD }
+};
+
+
+/* buttons shared with xbox and xbox360 */
+static const signed short xpad_common_btn[] = {
+ BTN_A, BTN_B, BTN_X, BTN_Y, /* "analog" buttons */
+ BTN_START, BTN_BACK, BTN_THUMBL, BTN_THUMBR, /* start/back/sticks */
+ -1 /* terminating entry */
+};
+
+/* original xbox controllers only */
+static const signed short xpad_btn[] = {
+ BTN_C, BTN_Z, /* "analog" buttons */
+ -1 /* terminating entry */
+};
+
+/* only used if MAP_DPAD_TO_BUTTONS */
+static const signed short xpad_btn_pad[] = {
+ BTN_LEFT, BTN_RIGHT, /* d-pad left, right */
+ BTN_0, BTN_1, /* d-pad up, down (XXX names??) */
+ -1 /* terminating entry */
+};
+
+/* buttons for x360 controller */
+static const signed short xpad360_btn[] = {
+ BTN_TL, BTN_TR, /* Button LB/RB */
+ BTN_MODE, /* The big X button */
+ -1
+};
+
+/* sticks and triggers common to all devices */
+static const signed short xpad_abs[] = {
+ ABS_X, ABS_Y, /* left stick */
+ ABS_RX, ABS_RY, /* right stick */
+ ABS_Z, ABS_RZ, /* triggers left/right */
+ -1 /* terminating entry */
+};
+
+/* only used if MAP_DPAD_TO_AXES */
+static const signed short xpad_abs_pad[] = {
+ ABS_HAT0X, ABS_HAT0Y, /* d-pad axes */
+ -1 /* terminating entry */
+};
+
+
+/* Xbox 360 has a vendor-specific class, so we cannot match it with only
+ * USB_INTERFACE_INFO (also specifically refused by USB subsystem), so we
+ * match against vendor id as well. Wired Xbox 360 devices have protocol 1,
+ * wireless controllers have protocol 129. */
+#define XPAD_XBOX360_VENDOR_PROTOCOL(vend,pr) \
+ .match_flags = USB_DEVICE_ID_MATCH_VENDOR | \
+ USB_DEVICE_ID_MATCH_INT_INFO, \
+ .idVendor = (vend), \
+ .bInterfaceClass = USB_CLASS_VENDOR_SPEC, \
+ .bInterfaceSubClass = 93, \
+ .bInterfaceProtocol = (pr)
+#define XPAD_XBOX360_VENDOR(vend) \
+ { XPAD_XBOX360_VENDOR_PROTOCOL(vend, 1) }, \
+ { XPAD_XBOX360_VENDOR_PROTOCOL(vend, 129) }
+
+static struct usb_device_id xpad_table[] = {
+ /* X-Box USB-IF not approved class */
+ { USB_INTERFACE_INFO('X', 'B', 0) },
+ XPAD_XBOX360_VENDOR(0x045e), /* Microsoft X-Box 360 controllers */
+ XPAD_XBOX360_VENDOR(0x046d), /* Logitech 360 style controllers */
+ XPAD_XBOX360_VENDOR(0x0738), /* Mad Catz X-Box 360 controllers */
+ XPAD_XBOX360_VENDOR(0x0e6f), /* 0x0e6f X-Box 360 controllers */
+ XPAD_XBOX360_VENDOR(0x1430), /* RedOctane X-Box 360 controllers */
+ { }
+};
+
+MODULE_DEVICE_TABLE(usb, xpad_table);
+
+
+/* Wireless 360 device identification.
+ *
+ * When a wireless controller connects, the 2nd packet it sends SEEMS to
+ * be some kind of unique controller identification message. Using usbmon
+ * (see Documentation/usb/usbmon.txt), I tried 4 gamepads and a guitar, and
+ * I collected the following 5 ID packets from the 5 devices:
+ *
+ * 000f00f0 00ccfd27 0060e226 63700010 13e3201d 30034001 5001ffff ff
+ * 000f00f0 f0ccfd27 0060d8c4 e9600009 13e7201d 30034001 5001ffff ff
+ * 000f00f0 00ccfd27 0060578b 82f00010 13e3201d 30034001 5001ffff ff
+ * 000f00f0 f0ccfd27 0060da1c b1500009 13e7201d 30034001 5001ffff ff
+ * 000f00f0 f0ccfd27 006002d1 71d10000 13e3201d 30034430 5107ffff ff
+ *
+ * From this trace data, I concocted the following (potentially incorrect)
+ * scheme for detecting type and unique ID:
+ *
+ * ******** xx****xx xxxxxxxx xxxx**xx **xx**** ****tttt tttt**** **
+ * | unique id | | type |
+ *
+ * It appears that some of the bytes in the first half of the message, noted
+ * above as "unique id" are some sort of serial number, though I cannot work
+ * out any correspondence between these bytes and the serial number printed
+ * under the battery pack. Many of the bytes in this possibly unique field
+ * are not unique across my controllers, and may not in fact be part of the
+ * controller's unique identification, but I figured it was better to have
+ * extra bytes on either end of the unique byte string instead of the
+ * alternative. In addition, the packet appears to indicate the type of
+ * the controller toward the end: the pads all send 4001 5001, while the
+ * guitar sends 4430 5107.
+ *
+ * Further testing over a wider variety of devices is probably needed to
+ * determine if changes need to be made to this scheme.
+ */
+static const struct w360_id {
+ u32 id_bytes;
+ u8 controller_type;
+} w360_id[] = {
+ { 0x40015001, XCONTROLLER_TYPE_PAD },
+ { 0x44305107, XCONTROLLER_TYPE_GUITAR },
+ { 0x00000000, XCONTROLLER_TYPE_NONE }
+};
+
+
+/* Some of the fields in the following structure are for later use with
+ * userspace applications to recognize individual controllers. The dead zones
+ * and axis limits can be changed "on the fly" and are effective immediately.
+ *
+ * The fields labeled "ro" and "rw" are intended to be read-only and
+ * read-write, respectively, when exposed in sysfs. Most of the read-only
+ * fields are to support *wireless* 360 controllers. The controller_number
+ * is used to set the LED, while controller_present tracks whether the
+ * controller is connected to the wireless receiver. Controller type applies
+ * to all models (wired and wireless), and tracks whether the device is a pad,
+ * guitar, etc. for later userspace use. See the comment above regarding
+ * type and unique ID detection on wireless 360 receivers.
+ */
+struct usb_xpad {
+ struct input_dev *dev; /* input device interface */
+ struct usb_device *udev; /* usb device */
+
+ struct urb *irq_in; /* urb for interrupt in report */
+ unsigned char *idata; /* input data */
+ dma_addr_t idata_dma;
+
+#if defined(CONFIG_JOYSTICK_XPAD_FF) || defined(CONFIG_JOYSTICK_XPAD_LEDS)
+ struct urb *irq_out; /* urb for interrupt out report */
+ unsigned char *odata; /* output data */
+ dma_addr_t odata_dma;
+ struct mutex odata_mutex;
+#endif
+
+#if defined(CONFIG_JOYSTICK_XPAD_LEDS)
+ struct xpad_led *led;
+#endif
+
+ char phys[64]; /* physical device path */
+
+ int dpad_mapping; /* map d-pad to buttons or to axes */
+ int xtype; /* type of xbox device */
+
+ /* Work structure for moving the call to xpad_send_led_command
+ * outside the interrupt handler for packet processing */
+ struct work_struct work;
+ int work_pending;
+
+ int controller_number; /* controller # (1-4) for 360w. ro */
+ int controller_present; /* 360w controller presence. ro */
+ int controller_type; /* controller type. ro */
+ char controller_unique_id[17]; /* unique ID of controller (360w). ro */
+ unsigned int left_dead_zone; /* dead zone for left stick. rw */
+ unsigned int right_dead_zone; /* dead zone for right stick. rw */
+ unsigned int left_stick_limit; /* axis limit for left stick. rw */
+ unsigned int right_stick_limit; /* axis limit for right stick. rw */
+ int rumble_enable; /* enable/disable rumble. rw */
+
+ int sysfs_ok; /* sysfs interface OK */
+};
+#define to_xpad(d) input_get_drvdata(to_input_dev(d))
+
+
+/* Function prototypes for non-sysfs interface functions */
+static void set_dead_zone(unsigned int new_size, unsigned int *dz,
+ unsigned int stick_limit);
+static void set_stick_limit(unsigned int new_size, unsigned int *sl,
+ unsigned int dead_zone);
+static void xpad_init_controller(struct usb_xpad *xpad);
+static void xpad_send_led_command(struct usb_xpad *xpad, int command);
+static void xpad_work_controller(struct work_struct *w);
+static void xpad_process_sticks(struct usb_xpad *xpad, unsigned char *data);
+static void xpad_process_packet(struct usb_xpad *xpad, u16 cmd,
+ unsigned char *data);
+static void xpad360_process_packet(struct usb_xpad *xpad,
+ u16 cmd, unsigned char *data);
+static void xpad360w_identify_controller(struct usb_xpad *xpad,
+ unsigned char *data);
+static void xpad360w_process_packet(struct usb_xpad *xpad, u16 cmd,
+ unsigned char *data);
+static void xpad_irq_in(struct urb *urb);
+static void xpad_irq_out(struct urb *urb);
+static int xpad_init_output(struct usb_interface *intf, struct usb_xpad *xpad);
+static void xpad_stop_output(struct usb_xpad *xpad);
+static void xpad_stop_output(struct usb_xpad *xpad);
+static int xpad_play_effect(struct input_dev *dev, void *data,
+ struct ff_effect *effect);
+static int xpad_init_ff(struct usb_xpad *xpad);
+static void xpad_send_led_command(struct usb_xpad *xpad, int command);
+static void xpad_led_set(struct led_classdev *led_cdev,
+ enum led_brightness value);
+static int xpad_led_probe(struct usb_xpad *xpad);
+static void xpad_led_disconnect(struct usb_xpad *xpad);
+static int xpad_open(struct input_dev *dev);
+static void xpad_close(struct input_dev *dev);
+static void xpad_set_up_abs(struct input_dev *input_dev, signed short abs);
+static int xpad_probe(struct usb_interface *intf,
+ const struct usb_device_id *id);
+static void xpad_disconnect(struct usb_interface *intf);
+static int __init usb_xpad_init(void);
+static void __exit usb_xpad_exit(void);
+
+
+/* sysfs interface */
+static ssize_t xpad_show_uint(struct device *dev, struct device_attribute *attr,
+ char *buf);
+static ssize_t xpad_store_uint(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count);
+static ssize_t xpad_store_rumble_enable(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count);
+static ssize_t xpad_store_ro(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count);
+static ssize_t xpad_show_int(struct device *dev, struct device_attribute *attr,
+ char *buf);
+static ssize_t xpad_show_id(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+
+
+/* Device attributes */
+static DEVICE_ATTR(left_dead_zone, 0666, xpad_show_uint, xpad_store_uint);
+static DEVICE_ATTR(right_dead_zone, 0666, xpad_show_uint, xpad_store_uint);
+static DEVICE_ATTR(left_stick_limit, 0666, xpad_show_uint, xpad_store_uint);
+static DEVICE_ATTR(right_stick_limit, 0666, xpad_show_uint, xpad_store_uint);
+static DEVICE_ATTR(rumble_enable, 0666, xpad_show_int,
+ xpad_store_rumble_enable);
+static DEVICE_ATTR(controller_number, 0444, xpad_show_int, xpad_store_ro);
+static DEVICE_ATTR(controller_present, 0444, xpad_show_int, xpad_store_ro);
+static DEVICE_ATTR(controller_type, 0444, xpad_show_int, xpad_store_ro);
+static DEVICE_ATTR(id, 0444, xpad_show_id, xpad_store_ro);
+
+static struct attribute *xpad_default_attrs[] = {
+ &dev_attr_left_dead_zone.attr,
+ &dev_attr_right_dead_zone.attr,
+ &dev_attr_left_stick_limit.attr,
+ &dev_attr_right_stick_limit.attr,
+ &dev_attr_rumble_enable.attr,
+ &dev_attr_controller_number.attr,
+ &dev_attr_controller_present.attr,
+ &dev_attr_controller_type.attr,
+ &dev_attr_id.attr,
+ NULL
+};
+
+static struct attribute_group xpad_default_attr_group = {
+ .attrs = xpad_default_attrs,
+ .name = "game_device",
+};
+
+
+
+static struct usb_driver xpad_driver = {
+ .name = "xpad",
+ .probe = xpad_probe,
+ .disconnect = xpad_disconnect,
+ .id_table = xpad_table,
+};
+
+module_init(usb_xpad_init);
+module_exit(usb_xpad_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+#endif
+
+/* Driver History:
+ *
+ * 2009-02-23 : Changes per mailing list (thanks Frederic Weisbecker)
+ * - no more check for CONFIG_SYSFS: sysfs functions will simply return
+ * 0 if sysfs has not been enabled
+ * - fixed weird ordering in sscanf return check
+ * - checked code with scripts/checkpatch.pl and made style adjustments
+ *
+ * 2009-02-21 : Refactored and changed stick handling
+ * - split code into two pieces (xpad.h and xpad.c)
+ * - cleaned up sysfs interface
+ * - changed square axis algorithm to an axis limit algorithm, which allows
+ * size of inscribed square to be adjusted; available for both sticks
+ * - dead zones now per-stick
+ *
+ * 2009-02-18 : Changes per mailing list (and some additions)
+ * - revised sysfs interface (thanks Greg K-H)
+ * - check return values of sscanf (thanks Oliver Neukum)
+ * - urb submission while holding mutex now once again GFP_KERNEL
+ * (thanks Oliver Neukum)
+ * - work structure fixes (thanks Oliver Neukum)
+ * - uevents generated for wireless controller online/offline
+ * - sysfs interface only if CONFIG_SYSFS is set
+ *
+ * 2009-02-15 : Minor adjustments
+ * - added KOBJ_ONLINE/KOBJ_OFFLINE events when controllers are connected to
+ * or disconnected from the wireless 360 receiver
+ * - ignore duplicate connect messages on the same connection
+ * - added option to enable/disable rumble on a per-controller basis
+ * - rumble events are not sent to guitar or dance pad devices
+ *
+ * 2009-02-14 : Added sysfs interface
+ * - dead zones and square axis settings can now be made per-controller
+ * - removed dead_zone and square_axis module parameters (use sysfs)
+ * - new square axis algorithm
+ *
+ * 2009-02-13 : Disable square axis for right stick
+ * - square axis applies to left stick only
+ *
+ * 2009-02-12 : Scaling for dead zone and square axis support
+ * - axes now scale from 0 to 32767 starting at edge of dead zone
+ * - increased default dead zone to 8192
+ * - initial square axis support (reliable only with left stick)
+ *
+ * 2009-02-07 : More wireless 360 controller fixes
+ * - removed bulk urb completely
+ * - use xpad_send_led_command to set controller number on LED display
+ * (wireless 360 controller)
+ * - dead_zone is now an adjustable module parameter
+ *
+ * 2009-02-06 : Axis handling improvements
+ * - unified handler for left and right sticks
+ * - initial support for dead zones
+ *
+ * 2009-02-02 : Wireless 360 controller fixes
+ * - followed PROTOCOL description from xboxdrv userspace driver
+ * - LED and rumble support added for wireless 360 controller (protocol
+ * is different from wired!)
+ *
+ * 2004-10-02 - 0.0.6 : DDR pad support
+ * - borrowed from the XBOX linux kernel
+ * - USB id's for commonly used dance pads are present
+ * - dance pads will map D-PAD to buttons, not axes
+ * - pass the module paramater 'dpad_to_buttons' to force
+ * the D-PAD to map to buttons if your pad is not detected
+ *
+ * 2002-07-17 - 0.0.5 : simplified d-pad handling
+ *
+ * 2002-07-16 - 0.0.4 : minor changes, merge with Vojtech's v0.0.3
+ * - verified the lack of HID and report descriptors
+ * - verified that ALL buttons WORK
+ * - fixed d-pad to axes mapping
+ *
+ * 2002-07-14 - 0.0.3 : rework by Vojtech Pavlik
+ * - indentation fixes
+ * - usb + input init sequence fixes
+ *
+ * 2002-07-02 - 0.0.2 : basic working version
+ * - all axes and 9 of the 10 buttons work (german InterAct device)
+ * - the black button does not work
+ *
+ * 2002-06-27 - 0.0.1 : first version, just said "XBOX HID controller"
+ *
+ */