1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2020 Vladimir Kondratyev <wulf@FreeBSD.org> 5 * Copyright (c) 2020 Val Packett <val@packett.cool> 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_GCB(usage, callback) \ 56 { HIDMAP_ANY_CB(HUP_GENERIC_DESKTOP, HUG_##usage, callback) } 57 #define HGAME_MAP_CRG(usage_from, usage_to, callback) \ 58 { HIDMAP_ANY_CB_RANGE(HUP_GENERIC_DESKTOP, \ 59 HUG_##usage_from, HUG_##usage_to, callback) } 60 #define HGAME_FINALCB(cb) \ 61 { HIDMAP_FINAL_CB(&cb) } 62 63 static const struct hidmap_item hgame_map[] = { 64 HGAME_MAP_BRG(1, 16, BTN_TRIGGER), 65 HGAME_MAP_ABS(X, ABS_X), 66 HGAME_MAP_ABS(Y, ABS_Y), 67 HGAME_MAP_ABS(Z, ABS_Z), 68 HGAME_MAP_ABS(RX, ABS_RX), 69 HGAME_MAP_ABS(RY, ABS_RY), 70 HGAME_MAP_ABS(RZ, ABS_RZ), 71 HGAME_MAP_GCB(HAT_SWITCH, hgame_hat_switch_cb), 72 HGAME_MAP_CRG(D_PAD_UP, D_PAD_LEFT, hgame_dpad_cb), 73 HGAME_MAP_BRG(17, 57, BTN_TRIGGER_HAPPY), 74 HGAME_FINALCB( hgame_final_cb), 75 }; 76 77 static const struct hid_device_id hgame_devs[] = { 78 { HID_TLC(HUP_GENERIC_DESKTOP, HUG_JOYSTICK), 79 HID_DRIVER_INFO(HUG_JOYSTICK) }, 80 { HID_TLC(HUP_GENERIC_DESKTOP, HUG_GAME_PAD), 81 HID_DRIVER_INFO(HUG_GAME_PAD) }, 82 }; 83 84 int 85 hgame_hat_switch_cb(HIDMAP_CB_ARGS) 86 { 87 static const struct { int32_t x; int32_t y; } hat_switch_map[] = { 88 {0, -1}, {1, -1}, {1, 0}, {1, 1}, {0, 1}, {-1, 1}, {-1, 0}, 89 {-1, -1},{0, 0} 90 }; 91 struct evdev_dev *evdev = HIDMAP_CB_GET_EVDEV(); 92 u_int idx; 93 94 switch (HIDMAP_CB_GET_STATE()) { 95 case HIDMAP_CB_IS_ATTACHING: 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 idx = MIN(nitems(hat_switch_map) - 1, (u_int)ctx.data); 103 evdev_push_abs(evdev, ABS_HAT0X, hat_switch_map[idx].x); 104 evdev_push_abs(evdev, ABS_HAT0Y, hat_switch_map[idx].y); 105 break; 106 107 default: 108 break; 109 } 110 111 return (0); 112 } 113 114 /* 115 * Emulate the hat switch report via the D-pad usages 116 * found on XInput/XBox style devices 117 */ 118 int 119 hgame_dpad_cb(HIDMAP_CB_ARGS) 120 { 121 struct hgame_softc *sc = HIDMAP_CB_GET_SOFTC(); 122 struct evdev_dev *evdev = HIDMAP_CB_GET_EVDEV(); 123 int32_t data; 124 125 switch (HIDMAP_CB_GET_STATE()) { 126 case HIDMAP_CB_IS_ATTACHING: 127 HIDMAP_CB_UDATA64 = HID_GET_USAGE(ctx.hi->usage); 128 evdev_support_event(evdev, EV_ABS); 129 evdev_support_abs(evdev, ABS_HAT0X, -1, 1, 0, 0, 0); 130 evdev_support_abs(evdev, ABS_HAT0Y, -1, 1, 0, 0, 0); 131 break; 132 133 case HIDMAP_CB_IS_RUNNING: 134 data = ctx.data; 135 switch (HIDMAP_CB_UDATA64) { 136 case HUG_D_PAD_UP: 137 if (sc->dpad_down) 138 return (ENOMSG); 139 evdev_push_abs(evdev, ABS_HAT0Y, (data == 0) ? 0 : -1); 140 sc->dpad_up = (data != 0); 141 break; 142 case HUG_D_PAD_DOWN: 143 if (sc->dpad_up) 144 return (ENOMSG); 145 evdev_push_abs(evdev, ABS_HAT0Y, (data == 0) ? 0 : 1); 146 sc->dpad_down = (data != 0); 147 break; 148 case HUG_D_PAD_RIGHT: 149 if (sc->dpad_left) 150 return (ENOMSG); 151 evdev_push_abs(evdev, ABS_HAT0X, (data == 0) ? 0 : 1); 152 sc->dpad_right = (data != 0); 153 break; 154 case HUG_D_PAD_LEFT: 155 if (sc->dpad_right) 156 return (ENOMSG); 157 evdev_push_abs(evdev, ABS_HAT0X, (data == 0) ? 0 : -1); 158 sc->dpad_left = (data != 0); 159 break; 160 } 161 break; 162 163 default: 164 break; 165 } 166 167 return (0); 168 } 169 170 int 171 hgame_final_cb(HIDMAP_CB_ARGS) 172 { 173 struct evdev_dev *evdev = HIDMAP_CB_GET_EVDEV(); 174 175 if (HIDMAP_CB_GET_STATE() == HIDMAP_CB_IS_ATTACHING) 176 evdev_support_prop(evdev, INPUT_PROP_DIRECT); 177 178 /* Do not execute callback at interrupt handler and detach */ 179 return (ENOSYS); 180 } 181 182 static int 183 hgame_probe(device_t dev) 184 { 185 const struct hid_device_info *hw = hid_get_device_info(dev); 186 struct hgame_softc *sc = device_get_softc(dev); 187 int error; 188 189 if (hid_test_quirk(hw, HQ_IS_XBOX360GP)) 190 return(ENXIO); 191 192 error = HIDMAP_PROBE(&sc->hm, dev, hgame_devs, hgame_map, NULL); 193 if (error > 0) 194 return (error); 195 196 hidbus_set_desc(dev, hidbus_get_driver_info(dev) == HUG_GAME_PAD ? 197 "Gamepad" : "Joystick"); 198 199 return (BUS_PROBE_GENERIC); 200 } 201 202 203 204 static int 205 hgame_attach(device_t dev) 206 { 207 struct hgame_softc *sc = device_get_softc(dev); 208 209 return (hidmap_attach(&sc->hm)); 210 } 211 212 static int 213 hgame_detach(device_t dev) 214 { 215 struct hgame_softc *sc = device_get_softc(dev); 216 217 return (hidmap_detach(&sc->hm)); 218 } 219 220 static device_method_t hgame_methods[] = { 221 DEVMETHOD(device_probe, hgame_probe), 222 DEVMETHOD(device_attach, hgame_attach), 223 DEVMETHOD(device_detach, hgame_detach), 224 225 DEVMETHOD_END 226 }; 227 228 DEFINE_CLASS_0(hgame, hgame_driver, hgame_methods, sizeof(struct hgame_softc)); 229 DRIVER_MODULE(hgame, hidbus, hgame_driver, NULL, NULL); 230 MODULE_DEPEND(hgame, hid, 1, 1, 1); 231 MODULE_DEPEND(hgame, hidbus, 1, 1, 1); 232 MODULE_DEPEND(hgame, hidmap, 1, 1, 1); 233 MODULE_DEPEND(hgame, evdev, 1, 1, 1); 234 MODULE_VERSION(hgame, 1); 235 HID_PNP_INFO(hgame_devs); 236