XBox One gamecontroller support

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
3 messages Options
Reply | Threaded
Open this post in threaded view
|

XBox One gamecontroller support

Thomas Frohwein-2
Hi,

This diff adds support for the XBox One gamecontroller in a similar way
to what we have for the (older) XBox 360 controller [1][2]. This diff
is based on the pertinent code in NetBSD's uhidev.c.

Similarities include that the device doesn't provide a report
descriptor, so this diff adds one to uhid_rdesc.h.

The biggest difference (that held this up for a while) is that the
controller needs an init byte sequence to "wake up."

The original report descriptor from NetBSD just maps the buttons and
axes in the order that they appear in the report. I modified the report
descriptor to assign the buttons/axes in the most logical order for
compatibility, which results in the same layout that we already have
for the XBox 360 controller.

The addition of the member sc_flags to struct uhidev_softc follows the
NetBSD example.

I have tested this with usbhidctl(1), sdl2-jstest (from sdl-jstest
package), and a couple of SDL2 games (Owlboy, Cryptark).

If you have an XBox One controller, the easiest way to test is with:

$ usbhidctl -f /dev/uhidX -l

Make sure to pick the right uhid device and that you have read
permissions for it.

Of course, this works only with the controller connected via USB, and
doesn't magically add wireless support.

If you test actual SDL2 applications, the button layout will likely
default to an odd configuration. I am simultaneously preparing a diff
for sdl2-2.0.14p0 that improves recognition and automatic mapping and
solves any issues with XBox One default button layout.

comments? ok?

[1] https://marc.info/?l=openbsd-cvs&m=138267062532046&q=mbox
[2] https://marc.info/?l=openbsd-cvs&m=144956819605939&q=mbox

Index: uhid_rdesc.h
===================================================================
RCS file: /cvs/src/sys/dev/usb/uhid_rdesc.h,v
retrieving revision 1.1
diff -u -p -r1.1 uhid_rdesc.h
--- uhid_rdesc.h 25 Oct 2013 03:09:59 -0000 1.1
+++ uhid_rdesc.h 9 Jan 2021 15:11:30 -0000
@@ -272,4 +272,94 @@ static const uByte uhid_xb360gp_report_d
     0x95, 0x01, /*  REPORT COUNT (1) */
     0x81, 0x01, /*  INPUT (Constant) */
     0xc0, /* END COLLECTION */
