lkml.org 
[lkml]   [2009]   [Feb]   [14]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
Date
Subject[PATCH] input: xpad.c - Xbox 360 wireless and sysfs support
From
Greetings,

The attached patch is a result of a few days of hacking to get better
Linux support for Xbox 360 wireless controllers. Major functional
improvements include support for the LEDs on the wireless controllers
(which are auto-set to the controller number as they are on the
official console), added support for rumble effects on wireless
controllers, added support for dead zones on all supported
controllers, and added support for a square axis mapping for the left
stick on all supported controllers. A large amount of the bulk in this
patch is from the addition of a sysfs interface to retrieve
information and adjust the dead zone/square axis settings.

I also fixed a few bugs:

- Removed the bulk urb altogether for setting the LED display. This
operation is now done using the irq_out urb in xpad_send_led_command
(via a shared work queue task to get out of the interrupt handler when
processing the presence change packet).

- Submission of urbs while holding a mutex is now GFP_ATOMIC

- The odata_mutex is now locked when changing the odata for sending a
force feedback effect.

There is still a bug somewhere in the force feedback code, I believe
in either ff-memless or lower, that causes a "warn_on_slowpath"
message in the log and an occasional "scheduling while atomic" BUG. I
have thus far been unable to isolate it.

This code has been tested only with a wireless 360 receiver, 4
controllers (individually and concurrently), and a RedOctane wireless
guitar. It needs more testing with a broader range of devices,
especially wired devices.

Patch generated against 2.6.28.2 (no changes in relevant in-kernel
driver from 2.6.28.2 to 2.6.28.5, so should apply against latest
stable).

Comments are appreciated. I am _NOT_ subscribed to the LKML, so please
CC me directly.

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
--- origdrv/drivers/input/joystick/xpad.c 2009-02-14 22:39:20.000000000 -0500
+++ newdrv/drivers/input/joystick/xpad.c 2009-02-14 22:41:27.000000000 -0500
@@ -1,5 +1,8 @@
/*
- * X-Box gamepad driver
+ * Xbox gamepad driver with Xbox 360 wired/wireless support
+ *
+ * Last Modified: 14 February 2009
+ * Mike Murphy <mamurph@cs.clemson.edu>
*
* Copyright (c) 2002 Marko Friedemann <mfr@bmx-chemnitz.de>
* 2004 Oliver Schwartz <Oliver.Schwartz@gmx.de>,
@@ -9,6 +12,7 @@
* 2005 Dominic Cerquetti <binary1230@yahoo.com>
* 2006 Adam Buchbinder <adam.buchbinder@gmail.com>
* 2007 Jan Kratochvil <honza@jikos.cz>
+ * 2009 Clemson University
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -30,6 +34,7 @@
* - 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
@@ -38,10 +43,16 @@
* - 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
+ * - fix input descriptor leak (requires finding cause)
+ * - to reproduce behavior:
+ * $ ls /sys/class/input
+ * # rmmod xpad
+ * # insmod ./xpad.ko
+ * $ ls /sys/class/input
+ * - fix warn_on_slowpath and occasional scheduling while atomic BUGs when
+ * using rumble (bug results from use of a timer somewhere in ff-memless).
*
* History:
*
@@ -69,7 +80,34 @@
* - 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.
+ * 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!)
+ *
+ * 2009-02-06 : Axis handling improvements
+ * - unified handler for left and right sticks
+ * - initial support for dead zones
+ *
+ * 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)
+ * - fixed urb submission while holding mutex to GFP_ATOMIC
+ * - dead_zone is now an adjustable module parameter
+ *
+ * 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-13 : Disable square axis for right stick
+ * - square axis applies to left stick only
+ *
+ * 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
*/

#include <linux/kernel.h>
@@ -78,9 +116,14 @@
#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>
+

#define DRIVER_AUTHOR "Marko Friedemann <mfr@bmx-chemnitz.de>"
-#define DRIVER_DESC "X-Box pad driver"
+#define DRIVER_DESC "X-Box/360 pad driver"

#define XPAD_PKT_LEN 32

@@ -90,62 +133,127 @@
#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

