Re: [patch] Improved xbox 360 controller support

"Matthew A. Nicholson" <[email protected]> Fri, 20 Jan 2006 03:34:51 -0600
Newsgroups gmane.linux.ports.xbox.devel
Message-ID <[email protected]>
This is a multi-part message in MIME format.
--------------010605090604000706000909
Content-Type: text/plain; charset=ISO-8859-1; format=flowed
Content-Transfer-Encoding: 7bit

Ok.  Here is the updated patch.  I have changed the led setting so that 
led '1' stays on.  This patch still appears to have problems although it 
does work.  You may need kernel 2.6.15 for this patch.

Matthew A. Nicholson wrote:
> There is a bug in that last patch that causes problems when the pad is 
> disconnected.  New patch coming soon.
> 
> Matthew A. Nicholson wrote:
> 
>> [...]
> 
> 

-- 
Matthew A. Nicholson
Matt-Land.com

--------------010605090604000706000909
Content-Type: text/plain;
 name="xpad_360.2.diff"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="xpad_360.2.diff"

diff -ruE orig/xpad.c new/xpad.c
--- orig/xpad.c	2006-01-19 01:35:20.000000000 -0600
+++ new/xpad.c	2006-01-20 03:22:02.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 */
 	{ }
 };
@@ -157,7 +170,7 @@
  */
 static void xpad_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *data, struct pt_regs *regs)
 {
-	struct input_dev *dev = &xpad->dev;
+	struct input_dev *dev = xpad->dev;
 	int i;
 
 	input_regs(dev, regs);
@@ -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,21 +395,22 @@
 	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;
-	xpad->dev.id.vendor = udev->descriptor.idVendor;
-	xpad->dev.id.product = udev->descriptor.idProduct;
-	xpad->dev.id.version = udev->descriptor.bcdDevice;
+	xpad->dev->id.bustype = BUS_USB;
+	xpad->dev->id.vendor = udev->descriptor.idVendor;
+	xpad->dev->id.product = udev->descriptor.idProduct;
+	xpad->dev->id.version = udev->descriptor.bcdDevice;
 #else
-	usb_to_input_id(udev, &xpad->dev.id);
+	usb_to_input_id(udev, &xpad->dev->id);
 #endif
-	xpad->dev.dev = &intf->dev;
-	xpad->dev.private = xpad;
-	xpad->dev.name = device.name;
-	xpad->dev.phys = xpad->phys;
-	xpad->dev.open = xpad_open;
-	xpad->dev.close = xpad_close;
+	xpad->dev->dev = &intf->dev;
+	xpad->dev->private = xpad;
+	xpad->dev->name = device.name;
+	xpad->dev->phys = xpad->phys;
+	xpad->dev->open = xpad_open;
+	xpad->dev->close = xpad_close;
 
 	usb_make_path(udev, path, 64);
 	snprintf(xpad->phys, 64, "%s/input0", path);
@@ -364,60 +419,97 @@
 	   of driver options (rumble on, etc...)
 	   yet, Vojtech said this is better done using sysfs (linux 2.6)
 	   plus, it needs a patch to the input subsystem */
-/*	xpad->dev.ioctl = xpad_ioctl;*/
+/*	xpad->dev->ioctl = xpad_ioctl;*/
 
 	if (xpad->isMat) {
-		xpad->dev.evbit[0] = BIT(EV_KEY);
+		xpad->dev->evbit[0] = BIT(EV_KEY);
 		for (i = 0; xpad_mat_btn[i] >= 0; ++i)
-			set_bit(xpad_mat_btn[i], xpad->dev.keybit);
+			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];
+		xpad->dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
+		if (xpad->is360) {
+			for (i = 0; x360_btn[i] >= 0; ++i)
+			set_bit(x360_btn[i], xpad->dev->keybit);
+
+			for (i = 0; x360_abs[i] >= 0; ++i) {
+				signed short t = x360_abs[i];
+
+				set_bit(t, xpad->dev->absbit);
+
+				switch (t) {
+				case ABS_X:
+				case ABS_Y:
+				case ABS_RX:
+				case ABS_RY:	/* the two sticks */
+					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 */
+			xpad->odata_led[2] = 0x06;	/* use 0x06 to turn on the '1' */
 
-			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 ((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");
 		}
 	}
 
-	input_register_device(&xpad->dev);
-	printk(KERN_INFO "input: %s on %s\n", xpad->dev.name, path);
+	input_register_device(xpad->dev);
+	printk(KERN_INFO "input: %s on %s\n", xpad->dev->name, path);
 }
 
 /**
@@ -431,6 +523,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 */
@@ -449,34 +542,76 @@
 				     !xpad_device[probedDevNum].idProduct))
 		return -ENODEV;
 
-	if ((xpad = kmalloc (sizeof(struct usb_xpad), GFP_KERNEL)) == NULL) {
+	if ((xpad = kzalloc (sizeof(struct usb_xpad), GFP_KERNEL)) == NULL) {
 		err("cannot allocate memory for new pad");
 		return -ENOMEM;
 	}
-	memset(xpad, 0, sizeof(struct usb_xpad));
+
+	if ((xpad->dev = input_allocate_device()) == NULL) {
+		kfree(xpad);
+		return -ENOMEM;
+	}
+	
+	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);
 
 	if (!xpad->idata) {
+		input_free_device(xpad->dev);
 		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);
+			input_free_device(xpad->dev);
+			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);
+		input_free_device(xpad->dev);
                 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);
+			input_free_device(xpad->dev);
+			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 +620,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);
@@ -507,20 +653,33 @@
 	usb_set_intfdata(intf, NULL);
 	if (xpad) {
 		usb_kill_urb(xpad->irq_in);
+
+		if (xpad->is360)
+			usb_kill_urb(xpad->irq_led_out);
+
 		if(!xpad->is360) {
 			xpad_rumble_close(xpad);
 		}
-		input_unregister_device(&xpad->dev);
+		input_unregister_device(xpad->dev);
 
 		usb_free_urb(xpad->irq_in);
-
+	
 		usb_buffer_free(interface_to_usbdev(intf), XPAD_PKT_LEN,
 				xpad->idata, xpad->idata_dma);
+		
+
+		
+		if (xpad->is360) {
+			usb_free_urb(xpad->irq_led_out);
+			usb_buffer_free(interface_to_usbdev(intf), X360_PKT_LEN_LED,
+					xpad->odata_led, xpad->odata_led_dma);
+		}
 
 		if(!xpad->is360) {
 			xpad_rumble_disconnect(xpad);
 		}
 
+		input_free_device(xpad->dev);
 		kfree(xpad);
 	}
 }
@@ -565,7 +724,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-20 03:17:58.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,18 +55,29 @@
 #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 {
-	struct input_dev dev;			/* input device interface */
+	struct input_dev *dev;			/* input device interface */
 	struct usb_device *udev;		/* usb device */
 
 	struct urb *irq_in;			/* urb for int. in report */
 	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;

--------------010605090604000706000909--


-------------------------------------------------------
This SF.net email is sponsored by: Splunk Inc. Do you grep through log files
for problems?  Stop!  Download the new AJAX search engine that makes
searching your log files as easy as surfing the  web.  DOWNLOAD SPLUNK!
http://sel.as-us.falkag.net/sel?cmd=lnk&kid=103432&bid=230486&dat=121642