+};
+
+static const uByte uhid_xbonegp_report_descr[] = {
+    0x05, 0x01, /* USAGE PAGE (Generic Desktop) */
+    0x09, 0x05, /* USAGE (Gamepad) */
+    0xa1, 0x01, /* COLLECTION (Application) */
+    /* Button packet */
+    0xa1, 0x00, /*  COLLECTION (Physical) */
+    0x85, 0x20, /*   REPORT ID (0x20) */
+    /* Skip unknown field and counter */
+    0x09, 0x00, /*   USAGE (Undefined) */
+    0x75, 0x08, /*   REPORT SIZE (8) */
+    0x95, 0x02, /*   REPORT COUNT (2) */
+    0x81, 0x03, /*   INPUT (Constant, Variable, Absolute) */
+    /* Payload size */
+    0x09, 0x3b, /*   USAGE (Byte Count) */
+    0x95, 0x01, /*   REPORT COUNT (1) */
+    0x81, 0x02, /*   INPUT (Data, Variable, Absolute) */
+    /* 16 Buttons: 1-2=Unknown, 3=Start, 4=Back, 5-8=ABXY,
+     *   9-12=D-Pad(Up,Dn,Lt,Rt), 13=LB, 14=RB, 15=LS, 16=RS
+     */
+    /* Skip the first 2 as they are not used */
+    0x75, 0x01, /*   REPORT SIZE (1) */
+    0x95, 0x02, /*   REPORT COUNT (2) */
+    0x81, 0x01, /*   INPUT (Constant) */
+    /* Assign buttons Start(7), Back(8), ABXY(1-4) */
+    0x15, 0x00, /*   LOGICAL MINIMUM (0) */
+    0x25, 0x01, /*   LOGICAL MAXIMUM (1) */
+    0x95, 0x06, /*   REPORT COUNT (6) */
+    0x05, 0x09, /*   USAGE PAGE (Button) */
+    0x09, 0x08, /*   USAGE (Button 8) */
+    0x09, 0x07, /*   USAGE (Button 7) */
+    0x09, 0x01, /*   USAGE (Button 1) */
+    0x09, 0x02, /*   USAGE (Button 2) */
+    0x09, 0x03, /*   USAGE (Button 3) */
+    0x09, 0x04, /*   USAGE (Button 4) */
+    0x81, 0x02, /*   INPUT (Data, Variable, Absolute) */
+    /* D-Pad */
+    0x05, 0x01, /*   USAGE PAGE (Generic Desktop) */
+    0x09, 0x01, /*   USAGE (Pointer) */
+    0xa1, 0x00, /*   COLLECTION (Physical) */
+    0x75, 0x01, /*    REPORT SIZE (1) */
+    0x15, 0x00, /*    LOGICAL MINIMUM (0) */
+    0x25, 0x01, /*    LOGICAL MAXIMUM (1) */
+    0x95, 0x04, /*    REPORT COUNT (4) */
+    0x05, 0x01, /*    USAGE PAGE (Generic Desktop) */
+    0x09, 0x90, /*    USAGE (D-Pad Up) */
+    0x09, 0x91, /*    USAGE (D-Pad Down) */
+    0x09, 0x93, /*    USAGE (D-Pad Left) */
+    0x09, 0x92, /*    USAGE (D-Pad Right) */
+    0x81, 0x02, /*    INPUT (Data, Variable, Absolute) */
+    0xc0, /*   END COLLECTION */
+    /* Buttons 5-6 (Shoulder Buttons) and 9-10 (Stick Buttons) */
+    0x15, 0x00, /*   LOGICAL MINIMUM (0) */
+    0x25, 0x01, /*   LOGICAL MAXIMUM (1) */
+    0x95, 0x04, /*   REPORT COUNT (4) */
+    0x05, 0x09, /*   USAGE PAGE (Button) */
+    0x09, 0x05, /*   USAGE (Button 5) */
+    0x09, 0x06, /*   USAGE (Button 6) */
+    0x09, 0x09, /*   USAGE (Button 9) */
+    0x09, 0x0a, /*   USAGE (Button 10) */
+    0x81, 0x02, /*   INPUT (Data, Variable, Absolute */
+    /* Triggers */
+    0x15, 0x00, /*   LOGICAL MINIMUM (0) */
+    0x26, 0xff, 0x03, /*   LOGICAL MAXIMUM (1023) */
+    0x75, 0x10, /*   REPORT SIZE (16) */
+    0x95, 0x02, /*   REPORT COUNT (2) */
+    0x05, 0x01, /*   USAGE PAGE (Generic Desktop) */
+    0x09, 0x32, /*   USAGE (Z) */
+    0x09, 0x35, /*   USAGE (Rz) */
+    0x81, 0x02, /*   INPUT (Data, Variable, Absolute) */
+    /* Sticks */
+    0x16, 0x00, 0x80, /*   LOGICAL MINIMUM (-32768) */
+    0x26, 0xff, 0x7f, /*   LOGICAL MAXIMUM (32767) */
+    0x09, 0x01, /*   USAGE (Pointer) */
+    0xa1, 0x00, /*   COLLECTION (Physical) */
+    0x95, 0x02, /*    REPORT COUNT (2) */
+    0x05, 0x01, /*    USAGE PAGE (Generic Desktop) */
+    0x09, 0x30, /*    USAGE (X) */
+    0x09, 0x31, /*    USAGE (Y) */
+    0x81, 0x02, /*    INPUT (Data, Variable, Absolute) */
+    0xc0, /*   END COLLECTION */
+    0x09, 0x01, /*   USAGE (Pointer) */
+    0xa1, 0x00, /*   COLLECTION (Physical) */
+    0x95, 0x02, /*    REPORT COUNT (2) */
+    0x09, 0x33, /*    USAGE (Rx) */
+    0x09, 0x34, /*    USAGE (Ry) */
+    0x81, 0x02, /*    INPUT (Data, Variable, Absolute) */
+    0xc0, /*   END COLLECTION */
+    0xc0, /* END COLLECTION */
 };
Index: uhidev.c
===================================================================
RCS file: /cvs/src/sys/dev/usb/uhidev.c,v
retrieving revision 1.83
diff -u -p -r1.83 uhidev.c
--- uhidev.c 31 Aug 2020 12:26:49 -0000 1.83
+++ uhidev.c 9 Jan 2021 15:11:30 -0000
@@ -63,8 +63,10 @@
 #include <dev/usb/uhid_rdesc.h>
 int uhidev_use_rdesc(struct uhidev_softc *, usb_interface_descriptor_t *,
  int, int, void **, int *);
-#define UISUBCLASS_XBOX360_CONTROLLER 0x5d
-#define UIPROTO_XBOX360_GAMEPAD 0x01
+#define UISUBCLASS_XBOX360_CONTROLLER 0x5d
+#define UIPROTO_XBOX360_GAMEPAD 0x01
+#define UISUBCLASS_XBOXONE_CONTROLLER 0x47
+#define UIPROTO_XBOXONE_GAMEPAD 0xd0
 #endif /* !SMALL_KERNEL */
 
 #define DEVNAME(sc) ((sc)->sc_dev.dv_xname)
@@ -124,6 +126,11 @@ uhidev_match(struct device *parent, void
     id->bInterfaceSubClass == UISUBCLASS_XBOX360_CONTROLLER &&
     id->bInterfaceProtocol == UIPROTO_XBOX360_GAMEPAD)
  return (UMATCH_IFACECLASS_IFACESUBCLASS_IFACEPROTO);
+ if (id->bInterfaceClass == UICLASS_VENDOR &&
+    id->bInterfaceSubClass == UISUBCLASS_XBOXONE_CONTROLLER &&
+    id->bInterfaceProtocol == UIPROTO_XBOXONE_GAMEPAD) {
+ return (UMATCH_IFACECLASS_IFACESUBCLASS_IFACEPROTO);
+ }
 #endif /* !SMALL_KERNEL */
  if (id->bInterfaceClass != UICLASS_HID)
  return (UMATCH_NONE);
