1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2020 Vladimir Kondratyev <wulf@FreeBSD.org> 5 * Copyright (c) 2020 Greg V <greg@unrelenting.technology> 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 __FBSDID("$FreeBSD$"); 31 32 /* 33 * Generic HID game controller (joystick/gamepad) driver, 34 */ 35 36 #include <sys/param.h> 37 #include <sys/bus.h> 38 #include <sys/kernel.h> 39 #include <sys/module.h> 40 #include <sys/sysctl.h> 41 42 #include <dev/evdev/input.h> 43 #include <dev/evdev/evdev.h> 44 45 #include <dev/hid/hgame.h> 46 #include <dev/hid/hid.h> 47 #include <dev/hid/hidbus.h> 48 #include <dev/hid/hidquirk.h> 49 #include <dev/hid/hidmap.h> 50 51 #define HGAME_MAP_BRG(number_from, number_to, code) \ 52 { HIDMAP_KEY_RANGE(HUP_BUTTON, number_from, number_to, code) } 53 #define HGAME_MAP_ABS(usage, code) \ 54 { HIDMAP_ABS(HUP_GENERIC_DESKTOP, HUG_##usage, code) } 55 #define HGAME_MAP_CRG(usage_from, usage_to, callback) \ 56 { HIDMAP_ANY_CB_RANGE(HUP_GENERIC_DESKTOP, \ 57 HUG_##usage_from, HUG_##usage_to, callback) } 58 #define HGAME_FINALCB(cb) \ 59 { HIDMAP_FINAL_CB(&cb) } 60 61 static const struct hidmap_item hgame_map[] = { 62 HGAME_MAP_BRG(1, 16, BTN_TRIGGER), 63 HGAME_MAP_ABS(X, ABS_X), 64 HGAME_MAP_ABS(Y, ABS_Y), 65 HGAME_MAP_ABS(Z, ABS_Z), 66 HGAME_MAP_ABS(RX, ABS_RX), 67 HGAME_MAP_ABS(RY, ABS_RY), 68 HGAME_MAP_ABS(RZ, ABS_RZ), 69 HGAME_MAP_ABS(HAT_SWITCH, ABS_HAT0X), 70 HGAME_MAP_CRG(D_PAD_UP, D_PAD_LEFT, hgame_dpad_cb), 71 HGAME_MAP_BRG(17, 57, BTN_TRIGGER_HAPPY), 72 HGAME_FINALCB( hgame_final_cb), 73 }; 74 75 static const struct hid_device_id hgame_devs[] = { 76 { HID_TLC(HUP_GENERIC_DESKTOP, HUG_JOYSTICK), 77 HID_DRIVER_INFO(HUG_JOYSTICK) }, 78 { HID_TLC(HUP_GENERIC_DESKTOP, HUG_GAME_PAD), 79 HID_DRIVER_INFO(HUG_GAME_PAD) }, 80 }; 81 82 /* 83 * Emulate the hat switch report via the D-pad usages 84 * found on XInput/XBox style devices 85 */ 86 int 87 hgame_dpad_cb(HIDMAP_CB_ARGS) 88 { 89 struct hgame_softc *sc = HIDMAP_CB_GET_SOFTC(); 90 struct evdev_dev *evdev = HIDMAP_CB_GET_EVDEV(); 91 int32_t data; 92 93 switch (HIDMAP_CB_GET_STATE()) { 94 case HIDMAP_CB_IS_ATTACHING: 95 HIDMAP_CB_UDATA64 = HID_GET_USAGE(ctx.hi->usage); 96 evdev_support_event(evdev, EV_ABS); 97 evdev_support_abs(evdev, ABS_HAT0X, -1, 1, 0, 0, 0); 98 evdev_support_abs(evdev, ABS_HAT0Y, -1, 1, 0, 0, 0); 99 break; 100 101 case HIDMAP_CB_IS_RUNNING: 102 data = ctx.data; 103 switch (HIDMAP_CB_UDATA64) { 104 case HUG_D_PAD_UP: 105 if (sc->dpad_down) 106 return (ENOMSG); 107 evdev_push_abs(evdev, ABS_HAT0Y, (data == 0) ? 0 : -1); 108 sc->dpad_up = (data != 0); 109 break; 110 case HUG_D_PAD_DOWN: 111 if (sc->dpad_up) 112 return (ENOMSG); 113 evdev_push_abs(evdev, ABS_HAT0Y, (data == 0) ? 0 : 1); 114 sc->dpad_down = (data != 0); 115 break; 116 case HUG_D_PAD_RIGHT: 117 if (sc->dpad_left) 118 return (ENOMSG); 119 evdev_push_abs(evdev, ABS_HAT0X, (data == 0) ? 0 : 1); 120 sc->dpad_right = (data != 0); 121 break; 122 case HUG_D_PAD_LEFT: 123 if (sc->dpad_right) 124 return (ENOMSG); 125 evdev_push_abs(evdev, ABS_HAT0X, (data == 0) ? 0 : -1); 126 sc->dpad_left = (data != 0); 127 break; 128 } 129 break; 130 131 default: 132 break; 133 } 134 135 return (0); 136 } 137 138 int 139 hgame_final_cb(HIDMAP_CB_ARGS) 140 { 141 struct evdev_dev *evdev = HIDMAP_CB_GET_EVDEV(); 142 143 if (HIDMAP_CB_GET_STATE() == HIDMAP_CB_IS_ATTACHING) 144 evdev_support_prop(evdev, INPUT_PROP_DIRECT); 145 146 /* Do not execute callback at interrupt handler and detach */ 147 return (ENOSYS); 148 } 149 150 static int 151 hgame_probe(device_t dev) 152 { 153 const struct hid_device_info *hw = hid_get_device_info(dev); 154 struct hgame_softc *sc = device_get_softc(dev); 155 int error; 156 157 if (hid_test_quirk(hw, HQ_IS_XBOX360GP)) 158 return(ENXIO); 159 160 error = HIDMAP_PROBE(&sc->hm, dev, hgame_devs, hgame_map, NULL); 161 if (error > 0) 162 return (error); 163 164 hidbus_set_desc(dev, hidbus_get_driver_info(dev) == HUG_GAME_PAD ? 165 "Gamepad" : "Joystick"); 166 167 return (BUS_PROBE_GENERIC); 168 } 169 170 171 172 static int 173 hgame_attach(device_t dev) 174 { 175 struct hgame_softc *sc = device_get_softc(dev); 176 177 return (hidmap_attach(&sc->hm)); 178 } 179 180 static int 181 hgame_detach(device_t dev) 182 { 183 struct hgame_softc *sc = device_get_softc(dev); 184 185 return (hidmap_detach(&sc->hm)); 186 } 187 188 static devclass_t hgame_devclass; 189 static device_method_t hgame_methods[] = { 190 DEVMETHOD(device_probe, hgame_probe), 191 DEVMETHOD(device_attach, hgame_attach), 192 DEVMETHOD(device_detach, hgame_detach), 193 194 DEVMETHOD_END 195 }; 196 197 DEFINE_CLASS_0(hgame, hgame_driver, hgame_methods, sizeof(struct hgame_softc)); 198 DRIVER_MODULE(hgame, hidbus, hgame_driver, hgame_devclass, NULL, 0); 199 MODULE_DEPEND(hgame, hid, 1, 1, 1); 200 MODULE_DEPEND(hgame, hidbus, 1, 1, 1); 201 MODULE_DEPEND(hgame, hidmap, 1, 1, 1); 202 MODULE_DEPEND(hgame, evdev, 1, 1, 1); 203 MODULE_VERSION(hgame, 1); 204 HID_PNP_INFO(hgame_devs); 205