Index: sys/dev/usb/input/uhid.c =================================================================== --- sys/dev/usb/input/uhid.c +++ sys/dev/usb/input/uhid.c @@ -70,6 +70,7 @@ #include #include #include +#include #define USB_DEBUG_VAR uhid_debug #include @@ -96,6 +97,20 @@ UHID_N_TRANSFER, }; +enum { + UHID_READ_PROCESS, + UHID_READ_FAKED, + UHID_READ_IGNORE, +}; + +struct uhid_outpacket { + STAILQ_ENTRY(uhid_outpacket) entries; + void (*free)(struct uhid_outpacket *); + void *buf; + uint16_t buflen; + uint8_t data[]; +}; + struct uhid_softc { struct usb_fifo_sc sc_fifo; struct mtx sc_mtx; @@ -104,6 +119,8 @@ struct usb_device *sc_udev; void *sc_repdesc_ptr; + STAILQ_HEAD(, uhid_outpacket) sc_outqueue; + uint32_t sc_isize; uint32_t sc_osize; uint32_t sc_fsize; @@ -119,9 +136,126 @@ #define UHID_FLAG_IMMED 0x01 /* set if read should be immediate */ #define UHID_FLAG_STATIC_DESC 0x04 /* set if report descriptors are * static */ + uint8_t sc_flavor; +#define UHID_FLAVOR_NONE 0 +#define UHID_FLAVOR_XB1 1 + }; +static void +uhid_outpacket_free(struct uhid_outpacket *pkt) +{ + free(pkt, M_USBDEV); +} + +static void +uhid_push_command(struct uhid_softc *sc, uint8_t *data, uint16_t len) +{ + unsigned long size = sizeof(struct uhid_outpacket) + len; + struct uhid_outpacket *pkt = malloc(size, M_USBDEV, M_WAITOK); + pkt->buf = pkt->data; + pkt->buflen = len; + pkt->free = uhid_outpacket_free; + memcpy(pkt->data, data, len); + STAILQ_INSERT_TAIL(&sc->sc_outqueue, pkt, entries); +} + +static int +uhid_xb1_handling(struct uhid_softc *sc, struct usb_page_cache *pc, int *len) +{ + struct usb_page_search page_info; + uint8_t *data; + + usbd_get_page(pc, 0, &page_info); + data = (uint8_t *) page_info.buffer; + + if (page_info.length < 3) { + return UHID_READ_IGNORE; + } + + if (data[0] == 0x07) { + if (data[1] == 0x30) { + uint8_t mode_report_ack[] = { + 0x01, 0x20, data[2], 0x09, + 0x00, 0x07, 0x20, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + uhid_push_command(sc, mode_report_ack, + sizeof(mode_report_ack)); + usbd_transfer_start(sc->sc_xfer[UHID_INTR_DT_WR]); + } + + return UHID_READ_PROCESS; + } + + if (data[0] != 0x20) { + return UHID_READ_IGNORE; + } + + return UHID_READ_PROCESS; +} + + + +/* + * The init sequences were gleaned from linux xpad.c + */ +static const uint8_t uhid_xb1_initseq_fw2015[] = { + 0x05, 0x20, 0x00, 0x01, 0x00 +}; +static const uint8_t uhid_xb1_initseq_hori[] = { + 0x01, 0x20, 0x00, 0x09, 0x00, 0x04, 0x20, 0x3a, + 0x00, 0x00, 0x00, 0x80, 0x00 +}; +static const uint8_t uhid_xb1_initseq_pdp_1[] = { + 0x0a, 0x20, 0x00, 0x03, 0x00, 0x01, 0x14 +}; +static const uint8_t uhid_xb1_initseq_pdp_2[] = { + 0x06, 0x20, 0x00, 0x02, 0x01, 0x00 +}; +static const uint8_t uhid_xb1_initseq_rumble_begin[] = { + 0x09, 0x00, 0x00, 0x09, 0x00, 0x0F, 0x00, 0x00, + 0x1D, 0x1D, 0xFF, 0x00, 0x00 +}; +static const uint8_t uhid_xb1_initseq_rumble_end[] = { + 0x09, 0x00, 0x00, 0x09, 0x00, 0x0F, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00 +}; + + +struct uhid_xb1_initseq { + uint16_t idVendor; + uint16_t idProduct; + uint16_t len; + const uint8_t *data; +}; + +#define UHID_XB1_INITSEQ_MATCH(_vid, _pid, _data) \ + { \ + .idVendor = (_vid), \ + .idProduct = (_pid), \ + .data = (_data), \ + .len = sizeof(_data), \ + } + +static const struct uhid_xb1_initseq uhid_xb1_initseq[] = { + UHID_XB1_INITSEQ_MATCH(0x0e6f, 0x0165, uhid_xb1_initseq_hori), + UHID_XB1_INITSEQ_MATCH(0x0f0d, 0x0067, uhid_xb1_initseq_hori), + UHID_XB1_INITSEQ_MATCH(0x0000, 0x0000, uhid_xb1_initseq_fw2015), + UHID_XB1_INITSEQ_MATCH(0x0e6f, 0x0000, uhid_xb1_initseq_pdp_1), + UHID_XB1_INITSEQ_MATCH(0x0e6f, 0x0000, uhid_xb1_initseq_pdp_2), + UHID_XB1_INITSEQ_MATCH(0x24c6, 0x541a, uhid_xb1_initseq_rumble_begin), + UHID_XB1_INITSEQ_MATCH(0x24c6, 0x542a, uhid_xb1_initseq_rumble_begin), + UHID_XB1_INITSEQ_MATCH(0x24c6, 0x543a, uhid_xb1_initseq_rumble_begin), + UHID_XB1_INITSEQ_MATCH(0x24c6, 0x541a, uhid_xb1_initseq_rumble_end), + UHID_XB1_INITSEQ_MATCH(0x24c6, 0x542a, uhid_xb1_initseq_rumble_end), + UHID_XB1_INITSEQ_MATCH(0x24c6, 0x543a, uhid_xb1_initseq_rumble_end), +}; + + static const uint8_t uhid_xb360gp_report_descr[] = {UHID_XB360GP_REPORT_DESCR()}; +static const uint8_t uhid_xb1_report_descr[] = { UHID_XB1_REPORT_DESCR() }; static const uint8_t uhid_graphire_report_descr[] = {UHID_GRAPHIRE_REPORT_DESCR()}; static const uint8_t uhid_graphire3_4x5_report_descr[] = {UHID_GRAPHIRE3_4X5_REPORT_DESCR()}; @@ -167,6 +301,20 @@ case USB_ST_SETUP: tr_setup: pc = usbd_xfer_get_frame(xfer, 0); + + if (!STAILQ_EMPTY(&sc->sc_outqueue)) { + struct uhid_outpacket *pkt = STAILQ_FIRST(&sc->sc_outqueue); + STAILQ_REMOVE_HEAD(&sc->sc_outqueue, entries); + + usbd_copy_in(pc, 0, pkt->buf, pkt->buflen); + usbd_xfer_set_frame_len(xfer, 0, pkt->buflen); + usbd_transfer_submit(xfer); + + if (pkt->free) + pkt->free(pkt); + return; + } + if (usb_fifo_get_data(sc->sc_fifo.fp[USB_FIFO_TX], pc, 0, usbd_xfer_max_len(xfer), &actlen, 0)) { usbd_xfer_set_frame_len(xfer, 0, actlen); @@ -199,6 +347,16 @@ pc = usbd_xfer_get_frame(xfer, 0); + if (sc->sc_flavor == UHID_FLAVOR_XB1) { + switch(uhid_xb1_handling(sc, pc, &actlen)) { + case UHID_READ_FAKED: + case UHID_READ_PROCESS: + break; + case UHID_READ_IGNORE: + goto ignored; + } + } + /* * If the ID byte is non zero we allow descriptors * having multiple sizes: @@ -211,6 +369,7 @@ usb_fifo_put_data(sc->sc_fifo.fp[USB_FIFO_RX], pc, 0, actlen, 1); } else { + ignored: /* ignore it */ DPRINTF("ignored transfer, %d bytes\n", actlen); } @@ -668,6 +827,10 @@ {USB_IFACE_CLASS(UICLASS_VENDOR), USB_IFACE_SUBCLASS(UISUBCLASS_XBOX360_CONTROLLER), USB_IFACE_PROTOCOL(UIPROTO_XBOX360_GAMEPAD),}, + /* the Xbox One gamepad doesn't use the HID class */ + {USB_IFACE_CLASS(UICLASS_VENDOR), + USB_IFACE_SUBCLASS(UISUBCLASS_XBOXONE_CONTROLLER), + USB_IFACE_PROTOCOL(UIPROTO_XBOXONE_GAMEPAD),}, }; static int @@ -689,6 +852,16 @@ return (ENXIO); /* + * Xbox One controller is matched by subclass/protocol + * but lists three interfaces, we can only deal with iface 0 + */ + if ((uaa->info.bInterfaceClass == UICLASS_VENDOR) && + (uaa->info.bInterfaceSubClass == UISUBCLASS_XBOXONE_CONTROLLER) && + (uaa->info.bInterfaceProtocol == UIPROTO_XBOXONE_GAMEPAD) && + (uaa->info.bIfaceIndex != 0)) + return (ENXIO); + + /* * Don't attach to mouse and keyboard devices, hence then no * "nomatch" event is generated and then ums and ukbd won't * attach properly when loaded. @@ -718,6 +891,9 @@ mtx_init(&sc->sc_mtx, "uhid lock", NULL, MTX_DEF | MTX_RECURSE); + sc->sc_flavor = UHID_FLAVOR_NONE; + STAILQ_INIT(&sc->sc_outqueue); + sc->sc_udev = uaa->device; sc->sc_iface_no = uaa->info.bIfaceNum; @@ -781,6 +957,26 @@ sc->sc_repdesc_size = sizeof(uhid_xb360gp_report_descr); sc->sc_repdesc_ptr = __DECONST(void *, &uhid_xb360gp_report_descr); sc->sc_flags |= UHID_FLAG_STATIC_DESC; + } else if ((uaa->info.bInterfaceClass == UICLASS_VENDOR) && + (uaa->info.bInterfaceSubClass == UISUBCLASS_XBOXONE_CONTROLLER) && + (uaa->info.bInterfaceProtocol == UIPROTO_XBOXONE_GAMEPAD)) { + /* the Xbox One gamepad has no report descriptor */ + sc->sc_repdesc_size = sizeof(uhid_xb1_report_descr); + sc->sc_repdesc_ptr = __DECONST(void *, &uhid_xb1_report_descr); + sc->sc_flags |= UHID_FLAG_STATIC_DESC; + + sc->sc_flavor = UHID_FLAVOR_XB1; + for (int i = 0 ; i < nitems(uhid_xb1_initseq) ; i++) { + const struct uhid_xb1_initseq *iseq = &uhid_xb1_initseq[i]; + + if ((iseq->idVendor == 0 || + iseq->idVendor == uaa->info.idVendor) && + (iseq->idProduct == 0 || + iseq->idProduct == uaa->info.idProduct)) { + uhid_push_command(sc, __DECONST(uint8_t *, iseq->data), + iseq->len); + } + } } if (sc->sc_repdesc_ptr == NULL) { @@ -835,6 +1031,11 @@ if (error) { goto detach; } + + if (!STAILQ_EMPTY(&sc->sc_outqueue)) { + usbd_transfer_start(sc->sc_xfer[UHID_INTR_DT_WR]); + } + return (0); /* success */ detach: Index: sys/dev/usb/input/usb_rdesc.h =================================================================== --- sys/dev/usb/input/usb_rdesc.h +++ sys/dev/usb/input/usb_rdesc.h @@ -276,6 +276,94 @@ 0x81, 0x01, /* INPUT (Constant) */\ 0xc0 /* END COLLECTION */\ +#define UHID_XB1_REPORT_DESCR(...) \ + 0x05, 0x01, /* USAGE PAGE (Generic Desktop) */\ + 0x09, 0x05, /* USAGE (Game pad) */\ + 0xa1, 0x01, /* COLLECTION (Application) */\ + /* Unused */\ + 0x75, 0x08, /* REPORT SIZE (8) */\ + 0x95, 0x02, /* REPORT COUNT (2) */\ + 0x81, 0x01, /* INPUT (Constant) */\ + /* Byte count */\ + 0x75, 0x08, /* REPORT SIZE (8) */\ + 0x95, 0x01, /* REPORT COUNT (1) */\ + 0x05, 0x01, /* USAGE PAGE (Generic Desktop) */\ + 0x09, 0x3b, /* USAGE (Byte Count) */\ + 0x81, 0x01, /* INPUT (Constant) */\ + /* Unused */\ + 0x75, 0x08, /* REPORT SIZE (8) */\ + 0x95, 0x01, /* REPORT COUNT (1) */\ + 0x81, 0x01, /* INPUT (Constant) */\ + /* Buttons */\ + /* Wireless, Mode, Start, Select, A, B, X, Y */\ + 0x05, 0x09, /* USAGE PAGE (Button) */\ + 0x19, 0x01, /* USAGE MINIMUM (Button 1) */\ + 0x29, 0x08, /* USAGE MAXIMUM (Button 8) */\ + 0x75, 0x01, /* REPORT SIZE (1) */\ + 0x95, 0x08, /* REPORT COUNT (8) */\ + 0x15, 0x00, /* LOGICAL MINIMUM (0) */\ + 0x25, 0x01, /* LOGICAL MAXIMUM (1) */\ + 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) */\ + 0x95, 0x04, /* REPORT COUNT (4) */\ + 0x15, 0x00, /* LOGICAL MINIMUM (0) */\ + 0x25, 0x01, /* LOGICAL MAXIMUM (1) */\ + 0x35, 0x00, /* PHYSICAL MINIMUM (0) */\ + 0x45, 0x01, /* PHYSICAL MAXIMUM (1) */\ + 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 */\ + /* Trigger left, Trigger right, Stick left, Stick right */\ + 0x05, 0x09, /* USAGE PAGE (Button) */\ + 0x19, 0x09, /* USAGE MINIMUM (Button 9) */\ + 0x29, 0x0c, /* USAGE MAXIMUM (Button 12) */\ + 0x15, 0x00, /* LOGICAL MINIMUM (0) */\ + 0x25, 0x01, /* LOGICAL MAXIMUM (1) */\ + 0x95, 0x04, /* REPORT COUNT (4) */\ + 0x75, 0x01, /* REPORT SIZE (1) */\ + 0x81, 0x02, /* INPUT (Data, Variable, Absolute) */\ + /* Triggers (left/right) */\ + 0x05, 0x01, /* USAGE PAGE (Generic Desktop) */\ + 0x75, 0x10, /* REPORT SIZE (16) */\ + 0x95, 0x02, /* REPORT COUNT (2) */\ + 0x09, 0x32, /* USAGE (Z) */\ + 0x09, 0x35, /* USAGE (Rz) */\ + 0x15, 0x00, /* LOGICAL MINIMUM (0) */\ + 0x26, 0xff, 0x03, /* LOGICAL MAXIMUM (1023) */\ + 0x35, 0x00, /* PHYSICAL MINIMUM (0) */\ + 0x46, 0xff, 0x03, /* PHYSICAL MAXIMUM (1023) */\ + 0x81, 0x02, /* INPUT (Data, Variable, Absolute) */\ + /* Sticks (left/right) */\ + 0x05, 0x01, /* USAGE PAGE (Generic Desktop) */\ + 0x75, 0x10, /* REPORT SIZE (16) */\ + 0x95, 0x02, /* REPORT COUNT (2) */\ + 0x16, 0x00, 0x80, /* LOGICAL MINIMUM (-32768) */\ + 0x26, 0xff, 0x7f, /* LOGICAL MAXIMUM (32767) */\ + 0x36, 0x00, 0x80, /* PHYSICAL MINIMUM (-32768) */\ + 0x46, 0xff, 0x7f, /* PHYSICAL MAXIMUM (32767) */\ + 0x09, 0x01, /* USAGE (Pointer) */\ + 0xa1, 0x00, /* COLLECTION (Physical) */\ + 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) */\ + 0x09, 0x33, /* USAGE (Rx) */\ + 0x09, 0x34, /* USAGE (Ry) */\ + 0x81, 0x02, /* INPUT (Data, Variable, Absolute) */\ + 0xc0, /* END COLLECTION */\ + 0xc0 /* END COLLECTION */\ + /* Fixed report descriptor for Super Nintendo gamepads */ #define UHID_SNES_REPORT_DESCR(...) \ 0x05, 0x01, /* Usage Page (Desktop), */\ Index: sys/dev/usb/usb.h =================================================================== --- sys/dev/usb/usb.h +++ sys/dev/usb/usb.h @@ -515,6 +515,8 @@ #define UICLASS_VENDOR 0xff #define UISUBCLASS_XBOX360_CONTROLLER 0x5d #define UIPROTO_XBOX360_GAMEPAD 0x01 +#define UISUBCLASS_XBOXONE_CONTROLLER 0x47 +#define UIPROTO_XBOXONE_GAMEPAD 0xd0 struct usb_endpoint_descriptor { uByte bLength; Index: sys/dev/usb/usbdevs =================================================================== --- sys/dev/usb/usbdevs +++ sys/dev/usb/usbdevs @@ -3233,6 +3233,7 @@ product MICROSOFT NATURAL4000 0x00db Natural Ergonomic Keyboard 4000 product MICROSOFT WLNOTEBOOK2 0x00e1 Wireless Optical Mouse 3000 (Model 1056) product MICROSOFT XBOX360 0x0292 XBOX 360 WLAN +product MICROSOFT XBOXONE 0x02ea XBOX One Controller /* Microtech products */ product MICROTECH SCSIDB25 0x0004 USB-SCSI-DB25