@@ -306,6 +313,13 @@ uhidev_use_rdesc(struct uhidev_softc *sc
  /* The Xbox 360 gamepad has no report descriptor. */
  size = sizeof(uhid_xb360gp_report_descr);
  descptr = uhid_xb360gp_report_descr;
+ } else if ((id->bInterfaceClass == UICLASS_VENDOR &&
+    id->bInterfaceSubClass == UISUBCLASS_XBOXONE_CONTROLLER &&
+    id->bInterfaceProtocol == UIPROTO_XBOXONE_GAMEPAD)) {
+ sc->sc_flags |= UHIDEV_F_XB1;
+ /* The Xbox One gamepad has no report descriptor. */
+ size = sizeof(uhid_xbonegp_report_descr);
+ descptr = uhid_xbonegp_report_descr;
  }
 
  if (descptr) {
@@ -557,6 +571,23 @@ uhidev_open(struct uhidev *scd)
  DPRINTF(("uhidev_open: couldn't allocate owxfer\n"));
  error = ENOMEM;
  goto out3;
+ }
+
+ /* XBox One controller initialization */
+ if (sc->sc_flags & UHIDEV_F_XB1) {
+ uint8_t init_data[] = { 0x05, 0x20 };
+ int init_data_len = sizeof(init_data);
+ usbd_setup_xfer(sc->sc_oxfer, sc->sc_opipe, 0,
+    init_data, init_data_len,
+    USBD_SYNCHRONOUS | USBD_CATCH, USBD_NO_TIMEOUT,
+    NULL);
+ err = usbd_transfer(sc->sc_oxfer);
+ if (err != USBD_NORMAL_COMPLETION) {
+ DPRINTF(("uhidev_open: xb1 init failed, "
+ "error=%d\n", err));
+ error = EIO;
+ goto out3;
+ }
  }
  }
 