-static int dpad_to_buttons;
+/* 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 axes for the sticks are radial */
+#define XSQUARE_AXIS_DEFAULT 0
+
+static int dpad_to_buttons = 0;
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;
+ u8 controller_type;
} 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 }
+ { 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 */
@@ -213,19 +321,80 @@

MODULE_DEVICE_TABLE (usb, xpad_table);

+
+/* Wireless 360 device identification. See lengthy notes about kobject data
+ * below.
+ */
+static const struct w360_id {
+ u32 id_bytes;
+ u8 controller_type;
+} w360_id[] = {
+ { 0x40415001, XCONTROLLER_TYPE_PAD },
+ { 0x44305107, XCONTROLLER_TYPE_GUITAR },
+ { 0x00000000, XCONTROLLER_TYPE_NONE }
+};
+
+
+/* kobject data for sysfs. Some of these fields are read-only, mostly for
+ * later use with userspace applications to recognize individual controllers.
+ * The dead_zone and square_axis settings can be changed "on the fly" and are
+ * effective immediately.
+ *
+ * Most of the read-only fields are to support *wireless* 360 controllers.
+ * The controller_number is used to set the LED, while pad_present tracks
+ * whether the correpsonding 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.
+ *
+ * The controller_unique_id is a field of bits in a particular wireless
+ * packet that SEEM to correspond to some kind of unique ID built into the
+ * controller. Using usbmon (see Documentation/usb/usbmon.txt), I tried 4
+ * gamepads and a guitar, and I got the following 5 packets at connection
+ * time for each device, respectively.
+ *
+ * 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 |
+ *
+ * Further testing over a wider variety of devices is probably needed to
+ * determine if changes need to be made to this scheme.
+ */
+struct xpad_data {
+ struct kobject kobj; /* kobject for sysfs */
+ int controller_number; /* controller number (1-4) for 360w. ro */
+ int pad_present; /* 360w pad presence detection. ro */
+ int controller_type; /* controller type. ro */
+ char controller_unique_id[17]; /* unique ID of controller (360w). ro */
+ unsigned int dead_zone; /* dead zone for both sticks. rw */
+ int square_axis; /* square axis for left stick. rw */
+};
+#define to_xpad_data(k) container_of(k, struct xpad_data, kobj)
+
+/* attribute structure for sysfs */
+struct xpad_attribute {
+ struct attribute attr;
+ ssize_t (*show)(struct xpad_data *xd, struct xpad_attribute *attr, char *buf);
+ ssize_t (*store)(struct xpad_data *xd, struct xpad_attribute *attr, const char *buf,
+ size_t count);
+};
+#define to_xpad_attr(k) container_of(k, struct xpad_attribute, attr)
+
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;

- 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 */
@@ -241,8 +410,286 @@

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;
+
+ /* Controller data is now in a kobject for use with sysfs */
+ struct xpad_data *controller_data;
};

