--- 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 * * Copyright (c) 2002 Marko Friedemann * 2004 Oliver Schwartz , @@ -9,6 +12,7 @@ * 2005 Dominic Cerquetti * 2006 Adam Buchbinder * 2007 Jan Kratochvil + * 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 @@ -78,9 +116,14 @@ #include #include #include +#include +#include +#include +#include + #define DRIVER_AUTHOR "Marko Friedemann " -#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); } }