Index: uhidev.h
===================================================================
RCS file: /cvs/src/sys/dev/usb/uhidev.h,v
retrieving revision 1.25
diff -u -p -r1.25 uhidev.h
--- uhidev.h 25 Aug 2018 18:32:05 -0000 1.25
+++ uhidev.h 9 Jan 2021 15:11:30 -0000
@@ -61,6 +61,9 @@ struct uhidev_softc {
  struct uhidev **sc_subdevs;
 
  int sc_refcnt;
+
+ u_int sc_flags;
+#define UHIDEV_F_XB1 0x0001 /* Xbox One controller */
 };
 
 struct uhidev {

Reply | Threaded
Open this post in threaded view
|

Re: XBox One gamecontroller support

Thomas Frohwein-2
On Sat, Jan 09, 2021 at 10:50:35AM -0700, Thomas Frohwein wrote:

> Hi,
>
> This diff adds support for the XBox One gamecontroller in a similar way
> to what we have for the (older) XBox 360 controller [1][2]. This diff
> is based on the pertinent code in NetBSD's uhidev.c.
>
> Similarities include that the device doesn't provide a report
> descriptor, so this diff adds one to uhid_rdesc.h.
>
> The biggest difference (that held this up for a while) is that the
> controller needs an init byte sequence to "wake up."
>
> The original report descriptor from NetBSD just maps the buttons and
> axes in the order that they appear in the report. I modified the report
> descriptor to assign the buttons/axes in the most logical order for
> compatibility, which results in the same layout that we already have
> for the XBox 360 controller.
>
> The addition of the member sc_flags to struct uhidev_softc follows the
> NetBSD example.
>
> I have tested this with usbhidctl(1), sdl2-jstest (from sdl-jstest
> package), and a couple of SDL2 games (Owlboy, Cryptark).
>
> If you have an XBox One controller, the easiest way to test is with:
>
> $ usbhidctl -f /dev/uhidX -l
>
> Make sure to pick the right uhid device and that you have read
> permissions for it.
>
> Of course, this works only with the controller connected via USB, and
> doesn't magically add wireless support.
>
> If you test actual SDL2 applications, the button layout will likely
> default to an odd configuration. I am simultaneously preparing a diff
> for sdl2-2.0.14p0 that improves recognition and automatic mapping and
> solves any issues with XBox One default button layout.

I have an update to this diff. 2 testers used a more recent XBox One
controller model (model number 1708 in both cases) and ran into
problems with the original diff. Below is a new diff that uses a longer
5-byte init sequence taken from Linux' xpad.c [1], compared to the
2-byte sequence from NetBSD's uhidev.c that I offered in the initial
diff. This fixed the issue for the 2 testers.

My own XBox One controller is model number 1537 and still works with
this diff.

[1] https://github.com/paroj/xpad/blob/master/xpad.c#L479

Index: uhid_rdesc.h
===================================================================
RCS file: /cvs/src/sys/dev/usb/uhid_rdesc.h,v
retrieving revision 1.1
diff -u -p -r1.1 uhid_rdesc.h
--- uhid_rdesc.h 25 Oct 2013 03:09:59 -0000 1.1
+++ uhid_rdesc.h 9 Jan 2021 15:11:30 -0000
@@ -272,4 +272,94 @@ static const uByte uhid_xb360gp_report_d
     0x95, 0x01, /*  REPORT COUNT (1) */
     0x81, 0x01, /*  INPUT (Constant) */
     0xc0, /* END COLLECTION */
+};
+
+static const uByte uhid_xbonegp_report_descr[] = {
+    0x05, 0x01, /* USAGE PAGE (Generic Desktop) */
+    0x09, 0x05, /* USAGE (Gamepad) */
+    0xa1, 0x01, /* COLLECTION (Application) */
+    /* Button packet */
+    0xa1, 0x00, /*  COLLECTION (Physical) */
+    0x85, 0x20, /*   REPORT ID (0x20) */
+    /* Skip unknown field and counter */
+    0x09, 0x00, /*   USAGE (Undefined) */
+    0x75, 0x08, /*   REPORT SIZE (8) */
+    0x95, 0x02, /*   REPORT COUNT (2) */
+    0x81, 0x03, /*   INPUT (Constant, Variable, Absolute) */
+    /* Payload size */
+    0x09, 0x3b, /*   USAGE (Byte Count) */
+    0x95, 0x01, /*   REPORT COUNT (1) */
+    0x81, 0x02, /*   INPUT (Data, Variable, Absolute) */
+    /* 16 Buttons: 1-2=Unknown, 3=Start, 4=Back, 5-8=ABXY,
+     *   9-12=D-Pad(Up,Dn,Lt,Rt), 13=LB, 14=RB, 15=LS, 16=RS
+     */
+    /* Skip the first 2 as they are not used */
+    0x75, 0x01, /*   REPORT SIZE (1) */
+    0x95, 0x02, /*   REPORT COUNT (2) */
+    0x81, 0x01, /*   INPUT (Constant) */
+    /* Assign buttons Start(7), Back(8), ABXY(1-4) */
+    0x15, 0x00, /*   LOGICAL MINIMUM (0) */
+    0x25, 0x01, /*   LOGICAL MAXIMUM (1) */
+    0x95, 0x06, /*   REPORT COUNT (6) */
+    0x05, 0x09, /*   USAGE PAGE (Button) */
+    0x09, 0x08, /*   USAGE (Button 8) */
+    0x09, 0x07, /*   USAGE (Button 7) */
+    0x09, 0x01, /*   USAGE (Button 1) */
+    0x09, 0x02, /*   USAGE (Button 2) */
+    0x09, 0x03, /*   USAGE (Button 3) */
+    0x09, 0x04, /*   USAGE (Button 4) */
+    0x81, 0x02, /*   INPUT (Data, Variable, Absolute) */
+    /* D-Pad */
+    0x05, 0x01, /*   USAGE PAGE (Generic Desktop) */
+    0x09, 0x01, /*   USAGE (Pointer) */
+    0xa1, 0x00, /*   COLLECTION (Physical) */
+    0x75, 0x01, /*    REPORT SIZE (1) */
+    0x15, 0x00, /*    LOGICAL MINIMUM (0) */
+    0x25, 0x01, /*    LOGICAL MAXIMUM (1) */
+    0x95, 0x04, /*    REPORT COUNT (4) */
+    0x05, 0x01, /*    USAGE PAGE (Generic Desktop) */
+    0x09, 0x90, /*    USAGE (D-Pad Up) */
+    0x09, 0x91, /*    USAGE (D-Pad Down) */
+    0x09, 0x93, /*    USAGE (D-Pad Left) */
+    0x09, 0x92, /*    USAGE (D-Pad Right) */
+    0x81, 0x02, /*    INPUT (Data, Variable, Absolute) */
+    0xc0, /*   END COLLECTION */
+    /* Buttons 5-6 (Shoulder Buttons) and 9-10 (Stick Buttons) */
+    0x15, 0x00, /*   LOGICAL MINIMUM (0) */
+    0x25, 0x01, /*   LOGICAL MAXIMUM (1) */
+    0x95, 0x04, /*   REPORT COUNT (4) */
+    0x05, 0x09, /*   USAGE PAGE (Button) */
+    0x09, 0x05, /*   USAGE (Button 5) */
+    0x09, 0x06, /*   USAGE (Button 6) */
+    0x09, 0x09, /*   USAGE (Button 9) */
+    0x09, 0x0a, /*   USAGE (Button 10) */
+    0x81, 0x02, /*   INPUT (Data, Variable, Absolute */
+    /* Triggers */
+    0x15, 0x00, /*   LOGICAL MINIMUM (0) */
+    0x26, 0xff, 0x03, /*   LOGICAL MAXIMUM (1023) */
+    0x75, 0x10, /*   REPORT SIZE (16) */
+    0x95, 0x02, /*   REPORT COUNT (2) */
+    0x05, 0x01, /*   USAGE PAGE (Generic Desktop) */
+    0x09, 0x32, /*   USAGE (Z) */
+    0x09, 0x35, /*   USAGE (Rz) */
+    0x81, 0x02, /*   INPUT (Data, Variable, Absolute) */
+    /* Sticks */
+    0x16, 0x00, 0x80, /*   LOGICAL MINIMUM (-32768) */
+    0x26, 0xff, 0x7f, /*   LOGICAL MAXIMUM (32767) */
+    0x09, 0x01, /*   USAGE (Pointer) */
+    0xa1, 0x00, /*   COLLECTION (Physical) */
+    0x95, 0x02, /*    REPORT COUNT (2) */
+    0x05, 0x01, /*    USAGE PAGE (Generic Desktop) */
+    0x09, 0x30, /*    USAGE (X) */
+    0x09, 0x31, /*    USAGE (Y) */
+    0x81, 0x02, /*    INPUT (Data, Variable, Absolute) */
+    0xc0, /*   END COLLECTION */
+    0x09, 0x01, /*   USAGE (Pointer) */
+    0xa1, 0x00, /*   COLLECTION (Physical) */
+    0x95, 0x02, /*    REPORT COUNT (2) */
+    0x09, 0x33, /*    USAGE (Rx) */
+    0x09, 0x34, /*    USAGE (Ry) */
+    0x81, 0x02, /*    INPUT (Data, Variable, Absolute) */
+    0xc0, /*   END COLLECTION */
+    0xc0, /* END COLLECTION */
 };
