shithub: front

Download patch

ref: ff65b74935abd849fe8d83839c86abbd01fb44c6
parent: 051804186780ef7f3b2f4cb548be1dd0685de5fe
author: Arne <cgnarne@netcologne.de>
date: Tue Dec 3 15:21:37 EST 2024

nusb/joy: support xbox360 controllers

Added a custom hid descriptor for Xbox360 controllers.
Up to six axes are now supported for analog input.
Added a simple deadband filter for analog axes.

--- 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,
--- 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,18 @@
 		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;
 		}
 		if((l[Usage] >> 16) == 0x09){
-			m = 1ULL << (l[Usage] & 0xff);
+			m = 1ULL << (l[Usage] & 0x3f);
 			p->btns &= ~m;
 			if(v != 0)
 				p->btns |= m;
@@ -363,13 +366,111 @@
 	}
 }
 
+static void 
+xbox360(KDev *kd)
+{
+	static uchar descr[] = {
+		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, 0x09,
+		0x09, 0x0c,
+		0x09, 0x0d,
+		0x09, 0x0e,
+		0x09, 0x0f,
+		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,
+	};
+	static uchar ledcmd[] = {1,3,0};
+	Dev *d = kd->dev;
+
+	memcpy(kd->rep, descr, kd->nrep = sizeof(descr));
+	/* no blinken lights */
+	usbcmd(d, Rh2d|Rclass|Riface, Setreport, Reportout, 0, ledcmd, 3);
+}
+
+
 /* apply quirks for special devices */
 static void
-quirks(Dev *d)
+quirks(KDev *kd)
 {
 	int ret;
+	Dev *d;
 	uchar buf[17];
 
+	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 +477,12 @@
 		if(ret < 0)
 			sysfatal("failed to enable ps3 controller: %r");
 	}
+
+	/* XBox360 controller and compatible return no HID descriptor, so we provide one */
+	if(d->usb->vid == 0x045e && d->usb->did == 0x028e
+	|| d->usb->vid == 0x1bad && d->usb->did == 0xf03a){
+		xbox360(kd);
+	}
 }
 
 static void
@@ -399,7 +506,10 @@
 		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 +519,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 +530,17 @@
 	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,7 +554,11 @@
 	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)
+		if(ep->type != Eintr || (ep->dir == Eout))
+			continue;
+		if(ep->iface->csp == JoyCSP) 
+			break;
+		if(ep->iface->csp == Xbox360CSP)
 			break;
 	}
 	if(ep == nil)
--