├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── hconf.4 ├── hconf.c ├── hconf.h ├── hcons.4 ├── hcons.c ├── hetp.c ├── hgame.4 ├── hgame.c ├── hgame.h ├── hid.c ├── hid.h ├── hid_debug.c ├── hid_debug.h ├── hid_if.m ├── hidbus.4 ├── hidbus.c ├── hidbus.h ├── hidmap.c ├── hidmap.h ├── hidquirk.4 ├── hidquirk.c ├── hidquirk.h ├── hidraw.4 ├── hidraw.c ├── hidraw.h ├── hkbd.4 ├── hkbd.c ├── hms.4 ├── hms.c ├── hmt.4 ├── hmt.c ├── hpen.4 ├── hpen.c ├── hsctrl.4 ├── hsctrl.c ├── hskbd.c ├── iichid.4 ├── iichid.c ├── ps4dshock.4 ├── ps4dshock.c ├── strcasestr.c ├── strcasestr.h ├── usbhid.4 ├── usbhid.c ├── xb360gp.4 └── xb360gp.c /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.ko 3 | *~ 4 | *.patch 5 | *.rej 6 | *.orig 7 | *_if.h 8 | opt_*.h 9 | vnode_if_*.h 10 | export_syms 11 | machine 12 | x86 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 1998-2004 The NetBSD Foundation, Inc. 2 | Copyright (c) 2008 Hans Petter Selasky. 3 | Copyright (c) 2018-2019 Marc Priggemeyer 4 | Copyright (c) 2019 Greg V 5 | Copyright (c) 2019-2020 Vladimir Kondratyev 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 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # $FreeBSD$ 2 | 3 | #.PATH: ${SRCTOP}/sys/dev/iicbus/input 4 | 5 | .if !defined(OSVERSION) 6 | # Search for kernel source tree in standard places. 7 | .if empty(KERNBUILDDIR) 8 | .if !defined(SYSDIR) 9 | .for _dir in ${SRCTOP:D${SRCTOP}/sys} /sys /usr/src/sys 10 | .if !defined(SYSDIR) && exists(${_dir}/kern/) && exists(${_dir}/conf/kmod.mk) 11 | SYSDIR= ${_dir:tA} 12 | .endif 13 | .endfor 14 | .endif 15 | .if !defined(SYSDIR) || !exists(${SYSDIR}/kern/) || \ 16 | !exists(${SYSDIR}/conf/kmod.mk) 17 | .error Unable to locate the kernel source tree. Set SYSDIR to override. 18 | .endif 19 | .endif 20 | OSVERSION!= awk '/^\#define[[:space:]]*__FreeBSD_version/ { print $$3 }' \ 21 | ${SYSDIR}/sys/param.h 22 | .endif 23 | 24 | KMOD = iichid 25 | .if !defined(DISABLE_IICHID) 26 | SRCS = iichid.c 27 | .endif 28 | SRCS += hconf.c hconf.h hgame.c hgame.h hms.c hmt.c hpen.c hsctrl.c hcons.c 29 | SRCS += hetp.c 30 | SRCS += xb360gp.c ps4dshock.c 31 | SRCS += hidbus.c hidbus.h hid_if.c hid_if.h hid.c hid.h 32 | SRCS += hid_debug.h hid_debug.c 33 | SRCS += hidmap.h hidmap.c 34 | SRCS += usbdevs.h 35 | SRCS += usbhid.c 36 | SRCS += hidraw.c hidraw.h 37 | SRCS += hidquirk.h hidquirk.c 38 | SRCS += acpi_if.h bus_if.h device_if.h iicbus_if.h 39 | SRCS += opt_acpi.h opt_usb.h opt_evdev.h 40 | SRCS += strcasestr.c strcasestr.h 41 | MAN += hidbus.4 hidquirk.4 hidraw.4 iichid.4 usbhid.4 42 | MAN += hconf.4 hcons.4 hgame.4 hkbd.4 hms.4 hmt.4 hpen.4 hsctrl.4 43 | MAN += ps4dshock.4 xb360gp.4 44 | # Revert 5d3a4a2 for compiling hkbd on pre 1300068 systems 45 | .if ${OSVERSION} >= 1300068 || \ 46 | (${OSVERSION} >= 1201507 && ${OSVERSION} < 1300000) 47 | SRCS += opt_kbd.h opt_hkbd.h hkbd.c 48 | CFLAGS += -DENABLE_HKBD 49 | .endif 50 | # hskbd conflicts with hkbd 51 | #SRCS += hskbd.c 52 | #CFLAGS += -DINVARIANTS -DINVARIANT_SUPPORT 53 | CFLAGS += -DHID_DEBUG 54 | CFLAGS += -DIICHID_DEBUG 55 | CFLAGS += -DEVDEV_SUPPORT 56 | .if defined(DISABLE_USBHID) 57 | CFLAGS += -DDISABLE_USBHID 58 | .endif 59 | #CFLAGS += -DHAVE_ACPI_EVALUATEDSMTYPED 60 | .if ${OSVERSION} >= 1300083 || \ 61 | (${OSVERSION} >= 1201513 && ${OSVERSION} < 1300000) 62 | CFLAGS += -DHAVE_ACPI_IICBUS 63 | .endif 64 | .if ${OSVERSION} >= 1300055 || \ 65 | (${OSVERSION} >= 1201507 && ${OSVERSION} < 1300000) 66 | CFLAGS += -DHAVE_IG4_POLLING 67 | .endif 68 | 69 | .include 70 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # iichid - Generic HID layer for FreeBSD 2 | 3 | iichid is a set of abstract HID drivers for FreeBSD. All the drivers use evdev 4 | protocol to communicate with userland applications like libinput and 5 | xf86-input-evdev. hidbus is used to connect HID drivers with transport 6 | backends (usbhid for USB and iichid for I2C) and allows multiple HID drivers 7 | to attach to a single physical device. 8 | 9 | Current drivers and respective HID devices supported by them are following: 10 | 11 | * **hms** - HID mouses. Physical relative devices with roller or optical sensor 12 | are supported as well as virtual absolute devices like VBox, Bhyve and VMWare 13 | mices. 14 | * **hmt** - MS Windows 7+ compatible Multi-touch devices like touchscreens 15 | and precission touchpads. 16 | * **hconf** - Mouse/touchpad mode switches on precission touchpads. 17 | * **hkbd** - HID keyboards. AT-key subset. 18 | * **hskbd** - Simple evdev-only HID keyboard driver. 19 | * **hpen** - Generic / MS Windows compatible HID pen tablets. 20 | * **hcons** - Consumer page AKA Multimedia keys. 21 | * **hsctrl** - System Controls page (Power/Sleep keys). 22 | * **hgame** - Game controllers including Xbox360-compatible and joysticks. 23 | * **hidraw** - Export raw HID data in uhid(4) and Linux hidraw-compatible way. 24 | 25 | ## System requirements 26 | 27 | FreeBSD 12.1+. Recent (any from 2020) CURRENT or 12-STABLE are preferred. 28 | 29 | ## Downloading 30 | 31 | This project does not have a special home page. The source code and the 32 | issue tracker are hosted on the Github: 33 | 34 | https://github.com/wulf7/iichid 35 | 36 | ## Building 37 | 38 | To build driver, cd in to extracted archive directory and type 39 | 40 | ``` 41 | $ make 42 | ``` 43 | 44 | You need the sources of the running operating system under **/usr/src** 45 | 46 | ## Installing 47 | 48 | To install file already built just type: 49 | 50 | ``` 51 | $ sudo make install 52 | ``` 53 | 54 | and you will get the compiled module installed in **/boot/modules** 55 | 56 | To load the module at a boot time, add both I2C driver module (it is usually 57 | an **ig4**) and HID module **iichid** to **kld_list** variable in 58 | **/etc/rc.conf**. 59 | 60 | To handle keyboards in single user mode and at the early stages 61 | of booting process, iichid should be loaded at kernel startup. 62 | Add following lines to **/boot/loader.conf** in that case: 63 | 64 | ``` 65 | ig4_load="YES" 66 | iicbus_load="YES" 67 | iichid_load="YES" 68 | ``` 69 | 70 | ## Configure Xorg 71 | 72 | Generally speaking, it is a bad idea to use static Xorg configuration for 73 | evdev devices due to dynamic unit number assignment. The simplest way 74 | to get new devices recognized by Xorg-server is to rebuild it with DEVD 75 | (since 1.20.7) or UDEV autoconfiguration backend enabled. 76 | 77 | Static configuration is still possible. Add following snippet to a file under 78 | **/usr/local/etc/X11/xorg.conf.d/** 79 | 80 | ``` 81 | Section "InputDevice" 82 | Identifier "HID device" 83 | Driver "libinput" 84 | Option "Device" "/dev/input/event6" 85 | Option "AutoServerLayout" "true" 86 | EndSection 87 | ``` 88 | 89 | You may need to run **sudo libinput list-devices** to find out unit number 90 | belonging to given device. Note that it can change across reboots. 91 | 92 | ## usbhid 93 | 94 | USB transport backend can (and usually does) interfere with OS built-in USB 95 | HID drivers like **uhid**, **ukbd**, **ums** and **wmt**. Which one will be 96 | active is depend on the order of loading, so genarally you should load 97 | **iichid.ko** from bootloader. In the case if the module is loaded 98 | with **kldload** it is necessary to issue 99 | 100 | ``` 101 | $ sudo usbconfig -d ugenX.X reset 102 | ``` 103 | 104 | command to force ugenX.X device reprobe at operating system run time. ugenX.X 105 | to reprobe can be found with issuing of simple **usbconfig** command: 106 | 107 | ``` 108 | $ sudo usbconfig 109 | ``` 110 | 111 | It is possible to build **iichid** with USB support disabled with following 112 | command: 113 | 114 | ``` 115 | $ make -DDISABLE_USBHID 116 | ``` 117 | 118 | ## I2C transport backend sampling (polling) mode 119 | 120 | Currently **iichid** is unable to utilize GPIO interrupts on i386 and amd64 121 | platforms due to absence of INTRNG support in PIC drivers and can not use any 122 | interrupts on 12.1-RELEASE due to inability of **ig4** driver to work in 123 | ithread context in 12.1. In this case it fallbacks to so called sampling 124 | mode with periodic polling of hardware by driver. It is possible to check 125 | mode with following command: 126 | ``` 127 | $ sysctl dev.iichid.0.sampling_rate_slow 128 | ``` 129 | Any positive number returned means that sampling mode is enabled. 130 | 131 | Unfortunatelly, using of sampling mode leads to lose of some data and 132 | often results in glitches. Most known are "drift of inactive mouse pointer" 133 | and "stuck of single finger touch". **iichid** has some internal hacks to 134 | workaround them but they are not reliable. 135 | 136 | ## Choosing of optimal sampling rate for I2C transport. 137 | 138 | Sampling rate is set to 60Hz by default. Although mostly it just works, in 139 | some cases e.g. if device internal scan rate lower than polling frequence 140 | it results in reading of empty reports, doubling of them or other unwanted 141 | effects. 142 | 143 | Typically driver polling frequency should be set to about 0.9 of device 144 | internal scan rate. The simplest way to measure it is to run a Linux live 145 | distro from USB flash than execute evemu-record utility and choose your I2C 146 | device. Than you should convert inter-report interval duration to Hz and 147 | set 0.9 of that value as sampling_rate_fast parameter with following command 148 | ``` 149 | $ sudo sysctl dev.iichid.0.sampling_rate_fast= 150 | ``` 151 | 152 | If your device is supported by **hmt** driver, scan rate can be obtained by 153 | analyze of hardware timestamps. To enable them HQ_MT_TIMESTAMP quirk should 154 | be set for HID device. At first get vendor and product IDs with following 155 | command: 156 | ``` 157 | $ devinfo -rv | grep 'hmt.* bus=0x18' 158 | ``` 159 | It will return something like: 160 | hmt0 pnpinfo page=0x000d usage=0x0004 bus=0x18 vendor=0x06cb product=0x1941 ... 161 | 162 | Then add following line to **/boot/loader.conf** replacing 0x6cb and 0x1941 163 | with vendor and product values from previous command output. 164 | ``` 165 | hw.hid.quirk.0="0x18 **0x6cb** **0x1941** 0 0xffff HQ_MT_TIMESTAMP" 166 | ``` 167 | That will enable output of hardware timestamps in hmt driver after reboot. 168 | Reboot than attach evemu-record to proper node and touch device surface. 169 | You willl see something like this on stdout: 170 | ``` 171 | E: 33.569577 0004 0005 1152000 # EV_MSC / MSC_TIMESTAMP 1152000 172 | E: 33.569577 0000 0000 0001 # ------------ SYN_REPORT (1) ---------- +18ms 173 | E: 33.587771 0004 0005 1161000 # EV_MSC / MSC_TIMESTAMP 1161000 174 | E: 33.587771 0000 0000 0001 # ------------ SYN_REPORT (1) ---------- +18ms 175 | E: 33.605326 0004 0005 1170000 # EV_MSC / MSC_TIMESTAMP 1170000 176 | E: 33.605326 0000 0000 0001 # ------------ SYN_REPORT (1) ---------- +18ms 177 | ``` 178 | Scan rate can be calculated with simple formula: 179 | ``` 180 | scan_rate = 1000000 / (MSC_TIMESTAMP(X) - MSC_TIMESTAMP(X-1)) 181 | ``` 182 | where MSC_TIMESTAMP(X) and MSC_TIMESTAMP(X-1) are values taken from two 183 | consecutive events. 184 | In aforementioned example, optimal polling frequency is expected to be 185 | ``` 186 | sampling_rate_fast = 0.9 × 1000000 ÷ (1161000 − 1152000) = 100 (Hz) 187 | ``` 188 | 189 | It is possible to use double of scan_rate as sampling rate with increasing 190 | of dev.iichid.0.sampling_hysteresis to 3 or 4 to filter out missing samples. 191 | Tuning of this value requires enabling of debug output in I2C transport 192 | and lies out of scope of this README. Generally, sampling_hysteresis should 193 | be left as 1 if sampling frequency lower or equal 0.9 of device scan rate. 194 | 195 | ## Bug reporting 196 | 197 | You can report bugs at 'Project Issues' page 198 | https://github.com/wulf7/iichid/issues 199 | It is recommended to enclose console output of 'kldload iichid.ko' command 200 | with your report. 201 | 202 | Some additional information that can be helpful especially if device has not 203 | been detected at all can be obtained with following commands: 204 | 205 | ``` 206 | $ devinfo -rv # Very verbose 207 | $ pciconf -lv 208 | $ sudo usbconfig 209 | $ dmesg 210 | ``` 211 | -------------------------------------------------------------------------------- /hconf.4: -------------------------------------------------------------------------------- 1 | .\" Copyright (c) 2020 Vladimir Kondratyev 2 | .\" 3 | .\" Redistribution and use in source and binary forms, with or without 4 | .\" modification, are permitted provided that the following conditions 5 | .\" are met: 6 | .\" 1. Redistributions of source code must retain the above copyright 7 | .\" notice, this list of conditions and the following disclaimer. 8 | .\" 2. Redistributions in binary form must reproduce the above copyright 9 | .\" notice, this list of conditions and the following disclaimer in the 10 | .\" documentation and/or other materials provided with the distribution. 11 | .\" 12 | .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 13 | .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 14 | .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 15 | .\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 16 | .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 17 | .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 18 | .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 19 | .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 20 | .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 21 | .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 22 | .\" SUCH DAMAGE. 23 | .\" 24 | .\" $FreeBSD$ 25 | .\" 26 | .Dd September 14, 2020 27 | .Dt HCONF 4 28 | .Os 29 | .Sh NAME 30 | .Nm hconf 31 | .Nd MS Windows Precision Touchpad configuration driver 32 | .Sh SYNOPSIS 33 | To compile this driver into the kernel, 34 | place the following lines in your 35 | kernel configuration file: 36 | .Bd -ragged -offset indent 37 | .Cd "device hconf" 38 | .Cd "device hid" 39 | .Cd "device hidbus" 40 | .Ed 41 | .Pp 42 | Alternatively, to load the driver as a 43 | module at boot time, place the following line in 44 | .Xr loader.conf 5 : 45 | .Bd -literal -offset indent 46 | hconf_load="YES" 47 | .Ed 48 | .Sh DESCRIPTION 49 | The 50 | .Nm 51 | driver provides support for generic MS Windows Precision Touchpad 52 | configuration collection. 53 | It enables the host to configure two different aspects of the device. 54 | One allows the host to select input mode, and the other allows the host to be 55 | selective in what is reported. 56 | .Sh SYSCTL VARIABLES 57 | Next parameters are available as 58 | .Xr sysctl 8 59 | variables. 60 | Debug parameter is available as 61 | .Xr loader 8 62 | tunable as well. 63 | .Bl -tag -width indent 64 | .It Va dev.hconf.*.input_mode 65 | HID device input mode: 0 = mouse, 3 = touchpad. 66 | .It Va dev.hconf.*.surface_switch 67 | Enable / disable switch for surface: 1 = on, 0 = off. 68 | .It Va dev.hconf.*.buttons_switch 69 | Enable / disable switch for buttons: 1 = on, 0 = off. 70 | .It Va hw.hid.hconf.debug 71 | Debug output level, where 0 is debugging disabled and larger values increase 72 | debug message verbosity. 73 | Default is 0. 74 | .El 75 | .Sh SEE ALSO 76 | .Xr hms 4 , 77 | .Xr hmt 4 78 | .Sh HISTORY 79 | The 80 | .Nm 81 | driver first appeared in 82 | .Fx 13.0. 83 | .Sh AUTHORS 84 | .An -nosplit 85 | The 86 | .Nm 87 | driver was written by 88 | .An Vladimir Kondratyev Aq Mt wulf@FreeBSD.org . 89 | Switch parameter support was added by 90 | .An Andriy Gapon Aq Mt avg@FreeBSD.org . 91 | -------------------------------------------------------------------------------- /hconf.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 | * 4 | * Copyright (c) 2019 Vladimir Kondratyev 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 | * SUCH DAMAGE. 26 | */ 27 | 28 | #include 29 | __FBSDID("$FreeBSD$"); 30 | 31 | /* 32 | * Digitizer configuration top-level collection support. 33 | * https://docs.microsoft.com/en-us/windows-hardware/design/component-guidelines/windows-precision-touchpad-required-hid-top-level-collections 34 | */ 35 | 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | 46 | #include "hid.h" 47 | #include "hidbus.h" 48 | 49 | #include "hconf.h" 50 | 51 | #define HID_DEBUG_VAR hconf_debug 52 | #include "hid_debug.h" 53 | 54 | #ifdef HID_DEBUG 55 | static int hconf_debug = 0; 56 | 57 | static SYSCTL_NODE(_hw_hid, OID_AUTO, hconf, CTLFLAG_RW, 0, 58 | "Digitizer configuration top-level collection"); 59 | SYSCTL_INT(_hw_hid_hconf, OID_AUTO, debug, CTLFLAG_RWTUN, 60 | &hconf_debug, 1, "Debug level"); 61 | #endif 62 | 63 | enum feature_control_type { 64 | INPUT_MODE = 0, 65 | SURFACE_SWITCH, 66 | BUTTONS_SWITCH, 67 | CONTROLS_COUNT 68 | }; 69 | 70 | struct feature_control_descr { 71 | const char *name; 72 | const char *descr; 73 | uint16_t usage; 74 | u_int value; 75 | } feature_control_descrs[] = { 76 | [INPUT_MODE] = { 77 | .name = "input_mode", 78 | .descr = "HID device input mode: 0 = mouse, 3 = touchpad", 79 | .usage = HUD_INPUT_MODE, 80 | .value = HCONF_INPUT_MODE_MOUSE, 81 | }, 82 | [SURFACE_SWITCH] = { 83 | .name = "surface_switch", 84 | .descr = "Enable / disable switch for surface: 1 = on, 0 = off", 85 | .usage = HUD_SURFACE_SWITCH, 86 | .value = 1, 87 | }, 88 | [BUTTONS_SWITCH] = { 89 | .name = "buttons_switch", 90 | .descr = "Enable / disable switch for buttons: 1 = on, 0 = off", 91 | .usage = HUD_BUTTONS_SWITCH, 92 | .value = 1, 93 | }, 94 | }; 95 | 96 | struct feature_control { 97 | u_int val; 98 | struct hid_location loc; 99 | hid_size_t rlen; 100 | uint8_t rid; 101 | }; 102 | 103 | struct hconf_softc { 104 | device_t dev; 105 | struct sx lock; 106 | 107 | struct feature_control feature_controls[CONTROLS_COUNT]; 108 | }; 109 | 110 | static device_probe_t hconf_probe; 111 | static device_attach_t hconf_attach; 112 | static device_detach_t hconf_detach; 113 | static device_resume_t hconf_resume; 114 | 115 | static devclass_t hconf_devclass; 116 | 117 | static device_method_t hconf_methods[] = { 118 | 119 | DEVMETHOD(device_probe, hconf_probe), 120 | DEVMETHOD(device_attach, hconf_attach), 121 | DEVMETHOD(device_detach, hconf_detach), 122 | DEVMETHOD(device_resume, hconf_resume), 123 | 124 | DEVMETHOD_END 125 | }; 126 | 127 | static driver_t hconf_driver = { 128 | .name = "hconf", 129 | .methods = hconf_methods, 130 | .size = sizeof(struct hconf_softc), 131 | }; 132 | 133 | static const struct hid_device_id hconf_devs[] = { 134 | { HID_TLC(HUP_DIGITIZERS, HUD_CONFIG) }, 135 | }; 136 | 137 | static int 138 | hconf_set_feature_control(struct hconf_softc *sc, int ctrl_id, u_int val) 139 | { 140 | struct feature_control *fc; 141 | uint8_t *fbuf; 142 | int error; 143 | int i; 144 | 145 | KASSERT(ctrl_id >= 0 && ctrl_id < CONTROLS_COUNT, 146 | ("impossible ctrl id %d", ctrl_id)); 147 | fc = &sc->feature_controls[ctrl_id]; 148 | if (fc->rlen <= 1) 149 | return (ENXIO); 150 | 151 | fbuf = malloc(fc->rlen, M_TEMP, M_WAITOK | M_ZERO); 152 | sx_xlock(&sc->lock); 153 | 154 | /* 155 | * Assume the report is write-only. Then we have to check for other 156 | * controls that may share the same report and set their bits as well. 157 | */ 158 | bzero(fbuf + 1, fc->rlen - 1); 159 | for (i = 0; i < nitems(sc->feature_controls); i++) { 160 | struct feature_control *ofc = &sc->feature_controls[i]; 161 | 162 | /* Skip unrelated report IDs. */ 163 | if (ofc->rid != fc->rid) 164 | continue; 165 | KASSERT(fc->rlen == ofc->rlen, 166 | ("different lengths for report %d: %d vs %d\n", 167 | fc->rid, fc->rlen, ofc->rlen)); 168 | hid_put_data_unsigned(fbuf + 1, ofc->rlen - 1, &ofc->loc, 169 | i == ctrl_id ? val : ofc->val); 170 | } 171 | 172 | fbuf[0] = fc->rid; 173 | 174 | error = hid_set_report(sc->dev, fbuf, fc->rlen, 175 | HID_FEATURE_REPORT, fc->rid); 176 | if (error == 0) 177 | fc->val = val; 178 | 179 | sx_unlock(&sc->lock); 180 | free(fbuf, M_TEMP); 181 | 182 | return (error); 183 | } 184 | 185 | static int 186 | hconf_feature_control_handler(SYSCTL_HANDLER_ARGS) 187 | { 188 | struct feature_control *fc; 189 | struct hconf_softc *sc = arg1; 190 | int ctrl_id = arg2; 191 | u_int value; 192 | int error; 193 | 194 | if (ctrl_id < 0 || ctrl_id >= CONTROLS_COUNT) 195 | return (ENXIO); 196 | 197 | fc = &sc->feature_controls[ctrl_id]; 198 | value = fc->val; 199 | error = sysctl_handle_int(oidp, &value, 0, req); 200 | if (error != 0 || req->newptr == NULL) 201 | return (error); 202 | 203 | error = hconf_set_feature_control(sc, ctrl_id, value); 204 | if (error != 0) { 205 | DPRINTF("Failed to set %s: %d\n", 206 | feature_control_descrs[ctrl_id].name, error); 207 | } 208 | return (0); 209 | } 210 | 211 | 212 | static int 213 | hconf_parse_feature(struct feature_control *fc, uint8_t tlc_index, 214 | uint16_t usage, void *d_ptr, hid_size_t d_len) 215 | { 216 | uint32_t flags; 217 | 218 | if (!hidbus_locate(d_ptr, d_len, HID_USAGE2(HUP_DIGITIZERS, usage), 219 | hid_feature, tlc_index, 0, &fc->loc, &flags, &fc->rid, NULL)) 220 | return (ENOENT); 221 | 222 | if ((flags & (HIO_VARIABLE | HIO_RELATIVE)) != HIO_VARIABLE) 223 | return (EINVAL); 224 | 225 | fc->rlen = hid_report_size_1(d_ptr, d_len, hid_feature, fc->rid); 226 | return (0); 227 | } 228 | 229 | static int 230 | hconf_probe(device_t dev) 231 | { 232 | int error; 233 | 234 | error = HIDBUS_LOOKUP_DRIVER_INFO(dev, hconf_devs); 235 | if (error != 0) 236 | return (error); 237 | 238 | hidbus_set_desc(dev, "Configuration"); 239 | 240 | return (BUS_PROBE_DEFAULT); 241 | } 242 | 243 | static int 244 | hconf_attach(device_t dev) 245 | { 246 | struct hconf_softc *sc = device_get_softc(dev); 247 | struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(dev); 248 | struct sysctl_oid *tree = device_get_sysctl_tree(dev); 249 | void *d_ptr; 250 | hid_size_t d_len; 251 | uint8_t tlc_index; 252 | int error; 253 | int i; 254 | 255 | error = hid_get_report_descr(dev, &d_ptr, &d_len); 256 | if (error) { 257 | device_printf(dev, "could not retrieve report descriptor from " 258 | "device: %d\n", error); 259 | return (ENXIO); 260 | } 261 | 262 | sc->dev = dev; 263 | sx_init(&sc->lock, device_get_nameunit(dev)); 264 | 265 | tlc_index = hidbus_get_index(dev); 266 | for (i = 0; i < nitems(sc->feature_controls); i++) { 267 | (void)hconf_parse_feature(&sc->feature_controls[i], tlc_index, 268 | feature_control_descrs[i].usage, d_ptr, d_len); 269 | if (sc->feature_controls[i].rlen > 1) { 270 | SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 271 | feature_control_descrs[i].name, 272 | CTLTYPE_UINT | CTLFLAG_RW, 273 | sc, i, hconf_feature_control_handler, "I", 274 | feature_control_descrs[i].descr); 275 | } 276 | sc->feature_controls[i].val = feature_control_descrs[i].value; 277 | } 278 | 279 | return (0); 280 | } 281 | 282 | static int 283 | hconf_detach(device_t dev) 284 | { 285 | struct hconf_softc *sc = device_get_softc(dev); 286 | 287 | sx_destroy(&sc->lock); 288 | 289 | return (0); 290 | } 291 | 292 | static int 293 | hconf_resume(device_t dev) 294 | { 295 | struct hconf_softc *sc = device_get_softc(dev); 296 | int error; 297 | int i; 298 | 299 | for (i = 0; i < nitems(sc->feature_controls); i++) { 300 | if (sc->feature_controls[i].rlen < 2) 301 | continue; 302 | /* Do not update usages to default value */ 303 | if (sc->feature_controls[i].val == 304 | feature_control_descrs[i].value) 305 | continue; 306 | error = hconf_set_feature_control(sc, i, 307 | sc->feature_controls[i].val); 308 | if (error != 0) { 309 | DPRINTF("Failed to restore %s: %d\n", 310 | feature_control_descrs[i].name, error); 311 | } 312 | } 313 | 314 | return (0); 315 | } 316 | 317 | int 318 | hconf_set_input_mode(device_t dev, enum hconf_input_mode mode) 319 | { 320 | struct hconf_softc *sc = device_get_softc(dev); 321 | 322 | return (hconf_set_feature_control(sc, INPUT_MODE, mode)); 323 | } 324 | 325 | DRIVER_MODULE(hconf, hidbus, hconf_driver, hconf_devclass, NULL, 0); 326 | MODULE_DEPEND(hconf, hidbus, 1, 1, 1); 327 | MODULE_DEPEND(hconf, hid, 1, 1, 1); 328 | MODULE_VERSION(hconf, 1); 329 | HID_PNP_INFO(hconf_devs); 330 | -------------------------------------------------------------------------------- /hconf.h: -------------------------------------------------------------------------------- 1 | /*- 2 | * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 | * 4 | * Copyright (c) 2019 Vladimir Kondratyev 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 | * SUCH DAMAGE. 26 | * 27 | * $FreeBSD$ 28 | */ 29 | 30 | #ifndef _HCONF_H_ 31 | #define _HCONF_H_ 32 | 33 | enum hconf_input_mode { 34 | HCONF_INPUT_MODE_MOUSE = 0x0, 35 | HCONF_INPUT_MODE_MT_TOUCHSCREEN = 0x2, 36 | HCONF_INPUT_MODE_MT_TOUCHPAD = 0x3, 37 | }; 38 | 39 | int hconf_set_input_mode(device_t, enum hconf_input_mode); 40 | 41 | #endif /* _HCONF_H_ */ 42 | -------------------------------------------------------------------------------- /hcons.4: -------------------------------------------------------------------------------- 1 | .\" Copyright (c) 2020 Vladimir Kondratyev 2 | .\" 3 | .\" Redistribution and use in source and binary forms, with or without 4 | .\" modification, are permitted provided that the following conditions 5 | .\" are met: 6 | .\" 1. Redistributions of source code must retain the above copyright 7 | .\" notice, this list of conditions and the following disclaimer. 8 | .\" 2. Redistributions in binary form must reproduce the above copyright 9 | .\" notice, this list of conditions and the following disclaimer in the 10 | .\" documentation and/or other materials provided with the distribution. 11 | .\" 12 | .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 13 | .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 14 | .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 15 | .\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 16 | .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 17 | .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 18 | .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 19 | .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 20 | .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 21 | .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 22 | .\" SUCH DAMAGE. 23 | .\" 24 | .\" $FreeBSD$ 25 | .\" 26 | .Dd September 14, 2020 27 | .Dt HCONS 4 28 | .Os 29 | .Sh NAME 30 | .Nm hcons 31 | .Nd HID consumer page controls driver 32 | .Sh SYNOPSIS 33 | To compile this driver into the kernel, 34 | place the following lines in your 35 | kernel configuration file: 36 | .Bd -ragged -offset indent 37 | .Cd "device hcons" 38 | .Cd "device hid" 39 | .Cd "device hidbus" 40 | .Cd "device hidmap" 41 | .Cd "device evdev" 42 | .Ed 43 | .Pp 44 | Alternatively, to load the driver as a 45 | module at boot time, place the following line in 46 | .Xr loader.conf 5 : 47 | .Bd -literal -offset indent 48 | hgame_load="YES" 49 | .Ed 50 | .Sh DESCRIPTION 51 | The 52 | .Nm 53 | driver provides support for HID consumer page controls most often used as 54 | "Multimedia keys" found on many keyboards. 55 | .Pp 56 | The 57 | .Pa /dev/input/event* 58 | device presents the consumer page controls as a 59 | .Ar evdev 60 | type device. 61 | .Sh SYSCTL VARIABLES 62 | The following variables are available as both 63 | .Xr sysctl 8 64 | variables and 65 | .Xr loader 8 66 | tunables: 67 | .Bl -tag -width indent 68 | .It Va hw.hid.hcons.debug 69 | Debug output level, where 0 is debugging disabled and larger values increase 70 | debug message verbosity. 71 | Default is 0. 72 | .El 73 | .Sh FILES 74 | .Bl -tag -width /dev/input/event* -compact 75 | .It Pa /dev/input/event* 76 | input event device node. 77 | .El 78 | .Sh SEE ALSO 79 | .Xr iichid 4 , 80 | .Xr usbhid 4 81 | .Sh HISTORY 82 | The 83 | .Nm 84 | driver first appeared in 85 | .Fx 13.0. 86 | .Sh AUTHORS 87 | .An -nosplit 88 | The 89 | .Nm 90 | driver was written by 91 | .An Vladimir Kondratyev Aq Mt wulf@FreeBSD.org . 92 | -------------------------------------------------------------------------------- /hcons.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 | * 4 | * Copyright (c) 2020 Vladimir Kondratyev 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 | * SUCH DAMAGE. 26 | */ 27 | 28 | #include 29 | __FBSDID("$FreeBSD$"); 30 | 31 | /* 32 | * Consumer Controls usage page driver 33 | * https://www.usb.org/sites/default/files/documents/hut1_12v2.pdf 34 | */ 35 | 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | 43 | #include 44 | #include 45 | 46 | #include "hid.h" 47 | #include "hidbus.h" 48 | #include "hidmap.h" 49 | 50 | #ifndef KEY_FULL_SCREEN 51 | #define KEY_FULL_SCREEN 0x174 52 | #endif 53 | #ifndef KEY_ASPECT_RATIO 54 | #define KEY_ASPECT_RATIO 0x177 55 | #endif 56 | #ifndef KEY_KBD_LAYOUT_NEXT 57 | #define KEY_KBD_LAYOUT_NEXT 0x248 58 | #endif 59 | 60 | static hidmap_cb_t hcons_rel_volume_cb; 61 | 62 | #define HCONS_MAP_KEY(usage, code) \ 63 | { HIDMAP_KEY(HUP_CONSUMER, usage, code) } 64 | #define HCONS_MAP_ABS(usage, code) \ 65 | { HIDMAP_ABS(HUP_CONSUMER, usage, code) } 66 | #define HCONS_MAP_REL(usage, code) \ 67 | { HIDMAP_REL(HUP_CONSUMER, usage, code) } 68 | #define HCONS_MAP_REL_CB(usage, callback) \ 69 | { HIDMAP_REL_CB(HUP_CONSUMER, usage, &callback) } 70 | 71 | static const struct hidmap_item hcons_map[] = { 72 | HCONS_MAP_KEY(0x030, KEY_POWER), 73 | HCONS_MAP_KEY(0x031, KEY_RESTART), 74 | HCONS_MAP_KEY(0x032, KEY_SLEEP), 75 | HCONS_MAP_KEY(0x034, KEY_SLEEP), 76 | HCONS_MAP_KEY(0x035, KEY_KBDILLUMTOGGLE), 77 | HCONS_MAP_KEY(0x036, BTN_MISC), 78 | HCONS_MAP_KEY(0x040, KEY_MENU), /* Menu */ 79 | HCONS_MAP_KEY(0x041, KEY_SELECT), /* Menu Pick */ 80 | HCONS_MAP_KEY(0x042, KEY_UP), /* Menu Up */ 81 | HCONS_MAP_KEY(0x043, KEY_DOWN), /* Menu Down */ 82 | HCONS_MAP_KEY(0x044, KEY_LEFT), /* Menu Left */ 83 | HCONS_MAP_KEY(0x045, KEY_RIGHT), /* Menu Right */ 84 | HCONS_MAP_KEY(0x046, KEY_ESC), /* Menu Escape */ 85 | HCONS_MAP_KEY(0x047, KEY_KPPLUS), /* Menu Value Increase */ 86 | HCONS_MAP_KEY(0x048, KEY_KPMINUS), /* Menu Value Decrease */ 87 | HCONS_MAP_KEY(0x060, KEY_INFO), /* Data On Screen */ 88 | HCONS_MAP_KEY(0x061, KEY_SUBTITLE), /* Closed Caption */ 89 | HCONS_MAP_KEY(0x063, KEY_VCR), /* VCR/TV */ 90 | HCONS_MAP_KEY(0x065, KEY_CAMERA), /* Snapshot */ 91 | HCONS_MAP_KEY(0x069, KEY_RED), 92 | HCONS_MAP_KEY(0x06a, KEY_GREEN), 93 | HCONS_MAP_KEY(0x06b, KEY_BLUE), 94 | HCONS_MAP_KEY(0x06c, KEY_YELLOW), 95 | HCONS_MAP_KEY(0x06d, KEY_ASPECT_RATIO), 96 | HCONS_MAP_KEY(0x06f, KEY_BRIGHTNESSUP), 97 | HCONS_MAP_KEY(0x070, KEY_BRIGHTNESSDOWN), 98 | HCONS_MAP_KEY(0x072, KEY_BRIGHTNESS_TOGGLE), 99 | HCONS_MAP_KEY(0x073, KEY_BRIGHTNESS_MIN), 100 | HCONS_MAP_KEY(0x074, KEY_BRIGHTNESS_MAX), 101 | HCONS_MAP_KEY(0x075, KEY_BRIGHTNESS_AUTO), 102 | HCONS_MAP_KEY(0x079, KEY_KBDILLUMUP), 103 | HCONS_MAP_KEY(0x07a, KEY_KBDILLUMDOWN), 104 | HCONS_MAP_KEY(0x07c, KEY_KBDILLUMTOGGLE), 105 | HCONS_MAP_KEY(0x082, KEY_VIDEO_NEXT), 106 | HCONS_MAP_KEY(0x083, KEY_LAST), 107 | HCONS_MAP_KEY(0x084, KEY_ENTER), 108 | HCONS_MAP_KEY(0x088, KEY_PC), 109 | HCONS_MAP_KEY(0x089, KEY_TV), 110 | HCONS_MAP_KEY(0x08a, KEY_WWW), 111 | HCONS_MAP_KEY(0x08b, KEY_DVD), 112 | HCONS_MAP_KEY(0x08c, KEY_PHONE), 113 | HCONS_MAP_KEY(0x08d, KEY_PROGRAM), 114 | HCONS_MAP_KEY(0x08e, KEY_VIDEOPHONE), 115 | HCONS_MAP_KEY(0x08f, KEY_GAMES), 116 | HCONS_MAP_KEY(0x090, KEY_MEMO), 117 | HCONS_MAP_KEY(0x091, KEY_CD), 118 | HCONS_MAP_KEY(0x092, KEY_VCR), 119 | HCONS_MAP_KEY(0x093, KEY_TUNER), 120 | HCONS_MAP_KEY(0x094, KEY_EXIT), 121 | HCONS_MAP_KEY(0x095, KEY_HELP), 122 | HCONS_MAP_KEY(0x096, KEY_TAPE), 123 | HCONS_MAP_KEY(0x097, KEY_TV2), 124 | HCONS_MAP_KEY(0x098, KEY_SAT), 125 | HCONS_MAP_KEY(0x09a, KEY_PVR), 126 | HCONS_MAP_KEY(0x09c, KEY_CHANNELUP), 127 | HCONS_MAP_KEY(0x09d, KEY_CHANNELDOWN), 128 | HCONS_MAP_KEY(0x0a0, KEY_VCR2), 129 | HCONS_MAP_KEY(0x0b0, KEY_PLAY), 130 | HCONS_MAP_KEY(0x0b1, KEY_PAUSE), 131 | HCONS_MAP_KEY(0x0b2, KEY_RECORD), 132 | HCONS_MAP_KEY(0x0b3, KEY_FASTFORWARD), 133 | HCONS_MAP_KEY(0x0b4, KEY_REWIND), 134 | HCONS_MAP_KEY(0x0b5, KEY_NEXTSONG), 135 | HCONS_MAP_KEY(0x0b6, KEY_PREVIOUSSONG), 136 | HCONS_MAP_KEY(0x0b7, KEY_STOPCD), 137 | HCONS_MAP_KEY(0x0b8, KEY_EJECTCD), 138 | HCONS_MAP_KEY(0x0bc, KEY_MEDIA_REPEAT), 139 | HCONS_MAP_KEY(0x0b9, KEY_SHUFFLE), 140 | HCONS_MAP_KEY(0x0bf, KEY_SLOW), 141 | HCONS_MAP_KEY(0x0cd, KEY_PLAYPAUSE), 142 | HCONS_MAP_KEY(0x0cf, KEY_VOICECOMMAND), 143 | HCONS_MAP_ABS(0x0e0, ABS_VOLUME), 144 | HCONS_MAP_REL_CB(0x0e0, hcons_rel_volume_cb), 145 | HCONS_MAP_KEY(0x0e2, KEY_MUTE), 146 | HCONS_MAP_KEY(0x0e5, KEY_BASSBOOST), 147 | HCONS_MAP_KEY(0x0e9, KEY_VOLUMEUP), 148 | HCONS_MAP_KEY(0x0ea, KEY_VOLUMEDOWN), 149 | HCONS_MAP_KEY(0x0f5, KEY_SLOW), 150 | HCONS_MAP_KEY(0x181, KEY_BUTTONCONFIG), 151 | HCONS_MAP_KEY(0x182, KEY_BOOKMARKS), 152 | HCONS_MAP_KEY(0x183, KEY_CONFIG), 153 | HCONS_MAP_KEY(0x184, KEY_WORDPROCESSOR), 154 | HCONS_MAP_KEY(0x185, KEY_EDITOR), 155 | HCONS_MAP_KEY(0x186, KEY_SPREADSHEET), 156 | HCONS_MAP_KEY(0x187, KEY_GRAPHICSEDITOR), 157 | HCONS_MAP_KEY(0x188, KEY_PRESENTATION), 158 | HCONS_MAP_KEY(0x189, KEY_DATABASE), 159 | HCONS_MAP_KEY(0x18a, KEY_MAIL), 160 | HCONS_MAP_KEY(0x18b, KEY_NEWS), 161 | HCONS_MAP_KEY(0x18c, KEY_VOICEMAIL), 162 | HCONS_MAP_KEY(0x18d, KEY_ADDRESSBOOK), 163 | HCONS_MAP_KEY(0x18e, KEY_CALENDAR), 164 | HCONS_MAP_KEY(0x18f, KEY_TASKMANAGER), 165 | HCONS_MAP_KEY(0x190, KEY_JOURNAL), 166 | HCONS_MAP_KEY(0x191, KEY_FINANCE), 167 | HCONS_MAP_KEY(0x192, KEY_CALC), 168 | HCONS_MAP_KEY(0x193, KEY_PLAYER), 169 | HCONS_MAP_KEY(0x194, KEY_FILE), 170 | HCONS_MAP_KEY(0x196, KEY_WWW), 171 | HCONS_MAP_KEY(0x199, KEY_CHAT), 172 | HCONS_MAP_KEY(0x19c, KEY_LOGOFF), 173 | HCONS_MAP_KEY(0x19e, KEY_COFFEE), 174 | HCONS_MAP_KEY(0x19f, KEY_CONTROLPANEL), 175 | HCONS_MAP_KEY(0x1a2, KEY_APPSELECT), 176 | HCONS_MAP_KEY(0x1a3, KEY_NEXT), 177 | HCONS_MAP_KEY(0x1a4, KEY_PREVIOUS), 178 | HCONS_MAP_KEY(0x1a6, KEY_HELP), 179 | HCONS_MAP_KEY(0x1a7, KEY_DOCUMENTS), 180 | HCONS_MAP_KEY(0x1ab, KEY_SPELLCHECK), 181 | HCONS_MAP_KEY(0x1ae, KEY_KEYBOARD), 182 | HCONS_MAP_KEY(0x1b1, KEY_SCREENSAVER), 183 | HCONS_MAP_KEY(0x1b4, KEY_FILE), 184 | HCONS_MAP_KEY(0x1b6, KEY_IMAGES), 185 | HCONS_MAP_KEY(0x1b7, KEY_AUDIO), 186 | HCONS_MAP_KEY(0x1b8, KEY_VIDEO), 187 | HCONS_MAP_KEY(0x1bc, KEY_MESSENGER), 188 | HCONS_MAP_KEY(0x1bd, KEY_INFO), 189 | HCONS_MAP_KEY(0x1cb, KEY_ASSISTANT), 190 | HCONS_MAP_KEY(0x201, KEY_NEW), 191 | HCONS_MAP_KEY(0x202, KEY_OPEN), 192 | HCONS_MAP_KEY(0x203, KEY_CLOSE), 193 | HCONS_MAP_KEY(0x204, KEY_EXIT), 194 | HCONS_MAP_KEY(0x207, KEY_SAVE), 195 | HCONS_MAP_KEY(0x208, KEY_PRINT), 196 | HCONS_MAP_KEY(0x209, KEY_PROPS), 197 | HCONS_MAP_KEY(0x21a, KEY_UNDO), 198 | HCONS_MAP_KEY(0x21b, KEY_COPY), 199 | HCONS_MAP_KEY(0x21c, KEY_CUT), 200 | HCONS_MAP_KEY(0x21d, KEY_PASTE), 201 | HCONS_MAP_KEY(0x21f, KEY_FIND), 202 | HCONS_MAP_KEY(0x221, KEY_SEARCH), 203 | HCONS_MAP_KEY(0x222, KEY_GOTO), 204 | HCONS_MAP_KEY(0x223, KEY_HOMEPAGE), 205 | HCONS_MAP_KEY(0x224, KEY_BACK), 206 | HCONS_MAP_KEY(0x225, KEY_FORWARD), 207 | HCONS_MAP_KEY(0x226, KEY_STOP), 208 | HCONS_MAP_KEY(0x227, KEY_REFRESH), 209 | HCONS_MAP_KEY(0x22a, KEY_BOOKMARKS), 210 | HCONS_MAP_KEY(0x22d, KEY_ZOOMIN), 211 | HCONS_MAP_KEY(0x22e, KEY_ZOOMOUT), 212 | HCONS_MAP_KEY(0x22f, KEY_ZOOMRESET), 213 | HCONS_MAP_KEY(0x232, KEY_FULL_SCREEN), 214 | HCONS_MAP_KEY(0x233, KEY_SCROLLUP), 215 | HCONS_MAP_KEY(0x234, KEY_SCROLLDOWN), 216 | HCONS_MAP_REL(0x238, REL_HWHEEL), /* AC Pan */ 217 | HCONS_MAP_KEY(0x23d, KEY_EDIT), 218 | HCONS_MAP_KEY(0x25f, KEY_CANCEL), 219 | HCONS_MAP_KEY(0x269, KEY_INSERT), 220 | HCONS_MAP_KEY(0x26a, KEY_DELETE), 221 | HCONS_MAP_KEY(0x279, KEY_REDO), 222 | HCONS_MAP_KEY(0x289, KEY_REPLY), 223 | HCONS_MAP_KEY(0x28b, KEY_FORWARDMAIL), 224 | HCONS_MAP_KEY(0x28c, KEY_SEND), 225 | HCONS_MAP_KEY(0x29d, KEY_KBD_LAYOUT_NEXT), 226 | HCONS_MAP_KEY(0x2c7, KEY_KBDINPUTASSIST_PREV), 227 | HCONS_MAP_KEY(0x2c8, KEY_KBDINPUTASSIST_NEXT), 228 | HCONS_MAP_KEY(0x2c9, KEY_KBDINPUTASSIST_PREVGROUP), 229 | HCONS_MAP_KEY(0x2ca, KEY_KBDINPUTASSIST_NEXTGROUP), 230 | HCONS_MAP_KEY(0x2cb, KEY_KBDINPUTASSIST_ACCEPT), 231 | HCONS_MAP_KEY(0x2cc, KEY_KBDINPUTASSIST_CANCEL), 232 | HCONS_MAP_KEY(0x29f, KEY_SCALE), 233 | }; 234 | 235 | static const struct hid_device_id hcons_devs[] = { 236 | { HID_TLC(HUP_CONSUMER, HUC_CONSUMER_CONTROL) }, 237 | }; 238 | 239 | /* 240 | * Emulate relative Consumer volume usage with pressing 241 | * VOLUMEUP and VOLUMEDOWN keys appropriate number of times 242 | */ 243 | static int 244 | hcons_rel_volume_cb(HIDMAP_CB_ARGS) 245 | { 246 | struct evdev_dev *evdev = HIDMAP_CB_GET_EVDEV(); 247 | int32_t code; 248 | int nrepeats; 249 | 250 | switch (HIDMAP_CB_GET_STATE()) { 251 | case HIDMAP_CB_IS_ATTACHING: 252 | evdev_support_event(evdev, EV_KEY); 253 | evdev_support_key(evdev, KEY_VOLUMEUP); 254 | evdev_support_key(evdev, KEY_VOLUMEDOWN); 255 | break; 256 | case HIDMAP_CB_IS_RUNNING: 257 | /* Nothing to report. */ 258 | if (ctx.data == 0) 259 | return (ENOMSG); 260 | code = ctx.data > 0 ? KEY_VOLUMEUP : KEY_VOLUMEDOWN; 261 | for (nrepeats = abs(ctx.data); nrepeats > 0; nrepeats--) { 262 | evdev_push_key(evdev, code, 1); 263 | evdev_push_key(evdev, code, 0); 264 | } 265 | break; 266 | default: 267 | break; 268 | } 269 | 270 | return (0); 271 | } 272 | 273 | static int 274 | hcons_probe(device_t dev) 275 | { 276 | return (HIDMAP_PROBE(device_get_softc(dev), dev, 277 | hcons_devs, hcons_map, "Consumer Control")); 278 | } 279 | 280 | static int 281 | hcons_attach(device_t dev) 282 | { 283 | return (hidmap_attach(device_get_softc(dev))); 284 | } 285 | 286 | static int 287 | hcons_detach(device_t dev) 288 | { 289 | return (hidmap_detach(device_get_softc(dev))); 290 | } 291 | 292 | static devclass_t hcons_devclass; 293 | static device_method_t hcons_methods[] = { 294 | DEVMETHOD(device_probe, hcons_probe), 295 | DEVMETHOD(device_attach, hcons_attach), 296 | DEVMETHOD(device_detach, hcons_detach), 297 | 298 | DEVMETHOD_END 299 | }; 300 | 301 | DEFINE_CLASS_0(hcons, hcons_driver, hcons_methods, sizeof(struct hidmap)); 302 | DRIVER_MODULE(hcons, hidbus, hcons_driver, hcons_devclass, NULL, 0); 303 | MODULE_DEPEND(hcons, hid, 1, 1, 1); 304 | MODULE_DEPEND(hcons, hidmap, 1, 1, 1); 305 | MODULE_DEPEND(hcons, evdev, 1, 1, 1); 306 | MODULE_VERSION(hcons, 1); 307 | HID_PNP_INFO(hcons_devs); 308 | -------------------------------------------------------------------------------- /hgame.4: -------------------------------------------------------------------------------- 1 | .\" Copyright (c) 2020 Vladimir Kondratyev 2 | .\" 3 | .\" Redistribution and use in source and binary forms, with or without 4 | .\" modification, are permitted provided that the following conditions 5 | .\" are met: 6 | .\" 1. Redistributions of source code must retain the above copyright 7 | .\" notice, this list of conditions and the following disclaimer. 8 | .\" 2. Redistributions in binary form must reproduce the above copyright 9 | .\" notice, this list of conditions and the following disclaimer in the 10 | .\" documentation and/or other materials provided with the distribution. 11 | .\" 12 | .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 13 | .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 14 | .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 15 | .\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 16 | .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 17 | .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 18 | .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 19 | .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 20 | .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 21 | .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 22 | .\" SUCH DAMAGE. 23 | .\" 24 | .\" $FreeBSD$ 25 | .\" 26 | .Dd September 14, 2020 27 | .Dt HGAME 4 28 | .Os 29 | .Sh NAME 30 | .Nm hgame 31 | .Nd Generic HID game controller (joystick/gamepad) driver 32 | .Sh SYNOPSIS 33 | To compile this driver into the kernel, 34 | place the following lines in your 35 | kernel configuration file: 36 | .Bd -ragged -offset indent 37 | .Cd "device hgame" 38 | .Cd "device hid" 39 | .Cd "device hidbus" 40 | .Cd "device hidmap" 41 | .Cd "device evdev" 42 | .Ed 43 | .Pp 44 | Alternatively, to load the driver as a 45 | module at boot time, place the following line in 46 | .Xr loader.conf 5 : 47 | .Bd -literal -offset indent 48 | hgame_load="YES" 49 | .Ed 50 | .Sh DESCRIPTION 51 | The 52 | .Nm 53 | driver provides support for generic game controllers (joysticks/gamepads) 54 | that attach to the HID transport backend. 55 | See 56 | .Xr iichid 4 57 | or 58 | .Xr usbhid 4 . 59 | .Pp 60 | The 61 | .Pa /dev/input/event* 62 | device presents the game controller as a 63 | .Ar evdev 64 | type device. 65 | .Sh SYSCTL VARIABLES 66 | The following variables are available as both 67 | .Xr sysctl 8 68 | variables and 69 | .Xr loader 8 70 | tunables: 71 | .Bl -tag -width indent 72 | .It Va hw.hid.hgame.debug 73 | Debug output level, where 0 is debugging disabled and larger values increase 74 | debug message verbosity. 75 | Default is 0. 76 | .El 77 | .Sh FILES 78 | .Bl -tag -width /dev/input/event* -compact 79 | .It Pa /dev/input/event* 80 | input event device node. 81 | .El 82 | .Sh SEE ALSO 83 | .Xr iichid 4 , 84 | .Xr usbhid 4 85 | .Sh HISTORY 86 | The 87 | .Nm 88 | driver first appeared in 89 | .Fx 13.0. 90 | .Sh AUTHORS 91 | .An -nosplit 92 | The 93 | .Nm 94 | driver was written by 95 | .An Greg V Aq Mt greg@unrelenting.technology . 96 | .Pp 97 | This manual page was written by 98 | .An Vladimir Kondratyev Aq Mt wulf@FreeBSD.org . 99 | -------------------------------------------------------------------------------- /hgame.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 | * 4 | * Copyright (c) 2020 Vladimir Kondratyev 5 | * Copyright (c) 2020 Greg V 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 30 | __FBSDID("$FreeBSD$"); 31 | 32 | /* 33 | * Generic HID game controller (joystick/gamepad) driver, 34 | */ 35 | 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | 42 | #include 43 | #include 44 | 45 | #include "hgame.h" 46 | #include "hid.h" 47 | #include "hidbus.h" 48 | #include "hidquirk.h" 49 | #include "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, 0, -1, 1, 0, 0, 0); 130 | evdev_support_abs(evdev, ABS_HAT0Y, 0, -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 devclass_t hgame_devclass; 221 | static device_method_t hgame_methods[] = { 222 | DEVMETHOD(device_probe, hgame_probe), 223 | DEVMETHOD(device_attach, hgame_attach), 224 | DEVMETHOD(device_detach, hgame_detach), 225 | 226 | DEVMETHOD_END 227 | }; 228 | 229 | DEFINE_CLASS_0(hgame, hgame_driver, hgame_methods, sizeof(struct hgame_softc)); 230 | DRIVER_MODULE(hgame, hidbus, hgame_driver, hgame_devclass, NULL, 0); 231 | MODULE_DEPEND(hgame, hid, 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 | -------------------------------------------------------------------------------- /hgame.h: -------------------------------------------------------------------------------- 1 | /*- 2 | * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 | * 4 | * Copyright (c) 2020 Vladimir Kondratyev 5 | * Copyright (c) 2020 Greg V 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 | #ifndef _HGAME_H_ 30 | #define _HGAME_H_ 31 | 32 | #include "hidmap.h" 33 | 34 | hidmap_cb_t hgame_hat_switch_cb; 35 | hidmap_cb_t hgame_dpad_cb; 36 | hidmap_cb_t hgame_final_cb; 37 | 38 | struct hgame_softc { 39 | struct hidmap hm; 40 | bool dpad_up; 41 | bool dpad_down; 42 | bool dpad_right; 43 | bool dpad_left; 44 | }; 45 | 46 | #endif /* !_HGAME_H_ */ 47 | -------------------------------------------------------------------------------- /hid.c: -------------------------------------------------------------------------------- 1 | /* $FreeBSD$ */ 2 | /* $NetBSD: hid.c,v 1.17 2001/11/13 06:24:53 lukem Exp $ */ 3 | /*- 4 | * SPDX-License-Identifier: BSD-2-Clause-NetBSD 5 | * 6 | * Copyright (c) 1998 The NetBSD Foundation, Inc. 7 | * All rights reserved. 8 | * 9 | * This code is derived from software contributed to The NetBSD Foundation 10 | * by Lennart Augustsson (lennart@augustsson.net) at 11 | * Carlstedt Research & Technology. 12 | * 13 | * Redistribution and use in source and binary forms, with or without 14 | * modification, are permitted provided that the following conditions 15 | * are met: 16 | * 1. Redistributions of source code must retain the above copyright 17 | * notice, this list of conditions and the following disclaimer. 18 | * 2. Redistributions in binary form must reproduce the above copyright 19 | * notice, this list of conditions and the following disclaimer in the 20 | * documentation and/or other materials provided with the distribution. 21 | * 22 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 23 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 24 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 25 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 26 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 27 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 28 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 29 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 30 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 31 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 32 | * POSSIBILITY OF SUCH DAMAGE. 33 | */ 34 | 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | 43 | #include "hid.h" 44 | #include "hidquirk.h" 45 | 46 | #include "hid_if.h" 47 | 48 | static hid_test_quirk_t hid_test_quirk_w; 49 | hid_test_quirk_t *hid_test_quirk_p = &hid_test_quirk_w; 50 | 51 | int 52 | hid_report_size_1(const void *buf, hid_size_t len, enum hid_kind k, uint8_t id) 53 | { 54 | struct hid_data *d; 55 | struct hid_item h; 56 | uint32_t temp; 57 | uint32_t hpos; 58 | uint32_t lpos; 59 | int report_id = 0; 60 | 61 | hpos = 0; 62 | lpos = 0xFFFFFFFF; 63 | 64 | for (d = hid_start_parse(buf, len, 1 << k); hid_get_item(d, &h);) { 65 | if (h.kind == k && h.report_ID == id) { 66 | /* compute minimum */ 67 | if (lpos > h.loc.pos) 68 | lpos = h.loc.pos; 69 | /* compute end position */ 70 | temp = h.loc.pos + (h.loc.size * h.loc.count); 71 | /* compute maximum */ 72 | if (hpos < temp) 73 | hpos = temp; 74 | if (h.report_ID != 0) 75 | report_id = 1; 76 | } 77 | } 78 | hid_end_parse(d); 79 | 80 | /* safety check - can happen in case of currupt descriptors */ 81 | if (lpos > hpos) 82 | temp = 0; 83 | else 84 | temp = hpos - lpos; 85 | 86 | /* return length in bytes rounded up */ 87 | return ((temp + 7) / 8 + report_id); 88 | } 89 | 90 | /*------------------------------------------------------------------------* 91 | * hid_test_quirk - test a device for a given quirk 92 | * 93 | * Return values: 94 | * false: The HID device does not have the given quirk. 95 | * true: The HID device has the given quirk. 96 | *------------------------------------------------------------------------*/ 97 | bool 98 | hid_test_quirk(const struct hid_device_info *dev_info, uint16_t quirk) 99 | { 100 | bool found; 101 | uint8_t x; 102 | 103 | if (quirk == HQ_NONE) 104 | return (false); 105 | 106 | /* search the automatic per device quirks first */ 107 | for (x = 0; x != HID_MAX_AUTO_QUIRK; x++) { 108 | if (dev_info->autoQuirk[x] == quirk) 109 | return (true); 110 | } 111 | 112 | /* search global quirk table, if any */ 113 | found = (hid_test_quirk_p) (dev_info, quirk); 114 | 115 | return (found); 116 | } 117 | 118 | static bool 119 | hid_test_quirk_w(const struct hid_device_info *dev_info, uint16_t quirk) 120 | { 121 | return (false); /* no match */ 122 | } 123 | 124 | int 125 | hid_add_dynamic_quirk(struct hid_device_info *dev_info, uint16_t quirk) 126 | { 127 | uint8_t x; 128 | 129 | for (x = 0; x != HID_MAX_AUTO_QUIRK; x++) { 130 | if (dev_info->autoQuirk[x] == 0 || 131 | dev_info->autoQuirk[x] == quirk) { 132 | dev_info->autoQuirk[x] = quirk; 133 | return (0); /* success */ 134 | } 135 | } 136 | return (ENOSPC); 137 | } 138 | 139 | void 140 | hidquirk_unload(void *arg) 141 | { 142 | /* reset function pointer */ 143 | hid_test_quirk_p = &hid_test_quirk_w; 144 | #ifdef NOT_YET 145 | hidquirk_ioctl_p = &hidquirk_ioctl_w; 146 | #endif 147 | 148 | /* wait for CPU to exit the loaded functions, if any */ 149 | 150 | /* XXX this is a tradeoff */ 151 | 152 | pause("WAIT", hz); 153 | } 154 | 155 | int 156 | hid_in_polling_mode(void) 157 | { 158 | return (HID_IN_POLLING_MODE_VALUE()); 159 | } 160 | 161 | int 162 | hid_get_rdesc(device_t dev, void *data, hid_size_t len) 163 | { 164 | return (HID_GET_RDESC(device_get_parent(dev), data, len)); 165 | } 166 | 167 | int 168 | hid_read(device_t dev, void *data, hid_size_t maxlen, hid_size_t *actlen) 169 | { 170 | return (HID_READ(device_get_parent(dev), data, maxlen, actlen)); 171 | } 172 | 173 | int 174 | hid_write(device_t dev, const void *data, hid_size_t len) 175 | { 176 | return (HID_WRITE(device_get_parent(dev), data, len)); 177 | } 178 | 179 | int 180 | hid_get_report(device_t dev, void *data, hid_size_t maxlen, hid_size_t *actlen, 181 | uint8_t type, uint8_t id) 182 | { 183 | return (HID_GET_REPORT(device_get_parent(dev), data, maxlen, actlen, 184 | type, id)); 185 | } 186 | 187 | int 188 | hid_set_report(device_t dev, const void *data, hid_size_t len, uint8_t type, 189 | uint8_t id) 190 | { 191 | return (HID_SET_REPORT(device_get_parent(dev), data, len, type, id)); 192 | } 193 | 194 | int 195 | hid_set_idle(device_t dev, uint16_t duration, uint8_t id) 196 | { 197 | return (HID_SET_IDLE(device_get_parent(dev), duration, id)); 198 | } 199 | 200 | int 201 | hid_set_protocol(device_t dev, uint16_t protocol) 202 | { 203 | return (HID_SET_PROTOCOL(device_get_parent(dev), protocol)); 204 | } 205 | 206 | MODULE_DEPEND(hid, usb, 1, 1, 1); 207 | MODULE_VERSION(hid, 1); 208 | -------------------------------------------------------------------------------- /hid.h: -------------------------------------------------------------------------------- 1 | /* $FreeBSD$ */ 2 | /*- 3 | * SPDX-License-Identifier: BSD-2-Clause-NetBSD 4 | * 5 | * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. 6 | * Copyright (c) 1998 The NetBSD Foundation, Inc. All rights reserved. 7 | * Copyright (c) 1998 Lennart Augustsson. All rights reserved. 8 | * 9 | * Redistribution and use in source and binary forms, with or without 10 | * modification, are permitted provided that the following conditions 11 | * are met: 12 | * 1. Redistributions of source code must retain the above copyright 13 | * notice, this list of conditions and the following disclaimer. 14 | * 2. Redistributions in binary form must reproduce the above copyright 15 | * notice, this list of conditions and the following disclaimer in the 16 | * documentation and/or other materials provided with the distribution. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 | * SUCH DAMAGE. 29 | */ 30 | 31 | #ifndef _HID_H_ 32 | #define _HID_H_ 33 | 34 | #include 35 | #include 36 | #include 37 | 38 | /* Declare parent SYSCTL USB node. */ 39 | #ifdef SYSCTL_DECL 40 | SYSCTL_DECL(_hw_hid); 41 | #endif 42 | 43 | #ifndef HUG_MULTIAXIS_CNTROLLER 44 | #define HUG_MULTIAXIS_CNTROLLER 0x0008 45 | #endif 46 | 47 | #ifndef HUG_D_PAD_UP 48 | #define HUG_D_PAD_UP 0x90 49 | #define HUG_D_PAD_DOWN 0x91 50 | #define HUG_D_PAD_RIGHT 0x92 51 | #define HUG_D_PAD_LEFT 0x93 52 | #endif 53 | 54 | #ifndef HUD_SURFACE_SWITCH 55 | #define HUD_SURFACE_SWITCH 0x0057 56 | #endif 57 | #ifndef HUD_BUTTONS_SWITCH 58 | #define HUD_BUTTONS_SWITCH 0x0058 59 | #endif 60 | #ifndef HUD_SEC_BARREL_SWITCH 61 | #define HUD_SEC_BARREL_SWITCH 0x5a 62 | #endif 63 | 64 | #ifndef HUC_CONSUMER_CONTROL 65 | #define HUC_CONSUMER_CONTROL 0x0001 66 | #endif 67 | #ifndef HUC_HEADPHONE 68 | #define HUC_HEADPHONE 0x05 69 | #endif 70 | 71 | #define HID_INPUT_REPORT 0x1 72 | #define HID_OUTPUT_REPORT 0x2 73 | #define HID_FEATURE_REPORT 0x3 74 | 75 | #define HID_MAX_AUTO_QUIRK 8 /* maximum number of dynamic quirks */ 76 | #define HID_PNP_ID_SIZE 20 /* includes null terminator */ 77 | 78 | #define HID_IN_POLLING_MODE_FUNC() hid_in_polling_mode() 79 | #define HID_IN_POLLING_MODE_VALUE() (SCHEDULER_STOPPED() || kdb_active) 80 | 81 | typedef usb_size_t hid_size_t; 82 | 83 | struct hid_absinfo { 84 | int32_t min; 85 | int32_t max; 86 | int32_t res; 87 | }; 88 | 89 | struct hid_device_info { 90 | char name[80]; 91 | char serial[80]; 92 | char idPnP[HID_PNP_ID_SIZE]; 93 | uint16_t idBus; 94 | uint16_t idVendor; 95 | uint16_t idProduct; 96 | uint16_t idVersion; 97 | hid_size_t rdescsize; /* Report descriptor size */ 98 | uint8_t autoQuirk[HID_MAX_AUTO_QUIRK]; 99 | }; 100 | 101 | struct hid_rdesc_info { 102 | void *data; 103 | hid_size_t len; 104 | hid_size_t isize; 105 | hid_size_t osize; 106 | hid_size_t fsize; 107 | uint8_t iid; 108 | uint8_t oid; 109 | uint8_t fid; 110 | /* Max sizes for HID requests supported by transport backend */ 111 | hid_size_t rdsize; 112 | hid_size_t wrsize; 113 | hid_size_t grsize; 114 | hid_size_t srsize; 115 | }; 116 | 117 | /* OpenBSD/NetBSD compat shim */ 118 | #define HID_GET_USAGE(u) ((u) & 0xffff) 119 | #define HID_GET_USAGE_PAGE(u) (((u) >> 16) & 0xffff) 120 | 121 | typedef void hid_intr_t(void *context, void *data, hid_size_t len); 122 | typedef bool hid_test_quirk_t(const struct hid_device_info *dev_info, 123 | uint16_t quirk); 124 | 125 | static __inline uint32_t 126 | hid_get_udata(const uint8_t *buf, hid_size_t len, struct hid_location *loc) 127 | { 128 | return (hid_get_data_unsigned(buf, len, loc)); 129 | } 130 | 131 | extern hid_test_quirk_t *hid_test_quirk_p; 132 | 133 | /* 134 | * hid_report_size_1 is a port of userland hid_report_size() from usbhid(3) 135 | * to kernel. XXX: to be renamed back to hid_report_size() 136 | */ 137 | int hid_report_size_1(const void *buf, hid_size_t len, enum hid_kind k, 138 | uint8_t id); 139 | bool hid_test_quirk(const struct hid_device_info *dev_info, uint16_t quirk); 140 | int hid_add_dynamic_quirk(struct hid_device_info *dev_info, 141 | uint16_t quirk); 142 | void hidquirk_unload(void *arg); 143 | int hid_in_polling_mode(void); 144 | 145 | int hid_get_rdesc(device_t, void *, hid_size_t); 146 | int hid_read(device_t, void *, hid_size_t, hid_size_t *); 147 | int hid_write(device_t, const void *, hid_size_t); 148 | int hid_get_report(device_t, void *, hid_size_t, hid_size_t *, uint8_t, 149 | uint8_t); 150 | int hid_set_report(device_t, const void *, hid_size_t, uint8_t, uint8_t); 151 | int hid_set_idle(device_t, uint16_t, uint8_t); 152 | int hid_set_protocol(device_t, uint16_t); 153 | 154 | #endif /* _HID_H_ */ 155 | -------------------------------------------------------------------------------- /hid_debug.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 | * 4 | * Copyright (c) 2008 Hans Petter Selasky. 5 | * Copyright (c) 2019 Vladimir Kondratyev 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 | * $FreeBSD$ 29 | */ 30 | 31 | #include 32 | #include 33 | #include 34 | 35 | #include "hid_debug.h" 36 | 37 | /* 38 | * Define this unconditionally in case a kernel module is loaded that 39 | * has been compiled with debugging options. 40 | */ 41 | int hid_debug = 0; 42 | 43 | SYSCTL_NODE(_hw, OID_AUTO, hid, CTLFLAG_RW, 0, "HID debugging"); 44 | SYSCTL_INT(_hw_hid, OID_AUTO, debug, CTLFLAG_RWTUN, 45 | &hid_debug, 0, "Debug level"); 46 | -------------------------------------------------------------------------------- /hid_debug.h: -------------------------------------------------------------------------------- 1 | /* $FreeBSD$ */ 2 | /*- 3 | * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 4 | * 5 | * Copyright (c) 2008 Hans Petter Selasky. 6 | * Copyright (c) 2019 Vladimir Kondratyev 7 | * 8 | * Redistribution and use in source and binary forms, with or without 9 | * modification, are permitted provided that the following conditions 10 | * are met: 11 | * 1. Redistributions of source code must retain the above copyright 12 | * notice, this list of conditions and the following disclaimer. 13 | * 2. Redistributions in binary form must reproduce the above copyright 14 | * notice, this list of conditions and the following disclaimer in the 15 | * documentation and/or other materials provided with the distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 | * SUCH DAMAGE. 28 | */ 29 | 30 | /* This file contains various factored out debug macros. */ 31 | 32 | #ifndef _HID_DEBUG_H_ 33 | #define _HID_DEBUG_H_ 34 | 35 | /* Declare global HID debug variable. */ 36 | extern int hid_debug; 37 | 38 | /* Check if HID debugging is enabled. */ 39 | #ifdef HID_DEBUG_VAR 40 | #ifdef HID_DEBUG 41 | #define DPRINTFN(n,fmt,...) do { \ 42 | if ((HID_DEBUG_VAR) >= (n)) { \ 43 | printf("%s: " fmt, \ 44 | __FUNCTION__ ,##__VA_ARGS__); \ 45 | } \ 46 | } while (0) 47 | #define DPRINTF(...) DPRINTFN(1, __VA_ARGS__) 48 | #else 49 | #define DPRINTF(...) do { } while (0) 50 | #define DPRINTFN(...) do { } while (0) 51 | #endif 52 | #endif 53 | 54 | #endif /* _HID_DEBUG_H_ */ 55 | -------------------------------------------------------------------------------- /hid_if.m: -------------------------------------------------------------------------------- 1 | #- 2 | # Copyright (c) 2019 Vladimir Kondratyev 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 1. Redistributions of source code must retain the above copyright 8 | # notice, this list of conditions and the following disclaimer. 9 | # 2. Redistributions in binary form must reproduce the above copyright 10 | # notice, this list of conditions and the following disclaimer in the 11 | # documentation and/or other materials provided with the distribution. 12 | # 13 | # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 17 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 | # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 | # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 | # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 | # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 | # SUCH DAMAGE. 24 | # 25 | # $FreeBSD$ 26 | # 27 | 28 | #include 29 | #include "hid.h" 30 | 31 | INTERFACE hid; 32 | 33 | # Interrupts interface 34 | 35 | # 36 | # Allocate memory and initialise interrupt transfers. 37 | # This function can sleep or block waiting for resources to become available. 38 | # intr callback function which is called if input data is available. 39 | # context is the private softc pointer, which will be used to callback. 40 | # mtx is the private mutex protecting the transfer structure and the softc. 41 | # isize, osize and fsize are requested maximal sizes of input, output and 42 | # feature reports and used to determine sizes of driver internal buffers. 43 | # This function returns zero upon success. A non-zero return value indicates 44 | # failure. 45 | # 46 | METHOD void intr_setup { 47 | device_t dev; 48 | struct mtx *lock; 49 | hid_intr_t intr; 50 | void *context; 51 | struct hid_rdesc_info *rdesc; 52 | }; 53 | 54 | # 55 | # Release all allocated resources associated with interrupt transfers. 56 | # This function can sleep waiting for transfers to complete. 57 | # It is not allowed to call this function from the interrupt callback. 58 | # 59 | METHOD void intr_unsetup { 60 | device_t dev; 61 | }; 62 | 63 | # 64 | # Start the interrupt transfers if not already started. This function is always 65 | # non-blocking and must be called with the so-called private HID mutex locked. 66 | # 67 | METHOD int intr_start { 68 | device_t dev; 69 | }; 70 | 71 | # 72 | # Stop the interrupt transfers if not already stopped. This function is always 73 | # non-blocking and must be called with the so-called private HID mutex locked. 74 | # 75 | METHOD int intr_stop { 76 | device_t dev; 77 | }; 78 | 79 | # 80 | # The following function gets called from the HID keyboard driver 81 | # when the system has paniced. 82 | # 83 | METHOD void intr_poll { 84 | device_t dev; 85 | }; 86 | 87 | # HID interface 88 | 89 | # 90 | # Read out an report descriptor from the HID device. 91 | # 92 | METHOD int get_rdesc { 93 | device_t dev; 94 | void *data; 95 | hid_size_t len; 96 | }; 97 | 98 | # 99 | # Get input data from the device. Data should be read in chunks 100 | # of the size prescribed by the report descriptor. 101 | # 102 | METHOD int read { 103 | device_t dev; 104 | void *data; 105 | hid_size_t maxlen; 106 | hid_size_t *actlen; 107 | }; 108 | 109 | # 110 | # Send data to the device. Data should be written in 111 | # chunks of the size prescribed by the report descriptor. 112 | # 113 | METHOD int write { 114 | device_t dev; 115 | const void *data; 116 | hid_size_t len; 117 | }; 118 | 119 | # 120 | # Get a report from the device without waiting for data on the interrupt. 121 | # Copies a maximum of len bytes of the report data into the memory specified 122 | # by data. Upon return actlen is set to the number of bytes copied. The type 123 | # field indicates which report is requested. It should be HID_INPUT_REPORT, 124 | # HID_OUTPUT_REPORT, or HID_FEATURE_REPORT. This call may fail if the device 125 | # does not support this feature. 126 | # 127 | METHOD int get_report { 128 | device_t dev; 129 | void *data; 130 | hid_size_t maxlen; 131 | hid_size_t *actlen; 132 | uint8_t type; 133 | uint8_t id; 134 | }; 135 | 136 | # 137 | # Set a report in the device. The type field indicates which report is to be 138 | # set. It should be HID_INPUT_REPORT, HID_OUTPUT_REPORT, or HID_FEATURE_REPORT. 139 | # The value of the report is specified by the data and the len fields. 140 | # This call may fail if the device does not support this feature. 141 | # 142 | METHOD int set_report { 143 | device_t dev; 144 | const void *data; 145 | hid_size_t len; 146 | uint8_t type; 147 | uint8_t id; 148 | }; 149 | 150 | # 151 | # Set duration between input reports (in mSec). 152 | # 153 | METHOD int set_idle { 154 | device_t dev; 155 | uint16_t duration; 156 | uint8_t id; 157 | }; 158 | 159 | # 160 | # Switch between the boot protocol and the report protocol (or vice versa). 161 | # 162 | METHOD int set_protocol { 163 | device_t dev; 164 | uint16_t protocol; 165 | }; 166 | -------------------------------------------------------------------------------- /hidbus.4: -------------------------------------------------------------------------------- 1 | .\" Copyright (c) 2020 Vladimir Kondratyev 2 | .\" 3 | .\" Redistribution and use in source and binary forms, with or without 4 | .\" modification, are permitted provided that the following conditions 5 | .\" are met: 6 | .\" 1. Redistributions of source code must retain the above copyright 7 | .\" notice, this list of conditions and the following disclaimer. 8 | .\" 2. Redistributions in binary form must reproduce the above copyright 9 | .\" notice, this list of conditions and the following disclaimer in the 10 | .\" documentation and/or other materials provided with the distribution. 11 | .\" 12 | .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 13 | .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 14 | .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 15 | .\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 16 | .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 17 | .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 18 | .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 19 | .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 20 | .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 21 | .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 22 | .\" SUCH DAMAGE. 23 | .\" 24 | .\" $FreeBSD$ 25 | .\" 26 | .Dd September 14, 2020 27 | .Dt HIDBUS 4 28 | .Os 29 | .Sh NAME 30 | .Nm hidbus 31 | .Nd generic HID bus driver 32 | .Sh SYNOPSIS 33 | To compile this driver into the kernel, 34 | place the following lines in your 35 | kernel configuration file: 36 | .Bd -ragged -offset indent 37 | .Cd "device hidbus" 38 | .Cd "device hid" 39 | .Ed 40 | .Pp 41 | Alternatively, to load the driver as a 42 | module at boot time, place the following line in 43 | .Xr loader.conf 5 : 44 | .Bd -literal -offset indent 45 | hidbus_load="YES" 46 | .Ed 47 | .Sh DESCRIPTION 48 | The 49 | .Nm 50 | driver provides support for multiple HID driver attachments to single HID 51 | transport backend. 52 | See 53 | .Xr iichid 4 54 | or 55 | .Xr usbhid 4 . 56 | .Pp 57 | Each HID device can have several components, e.g., a keyboard and 58 | a mouse. 59 | These components use different report identifiers (a byte) combined into 60 | groups called collections to distinguish which one data is coming from. 61 | The 62 | .Nm 63 | driver has other drivers attached that handle particular 64 | kinds of devices and 65 | .Nm 66 | broadcasts data to all of them. 67 | .Sh SYSCTL VARIABLES 68 | The following variables are available as both 69 | .Xr sysctl 8 70 | variables and 71 | .Xr loader 8 72 | tunables: 73 | .Bl -tag -width indent 74 | .It Va hw.hid.hidbus.debug 75 | Debug output level, where 0 is debugging disabled and larger values increase 76 | debug message verbosity. 77 | Default is 0. 78 | .El 79 | .Sh SEE ALSO 80 | .Xr hconf 4 , 81 | .Xr hcons 4 , 82 | .Xr hgame 4 , 83 | .Xr hidraw 4 , 84 | .Xr hkbd 4 , 85 | .Xr hms 4 , 86 | .Xr hmt 4 , 87 | .Xr hpen 4 , 88 | .Xr hsctrl 4 , 89 | .Xr hskbd 4 , 90 | .Xr iichid 4 , 91 | .Xr usbhid 4 92 | .Sh HISTORY 93 | The 94 | .Nm 95 | driver first appeared in 96 | .Fx 13.0. 97 | .Sh AUTHORS 98 | .An -nosplit 99 | The 100 | .Nm 101 | driver was written by 102 | .An Vladimir Kondratyev Aq Mt wulf@FreeBSD.org . 103 | -------------------------------------------------------------------------------- /hidbus.h: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 2019 Vladimir Kondratyev 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 1. Redistributions of source code must retain the above copyright 8 | * notice, this list of conditions and the following disclaimer. 9 | * 2. Redistributions in binary form must reproduce the above copyright 10 | * notice, this list of conditions and the following disclaimer in the 11 | * documentation and/or other materials provided with the distribution. 12 | * 13 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 17 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 | * SUCH DAMAGE. 24 | */ 25 | 26 | #ifndef _HIDBUS_H_ 27 | #define _HIDBUS_H_ 28 | 29 | enum { 30 | HIDBUS_IVAR_USAGE, 31 | HIDBUS_IVAR_INDEX, 32 | HIDBUS_IVAR_FLAGS, 33 | #define HIDBUS_FLAG_AUTOCHILD (0<<1) /* Child is autodiscovered */ 34 | #define HIDBUS_FLAG_CAN_POLL (1<<1) /* Child can work during panic */ 35 | HIDBUS_IVAR_DRIVER_INFO, 36 | }; 37 | 38 | #define HIDBUS_ACCESSOR(A, B, T) \ 39 | __BUS_ACCESSOR(hidbus, A, HIDBUS, B, T) 40 | 41 | HIDBUS_ACCESSOR(usage, USAGE, int32_t) 42 | HIDBUS_ACCESSOR(index, INDEX, uint8_t) 43 | HIDBUS_ACCESSOR(flags, FLAGS, uint32_t) 44 | HIDBUS_ACCESSOR(driver_info, DRIVER_INFO, uintptr_t) 45 | 46 | /* 47 | * The following structure is used when looking up an HID driver for 48 | * an HID device. It is inspired by the structure called "usb_device_id". 49 | * which is originated in Linux and ported to FreeBSD. 50 | */ 51 | struct hid_device_id { 52 | 53 | /* Select which fields to match against */ 54 | #if BYTE_ORDER == LITTLE_ENDIAN 55 | uint16_t 56 | match_flag_page:1, 57 | match_flag_usage:1, 58 | match_flag_bus:1, 59 | match_flag_vendor:1, 60 | match_flag_product:1, 61 | match_flag_ver_lo:1, 62 | match_flag_ver_hi:1, 63 | match_flag_pnp:1, 64 | match_flag_unused:8; 65 | #else 66 | uint16_t 67 | match_flag_unused:8, 68 | match_flag_pnp:1, 69 | match_flag_ver_hi:1, 70 | match_flag_ver_lo:1, 71 | match_flag_product:1, 72 | match_flag_vendor:1, 73 | match_flag_bus:1, 74 | match_flag_usage:1, 75 | match_flag_page:1; 76 | #endif 77 | 78 | /* Used for top level collection usage matches */ 79 | uint16_t page; 80 | uint16_t usage; 81 | 82 | /* Used for product specific matches; the Version range is inclusive */ 83 | uint8_t idBus; 84 | uint16_t idVendor; 85 | uint16_t idProduct; 86 | uint16_t idVersion_lo; 87 | uint16_t idVersion_hi; 88 | char *idPnP; 89 | 90 | /* Hook for driver specific information */ 91 | uintptr_t driver_info; 92 | }; 93 | 94 | #define HID_STD_PNP_INFO \ 95 | "M16:mask;U16:page;U16:usage;U8:bus;U16:vendor;U16:product;" \ 96 | "L16:version;G16:version;Z:_HID" 97 | #define HID_PNP_INFO(table) \ 98 | MODULE_PNP_INFO(HID_STD_PNP_INFO, hidbus, table, table, nitems(table)) 99 | 100 | #define HID_TLC(pg,usg) \ 101 | .match_flag_page = 1, .match_flag_usage = 1, .page = (pg), .usage = (usg) 102 | 103 | #define HID_BUS(bus) \ 104 | .match_flag_bus = 1, .idBus = (bus) 105 | 106 | #define HID_VENDOR(vend) \ 107 | .match_flag_vendor = 1, .idVendor = (vend) 108 | 109 | #define HID_PRODUCT(prod) \ 110 | .match_flag_product = 1, .idProduct = (prod) 111 | 112 | #define HID_VP(vend,prod) \ 113 | HID_VENDOR(vend), HID_PRODUCT(prod) 114 | 115 | #define HID_BVP(bus,vend,prod) \ 116 | HID_BUS(bus), HID_VENDOR(vend), HID_PRODUCT(prod) 117 | 118 | #define HID_BVPI(bus,vend,prod,info) \ 119 | HID_BUS(bus), HID_VENDOR(vend), HID_PRODUCT(prod), HID_DRIVER_INFO(info) 120 | 121 | #define HID_VERSION_GTEQ(lo) /* greater than or equal */ \ 122 | .match_flag_ver_lo = 1, .idVersion_lo = (lo) 123 | 124 | #define HID_VERSION_LTEQ(hi) /* less than or equal */ \ 125 | .match_flag_ver_hi = 1, .idVersion_hi = (hi) 126 | 127 | #define HID_PNP(pnp) \ 128 | .match_flag_pnp = 1, .idPnP = (pnp) 129 | 130 | #define HID_DRIVER_INFO(n) \ 131 | .driver_info = (n) 132 | 133 | #define HID_GET_DRIVER_INFO(did) \ 134 | (did)->driver_info 135 | 136 | /* 137 | * General purpose locking wrappers to ease supporting 138 | * HID polled mode: 139 | */ 140 | #define HID_SYSCONS_MTX (&Giant) 141 | #ifdef INVARIANTS 142 | #define HID_MTX_ASSERT(_m, _t) do { \ 143 | if (!HID_IN_POLLING_MODE_FUNC()) \ 144 | mtx_assert(_m, _t); \ 145 | } while (0) 146 | #else 147 | #define HID_MTX_ASSERT(_m, _t) do { } while (0) 148 | #endif 149 | 150 | #define HID_MTX_LOCK(_m) do { \ 151 | if (!HID_IN_POLLING_MODE_FUNC()) \ 152 | mtx_lock(_m); \ 153 | } while (0) 154 | 155 | #define HID_MTX_UNLOCK(_m) do { \ 156 | if (!HID_IN_POLLING_MODE_FUNC()) \ 157 | mtx_unlock(_m); \ 158 | } while (0) 159 | 160 | #define HIDBUS_LOOKUP_ID(d, h) hidbus_lookup_id((d), (h), nitems(h)) 161 | #define HIDBUS_LOOKUP_DRIVER_INFO(d, h) \ 162 | hidbus_lookup_driver_info((d), (h), nitems(h)) 163 | 164 | /* 165 | * Walk through all HID items hi belonging Top Level Collection #tlc_index 166 | */ 167 | #define HIDBUS_FOREACH_ITEM(hd, hi, tlc_index) \ 168 | for (uint8_t _iter = 0; \ 169 | _iter <= (tlc_index) && hid_get_item((hd), (hi)); \ 170 | _iter += (hi)->kind == hid_endcollection && (hi)->collevel == 0) \ 171 | if (_iter == (tlc_index)) 172 | 173 | int hidbus_locate(const void *desc, hid_size_t size, int32_t u, 174 | enum hid_kind k, uint8_t tlc_index, uint8_t index, 175 | struct hid_location *loc, uint32_t *flags, uint8_t *id, 176 | struct hid_absinfo *ai); 177 | 178 | const struct hid_device_id *hidbus_lookup_id(device_t, 179 | const struct hid_device_id *, int); 180 | struct hid_rdesc_info *hidbus_get_rdesc_info(device_t); 181 | int hidbus_lookup_driver_info(device_t, 182 | const struct hid_device_id *, int); 183 | struct mtx * hidbus_get_lock(device_t); 184 | void hidbus_set_intr(device_t, hid_intr_t*, void *); 185 | int hidbus_intr_start(device_t); 186 | int hidbus_intr_stop(device_t); 187 | void hidbus_intr_poll(device_t); 188 | void hidbus_set_desc(device_t, const char *); 189 | device_t hidbus_find_child(device_t, int32_t); 190 | 191 | /* hidbus HID interface */ 192 | int hid_get_report_descr(device_t, void **, hid_size_t *); 193 | int hid_set_report_descr(device_t, const void *, hid_size_t); 194 | 195 | const struct hid_device_info *hid_get_device_info(device_t); 196 | 197 | extern devclass_t hidbus_devclass; 198 | 199 | #endif /* _HIDBUS_H_ */ 200 | -------------------------------------------------------------------------------- /hidmap.h: -------------------------------------------------------------------------------- 1 | /*- 2 | * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 | * 4 | * Copyright (c) 2020 Vladimir Kondratyev 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 | * SUCH DAMAGE. 26 | */ 27 | 28 | #ifndef _HIDMAP_H_ 29 | #define _HIDMAP_H_ 30 | 31 | #include 32 | 33 | #include "hid.h" 34 | 35 | #define HIDMAP_MAX_MAPS 4 36 | 37 | struct hid_device_id; 38 | struct hidmap_hid_item; 39 | struct hidmap_item; 40 | struct hidmap; 41 | 42 | enum hidmap_cb_state { 43 | HIDMAP_CB_IS_PROBING, 44 | HIDMAP_CB_IS_ATTACHING, 45 | HIDMAP_CB_IS_RUNNING, 46 | HIDMAP_CB_IS_DETACHING, 47 | }; 48 | 49 | #define HIDMAP_KEY_NULL 0xFF /* Special event code to discard input */ 50 | 51 | /* Third parameter of hidmap callback has different type depending on state */ 52 | union hidmap_cb_ctx { 53 | struct hid_item *hi; /* Probe- and attach-stage callbacks */ 54 | int32_t data; /* Run-stage callbacks */ 55 | uint8_t rid; /* Run-stage finalizing callbacks */ 56 | }; 57 | 58 | #define HIDMAP_CB_ARGS \ 59 | struct hidmap *hm, struct hidmap_hid_item *hi, union hidmap_cb_ctx ctx 60 | typedef int hidmap_cb_t(HIDMAP_CB_ARGS); 61 | 62 | /* These helpers can be used at any stage of any callbacks */ 63 | #define HIDMAP_CB_GET_STATE(...) \ 64 | ((hm == NULL) ? HIDMAP_CB_IS_PROBING : hm->cb_state) 65 | #define HIDMAP_CB_GET_SOFTC(...) \ 66 | (hm == NULL ? NULL : device_get_softc(hm->dev)) 67 | #define HIDMAP_CB_GET_EVDEV(...) \ 68 | (hm == NULL ? NULL : hm->evdev) 69 | #define HIDMAP_CB_UDATA (hi->udata) 70 | #define HIDMAP_CB_UDATA64 (hi->udata64) 71 | /* Special helpers for run-stage of finalizing callbacks */ 72 | #define HIDMAP_CB_GET_RID(...) (ctx.rid) 73 | #define HIDMAP_CB_GET_DATA(loc) \ 74 | hid_get_data(hm->intr_buf, hm->intr_len, (loc)) 75 | #define HIDMAP_CB_GET_UDATA(loc) \ 76 | hid_get_udata(hm->intr_buf, hm->intr_len, (loc)) 77 | 78 | enum hidmap_relabs { 79 | HIDMAP_RELABS_ANY = 0, 80 | HIDMAP_RELATIVE, 81 | HIDMAP_ABSOLUTE, 82 | }; 83 | 84 | struct hidmap_item { 85 | union { 86 | struct { 87 | uint16_t type; /* Evdev event type */ 88 | uint16_t code; /* Evdev event code */ 89 | uint16_t fuzz; /* Evdev event fuzz */ 90 | uint16_t flat; /* Evdev event flat */ 91 | }; 92 | hidmap_cb_t *cb; /* Reporting callback */ 93 | }; 94 | int32_t usage; /* HID usage (base) */ 95 | uint16_t nusages; /* number of usages */ 96 | bool required:1; /* Required by driver */ 97 | enum hidmap_relabs relabs:2; 98 | bool has_cb:1; 99 | bool final_cb:1; 100 | bool invert_value:1; 101 | u_int reserved:10; 102 | }; 103 | 104 | #define HIDMAP_ANY(_page, _usage, _type, _code) \ 105 | .usage = HID_USAGE2((_page), (_usage)), \ 106 | .nusages = 1, \ 107 | .type = (_type), \ 108 | .code = (_code) 109 | #define HIDMAP_ANY_RANGE(_page, _usage_from, _usage_to, _type, _code) \ 110 | .usage = HID_USAGE2((_page), (_usage_from)), \ 111 | .nusages = (_usage_to) - (_usage_from) + 1, \ 112 | .type = (_type), \ 113 | .code = (_code) 114 | #define HIDMAP_ANY_CB(_page, _usage, _callback) \ 115 | .usage = HID_USAGE2((_page), (_usage)), \ 116 | .nusages = 1, \ 117 | .cb = (_callback), \ 118 | .has_cb = true 119 | #define HIDMAP_ANY_CB_RANGE(_page, _usage_from, _usage_to, _callback) \ 120 | .usage = HID_USAGE2((_page), (_usage_from)), \ 121 | .nusages = (_usage_to) - (_usage_from) + 1, \ 122 | .cb = (_callback), \ 123 | .has_cb = true 124 | #define HIDMAP_KEY(_page, _usage, _code) \ 125 | HIDMAP_ANY((_page), (_usage), EV_KEY, (_code)), \ 126 | .relabs = HIDMAP_RELABS_ANY 127 | #define HIDMAP_KEY_RANGE(_page, _ufrom, _uto, _code) \ 128 | HIDMAP_ANY_RANGE((_page), (_ufrom), (_uto), EV_KEY, (_code)), \ 129 | .relabs = HIDMAP_RELABS_ANY 130 | #define HIDMAP_REL(_page, _usage, _code) \ 131 | HIDMAP_ANY((_page), (_usage), EV_REL, (_code)), \ 132 | .relabs = HIDMAP_RELATIVE 133 | #define HIDMAP_ABS(_page, _usage, _code) \ 134 | HIDMAP_ANY((_page), (_usage), EV_ABS, (_code)), \ 135 | .relabs = HIDMAP_ABSOLUTE 136 | #define HIDMAP_SW(_page, _usage, _code) \ 137 | HIDMAP_ANY((_page), (_usage), EV_SW, (_code)), \ 138 | .relabs = HIDMAP_RELABS_ANY 139 | #define HIDMAP_REL_CB(_page, _usage, _callback) \ 140 | HIDMAP_ANY_CB((_page), (_usage), (_callback)), \ 141 | .relabs = HIDMAP_RELATIVE 142 | #define HIDMAP_ABS_CB(_page, _usage, _callback) \ 143 | HIDMAP_ANY_CB((_page), (_usage), (_callback)), \ 144 | .relabs = HIDMAP_ABSOLUTE 145 | /* 146 | * Special callback function which is not tied to particular HID input usage 147 | * but called at the end evdev properties setting or interrupt handler 148 | * just before evdev_register() or evdev_sync() calls. 149 | */ 150 | #define HIDMAP_FINAL_CB(_callback) \ 151 | HIDMAP_ANY_CB(0, 0, (_callback)), .final_cb = true 152 | 153 | enum hidmap_type { 154 | HIDMAP_TYPE_FINALCB = 0,/* No HID item associated. Runs unconditionally 155 | * at the end of other items processing */ 156 | HIDMAP_TYPE_CALLBACK, /* HID item is reported with user callback */ 157 | HIDMAP_TYPE_VARIABLE, /* HID item is variable (single usage) */ 158 | HIDMAP_TYPE_VAR_NULLST, /* HID item is null state variable */ 159 | HIDMAP_TYPE_ARR_LIST, /* HID item is array with list of usages */ 160 | HIDMAP_TYPE_ARR_RANGE, /* Array with range (min;max) of usages */ 161 | }; 162 | 163 | struct hidmap_hid_item { 164 | union { 165 | hidmap_cb_t *cb; /* Callback */ 166 | struct { /* Variable */ 167 | uint16_t evtype; /* Evdev event type */ 168 | uint16_t code; /* Evdev event code */ 169 | }; 170 | uint16_t *codes; /* Array list map type */ 171 | int32_t umin; /* Array range map type */ 172 | }; 173 | union { 174 | void *udata; /* Callback private context */ 175 | uint64_t udata64; 176 | int32_t last_val; /* Last reported value (var) */ 177 | uint16_t last_key; /* Last reported key (array) */ 178 | }; 179 | struct hid_location loc; /* HID item location */ 180 | int32_t lmin; /* HID item logical minimum */ 181 | int32_t lmax; /* HID item logical maximum */ 182 | enum hidmap_type type:8; 183 | uint8_t id; /* Report ID */ 184 | bool invert_value; 185 | }; 186 | 187 | struct hidmap { 188 | device_t dev; 189 | 190 | struct evdev_dev *evdev; 191 | struct evdev_methods evdev_methods; 192 | 193 | /* Scatter-gather list of maps */ 194 | int nmaps; 195 | uint32_t nmap_items[HIDMAP_MAX_MAPS]; 196 | const struct hidmap_item *map[HIDMAP_MAX_MAPS]; 197 | 198 | /* List of preparsed HID items */ 199 | uint32_t nhid_items; 200 | struct hidmap_hid_item *hid_items; 201 | 202 | /* Key event merging buffers */ 203 | uint8_t *key_press; 204 | uint8_t *key_rel; 205 | uint16_t key_min; 206 | uint16_t key_max; 207 | 208 | int *debug_var; 209 | int debug_level; 210 | enum hidmap_cb_state cb_state; 211 | void * intr_buf; 212 | hid_size_t intr_len; 213 | }; 214 | 215 | typedef uint8_t * hidmap_caps_t; 216 | #define HIDMAP_CAPS_SZ(nitems) (((nitems) + 7) / 8) 217 | #define HIDMAP_CAPS(name, map) uint8_t (name)[HIDMAP_CAPS_SZ(nitems(map))] 218 | static inline bool 219 | hidmap_test_cap(hidmap_caps_t caps, int cap) 220 | { 221 | return (isset(caps, cap) != 0); 222 | } 223 | 224 | /* 225 | * It is safe to call any of following procedures in device_probe context 226 | * that makes possible to write probe-only drivers with attach/detach handlers 227 | * inherited from hidmap. See hcons and hsctrl drivers for example. 228 | */ 229 | static inline void 230 | hidmap_set_dev(struct hidmap *hm, device_t dev) 231 | { 232 | hm->dev = dev; 233 | } 234 | 235 | /* Hack to avoid #ifdef-ing of hidmap_set_debug_var in hidmap based drivers */ 236 | #ifdef HID_DEBUG 237 | #define hidmap_set_debug_var(h, d) _hidmap_set_debug_var((h), (d)) 238 | #else 239 | #define hidmap_set_debug_var(...) 240 | #endif 241 | void _hidmap_set_debug_var(struct hidmap *hm, int *debug_var); 242 | #define HIDMAP_ADD_MAP(hm, map, caps) \ 243 | hidmap_add_map((hm), (map), nitems(map), (caps)) 244 | uint32_t hidmap_add_map(struct hidmap *hm, 245 | const struct hidmap_item *map, int nitems_map, 246 | hidmap_caps_t caps); 247 | 248 | /* Versions of evdev_* functions capable to merge key events with same codes */ 249 | void hidmap_support_key(struct hidmap *hm, uint16_t key); 250 | void hidmap_push_key(struct hidmap *hm, uint16_t key, int32_t value); 251 | 252 | void hidmap_intr(void *context, void *buf, hid_size_t len); 253 | #define HIDMAP_PROBE(hm, dev, id, map, suffix) \ 254 | hidmap_probe((hm), (dev), (id), nitems(id), (map), nitems(map), \ 255 | (suffix), NULL) 256 | int hidmap_probe(struct hidmap* hm, device_t dev, 257 | const struct hid_device_id *id, int nitems_id, 258 | const struct hidmap_item *map, int nitems_map, 259 | const char *suffix, hidmap_caps_t caps); 260 | int hidmap_attach(struct hidmap *hm); 261 | int hidmap_detach(struct hidmap *hm); 262 | 263 | #endif /* _HIDMAP_H_ */ 264 | -------------------------------------------------------------------------------- /hidquirk.4: -------------------------------------------------------------------------------- 1 | .\" 2 | .\" Copyright (c) 2010 AnyWi Technologies 3 | .\" All rights reserved. 4 | .\" 5 | .\" Permission to use, copy, modify, and distribute this software for any 6 | .\" purpose with or without fee is hereby granted, provided that the above 7 | .\" copyright notice and this permission notice appear in all copies. 8 | .\" 9 | .\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | .\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | .\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | .\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | .\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | .\" 17 | .\" $FreeBSD$ 18 | .\" 19 | .Dd September 16, 2020 20 | .Dt HIDQUIRK 4 21 | .Os 22 | .Sh NAME 23 | .Nm hidquirk 24 | .Nd HID quirks module 25 | .Sh SYNOPSIS 26 | To compile this module into the kernel, 27 | place the following line in your 28 | kernel configuration file: 29 | .Bd -ragged -offset indent 30 | .Cd "device hid" 31 | .Ed 32 | .Pp 33 | Alternatively, to load the module at boot 34 | time, place the following line in 35 | .Xr loader.conf 5 : 36 | .Bd -literal -offset indent 37 | hidquirk_load="YES" 38 | .Ed 39 | .Sh DESCRIPTION 40 | The 41 | .Nm 42 | module provides support for adding quirks for HID devices 43 | .Bl -tag -width Ds 44 | .It HQ_HID_IGNORE 45 | device should be ignored by hid class 46 | .It HQ_KBD_BOOTPROTO 47 | device should set the boot protocol 48 | .It HQ_MS_BOOTPROTO 49 | device should set the boot protocol 50 | .It HQ_MS_BAD_CLASS 51 | doesn't identify properly 52 | .It HQ_MS_LEADING_BYTE 53 | mouse sends an unknown leading byte 54 | .It HQ_MS_REVZ 55 | mouse has Z-axis reversed 56 | .It HQ_SPUR_BUT_UP 57 | spurious mouse button up events 58 | .It HQ_MT_TIMESTAMP 59 | Multitouch device exports HW timestamps 60 | .Dv 0x1b5a01 61 | .El 62 | .Pp 63 | See 64 | .Pa /sys/dev/hid/hidquirk.h 65 | for the complete list of supported quirks. 66 | .Sh LOADER TUNABLE 67 | The following tunable can be set at the 68 | .Xr loader 8 69 | prompt before booting the kernel, or stored in 70 | .Xr loader.conf 5 . 71 | .Bl -tag -width indent 72 | .It Va hw.hid.quirk.%d 73 | The value is a string whose format is: 74 | .Bd -literal -offset indent 75 | .Qo BusId VendorId ProductId LowRevision HighRevision HQ_QUIRK,... Qc 76 | .Ed 77 | .Pp 78 | Installs the quirks 79 | .Ic HQ_QUIRK,... 80 | for all HID devices matching 81 | .Ic BusId 82 | and 83 | .Ic VendorId 84 | and 85 | .Ic ProductId 86 | which have a hardware revision between and including 87 | .Ic LowRevision 88 | and 89 | .Ic HighRevision . 90 | .Pp 91 | .Ic BusId , 92 | .Ic VendorId , 93 | .Ic ProductId , 94 | .Ic LowRevision 95 | and 96 | .Ic HighRevision 97 | are all 16 bits numbers which can be decimal or hexadecimal based. 98 | .Pp 99 | A maximum of 100 variables 100 | .Ic hw.hid.quirk.0, .1, ..., .99 101 | can be defined. 102 | .Pp 103 | If a matching entry is found in the kernel's internal quirks table, it 104 | is replaced by the new definition. 105 | .Pp 106 | Else a new entry is created given that the quirk table is not full. 107 | .Pp 108 | The kernel iterates over the 109 | .Ic hw.hid.quirk.N 110 | variables starting at 111 | .Ic N = 0 112 | and stops at 113 | .Ic N = 99 114 | or the first non-existing one. 115 | .El 116 | .Sh EXAMPLES 117 | To install a quirk at boot time, place one or several lines like the 118 | following in 119 | .Xr loader.conf 5 : 120 | .Bd -literal -offset indent 121 | hw.hid.quirk.0="0x18 0x6cb 0x1941 0 0xffff HQ_MT_TIMESTAMP" 122 | .Ed 123 | .Sh HISTORY 124 | The 125 | .Nm 126 | module appeared in 127 | .Fx 13.0 . 128 | .Sh AUTHORS 129 | .An -nosplit 130 | The 131 | .Nm 132 | driver was written by 133 | .An Hans Petter Selasky Aq Mt hselasky@FreeBSD.org 134 | for 135 | .Xr usb 4 136 | subsystem and adopted to 137 | .Xr hid 4 138 | by 139 | .An Vladimir Kondratyev Aq Mt wulf@FreeBSD.org . 140 | This manual page is based on 141 | .Xr usb_quirk 4 142 | manual page written by 143 | .An Nick Hibma Aq Mt n_hibma@FreeBSD.org . 144 | -------------------------------------------------------------------------------- /hidquirk.c: -------------------------------------------------------------------------------- 1 | /* $FreeBSD$ */ 2 | /*- 3 | * SPDX-License-Identifier: BSD-2-Clause-NetBSD 4 | * 5 | * Copyright (c) 1998 The NetBSD Foundation, Inc. All rights reserved. 6 | * Copyright (c) 1998 Lennart Augustsson. All rights reserved. 7 | * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. 8 | * Copyright (c) 2020 Vladimir Kondratyev 9 | * 10 | * Redistribution and use in source and binary forms, with or without 11 | * modification, are permitted provided that the following conditions 12 | * are met: 13 | * 1. Redistributions of source code must retain the above copyright 14 | * notice, this list of conditions and the following disclaimer. 15 | * 2. Redistributions in binary form must reproduce the above copyright 16 | * notice, this list of conditions and the following disclaimer in the 17 | * documentation and/or other materials provided with the distribution. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 20 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 23 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 | * SUCH DAMAGE. 30 | */ 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | #include 50 | 51 | #include 52 | 53 | #include "hid.h" 54 | #include "hidbus.h" 55 | #include "hidquirk.h" 56 | #include "usbdevs.h" 57 | 58 | #define HID_DEBUG_VAR hid_debug 59 | #include "hid_debug.h" 60 | 61 | 62 | MODULE_DEPEND(hidquirk, hid, 1, 1, 1); 63 | MODULE_VERSION(hidquirk, 1); 64 | 65 | #define HID_DEV_QUIRKS_MAX 384 66 | #define HID_SUB_QUIRKS_MAX 8 67 | #define HID_QUIRK_ENVROOT "hw.hid.quirk." 68 | 69 | struct hidquirk_entry { 70 | uint16_t bus; 71 | uint16_t vid; 72 | uint16_t pid; 73 | uint16_t lo_rev; 74 | uint16_t hi_rev; 75 | uint16_t quirks[HID_SUB_QUIRKS_MAX]; 76 | }; 77 | 78 | static struct mtx hidquirk_mtx; 79 | 80 | #define HID_QUIRK_VP(b,v,p,l,h,...) \ 81 | { .bus = (b), .vid = (v), .pid = (p), .lo_rev = (l), .hi_rev = (h), \ 82 | .quirks = { __VA_ARGS__ } } 83 | #define USB_QUIRK(v,p,l,h,...) \ 84 | HID_QUIRK_VP(BUS_USB, USB_VENDOR_##v, USB_PRODUCT_##v##_##p, l, h, __VA_ARGS__) 85 | 86 | #ifndef USB_VENDOR_MICROCHIP 87 | #define USB_VENDOR_MICROCHIP USB_VENDOR_ITUNERNET 88 | #endif 89 | #ifndef USB_PRODUCT_MICROCHIP_PICOLCD20X2 90 | #define USB_PRODUCT_MICROCHIP_PICOLCD20X2 USB_PRODUCT_ITUNERNET_USBLCD2X20 91 | #endif 92 | #ifndef USB_PRODUCT_MICROCHIP_PICOLCD4X20 93 | #define USB_PRODUCT_MICROCHIP_PICOLCD4X20 USB_PRODUCT_ITUNERNET_USBLCD4X20 94 | #endif 95 | 96 | static struct hidquirk_entry hidquirks[HID_DEV_QUIRKS_MAX] = { 97 | USB_QUIRK(ASUS, LCM, 0x0000, 0xffff, HQ_HID_IGNORE), 98 | USB_QUIRK(QTRONIX, 980N, 0x110, 0x110, HQ_SPUR_BUT_UP), 99 | USB_QUIRK(ALCOR2, KBD_HUB, 0x001, 0x001, HQ_SPUR_BUT_UP), 100 | USB_QUIRK(LOGITECH, G510S, 0x0000, 0xFFFF, HQ_KBD_BOOTPROTO), 101 | /* Devices which should be ignored by usbhid */ 102 | USB_QUIRK(APC, UPS, 0x0000, 0xffff, HQ_HID_IGNORE), 103 | USB_QUIRK(BELKIN, F6H375USB, 0x0000, 0xffff, HQ_HID_IGNORE), 104 | USB_QUIRK(BELKIN, F6C550AVR, 0x0000, 0xffff, HQ_HID_IGNORE), 105 | USB_QUIRK(BELKIN, F6C1250TWRK, 0x0000, 0xffff, HQ_HID_IGNORE), 106 | USB_QUIRK(BELKIN, F6C1500TWRK, 0x0000, 0xffff, HQ_HID_IGNORE), 107 | USB_QUIRK(BELKIN, F6C900UNV, 0x0000, 0xffff, HQ_HID_IGNORE), 108 | USB_QUIRK(BELKIN, F6C100UNV, 0x0000, 0xffff, HQ_HID_IGNORE), 109 | USB_QUIRK(BELKIN, F6C120UNV, 0x0000, 0xffff, HQ_HID_IGNORE), 110 | USB_QUIRK(BELKIN, F6C800UNV, 0x0000, 0xffff, HQ_HID_IGNORE), 111 | USB_QUIRK(BELKIN, F6C1100UNV, 0x0000, 0xffff, HQ_HID_IGNORE), 112 | USB_QUIRK(CYBERPOWER, BC900D, 0x0000, 0xffff, HQ_HID_IGNORE), 113 | USB_QUIRK(CYBERPOWER, 1500CAVRLCD, 0x0000, 0xffff, HQ_HID_IGNORE), 114 | USB_QUIRK(CYBERPOWER, OR2200LCDRM2U, 0x0000, 0xffff, HQ_HID_IGNORE), 115 | USB_QUIRK(DELL2, VARIOUS_UPS, 0x0000, 0xffff, HQ_HID_IGNORE), 116 | USB_QUIRK(CYPRESS, SILVERSHIELD, 0x0000, 0xffff, HQ_HID_IGNORE), 117 | USB_QUIRK(DELORME, EARTHMATE, 0x0000, 0xffff, HQ_HID_IGNORE), 118 | USB_QUIRK(DREAMLINK, DL100B, 0x0000, 0xffff, HQ_HID_IGNORE), 119 | USB_QUIRK(MICROCHIP, PICOLCD20X2, 0x0000, 0xffff, HQ_HID_IGNORE), 120 | USB_QUIRK(MICROCHIP, PICOLCD4X20, 0x0000, 0xffff, HQ_HID_IGNORE), 121 | USB_QUIRK(LIEBERT, POWERSURE_PXT, 0x0000, 0xffff, HQ_HID_IGNORE), 122 | USB_QUIRK(LIEBERT2, PSI1000, 0x0000, 0xffff, HQ_HID_IGNORE), 123 | USB_QUIRK(LIEBERT2, POWERSURE_PSA, 0x0000, 0xffff, HQ_HID_IGNORE), 124 | USB_QUIRK(MGE, UPS1, 0x0000, 0xffff, HQ_HID_IGNORE), 125 | USB_QUIRK(MGE, UPS2, 0x0000, 0xffff, HQ_HID_IGNORE), 126 | USB_QUIRK(POWERCOM, IMPERIAL_SERIES, 0x0000, 0xffff, HQ_HID_IGNORE), 127 | USB_QUIRK(POWERCOM, SMART_KING_PRO, 0x0000, 0xffff, HQ_HID_IGNORE), 128 | USB_QUIRK(POWERCOM, WOW, 0x0000, 0xffff, HQ_HID_IGNORE), 129 | USB_QUIRK(POWERCOM, VANGUARD, 0x0000, 0xffff, HQ_HID_IGNORE), 130 | USB_QUIRK(POWERCOM, BLACK_KNIGHT_PRO, 0x0000, 0xffff, HQ_HID_IGNORE), 131 | USB_QUIRK(TRIPPLITE2, AVR550U, 0x0000, 0xffff, HQ_HID_IGNORE), 132 | USB_QUIRK(TRIPPLITE2, AVR750U, 0x0000, 0xffff, HQ_HID_IGNORE), 133 | USB_QUIRK(TRIPPLITE2, ECO550UPS, 0x0000, 0xffff, HQ_HID_IGNORE), 134 | USB_QUIRK(TRIPPLITE2, T750_INTL, 0x0000, 0xffff, HQ_HID_IGNORE), 135 | USB_QUIRK(TRIPPLITE2, RT_2200_INTL, 0x0000, 0xffff, HQ_HID_IGNORE), 136 | USB_QUIRK(TRIPPLITE2, OMNI1000LCD, 0x0000, 0xffff, HQ_HID_IGNORE), 137 | USB_QUIRK(TRIPPLITE2, OMNI900LCD, 0x0000, 0xffff, HQ_HID_IGNORE), 138 | USB_QUIRK(TRIPPLITE2, SMART_2200RMXL2U, 0x0000, 0xffff, HQ_HID_IGNORE), 139 | USB_QUIRK(TRIPPLITE2, UPS_3014, 0x0000, 0xffff, HQ_HID_IGNORE), 140 | USB_QUIRK(TRIPPLITE2, SU1500RTXL2UA, 0x0000, 0xffff, HQ_HID_IGNORE), 141 | USB_QUIRK(TRIPPLITE2, SU6000RT4U, 0x0000, 0xffff, HQ_HID_IGNORE), 142 | USB_QUIRK(TRIPPLITE2, SU1500RTXL2UA_2, 0x0000, 0xffff, HQ_HID_IGNORE), 143 | USB_QUIRK(APPLE, IPHONE, 0x0000, 0xffff, HQ_HID_IGNORE), 144 | USB_QUIRK(APPLE, IPHONE_3G, 0x0000, 0xffff, HQ_HID_IGNORE), 145 | USB_QUIRK(MEGATEC, UPS, 0x0000, 0xffff, HQ_HID_IGNORE), 146 | /* Devices which should be ignored by both ukbd and uhid */ 147 | USB_QUIRK(CYPRESS, WISPY1A, 0x0000, 0xffff, HQ_HID_IGNORE), 148 | USB_QUIRK(METAGEEK, WISPY1B, 0x0000, 0xffff, HQ_HID_IGNORE), 149 | USB_QUIRK(METAGEEK, WISPY24X, 0x0000, 0xffff, HQ_HID_IGNORE), 150 | USB_QUIRK(METAGEEK2, WISPYDBX, 0x0000, 0xffff, HQ_HID_IGNORE), 151 | /* MS keyboards do weird things */ 152 | USB_QUIRK(MICROSOFT, NATURAL4000, 0x0000, 0xFFFF, HQ_KBD_BOOTPROTO), 153 | USB_QUIRK(MICROSOFT, WLINTELLIMOUSE, 0x0000, 0xffff, HQ_MS_LEADING_BYTE), 154 | /* Quirk for Corsair Vengeance K60 keyboard */ 155 | USB_QUIRK(CORSAIR, K60, 0x0000, 0xffff, HQ_KBD_BOOTPROTO), 156 | /* Quirk for Corsair Gaming K68 keyboard */ 157 | USB_QUIRK(CORSAIR, K68, 0x0000, 0xffff, HQ_KBD_BOOTPROTO), 158 | /* Quirk for Corsair Vengeance K70 keyboard */ 159 | USB_QUIRK(CORSAIR, K70, 0x0000, 0xffff, HQ_KBD_BOOTPROTO), 160 | /* Quirk for Corsair K70 RGB keyboard */ 161 | USB_QUIRK(CORSAIR, K70_RGB, 0x0000, 0xffff, HQ_KBD_BOOTPROTO), 162 | /* Quirk for Corsair STRAFE Gaming keyboard */ 163 | USB_QUIRK(CORSAIR, STRAFE, 0x0000, 0xffff, HQ_KBD_BOOTPROTO), 164 | USB_QUIRK(CORSAIR, STRAFE2, 0x0000, 0xffff, HQ_KBD_BOOTPROTO), 165 | /* Holtek USB gaming keyboard */ 166 | USB_QUIRK(HOLTEK, F85, 0x0000, 0xffff, HQ_KBD_BOOTPROTO), 167 | }; 168 | #undef HID_QUIRK_VP 169 | #undef USB_QUIRK 170 | 171 | /* hidquirk.h exposes only HID_QUIRK_LIST macro when HQ() is defined */ 172 | #define HQ(x) [HQ_##x] = "HQ_"#x 173 | #include "hidquirk.h" 174 | static const char *hidquirk_str[HID_QUIRK_MAX] = { HID_QUIRK_LIST() }; 175 | #undef HQ 176 | 177 | static hid_test_quirk_t hid_test_quirk_by_info; 178 | 179 | /*------------------------------------------------------------------------* 180 | * hidquirkstr 181 | * 182 | * This function converts an USB quirk code into a string. 183 | *------------------------------------------------------------------------*/ 184 | static const char * 185 | hidquirkstr(uint16_t quirk) 186 | { 187 | return ((quirk < HID_QUIRK_MAX && hidquirk_str[quirk] != NULL) ? 188 | hidquirk_str[quirk] : "HQ_UNKNOWN"); 189 | } 190 | 191 | /*------------------------------------------------------------------------* 192 | * hid_strquirk 193 | * 194 | * This function converts a string into a HID quirk code. 195 | * 196 | * Returns: 197 | * Less than HID_QUIRK_MAX: Quirk code 198 | * Else: Quirk code not found 199 | *------------------------------------------------------------------------*/ 200 | static uint16_t 201 | hid_strquirk(const char *str, size_t len) 202 | { 203 | const char *quirk; 204 | uint16_t x; 205 | 206 | for (x = 0; x != HID_QUIRK_MAX; x++) { 207 | quirk = hidquirkstr(x); 208 | if (strncmp(str, quirk, len) == 0 && 209 | quirk[len] == 0) 210 | break; 211 | } 212 | return (x); 213 | } 214 | 215 | /*------------------------------------------------------------------------* 216 | * hid_test_quirk_by_info 217 | * 218 | * Returns: 219 | * false: Quirk not found 220 | * true: Quirk found 221 | *------------------------------------------------------------------------*/ 222 | bool 223 | hid_test_quirk_by_info(const struct hid_device_info *info, uint16_t quirk) 224 | { 225 | uint16_t x; 226 | uint16_t y; 227 | 228 | if (quirk == HQ_NONE) 229 | goto done; 230 | 231 | HID_MTX_LOCK(&hidquirk_mtx); 232 | 233 | for (x = 0; x != HID_DEV_QUIRKS_MAX; x++) { 234 | /* see if quirk information does not match */ 235 | if ((hidquirks[x].bus != info->idBus) || 236 | (hidquirks[x].vid != info->idVendor) || 237 | (hidquirks[x].lo_rev > info->idVersion) || 238 | (hidquirks[x].hi_rev < info->idVersion)) { 239 | continue; 240 | } 241 | /* see if quirk only should match vendor ID */ 242 | if (hidquirks[x].pid != info->idProduct) { 243 | if (hidquirks[x].pid != 0) 244 | continue; 245 | 246 | for (y = 0; y != HID_SUB_QUIRKS_MAX; y++) { 247 | if (hidquirks[x].quirks[y] == HQ_MATCH_VENDOR_ONLY) 248 | break; 249 | } 250 | if (y == HID_SUB_QUIRKS_MAX) 251 | continue; 252 | } 253 | /* lookup quirk */ 254 | for (y = 0; y != HID_SUB_QUIRKS_MAX; y++) { 255 | if (hidquirks[x].quirks[y] == quirk) { 256 | HID_MTX_UNLOCK(&hidquirk_mtx); 257 | DPRINTF("Found quirk '%s'.\n", hidquirkstr(quirk)); 258 | return (true); 259 | } 260 | } 261 | } 262 | HID_MTX_UNLOCK(&hidquirk_mtx); 263 | done: 264 | return (false); /* no quirk match */ 265 | } 266 | 267 | static struct hidquirk_entry * 268 | hidquirk_get_entry(uint16_t bus, uint16_t vid, uint16_t pid, 269 | uint16_t lo_rev, uint16_t hi_rev, uint8_t do_alloc) 270 | { 271 | uint16_t x; 272 | 273 | HID_MTX_ASSERT(&hidquirk_mtx, MA_OWNED); 274 | 275 | if ((bus | vid | pid | lo_rev | hi_rev) == 0) { 276 | /* all zero - special case */ 277 | return (hidquirks + HID_DEV_QUIRKS_MAX - 1); 278 | } 279 | /* search for an existing entry */ 280 | for (x = 0; x != HID_DEV_QUIRKS_MAX; x++) { 281 | /* see if quirk information does not match */ 282 | if ((hidquirks[x].bus != bus) || 283 | (hidquirks[x].vid != vid) || 284 | (hidquirks[x].pid != pid) || 285 | (hidquirks[x].lo_rev != lo_rev) || 286 | (hidquirks[x].hi_rev != hi_rev)) { 287 | continue; 288 | } 289 | return (hidquirks + x); 290 | } 291 | 292 | if (do_alloc == 0) { 293 | /* no match */ 294 | return (NULL); 295 | } 296 | /* search for a free entry */ 297 | for (x = 0; x != HID_DEV_QUIRKS_MAX; x++) { 298 | /* see if quirk information does not match */ 299 | if ((hidquirks[x].bus | 300 | hidquirks[x].vid | 301 | hidquirks[x].pid | 302 | hidquirks[x].lo_rev | 303 | hidquirks[x].hi_rev) != 0) { 304 | continue; 305 | } 306 | hidquirks[x].bus = bus; 307 | hidquirks[x].vid = vid; 308 | hidquirks[x].pid = pid; 309 | hidquirks[x].lo_rev = lo_rev; 310 | hidquirks[x].hi_rev = hi_rev; 311 | 312 | return (hidquirks + x); 313 | } 314 | 315 | /* no entry found */ 316 | return (NULL); 317 | } 318 | 319 | #ifdef NOT_YET 320 | /*------------------------------------------------------------------------* 321 | * usb_quirk_ioctl - handle quirk IOCTLs 322 | * 323 | * Returns: 324 | * 0: Success 325 | * Else: Failure 326 | *------------------------------------------------------------------------*/ 327 | static int 328 | hidquirk_ioctl(unsigned long cmd, caddr_t data, 329 | int fflag, struct thread *td) 330 | { 331 | struct usb_gen_quirk *pgq; 332 | struct usb_quirk_entry *pqe; 333 | uint32_t x; 334 | uint32_t y; 335 | int err; 336 | 337 | switch (cmd) { 338 | case USB_DEV_QUIRK_GET: 339 | pgq = (void *)data; 340 | x = pgq->index % USB_SUB_QUIRKS_MAX; 341 | y = pgq->index / USB_SUB_QUIRKS_MAX; 342 | if (y >= USB_DEV_QUIRKS_MAX) { 343 | return (EINVAL); 344 | } 345 | USB_MTX_LOCK(&hidquirk_mtx); 346 | /* copy out data */ 347 | pgq->vid = hidquirks[y].vid; 348 | pgq->pid = hidquirks[y].pid; 349 | pgq->bcdDeviceLow = hidquirks[y].lo_rev; 350 | pgq->bcdDeviceHigh = hidquirks[y].hi_rev; 351 | strlcpy(pgq->quirkname, 352 | hidquirkstr(hidquirks[y].quirks[x]), 353 | sizeof(pgq->quirkname)); 354 | USB_MTX_UNLOCK(&hidquirk_mtx); 355 | return (0); /* success */ 356 | 357 | case USB_QUIRK_NAME_GET: 358 | pgq = (void *)data; 359 | x = pgq->index; 360 | if (x >= USB_QUIRK_MAX) { 361 | return (EINVAL); 362 | } 363 | strlcpy(pgq->quirkname, 364 | hidquirkstr(x), sizeof(pgq->quirkname)); 365 | return (0); /* success */ 366 | 367 | case USB_DEV_QUIRK_ADD: 368 | pgq = (void *)data; 369 | 370 | /* check privileges */ 371 | err = priv_check(curthread, PRIV_DRIVER); 372 | if (err) { 373 | return (err); 374 | } 375 | /* convert quirk string into numerical */ 376 | for (y = 0; y != USB_DEV_QUIRKS_MAX; y++) { 377 | if (strcmp(pgq->quirkname, hidquirkstr(y)) == 0) { 378 | break; 379 | } 380 | } 381 | if (y == USB_DEV_QUIRKS_MAX) { 382 | return (EINVAL); 383 | } 384 | if (y == UQ_NONE) { 385 | return (EINVAL); 386 | } 387 | USB_MTX_LOCK(&hidquirk_mtx); 388 | pqe = usb_quirk_get_entry(pgq->vid, pgq->pid, 389 | pgq->bcdDeviceLow, pgq->bcdDeviceHigh, 1); 390 | if (pqe == NULL) { 391 | USB_MTX_UNLOCK(&hidquirk_mtx); 392 | return (EINVAL); 393 | } 394 | for (x = 0; x != USB_SUB_QUIRKS_MAX; x++) { 395 | if (pqe->quirks[x] == UQ_NONE) { 396 | pqe->quirks[x] = y; 397 | break; 398 | } 399 | } 400 | USB_MTX_UNLOCK(&hidquirk_mtx); 401 | if (x == USB_SUB_QUIRKS_MAX) { 402 | return (ENOMEM); 403 | } 404 | return (0); /* success */ 405 | 406 | case USB_DEV_QUIRK_REMOVE: 407 | pgq = (void *)data; 408 | /* check privileges */ 409 | err = priv_check(curthread, PRIV_DRIVER); 410 | if (err) { 411 | return (err); 412 | } 413 | /* convert quirk string into numerical */ 414 | for (y = 0; y != USB_DEV_QUIRKS_MAX; y++) { 415 | if (strcmp(pgq->quirkname, hidquirkstr(y)) == 0) { 416 | break; 417 | } 418 | } 419 | if (y == USB_DEV_QUIRKS_MAX) { 420 | return (EINVAL); 421 | } 422 | if (y == UQ_NONE) { 423 | return (EINVAL); 424 | } 425 | USB_MTX_LOCK(&hidquirk_mtx); 426 | pqe = usb_quirk_get_entry(pgq->vid, pgq->pid, 427 | pgq->bcdDeviceLow, pgq->bcdDeviceHigh, 0); 428 | if (pqe == NULL) { 429 | USB_MTX_UNLOCK(&hidquirk_mtx); 430 | return (EINVAL); 431 | } 432 | for (x = 0; x != USB_SUB_QUIRKS_MAX; x++) { 433 | if (pqe->quirks[x] == y) { 434 | pqe->quirks[x] = UQ_NONE; 435 | break; 436 | } 437 | } 438 | if (x == USB_SUB_QUIRKS_MAX) { 439 | USB_MTX_UNLOCK(&hidquirk_mtx); 440 | return (ENOMEM); 441 | } 442 | for (x = 0; x != USB_SUB_QUIRKS_MAX; x++) { 443 | if (pqe->quirks[x] != UQ_NONE) { 444 | break; 445 | } 446 | } 447 | if (x == USB_SUB_QUIRKS_MAX) { 448 | /* all quirk entries are unused - release */ 449 | memset(pqe, 0, sizeof(*pqe)); 450 | } 451 | USB_MTX_UNLOCK(&hidquirk_mtx); 452 | return (0); /* success */ 453 | 454 | default: 455 | break; 456 | } 457 | return (ENOIOCTL); 458 | } 459 | #endif 460 | 461 | /*------------------------------------------------------------------------* 462 | * usb_quirk_strtou16 463 | * 464 | * Helper function to scan a 16-bit integer. 465 | *------------------------------------------------------------------------*/ 466 | static uint16_t 467 | hidquirk_strtou16(const char **pptr, const char *name, const char *what) 468 | { 469 | unsigned long value; 470 | char *end; 471 | 472 | value = strtoul(*pptr, &end, 0); 473 | if (value > 65535 || *pptr == end || (*end != ' ' && *end != '\t')) { 474 | printf("%s: %s 16-bit %s value set to zero\n", 475 | name, what, *end == 0 ? "incomplete" : "invalid"); 476 | return (0); 477 | } 478 | *pptr = end + 1; 479 | return ((uint16_t)value); 480 | } 481 | 482 | /*------------------------------------------------------------------------* 483 | * usb_quirk_add_entry_from_str 484 | * 485 | * Add a USB quirk entry from string. 486 | * "VENDOR PRODUCT LO_REV HI_REV QUIRK[,QUIRK[,...]]" 487 | *------------------------------------------------------------------------*/ 488 | static void 489 | hidquirk_add_entry_from_str(const char *name, const char *env) 490 | { 491 | struct hidquirk_entry entry = { }; 492 | struct hidquirk_entry *new; 493 | uint16_t quirk_idx; 494 | uint16_t quirk; 495 | const char *end; 496 | 497 | /* check for invalid environment variable */ 498 | if (name == NULL || env == NULL) 499 | return; 500 | 501 | if (bootverbose) 502 | printf("Adding HID QUIRK '%s' = '%s'\n", name, env); 503 | 504 | /* parse device information */ 505 | entry.bus = hidquirk_strtou16(&env, name, "Bus ID"); 506 | entry.vid = hidquirk_strtou16(&env, name, "Vendor ID"); 507 | entry.pid = hidquirk_strtou16(&env, name, "Product ID"); 508 | entry.lo_rev = hidquirk_strtou16(&env, name, "Low revision"); 509 | entry.hi_rev = hidquirk_strtou16(&env, name, "High revision"); 510 | 511 | /* parse quirk information */ 512 | quirk_idx = 0; 513 | while (*env != 0 && quirk_idx != HID_SUB_QUIRKS_MAX) { 514 | /* skip whitespace before quirks */ 515 | while (*env == ' ' || *env == '\t') 516 | env++; 517 | 518 | /* look for quirk separation character */ 519 | end = strchr(env, ','); 520 | if (end == NULL) 521 | end = env + strlen(env); 522 | 523 | /* lookup quirk in string table */ 524 | quirk = hid_strquirk(env, end - env); 525 | if (quirk < HID_QUIRK_MAX) { 526 | entry.quirks[quirk_idx++] = quirk; 527 | } else { 528 | printf("%s: unknown HID quirk '%.*s' (skipped)\n", 529 | name, (int)(end - env), env); 530 | } 531 | env = end; 532 | 533 | /* skip quirk delimiter, if any */ 534 | if (*env != 0) 535 | env++; 536 | } 537 | 538 | /* register quirk */ 539 | if (quirk_idx != 0) { 540 | if (*env != 0) { 541 | printf("%s: Too many HID quirks, only %d allowed!\n", 542 | name, HID_SUB_QUIRKS_MAX); 543 | } 544 | HID_MTX_LOCK(&hidquirk_mtx); 545 | new = hidquirk_get_entry(entry.bus, entry.vid, entry.pid, 546 | entry.lo_rev, entry.hi_rev, 1); 547 | if (new == NULL) 548 | printf("%s: HID quirks table is full!\n", name); 549 | else 550 | memcpy(new->quirks, entry.quirks, sizeof(entry.quirks)); 551 | HID_MTX_UNLOCK(&hidquirk_mtx); 552 | } else { 553 | printf("%s: No USB quirks found!\n", name); 554 | } 555 | } 556 | 557 | static void 558 | hidquirk_init(void *arg) 559 | { 560 | char envkey[sizeof(HID_QUIRK_ENVROOT) + 2]; /* 2 digits max, 0 to 99 */ 561 | int i; 562 | 563 | /* initialize mutex */ 564 | mtx_init(&hidquirk_mtx, "HID quirk", NULL, MTX_DEF); 565 | 566 | /* look for quirks defined by the environment variable */ 567 | for (i = 0; i != 100; i++) { 568 | snprintf(envkey, sizeof(envkey), HID_QUIRK_ENVROOT "%d", i); 569 | 570 | /* Stop at first undefined var */ 571 | if (!testenv(envkey)) 572 | break; 573 | 574 | /* parse environment variable */ 575 | hidquirk_add_entry_from_str(envkey, kern_getenv(envkey)); 576 | } 577 | 578 | /* register our function */ 579 | hid_test_quirk_p = &hid_test_quirk_by_info; 580 | #ifdef NOT_YET 581 | hidquirk_ioctl_p = &hidquirk_ioctl; 582 | #endif 583 | } 584 | 585 | static void 586 | hidquirk_uninit(void *arg) 587 | { 588 | hidquirk_unload(arg); 589 | 590 | /* destroy mutex */ 591 | mtx_destroy(&hidquirk_mtx); 592 | } 593 | 594 | SYSINIT(hidquirk_init, SI_SUB_LOCK, SI_ORDER_FIRST, hidquirk_init, NULL); 595 | SYSUNINIT(hidquirk_uninit, SI_SUB_LOCK, SI_ORDER_ANY, hidquirk_uninit, NULL); 596 | -------------------------------------------------------------------------------- /hidquirk.h: -------------------------------------------------------------------------------- 1 | /* $FreeBSD$ */ 2 | /*- 3 | * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 4 | * 5 | * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. 6 | * Copyright (c) 2020 Vladimir Kondratyev 7 | * 8 | * Redistribution and use in source and binary forms, with or without 9 | * modification, are permitted provided that the following conditions 10 | * are met: 11 | * 1. Redistributions of source code must retain the above copyright 12 | * notice, this list of conditions and the following disclaimer. 13 | * 2. Redistributions in binary form must reproduce the above copyright 14 | * notice, this list of conditions and the following disclaimer in the 15 | * documentation and/or other materials provided with the distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 | * SUCH DAMAGE. 28 | */ 29 | 30 | /* 31 | * Screening of all content of this file except HID_QUIRK_LIST is a kind of 32 | * hack that allows multiple HID_QUIRK_LIST inclusion with different HQ() 33 | * wrappers. That save us splitting hidquirk.h on two header files. 34 | */ 35 | #ifndef HQ 36 | #ifndef _HID_QUIRK_H_ 37 | #define _HID_QUIRK_H_ 38 | #endif 39 | 40 | /* 41 | * Keep in sync with share/man/man4/hidquirk.4 42 | */ 43 | #define HID_QUIRK_LIST(...) \ 44 | HQ(NONE), /* not a valid quirk */ \ 45 | \ 46 | HQ(MATCH_VENDOR_ONLY), /* match quirk on vendor only */ \ 47 | \ 48 | /* Autoquirks */ \ 49 | HQ(HAS_KBD_BOOTPROTO), /* device supports keyboard boot protocol */ \ 50 | HQ(HAS_MS_BOOTPROTO), /* device supports mouse boot protocol */ \ 51 | HQ(IS_XBOX360GP), /* device is XBox 360 GamePad */ \ 52 | HQ(NOWRITE), /* device does not support writes */ \ 53 | HQ(IICHID_SAMPLING), /* IIC backend runs in sampling mode */ \ 54 | \ 55 | /* Various quirks */ \ 56 | HQ(HID_IGNORE), /* device should be ignored by hid class */ \ 57 | HQ(KBD_BOOTPROTO), /* device should set the boot protocol */ \ 58 | HQ(MS_BOOTPROTO), /* device should set the boot protocol */ \ 59 | HQ(MS_BAD_CLASS), /* doesn't identify properly */ \ 60 | HQ(MS_LEADING_BYTE), /* mouse sends an unknown leading byte */ \ 61 | HQ(MS_REVZ), /* mouse has Z-axis reversed */ \ 62 | HQ(SPUR_BUT_UP), /* spurious mouse button up events */ \ 63 | HQ(MT_TIMESTAMP) /* Multitouch device exports HW timestamps */ 64 | 65 | #ifndef HQ 66 | #define HQ(x) HQ_##x 67 | enum { 68 | HID_QUIRK_LIST(), 69 | HID_QUIRK_MAX 70 | }; 71 | #undef HQ 72 | 73 | #endif /* _HID_QUIRK_H_ */ 74 | #endif /* HQ */ 75 | -------------------------------------------------------------------------------- /hidraw.4: -------------------------------------------------------------------------------- 1 | .\" $NetBSD: uhid.4,v 1.13 2001/12/29 14:41:59 augustss Exp $ 2 | .\" 3 | .\" Copyright (c) 1999, 2001 The NetBSD Foundation, Inc. 4 | .\" All rights reserved. 5 | .\" 6 | .\" This code is derived from software contributed to The NetBSD Foundation 7 | .\" by Lennart Augustsson. 8 | .\" 9 | .\" Redistribution and use in source and binary forms, with or without 10 | .\" modification, are permitted provided that the following conditions 11 | .\" are met: 12 | .\" 1. Redistributions of source code must retain the above copyright 13 | .\" notice, this list of conditions and the following disclaimer. 14 | .\" 2. Redistributions in binary form must reproduce the above copyright 15 | .\" notice, this list of conditions and the following disclaimer in the 16 | .\" documentation and/or other materials provided with the distribution. 17 | .\" 18 | .\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 19 | .\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 20 | .\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 21 | .\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 22 | .\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | .\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | .\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | .\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | .\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | .\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | .\" POSSIBILITY OF SUCH DAMAGE. 29 | .\" 30 | .\" $FreeBSD$ 31 | .\" 32 | .Dd July 1, 2018 33 | .Dt HIDRAW 4 34 | .Os 35 | .Sh NAME 36 | .Nm hidraw 37 | .Nd Raw Access to HID devices 38 | .Sh SYNOPSIS 39 | To compile this driver into the kernel, 40 | place the following line in your 41 | kernel configuration file: 42 | .Bd -ragged -offset indent 43 | .Cd "device hidraw" 44 | .Cd "device hid" 45 | .Cd "device hidbus" 46 | .Ed 47 | .Pp 48 | Alternatively, to load the driver as a 49 | module at boot time, place the following line in 50 | .Xr loader.conf 5 : 51 | .Bd -literal -offset indent 52 | hidraw_load="YES" 53 | .Ed 54 | .Sh DESCRIPTION 55 | The 56 | .Nm 57 | driver provides a raw interface to Human Interface Devices (HIDs). 58 | The reports are sent to and received from the device unmodified. 59 | .Pp 60 | The device handles 2 sets of 61 | .Xr ioctl 2 62 | calls: 63 | .Pp 64 | .Fx 65 | .Xr uhid 4 66 | \-compatible calls: 67 | .Bl -tag -width indent 68 | .It Dv USB_GET_REPORT_ID Pq Vt int 69 | Get the report identifier used by this HID report. 70 | .It Dv USB_GET_REPORT_DESC Pq Vt "struct usb_gen_descriptor" 71 | Get the HID report descriptor. 72 | Copies a maximum of 73 | .Va ugd_maxlen 74 | bytes of the report descriptor data into the memory 75 | specified by 76 | .Va ugd_data . 77 | Upon return 78 | .Va ugd_actlen 79 | is set to the number of bytes copied. 80 | Using 81 | this descriptor the exact layout and meaning of data to/from 82 | the device can be found. 83 | The report descriptor is delivered 84 | without any processing. 85 | .Bd -literal 86 | struct usb_gen_descriptor { 87 | void *ugd_data; 88 | uint16_t ugd_maxlen; 89 | uint16_t ugd_actlen; 90 | uint8_t ugd_report_type; 91 | ... 92 | }; 93 | .Ed 94 | .It Dv USB_SET_IMMED Pq Vt int 95 | Sets the device in a mode where each 96 | .Xr read 2 97 | will return the current value of the input report. 98 | Normally 99 | a 100 | .Xr read 2 101 | will only return the data that the device reports on its 102 | interrupt pipe. 103 | This call may fail if the device does not support 104 | this feature. 105 | .It Dv USB_GET_REPORT Pq Vt "struct usb_gen_descriptor" 106 | Get a report from the device without waiting for data on 107 | the interrupt pipe. 108 | Copies a maximum of 109 | .Va ugd_maxlen 110 | bytes of the report data into the memory specified by 111 | .Va ugd_data . 112 | Upon return 113 | .Va ugd_actlen 114 | is set to the number of bytes copied. 115 | The 116 | .Va ugd_report_type 117 | field indicates which report is requested. 118 | It should be 119 | .Dv UHID_INPUT_REPORT , 120 | .Dv UHID_OUTPUT_REPORT , 121 | or 122 | .Dv UHID_FEATURE_REPORT . 123 | On a device which uses numbered reports, the first byte of the returned data 124 | is the report number. 125 | The report data begins from the second byte. 126 | For devices which do not use numbered reports, the report data begins at the 127 | first byte. 128 | This call may fail if the device does not support this feature. 129 | .It Dv USB_SET_REPORT Pq Vt "struct usb_gen_descriptor" 130 | Set a report in the device. 131 | The 132 | .Va ugd_report_type 133 | field indicates which report is to be set. 134 | It should be 135 | .Dv UHID_INPUT_REPORT , 136 | .Dv UHID_OUTPUT_REPORT , 137 | or 138 | .Dv UHID_FEATURE_REPORT . 139 | The value of the report is specified by the 140 | .Va ugd_data 141 | and the 142 | .Va ugd_maxlen 143 | fields. 144 | On a device which uses numbered reports, the first byte of data to be sent is 145 | the report number. 146 | The report data begins from the second byte. 147 | For devices which do not use numbered reports, the report data begins at the 148 | first byte. 149 | This call may fail if the device does not support this feature. 150 | .El 151 | .Pp 152 | Linux 153 | .Nm 154 | \-compatible calls: 155 | .Bl -tag -width indent 156 | .It Dv HIDIOCGRDESCSIZE Pq Vt int 157 | Get the HID report descriptor size. 158 | Returns the size of the device report descriptor to use with 159 | .Dv HIDIOCGRDESC 160 | .Xr ioctl 2 . 161 | .It Dv HIDIOCGRDESC Pq Vt "struct hidraw_report_descriptor" 162 | Get the HID report descriptor. 163 | Copies a maximum of 164 | .Va size 165 | bytes of the report descriptor data into the memory 166 | specified by 167 | .Va value . 168 | .Bd -literal 169 | struct hidraw_report_descriptor { 170 | uint32_t size; 171 | uint8_t value[HID_MAX_DESCRIPTOR_SIZE]; 172 | }; 173 | .Ed 174 | .It Dv HIDIOCGRAWINFO Pq Vt "struct hidraw_devinfo" 175 | Get structure containing the bus type, the vendor ID (VID), and product ID 176 | (PID) of the device. The bus type can be one of: 177 | .Dv BUS_USB 178 | or 179 | .Dv BUS_I2C 180 | which are defined in dev/evdev/input.h. 181 | .Bd -literal 182 | struct hidraw_devinfo { 183 | uint32_t bustype; 184 | int16_t vendor; 185 | int16_t product; 186 | }; 187 | .Ed 188 | .It Dv HIDIOCGRAWNAME(len) Pq Vt "char[] buf" 189 | Get device description. 190 | Copies a maximum of 191 | .Va len 192 | bytes of the device description previously set with 193 | .Xr device_set_desc 9 194 | procedure into the memory 195 | specified by 196 | .Va buf . 197 | .It Dv HIDIOCGRAWPHYS(len) Pq Vt "char[] buf" 198 | Get the newbus path to the device. 199 | .\For Bluetooth devices, it returns the hardware (MAC) address of the device. 200 | Copies a maximum of 201 | .Va len 202 | bytes of the newbus device path 203 | into the memory 204 | specified by 205 | .Va buf . 206 | .It Dv HIDIOCGFEATURE(len) Pq Vt "void[] buf" 207 | Get a feature report from the device. 208 | Copies a maximum of 209 | .Va len 210 | bytes of the feature report data into the memory specified by 211 | .Va buf . 212 | The first byte of the supplied buffer should be set to the report 213 | number of the requested report. 214 | For devices which do not use numbered reports, set the first byte to 0. 215 | The report will be returned starting at the first byte of the buffer 216 | (ie: the report number is not returned). 217 | This call may fail if the device does not support this feature. 218 | .It Dv HIDIOCSFEATURE(len) Pq Vt "void[] buf" 219 | Set a feature Report in the device. 220 | The value of the report is specified by the 221 | .Va buf 222 | and the 223 | .Va len 224 | parameters. 225 | Set the first byte of the supplied buffer to the report number. 226 | For devices which do not use numbered reports, set the first byte to 0. 227 | The report data begins in the second byte. 228 | Make sure to set len accordingly, to one more than the length of the report 229 | (to account for the report number). 230 | This call may fail if the device does not support this feature. 231 | .El 232 | .Pp 233 | Use 234 | .Xr read 2 235 | to get data from the device. 236 | Data should be read in chunks of the 237 | size prescribed by the report descriptor. 238 | On a device which uses numbered reports, the first byte of the returned data 239 | is the report number. 240 | The report data begins from the second byte. 241 | For devices which do not use numbered reports, the report data begins at the 242 | first byte. 243 | .Pp 244 | Use 245 | .Xr write 2 246 | to send data to the device. 247 | Data should be written in chunks of the 248 | size prescribed by the report descriptor. 249 | The first byte of the buffer passed to 250 | .Xr write 2 251 | should be set to the report number. 252 | If the device does not use numbered reports, there are 2 operation modes: 253 | .Nm 254 | mode and 255 | .Xr uhid 4 256 | mode. 257 | In the 258 | .Nm 259 | mode, the first byte should be set to 0 and the report data itself should 260 | begin at the second byte. 261 | In the 262 | .Xr uhid 4 263 | mode, the report data should begin at the first byte. 264 | The modes can be switched with issuing one of 265 | .Dv HIDIOCGRDESC 266 | or 267 | .Dv USB_GET_REPORT_DESC 268 | .Xr ioctl 2 269 | accordingly. 270 | Default mode is 271 | .Nm . 272 | .Sh SYSCTL VARIABLES 273 | The following variables are available as both 274 | .Xr sysctl 8 275 | variables and 276 | .Xr loader 8 277 | tunables: 278 | .Bl -tag -width indent 279 | .It Va hw.hid.hidraw.debug 280 | Debug output level, where 0 is debugging disabled and larger values increase 281 | debug message verbosity. 282 | Default is 0. 283 | .El 284 | .Sh FILES 285 | .Bl -tag -width ".Pa /dev/hidraw?" 286 | .It Pa /dev/hidraw? 287 | .El 288 | .Sh SEE ALSO 289 | .Xr usbhidctl 1 , 290 | .Xr hid 4 , 291 | .Xr hidbus 4 , 292 | .Xr uhid 4 293 | .Sh HISTORY 294 | The 295 | .Xr uhid 4 296 | driver 297 | appeared in 298 | .Nx 1.4 . 299 | .Nm 300 | protocol support was added in 301 | .Fx 13 302 | by 303 | .An Vladimir Kondratyev Aq Mt wulf@FreeBSD.org . 304 | This manual page was adopted from 305 | .Nx 306 | by 307 | .An Tom Rhodes Aq Mt trhodes@FreeBSD.org 308 | in April 2002. 309 | -------------------------------------------------------------------------------- /hidraw.h: -------------------------------------------------------------------------------- 1 | /*- 2 | * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 | * 4 | * Copyright (c) 2020 Vladimir Kondratyev 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 | * SUCH DAMAGE. 26 | * 27 | * $FreeBSD$ 28 | */ 29 | 30 | #ifndef _HIDRAW_H 31 | #define _HIDRAW_H 32 | 33 | #include 34 | 35 | #define HIDRAW_BUFFER_SIZE 64 /* number of input reports buffered */ 36 | #define HID_MAX_DESCRIPTOR_SIZE 4096 /* artificial limit taken from Linux */ 37 | 38 | struct hidraw_report_descriptor { 39 | uint32_t size; 40 | uint8_t value[HID_MAX_DESCRIPTOR_SIZE]; 41 | }; 42 | 43 | struct hidraw_devinfo { 44 | uint32_t bustype; 45 | int16_t vendor; 46 | int16_t product; 47 | }; 48 | 49 | #define HIDIOCGRDESCSIZE _IOR('U', 30, int) 50 | #define HIDIOCGRDESC _IO ('U', 31) 51 | #define HIDIOCGRAWINFO _IOR('U', 32, struct hidraw_devinfo) 52 | #define HIDIOCGRAWNAME(len) _IOC(IOC_OUT, 'U', 33, len) 53 | #define HIDIOCGRAWPHYS(len) _IOC(IOC_OUT, 'U', 34, len) 54 | #define HIDIOCSFEATURE(len) _IOC(IOC_IN, 'U', 35, len) 55 | #define HIDIOCGFEATURE(len) _IOC(IOC_INOUT, 'U', 36, len) 56 | #define HIDIOCGRAWUNIQ(len) _IOC(IOC_OUT, 'U', 37, len) 57 | 58 | /* FreeBSD extension. Set report descriptor. */ 59 | #define HIDIOCSRDESC(len) _IOC(IOC_IN, 'U', 26, len) 60 | 61 | #endif /* _HIDRAW_H */ 62 | -------------------------------------------------------------------------------- /hkbd.4: -------------------------------------------------------------------------------- 1 | .\" Copyright (c) 1997, 1998 2 | .\" Nick Hibma . All rights reserved. 3 | .\" 4 | .\" Redistribution and use in source and binary forms, with or without 5 | .\" modification, are permitted provided that the following conditions 6 | .\" are met: 7 | .\" 1. Redistributions of source code must retain the above copyright 8 | .\" notice, this list of conditions and the following disclaimer. 9 | .\" 2. Redistributions in binary form must reproduce the above copyright 10 | .\" notice, this list of conditions and the following disclaimer in the 11 | .\" documentation and/or other materials provided with the distribution. 12 | .\" 13 | .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14 | .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | .\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 17 | .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 | .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 | .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 | .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 | .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 | .\" SUCH DAMAGE. 24 | .\" 25 | .\" $FreeBSD$ 26 | .\" 27 | .Dd September 12, 2020 28 | .Dt HKBD 4 29 | .Os 30 | .Sh NAME 31 | .Nm hkbd 32 | .Nd HID keyboard driver 33 | .Sh SYNOPSIS 34 | To compile this driver into the kernel, 35 | place the following line in your 36 | kernel configuration file: 37 | .Bd -ragged -offset indent 38 | .Cd "device hkbd" 39 | .Cd "device hid" 40 | .Cd "device hidbus" 41 | .Cd "device evdev" 42 | .Cd "options EVDEV_SUPPORT" 43 | .Ed 44 | .Pp 45 | Alternatively, to load the driver as a 46 | module at boot time, place the following line in 47 | .Xr loader.conf 5 : 48 | .Bd -literal -offset indent 49 | hkbd_load="YES" 50 | .Ed 51 | .Sh DESCRIPTION 52 | The 53 | .Nm 54 | driver provides support for keyboards that attach to the HID transport 55 | backend. 56 | .Xr hid 4 , 57 | .Xr hidbus 4 , 58 | and one of 59 | .Xr iichid 4 60 | or 61 | .Xr usbhid 4 62 | must be configured in the kernel as well. 63 | .Sh CONFIGURATION 64 | By default, the keyboard subsystem does not create the appropriate devices yet. 65 | Make sure you reconfigure your kernel with the following option in the kernel 66 | config file: 67 | .Pp 68 | .Dl "options KBD_INSTALL_CDEV" 69 | .Pp 70 | If both an AT keyboard USB keyboards are used at the same time, the 71 | AT keyboard will appear as 72 | .Pa kbd0 73 | in 74 | .Pa /dev . 75 | The USB keyboards will be 76 | .Pa kbd1 , kbd2 , 77 | etc. 78 | You can see some information about the keyboard with the following command: 79 | .Pp 80 | .Dl "kbdcontrol -i < /dev/kbd1" 81 | .Pp 82 | or load a keymap with 83 | .Pp 84 | .Dl "kbdcontrol -l keymaps/pt.iso < /dev/kbd1" 85 | .Pp 86 | See 87 | .Xr kbdcontrol 1 88 | for more possible options. 89 | .Pp 90 | You can swap console keyboards by using the command 91 | .Pp 92 | .Dl "kbdcontrol -k /dev/kbd1" 93 | .Pp 94 | From this point on, the first USB keyboard will be the keyboard 95 | to be used by the console. 96 | .Pp 97 | If you want to use a USB keyboard as your default and not use an AT keyboard at 98 | all, you will have to remove the 99 | .Cd "device atkbd" 100 | line from the kernel configuration file. 101 | Because of the device initialization order, 102 | the USB keyboard will be detected 103 | .Em after 104 | the console driver 105 | initializes itself and you have to explicitly tell the console 106 | driver to use the existence of the USB keyboard. 107 | This can be done in 108 | one of the following two ways. 109 | .Pp 110 | Run the following command as a part of system initialization: 111 | .Pp 112 | .Dl "kbdcontrol -k /dev/kbd0 < /dev/ttyv0 > /dev/null" 113 | .Pp 114 | (Note that as the USB keyboard is the only keyboard, it is accessed as 115 | .Pa /dev/kbd0 ) 116 | or otherwise tell the console driver to periodically look for a 117 | keyboard by setting a flag in the kernel configuration file: 118 | .Pp 119 | .Dl "device sc0 at isa? flags 0x100" 120 | .Pp 121 | With the above flag, the console driver will try to detect any 122 | keyboard in the system if it did not detect one while it was 123 | initialized at boot time. 124 | .Sh DRIVER CONFIGURATION 125 | .D1 Cd "options KBD_INSTALL_CDEV" 126 | .Pp 127 | Make the keyboards available through a character device in 128 | .Pa /dev . 129 | .Pp 130 | .D1 Cd options HKBD_DFLT_KEYMAP 131 | .D1 Cd makeoptions HKBD_DFLT_KEYMAP=fr.iso 132 | .Pp 133 | The above lines will put the French ISO keymap in the ukbd driver. 134 | You can specify any keymap in 135 | .Pa /usr/share/syscons/keymaps 136 | or 137 | .Pa /usr/share/vt/keymaps 138 | (depending on the console driver being used) with this option. 139 | .Pp 140 | .D1 Cd "options KBD_DISABLE_KEYMAP_LOADING" 141 | .Pp 142 | Do not allow the user to change the keymap. 143 | Note that these options also affect the AT keyboard driver, 144 | .Xr atkbd 4 . 145 | .Sh SYSCTL VARIABLES 146 | The following variables are available as both 147 | .Xr sysctl 8 148 | variables and 149 | .Xr loader 8 150 | tunables: 151 | .Bl -tag -width indent 152 | .It Va hw.hid.hkbd.debug 153 | Debug output level, where 0 is debugging disabled and larger values increase 154 | debug message verbosity. 155 | Default is 0. 156 | .El 157 | .Sh FILES 158 | .Bl -tag -width ".Pa /dev/input/event*" -compact 159 | .It Pa /dev/kbd* 160 | blocking device nodes 161 | .It Pa /dev/input/event* 162 | input event device nodes. 163 | .El 164 | .Sh EXAMPLES 165 | .D1 Cd "device hkbd" 166 | .Pp 167 | Add the 168 | .Nm 169 | driver to the kernel. 170 | .Sh SEE ALSO 171 | .Xr kbdcontrol 1 , 172 | .Xr hid 4 , 173 | .Xr hidbus 4 , 174 | .Xr iichid 4 , 175 | .Xr syscons 4 , 176 | .Xr usbhid 4 , 177 | .Xr vt 4 , 178 | .Xr config 8 179 | .Sh AUTHORS 180 | .An -nosplit 181 | The 182 | .Nm 183 | driver was written by 184 | .An Lennart Augustsson Aq Mt augustss@cs.chalmers.se 185 | for 186 | .Nx 187 | and was substantially rewritten for 188 | .Fx 189 | by 190 | .An Kazutaka YOKOTA Aq Mt yokota@zodiac.mech.utsunomiya-u.ac.jp . 191 | .Pp 192 | This manual page was written by 193 | .An Nick Hibma Aq Mt n_hibma@FreeBSD.org 194 | with a large amount of input from 195 | .An Kazutaka YOKOTA Aq Mt yokota@zodiac.mech.utsunomiya-u.ac.jp . 196 | -------------------------------------------------------------------------------- /hms.4: -------------------------------------------------------------------------------- 1 | .\" Copyright (c) 2 | .\" 1999 Nick Hibma . All rights reserved. 3 | .\" 2020 Vladimir Kondratyev . 4 | .\" 5 | .\" Redistribution and use in source and binary forms, with or without 6 | .\" modification, are permitted provided that the following conditions 7 | .\" are met: 8 | .\" 1. Redistributions of source code must retain the above copyright 9 | .\" notice, this list of conditions and the following disclaimer. 10 | .\" 2. Redistributions in binary form must reproduce the above copyright 11 | .\" notice, this list of conditions and the following disclaimer in the 12 | .\" documentation and/or other materials provided with the distribution. 13 | .\" 14 | .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 | .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | .\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 | .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 | .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 | .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 | .\" SUCH DAMAGE. 25 | .\" 26 | .\" $FreeBSD$ 27 | .\" 28 | .Dd September 12, 2020 29 | .Dt HMS 4 30 | .Os 31 | .Sh NAME 32 | .Nm hms 33 | .Nd HID mouse driver 34 | .Sh SYNOPSIS 35 | To compile this driver into the kernel, 36 | place the following lines in your 37 | kernel configuration file: 38 | .Bd -ragged -offset indent 39 | .Cd "device hms" 40 | .Cd "device hidbus" 41 | .Cd "device hid" 42 | .Cd "device evdev" 43 | .Ed 44 | .Pp 45 | Alternatively, to load the driver as a 46 | module at boot time, place the following line in 47 | .Xr loader.conf 5 : 48 | .Bd -literal -offset indent 49 | hms_load="YES" 50 | .Ed 51 | .Sh DESCRIPTION 52 | The 53 | .Nm 54 | driver provides support for HID mice that attach to the HID transport 55 | backend. 56 | See 57 | .Xr iichid 4 58 | or 59 | .Xr usbhid 4 . 60 | Supported are 61 | mice with any number of buttons, mice with a wheel and absolute mice. 62 | .Pp 63 | The 64 | .Pa /dev/input/eventX 65 | device presents the mouse as a 66 | .Ar evdev 67 | type device. 68 | .Sh SYSCTL VARIABLES 69 | The following variables are available as both 70 | .Xr sysctl 8 71 | variables and 72 | .Xr loader 8 73 | tunables: 74 | .Bl -tag -width indent 75 | .It Va hw.hid.hms.debug 76 | Debug output level, where 0 is debugging disabled and larger values increase 77 | debug message verbosity. 78 | Default is 0. 79 | .El 80 | .Sh FILES 81 | .Bl -tag -width /dev/input/eventX -compact 82 | .It Pa /dev/input/eventX 83 | input event device node. 84 | .El 85 | .Sh SEE ALSO 86 | .Xr iichid 4 , 87 | .Xr usbhid 4 , 88 | .Xr xorg.conf 5 Pq Pa ports/x11/xorg 89 | .\.Xr moused 8 90 | .Sh BUGS 91 | .Nm 92 | cannot act like 93 | .Xr sysmouse 4 94 | .Sh AUTHORS 95 | .An -nosplit 96 | The 97 | .Nm 98 | driver was written by 99 | .An Vladimir Kondratyev Aq Mt wulf@FreeBSD.org . 100 | .Pp 101 | This manual page was originally written by 102 | .An Nick Hibma Aq Mt n_hibma@FreeBSD.org 103 | for 104 | .Xr umt 4 105 | driver and was adopted for 106 | .Nm 107 | by 108 | .An Vladimir Kondratyev Aq Mt wulf@FreeBSD.org . 109 | -------------------------------------------------------------------------------- /hms.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 | * 4 | * Copyright (c) 2020 Vladimir Kondratyev 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 | * SUCH DAMAGE. 26 | */ 27 | 28 | #include 29 | __FBSDID("$FreeBSD$"); 30 | 31 | /* 32 | * HID spec: https://www.usb.org/sites/default/files/documents/hid1_11.pdf 33 | */ 34 | 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | 41 | #include 42 | #include 43 | 44 | #include "hid.h" 45 | #include "hidbus.h" 46 | #include "hidquirk.h" 47 | #include "hidmap.h" 48 | 49 | static const uint8_t hms_boot_desc[] = { 50 | 0x05, 0x01, // Usage Page (Generic Desktop Ctrls) 51 | 0x09, 0x02, // Usage (Mouse) 52 | 0xA1, 0x01, // Collection (Application) 53 | 0x09, 0x01, // Usage (Pointer) 54 | 0xA1, 0x00, // Collection (Physical) 55 | 0x95, 0x03, // Report Count (3) 56 | 0x75, 0x01, // Report Size (1) 57 | 0x05, 0x09, // Usage Page (Button) 58 | 0x19, 0x01, // Usage Minimum (0x01) 59 | 0x29, 0x03, // Usage Maximum (0x03) 60 | 0x15, 0x00, // Logical Minimum (0) 61 | 0x25, 0x01, // Logical Maximum (1) 62 | 0x81, 0x02, // Input (Data,Var,Abs) 63 | 0x95, 0x01, // Report Count (1) 64 | 0x75, 0x05, // Report Size (5) 65 | 0x81, 0x03, // Input (Const) 66 | 0x75, 0x08, // Report Size (8) 67 | 0x95, 0x02, // Report Count (2) 68 | 0x05, 0x01, // Usage Page (Generic Desktop Ctrls) 69 | 0x09, 0x30, // Usage (X) 70 | 0x09, 0x31, // Usage (Y) 71 | 0x15, 0x81, // Logical Minimum (-127) 72 | 0x25, 0x7F, // Logical Maximum (127) 73 | 0x81, 0x06, // Input (Data,Var,Rel) 74 | 0xC0, // End Collection 75 | 0xC0, // End Collection 76 | }; 77 | 78 | enum { 79 | HMS_REL_X, 80 | HMS_REL_Y, 81 | HMS_REL_Z, 82 | HMS_ABS_X, 83 | HMS_ABS_Y, 84 | HMS_ABS_Z, 85 | HMS_HWHEEL, 86 | HMS_BTN, 87 | HMS_BTN_MS1, 88 | HMS_BTN_MS2, 89 | HMS_FINAL_CB, 90 | }; 91 | 92 | static hidmap_cb_t hms_final_cb; 93 | static hid_intr_t hms_intr; 94 | 95 | #define HMS_MAP_BUT_RG(usage_from, usage_to, code) \ 96 | { HIDMAP_KEY_RANGE(HUP_BUTTON, usage_from, usage_to, code) } 97 | #define HMS_MAP_BUT_MS(usage, code) \ 98 | { HIDMAP_KEY(HUP_MICROSOFT, usage, code) } 99 | #define HMS_MAP_ABS(usage, code) \ 100 | { HIDMAP_ABS(HUP_GENERIC_DESKTOP, usage, code) } 101 | #define HMS_MAP_REL(usage, code) \ 102 | { HIDMAP_REL(HUP_GENERIC_DESKTOP, usage, code) } 103 | #define HMS_MAP_REL_REV(usage, code) \ 104 | { HIDMAP_REL(HUP_GENERIC_DESKTOP, usage, code), .invert_value = true } 105 | #define HMS_MAP_REL_CN(usage, code) \ 106 | { HIDMAP_REL(HUP_CONSUMER, usage, code) } 107 | #define HMS_FINAL_CB(cb) \ 108 | { HIDMAP_FINAL_CB(&cb) } 109 | 110 | static const struct hidmap_item hms_map[] = { 111 | [HMS_REL_X] = HMS_MAP_REL(HUG_X, REL_X), 112 | [HMS_REL_Y] = HMS_MAP_REL(HUG_Y, REL_Y), 113 | [HMS_REL_Z] = HMS_MAP_REL(HUG_Z, REL_Z), 114 | [HMS_ABS_X] = HMS_MAP_ABS(HUG_X, ABS_X), 115 | [HMS_ABS_Y] = HMS_MAP_ABS(HUG_Y, ABS_Y), 116 | [HMS_ABS_Z] = HMS_MAP_ABS(HUG_Z, ABS_Z), 117 | [HMS_HWHEEL] = HMS_MAP_REL_CN(HUC_AC_PAN, REL_HWHEEL), 118 | [HMS_BTN] = HMS_MAP_BUT_RG(1, 16, BTN_MOUSE), 119 | [HMS_BTN_MS1] = HMS_MAP_BUT_MS(1, BTN_RIGHT), 120 | [HMS_BTN_MS2] = HMS_MAP_BUT_MS(2, BTN_MIDDLE), 121 | [HMS_FINAL_CB] = HMS_FINAL_CB(hms_final_cb), 122 | }; 123 | 124 | static const struct hidmap_item hms_map_wheel[] = { 125 | HMS_MAP_REL(HUG_WHEEL, REL_WHEEL), 126 | }; 127 | static const struct hidmap_item hms_map_wheel_rev[] = { 128 | HMS_MAP_REL_REV(HUG_WHEEL, REL_WHEEL), 129 | }; 130 | 131 | /* A match on these entries will load hms */ 132 | static const struct hid_device_id hms_devs[] = { 133 | { HID_TLC(HUP_GENERIC_DESKTOP, HUG_MOUSE) }, 134 | }; 135 | 136 | struct hms_softc { 137 | struct hidmap hm; 138 | HIDMAP_CAPS(caps, hms_map); 139 | bool iichid_sampling; 140 | void *last_ir; 141 | hid_size_t last_irsize; 142 | hid_size_t isize; 143 | uint32_t drift_cnt; 144 | uint32_t drift_thresh; 145 | }; 146 | 147 | static void 148 | hms_intr(void *context, void *buf, hid_size_t len) 149 | { 150 | struct hidmap *hm = context; 151 | struct hms_softc *sc = device_get_softc(hm->dev); 152 | 153 | if (len > sc->isize) 154 | len = sc->isize; 155 | 156 | /* 157 | * Many I2C "compatibility" mouse devices found on touchpads continue 158 | * to return last report data in sampling mode even after touch has 159 | * been ended. That results in cursor drift. Filter out such a 160 | * reports through comparing with previous one. 161 | */ 162 | if (len == sc->last_irsize && memcmp(buf, sc->last_ir, len) == 0) { 163 | sc->drift_cnt++; 164 | if (sc->drift_thresh != 0 && sc->drift_cnt >= sc->drift_thresh) 165 | return; 166 | } else { 167 | sc->drift_cnt = 0; 168 | sc->last_irsize = len; 169 | bcopy(buf, sc->last_ir, len); 170 | } 171 | 172 | hidmap_intr(context, buf, len); 173 | } 174 | 175 | static int 176 | hms_final_cb(HIDMAP_CB_ARGS) 177 | { 178 | struct hms_softc *sc = HIDMAP_CB_GET_SOFTC(); 179 | struct evdev_dev *evdev = HIDMAP_CB_GET_EVDEV(); 180 | 181 | if (HIDMAP_CB_GET_STATE() == HIDMAP_CB_IS_ATTACHING) { 182 | if (hidmap_test_cap(sc->caps, HMS_ABS_X) || 183 | hidmap_test_cap(sc->caps, HMS_ABS_Y)) 184 | evdev_support_prop(evdev, INPUT_PROP_DIRECT); 185 | else 186 | evdev_support_prop(evdev, INPUT_PROP_POINTER); 187 | /* Overload interrupt handler to skip identical reports */ 188 | if (sc->iichid_sampling) 189 | hidbus_set_intr(sc->hm.dev, hms_intr, &sc->hm); 190 | } 191 | 192 | /* Do not execute callback at interrupt handler and detach */ 193 | return (ENOSYS); 194 | } 195 | 196 | static void 197 | hms_identify(driver_t *driver, device_t parent) 198 | { 199 | const struct hid_device_info *hw = hid_get_device_info(parent); 200 | void *d_ptr; 201 | hid_size_t d_len; 202 | int error; 203 | 204 | /* 205 | * If device claimed boot protocol support but do not have report 206 | * descriptor, load one defined in "Appendix B.2" of HID1_11.pdf 207 | */ 208 | error = hid_get_report_descr(parent, &d_ptr, &d_len); 209 | if ((error != 0 && hid_test_quirk(hw, HQ_HAS_MS_BOOTPROTO)) || 210 | (error == 0 && hid_test_quirk(hw, HQ_MS_BOOTPROTO) && 211 | hid_is_mouse(d_ptr, d_len))) 212 | (void)hid_set_report_descr(parent, hms_boot_desc, 213 | sizeof(hms_boot_desc)); 214 | } 215 | 216 | static int 217 | hms_probe(device_t dev) 218 | { 219 | struct hms_softc *sc = device_get_softc(dev); 220 | int error; 221 | 222 | error = HIDBUS_LOOKUP_DRIVER_INFO(dev, hms_devs); 223 | if (error != 0) 224 | return (error); 225 | 226 | hidmap_set_dev(&sc->hm, dev); 227 | 228 | /* Check if report descriptor belongs to mouse */ 229 | error = HIDMAP_ADD_MAP(&sc->hm, hms_map, sc->caps); 230 | if (error != 0) 231 | return (error); 232 | 233 | /* There should be at least one X or Y axis */ 234 | if (!hidmap_test_cap(sc->caps, HMS_REL_X) && 235 | !hidmap_test_cap(sc->caps, HMS_REL_X) && 236 | !hidmap_test_cap(sc->caps, HMS_ABS_X) && 237 | !hidmap_test_cap(sc->caps, HMS_ABS_Y)) 238 | return (ENXIO); 239 | 240 | if (hidmap_test_cap(sc->caps, HMS_ABS_X) || 241 | hidmap_test_cap(sc->caps, HMS_ABS_Y)) 242 | hidbus_set_desc(dev, "Tablet"); 243 | else 244 | hidbus_set_desc(dev, "Mouse"); 245 | 246 | return (BUS_PROBE_DEFAULT); 247 | } 248 | 249 | static int 250 | hms_attach(device_t dev) 251 | { 252 | struct hms_softc *sc = device_get_softc(dev); 253 | const struct hid_device_info *hw = hid_get_device_info(dev); 254 | struct hidmap_hid_item *hi; 255 | HIDMAP_CAPS(cap_wheel, hms_map_wheel); 256 | void *d_ptr; 257 | hid_size_t d_len; 258 | bool set_report_proto; 259 | int error, nbuttons = 0; 260 | 261 | /* 262 | * Set the report (non-boot) protocol if report descriptor has not been 263 | * overloaded with boot protocol report descriptor. 264 | * 265 | * Mice without boot protocol support may choose not to implement 266 | * Set_Protocol at all; Ignore any error. 267 | */ 268 | error = hid_get_report_descr(dev, &d_ptr, &d_len); 269 | set_report_proto = !(error == 0 && d_len == sizeof(hms_boot_desc) && 270 | memcmp(d_ptr, hms_boot_desc, sizeof(hms_boot_desc)) == 0); 271 | (void)hid_set_protocol(dev, set_report_proto ? 1 : 0); 272 | 273 | if (hid_test_quirk(hw, HQ_MS_REVZ)) 274 | HIDMAP_ADD_MAP(&sc->hm, hms_map_wheel_rev, cap_wheel); 275 | else 276 | HIDMAP_ADD_MAP(&sc->hm, hms_map_wheel, cap_wheel); 277 | 278 | if (hid_test_quirk(hw, HQ_IICHID_SAMPLING) && 279 | hidmap_test_cap(sc->caps, HMS_REL_X) && 280 | hidmap_test_cap(sc->caps, HMS_REL_Y)) { 281 | sc->iichid_sampling = true; 282 | sc->isize = hid_report_size(d_ptr, d_len, hid_input, NULL); 283 | sc->last_ir = malloc(sc->isize, M_DEVBUF, M_WAITOK | M_ZERO); 284 | sc->drift_thresh = 2; 285 | SYSCTL_ADD_U32(device_get_sysctl_ctx(dev), 286 | SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 287 | "drift_thresh", CTLFLAG_RW, &sc->drift_thresh, 0, 288 | "drift detection threshhold"); 289 | } 290 | 291 | error = hidmap_attach(&sc->hm); 292 | if (error) 293 | return (error); 294 | 295 | /* Count number of input usages of variable type mapped to buttons */ 296 | for (hi = sc->hm.hid_items; 297 | hi < sc->hm.hid_items + sc->hm.nhid_items; 298 | hi++) 299 | if (hi->type == HIDMAP_TYPE_VARIABLE && hi->evtype == EV_KEY) 300 | nbuttons++; 301 | 302 | /* announce information about the mouse */ 303 | device_printf(dev, "%d buttons and [%s%s%s%s%s] coordinates ID=%u\n", 304 | nbuttons, 305 | (hidmap_test_cap(sc->caps, HMS_REL_X) || 306 | hidmap_test_cap(sc->caps, HMS_ABS_X)) ? "X" : "", 307 | (hidmap_test_cap(sc->caps, HMS_REL_Y) || 308 | hidmap_test_cap(sc->caps, HMS_ABS_Y)) ? "Y" : "", 309 | (hidmap_test_cap(sc->caps, HMS_REL_Z) || 310 | hidmap_test_cap(sc->caps, HMS_ABS_Z)) ? "Z" : "", 311 | hidmap_test_cap(cap_wheel, 0) ? "W" : "", 312 | hidmap_test_cap(sc->caps, HMS_HWHEEL) ? "H" : "", 313 | sc->hm.hid_items[0].id); 314 | 315 | return (0); 316 | } 317 | 318 | static int 319 | hms_detach(device_t dev) 320 | { 321 | struct hms_softc *sc = device_get_softc(dev); 322 | int error; 323 | 324 | error = hidmap_detach(&sc->hm); 325 | if (error == 0) 326 | free(sc->last_ir, M_DEVBUF); 327 | return (error); 328 | } 329 | 330 | static devclass_t hms_devclass; 331 | static device_method_t hms_methods[] = { 332 | DEVMETHOD(device_identify, hms_identify), 333 | DEVMETHOD(device_probe, hms_probe), 334 | DEVMETHOD(device_attach, hms_attach), 335 | DEVMETHOD(device_detach, hms_detach), 336 | 337 | DEVMETHOD_END 338 | }; 339 | 340 | DEFINE_CLASS_0(hms, hms_driver, hms_methods, sizeof(struct hms_softc)); 341 | DRIVER_MODULE(hms, hidbus, hms_driver, hms_devclass, NULL, 0); 342 | MODULE_DEPEND(hms, hid, 1, 1, 1); 343 | MODULE_DEPEND(hms, hidmap, 1, 1, 1); 344 | MODULE_DEPEND(hms, evdev, 1, 1, 1); 345 | MODULE_VERSION(hms, 1); 346 | HID_PNP_INFO(hms_devs); 347 | -------------------------------------------------------------------------------- /hmt.4: -------------------------------------------------------------------------------- 1 | .\" Copyright (c) 2014-2020 Vladimir Kondratyev 2 | .\" All rights reserved. 3 | .\" 4 | .\" Redistribution and use in source and binary forms, with or without 5 | .\" modification, are permitted provided that the following conditions 6 | .\" are met: 7 | .\" 1. Redistributions of source code must retain the above copyright 8 | .\" notice, this list of conditions and the following disclaimer. 9 | .\" 2. Redistributions in binary form must reproduce the above copyright 10 | .\" notice, this list of conditions and the following disclaimer in the 11 | .\" documentation and/or other materials provided with the distribution. 12 | .\" 13 | .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14 | .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | .\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 17 | .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 | .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 | .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 | .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 | .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 | .\" SUCH DAMAGE. 24 | .\" 25 | .\" $FreeBSD$ 26 | .\" 27 | .Dd August 11, 2020 28 | .Dt HMT 4 29 | .Os 30 | .Sh NAME 31 | .Nm hmt 32 | .Nd MS Windows 7/8/10 - compatible HID multi-touch device driver 33 | .Sh SYNOPSIS 34 | To compile this driver into the kernel, place the following lines into 35 | your kernel configuration file: 36 | .Bd -ragged -offset indent 37 | .Cd "device hmt" 38 | .Cd "device hidbus" 39 | .Cd "device hid" 40 | .Cd "device hconf" 41 | .Cd "device evdev" 42 | 43 | .Ed 44 | .Pp 45 | Alternatively, to load the driver as a 46 | module at boot time, place the following line in 47 | .Xr loader.conf 5 : 48 | .Bd -literal -offset indent 49 | hmt_load="YES" 50 | .Ed 51 | .Sh DESCRIPTION 52 | The 53 | .Nm 54 | driver provides support for the MS Windows 7/8/10 - compatible HID 55 | multi-touch devices found in many laptops. 56 | .Pp 57 | To get multi-touch device working in 58 | .Xr X 7 , 59 | install 60 | .Pa ports/x11-drivers/xf86-input-evdev . 61 | .Sh FILES 62 | .Nm 63 | creates a pseudo-device file, 64 | .Pa /dev/input/eventX 65 | which presents the multi-touch device as an input event device. 66 | .Sh SEE ALSO 67 | .Xr hid 4 , 68 | .Xr loader.conf 5 , 69 | .Xr xorg.conf 5 Pq Pa ports/x11/xorg , 70 | .Xr evdev 4 Pq Pa ports/x11-drivers/xf86-input-evdev . 71 | .Sh AUTHORS 72 | .An -nosplit 73 | The 74 | .Nm 75 | driver was written by 76 | .An Vladimir Kondratyev Aq Mt wulf@FreeBSD.org . 77 | .Sh BUGS 78 | .Nm 79 | cannot act like 80 | .Xr sysmouse 4 81 | -------------------------------------------------------------------------------- /hpen.4: -------------------------------------------------------------------------------- 1 | .\" Copyright (c) 2020 Vladimir Kondratyev 2 | .\" 3 | .\" Redistribution and use in source and binary forms, with or without 4 | .\" modification, are permitted provided that the following conditions 5 | .\" are met: 6 | .\" 1. Redistributions of source code must retain the above copyright 7 | .\" notice, this list of conditions and the following disclaimer. 8 | .\" 2. Redistributions in binary form must reproduce the above copyright 9 | .\" notice, this list of conditions and the following disclaimer in the 10 | .\" documentation and/or other materials provided with the distribution. 11 | .\" 12 | .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 13 | .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 14 | .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 15 | .\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 16 | .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 17 | .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 18 | .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 19 | .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 20 | .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 21 | .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 22 | .\" SUCH DAMAGE. 23 | .\" 24 | .\" $FreeBSD$ 25 | .\" 26 | .Dd September 14, 2020 27 | .Dt HPEN 4 28 | .Os 29 | .Sh NAME 30 | .Nm hpen 31 | .Nd MS Windows compatible HID pen tablet driver 32 | .Sh SYNOPSIS 33 | To compile this driver into the kernel, 34 | place the following lines in your 35 | kernel configuration file: 36 | .Bd -ragged -offset indent 37 | .Cd "device hpen" 38 | .Cd "device hid" 39 | .Cd "device hidbus" 40 | .Cd "device hidmap" 41 | .Cd "device evdev" 42 | .Ed 43 | .Pp 44 | Alternatively, to load the driver as a 45 | module at boot time, place the following line in 46 | .Xr loader.conf 5 : 47 | .Bd -literal -offset indent 48 | hpen_load="YES" 49 | .Ed 50 | .Sh DESCRIPTION 51 | The 52 | .Nm 53 | driver provides support for generic MS Windows compatible HID pen tablet 54 | and digitizer that attach to the HID transport backend. 55 | See 56 | .Xr iichid 4 57 | or 58 | .Xr usbhid 4 . 59 | .Pp 60 | The 61 | .Pa /dev/input/event* 62 | device presents the pen as a 63 | .Ar evdev 64 | type device. 65 | .Sh SYSCTL VARIABLES 66 | The following variables are available as both 67 | .Xr sysctl 8 68 | variables and 69 | .Xr loader 8 70 | tunables: 71 | .Bl -tag -width indent 72 | .It Va hw.hid.hpen.debug 73 | Debug output level, where 0 is debugging disabled and larger values increase 74 | debug message verbosity. 75 | Default is 0. 76 | .El 77 | .Sh FILES 78 | .Bl -tag -width /dev/input/event* -compact 79 | .It Pa /dev/input/event* 80 | input event device node. 81 | .El 82 | .Sh SEE ALSO 83 | .Xr iichid 4 , 84 | .Xr usbhid 4 , 85 | .Xr xorg.conf 5 Pq Pa ports/x11/xorg 86 | .Sh BUGS 87 | .Nm 88 | cannot act like 89 | .Xr sysmouse 4 . 90 | .Pp 91 | Pen battery charge level reporting is not supported. 92 | .Sh HISTORY 93 | The 94 | .Nm 95 | driver first appeared in 96 | .Fx 13.0. 97 | .Sh AUTHORS 98 | .An -nosplit 99 | The 100 | .Nm 101 | driver was written by 102 | .An Greg V Aq Mt greg@unrelenting.technology . 103 | .Pp 104 | This manual page was written by 105 | .An Vladimir Kondratyev Aq Mt wulf@FreeBSD.org . 106 | -------------------------------------------------------------------------------- /hpen.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 | * 4 | * Copyright (c) 2019 Vladimir Kondratyev 5 | * Copyright (c) 2019 Greg V 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 30 | __FBSDID("$FreeBSD$"); 31 | 32 | /* 33 | * Generic / MS Windows compatible HID pen tablet driver: 34 | * https://docs.microsoft.com/en-us/windows-hardware/design/component-guidelines/required-hid-top-level-collections 35 | * 36 | * Tested on: Wacom WCOM50C1 (Google Pixelbook "eve") 37 | */ 38 | 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | 45 | #include 46 | #include 47 | 48 | #include "usbdevs.h" 49 | #include 50 | 51 | #include "hid.h" 52 | #include "hidbus.h" 53 | #include "hidmap.h" 54 | 55 | static const uint8_t hpen_graphire_report_descr[] = 56 | { UHID_GRAPHIRE_REPORT_DESCR() }; 57 | static const uint8_t hpen_graphire3_4x5_report_descr[] = 58 | { UHID_GRAPHIRE3_4X5_REPORT_DESCR() }; 59 | 60 | static hidmap_cb_t hpen_battery_strenght_cb; 61 | static hidmap_cb_t hpen_final_digi_cb; 62 | static hidmap_cb_t hpen_final_pen_cb; 63 | 64 | #define HPEN_MAP_BUT(usage, code) \ 65 | HIDMAP_KEY(HUP_DIGITIZERS, HUD_##usage, code) 66 | #define HPEN_MAP_ABS(usage, code) \ 67 | HIDMAP_ABS(HUP_DIGITIZERS, HUD_##usage, code) 68 | #define HPEN_MAP_ABS_GD(usage, code) \ 69 | HIDMAP_ABS(HUP_GENERIC_DESKTOP, HUG_##usage, code) 70 | #define HPEN_MAP_ABS_CB(usage, cb) \ 71 | HIDMAP_ABS_CB(HUP_DIGITIZERS, HUD_##usage, &cb) 72 | 73 | /* Generic map digitizer page map according to hut1_12v2.pdf */ 74 | static const struct hidmap_item hpen_map_digi[] = { 75 | { HPEN_MAP_ABS_GD(X, ABS_X), .required = true }, 76 | { HPEN_MAP_ABS_GD(Y, ABS_Y), .required = true }, 77 | { HPEN_MAP_ABS( TIP_PRESSURE, ABS_PRESSURE) }, 78 | { HPEN_MAP_ABS( X_TILT, ABS_TILT_X) }, 79 | { HPEN_MAP_ABS( Y_TILT, ABS_TILT_Y) }, 80 | { HPEN_MAP_ABS_CB(BATTERY_STRENGTH, hpen_battery_strenght_cb) }, 81 | { HPEN_MAP_BUT( TOUCH, BTN_TOUCH) }, 82 | { HPEN_MAP_BUT( TIP_SWITCH, BTN_TOUCH) }, 83 | { HPEN_MAP_BUT( SEC_TIP_SWITCH, BTN_TOUCH) }, 84 | { HPEN_MAP_BUT( IN_RANGE, BTN_TOOL_PEN) }, 85 | { HPEN_MAP_BUT( BARREL_SWITCH, BTN_STYLUS) }, 86 | { HPEN_MAP_BUT( INVERT, BTN_TOOL_RUBBER) }, 87 | { HPEN_MAP_BUT( ERASER, BTN_TOUCH) }, 88 | { HPEN_MAP_BUT( TABLET_PICK, BTN_STYLUS2) }, 89 | { HPEN_MAP_BUT( SEC_BARREL_SWITCH,BTN_STYLUS2) }, 90 | { HIDMAP_FINAL_CB( &hpen_final_digi_cb) }, 91 | }; 92 | 93 | /* Microsoft-standardized pen support */ 94 | static const struct hidmap_item hpen_map_pen[] = { 95 | { HPEN_MAP_ABS_GD(X, ABS_X), .required = true }, 96 | { HPEN_MAP_ABS_GD(Y, ABS_Y), .required = true }, 97 | { HPEN_MAP_ABS( TIP_PRESSURE, ABS_PRESSURE), .required = true }, 98 | { HPEN_MAP_ABS( X_TILT, ABS_TILT_X) }, 99 | { HPEN_MAP_ABS( Y_TILT, ABS_TILT_Y) }, 100 | { HPEN_MAP_ABS_CB(BATTERY_STRENGTH, hpen_battery_strenght_cb) }, 101 | { HPEN_MAP_BUT( TIP_SWITCH, BTN_TOUCH), .required = true }, 102 | { HPEN_MAP_BUT( IN_RANGE, BTN_TOOL_PEN), .required = true }, 103 | { HPEN_MAP_BUT( BARREL_SWITCH, BTN_STYLUS) }, 104 | { HPEN_MAP_BUT( INVERT, BTN_TOOL_RUBBER), .required = true }, 105 | { HPEN_MAP_BUT( ERASER, BTN_TOUCH), .required = true }, 106 | { HIDMAP_FINAL_CB( &hpen_final_pen_cb) }, 107 | }; 108 | 109 | static const struct hid_device_id hpen_devs[] = { 110 | { HID_TLC(HUP_DIGITIZERS, HUD_DIGITIZER) }, 111 | { HID_TLC(HUP_DIGITIZERS, HUD_PEN) }, 112 | }; 113 | 114 | static int 115 | hpen_battery_strenght_cb(HIDMAP_CB_ARGS) 116 | { 117 | struct evdev_dev *evdev = HIDMAP_CB_GET_EVDEV(); 118 | int32_t data; 119 | 120 | switch (HIDMAP_CB_GET_STATE()) { 121 | case HIDMAP_CB_IS_ATTACHING: 122 | evdev_support_event(evdev, EV_PWR); 123 | /* TODO */ 124 | break; 125 | case HIDMAP_CB_IS_RUNNING: 126 | data = ctx.data; 127 | /* TODO */ 128 | break; 129 | default: 130 | break; 131 | } 132 | 133 | return (0); 134 | } 135 | 136 | static int 137 | hpen_final_digi_cb(HIDMAP_CB_ARGS) 138 | { 139 | struct evdev_dev *evdev = HIDMAP_CB_GET_EVDEV(); 140 | 141 | if (HIDMAP_CB_GET_STATE() == HIDMAP_CB_IS_ATTACHING) 142 | evdev_support_prop(evdev, INPUT_PROP_POINTER); 143 | 144 | /* Do not execute callback at interrupt handler and detach */ 145 | return (ENOSYS); 146 | } 147 | 148 | static int 149 | hpen_final_pen_cb(HIDMAP_CB_ARGS) 150 | { 151 | struct evdev_dev *evdev = HIDMAP_CB_GET_EVDEV(); 152 | 153 | if (HIDMAP_CB_GET_STATE() == HIDMAP_CB_IS_ATTACHING) 154 | evdev_support_prop(evdev, INPUT_PROP_DIRECT); 155 | 156 | /* Do not execute callback at interrupt handler and detach */ 157 | return (ENOSYS); 158 | } 159 | 160 | static void 161 | hpen_identify(driver_t *driver, device_t parent) 162 | { 163 | const struct hid_device_info *hw = hid_get_device_info(parent); 164 | 165 | /* the report descriptor for the Wacom Graphire is broken */ 166 | if (hw->idBus == BUS_USB && hw->idVendor == USB_VENDOR_WACOM) { 167 | switch (hw->idProduct) { 168 | case USB_PRODUCT_WACOM_GRAPHIRE: 169 | hid_set_report_descr(parent, 170 | hpen_graphire_report_descr, 171 | sizeof(hpen_graphire_report_descr)); 172 | break; 173 | 174 | case USB_PRODUCT_WACOM_GRAPHIRE3_4X5: 175 | hid_set_report_descr(parent, 176 | hpen_graphire3_4x5_report_descr, 177 | sizeof(hpen_graphire3_4x5_report_descr)); 178 | break; 179 | } 180 | } 181 | } 182 | 183 | static int 184 | hpen_probe(device_t dev) 185 | { 186 | struct hidmap *hm = device_get_softc(dev); 187 | int error; 188 | bool is_pen; 189 | 190 | error = HIDBUS_LOOKUP_DRIVER_INFO(dev, hpen_devs); 191 | if (error != 0) 192 | return (error); 193 | 194 | hidmap_set_dev(hm, dev); 195 | 196 | /* Check if report descriptor belongs to a HID tablet device */ 197 | is_pen = hidbus_get_usage(dev) == HID_USAGE2(HUP_DIGITIZERS, HUD_PEN); 198 | error = is_pen 199 | ? HIDMAP_ADD_MAP(hm, hpen_map_pen, NULL) 200 | : HIDMAP_ADD_MAP(hm, hpen_map_digi, NULL); 201 | if (error != 0) 202 | return (error); 203 | 204 | hidbus_set_desc(dev, is_pen ? "Pen" : "Digitizer"); 205 | 206 | return (BUS_PROBE_DEFAULT); 207 | } 208 | 209 | static int 210 | hpen_attach(device_t dev) 211 | { 212 | const struct hid_device_info *hw = hid_get_device_info(dev); 213 | struct hidmap *hm = device_get_softc(dev); 214 | int error; 215 | 216 | if (hw->idBus == BUS_USB && hw->idVendor == USB_VENDOR_WACOM && 217 | hw->idProduct == USB_PRODUCT_WACOM_GRAPHIRE3_4X5) { 218 | /* 219 | * The Graphire3 needs 0x0202 to be written to 220 | * feature report ID 2 before it'll start 221 | * returning digitizer data. 222 | */ 223 | static const uint8_t reportbuf[3] = {2, 2, 2}; 224 | error = hid_set_report(dev, reportbuf, sizeof(reportbuf), 225 | HID_FEATURE_REPORT, reportbuf[0]); 226 | if (error) 227 | device_printf(dev, "set feature report failed, " 228 | "error=%d (ignored)\n", error); 229 | } 230 | 231 | return (hidmap_attach(hm)); 232 | } 233 | 234 | static int 235 | hpen_detach(device_t dev) 236 | { 237 | return (hidmap_detach(device_get_softc(dev))); 238 | } 239 | 240 | 241 | static devclass_t hpen_devclass; 242 | static device_method_t hpen_methods[] = { 243 | DEVMETHOD(device_identify, hpen_identify), 244 | DEVMETHOD(device_probe, hpen_probe), 245 | DEVMETHOD(device_attach, hpen_attach), 246 | DEVMETHOD(device_detach, hpen_detach), 247 | 248 | DEVMETHOD_END 249 | }; 250 | 251 | DEFINE_CLASS_0(hpen, hpen_driver, hpen_methods, sizeof(struct hidmap)); 252 | DRIVER_MODULE(hpen, hidbus, hpen_driver, hpen_devclass, NULL, 0); 253 | MODULE_DEPEND(hpen, hid, 1, 1, 1); 254 | MODULE_DEPEND(hpen, hidmap, 1, 1, 1); 255 | MODULE_DEPEND(hpen, evdev, 1, 1, 1); 256 | MODULE_VERSION(hpen, 1); 257 | HID_PNP_INFO(hpen_devs); 258 | -------------------------------------------------------------------------------- /hsctrl.4: -------------------------------------------------------------------------------- 1 | .\" Copyright (c) 2020 Vladimir Kondratyev 2 | .\" 3 | .\" Redistribution and use in source and binary forms, with or without 4 | .\" modification, are permitted provided that the following conditions 5 | .\" are met: 6 | .\" 1. Redistributions of source code must retain the above copyright 7 | .\" notice, this list of conditions and the following disclaimer. 8 | .\" 2. Redistributions in binary form must reproduce the above copyright 9 | .\" notice, this list of conditions and the following disclaimer in the 10 | .\" documentation and/or other materials provided with the distribution. 11 | .\" 12 | .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 13 | .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 14 | .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 15 | .\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 16 | .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 17 | .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 18 | .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 19 | .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 20 | .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 21 | .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 22 | .\" SUCH DAMAGE. 23 | .\" 24 | .\" $FreeBSD$ 25 | .\" 26 | .Dd September 14, 2020 27 | .Dt HSCTRL 4 28 | .Os 29 | .Sh NAME 30 | .Nm hsctrl 31 | .Nd HID system controls driver 32 | .Sh SYNOPSIS 33 | To compile this driver into the kernel, 34 | place the following lines in your 35 | kernel configuration file: 36 | .Bd -ragged -offset indent 37 | .Cd "device hsctrl" 38 | .Cd "device hid" 39 | .Cd "device hidbus" 40 | .Cd "device hidmap" 41 | .Cd "device evdev" 42 | .Ed 43 | .Pp 44 | Alternatively, to load the driver as a 45 | module at boot time, place the following line in 46 | .Xr loader.conf 5 : 47 | .Bd -literal -offset indent 48 | hgame_load="YES" 49 | .Ed 50 | .Sh DESCRIPTION 51 | The 52 | .Nm 53 | driver provides support for HID system controls most often used as 54 | "Power off/Sleep keys" found on many keyboards. 55 | .Pp 56 | The 57 | .Pa /dev/input/event* 58 | device presents the consumer page controls as a 59 | .Ar evdev 60 | type device. 61 | .Sh SYSCTL VARIABLES 62 | The following variables are available as both 63 | .Xr sysctl 8 64 | variables and 65 | .Xr loader 8 66 | tunables: 67 | .Bl -tag -width indent 68 | .It Va hw.hid.hsctrl.debug 69 | Debug output level, where 0 is debugging disabled and larger values increase 70 | debug message verbosity. 71 | Default is 0. 72 | .El 73 | .Sh FILES 74 | .Bl -tag -width /dev/input/event* -compact 75 | .It Pa /dev/input/event* 76 | input event device node. 77 | .El 78 | .Sh SEE ALSO 79 | .Xr iichid 4 , 80 | .Xr usbhid 4 81 | .Sh HISTORY 82 | The 83 | .Nm 84 | driver first appeared in 85 | .Fx 13.0. 86 | .Sh AUTHORS 87 | .An -nosplit 88 | The 89 | .Nm 90 | driver was written by 91 | .An Vladimir Kondratyev Aq Mt wulf@FreeBSD.org . 92 | -------------------------------------------------------------------------------- /hsctrl.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 | * 4 | * Copyright (c) 2020 Vladimir Kondratyev 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 | * SUCH DAMAGE. 26 | */ 27 | 28 | #include 29 | __FBSDID("$FreeBSD$"); 30 | 31 | /* 32 | * General Desktop/System Controls usage page driver 33 | * https://www.usb.org/sites/default/files/documents/hut1_12v2.pdf 34 | */ 35 | 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | 43 | #include 44 | #include 45 | 46 | #include "hid.h" 47 | #include "hidbus.h" 48 | #include "hidmap.h" 49 | 50 | #define HUG_SYSTEM_POWER_UP 0x008e 51 | #define HUG_SYSTEM_RESTART 0x008f 52 | 53 | #define HSCTRL_MAP(usage, code) \ 54 | { HIDMAP_KEY(HUP_GENERIC_DESKTOP, HUG_SYSTEM_##usage, code) } 55 | 56 | static const struct hidmap_item hsctrl_map[] = { 57 | HSCTRL_MAP(POWER_DOWN, KEY_POWER), 58 | HSCTRL_MAP(SLEEP, KEY_SLEEP), 59 | HSCTRL_MAP(WAKEUP, KEY_WAKEUP), 60 | HSCTRL_MAP(CONTEXT_MENU, KEY_CONTEXT_MENU), 61 | HSCTRL_MAP(MAIN_MENU, KEY_MENU), 62 | HSCTRL_MAP(APP_MENU, KEY_PROG1), 63 | HSCTRL_MAP(MENU_HELP, KEY_HELP), 64 | HSCTRL_MAP(MENU_EXIT, KEY_EXIT), 65 | HSCTRL_MAP(MENU_SELECT, KEY_SELECT), 66 | HSCTRL_MAP(MENU_RIGHT, KEY_RIGHT), 67 | HSCTRL_MAP(MENU_LEFT, KEY_LEFT), 68 | HSCTRL_MAP(MENU_UP, KEY_UP), 69 | HSCTRL_MAP(MENU_DOWN, KEY_DOWN), 70 | HSCTRL_MAP(POWER_UP, KEY_POWER2), 71 | HSCTRL_MAP(RESTART, KEY_RESTART), 72 | }; 73 | 74 | static const struct hid_device_id hsctrl_devs[] = { 75 | { HID_TLC(HUP_GENERIC_DESKTOP, HUG_SYSTEM_CONTROL) }, 76 | }; 77 | 78 | static int 79 | hsctrl_probe(device_t dev) 80 | { 81 | return (HIDMAP_PROBE(device_get_softc(dev), dev, 82 | hsctrl_devs, hsctrl_map, "System Control")); 83 | } 84 | 85 | static int 86 | hsctrl_attach(device_t dev) 87 | { 88 | return (hidmap_attach(device_get_softc(dev))); 89 | } 90 | 91 | static int 92 | hsctrl_detach(device_t dev) 93 | { 94 | return (hidmap_detach(device_get_softc(dev))); 95 | } 96 | 97 | static devclass_t hsctrl_devclass; 98 | static device_method_t hsctrl_methods[] = { 99 | DEVMETHOD(device_probe, hsctrl_probe), 100 | DEVMETHOD(device_attach, hsctrl_attach), 101 | DEVMETHOD(device_detach, hsctrl_detach), 102 | 103 | DEVMETHOD_END 104 | }; 105 | 106 | DEFINE_CLASS_0(hsctrl, hsctrl_driver, hsctrl_methods, sizeof(struct hidmap)); 107 | DRIVER_MODULE(hsctrl, hidbus, hsctrl_driver, hsctrl_devclass, NULL, 0); 108 | MODULE_DEPEND(hsctrl, hid, 1, 1, 1); 109 | MODULE_DEPEND(hsctrl, hidmap, 1, 1, 1); 110 | MODULE_DEPEND(hsctrl, evdev, 1, 1, 1); 111 | MODULE_VERSION(hsctrl, 1); 112 | HID_PNP_INFO(hsctrl_devs); 113 | -------------------------------------------------------------------------------- /hskbd.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 | * 4 | * Copyright (c) 2020 Vladimir Kondratyev 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 | * SUCH DAMAGE. 26 | */ 27 | 28 | #include 29 | __FBSDID("$FreeBSD$"); 30 | 31 | /* 32 | * Simple evdev-only HID kbd driver. Does not support or depend on VT/SysCons. 33 | * HID specs: https://www.usb.org/sites/default/files/documents/hid1_11.pdf 34 | */ 35 | 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | 44 | #include 45 | #include 46 | 47 | #include "hid.h" 48 | #include "hidbus.h" 49 | #include "hidquirk.h" 50 | #include "hidmap.h" 51 | 52 | #define HID_DEBUG_VAR hskbd_debug 53 | #include "hid_debug.h" 54 | 55 | #ifdef HID_DEBUG 56 | static int hskbd_debug = 1; 57 | 58 | static SYSCTL_NODE(_hw_hid, OID_AUTO, hskbd, CTLFLAG_RW, 0, 59 | "Simple keyboard"); 60 | SYSCTL_INT(_hw_hid_hskbd, OID_AUTO, debug, CTLFLAG_RWTUN, 61 | &hskbd_debug, 0, "Debug level"); 62 | #endif 63 | 64 | static const uint8_t hskbd_boot_desc[] = { 65 | 0x05, 0x01, // Usage Page (Generic Desktop Ctrls) 66 | 0x09, 0x06, // Usage (Keyboard) 67 | 0xA1, 0x01, // Collection (Application) 68 | 0x05, 0x07, // Usage Page (Kbrd/Keypad) 69 | 0x19, 0xE0, // Usage Minimum (0xE0) 70 | 0x29, 0xE7, // Usage Maximum (0xE7) 71 | 0x15, 0x00, // Logical Minimum (0) 72 | 0x25, 0x01, // Logical Maximum (1) 73 | 0x75, 0x01, // Report Size (1) 74 | 0x95, 0x08, // Report Count (8) 75 | 0x81, 0x02, // Input (Data,Var,Abs) 76 | 0x95, 0x01, // Report Count (1) 77 | 0x75, 0x08, // Report Size (8) 78 | 0x81, 0x01, // Input (Const,Array,Abs) 79 | 0x95, 0x03, // Report Count (3) 80 | 0x75, 0x01, // Report Size (1) 81 | 0x05, 0x08, // Usage Page (LEDs) 82 | 0x19, 0x01, // Usage Minimum (Num Lock) 83 | 0x29, 0x03, // Usage Maximum (Scroll Lock) 84 | 0x91, 0x02, // Output (Data,Var,Abs) 85 | 0x95, 0x05, // Report Count (5) 86 | 0x75, 0x01, // Report Size (1) 87 | 0x91, 0x01, // Output (Const,Array,Abs) 88 | 0x95, 0x06, // Report Count (6) 89 | 0x75, 0x08, // Report Size (8) 90 | 0x15, 0x00, // Logical Minimum (0) 91 | 0x26, 0xFF, 0x00, // Logical Maximum (255) 92 | 0x05, 0x07, // Usage Page (Kbrd/Keypad) 93 | 0x19, 0x00, // Usage Minimum (0x00) 94 | 0x2A, 0xFF, 0x00, // Usage Maximum (0xFF) 95 | 0x81, 0x00, // Input (Data,Array,Abs) 96 | 0xC0, // End Collection 97 | }; 98 | 99 | #define HSKBD_BUFFER_SIZE 8 /* bytes */ 100 | 101 | static evdev_event_t hskbd_ev_event; 102 | static hidmap_cb_t hskbd_final_cb; 103 | 104 | #define HSKBD_KEY(usage, code) { HIDMAP_KEY(HUP_KEYBOARD, usage, code) } 105 | #define HSKBD_FINAL_CB(cb) { HIDMAP_FINAL_CB(&cb) } 106 | 107 | static struct hidmap_item hskbd_map[256] = { 108 | HSKBD_KEY(0x00, KEY_RESERVED), /* No event indicated */ 109 | HSKBD_KEY(0x01, HIDMAP_KEY_NULL), /* Error RollOver */ 110 | HSKBD_KEY(0x02, HIDMAP_KEY_NULL), /* POSTFail */ 111 | HSKBD_KEY(0x03, KEY_RESERVED), /* Error Undefined */ 112 | HSKBD_FINAL_CB(hskbd_final_cb), 113 | }; 114 | /* Map items starting from 5-th are filled by SYSINIT handler */ 115 | static int hskbd_nmap_items = 5; 116 | static void 117 | hskbd_init(void) 118 | { 119 | uint16_t code, i; 120 | 121 | for (i = 4; i < 0x100; i++) 122 | if ((code = evdev_hid2key(i)) != KEY_RESERVED) 123 | hskbd_map[hskbd_nmap_items++] = 124 | (struct hidmap_item) HSKBD_KEY(i, code); 125 | } 126 | 127 | static const struct hid_device_id hskbd_devs[] = { 128 | { HID_TLC(HUP_GENERIC_DESKTOP, HUG_KEYBOARD) }, 129 | }; 130 | 131 | struct hskbd_softc { 132 | struct hidmap hm; 133 | 134 | /* LED report parameters */ 135 | struct hid_location sc_loc_numlock; 136 | struct hid_location sc_loc_capslock; 137 | struct hid_location sc_loc_scrolllock; 138 | int sc_led_size; 139 | uint8_t sc_id_leds; 140 | 141 | /* Flags */ 142 | bool sc_numlock_exists:1; 143 | bool sc_numlock_enabled:1; 144 | bool sc_capslock_exists:1; 145 | bool sc_capslock_enabled:1; 146 | bool sc_scrolllock_exists:1; 147 | bool sc_scrolllock_enabled:1; 148 | u_char reserved:2; 149 | 150 | uint8_t sc_buffer[HSKBD_BUFFER_SIZE]; 151 | }; 152 | 153 | static void 154 | hskbd_ev_event(struct evdev_dev *evdev, uint16_t type, uint16_t code, 155 | int32_t value) 156 | { 157 | device_t dev = evdev_get_softc(evdev); 158 | struct hskbd_softc *sc = device_get_softc(dev); 159 | uint8_t id, *buf; 160 | int len; 161 | 162 | if (type != EV_LED) 163 | return; 164 | 165 | /* If no leds, nothing to do. */ 166 | if (!sc->sc_numlock_exists && !sc->sc_scrolllock_exists && 167 | !sc->sc_capslock_exists) 168 | return; 169 | 170 | DPRINTFN(5, "led(%u)=%d\n", type, value); 171 | 172 | mtx_lock(hidbus_get_lock(dev)); 173 | 174 | switch (code) { 175 | case LED_CAPSL: 176 | sc->sc_capslock_enabled = value != 0; 177 | break; 178 | case LED_NUML: 179 | sc->sc_numlock_enabled = value != 0; 180 | break; 181 | case LED_SCROLLL: 182 | sc->sc_scrolllock_enabled = value != 0; 183 | break; 184 | } 185 | 186 | memset(sc->sc_buffer, 0, HSKBD_BUFFER_SIZE); 187 | id = sc->sc_id_leds; 188 | 189 | /* Assumption: All led bits must be in the same ID. */ 190 | if (sc->sc_numlock_exists) 191 | hid_put_data_unsigned(sc->sc_buffer + 1, HSKBD_BUFFER_SIZE - 1, 192 | &sc->sc_loc_numlock, sc->sc_numlock_enabled ? 1 : 0); 193 | if (sc->sc_scrolllock_exists) 194 | hid_put_data_unsigned(sc->sc_buffer + 1, HSKBD_BUFFER_SIZE - 1, 195 | &sc->sc_loc_scrolllock, sc->sc_scrolllock_enabled ? 1 : 0); 196 | if (sc->sc_capslock_exists) 197 | hid_put_data_unsigned(sc->sc_buffer + 1, HSKBD_BUFFER_SIZE - 1, 198 | &sc->sc_loc_capslock, sc->sc_capslock_enabled ? 1 : 0); 199 | 200 | /* Range check output report length. */ 201 | len = sc->sc_led_size; 202 | if (len > (HSKBD_BUFFER_SIZE - 1)) 203 | len = (HSKBD_BUFFER_SIZE - 1); 204 | 205 | /* Check if we need to prefix an ID byte. */ 206 | if (id != 0) { 207 | sc->sc_buffer[0] = id; 208 | buf = sc->sc_buffer; 209 | } else { 210 | buf = sc->sc_buffer + 1; 211 | } 212 | 213 | DPRINTFN(5, "len=%d, id=%d\n", len, id); 214 | 215 | /* Start data transfer. */ 216 | evdev_push_event(sc->hm.evdev, type, code, value); 217 | mtx_unlock(hidbus_get_lock(dev)); 218 | hid_write(dev, buf, len); 219 | } 220 | 221 | static int 222 | hskbd_final_cb(HIDMAP_CB_ARGS) 223 | { 224 | struct hskbd_softc *sc = HIDMAP_CB_GET_SOFTC(); 225 | struct evdev_dev *evdev = HIDMAP_CB_GET_EVDEV(); 226 | 227 | if (HIDMAP_CB_GET_STATE() == HIDMAP_CB_IS_ATTACHING) { 228 | if (sc->sc_numlock_exists || sc->sc_capslock_exists || 229 | sc->sc_scrolllock_exists) 230 | evdev_support_event(evdev, EV_LED); 231 | if (sc->sc_numlock_exists) 232 | evdev_support_led(evdev, LED_NUML); 233 | if (sc->sc_capslock_exists) 234 | evdev_support_led(evdev, LED_CAPSL); 235 | if (sc->sc_scrolllock_exists) 236 | evdev_support_led(evdev, LED_SCROLLL); 237 | evdev_support_event(evdev, EV_REP); 238 | evdev_set_flag(evdev, EVDEV_FLAG_SOFTREPEAT); 239 | sc->hm.evdev_methods.ev_event = &hskbd_ev_event; 240 | } 241 | 242 | /* Do not execute callback at interrupt handler and detach */ 243 | return (ENOSYS); 244 | } 245 | 246 | static void 247 | hskbd_identify(driver_t *driver, device_t parent) 248 | { 249 | const struct hid_device_info *hw = hid_get_device_info(parent); 250 | void *d_ptr; 251 | hid_size_t d_len; 252 | int error; 253 | 254 | /* 255 | * If device claimed boot protocol support but do not have report 256 | * descriptor, load one defined in "Appendix B.2" of HID1_11.pdf 257 | */ 258 | error = hid_get_report_descr(parent, &d_ptr, &d_len); 259 | if ((error != 0 && hid_test_quirk(hw, HQ_HAS_KBD_BOOTPROTO)) || 260 | (error == 0 && hid_test_quirk(hw, HQ_KBD_BOOTPROTO) && 261 | hid_is_keyboard(d_ptr, d_len))) 262 | (void)hid_set_report_descr(parent, hskbd_boot_desc, 263 | sizeof(hskbd_boot_desc)); 264 | } 265 | 266 | static int 267 | hskbd_probe(device_t dev) 268 | { 269 | struct hskbd_softc *sc = device_get_softc(dev); 270 | 271 | hidmap_set_debug_var(&sc->hm, &HID_DEBUG_VAR); 272 | return (hidmap_probe(&sc->hm, dev, hskbd_devs, nitems(hskbd_devs), 273 | hskbd_map, hskbd_nmap_items, "Simple Keyboard", NULL)); 274 | } 275 | 276 | static int 277 | hskbd_attach(device_t dev) 278 | { 279 | struct hskbd_softc *sc = device_get_softc(dev); 280 | void *d_ptr; 281 | hid_size_t d_len; 282 | bool set_report_proto; 283 | int error; 284 | uint32_t flags; 285 | uint8_t id; 286 | uint8_t tlc_index = hidbus_get_index(dev); 287 | 288 | /* 289 | * Set the report (non-boot) protocol if report descriptor has not been 290 | * overloaded with boot protocol report descriptor. 291 | * 292 | * Keyboards without boot protocol support may choose not to implement 293 | * Set_Protocol at all; Ignore any error. 294 | */ 295 | error = hid_get_report_descr(dev, &d_ptr, &d_len); 296 | set_report_proto = !(error == 0 && d_len == sizeof(hskbd_boot_desc) && 297 | memcmp(d_ptr, hskbd_boot_desc, sizeof(hskbd_boot_desc)) == 0); 298 | (void)hid_set_protocol(dev, set_report_proto ? 1 : 0); 299 | 300 | /* figure out leds on keyboard */ 301 | if (hidbus_locate(d_ptr, d_len, HID_USAGE2(HUP_LEDS, 0x01), 302 | hid_output, tlc_index, 0, &sc->sc_loc_numlock, &flags, 303 | &sc->sc_id_leds, NULL)) { 304 | if (flags & HIO_VARIABLE) 305 | sc->sc_numlock_exists = true; 306 | DPRINTFN(1, "Found keyboard numlock\n"); 307 | } 308 | if (hidbus_locate(d_ptr, d_len, HID_USAGE2(HUP_LEDS, 0x02), 309 | hid_output, tlc_index, 0, &sc->sc_loc_capslock, &flags, 310 | &id, NULL)) { 311 | if (!sc->sc_numlock_exists) 312 | sc->sc_id_leds = id; 313 | if (flags & HIO_VARIABLE && sc->sc_id_leds == id) 314 | sc->sc_capslock_exists = true; 315 | DPRINTFN(1, "Found keyboard capslock\n"); 316 | } 317 | if (hidbus_locate(d_ptr, d_len, HID_USAGE2(HUP_LEDS, 0x03), 318 | hid_output, tlc_index, 0, &sc->sc_loc_scrolllock, &flags, 319 | &id, NULL)) { 320 | if (!sc->sc_numlock_exists && !sc->sc_capslock_exists) 321 | sc->sc_id_leds = id; 322 | if (flags & HIO_VARIABLE && sc->sc_id_leds == id) 323 | sc->sc_scrolllock_exists = true; 324 | DPRINTFN(1, "Found keyboard scrolllock\n"); 325 | } 326 | 327 | if (sc->sc_numlock_exists || sc->sc_capslock_exists || 328 | sc->sc_scrolllock_exists) 329 | sc->sc_led_size = hid_report_size_1(d_ptr, d_len, 330 | hid_output, sc->sc_id_leds); 331 | 332 | return (hidmap_attach(&sc->hm)); 333 | } 334 | 335 | static int 336 | hskbd_detach(device_t dev) 337 | { 338 | struct hskbd_softc *sc = device_get_softc(dev); 339 | 340 | return (hidmap_detach(&sc->hm)); 341 | } 342 | 343 | static devclass_t hskbd_devclass; 344 | static device_method_t hskbd_methods[] = { 345 | DEVMETHOD(device_identify, hskbd_identify), 346 | DEVMETHOD(device_probe, hskbd_probe), 347 | DEVMETHOD(device_attach, hskbd_attach), 348 | DEVMETHOD(device_detach, hskbd_detach), 349 | 350 | DEVMETHOD_END 351 | }; 352 | 353 | SYSINIT(hskbd_init, SI_SUB_DRIVERS, SI_ORDER_ANY, hskbd_init, NULL); 354 | 355 | DEFINE_CLASS_0(hskbd, hskbd_driver, hskbd_methods, sizeof(struct hskbd_softc)); 356 | DRIVER_MODULE(hskbd, hidbus, hskbd_driver, hskbd_devclass, NULL, 0); 357 | MODULE_DEPEND(hskbd, hid, 1, 1, 1); 358 | MODULE_DEPEND(hskbd, hidmap, 1, 1, 1); 359 | MODULE_DEPEND(hskbd, evdev, 1, 1, 1); 360 | MODULE_VERSION(hskbd, 1); 361 | HID_PNP_INFO(hskbd_devs); 362 | -------------------------------------------------------------------------------- /iichid.4: -------------------------------------------------------------------------------- 1 | .\" Copyright (c) 2020 Vladimir Kondratyev 2 | .\" 3 | .\" Redistribution and use in source and binary forms, with or without 4 | .\" modification, are permitted provided that the following conditions 5 | .\" are met: 6 | .\" 1. Redistributions of source code must retain the above copyright 7 | .\" notice, this list of conditions and the following disclaimer. 8 | .\" 2. Redistributions in binary form must reproduce the above copyright 9 | .\" notice, this list of conditions and the following disclaimer in the 10 | .\" documentation and/or other materials provided with the distribution. 11 | .\" 12 | .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 13 | .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 14 | .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 15 | .\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 16 | .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 17 | .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 18 | .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 19 | .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 20 | .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 21 | .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 22 | .\" SUCH DAMAGE. 23 | .\" 24 | .\" $FreeBSD$ 25 | .\" 26 | .Dd September 21, 2020 27 | .Dt IICHID 4 28 | .Os 29 | .Sh NAME 30 | .Nm iichid 31 | .Nd I2C HID transport driver 32 | .Sh SYNOPSIS 33 | To compile this driver into the kernel, 34 | place the following lines in your 35 | kernel configuration file: 36 | .Bd -ragged -offset indent 37 | .Cd "device iichid" 38 | .Ed 39 | .Pp 40 | Alternatively, to load the driver as a 41 | module at boot time, place the following line in 42 | .Xr loader.conf 5 : 43 | .Bd -literal -offset indent 44 | iichid_load="YES" 45 | .Ed 46 | .Sh DESCRIPTION 47 | The 48 | .Nm 49 | driver provides a interface to I2C Human Interface Devices (HIDs). 50 | .Sh SYSCTL VARIABLES 51 | Next parameters are available as 52 | .Xr sysctl 8 53 | variables. 54 | Debug parameter is available as 55 | .Xr loader 8 56 | tunable as well. 57 | .Bl -tag -width indent 58 | .It Va dev.iichid.*.sampling_rate_fast 59 | Active sampling rate in num/second (for sampling mode). 60 | .It Va dev.iichid.*.sampling_rate_slow 61 | Idle sampling rate in num/second (for sampling mode). 62 | .It Va dev.iichid.*.sampling_hysteresis 63 | Number of missing samples before enabling of slow mode (for sampling mode). 64 | .It Va hw.iichid.debug 65 | Debug output level, where 0 is debugging disabled and larger values increase 66 | debug message verbosity. 67 | Default is 0. 68 | .El 69 | .Sh SEE ALSO 70 | .Xr ig4 4 71 | .Sh BUGS 72 | The 73 | .Nm 74 | does not support GPIO interrupts yet. 75 | In that case 76 | .Nm 77 | enables sampling mode with periodic polling of hardware by driver means. 78 | See dev.iichid.*.sampling_* 79 | .Xr sysctl 4 80 | variables for tuning of sampling parameters. 81 | .Sh HISTORY 82 | The 83 | .Nm 84 | driver first appeared in 85 | .Fx 13.0. 86 | .Sh AUTHORS 87 | .An -nosplit 88 | The 89 | .Nm 90 | driver was written by 91 | .An Marc Priggemeyer Aq Mt marc.priggemeyer@gmail.com 92 | and 93 | .An Vladimir Kondratyev Aq Mt wulf@FreeBSD.org . 94 | .Pp 95 | This manual page was written by 96 | .An Vladimir Kondratyev Aq Mt wulf@FreeBSD.org . 97 | -------------------------------------------------------------------------------- /ps4dshock.4: -------------------------------------------------------------------------------- 1 | .\" Copyright (c) 2020 Vladimir Kondratyev 2 | .\" 3 | .\" Redistribution and use in source and binary forms, with or without 4 | .\" modification, are permitted provided that the following conditions 5 | .\" are met: 6 | .\" 1. Redistributions of source code must retain the above copyright 7 | .\" notice, this list of conditions and the following disclaimer. 8 | .\" 2. Redistributions in binary form must reproduce the above copyright 9 | .\" notice, this list of conditions and the following disclaimer in the 10 | .\" documentation and/or other materials provided with the distribution. 11 | .\" 12 | .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 13 | .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 14 | .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 15 | .\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 16 | .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 17 | .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 18 | .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 19 | .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 20 | .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 21 | .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 22 | .\" SUCH DAMAGE. 23 | .\" 24 | .\" $FreeBSD$ 25 | .\" 26 | .Dd September 19, 2020 27 | .Dt PS4DSHOCK 4 28 | .Os 29 | .Sh NAME 30 | .Nm ps4dshock 31 | .Nd Sony PlayStation 4 Dualshock 4 gamepad driver 32 | .Sh SYNOPSIS 33 | To compile this driver into the kernel, 34 | place the following lines in your 35 | kernel configuration file: 36 | .Bd -ragged -offset indent 37 | .Cd "device ps4dshock" 38 | .Cd "device hid" 39 | .Cd "device hidbus" 40 | .Cd "device hidmap" 41 | .Cd "device evdev" 42 | .Ed 43 | .Pp 44 | Alternatively, to load the driver as a 45 | module at boot time, place the following line in 46 | .Xr loader.conf 5 : 47 | .Bd -literal -offset indent 48 | ps4dshock_load="YES" 49 | .Ed 50 | .Sh DESCRIPTION 51 | The 52 | .Nm 53 | driver provides support for Sony PlayStation 4 Dualshock 4 gamepad driver. 54 | .Pp 55 | The 56 | .Pa /dev/input/event* 57 | device presents the game controller as a 58 | .Ar evdev 59 | type device. 60 | .Sh SYSCTL VARIABLES 61 | Next parameters are available as 62 | .Xr sysctl 8 63 | variables. 64 | Debug parameter is available as 65 | .Xr loader 8 66 | tunable as well. 67 | .Bl -tag -width indent 68 | .It Va dev.p4dshock.*.led_state 69 | LED state: 0 - off, 1 - on, 2 - blinking. 70 | .It Va dev.p4dshock.*.led_color_r 71 | LED color. 72 | Red component. 73 | .It Va dev.p4dshock.*.led_color_g 74 | LED color. 75 | Green component. 76 | .It Va dev.p4dshock.*.led_color_b 77 | LED color. 78 | Blue component. 79 | .It Va dev.p4dshock.*.led_delay_on 80 | LED blink. 81 | On delay, msecs. 82 | .It Va dev.p4dshock.*.led_delay_off 83 | LED blink. 84 | Off delay, msecs. 85 | .It Va hw.hid.ps4dshock.debug 86 | Debug output level, where 0 is debugging disabled and larger values increase 87 | debug message verbosity. 88 | Default is 0. 89 | .El 90 | .Sh FILES 91 | .Bl -tag -width /dev/input/event* -compact 92 | .It Pa /dev/input/event* 93 | input event device node. 94 | .El 95 | .Sh BUGS 96 | The 97 | .Nm 98 | does not support force-feedback events. 99 | .Sh HISTORY 100 | The 101 | .Nm 102 | driver first appeared in 103 | .Fx 13.0. 104 | .Sh AUTHORS 105 | .An -nosplit 106 | The 107 | .Nm 108 | driver was written by 109 | .An Vladimir Kondratyev Aq Mt wulf@FreeBSD.org . 110 | -------------------------------------------------------------------------------- /strcasestr.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * SPDX-License-Identifier: BSD-3-Clause 3 | * 4 | * Copyright (c) 1990, 1993 5 | * The Regents of the University of California. All rights reserved. 6 | * 7 | * This code is derived from software contributed to Berkeley by 8 | * Chris Torek. 9 | * 10 | * Redistribution and use in source and binary forms, with or without 11 | * modification, are permitted provided that the following conditions 12 | * are met: 13 | * 1. Redistributions of source code must retain the above copyright 14 | * notice, this list of conditions and the following disclaimer. 15 | * 2. Redistributions in binary form must reproduce the above copyright 16 | * notice, this list of conditions and the following disclaimer in the 17 | * documentation and/or other materials provided with the distribution. 18 | * 3. Neither the name of the University nor the names of its contributors 19 | * may be used to endorse or promote products derived from this software 20 | * without specific prior written permission. 21 | * 22 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 | * SUCH DAMAGE. 33 | */ 34 | 35 | #include 36 | __FBSDID("$FreeBSD$"); 37 | 38 | #include 39 | #include 40 | #include 41 | 42 | #include "strcasestr.h" 43 | 44 | /* 45 | * Find the first occurrence of find in s, ignore case. 46 | */ 47 | char * 48 | strcasestr(const char *s, const char *find) 49 | { 50 | char c, sc; 51 | size_t len; 52 | 53 | if ((c = *find++) != 0) { 54 | c = tolower((unsigned char)c); 55 | len = strlen(find); 56 | do { 57 | do { 58 | if ((sc = *s++) == 0) 59 | return (NULL); 60 | } while ((char)tolower((unsigned char)sc) != c); 61 | } while (strncasecmp(s, find, len) != 0); 62 | s--; 63 | } 64 | return (__DECONST(char *, s)); 65 | } 66 | -------------------------------------------------------------------------------- /strcasestr.h: -------------------------------------------------------------------------------- 1 | #ifndef _STRCASESTR_H_ 2 | #define _STRCASESTR_H_ 3 | 4 | char *strcasestr(const char *, const char *); 5 | 6 | #endif /* !_STRCASESTR_H_ */ 7 | -------------------------------------------------------------------------------- /usbhid.4: -------------------------------------------------------------------------------- 1 | .\" Copyright (c) 2020 Vladimir Kondratyev 2 | .\" 3 | .\" Redistribution and use in source and binary forms, with or without 4 | .\" modification, are permitted provided that the following conditions 5 | .\" are met: 6 | .\" 1. Redistributions of source code must retain the above copyright 7 | .\" notice, this list of conditions and the following disclaimer. 8 | .\" 2. Redistributions in binary form must reproduce the above copyright 9 | .\" notice, this list of conditions and the following disclaimer in the 10 | .\" documentation and/or other materials provided with the distribution. 11 | .\" 12 | .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 13 | .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 14 | .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 15 | .\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 16 | .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 17 | .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 18 | .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 19 | .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 20 | .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 21 | .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 22 | .\" SUCH DAMAGE. 23 | .\" 24 | .\" $FreeBSD$ 25 | .\" 26 | .Dd September 21, 2020 27 | .Dt USBHID 4 28 | .Os 29 | .Sh NAME 30 | .Nm usbhid 31 | .Nd USB HID transport driver 32 | .Sh SYNOPSIS 33 | To compile this driver into the kernel, 34 | place the following lines in your 35 | kernel configuration file: 36 | .Bd -ragged -offset indent 37 | .Cd "device usbhid" 38 | .Ed 39 | .Pp 40 | Alternatively, to load the driver as a 41 | module at boot time, place the following line in 42 | .Xr loader.conf 5 : 43 | .Bd -literal -offset indent 44 | usbhid_load="YES" 45 | .Ed 46 | .Sh DESCRIPTION 47 | The 48 | .Nm 49 | driver provides a interface to USB Human Interface Devices (HIDs). 50 | .Sh SYSCTL VARIABLES 51 | The following variables are available as both 52 | .Xr sysctl 8 53 | variables and 54 | .Xr loader 8 55 | tunables: 56 | .Bl -tag -width indent 57 | .It Va hw.usb.usbhid.debug 58 | Debug output level, where 0 is debugging disabled and larger values increase 59 | debug message verbosity. 60 | Default is 0. 61 | .El 62 | .Sh SEE ALSO 63 | .Xr ehci 4 , 64 | .Xr ohci 4 , 65 | .Xr uhci 4 , 66 | .Xr usb 4 , 67 | .Xr xhci 4 , 68 | .Xr usbconfig 8 69 | .Sh HISTORY 70 | The 71 | .Nm 72 | driver first appeared in 73 | .Fx 13.0. 74 | .Sh AUTHORS 75 | .An -nosplit 76 | The 77 | .Nm 78 | driver was written by 79 | .An Vladimir Kondratyev Aq Mt wulf@FreeBSD.org . 80 | -------------------------------------------------------------------------------- /xb360gp.4: -------------------------------------------------------------------------------- 1 | .\" Copyright (c) 2020 Vladimir Kondratyev 2 | .\" 3 | .\" Redistribution and use in source and binary forms, with or without 4 | .\" modification, are permitted provided that the following conditions 5 | .\" are met: 6 | .\" 1. Redistributions of source code must retain the above copyright 7 | .\" notice, this list of conditions and the following disclaimer. 8 | .\" 2. Redistributions in binary form must reproduce the above copyright 9 | .\" notice, this list of conditions and the following disclaimer in the 10 | .\" documentation and/or other materials provided with the distribution. 11 | .\" 12 | .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 13 | .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 14 | .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 15 | .\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 16 | .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 17 | .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 18 | .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 19 | .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 20 | .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 21 | .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 22 | .\" SUCH DAMAGE. 23 | .\" 24 | .\" $FreeBSD$ 25 | .\" 26 | .Dd September 16, 2020 27 | .Dt XB360GP 4 28 | .Os 29 | .Sh NAME 30 | .Nm xb360gp 31 | .Nd XBox 360 gamepad driver 32 | .Sh SYNOPSIS 33 | To compile this driver into the kernel, 34 | place the following lines in your 35 | kernel configuration file: 36 | .Bd -ragged -offset indent 37 | .Cd "device xb360gp" 38 | .Cd "device hgame" 39 | .Cd "device hid" 40 | .Cd "device hidbus" 41 | .Cd "device hidmap" 42 | .Cd "device evdev" 43 | .Ed 44 | .Pp 45 | Alternatively, to load the driver as a 46 | module at boot time, place the following line in 47 | .Xr loader.conf 5 : 48 | .Bd -literal -offset indent 49 | xb360gp_load="YES" 50 | .Ed 51 | .Sh DESCRIPTION 52 | The 53 | .Nm 54 | driver provides support for XBox 360 gamepad driver. 55 | .Pp 56 | The 57 | .Pa /dev/input/event* 58 | device presents the game controller as a 59 | .Ar evdev 60 | type device. 61 | .Sh SYSCTL VARIABLES 62 | The following variables are available as both 63 | .Xr sysctl 8 64 | variables and 65 | .Xr loader 8 66 | tunables: 67 | .Bl -tag -width indent 68 | .It Va hw.hid.xb360gp.debug 69 | Debug output level, where 0 is debugging disabled and larger values increase 70 | debug message verbosity. 71 | Default is 0. 72 | .El 73 | .Sh FILES 74 | .Bl -tag -width /dev/input/event* -compact 75 | .It Pa /dev/input/event* 76 | input event device node. 77 | .El 78 | .Sh HISTORY 79 | The 80 | .Nm 81 | driver first appeared in 82 | .Fx 13.0. 83 | .Sh AUTHORS 84 | .An -nosplit 85 | The 86 | .Nm 87 | driver was written by 88 | .An Greg V Aq Mt greg@unrelenting.technology . 89 | .Pp 90 | This manual page was written by 91 | .An Vladimir Kondratyev Aq Mt wulf@FreeBSD.org . 92 | -------------------------------------------------------------------------------- /xb360gp.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 | * 4 | * Copyright (c) 2020 Vladimir Kondratyev 5 | * Copyright (c) 2020 Greg V 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 30 | __FBSDID("$FreeBSD$"); 31 | 32 | /* 33 | * XBox 360 gamepad driver thanks to the custom descriptor in usbhid. 34 | * 35 | * Tested on: SVEN GC-5070 in both XInput (XBox 360) and DirectInput modes 36 | */ 37 | 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | 44 | #include 45 | #include 46 | 47 | #include 48 | 49 | #include "hgame.h" 50 | #include "hid.h" 51 | #include "hidbus.h" 52 | #include "hidquirk.h" 53 | #include "hidmap.h" 54 | 55 | static const uint8_t xb360gp_rdesc[] = {UHID_XB360GP_REPORT_DESCR()}; 56 | 57 | #define XB360GP_MAP_BUT(number, code) \ 58 | { HIDMAP_KEY(HUP_BUTTON, number, code) } 59 | #define XB360GP_MAP_ABS(usage, code) \ 60 | { HIDMAP_ABS(HUP_GENERIC_DESKTOP, HUG_##usage, code) } 61 | #define XB360GP_MAP_ABS_FLT(usage, code) \ 62 | { HIDMAP_ABS(HUP_GENERIC_DESKTOP, HUG_##usage, code), \ 63 | .fuzz = 16, .flat = 128 } 64 | #define XB360GP_MAP_ABS_INV(usage, code) \ 65 | { HIDMAP_ABS(HUP_GENERIC_DESKTOP, HUG_##usage, code), \ 66 | .fuzz = 16, .flat = 128, .invert_value = true } 67 | #define XB360GP_MAP_CRG(usage_from, usage_to, callback) \ 68 | { HIDMAP_ANY_CB_RANGE(HUP_GENERIC_DESKTOP, \ 69 | HUG_##usage_from, HUG_##usage_to, callback) } 70 | #define XB360GP_FINALCB(cb) \ 71 | { HIDMAP_FINAL_CB(&cb) } 72 | 73 | /* Customized to match usbhid's XBox 360 descriptor */ 74 | static const struct hidmap_item xb360gp_map[] = { 75 | XB360GP_MAP_BUT(1, BTN_SOUTH), 76 | XB360GP_MAP_BUT(2, BTN_EAST), 77 | XB360GP_MAP_BUT(3, BTN_WEST), 78 | XB360GP_MAP_BUT(4, BTN_NORTH), 79 | XB360GP_MAP_BUT(5, BTN_TL), 80 | XB360GP_MAP_BUT(6, BTN_TR), 81 | XB360GP_MAP_BUT(7, BTN_SELECT), 82 | XB360GP_MAP_BUT(8, BTN_START), 83 | XB360GP_MAP_BUT(9, BTN_THUMBL), 84 | XB360GP_MAP_BUT(10, BTN_THUMBR), 85 | XB360GP_MAP_BUT(11, BTN_MODE), 86 | XB360GP_MAP_CRG(D_PAD_UP, D_PAD_LEFT, hgame_dpad_cb), 87 | XB360GP_MAP_ABS_FLT(X, ABS_X), 88 | XB360GP_MAP_ABS_INV(Y, ABS_Y), 89 | XB360GP_MAP_ABS(Z, ABS_Z), 90 | XB360GP_MAP_ABS_FLT(RX, ABS_RX), 91 | XB360GP_MAP_ABS_INV(RY, ABS_RY), 92 | XB360GP_MAP_ABS(RZ, ABS_RZ), 93 | XB360GP_FINALCB( hgame_final_cb), 94 | }; 95 | 96 | static const STRUCT_USB_HOST_ID xb360gp_devs[] = { 97 | /* the Xbox 360 gamepad doesn't use the HID class */ 98 | {USB_IFACE_CLASS(UICLASS_VENDOR), 99 | USB_IFACE_SUBCLASS(UISUBCLASS_XBOX360_CONTROLLER), 100 | USB_IFACE_PROTOCOL(UIPROTO_XBOX360_GAMEPAD),}, 101 | }; 102 | 103 | static void 104 | xb360gp_identify(driver_t *driver, device_t parent) 105 | { 106 | const struct hid_device_info *hw = hid_get_device_info(parent); 107 | 108 | /* the Xbox 360 gamepad has no report descriptor */ 109 | if (hid_test_quirk(hw, HQ_IS_XBOX360GP)) 110 | hid_set_report_descr(parent, xb360gp_rdesc, 111 | sizeof(xb360gp_rdesc)); 112 | } 113 | 114 | static int 115 | xb360gp_probe(device_t dev) 116 | { 117 | struct hgame_softc *sc = device_get_softc(dev); 118 | const struct hid_device_info *hw = hid_get_device_info(dev); 119 | int error; 120 | 121 | if (!hid_test_quirk(hw, HQ_IS_XBOX360GP)) 122 | return (ENXIO); 123 | 124 | hidmap_set_dev(&sc->hm, dev); 125 | 126 | error = HIDMAP_ADD_MAP(&sc->hm, xb360gp_map, NULL); 127 | if (error != 0) 128 | return (error); 129 | 130 | device_set_desc(dev, "XBox 360 Gamepad"); 131 | 132 | return (BUS_PROBE_DEFAULT); 133 | } 134 | 135 | static int 136 | xb360gp_attach(device_t dev) 137 | { 138 | struct hgame_softc *sc = device_get_softc(dev); 139 | int error; 140 | 141 | /* 142 | * Turn off the four LEDs on the gamepad which 143 | * are blinking by default: 144 | */ 145 | static const uint8_t reportbuf[3] = {1, 3, 0}; 146 | error = hid_set_report(dev, reportbuf, sizeof(reportbuf), 147 | HID_OUTPUT_REPORT, 0); 148 | if (error) 149 | device_printf(dev, "set output report failed, error=%d " 150 | "(ignored)\n", error); 151 | 152 | return (hidmap_attach(&sc->hm)); 153 | } 154 | 155 | static int 156 | xb360gp_detach(device_t dev) 157 | { 158 | struct hgame_softc *sc = device_get_softc(dev); 159 | 160 | return (hidmap_detach(&sc->hm)); 161 | } 162 | 163 | static devclass_t xb360gp_devclass; 164 | static device_method_t xb360gp_methods[] = { 165 | DEVMETHOD(device_identify, xb360gp_identify), 166 | DEVMETHOD(device_probe, xb360gp_probe), 167 | DEVMETHOD(device_attach, xb360gp_attach), 168 | DEVMETHOD(device_detach, xb360gp_detach), 169 | DEVMETHOD_END 170 | }; 171 | 172 | DEFINE_CLASS_0(xb360gp, xb360gp_driver, xb360gp_methods, 173 | sizeof(struct hgame_softc)); 174 | DRIVER_MODULE(xb360gp, hidbus, xb360gp_driver, xb360gp_devclass, NULL, 0); 175 | MODULE_DEPEND(xb360gp, hid, 1, 1, 1); 176 | MODULE_DEPEND(xb360gp, hidmap, 1, 1, 1); 177 | MODULE_DEPEND(xb360gp, hgame, 1, 1, 1); 178 | MODULE_DEPEND(xb360gp, evdev, 1, 1, 1); 179 | MODULE_VERSION(xb360gp, 1); 180 | USB_PNP_HOST_INFO(xb360gp_devs); 181 | --------------------------------------------------------------------------------