1*d97d5c0cSVladimir Kondratyev /*- 2*d97d5c0cSVladimir Kondratyev * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3*d97d5c0cSVladimir Kondratyev * 4*d97d5c0cSVladimir Kondratyev * Copyright (c) 2020 Vladimir Kondratyev <wulf@FreeBSD.org> 5*d97d5c0cSVladimir Kondratyev * Copyright (c) 2020 Greg V <greg@unrelenting.technology> 6*d97d5c0cSVladimir Kondratyev * 7*d97d5c0cSVladimir Kondratyev * Redistribution and use in source and binary forms, with or without 8*d97d5c0cSVladimir Kondratyev * modification, are permitted provided that the following conditions 9*d97d5c0cSVladimir Kondratyev * are met: 10*d97d5c0cSVladimir Kondratyev * 1. Redistributions of source code must retain the above copyright 11*d97d5c0cSVladimir Kondratyev * notice, this list of conditions and the following disclaimer. 12*d97d5c0cSVladimir Kondratyev * 2. Redistributions in binary form must reproduce the above copyright 13*d97d5c0cSVladimir Kondratyev * notice, this list of conditions and the following disclaimer in the 14*d97d5c0cSVladimir Kondratyev * documentation and/or other materials provided with the distribution. 15*d97d5c0cSVladimir Kondratyev * 16*d97d5c0cSVladimir Kondratyev * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17*d97d5c0cSVladimir Kondratyev * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18*d97d5c0cSVladimir Kondratyev * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19*d97d5c0cSVladimir Kondratyev * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20*d97d5c0cSVladimir Kondratyev * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21*d97d5c0cSVladimir Kondratyev * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22*d97d5c0cSVladimir Kondratyev * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23*d97d5c0cSVladimir Kondratyev * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24*d97d5c0cSVladimir Kondratyev * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25*d97d5c0cSVladimir Kondratyev * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26*d97d5c0cSVladimir Kondratyev * SUCH DAMAGE. 27*d97d5c0cSVladimir Kondratyev */ 28*d97d5c0cSVladimir Kondratyev 29*d97d5c0cSVladimir Kondratyev #include <sys/cdefs.h> 30*d97d5c0cSVladimir Kondratyev __FBSDID("$FreeBSD$"); 31*d97d5c0cSVladimir Kondratyev 32*d97d5c0cSVladimir Kondratyev /* 33*d97d5c0cSVladimir Kondratyev * XBox 360 gamepad driver thanks to the custom descriptor in usbhid. 34*d97d5c0cSVladimir Kondratyev * 35*d97d5c0cSVladimir Kondratyev * Tested on: SVEN GC-5070 in both XInput (XBox 360) and DirectInput modes 36*d97d5c0cSVladimir Kondratyev */ 37*d97d5c0cSVladimir Kondratyev 38*d97d5c0cSVladimir Kondratyev #include <sys/param.h> 39*d97d5c0cSVladimir Kondratyev #include <sys/bus.h> 40*d97d5c0cSVladimir Kondratyev #include <sys/kernel.h> 41*d97d5c0cSVladimir Kondratyev #include <sys/module.h> 42*d97d5c0cSVladimir Kondratyev #include <sys/sysctl.h> 43*d97d5c0cSVladimir Kondratyev 44*d97d5c0cSVladimir Kondratyev #include <dev/evdev/input.h> 45*d97d5c0cSVladimir Kondratyev #include <dev/evdev/evdev.h> 46*d97d5c0cSVladimir Kondratyev 47*d97d5c0cSVladimir Kondratyev #include <dev/hid/hgame.h> 48*d97d5c0cSVladimir Kondratyev #include <dev/hid/hid.h> 49*d97d5c0cSVladimir Kondratyev #include <dev/hid/hidbus.h> 50*d97d5c0cSVladimir Kondratyev #include <dev/hid/hidmap.h> 51*d97d5c0cSVladimir Kondratyev #include <dev/hid/hidquirk.h> 52*d97d5c0cSVladimir Kondratyev #include <dev/hid/hidrdesc.h> 53*d97d5c0cSVladimir Kondratyev 54*d97d5c0cSVladimir Kondratyev #include <dev/usb/usb.h> 55*d97d5c0cSVladimir Kondratyev #include <dev/usb/usbdi.h> 56*d97d5c0cSVladimir Kondratyev 57*d97d5c0cSVladimir Kondratyev static const uint8_t xb360gp_rdesc[] = {HID_XB360GP_REPORT_DESCR()}; 58*d97d5c0cSVladimir Kondratyev 59*d97d5c0cSVladimir Kondratyev #define XB360GP_MAP_BUT(number, code) \ 60*d97d5c0cSVladimir Kondratyev { HIDMAP_KEY(HUP_BUTTON, number, code) } 61*d97d5c0cSVladimir Kondratyev #define XB360GP_MAP_ABS(usage, code) \ 62*d97d5c0cSVladimir Kondratyev { HIDMAP_ABS(HUP_GENERIC_DESKTOP, HUG_##usage, code) } 63*d97d5c0cSVladimir Kondratyev #define XB360GP_MAP_ABS_FLT(usage, code) \ 64*d97d5c0cSVladimir Kondratyev { HIDMAP_ABS(HUP_GENERIC_DESKTOP, HUG_##usage, code), \ 65*d97d5c0cSVladimir Kondratyev .fuzz = 16, .flat = 128 } 66*d97d5c0cSVladimir Kondratyev #define XB360GP_MAP_ABS_INV(usage, code) \ 67*d97d5c0cSVladimir Kondratyev { HIDMAP_ABS(HUP_GENERIC_DESKTOP, HUG_##usage, code), \ 68*d97d5c0cSVladimir Kondratyev .fuzz = 16, .flat = 128, .invert_value = true } 69*d97d5c0cSVladimir Kondratyev #define XB360GP_MAP_CRG(usage_from, usage_to, callback) \ 70*d97d5c0cSVladimir Kondratyev { HIDMAP_ANY_CB_RANGE(HUP_GENERIC_DESKTOP, \ 71*d97d5c0cSVladimir Kondratyev HUG_##usage_from, HUG_##usage_to, callback) } 72*d97d5c0cSVladimir Kondratyev #define XB360GP_FINALCB(cb) \ 73*d97d5c0cSVladimir Kondratyev { HIDMAP_FINAL_CB(&cb) } 74*d97d5c0cSVladimir Kondratyev 75*d97d5c0cSVladimir Kondratyev /* Customized to match usbhid's XBox 360 descriptor */ 76*d97d5c0cSVladimir Kondratyev static const struct hidmap_item xb360gp_map[] = { 77*d97d5c0cSVladimir Kondratyev XB360GP_MAP_BUT(1, BTN_SOUTH), 78*d97d5c0cSVladimir Kondratyev XB360GP_MAP_BUT(2, BTN_EAST), 79*d97d5c0cSVladimir Kondratyev XB360GP_MAP_BUT(3, BTN_WEST), 80*d97d5c0cSVladimir Kondratyev XB360GP_MAP_BUT(4, BTN_NORTH), 81*d97d5c0cSVladimir Kondratyev XB360GP_MAP_BUT(5, BTN_TL), 82*d97d5c0cSVladimir Kondratyev XB360GP_MAP_BUT(6, BTN_TR), 83*d97d5c0cSVladimir Kondratyev XB360GP_MAP_BUT(7, BTN_SELECT), 84*d97d5c0cSVladimir Kondratyev XB360GP_MAP_BUT(8, BTN_START), 85*d97d5c0cSVladimir Kondratyev XB360GP_MAP_BUT(9, BTN_THUMBL), 86*d97d5c0cSVladimir Kondratyev XB360GP_MAP_BUT(10, BTN_THUMBR), 87*d97d5c0cSVladimir Kondratyev XB360GP_MAP_BUT(11, BTN_MODE), 88*d97d5c0cSVladimir Kondratyev XB360GP_MAP_CRG(D_PAD_UP, D_PAD_LEFT, hgame_dpad_cb), 89*d97d5c0cSVladimir Kondratyev XB360GP_MAP_ABS_FLT(X, ABS_X), 90*d97d5c0cSVladimir Kondratyev XB360GP_MAP_ABS_INV(Y, ABS_Y), 91*d97d5c0cSVladimir Kondratyev XB360GP_MAP_ABS(Z, ABS_Z), 92*d97d5c0cSVladimir Kondratyev XB360GP_MAP_ABS_FLT(RX, ABS_RX), 93*d97d5c0cSVladimir Kondratyev XB360GP_MAP_ABS_INV(RY, ABS_RY), 94*d97d5c0cSVladimir Kondratyev XB360GP_MAP_ABS(RZ, ABS_RZ), 95*d97d5c0cSVladimir Kondratyev XB360GP_FINALCB( hgame_final_cb), 96*d97d5c0cSVladimir Kondratyev }; 97*d97d5c0cSVladimir Kondratyev 98*d97d5c0cSVladimir Kondratyev static const STRUCT_USB_HOST_ID xb360gp_devs[] = { 99*d97d5c0cSVladimir Kondratyev /* the Xbox 360 gamepad doesn't use the HID class */ 100*d97d5c0cSVladimir Kondratyev {USB_IFACE_CLASS(UICLASS_VENDOR), 101*d97d5c0cSVladimir Kondratyev USB_IFACE_SUBCLASS(UISUBCLASS_XBOX360_CONTROLLER), 102*d97d5c0cSVladimir Kondratyev USB_IFACE_PROTOCOL(UIPROTO_XBOX360_GAMEPAD),}, 103*d97d5c0cSVladimir Kondratyev }; 104*d97d5c0cSVladimir Kondratyev 105*d97d5c0cSVladimir Kondratyev static void 106*d97d5c0cSVladimir Kondratyev xb360gp_identify(driver_t *driver, device_t parent) 107*d97d5c0cSVladimir Kondratyev { 108*d97d5c0cSVladimir Kondratyev const struct hid_device_info *hw = hid_get_device_info(parent); 109*d97d5c0cSVladimir Kondratyev 110*d97d5c0cSVladimir Kondratyev /* the Xbox 360 gamepad has no report descriptor */ 111*d97d5c0cSVladimir Kondratyev if (hid_test_quirk(hw, HQ_IS_XBOX360GP)) 112*d97d5c0cSVladimir Kondratyev hid_set_report_descr(parent, xb360gp_rdesc, 113*d97d5c0cSVladimir Kondratyev sizeof(xb360gp_rdesc)); 114*d97d5c0cSVladimir Kondratyev } 115*d97d5c0cSVladimir Kondratyev 116*d97d5c0cSVladimir Kondratyev static int 117*d97d5c0cSVladimir Kondratyev xb360gp_probe(device_t dev) 118*d97d5c0cSVladimir Kondratyev { 119*d97d5c0cSVladimir Kondratyev struct hgame_softc *sc = device_get_softc(dev); 120*d97d5c0cSVladimir Kondratyev const struct hid_device_info *hw = hid_get_device_info(dev); 121*d97d5c0cSVladimir Kondratyev int error; 122*d97d5c0cSVladimir Kondratyev 123*d97d5c0cSVladimir Kondratyev if (!hid_test_quirk(hw, HQ_IS_XBOX360GP)) 124*d97d5c0cSVladimir Kondratyev return (ENXIO); 125*d97d5c0cSVladimir Kondratyev 126*d97d5c0cSVladimir Kondratyev hidmap_set_dev(&sc->hm, dev); 127*d97d5c0cSVladimir Kondratyev 128*d97d5c0cSVladimir Kondratyev error = HIDMAP_ADD_MAP(&sc->hm, xb360gp_map, NULL); 129*d97d5c0cSVladimir Kondratyev if (error != 0) 130*d97d5c0cSVladimir Kondratyev return (error); 131*d97d5c0cSVladimir Kondratyev 132*d97d5c0cSVladimir Kondratyev device_set_desc(dev, "XBox 360 Gamepad"); 133*d97d5c0cSVladimir Kondratyev 134*d97d5c0cSVladimir Kondratyev return (BUS_PROBE_DEFAULT); 135*d97d5c0cSVladimir Kondratyev } 136*d97d5c0cSVladimir Kondratyev 137*d97d5c0cSVladimir Kondratyev static int 138*d97d5c0cSVladimir Kondratyev xb360gp_attach(device_t dev) 139*d97d5c0cSVladimir Kondratyev { 140*d97d5c0cSVladimir Kondratyev struct hgame_softc *sc = device_get_softc(dev); 141*d97d5c0cSVladimir Kondratyev int error; 142*d97d5c0cSVladimir Kondratyev 143*d97d5c0cSVladimir Kondratyev /* 144*d97d5c0cSVladimir Kondratyev * Turn off the four LEDs on the gamepad which 145*d97d5c0cSVladimir Kondratyev * are blinking by default: 146*d97d5c0cSVladimir Kondratyev */ 147*d97d5c0cSVladimir Kondratyev static const uint8_t reportbuf[3] = {1, 3, 0}; 148*d97d5c0cSVladimir Kondratyev error = hid_set_report(dev, reportbuf, sizeof(reportbuf), 149*d97d5c0cSVladimir Kondratyev HID_OUTPUT_REPORT, 0); 150*d97d5c0cSVladimir Kondratyev if (error) 151*d97d5c0cSVladimir Kondratyev device_printf(dev, "set output report failed, error=%d " 152*d97d5c0cSVladimir Kondratyev "(ignored)\n", error); 153*d97d5c0cSVladimir Kondratyev 154*d97d5c0cSVladimir Kondratyev return (hidmap_attach(&sc->hm)); 155*d97d5c0cSVladimir Kondratyev } 156*d97d5c0cSVladimir Kondratyev 157*d97d5c0cSVladimir Kondratyev static int 158*d97d5c0cSVladimir Kondratyev xb360gp_detach(device_t dev) 159*d97d5c0cSVladimir Kondratyev { 160*d97d5c0cSVladimir Kondratyev struct hgame_softc *sc = device_get_softc(dev); 161*d97d5c0cSVladimir Kondratyev 162*d97d5c0cSVladimir Kondratyev return (hidmap_detach(&sc->hm)); 163*d97d5c0cSVladimir Kondratyev } 164*d97d5c0cSVladimir Kondratyev 165*d97d5c0cSVladimir Kondratyev static devclass_t xb360gp_devclass; 166*d97d5c0cSVladimir Kondratyev static device_method_t xb360gp_methods[] = { 167*d97d5c0cSVladimir Kondratyev DEVMETHOD(device_identify, xb360gp_identify), 168*d97d5c0cSVladimir Kondratyev DEVMETHOD(device_probe, xb360gp_probe), 169*d97d5c0cSVladimir Kondratyev DEVMETHOD(device_attach, xb360gp_attach), 170*d97d5c0cSVladimir Kondratyev DEVMETHOD(device_detach, xb360gp_detach), 171*d97d5c0cSVladimir Kondratyev DEVMETHOD_END 172*d97d5c0cSVladimir Kondratyev }; 173*d97d5c0cSVladimir Kondratyev 174*d97d5c0cSVladimir Kondratyev DEFINE_CLASS_0(xb360gp, xb360gp_driver, xb360gp_methods, 175*d97d5c0cSVladimir Kondratyev sizeof(struct hgame_softc)); 176*d97d5c0cSVladimir Kondratyev DRIVER_MODULE(xb360gp, hidbus, xb360gp_driver, xb360gp_devclass, NULL, 0); 177*d97d5c0cSVladimir Kondratyev MODULE_DEPEND(xb360gp, hid, 1, 1, 1); 178*d97d5c0cSVladimir Kondratyev MODULE_DEPEND(xb360gp, hidbus, 1, 1, 1); 179*d97d5c0cSVladimir Kondratyev MODULE_DEPEND(xb360gp, hidmap, 1, 1, 1); 180*d97d5c0cSVladimir Kondratyev MODULE_DEPEND(xb360gp, hgame, 1, 1, 1); 181*d97d5c0cSVladimir Kondratyev MODULE_DEPEND(xb360gp, evdev, 1, 1, 1); 182*d97d5c0cSVladimir Kondratyev MODULE_VERSION(xb360gp, 1); 183*d97d5c0cSVladimir Kondratyev USB_PNP_HOST_INFO(xb360gp_devs); 184