1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 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 /* 31 * Generic HID game controller (joystick/gamepad) driver, 32 */ 33 34 #include <sys/param.h> 35 #include <sys/bus.h> 36 #include <sys/kernel.h> 37 #include <sys/module.h> 38 #include <sys/sysctl.h> 39 40 #include <dev/evdev/input.h> 41 #include <dev/evdev/evdev.h> 42 43 #include <dev/hid/hgame.h> 44 #include <dev/hid/hid.h> 45 #include <dev/hid/hidbus.h> 46 #include <dev/hid/hidquirk.h> 47 #include <dev/hid/hidmap.h> 48 49 #define HGAME_MAP_BRG(number_from, number_to, code) \ 50 { HIDMAP_KEY_RANGE(HUP_BUTTON, number_from, number_to, code) } 51 #define HGAME_MAP_ABS(usage, code) \ 52 { HIDMAP_ABS(HUP_GENERIC_DESKTOP, HUG_##usage, code) } 53 #define HGAME_MAP_GCB(usage, callback) \ 54 { HIDMAP_ANY_CB(HUP_GENERIC_DESKTOP, HUG_##usage, callback) } 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_GCB(HAT_SWITCH, hgame_hat_switch_cb), 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 int 83 hgame_hat_switch_cb(HIDMAP_CB_ARGS) 84 { 85 static const struct { int32_t x; int32_t y; } hat_switch_map[] = { 86 {0, -1}, {1, -1}, {1, 0}, {1, 1}, {0, 1}, {-1, 1}, {-1, 0}, 87 {-1, -1},{0, 0} 88 }; 89 struct evdev_dev *evdev = HIDMAP_CB_GET_EVDEV(); 90 u_int idx; 91 92 switch (HIDMAP_CB_GET_STATE()) { 93 case HIDMAP_CB_IS_ATTACHING: 94 evdev_support_event(evdev, EV_ABS); 95 evdev_support_abs(evdev, ABS_HAT0X, -1, 1, 0, 0, 0); 96 evdev_support_abs(evdev, ABS_HAT0Y, -1, 1, 0, 0, 0); 97 break; 98 99 case HIDMAP_CB_IS_RUNNING: 100 idx = MIN(nitems(hat_switch_map) - 1, (u_int)ctx.data); 101 evdev_push_abs(evdev, ABS_HAT0X, hat_switch_map[idx].x); 102 evdev_push_abs(evdev, ABS_HAT0Y, hat_switch_map[idx].y); 103 break; 104 105 default: 106 break; 107 } 108 109 return (0); 110 } 111 112 /* 113 * Emulate the hat switch report via the D-pad usages 114 * found on XInput/XBox style devices 115 */ 116 int 117 hgame_dpad_cb(HIDMAP_CB_ARGS) 118 { 119 struct hgame_softc *sc = HIDMAP_CB_GET_SOFTC(); 120 struct evdev_dev *evdev = HIDMAP_CB_GET_EVDEV(); 121 int32_t data; 122 123 switch (HIDMAP_CB_GET_STATE()) { 124 case HIDMAP_CB_IS_ATTACHING: 125 HIDMAP_CB_UDATA64 = HID_GET_USAGE(ctx.hi->usage); 126 evdev_support_event(evdev, EV_ABS); 127 evdev_support_abs(evdev, ABS_HAT0X, -1, 1, 0, 0, 0); 128 evdev_support_abs(evdev, ABS_HAT0Y, -1, 1, 0, 0, 0); 129 break; 130 131 case HIDMAP_CB_IS_RUNNING: 132 data = ctx.data; 133 switch (HIDMAP_CB_UDATA64) { 134 case HUG_D_PAD_UP: 135 if (sc->dpad_down) 136 return (ENOMSG); 137 evdev_push_abs(evdev, ABS_HAT0Y, (data == 0) ? 0 : -1); 138 sc->dpad_up = (data != 0); 139 break; 140 case HUG_D_PAD_DOWN: 141 if (sc->dpad_up) 142 return (ENOMSG); 143 evdev_push_abs(evdev, ABS_HAT0Y, (data == 0) ? 0 : 1); 144 sc->dpad_down = (data != 0); 145 break; 146 case HUG_D_PAD_RIGHT: 147 if (sc->dpad_left) 148 return (ENOMSG); 149 evdev_push_abs(evdev, ABS_HAT0X, (data == 0) ? 0 : 1); 150 sc->dpad_right = (data != 0); 151 break; 152 case HUG_D_PAD_LEFT: 153 if (sc->dpad_right) 154 return (ENOMSG); 155 evdev_push_abs(evdev, ABS_HAT0X, (data == 0) ? 0 : -1); 156 sc->dpad_left = (data != 0); 157 break; 158 } 159 break; 160 161 default: 162 break; 163 } 164 165 return (0); 166 } 167 168 int 169 hgame_final_cb(HIDMAP_CB_ARGS) 170 { 171 struct evdev_dev *evdev = HIDMAP_CB_GET_EVDEV(); 172 173 if (HIDMAP_CB_GET_STATE() == HIDMAP_CB_IS_ATTACHING) 174 evdev_support_prop(evdev, INPUT_PROP_DIRECT); 175 176 /* Do not execute callback at interrupt handler and detach */ 177 return (ENOSYS); 178 } 179 180 static int 181 hgame_probe(device_t dev) 182 { 183 const struct hid_device_info *hw = hid_get_device_info(dev); 184 struct hgame_softc *sc = device_get_softc(dev); 185 int error; 186 187 if (hid_test_quirk(hw, HQ_IS_XBOX360GP)) 188 return(ENXIO); 189 190 error = HIDMAP_PROBE(&sc->hm, dev, hgame_devs, hgame_map, NULL); 191 if (error > 0) 192 return (error); 193 194 hidbus_set_desc(dev, hidbus_get_driver_info(dev) == HUG_GAME_PAD ? 195 "Gamepad" : "Joystick"); 196 197 return (BUS_PROBE_GENERIC); 198 } 199 200 201 202 static int 203 hgame_attach(device_t dev) 204 { 205 struct hgame_softc *sc = device_get_softc(dev); 206 207 return (hidmap_attach(&sc->hm)); 208 } 209 210 static int 211 hgame_detach(device_t dev) 212 { 213 struct hgame_softc *sc = device_get_softc(dev); 214 215 return (hidmap_detach(&sc->hm)); 216 } 217 218 static device_method_t hgame_methods[] = { 219 DEVMETHOD(device_probe, hgame_probe), 220 DEVMETHOD(device_attach, hgame_attach), 221 DEVMETHOD(device_detach, hgame_detach), 222 223 DEVMETHOD_END 224 }; 225 226 DEFINE_CLASS_0(hgame, hgame_driver, hgame_methods, sizeof(struct hgame_softc)); 227 DRIVER_MODULE(hgame, hidbus, hgame_driver, NULL, NULL); 228 MODULE_DEPEND(hgame, hid, 1, 1, 1); 229 MODULE_DEPEND(hgame, hidbus, 1, 1, 1); 230 MODULE_DEPEND(hgame, hidmap, 1, 1, 1); 231 MODULE_DEPEND(hgame, evdev, 1, 1, 1); 232 MODULE_VERSION(hgame, 1); 233 HID_PNP_INFO(hgame_devs); 234