+
+/****************************************************************************/
+/*
+ * SysFs interface implemented using Documentation/kobject.txt and examples in
+ * samples/kobject.
+ */
+static ssize_t xpad_attr_show(struct kobject *kobj,
+ struct attribute *attr,
+ char *buf)
+{
+ struct xpad_attribute *attribute;
+ struct xpad_data *xd;
+
+ attribute = to_xpad_attr(attr);
+ xd = to_xpad_data(kobj);
+
+ if (!attribute->show)
+ return -EIO;
+
+ return attribute->show(xd, attribute, buf);
+}
+
+static ssize_t xpad_attr_store(struct kobject *kobj,
+ struct attribute *attr,
+ const char *buf, size_t len)
+{
+ struct xpad_attribute *attribute;
+ struct xpad_data *xd;
+
+ attribute = to_xpad_attr(attr);
+ xd = to_xpad_data(kobj);
+
+ if (!attribute->store)
+ return -EIO;
+
+ return attribute->store(xd, attribute, buf, len);
+}
+
+static struct sysfs_ops xpad_sysfs_ops = {
+ .show = xpad_attr_show,
+ .store = xpad_attr_store,
+};
+
+static void xpad_release(struct kobject *kobj)
+{
+ struct xpad_data *xd;
+ xd = to_xpad_data(kobj);
+ kfree(xd);
+}
+
+static ssize_t xpad_show_dead_zone(struct xpad_data *xd,
+ struct xpad_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%d\n", xd->dead_zone);
+}
+
+static ssize_t xpad_store_dead_zone(struct xpad_data *xd,
+ struct xpad_attribute *attr, const char *buf, size_t count)
+{
+ int newdz;
+ sscanf(buf, "%d", &newdz);
+ if ((newdz >= 0) && (newdz < 32760)) {
+ xd->dead_zone = newdz;
+ }
+ return count;
+}
+
+static ssize_t xpad_show_square_axis(struct xpad_data *xd,
+ struct xpad_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%d\n", xd->square_axis);
+}
+
+static ssize_t xpad_store_square_axis(struct xpad_data *xd,
+ struct xpad_attribute *attr, const char *buf, size_t count)
+{
+ int newaxis = 0;
+ sscanf(buf, "%d", &newaxis);
+ xd->square_axis = (newaxis) ? 1 : 0;
+ return count;
+}
+
+/* read-only attrs */
+static ssize_t xpad_show_int(struct xpad_data *xd, struct xpad_attribute *attr,
+ char *buf)
+{
+ int value;
+ if (!strcmp(attr->attr.name, "controller_number"))
+ value = xd->controller_number;
+ else if (!strcmp(attr->attr.name, "pad_present"))
+ value = xd->pad_present;
+ else if (!strcmp(attr->attr.name, "controller_type"))
+ value = xd->controller_type;
+ else
+ value = 0;
+ return sprintf(buf, "%d\n", value);
+}
+
+static ssize_t xpad_show_id(struct xpad_data *xd, struct xpad_attribute *attr,
+ char *buf)
+{
+ return sprintf(buf, "%s\n", xd->controller_unique_id);
+}
+
+/* maps */
+static struct xpad_attribute controller_number_attr =
+ __ATTR(controller_number, 0444, xpad_show_int, NULL);
+static struct xpad_attribute pad_present_attr =
+ __ATTR(pad_present, 0444, xpad_show_int, NULL);
+static struct xpad_attribute controller_type_attr =
+ __ATTR(controller_type, 0444, xpad_show_int, NULL);
+static struct xpad_attribute controller_unique_id_attr =
+ __ATTR(controller_unique_id, 0444, xpad_show_id, NULL);
+
+/* rw maps */
+static struct xpad_attribute dead_zone_attr =
+ __ATTR(dead_zone, 0666, xpad_show_dead_zone, xpad_store_dead_zone);
+static struct xpad_attribute square_axis_attr =
+ __ATTR(square_axis, 0666, xpad_show_square_axis, xpad_store_square_axis);
+
+
+static struct attribute *xpad_default_attrs[] = {
+ &controller_number_attr.attr,
+ &pad_present_attr.attr,
+ &controller_type_attr.attr,
+ &controller_unique_id_attr.attr,
+ &dead_zone_attr.attr,
+ &square_axis_attr.attr,
+ NULL
+};
+
+static struct kobj_type xpad_ktype = {
+ .sysfs_ops = &xpad_sysfs_ops,
+ .release = xpad_release,
+ .default_attrs = xpad_default_attrs,
+};
+
+static struct xpad_data *xpad_create_data(const char *name, struct kobject *parent) {
+ struct xpad_data *data = NULL;
+ int check;
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return NULL;
+
+ check = kobject_init_and_add(&data->kobj, &xpad_ktype, parent, "%s", name);
+ if (check) {
+ kobject_put(&data->kobj);
+ return NULL;
+ }
+
+ kobject_uevent(&data->kobj, KOBJ_ADD);
+ return data;
+}
+
+static void xpad_destroy_data(struct xpad_data *data)
+{
+ kobject_put(&data->kobj);
+}
+
+/*****************************************************************************/
+
+
+/* Need prototype due to module order */
+static void xpad_send_led_command(struct usb_xpad *xpad, int command);
+
+
+/*
+ * 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_data->controller_number + 1);
+}
+
+
+/*
+ * 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.
+ *
+ * Square axis algorithm: for the LEFT STICK ONLY (X and Y axes), the
+ * coordinate system can be set to be more like classic controllers
+ * and PC joysticks. This mapping is accomplished conceptually by
+ * inscribing a square inside the radial opening of the plastic case
+ * on the controller, which has a radius of 1.0 on a floating-point
+ * scale (23170 on the integer scale). As a result, the X and Y axis
+ * will have a smaller range, reaching 32767 at the point that was
+ * formerly 23170. In practice, the implementation is fairly complex
+ * to understand, due to the need to integrate it with the dead
+ * zone scaling.
+ */
+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 tmp, adjust, c;
+ int abs_magnitude;
+ int dead_zone, square_axis;
+
+ dead_zone = xpad->controller_data->dead_zone;
+ square_axis = xpad->controller_data->square_axis;
+
+ 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++) {
+ adjust = 0;
+ abs_magnitude = (int) coords[c];
+ if (abs_magnitude < 0)
+ abs_magnitude = -abs_magnitude;
+
+ if (dead_zone || square_axis) {
+ adjust = (square_axis && (c < 2)) ? 23170 : 32767;
+ /* Careful: removing the following check will result
+ * in division by zero if the dead_zone is zero... */
+ if (dead_zone)
+ adjust = (adjust - dead_zone) / dead_zone;
+ if (adjust < 0) {
+ adjust = 0;
+ }
+ }
+
+ if (abs_magnitude < dead_zone) {
+ coords[c] = 0;
+ }
+ else if (adjust) {
+ tmp = ((abs_magnitude - dead_zone) / adjust) + abs_magnitude - dead_zone;
+ if (tmp > 32767) {
+ tmp = 32767;
+ }
+ coords[c] = (coords[c] < 0) ? -tmp : tmp;
+ }
+ }
+
+ 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]);
+}
+
/*
* xpad_process_packet
*
@@ -256,18 +703,9 @@
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]);
@@ -350,18 +788,9 @@
input_report_key(dev, BTN_TL, data[3] & 0x01);
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]);
@@ -379,6 +808,7 @@
* 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
@@ -387,20 +817,50 @@

static void xpad360w_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *data)
{
- /* Presence change */
+ int padnum = 0;
+ u32 id;
+ int i;
+
+ /* Presence change */
if (data[0] & 0x08) {
+ padnum = xpad->controller_data->controller_number;
if (data[1] & 0x80) {
- xpad->pad_present = 1;
- usb_submit_urb(xpad->bulk_out, GFP_ATOMIC);
- } else
- xpad->pad_present = 0;
+ printk(KERN_INFO "Wireless Xbox 360 pad #%d present\n", padnum);
+ xpad->controller_data->pad_present = 1;
+
+ INIT_WORK(&xpad->work, &xpad_work_controller);
+ schedule_work(&xpad->work);
+ }
+ else {
+ printk(KERN_INFO "Wireless Xbox 360 pad #%d disconnected\n", padnum);
+ xpad->controller_data->pad_present = 0;
+ xpad->controller_data->controller_unique_id[0] = '\0';
+ xpad->controller_data->controller_type = XCONTROLLER_TYPE_NONE;
+ }
}