Index: uhidev.c
===================================================================
RCS file: /cvs/src/sys/dev/usb/uhidev.c,v
retrieving revision 1.83
diff -u -p -r1.83 uhidev.c
--- uhidev.c 31 Aug 2020 12:26:49 -0000 1.83
+++ uhidev.c 9 Jan 2021 15:11:30 -0000
@@ -63,8 +63,10 @@
 #include <dev/usb/uhid_rdesc.h>
 int uhidev_use_rdesc(struct uhidev_softc *, usb_interface_descriptor_t *,
  int, int, void **, int *);
-#define UISUBCLASS_XBOX360_CONTROLLER 0x5d
-#define UIPROTO_XBOX360_GAMEPAD 0x01
+#define UISUBCLASS_XBOX360_CONTROLLER 0x5d
+#define UIPROTO_XBOX360_GAMEPAD 0x01
+#define UISUBCLASS_XBOXONE_CONTROLLER 0x47
+#define UIPROTO_XBOXONE_GAMEPAD 0xd0
 #endif /* !SMALL_KERNEL */
 
 #define DEVNAME(sc) ((sc)->sc_dev.dv_xname)
@@ -124,6 +126,11 @@ uhidev_match(struct device *parent, void
     id->bInterfaceSubClass == UISUBCLASS_XBOX360_CONTROLLER &&
     id->bInterfaceProtocol == UIPROTO_XBOX360_GAMEPAD)
  return (UMATCH_IFACECLASS_IFACESUBCLASS_IFACEPROTO);
+ if (id->bInterfaceClass == UICLASS_VENDOR &&
+    id->bInterfaceSubClass == UISUBCLASS_XBOXONE_CONTROLLER &&
+    id->bInterfaceProtocol == UIPROTO_XBOXONE_GAMEPAD) {
+ return (UMATCH_IFACECLASS_IFACESUBCLASS_IFACEPROTO);
+ }
 #endif /* !SMALL_KERNEL */
  if (id->bInterfaceClass != UICLASS_HID)
  return (UMATCH_NONE);
@@ -306,6 +313,13 @@ uhidev_use_rdesc(struct uhidev_softc *sc
  /* The Xbox 360 gamepad has no report descriptor. */
  size = sizeof(uhid_xb360gp_report_descr);
  descptr = uhid_xb360gp_report_descr;
+ } else if ((id->bInterfaceClass == UICLASS_VENDOR &&
+    id->bInterfaceSubClass == UISUBCLASS_XBOXONE_CONTROLLER &&
+    id->bInterfaceProtocol == UIPROTO_XBOXONE_GAMEPAD)) {
+ sc->sc_flags |= UHIDEV_F_XB1;
+ /* The Xbox One gamepad has no report descriptor. */
+ size = sizeof(uhid_xbonegp_report_descr);
+ descptr = uhid_xbonegp_report_descr;
  }
 
  if (descptr) {
@@ -557,6 +571,23 @@ uhidev_open(struct uhidev *scd)
  DPRINTF(("uhidev_open: couldn't allocate owxfer\n"));
  error = ENOMEM;
  goto out3;
+ }
+
+ /* XBox One controller initialization */
+ if (sc->sc_flags & UHIDEV_F_XB1) {
+ uint8_t init_data[] = { 0x05, 0x20, 0x00, 0x01, 0x00 };
+ int init_data_len = sizeof(init_data);
+ usbd_setup_xfer(sc->sc_oxfer, sc->sc_opipe, 0,
+    init_data, init_data_len,
+    USBD_SYNCHRONOUS | USBD_CATCH, USBD_NO_TIMEOUT,
+    NULL);
+ err = usbd_transfer(sc->sc_oxfer);
+ if (err != USBD_NORMAL_COMPLETION) {
+ DPRINTF(("uhidev_open: xb1 init failed, "
+ "error=%d\n", err));
+ error = EIO;
+ goto out3;
+ }
  }
  }
 
