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 {
>