diff 0c4e4cb9e92e372dc0cb3df182790dc0b1c757ad uncommitted
--- a/sys/man/4/nusb
+++ b/sys/man/4/nusb
@@ -44,6 +44,9 @@
 .B nusb/joy
 [
 .B -d
+] [
+.B -b
+.I deadband
 ]
 .I devid
 .PP
@@ -190,7 +193,7 @@
 any changes in the device's axes or buttons.
 Buttons are identified via an integer id.
 .PP
-Directional buttons are reported as axes
+Directional buttons are reported either as buttons or axes
 with 3 positions:
 .IR 0 ,
 .IR 128 ,
@@ -198,6 +201,12 @@
 .I 255
 for left (or up), center and right (or down) respectively.
 Messages are in the form \fIaxis id position\fR.
+.PP
+A deadband in the range of
+.RI [ 0.0 , 1.0 ]
+can be set with the
+.B \-b 
+option to filter out input below a threshold on all analog axes.
 .PP
 On/off buttons are reported as either
 .I down
--- a/sys/src/cmd/nusb/joy/hid.h
+++ b/sys/src/cmd/nusb/joy/hid.h
@@ -7,8 +7,9 @@
 
 	/* HID class subclass protocol ids */
 	JoyCSP		= 0x000003,
+	Xbox360CSP	= 0x015dff,
 	
-	Maxaxes = 3,
+	Maxaxes = 6,
 
 	/* Requests */
 	Getreport = 0x01,
@@ -74,4 +75,88 @@
 	Flinear	= 0<<4,	Fnonlin	= 1<<4,
 	Fpref	= 0<<5,	Fnopref	= 1<<5,
 	Fnonull	= 0<<6,	Fnullst	= 1<<6,
+};
+
+uchar xbox360desc[] = {
+    0x05, 0x01,
+    0x09, 0x05,
+    0xa1, 0x01,
+    0x75, 0x08,	
+    0x95, 0x01,	
+    0x81, 0x01,
+    0x75, 0x08,
+    0x95, 0x01,
+    0x05, 0x01,
+    0x09, 0x3b,
+    0x81, 0x01,
+    0x05, 0x01,	
+    0x09, 0x01,
+    0xa1, 0x00,
+    0x75, 0x01,
+    0x15, 0x00,
+    0x25, 0x01,
+    0x35, 0x00,	
+    0x45, 0x01,	
+    0x95, 0x04,
+    0x05, 0x01,
+    0x09, 0x90,
+    0x09, 0x91,
+    0x09, 0x93,
+    0x09, 0x92,
+    0x81, 0x02,
+    0xc0,
+    0x75, 0x01,
+    0x15, 0x00,
+    0x25, 0x01,
+    0x35, 0x00,
+    0x45, 0x01,
+    0x95, 0x07,
+    0x05, 0x09,
+    0x09, 0x08,
+    0x09, 0x07,
+    0x09, 0x09,
+    0x09, 0x0a,
+    0x09, 0x05,
+    0x09, 0x06,
+    0x09, 0x0b,
+    0x81, 0x02,
+    0x75, 0x01,
+    0x95, 0x01,
+    0x81, 0x01,
+    0x75, 0x01,
+    0x15, 0x00,
+    0x25, 0x01,
+    0x35, 0x00,
+    0x45, 0x01,
+    0x95, 0x04,
+    0x05, 0x09,
+    0x19, 0x01,
+    0x29, 0x04,
+    0x81, 0x02,	
+    0x75, 0x08,
+    0x15, 0x00,
+    0x26, 0xff, 0x00,
+    0x35, 0x00,
+    0x46, 0xff, 0x00,
+    0x95, 0x02,
+    0x05, 0x01,
+    0x09, 0x32,
+    0x09, 0x35,
+    0x81, 0x02,
+    0x75, 0x10,
+    0x16, 0x00, 0x80,
+    0x26, 0xff, 0x7f,
+    0x36, 0x00, 0x80,
+    0x46, 0xff, 0x7f,
+    0x95, 0x04,
+    0x05, 0x01,
+    0x09, 0x30,
+    0x09, 0x31,
+    0x09, 0x33,
+    0x09, 0x34,
+    0x81, 0x02,
+    0x75, 0x30,
+    0x95, 0x01,
+    0x81, 0x01,
+    0xc0,
 };
--- a/sys/src/cmd/nusb/joy/joy.c
+++ b/sys/src/cmd/nusb/joy/joy.c
@@ -47,6 +47,7 @@
 }
 
 static int debug, kbd;
