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