- /* 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_data->controller_unique_id[0]) {
+ snprintf(xpad->controller_data->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 = (data[22] << 24) + (data[23] << 16) + (data[24] << 8) + data[25];
+ xpad->controller_data->controller_type = XCONTROLLER_TYPE_OTHER;
+ for (i = 0; w360_id[i].id_bytes; i++) {
+ if (id == w360_id[i].id_bytes) {
+ xpad->controller_data->controller_type =
+ w360_id[i].controller_type;
+ }
+ }
+ }
+ }
+ else if (data[1] & 0x1) {
+ xpad360_process_packet(xpad, cmd, &data[4]);
+ }
}

static void xpad_irq_in(struct urb *urb)
@@ -445,23 +905,6 @@
__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);
- }
-}
-
#if defined(CONFIG_JOYSTICK_XPAD_FF) || defined(CONFIG_JOYSTICK_XPAD_LEDS)
static void xpad_irq_out(struct urb *urb)
{
@@ -498,7 +941,7 @@
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,
@@ -528,13 +971,13 @@

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);
@@ -547,6 +990,11 @@
#endif

#ifdef 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)
{
@@ -555,16 +1003,35 @@
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;
- usb_submit_urb(xpad->irq_out, GFP_KERNEL);
+ 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_ATOMIC);
+ mutex_unlock(&xpad->odata_mutex);
}

return 0;
@@ -572,7 +1039,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);
@@ -593,15 +1060,36 @@
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;
- usb_submit_urb(xpad->irq_out, GFP_KERNEL);
+ 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_ATOMIC);
mutex_unlock(&xpad->odata_mutex);
}
}
@@ -615,6 +1103,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 +1112,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);
@@ -721,6 +1210,7 @@
struct usb_xpad *xpad;
struct input_dev *input_dev;
struct usb_endpoint_descriptor *ep_irq_in;
+ int controller_type;
int i;
int error = -ENOMEM;

@@ -747,6 +1237,7 @@
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) {
@@ -819,6 +1310,22 @@
goto fail4;

usb_set_intfdata(intf, xpad);
+
+ /* Set up data object */
+ if (!(&input_dev->dev.kobj)) {
+ error = 99;
+ goto fail5;
+ }
+ xpad->controller_data = xpad_create_data("xpad", &input_dev->dev.kobj);
+ if (!xpad->controller_data) {
+ error = 100;
+ goto fail5;
+ }
+
+ /* Set defaults common to all controller types */
+ xpad->controller_data->controller_type = controller_type;
+ xpad->controller_data->dead_zone = XDEAD_ZONE_DEFAULT;
+ xpad->controller_data->square_axis = XSQUARE_AXIS_DEFAULT;

/*
* Submit the int URB immediatly rather than waiting for open
@@ -828,48 +1335,26 @@
* we're waiting for.
*/
if (xpad->xtype == XTYPE_XBOX360W) {
+ xpad->controller_data->pad_present = 0;
+ xpad->controller_data->controller_unique_id[0] = '\0';
+ xpad->controller_data->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;
-
- 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);
+ }
+ else {
+ xpad->controller_data->pad_present = 1;
+ strncpy(xpad->controller_data->controller_unique_id, "wired", 17);
+ xpad->controller_data->controller_number = 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);
@@ -885,17 +1370,17 @@

usb_set_intfdata(intf, NULL);
if (xpad) {
+ xpad_destroy_data(xpad->controller_data);
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);
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);
+ input_free_device(xpad->dev);
kfree(xpad);
}
}
\
 
 \ /
  Last update: 2009-02-15 05:11    [W:1.143 / U:0.068 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site