+static double deadband;
 
 static int
 signext(int v, int bits)
@@ -219,8 +220,7 @@
 			fprint(2, "\n");
 		}
 		proto = Reportproto;
-	}else
-		kbfatal(f, "no report");
+	}
 
 	/*
 	 * if a HID's subclass code is 1 (boot mode), it will support
@@ -283,15 +283,28 @@
 		case 0x010030:
 		case 0x010031:
 		case 0x010032:
+		case 0x010033:
+		case 0x010034:
+		case 0x010035:
 			i = l[Usage] - 0x010030;
 			if((f & (Fabs|Frel)) == Fabs)
-				p->axes[i] = v;
+				p->axes[i] = (abs(v)<(g[LogiMax]*deadband))?0:v;
 			else
-				p->axes[i] += v;
+				p->axes[i] += (abs(v)<(g[LogiMax]*deadband))?0:v;
 			break;
+
+		case 0x010090:
+		case 0x010091:
+		case 0x010092:
+		case 0x010093:
+			m = 1ULL << (l[Usage] & 0x1f);
+			p->btns &= ~m;			
+			if(v != 0)
+				p->btns |= m;
+			break;
 		}
 		if((l[Usage] >> 16) == 0x09){
-			m = 1ULL << (l[Usage] & 0xff);
+			m = 1ULL << (l[Usage] & 0x0f);
 			p->btns &= ~m;
 			if(v != 0)
 				p->btns |= m;
@@ -365,11 +378,15 @@
 
 /* apply quirks for special devices */
 static void
-quirks(Dev *d)
+quirks(KDev *kd)
 {
 	int ret;
+	Dev *d;
 	uchar buf[17];
+	static uchar xbox360ledcmd[] = {1,3,0};
 
+	d = kd->dev;
+
 	/* sony dualshock 3 (ps3) controller requires special enable command */
 	if(d->usb->vid == 0x054c && d->usb->did == 0x0268){
 		ret = usbcmd(d, Rd2h|Rclass|Riface, Getreport, (0x3<<8) | 0xF2, 0, buf, sizeof(buf));
@@ -376,6 +393,15 @@
 		if(ret < 0)
 			sysfatal("failed to enable ps3 controller: %r");
 	}
+
+	/* XBox360 controller returns no HID descriptor, so we provide one */
+	if(d->usb->vid == 0x045e && d->usb->did == 0x028e
+	|| d->usb->vid == 0x1bad && d->usb->did == 0xf03a){
+		memcpy(kd->rep, xbox360desc, sizeof(xbox360desc));
+		kd->nrep = sizeof(xbox360desc);
+		/* no blinken lights */
+		usbcmd(d, Rh2d|Rclass|Riface, Setreport, Reportout, 0, xbox360ledcmd, 3);
+	}
 }
 
 static void
@@ -399,7 +425,9 @@
 		fprint(2, "%s: %s: opendevdata: %r\n", argv0, kd->ep->dir);
 		goto Err;
 	}
-	quirks(kd->dev);
+	quirks(kd);
+	if(kd->nrep == 0) kbfatal(kd, "no report");
+
 	f(kd);
 	return;
 Err:
@@ -409,7 +437,7 @@
 static void
 usage(void)
 {
-	fprint(2, "usage: %s [-d] devid\n", argv0);
+	fprint(2, "usage: %s [-d] [-b deadband]  devid\n", argv0);
 	threadexits("usage");
 }
 
@@ -420,11 +448,16 @@
 	Dev *d;
 	Ep *ep;
 	Usbdev *ud;
+	char *b;
 
 	ARGBEGIN{
 	case 'd':
 		debug++;
 		break;
+	case 'b':
+		b = EARGF(usage());
+		deadband = atof(b);
+		if(deadband > 0.0 && deadband < 1.0) break;
 	default:
 		usage();
 	}ARGEND;
@@ -438,8 +471,10 @@
 	for(i = 0; i < nelem(ud->ep); i++){
 		if((ep = ud->ep[i]) == nil)
 			continue;
-		if(ep->type == Eintr && ep->dir == Ein && ep->iface->csp == JoyCSP)
-			break;
+		if(ep->type != Eintr || (ep->dir == Eout))
+			continue;
+		if(ep->iface->csp == JoyCSP) break;
+		if(ep->iface->csp == Xbox360CSP) break;		
 	}
 	if(ep == nil)
 		sysfatal("no suitable endpoint found");
