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 via email to