Index: uhidev.h
===================================================================
RCS file: /cvs/src/sys/dev/usb/uhidev.h,v
retrieving revision 1.25
diff -u -p -r1.25 uhidev.h
--- uhidev.h 25 Aug 2018 18:32:05 -0000 1.25
+++ uhidev.h 9 Jan 2021 15:11:30 -0000
@@ -61,6 +61,9 @@ struct uhidev_softc {
  struct uhidev **sc_subdevs;
 
  int sc_refcnt;
+
+ u_int sc_flags;
+#define UHIDEV_F_XB1 0x0001 /* Xbox One controller */
 };
 
 struct uhidev {

Reply | Threaded
Open this post in threaded view
|

Re: XBox One gamecontroller support

Thomas Frohwein-2
On Fri, Jan 15, 2021 at 06:32:04AM -0700, Thomas Frohwein wrote:

> On Sat, Jan 09, 2021 at 10:50:35AM -0700, Thomas Frohwein wrote:
> > Hi,
> >
> > This diff adds support for the XBox One gamecontroller in a similar way
> > to what we have for the (older) XBox 360 controller [1][2]. This diff
> > is based on the pertinent code in NetBSD's uhidev.c.
> >
> > Similarities include that the device doesn't provide a report
> > descriptor, so this diff adds one to uhid_rdesc.h.
> >
> > The biggest difference (that held this up for a while) is that the
> > controller needs an init byte sequence to "wake up."
> >
> > The original report descriptor from NetBSD just maps the buttons and
> > axes in the order that they appear in the report. I modified the report
> > descriptor to assign the buttons/axes in the most logical order for
> > compatibility, which results in the same layout that we already have
> > for the XBox 360 controller.
> >
> > The addition of the member sc_flags to struct uhidev_softc follows the
> > NetBSD example.
> >
> > I have tested this with usbhidctl(1), sdl2-jstest (from sdl-jstest
> > package), and a couple of SDL2 games (Owlboy, Cryptark).
> >
> > If you have an XBox One controller, the easiest way to test is with:
> >
> > $ usbhidctl -f /dev/uhidX -l
> >
> > Make sure to pick the right uhid device and that you have read
> > permissions for it.
> >
> > Of course, this works only with the controller connected via USB, and
> > doesn't magically add wireless support.
> >
> > If you test actual SDL2 applications, the button layout will likely
> > default to an odd configuration. I am simultaneously preparing a diff
> > for sdl2-2.0.14p0 that improves recognition and automatic mapping and
> > solves any issues with XBox One default button layout.
>
> I have an update to this diff. 2 testers used a more recent XBox One
> controller model (model number 1708 in both cases) and ran into
> problems with the original diff. Below is a new diff that uses a longer
> 5-byte init sequence taken from Linux' xpad.c [1], compared to the
> 2-byte sequence from NetBSD's uhidev.c that I offered in the initial
> diff. This fixed the issue for the 2 testers.
>
> My own XBox One controller is model number 1537 and still works with
> this diff.
>
> [1] https://github.com/paroj/xpad/blob/master/xpad.c#L479

solene@ has tested this and is ok with it, but I was hoping to get an
opinion from someone with more mileage with the USB internals.

The items of note to direct anyone wanting to give a quick opinion
inlined below.

>
> Index: uhid_rdesc.h
> ===================================================================
> RCS file: /cvs/src/sys/dev/usb/uhid_rdesc.h,v
> retrieving revision 1.1
> diff -u -p -r1.1 uhid_rdesc.h
> --- uhid_rdesc.h 25 Oct 2013 03:09:59 -0000 1.1
> +++ uhid_rdesc.h 9 Jan 2021 15:11:30 -0000
> @@ -272,4 +272,94 @@ static const uByte uhid_xb360gp_report_d
>      0x95, 0x01, /*  REPORT COUNT (1) */
>      0x81, 0x01, /*  INPUT (Constant) */
>      0xc0, /* END COLLECTION */
> +};
> +
> +static const uByte uhid_xbonegp_report_descr[] = {
> +    0x05, 0x01, /* USAGE PAGE (Generic Desktop) */
> +    0x09, 0x05, /* USAGE (Gamepad) */
> +    0xa1, 0x01, /* COLLECTION (Application) */
> +    /* Button packet */
> +    0xa1, 0x00, /*  COLLECTION (Physical) */
> +    0x85, 0x20, /*   REPORT ID (0x20) */
> +    /* Skip unknown field and counter */
> +    0x09, 0x00, /*   USAGE (Undefined) */
> +    0x75, 0x08, /*   REPORT SIZE (8) */
> +    0x95, 0x02, /*   REPORT COUNT (2) */
> +    0x81, 0x03, /*   INPUT (Constant, Variable, Absolute) */
> +    /* Payload size */
> +    0x09, 0x3b, /*   USAGE (Byte Count) */
> +    0x95, 0x01, /*   REPORT COUNT (1) */
> +    0x81, 0x02, /*   INPUT (Data, Variable, Absolute) */
> +    /* 16 Buttons: 1-2=Unknown, 3=Start, 4=Back, 5-8=ABXY,
> +     *   9-12=D-Pad(Up,Dn,Lt,Rt), 13=LB, 14=RB, 15=LS, 16=RS
> +     */
> +    /* Skip the first 2 as they are not used */
> +    0x75, 0x01, /*   REPORT SIZE (1) */
> +    0x95, 0x02, /*   REPORT COUNT (2) */
> +    0x81, 0x01, /*   INPUT (Constant) */
> +    /* Assign buttons Start(7), Back(8), ABXY(1-4) */
> +    0x15, 0x00, /*   LOGICAL MINIMUM (0) */
> +    0x25, 0x01, /*   LOGICAL MAXIMUM (1) */
> +    0x95, 0x06, /*   REPORT COUNT (6) */
> +    0x05, 0x09, /*   USAGE PAGE (Button) */
> +    0x09, 0x08, /*   USAGE (Button 8) */
> +    0x09, 0x07, /*   USAGE (Button 7) */
> +    0x09, 0x01, /*   USAGE (Button 1) */
> +    0x09, 0x02, /*   USAGE (Button 2) */
> +    0x09, 0x03, /*   USAGE (Button 3) */
> +    0x09, 0x04, /*   USAGE (Button 4) */
> +    0x81, 0x02, /*   INPUT (Data, Variable, Absolute) */
> +    /* D-Pad */
> +    0x05, 0x01, /*   USAGE PAGE (Generic Desktop) */
> +    0x09, 0x01, /*   USAGE (Pointer) */
> +    0xa1, 0x00, /*   COLLECTION (Physical) */
> +    0x75, 0x01, /*    REPORT SIZE (1) */
> +    0x15, 0x00, /*    LOGICAL MINIMUM (0) */
> +    0x25, 0x01, /*    LOGICAL MAXIMUM (1) */
> +    0x95, 0x04, /*    REPORT COUNT (4) */
> +    0x05, 0x01, /*    USAGE PAGE (Generic Desktop) */
> +    0x09, 0x90, /*    USAGE (D-Pad Up) */
> +    0x09, 0x91, /*    USAGE (D-Pad Down) */
> +    0x09, 0x93, /*    USAGE (D-Pad Left) */
> +    0x09, 0x92, /*    USAGE (D-Pad Right) */
> +    0x81, 0x02, /*    INPUT (Data, Variable, Absolute) */
> +    0xc0, /*   END COLLECTION */
> +    /* Buttons 5-6 (Shoulder Buttons) and 9-10 (Stick Buttons) */
> +    0x15, 0x00, /*   LOGICAL MINIMUM (0) */
> +    0x25, 0x01, /*   LOGICAL MAXIMUM (1) */
> +    0x95, 0x04, /*   REPORT COUNT (4) */
> +    0x05, 0x09, /*   USAGE PAGE (Button) */
> +    0x09, 0x05, /*   USAGE (Button 5) */
> +    0x09, 0x06, /*   USAGE (Button 6) */
> +    0x09, 0x09, /*   USAGE (Button 9) */
> +    0x09, 0x0a, /*   USAGE (Button 10) */
> +    0x81, 0x02, /*   INPUT (Data, Variable, Absolute */
> +    /* Triggers */
> +    0x15, 0x00, /*   LOGICAL MINIMUM (0) */
> +    0x26, 0xff, 0x03, /*   LOGICAL MAXIMUM (1023) */
> +    0x75, 0x10, /*   REPORT SIZE (16) */
> +    0x95, 0x02, /*   REPORT COUNT (2) */
> +    0x05, 0x01, /*   USAGE PAGE (Generic Desktop) */
> +    0x09, 0x32, /*   USAGE (Z) */
> +    0x09, 0x35, /*   USAGE (Rz) */
> +    0x81, 0x02, /*   INPUT (Data, Variable, Absolute) */
> +    /* Sticks */
> +    0x16, 0x00, 0x80, /*   LOGICAL MINIMUM (-32768) */
> +    0x26, 0xff, 0x7f, /*   LOGICAL MAXIMUM (32767) */
> +    0x09, 0x01, /*   USAGE (Pointer) */
> +    0xa1, 0x00, /*   COLLECTION (Physical) */
> +    0x95, 0x02, /*    REPORT COUNT (2) */
> +    0x05, 0x01, /*    USAGE PAGE (Generic Desktop) */
> +    0x09, 0x30, /*    USAGE (X) */
> +    0x09, 0x31, /*    USAGE (Y) */
> +    0x81, 0x02, /*    INPUT (Data, Variable, Absolute) */
> +    0xc0, /*   END COLLECTION */
> +    0x09, 0x01, /*   USAGE (Pointer) */
> +    0xa1, 0x00, /*   COLLECTION (Physical) */
> +    0x95, 0x02, /*    REPORT COUNT (2) */
> +    0x09, 0x33, /*    USAGE (Rx) */
> +    0x09, 0x34, /*    USAGE (Ry) */
> +    0x81, 0x02, /*    INPUT (Data, Variable, Absolute) */
> +    0xc0, /*   END COLLECTION */
> +    0xc0, /* END COLLECTION */
>  };
> Index: uhidev.c
> ===================================================================
> RCS file: /cvs/src/sys/dev/usb/uhidev.c,v
> retrieving revision 1.83
> diff -u -p -r1.83 uhidev.c
> --- uhidev.c 31 Aug 2020 12:26:49 -0000 1.83
> +++ uhidev.c 9 Jan 2021 15:11:30 -0000
> @@ -63,8 +63,10 @@
>  #include <dev/usb/uhid_rdesc.h>
>  int uhidev_use_rdesc(struct uhidev_softc *, usb_interface_descriptor_t *,
>   int, int, void **, int *);
> -#define UISUBCLASS_XBOX360_CONTROLLER 0x5d
> -#define UIPROTO_XBOX360_GAMEPAD 0x01
> +#define UISUBCLASS_XBOX360_CONTROLLER 0x5d
> +#define UIPROTO_XBOX360_GAMEPAD 0x01
> +#define UISUBCLASS_XBOXONE_CONTROLLER 0x47
> +#define UIPROTO_XBOXONE_GAMEPAD 0xd0
>  #endif /* !SMALL_KERNEL */
>  
>  #define DEVNAME(sc) ((sc)->sc_dev.dv_xname)
> @@ -124,6 +126,11 @@ uhidev_match(struct device *parent, void
>      id->bInterfaceSubClass == UISUBCLASS_XBOX360_CONTROLLER &&
>      id->bInterfaceProtocol == UIPROTO_XBOX360_GAMEPAD)
>   return (UMATCH_IFACECLASS_IFACESUBCLASS_IFACEPROTO);
> + if (id->bInterfaceClass == UICLASS_VENDOR &&
> +    id->bInterfaceSubClass == UISUBCLASS_XBOXONE_CONTROLLER &&
> +    id->bInterfaceProtocol == UIPROTO_XBOXONE_GAMEPAD) {
> + return (UMATCH_IFACECLASS_IFACESUBCLASS_IFACEPROTO);
> + }
>  #endif /* !SMALL_KERNEL */
>   if (id->bInterfaceClass != UICLASS_HID)
>   return (UMATCH_NONE);
> @@ -306,6 +313,13 @@ uhidev_use_rdesc(struct uhidev_softc *sc
>   /* The Xbox 360 gamepad has no report descriptor. */
>   size = sizeof(uhid_xb360gp_report_descr);
>   descptr = uhid_xb360gp_report_descr;
> + } else if ((id->bInterfaceClass == UICLASS_VENDOR &&
> +    id->bInterfaceSubClass == UISUBCLASS_XBOXONE_CONTROLLER &&
> +    id->bInterfaceProtocol == UIPROTO_XBOXONE_GAMEPAD)) {
> + sc->sc_flags |= UHIDEV_F_XB1;

This is taken from NetBSD's approach; UHIDEV_F_XB1 flag is used to let
uhidev_open know to run the special init sequence for this device.

> + /* The Xbox One gamepad has no report descriptor. */
> + size = sizeof(uhid_xbonegp_report_descr);
> + descptr = uhid_xbonegp_report_descr;
>   }
>  
>   if (descptr) {
> @@ -557,6 +571,23 @@ uhidev_open(struct uhidev *scd)
>   DPRINTF(("uhidev_open: couldn't allocate owxfer\n"));
>   error = ENOMEM;
>   goto out3;
> + }
> +
> + /* XBox One controller initialization */
> + if (sc->sc_flags & UHIDEV_F_XB1) {
> + uint8_t init_data[] = { 0x05, 0x20, 0x00, 0x01, 0x00 };

The following is the main usbd transfer part that would deserve a close
look. It works, but I would appreciate another set of eyes.

> + int init_data_len = sizeof(init_data);
> + usbd_setup_xfer(sc->sc_oxfer, sc->sc_opipe, 0,
> +    init_data, init_data_len,
> +    USBD_SYNCHRONOUS | USBD_CATCH, USBD_NO_TIMEOUT,
> +    NULL);
> + err = usbd_transfer(sc->sc_oxfer);
> + if (err != USBD_NORMAL_COMPLETION) {
> + DPRINTF(("uhidev_open: xb1 init failed, "
> + "error=%d\n", err));
> + error = EIO;
> + goto out3;
> + }
>   }
>   }
>  
> Index: uhidev.h
> ===================================================================
> RCS file: /cvs/src/sys/dev/usb/uhidev.h,v
> retrieving revision 1.25
> diff -u -p -r1.25 uhidev.h
> --- uhidev.h 25 Aug 2018 18:32:05 -0000 1.25
> +++ uhidev.h 9 Jan 2021 15:11:30 -0000
> @@ -61,6 +61,9 @@ struct uhidev_softc {
>   struct uhidev **sc_subdevs;
>  
>   int sc_refcnt;
> +
> + u_int sc_flags;
> +#define UHIDEV_F_XB1 0x0001 /* Xbox One controller */
>  };

The location of the UHIDEV_F_XB1 within the struct follows what NetBSD
does.

>  
>  struct uhidev {
>