[patch] Improved xbox 360 controller support
"Matthew A. Nicholson" <[email protected]> Thu, 19 Jan 2006 02:02:01 -0600
| Newsgroups | gmane.linux.ports.xbox.devel |
|---|---|
| Message-ID | <[email protected]> |
This patch improves support for the Xbox 360 controller in xpad.c and
xpad.h. Several things have been added or modified including:
* Formatting changes (changed Xbox360 to Xbox 360, vim modelines)
* Removed extra axes and buttons not on xbox 360 pad
* fixed xpad_table to detect xbox 360 pad properly
* swapped x and y buttons
* removed flipping of y axis on right stick (unnecessary)
* tunned default calibration settings for xbox 360 pad so that triggers
start at 0 (not -32767) and axis range is much better.
* Added code to turn of xbox 360 pad leds when the pad is initialized
(so they don't just flash)
This has been tested, but I am new to this kind of thing. Please review
the patch before commiting. I really don't know if my xpad_irq_out
callback is done correctly.
I think that I can impliment rumble support for the pad as well, but
before I do that I need to know what the current status of rumble
support in the driver is.
--
Matthew A. Nicholson
Matt-Land.com
xpad_360.diff
(text/plain, 13.2 KB)
diff -ruE orig/xpad.c new/xpad.c --- orig/xpad.c 2006-01-19 01:35:20.000000000 -0600 +++ new/xpad.c 2006-01-19 01:58:47.000000000 -0600 @@ -1,4 +1,4 @@ -/* +/* vim: set noet sw=8: * Xbox input device driver for Linux - v0.1.6 * * Copyright (c) 2002 - 2004 Marko Friedemann <[email protected]> @@ -11,6 +11,7 @@ * Franz Lehner <[email protected]>, * Ivan Hawkes <[email protected]> * Edgar Hucek <[email protected]> + * Matthew A. Nicholson <[email protected]> * * * This program is free software; you can redistribute it and/or @@ -72,13 +73,13 @@ static struct xpad_device xpad_device[] = { /* please keep those ordered wrt. vendor/product ids - vendor, product, isMat, name */ + vendor, product, isMat, name, is360 */ { 0x044f, 0x0f07, 0, "Thrustmaster, Inc. Controller", 0}, { 0x045e, 0x0202, 0, "Microsoft Xbox Controller", 0}, { 0x045e, 0x0285, 0, "Microsoft Xbox Controller S", 0}, { 0x045e, 0x0287, 0, "Microsoft Xbox Controller S", 0}, { 0x045e, 0x0289, 0, "Microsoft Xbox Controller S", 0}, /* microsoft is stupid */ - { 0x045e, 0x028e, 0, "Microsoft Xbox360 Controller", 1}, + { 0x045e, 0x028e, 0, "Microsoft Xbox 360 Controller", 1}, { 0x046d, 0xca84, 0, "Logitech Xbox Cordless Controller", 0}, { 0x046d, 0xca88, 0, "Logitech Compact Controller for Xbox", 0}, { 0x05fd, 0x1007, 0, "???Mad Catz Controller???", 0}, /* CHECKME: this seems strange */ @@ -112,6 +113,13 @@ BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, /* analogue buttons */ BTN_START, BTN_BACK, BTN_THUMBL, BTN_THUMBR, /* start/back/sticks */ BTN_0, BTN_1, BTN_2, BTN_3, /* d-pad as buttons */ + -1 /* terminating entry */ +}; + +static signed short x360_btn[] = { + BTN_A, BTN_B, BTN_X, BTN_Y, /* face buttons */ + BTN_START, BTN_BACK, BTN_THUMBL, BTN_THUMBR, /* start/back/sticks */ + BTN_0, BTN_1, BTN_2, BTN_3, /* d-pad as buttons */ BTN_TL, BTN_TR, /* Button LB/RB */ BTN_MODE, /* The big X */ -1 /* terminating entry */ @@ -135,11 +143,16 @@ -1 /* terminating entry */ }; +static signed short x360_abs[] = { + ABS_X, ABS_Y, /* left stick */ + ABS_RX, ABS_RY, /* right stick */ + ABS_Z, ABS_RZ, /* triggers left/right */ + -1 /* terminating entry */ +}; + static struct usb_device_id xpad_table [] = { { USB_INTERFACE_INFO('X', 'B', 0) }, /* Xbox USB-IF not approved class */ { USB_INTERFACE_INFO( 3 , 0 , 0) }, /* for Joytech Advanced Controller */ - { USB_INTERFACE_INFO( 255 , 93 , 3) }, /* Xbox 360 */ - { USB_INTERFACE_INFO( 255 , 93 , 2) }, /* Xbox 360 */ { USB_INTERFACE_INFO( 255 , 93 , 1) }, /* Xbox 360 */ { } }; @@ -170,6 +183,12 @@ printk("\n"); } + if (xpad->is360) { + /* make sure this is an input report message */ + if (data[0] != 0x00) + return; + } + /* digital pad (button mode) bits (3 2 1 0) (right left down up) */ input_report_key(dev, BTN_0, (data[2] & 0x01)); input_report_key(dev, BTN_1, (data[2] & 0x08) >> 3); @@ -188,8 +207,8 @@ if(xpad->is360) { input_report_key(dev, BTN_A, (data[3] & 0x10) >> 4); input_report_key(dev, BTN_B, (data[3] & 0x20) >> 5); - input_report_key(dev, BTN_X, (data[3] & 0x80) >> 7); - input_report_key(dev, BTN_Y, (data[3] & 0x40) >> 6); + input_report_key(dev, BTN_X, (data[3] & 0x40) >> 6); + input_report_key(dev, BTN_Y, (data[3] & 0x80) >> 7); input_report_key(dev, BTN_TL, data[3] & 0x01 ); input_report_key(dev, BTN_TR, (data[3] & 0x02) >> 1); input_report_key(dev, BTN_MODE, (data[3] & 0x04) >> 2); @@ -203,11 +222,12 @@ if (xpad->isMat) return; - /* left stick (Y axis needs to be flipped) */ + /* left stick */ if(xpad->is360) { input_report_abs(dev, ABS_X, (__s16)(((__s16)data[7] << 8) | (__s16)data[6])); - input_report_abs(dev, ABS_Y, ~(__s16)(((__s16)data[9] << 8) | data[8])); + input_report_abs(dev, ABS_Y, (__s16)(((__s16)data[9] << 8) | data[8])); } else { + /* Y axis needs to be flipped */ input_report_abs(dev, ABS_X, (__s16)(((__s16)data[13] << 8) | (__s16)data[12])); input_report_abs(dev, ABS_Y, ~(__s16)(((__s16)data[15] << 8) | data[14])); } @@ -291,6 +311,40 @@ } /** + * xpad_irq_out + * + * Completion handler for interrupt in transfers (led and rumble output). + */ +static void xpad_irq_out(struct urb *urb, struct pt_regs *regs) +{ + struct usb_xpad *xpad = urb->context; + int retval; + + switch (urb->status) { + case 0: + /* success */ + break; + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + /* this urb is terminated, clean up */ + dbg("%s - urb shutting down with status: %d", + __FUNCTION__, urb->status); + return; + default: + dbg("%s - nonzero urb status received: %d", + __FUNCTION__, urb->status); + goto exit; + } + +exit: + retval = usb_submit_urb(urb, GFP_ATOMIC); + if (retval) + err("%s - usb_submit_urb failed with result %d", + __FUNCTION__, retval); +} + +/** * xpad_open * * Called when a an application opens the device. @@ -341,6 +395,7 @@ struct usb_device *udev = interface_to_usbdev(intf); char path[64]; int i; + int status; #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 13) xpad->dev.id.bustype = BUS_USB; @@ -372,45 +427,80 @@ set_bit(xpad_mat_btn[i], xpad->dev.keybit); } else { xpad->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); - - for (i = 0; xpad_btn[i] >= 0; ++i) - set_bit(xpad_btn[i], xpad->dev.keybit); - - for (i = 0; xpad_abs[i] >= 0; ++i) { - signed short t = xpad_abs[i]; - - set_bit(t, xpad->dev.absbit); - - switch (t) { - case ABS_X: - case ABS_Y: - case ABS_RX: - case ABS_RY: /* the two sticks */ - xpad->dev.absmax[t] = 32767; - xpad->dev.absmin[t] = -32768; - xpad->dev.absflat[t] = 12000; - xpad->dev.absfuzz[t] = 16; - break; - case ABS_Z: /* left trigger */ - case ABS_RZ: /* right trigger */ - case ABS_HAT1X: /* analogue button A */ - case ABS_HAT1Y: /* analogue button B */ - case ABS_HAT2X: /* analogue button C */ - case ABS_HAT2Y: /* analogue button X */ - case ABS_HAT3X: /* analogue button Y */ - case ABS_HAT3Y: /* analogue button Z */ - xpad->dev.absmax[t] = 255; - xpad->dev.absmin[t] = 0; - break; - case ABS_HAT0X: - case ABS_HAT0Y: /* the d-pad */ - xpad->dev.absmax[t] = 1; - xpad->dev.absmin[t] = -1; - break; + if (xpad->is360) { + for (i = 0; x360_btn[i] >= 0; ++i) + set_bit(x360_btn[i], xpad->dev.keybit); + + for (i = 0; x360_abs[i] >= 0; ++i) { + signed short t = x360_abs[i]; + + set_bit(t, xpad->dev.absbit); + + switch (t) { + case ABS_X: + case ABS_Y: + case ABS_RX: + case ABS_RY: /* the two sticks */ + xpad->dev.absmax[t] = 32767; + xpad->dev.absmin[t] = -32768; + xpad->dev.absflat[t] = 5000; + xpad->dev.absfuzz[t] = 16; + break; + case ABS_Z: /* left trigger */ + case ABS_RZ: /* right trigger */ + xpad->dev.absmax[t] = 255; + xpad->dev.absflat[t] = 0; + xpad->dev.absmin[t] = -255; + xpad->dev.absfuzz[t] = 0; + break; + } + } + + /* set xpad 360 leds */ + xpad->odata_led[0] = 0x01; /* message type */ + xpad->odata_led[1] = 0x03; /* message length */ + xpad->odata_led[2] = 0x00; /* use 0x00 to turn the leds off */ + if ((status = usb_submit_urb(xpad->irq_led_out, GFP_KERNEL))) { + err("sending led output urb failed: %d", status); } - } - if(!xpad->is360) { + } else { + for (i = 0; xpad_btn[i] >= 0; ++i) + set_bit(xpad_btn[i], xpad->dev.keybit); + + for (i = 0; xpad_abs[i] >= 0; ++i) { + signed short t = xpad_abs[i]; + + set_bit(t, xpad->dev.absbit); + + switch (t) { + case ABS_X: + case ABS_Y: + case ABS_RX: + case ABS_RY: /* the two sticks */ + xpad->dev.absmax[t] = 32767; + xpad->dev.absmin[t] = -32768; + xpad->dev.absflat[t] = 12000; + xpad->dev.absfuzz[t] = 16; + break; + case ABS_Z: /* left trigger */ + case ABS_RZ: /* right trigger */ + case ABS_HAT1X: /* analogue button A */ + case ABS_HAT1Y: /* analogue button B */ + case ABS_HAT2X: /* analogue button C */ + case ABS_HAT2Y: /* analogue button X */ + case ABS_HAT3X: /* analogue button Y */ + case ABS_HAT3Y: /* analogue button Z */ + xpad->dev.absmax[t] = 255; + xpad->dev.absmin[t] = 0; + break; + case ABS_HAT0X: + case ABS_HAT0Y: /* the d-pad */ + xpad->dev.absmax[t] = 1; + xpad->dev.absmin[t] = -1; + break; + } + } if (xpad_rumble_probe(udev, xpad, ifnum) != 0) err("could not init rumble"); } @@ -431,6 +521,7 @@ struct usb_device *udev = interface_to_usbdev(intf); struct usb_xpad *xpad = NULL; struct usb_endpoint_descriptor *ep_irq_in; + struct usb_endpoint_descriptor *ep_irq_out; int i; int probedDevNum = -1; /* this takes the index into the known devices array for the recognized device */ @@ -454,6 +545,10 @@ return -ENOMEM; } memset(xpad, 0, sizeof(struct usb_xpad)); + + xpad->udev = udev; + xpad->isMat = xpad_device[probedDevNum].isMat; + xpad->is360 = xpad_device[probedDevNum].is360; xpad->idata = usb_buffer_alloc(udev, XPAD_PKT_LEN, SLAB_ATOMIC, &xpad->idata_dma); @@ -462,21 +557,51 @@ kfree(xpad); return -ENOMEM; } + + /* allocate buffer for led output packets */ + if (xpad->is360) { + xpad->odata_led = usb_buffer_alloc(udev, X360_PKT_LEN_LED, + SLAB_ATOMIC, &xpad->odata_led_dma); + if (!xpad->odata_led) { + usb_buffer_free(udev, XPAD_PKT_LEN, xpad->idata, xpad->idata_dma); + kfree(xpad); + return -ENOMEM; + } + } /* setup input interrupt pipe (button and axis state) */ xpad->irq_in = usb_alloc_urb(0, GFP_KERNEL); if (!xpad->irq_in) { err("cannot allocate memory for new pad irq urb"); + + if (xpad->is360) + usb_buffer_free(udev, X360_PKT_LEN_LED, xpad->odata_led, xpad->odata_led_dma); + usb_buffer_free(udev, XPAD_PKT_LEN, xpad->idata, xpad->idata_dma); kfree(xpad); return -ENOMEM; } + if (xpad->is360) { + /* setup output interrupt pipe (led and rumble control) */ + xpad->irq_led_out = usb_alloc_urb(0, GFP_KERNEL); + if (!xpad->irq_led_out) { + err("cannot allocate memory for new pad bulk urb"); + + usb_free_urb(xpad->irq_in); + + if (xpad->is360) + usb_buffer_free(udev, X360_PKT_LEN_LED, xpad->odata_led, xpad->odata_led_dma); + + usb_buffer_free(udev, XPAD_PKT_LEN, xpad->idata, xpad->idata_dma); + kfree(xpad); + return -ENOMEM; + } + } + ep_irq_in = &intf->cur_altsetting->endpoint[0].desc; + ep_irq_out = &intf->cur_altsetting->endpoint[1].desc; - xpad->udev = udev; - xpad->isMat = xpad_device[probedDevNum].isMat; - xpad->is360 = xpad_device[probedDevNum].is360; /* init input URB for USB INT transfer from device */ usb_fill_int_urb(xpad->irq_in, udev, @@ -485,6 +610,17 @@ xpad_irq_in, xpad, ep_irq_in->bInterval); xpad->irq_in->transfer_dma = xpad->idata_dma; xpad->irq_in->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + if (xpad->is360) { + /* init output URB for USB INT transfer to device */ + usb_fill_int_urb(xpad->irq_led_out, udev, + usb_sndintpipe(udev, ep_irq_out->bEndpointAddress), + xpad->odata_led, X360_PKT_LEN_LED, + xpad_irq_out, xpad, ep_irq_out->bInterval); + xpad->irq_led_out->transfer_dma = xpad->odata_led_dma; + xpad->irq_led_out->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + } + /* we set this here so we can extract it in the two functions below */ usb_set_intfdata(intf, xpad); @@ -513,9 +649,14 @@ input_unregister_device(&xpad->dev); usb_free_urb(xpad->irq_in); + usb_free_urb(xpad->irq_led_out); usb_buffer_free(interface_to_usbdev(intf), XPAD_PKT_LEN, xpad->idata, xpad->idata_dma); + + if (xpad->is360) + usb_buffer_free(interface_to_usbdev(intf), X360_PKT_LEN_LED, + xpad->odata_led, xpad->odata_led_dma); if(!xpad->is360) { xpad_rumble_disconnect(xpad); @@ -565,7 +706,7 @@ * driver history * ---------------- * - * 2005-11-25 - 0.1.6 : Added Xbox360 Controller support + * 2005-11-25 - 0.1.6 : Added Xbox 360 Controller support * * 2005-03-15 - 0.1.5 : Mouse emulation removed. Deadzones increased. * - Flipped the Y axis of the left joystick (it was inverted, like on a diff -ruE orig/xpad.h new/xpad.h --- orig/xpad.h 2006-01-19 01:35:34.000000000 -0600 +++ new/xpad.h 2006-01-19 01:47:28.000000000 -0600 @@ -1,4 +1,4 @@ -/* +/* vim: set noet sw=8: * Xbox Controller driver for Linux - v0.1.5 * * header file containing ioctl definitions @@ -55,8 +55,11 @@ #define XPAD_MAX_DEVICES 4 #define XPAD_PKT_LEN 32 /* input packet size */ #define XPAD_PKT_LEN_FF 6 /* output packet size - rumble */ +#define X360_PKT_LEN_FF 8 /* output packet size - rumble */ +#define X360_PKT_LEN_LED 3 /* output packet size - led */ #define XPAD_TX_BUFSIZE XPAD_PKT_LEN_FF * 8 /* max. 8 requests */ +#define X360_TX_BUFSIZE X360_PKT_LEN_FF * 8 /* max. 8 requests */ /************************* the device struct **************************/ struct usb_xpad { @@ -67,6 +70,14 @@ unsigned char *idata; /* input data */ dma_addr_t idata_dma; + struct urb *irq_led_out; /* urb for int. transfer of led data */ + unsigned char *odata_led; /* output data buffer (led) */ + dma_addr_t odata_led_dma; + + struct urb *irq_ff_out; /* urb for int. transfer of force feedback data */ + unsigned char *odata_ff; /* output data buffer (force feedback) */ + dma_addr_t odata_ff_dma; + char phys[65]; /* physical input dev path */ unsigned char offsetset_compensation;