aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVladimir Kondratyev <wulf@FreeBSD.org>2020-10-14 22:02:15 +0000
committerVladimir Kondratyev <wulf@FreeBSD.org>2021-01-07 23:18:44 +0000
commitafd590d9e56686e179cbbf626ec567aeaaa49199 (patch)
tree3e15c389f9bdd4afef472dc2c685176a9a6767e8
parent3b2175fdb62c7f872e78ce732952f2ac2ff99c8d (diff)
downloadsrc-afd590d9e56686e179cbbf626ec567aeaaa49199.tar.gz
src-afd590d9e56686e179cbbf626ec567aeaaa49199.zip
hid: Import hidmap and bunch of drivers based on it
hidmap is a kernel module that maps HID input usages to evdev events. Following dependent drivers is included in the commit: hms - HID mouse driver. hcons - Consumer page AKA Multimedia keys driver. hsctrl - System Controls page (Power/Sleep keys) driver. ps4dshock - Sony DualShock 4 gamepad driver. Reviewed by: hselasky Differential revision: https://reviews.freebsd.org/D27993
-rw-r--r--share/man/man4/Makefile4
-rw-r--r--share/man/man4/hcons.498
-rw-r--r--share/man/man4/hms.4115
-rw-r--r--share/man/man4/hsctrl.498
-rw-r--r--share/man/man4/ps4dshock.4109
-rw-r--r--sys/conf/files5
-rw-r--r--sys/dev/hid/hcons.c295
-rw-r--r--sys/dev/hid/hidmap.c832
-rw-r--r--sys/dev/hid/hidmap.h262
-rw-r--r--sys/dev/hid/hms.c267
-rw-r--r--sys/dev/hid/hsctrl.c110
-rw-r--r--sys/dev/hid/ps4dshock.c1406
-rw-r--r--sys/modules/hid/Makefile7
-rw-r--r--sys/modules/hid/hcons/Makefile9
-rw-r--r--sys/modules/hid/hidmap/Makefile9
-rw-r--r--sys/modules/hid/hms/Makefile9
-rw-r--r--sys/modules/hid/hsctrl/Makefile9
-rw-r--r--sys/modules/hid/ps4dshock/Makefile9
18 files changed, 3652 insertions, 1 deletions
diff --git a/share/man/man4/Makefile b/share/man/man4/Makefile
index 02d1bca75a3f..60c1b5f19a9b 100644
--- a/share/man/man4/Makefile
+++ b/share/man/man4/Makefile
@@ -180,11 +180,13 @@ MAN= aac.4 \
gre.4 \
h_ertt.4 \
hconf.4 \
+ hcons.4 \
hidbus.4 \
hidquirk.4 \
hidraw.4 \
hifn.4 \
hkbd.4 \
+ hms.4 \
hmt.4 \
hpet.4 \
${_hpt27xx.4} \
@@ -192,6 +194,7 @@ MAN= aac.4 \
${_hptmv.4} \
${_hptnr.4} \
${_hptrr.4} \
+ hsctrl.4 \
${_hv_kvp.4} \
${_hv_netvsc.4} \
${_hv_storvsc.4} \
@@ -432,6 +435,7 @@ MAN= aac.4 \
ppi.4 \
procdesc.4 \
proto.4 \
+ ps4dshock.4 \
psm.4 \
pst.4 \
pt.4 \
diff --git a/share/man/man4/hcons.4 b/share/man/man4/hcons.4
new file mode 100644
index 000000000000..a8fe22983453
--- /dev/null
+++ b/share/man/man4/hcons.4
@@ -0,0 +1,98 @@
+.\" Copyright (c) 2020 Vladimir Kondratyev <wulf@FreeBSD.org>
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd September 14, 2020
+.Dt HCONS 4
+.Os
+.Sh NAME
+.Nm hcons
+.Nd HID consumer page controls driver
+.Sh SYNOPSIS
+To compile this driver into the kernel,
+place the following lines in your
+kernel configuration file:
+.Bd -ragged -offset indent
+.Cd "device hcons"
+.Cd "device hid"
+.Cd "device hidbus"
+.Cd "device hidmap"
+.Cd "device evdev"
+.Ed
+.Pp
+Alternatively, to load the driver as a
+module at boot time, place the following line in
+.Xr loader.conf 5 :
+.Bd -literal -offset indent
+hgame_load="YES"
+.Ed
+.Sh DESCRIPTION
+The
+.Nm
+driver provides support for HID consumer page controls most often used as
+"Multimedia keys" found on many keyboards.
+.Pp
+The
+.Pa /dev/input/event*
+device presents the consumer page controls as a
+.Ar evdev
+type device.
+.Sh SYSCTL VARIABLES
+The following variable is available as both
+.Xr sysctl 8
+variable and
+.Xr loader 8
+tunable:
+.Bl -tag -width indent
+.It Va dev.hcons.X.debug
+Debug output level, where 0 is debugging disabled and larger values increase
+debug message verbosity.
+Default is 0.
+.El
+.Pp
+It default value is set with
+.Xr loader 8
+tunable:
+.Bl -tag -width indent
+.It Va hw.hid.hcons.debug
+.El
+.Sh FILES
+.Bl -tag -width /dev/input/event* -compact
+.It Pa /dev/input/event*
+input event device node.
+.El
+.Sh SEE ALSO
+.Xr iichid 4 ,
+.Xr usbhid 4
+.Sh HISTORY
+The
+.Nm
+driver first appeared in
+.Fx 13.0.
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+driver was written by
+.An Vladimir Kondratyev Aq Mt wulf@FreeBSD.org .
diff --git a/share/man/man4/hms.4 b/share/man/man4/hms.4
new file mode 100644
index 000000000000..eb413e943976
--- /dev/null
+++ b/share/man/man4/hms.4
@@ -0,0 +1,115 @@
+.\" Copyright (c)
+.\" 1999 Nick Hibma <n_hibma@FreeBSD.org>. All rights reserved.
+.\" 2020 Vladimir Kondratyev <wulf@FreeBSD.org>.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd September 12, 2020
+.Dt HMS 4
+.Os
+.Sh NAME
+.Nm hms
+.Nd HID mouse driver
+.Sh SYNOPSIS
+To compile this driver into the kernel,
+place the following lines in your
+kernel configuration file:
+.Bd -ragged -offset indent
+.Cd "device hms"
+.Cd "device hidbus"
+.Cd "device hid"
+.Cd "device evdev"
+.Ed
+.Pp
+Alternatively, to load the driver as a
+module at boot time, place the following line in
+.Xr loader.conf 5 :
+.Bd -literal -offset indent
+hms_load="YES"
+.Ed
+.Sh DESCRIPTION
+The
+.Nm
+driver provides support for HID mice that attach to the HID transport
+backend.
+See
+.Xr iichid 4
+or
+.Xr usbhid 4 .
+Supported are
+mice with any number of buttons, mice with a wheel and absolute mice.
+.Pp
+The
+.Pa /dev/input/eventX
+device presents the mouse as a
+.Ar evdev
+type device.
+.Sh SYSCTL VARIABLES
+The following variable is available as both
+.Xr sysctl 8
+variable and
+.Xr loader 8
+tunable:
+.Bl -tag -width indent
+.It Va dev.hms.X.debug
+Debug output level, where 0 is debugging disabled and larger values increase
+debug message verbosity.
+Default is 0.
+.El
+.Pp
+It default value is derived from
+.Xr loader 8
+tunable:
+.Bl -tag -width indent
+.It Va hw.hid.hms.debug
+.El
+.Sh FILES
+.Bl -tag -width /dev/input/eventX -compact
+.It Pa /dev/input/eventX
+input event device node.
+.El
+.Sh SEE ALSO
+.Xr iichid 4 ,
+.Xr usbhid 4 ,
+.Xr xorg.conf 5 Pq Pa ports/x11/xorg
+.\.Xr moused 8
+.Sh BUGS
+.Nm
+cannot act like
+.Xr sysmouse 4
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+driver was written by
+.An Vladimir Kondratyev Aq Mt wulf@FreeBSD.org .
+.Pp
+This manual page was originally written by
+.An Nick Hibma Aq Mt n_hibma@FreeBSD.org
+for
+.Xr umt 4
+driver and was adopted for
+.Nm
+by
+.An Vladimir Kondratyev Aq Mt wulf@FreeBSD.org .
diff --git a/share/man/man4/hsctrl.4 b/share/man/man4/hsctrl.4
new file mode 100644
index 000000000000..ba7976c3c03d
--- /dev/null
+++ b/share/man/man4/hsctrl.4
@@ -0,0 +1,98 @@
+.\" Copyright (c) 2020 Vladimir Kondratyev <wulf@FreeBSD.org>
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd September 14, 2020
+.Dt HSCTRL 4
+.Os
+.Sh NAME
+.Nm hsctrl
+.Nd HID system controls driver
+.Sh SYNOPSIS
+To compile this driver into the kernel,
+place the following lines in your
+kernel configuration file:
+.Bd -ragged -offset indent
+.Cd "device hsctrl"
+.Cd "device hid"
+.Cd "device hidbus"
+.Cd "device hidmap"
+.Cd "device evdev"
+.Ed
+.Pp
+Alternatively, to load the driver as a
+module at boot time, place the following line in
+.Xr loader.conf 5 :
+.Bd -literal -offset indent
+hgame_load="YES"
+.Ed
+.Sh DESCRIPTION
+The
+.Nm
+driver provides support for HID system controls most often used as
+"Power off/Sleep keys" found on many keyboards.
+.Pp
+The
+.Pa /dev/input/event*
+device presents the consumer page controls as a
+.Ar evdev
+type device.
+.Sh SYSCTL VARIABLES
+The following variable is available as both
+.Xr sysctl 8
+variable and
+.Xr loader 8
+tunable:
+.Bl -tag -width indent
+.It Va dev.hsctrl.X.debug
+Debug output level, where 0 is debugging disabled and larger values increase
+debug message verbosity.
+Default is 0.
+.El
+.Pp
+It default value is set with
+.Xr loader 8
+tunable:
+.Bl -tag -width indent
+.It Va hw.hid.hsctrl.debug
+.El
+.Sh FILES
+.Bl -tag -width /dev/input/event* -compact
+.It Pa /dev/input/event*
+input event device node.
+.El
+.Sh SEE ALSO
+.Xr iichid 4 ,
+.Xr usbhid 4
+.Sh HISTORY
+The
+.Nm
+driver first appeared in
+.Fx 13.0.
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+driver was written by
+.An Vladimir Kondratyev Aq Mt wulf@FreeBSD.org .
diff --git a/share/man/man4/ps4dshock.4 b/share/man/man4/ps4dshock.4
new file mode 100644
index 000000000000..e2295533989f
--- /dev/null
+++ b/share/man/man4/ps4dshock.4
@@ -0,0 +1,109 @@
+.\" Copyright (c) 2020 Vladimir Kondratyev <wulf@FreeBSD.org>
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd September 19, 2020
+.Dt PS4DSHOCK 4
+.Os
+.Sh NAME
+.Nm ps4dshock
+.Nd Sony PlayStation 4 Dualshock 4 gamepad driver
+.Sh SYNOPSIS
+To compile this driver into the kernel,
+place the following lines in your
+kernel configuration file:
+.Bd -ragged -offset indent
+.Cd "device ps4dshock"
+.Cd "device hid"
+.Cd "device hidbus"
+.Cd "device hidmap"
+.Cd "device evdev"
+.Ed
+.Pp
+Alternatively, to load the driver as a
+module at boot time, place the following line in
+.Xr loader.conf 5 :
+.Bd -literal -offset indent
+ps4dshock_load="YES"
+.Ed
+.Sh DESCRIPTION
+The
+.Nm
+driver provides support for Sony PlayStation 4 Dualshock 4 gamepad driver.
+.Pp
+The
+.Pa /dev/input/event*
+device presents the game controller as a
+.Ar evdev
+type device.
+.Sh SYSCTL VARIABLES
+Next parameters are available as
+.Xr sysctl 8
+variables.
+Debug parameter is available as
+.Xr loader 8
+tunable as well.
+.Bl -tag -width indent
+.It Va dev.p4dshock.*.led_state
+LED state: 0 - off, 1 - on, 2 - blinking.
+.It Va dev.p4dshock.*.led_color_r
+LED color.
+Red component.
+.It Va dev.p4dshock.*.led_color_g
+LED color.
+Green component.
+.It Va dev.p4dshock.*.led_color_b
+LED color.
+Blue component.
+.It Va dev.p4dshock.*.led_delay_on
+LED blink.
+On delay, msecs.
+.It Va dev.p4dshock.*.led_delay_off
+LED blink.
+Off delay, msecs.
+.It Va hw.hid.ps4dshock.debug
+Debug output level, where 0 is debugging disabled and larger values increase
+debug message verbosity.
+Default is 0.
+.El
+.Sh FILES
+.Bl -tag -width /dev/input/event* -compact
+.It Pa /dev/input/event*
+input event device node.
+.El
+.Sh BUGS
+The
+.Nm
+does not support force-feedback events.
+.Sh HISTORY
+The
+.Nm
+driver first appeared in
+.Fx 13.0.
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+driver was written by
+.An Vladimir Kondratyev Aq Mt wulf@FreeBSD.org .
diff --git a/sys/conf/files b/sys/conf/files
index cea809deb039..31a06150a329 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -1816,13 +1816,18 @@ dev/gpio/gpiobus_if.m optional gpio
dev/gpio/gpiopps.c optional gpiopps fdt
dev/gpio/ofw_gpiobus.c optional fdt gpio
dev/hid/hconf.c optional hconf
+dev/hid/hcons.c optional hcons
dev/hid/hid.c optional hid
dev/hid/hid_if.m optional hid
dev/hid/hidbus.c optional hidbus
+dev/hid/hidmap.c optional hidmap
dev/hid/hidquirk.c optional hid
dev/hid/hidraw.c optional hidraw
dev/hid/hkbd.c optional hkbd
+dev/hid/hms.c optional hms
dev/hid/hmt.c optional hmt hconf
+dev/hid/hsctrl.c optional hsctrl
+dev/hid/ps4dshock.c optional ps4dshock
dev/hifn/hifn7751.c optional hifn
dev/hptiop/hptiop.c optional hptiop scbus
dev/hwpmc/hwpmc_logging.c optional hwpmc
diff --git a/sys/dev/hid/hcons.c b/sys/dev/hid/hcons.c
new file mode 100644
index 000000000000..559e84d468f3
--- /dev/null
+++ b/sys/dev/hid/hcons.c
@@ -0,0 +1,295 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2020 Vladimir Kondratyev <wulf@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Consumer Controls usage page driver
+ * https://www.usb.org/sites/default/files/documents/hut1_12v2.pdf
+ */
+
+#include <sys/param.h>
+#include <sys/bitstring.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/sysctl.h>
+
+#include <dev/evdev/input.h>
+#include <dev/evdev/evdev.h>
+
+#include <dev/hid/hid.h>
+#include <dev/hid/hidbus.h>
+#include <dev/hid/hidmap.h>
+
+static hidmap_cb_t hcons_rel_volume_cb;
+
+#define HCONS_MAP_KEY(usage, code) \
+ { HIDMAP_KEY(HUP_CONSUMER, usage, code) }
+#define HCONS_MAP_ABS(usage, code) \
+ { HIDMAP_ABS(HUP_CONSUMER, usage, code) }
+#define HCONS_MAP_REL(usage, code) \
+ { HIDMAP_REL(HUP_CONSUMER, usage, code) }
+#define HCONS_MAP_REL_CB(usage, callback) \
+ { HIDMAP_REL_CB(HUP_CONSUMER, usage, &callback) }
+
+static const struct hidmap_item hcons_map[] = {
+ HCONS_MAP_KEY(0x030, KEY_POWER),
+ HCONS_MAP_KEY(0x031, KEY_RESTART),
+ HCONS_MAP_KEY(0x032, KEY_SLEEP),
+ HCONS_MAP_KEY(0x034, KEY_SLEEP),
+ HCONS_MAP_KEY(0x035, KEY_KBDILLUMTOGGLE),
+ HCONS_MAP_KEY(0x036, BTN_MISC),
+ HCONS_MAP_KEY(0x040, KEY_MENU), /* Menu */
+ HCONS_MAP_KEY(0x041, KEY_SELECT), /* Menu Pick */
+ HCONS_MAP_KEY(0x042, KEY_UP), /* Menu Up */
+ HCONS_MAP_KEY(0x043, KEY_DOWN), /* Menu Down */
+ HCONS_MAP_KEY(0x044, KEY_LEFT), /* Menu Left */
+ HCONS_MAP_KEY(0x045, KEY_RIGHT), /* Menu Right */
+ HCONS_MAP_KEY(0x046, KEY_ESC), /* Menu Escape */
+ HCONS_MAP_KEY(0x047, KEY_KPPLUS), /* Menu Value Increase */
+ HCONS_MAP_KEY(0x048, KEY_KPMINUS), /* Menu Value Decrease */
+ HCONS_MAP_KEY(0x060, KEY_INFO), /* Data On Screen */
+ HCONS_MAP_KEY(0x061, KEY_SUBTITLE), /* Closed Caption */
+ HCONS_MAP_KEY(0x063, KEY_VCR), /* VCR/TV */
+ HCONS_MAP_KEY(0x065, KEY_CAMERA), /* Snapshot */
+ HCONS_MAP_KEY(0x069, KEY_RED),
+ HCONS_MAP_KEY(0x06a, KEY_GREEN),
+ HCONS_MAP_KEY(0x06b, KEY_BLUE),
+ HCONS_MAP_KEY(0x06c, KEY_YELLOW),
+ HCONS_MAP_KEY(0x06d, KEY_ASPECT_RATIO),
+ HCONS_MAP_KEY(0x06f, KEY_BRIGHTNESSUP),
+ HCONS_MAP_KEY(0x070, KEY_BRIGHTNESSDOWN),
+ HCONS_MAP_KEY(0x072, KEY_BRIGHTNESS_TOGGLE),
+ HCONS_MAP_KEY(0x073, KEY_BRIGHTNESS_MIN),
+ HCONS_MAP_KEY(0x074, KEY_BRIGHTNESS_MAX),
+ HCONS_MAP_KEY(0x075, KEY_BRIGHTNESS_AUTO),
+ HCONS_MAP_KEY(0x079, KEY_KBDILLUMUP),
+ HCONS_MAP_KEY(0x07a, KEY_KBDILLUMDOWN),
+ HCONS_MAP_KEY(0x07c, KEY_KBDILLUMTOGGLE),
+ HCONS_MAP_KEY(0x082, KEY_VIDEO_NEXT),
+ HCONS_MAP_KEY(0x083, KEY_LAST),
+ HCONS_MAP_KEY(0x084, KEY_ENTER),
+ HCONS_MAP_KEY(0x088, KEY_PC),
+ HCONS_MAP_KEY(0x089, KEY_TV),
+ HCONS_MAP_KEY(0x08a, KEY_WWW),
+ HCONS_MAP_KEY(0x08b, KEY_DVD),
+ HCONS_MAP_KEY(0x08c, KEY_PHONE),
+ HCONS_MAP_KEY(0x08d, KEY_PROGRAM),
+ HCONS_MAP_KEY(0x08e, KEY_VIDEOPHONE),
+ HCONS_MAP_KEY(0x08f, KEY_GAMES),
+ HCONS_MAP_KEY(0x090, KEY_MEMO),
+ HCONS_MAP_KEY(0x091, KEY_CD),
+ HCONS_MAP_KEY(0x092, KEY_VCR),
+ HCONS_MAP_KEY(0x093, KEY_TUNER),
+ HCONS_MAP_KEY(0x094, KEY_EXIT),
+ HCONS_MAP_KEY(0x095, KEY_HELP),
+ HCONS_MAP_KEY(0x096, KEY_TAPE),
+ HCONS_MAP_KEY(0x097, KEY_TV2),
+ HCONS_MAP_KEY(0x098, KEY_SAT),
+ HCONS_MAP_KEY(0x09a, KEY_PVR),
+ HCONS_MAP_KEY(0x09c, KEY_CHANNELUP),
+ HCONS_MAP_KEY(0x09d, KEY_CHANNELDOWN),
+ HCONS_MAP_KEY(0x0a0, KEY_VCR2),
+ HCONS_MAP_KEY(0x0b0, KEY_PLAY),
+ HCONS_MAP_KEY(0x0b1, KEY_PAUSE),
+ HCONS_MAP_KEY(0x0b2, KEY_RECORD),
+ HCONS_MAP_KEY(0x0b3, KEY_FASTFORWARD),
+ HCONS_MAP_KEY(0x0b4, KEY_REWIND),
+ HCONS_MAP_KEY(0x0b5, KEY_NEXTSONG),
+ HCONS_MAP_KEY(0x0b6, KEY_PREVIOUSSONG),
+ HCONS_MAP_KEY(0x0b7, KEY_STOPCD),
+ HCONS_MAP_KEY(0x0b8, KEY_EJECTCD),
+ HCONS_MAP_KEY(0x0bc, KEY_MEDIA_REPEAT),
+ HCONS_MAP_KEY(0x0b9, KEY_SHUFFLE),
+ HCONS_MAP_KEY(0x0bf, KEY_SLOW),
+ HCONS_MAP_KEY(0x0cd, KEY_PLAYPAUSE),
+ HCONS_MAP_KEY(0x0cf, KEY_VOICECOMMAND),
+ HCONS_MAP_ABS(0x0e0, ABS_VOLUME),
+ HCONS_MAP_REL_CB(0x0e0, hcons_rel_volume_cb),
+ HCONS_MAP_KEY(0x0e2, KEY_MUTE),
+ HCONS_MAP_KEY(0x0e5, KEY_BASSBOOST),
+ HCONS_MAP_KEY(0x0e9, KEY_VOLUMEUP),
+ HCONS_MAP_KEY(0x0ea, KEY_VOLUMEDOWN),
+ HCONS_MAP_KEY(0x0f5, KEY_SLOW),
+ HCONS_MAP_KEY(0x181, KEY_BUTTONCONFIG),
+ HCONS_MAP_KEY(0x182, KEY_BOOKMARKS),
+ HCONS_MAP_KEY(0x183, KEY_CONFIG),
+ HCONS_MAP_KEY(0x184, KEY_WORDPROCESSOR),
+ HCONS_MAP_KEY(0x185, KEY_EDITOR),
+ HCONS_MAP_KEY(0x186, KEY_SPREADSHEET),
+ HCONS_MAP_KEY(0x187, KEY_GRAPHICSEDITOR),
+ HCONS_MAP_KEY(0x188, KEY_PRESENTATION),
+ HCONS_MAP_KEY(0x189, KEY_DATABASE),
+ HCONS_MAP_KEY(0x18a, KEY_MAIL),
+ HCONS_MAP_KEY(0x18b, KEY_NEWS),
+ HCONS_MAP_KEY(0x18c, KEY_VOICEMAIL),
+ HCONS_MAP_KEY(0x18d, KEY_ADDRESSBOOK),
+ HCONS_MAP_KEY(0x18e, KEY_CALENDAR),
+ HCONS_MAP_KEY(0x18f, KEY_TASKMANAGER),
+ HCONS_MAP_KEY(0x190, KEY_JOURNAL),
+ HCONS_MAP_KEY(0x191, KEY_FINANCE),
+ HCONS_MAP_KEY(0x192, KEY_CALC),
+ HCONS_MAP_KEY(0x193, KEY_PLAYER),
+ HCONS_MAP_KEY(0x194, KEY_FILE),
+ HCONS_MAP_KEY(0x196, KEY_WWW),
+ HCONS_MAP_KEY(0x199, KEY_CHAT),
+ HCONS_MAP_KEY(0x19c, KEY_LOGOFF),
+ HCONS_MAP_KEY(0x19e, KEY_COFFEE),
+ HCONS_MAP_KEY(0x19f, KEY_CONTROLPANEL),
+ HCONS_MAP_KEY(0x1a2, KEY_APPSELECT),
+ HCONS_MAP_KEY(0x1a3, KEY_NEXT),
+ HCONS_MAP_KEY(0x1a4, KEY_PREVIOUS),
+ HCONS_MAP_KEY(0x1a6, KEY_HELP),
+ HCONS_MAP_KEY(0x1a7, KEY_DOCUMENTS),
+ HCONS_MAP_KEY(0x1ab, KEY_SPELLCHECK),
+ HCONS_MAP_KEY(0x1ae, KEY_KEYBOARD),
+ HCONS_MAP_KEY(0x1b1, KEY_SCREENSAVER),
+ HCONS_MAP_KEY(0x1b4, KEY_FILE),
+ HCONS_MAP_KEY(0x1b6, KEY_IMAGES),
+ HCONS_MAP_KEY(0x1b7, KEY_AUDIO),
+ HCONS_MAP_KEY(0x1b8, KEY_VIDEO),
+ HCONS_MAP_KEY(0x1bc, KEY_MESSENGER),
+ HCONS_MAP_KEY(0x1bd, KEY_INFO),
+ HCONS_MAP_KEY(0x1cb, KEY_ASSISTANT),
+ HCONS_MAP_KEY(0x201, KEY_NEW),
+ HCONS_MAP_KEY(0x202, KEY_OPEN),
+ HCONS_MAP_KEY(0x203, KEY_CLOSE),
+ HCONS_MAP_KEY(0x204, KEY_EXIT),
+ HCONS_MAP_KEY(0x207, KEY_SAVE),
+ HCONS_MAP_KEY(0x208, KEY_PRINT),
+ HCONS_MAP_KEY(0x209, KEY_PROPS),
+ HCONS_MAP_KEY(0x21a, KEY_UNDO),
+ HCONS_MAP_KEY(0x21b, KEY_COPY),
+ HCONS_MAP_KEY(0x21c, KEY_CUT),
+ HCONS_MAP_KEY(0x21d, KEY_PASTE),
+ HCONS_MAP_KEY(0x21f, KEY_FIND),
+ HCONS_MAP_KEY(0x221, KEY_SEARCH),
+ HCONS_MAP_KEY(0x222, KEY_GOTO),
+ HCONS_MAP_KEY(0x223, KEY_HOMEPAGE),
+ HCONS_MAP_KEY(0x224, KEY_BACK),
+ HCONS_MAP_KEY(0x225, KEY_FORWARD),
+ HCONS_MAP_KEY(0x226, KEY_STOP),
+ HCONS_MAP_KEY(0x227, KEY_REFRESH),
+ HCONS_MAP_KEY(0x22a, KEY_BOOKMARKS),
+ HCONS_MAP_KEY(0x22d, KEY_ZOOMIN),
+ HCONS_MAP_KEY(0x22e, KEY_ZOOMOUT),
+ HCONS_MAP_KEY(0x22f, KEY_ZOOMRESET),
+ HCONS_MAP_KEY(0x232, KEY_FULL_SCREEN),
+ HCONS_MAP_KEY(0x233, KEY_SCROLLUP),
+ HCONS_MAP_KEY(0x234, KEY_SCROLLDOWN),
+ HCONS_MAP_REL(0x238, REL_HWHEEL), /* AC Pan */
+ HCONS_MAP_KEY(0x23d, KEY_EDIT),
+ HCONS_MAP_KEY(0x25f, KEY_CANCEL),
+ HCONS_MAP_KEY(0x269, KEY_INSERT),
+ HCONS_MAP_KEY(0x26a, KEY_DELETE),
+ HCONS_MAP_KEY(0x279, KEY_REDO),
+ HCONS_MAP_KEY(0x289, KEY_REPLY),
+ HCONS_MAP_KEY(0x28b, KEY_FORWARDMAIL),
+ HCONS_MAP_KEY(0x28c, KEY_SEND),
+ HCONS_MAP_KEY(0x29d, KEY_KBD_LAYOUT_NEXT),
+ HCONS_MAP_KEY(0x2c7, KEY_KBDINPUTASSIST_PREV),
+ HCONS_MAP_KEY(0x2c8, KEY_KBDINPUTASSIST_NEXT),
+ HCONS_MAP_KEY(0x2c9, KEY_KBDINPUTASSIST_PREVGROUP),
+ HCONS_MAP_KEY(0x2ca, KEY_KBDINPUTASSIST_NEXTGROUP),
+ HCONS_MAP_KEY(0x2cb, KEY_KBDINPUTASSIST_ACCEPT),
+ HCONS_MAP_KEY(0x2cc, KEY_KBDINPUTASSIST_CANCEL),
+ HCONS_MAP_KEY(0x29f, KEY_SCALE),
+};
+
+static const struct hid_device_id hcons_devs[] = {
+ { HID_TLC(HUP_CONSUMER, HUC_CONTROL) },
+};
+
+/*
+ * Emulate relative Consumer volume usage with pressing
+ * VOLUMEUP and VOLUMEDOWN keys appropriate number of times
+ */
+static int
+hcons_rel_volume_cb(HIDMAP_CB_ARGS)
+{
+ struct evdev_dev *evdev = HIDMAP_CB_GET_EVDEV();
+ int32_t code;
+ int nrepeats;
+
+ switch (HIDMAP_CB_GET_STATE()) {
+ case HIDMAP_CB_IS_ATTACHING:
+ evdev_support_event(evdev, EV_KEY);
+ evdev_support_key(evdev, KEY_VOLUMEUP);
+ evdev_support_key(evdev, KEY_VOLUMEDOWN);
+ break;
+ case HIDMAP_CB_IS_RUNNING:
+ /* Nothing to report. */
+ if (ctx.data == 0)
+ return (ENOMSG);
+ code = ctx.data > 0 ? KEY_VOLUMEUP : KEY_VOLUMEDOWN;
+ for (nrepeats = abs(ctx.data); nrepeats > 0; nrepeats--) {
+ evdev_push_key(evdev, code, 1);
+ evdev_push_key(evdev, code, 0);
+ }
+ }
+
+ return (0);
+}
+
+static int
+hcons_probe(device_t dev)
+{
+ return (HIDMAP_PROBE(device_get_softc(dev), dev,
+ hcons_devs, hcons_map, "Consumer Control"));
+}
+
+static int
+hcons_attach(device_t dev)
+{
+ return (hidmap_attach(device_get_softc(dev)));
+}
+
+static int
+hcons_detach(device_t dev)
+{
+ return (hidmap_detach(device_get_softc(dev)));
+}
+
+static devclass_t hcons_devclass;
+static device_method_t hcons_methods[] = {
+ DEVMETHOD(device_probe, hcons_probe),
+ DEVMETHOD(device_attach, hcons_attach),
+ DEVMETHOD(device_detach, hcons_detach),
+
+ DEVMETHOD_END
+};
+
+DEFINE_CLASS_0(hcons, hcons_driver, hcons_methods, sizeof(struct hidmap));
+DRIVER_MODULE(hcons, hidbus, hcons_driver, hcons_devclass, NULL, 0);
+MODULE_DEPEND(hcons, hid, 1, 1, 1);
+MODULE_DEPEND(hcons, hidbus, 1, 1, 1);
+MODULE_DEPEND(hcons, hidmap, 1, 1, 1);
+MODULE_DEPEND(hcons, evdev, 1, 1, 1);
+MODULE_VERSION(hcons, 1);
+HID_PNP_INFO(hcons_devs);
diff --git a/sys/dev/hid/hidmap.c b/sys/dev/hid/hidmap.c
new file mode 100644
index 000000000000..163d63c20232
--- /dev/null
+++ b/sys/dev/hid/hidmap.c
@@ -0,0 +1,832 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2020 Vladimir Kondratyev <wulf@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Abstract 1 to 1 HID input usage to evdev event mapper driver.
+ */
+
+#include "opt_hid.h"
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/sysctl.h>
+#include <sys/systm.h>
+
+#include <dev/evdev/input.h>
+#include <dev/evdev/evdev.h>
+
+#include <dev/hid/hid.h>
+#include <dev/hid/hidbus.h>
+#include <dev/hid/hidmap.h>
+
+#ifdef HID_DEBUG
+#define DPRINTFN(hm, n, fmt, ...) do { \
+ if ((hm)->debug_var != NULL && *(hm)->debug_var >= (n)) { \
+ device_printf((hm)->dev, "%s: " fmt, \
+ __FUNCTION__ ,##__VA_ARGS__); \
+ } \
+} while (0)
+#define DPRINTF(hm, ...) DPRINTFN(hm, 1, __VA_ARGS__)
+#else
+#define DPRINTF(...) do { } while (0)
+#define DPRINTFN(...) do { } while (0)
+#endif
+
+static evdev_open_t hidmap_ev_open;
+static evdev_close_t hidmap_ev_close;
+
+#define HIDMAP_WANT_MERGE_KEYS(hm) ((hm)->key_rel != NULL)
+
+#define HIDMAP_FOREACH_ITEM(hm, mi, uoff) \
+ for (u_int _map = 0, _item = 0, _uoff_priv = -1; \
+ ((mi) = hidmap_get_next_map_item( \
+ (hm), &_map, &_item, &_uoff_priv, &(uoff))) != NULL;)
+
+static inline bool
+hidmap_get_next_map_index(const struct hidmap_item *map, int nmap_items,
+ uint32_t *index, uint16_t *usage_offset)
+{
+
+ ++*usage_offset;
+ if ((*index != 0 || *usage_offset != 0) &&
+ *usage_offset >= map[*index].nusages) {
+ ++*index;
+ *usage_offset = 0;
+ }
+
+ return (*index < nmap_items);
+}
+
+static inline const struct hidmap_item *
+hidmap_get_next_map_item(struct hidmap *hm, u_int *map, u_int *item,
+ u_int *uoff_priv, uint16_t *uoff)
+{
+
+ *uoff = *uoff_priv;
+ while (!hidmap_get_next_map_index(
+ hm->map[*map], hm->nmap_items[*map], item, uoff)) {
+ ++*map;
+ *item = 0;
+ *uoff = -1;
+ if (*map >= hm->nmaps)
+ return (NULL);
+ }
+ *uoff_priv = *uoff;
+
+ return (hm->map[*map] + *item);
+}
+
+void
+_hidmap_set_debug_var(struct hidmap *hm, int *debug_var)
+{
+#ifdef HID_DEBUG
+ hm->debug_var = debug_var;
+#endif
+}
+
+static int
+hidmap_ev_close(struct evdev_dev *evdev)
+{
+ return (hidbus_intr_stop(evdev_get_softc(evdev)));
+}
+
+static int
+hidmap_ev_open(struct evdev_dev *evdev)
+{
+ return (hidbus_intr_start(evdev_get_softc(evdev)));
+}
+
+void
+hidmap_support_key(struct hidmap *hm, uint16_t key)
+{
+ if (hm->key_press == NULL) {
+ hm->key_press = malloc(howmany(KEY_CNT, 8), M_DEVBUF,
+ M_ZERO | M_WAITOK);
+ evdev_support_event(hm->evdev, EV_KEY);
+ hm->key_min = key;
+ hm->key_max = key;
+ }
+ hm->key_min = MIN(hm->key_min, key);
+ hm->key_max = MAX(hm->key_max, key);
+ if (isset(hm->key_press, key)) {
+ if (hm->key_rel == NULL)
+ hm->key_rel = malloc(howmany(KEY_CNT, 8), M_DEVBUF,
+ M_ZERO | M_WAITOK);
+ } else {
+ setbit(hm->key_press, key);
+ evdev_support_key(hm->evdev, key);
+ }
+}
+
+void
+hidmap_push_key(struct hidmap *hm, uint16_t key, int32_t value)
+{
+ if (HIDMAP_WANT_MERGE_KEYS(hm))
+ setbit(value != 0 ? hm->key_press : hm->key_rel, key);
+ else
+ evdev_push_key(hm->evdev, key, value);
+}
+
+static void
+hidmap_sync_keys(struct hidmap *hm)
+{
+ int i, j;
+ bool press, rel;
+
+ for (j = hm->key_min / 8; j <= hm->key_max / 8; j++) {
+ if (hm->key_press[j] != hm->key_rel[j]) {
+ for (i = j * 8; i < j * 8 + 8; i++) {
+ press = isset(hm->key_press, i);
+ rel = isset(hm->key_rel, i);
+ if (press != rel)
+ evdev_push_key(hm->evdev, i, press);
+ }
+ }
+ }
+ bzero(hm->key_press, howmany(KEY_CNT, 8));
+ bzero(hm->key_rel, howmany(KEY_CNT, 8));
+}
+
+void
+hidmap_intr(void *context, void *buf, hid_size_t len)
+{
+ struct hidmap *hm = context;
+ struct hidmap_hid_item *hi;
+ const struct hidmap_item *mi;
+ int32_t usage;
+ int32_t data;
+ uint16_t key, uoff;
+ uint8_t id = 0;
+ bool found, do_sync = false;
+
+ DPRINTFN(hm, 6, "hm=%p len=%d\n", hm, len);
+ DPRINTFN(hm, 6, "data = %*D\n", len, buf, " ");
+
+ /* Strip leading "report ID" byte */
+ if (hm->hid_items[0].id) {
+ id = *(uint8_t *)buf;
+ len--;
+ buf = (uint8_t *)buf + 1;
+ }
+
+ hm->intr_buf = buf;
+ hm->intr_len = len;
+
+ for (hi = hm->hid_items; hi < hm->hid_items + hm->nhid_items; hi++) {
+ /* At first run callbacks that not tied to HID items */
+ if (hi->type == HIDMAP_TYPE_FINALCB) {
+ DPRINTFN(hm, 6, "type=%d item=%*D\n", hi->type,
+ (int)sizeof(hi->cb), &hi->cb, " ");
+ if (hi->cb(hm, hi, (union hidmap_cb_ctx){.rid = id})
+ == 0)
+ do_sync = true;
+ continue;
+ }
+
+ /* Ignore irrelevant reports */
+ if (id != hi->id)
+ continue;
+
+ /*
+ * 5.8. If Logical Minimum and Logical Maximum are both
+ * positive values then the contents of a field can be assumed
+ * to be an unsigned value. Otherwise, all integer values are
+ * signed values represented in 2’s complement format.
+ */
+ data = hi->lmin < 0 || hi->lmax < 0
+ ? hid_get_data(buf, len, &hi->loc)
+ : hid_get_udata(buf, len, &hi->loc);
+
+ DPRINTFN(hm, 6, "type=%d data=%d item=%*D\n", hi->type, data,
+ (int)sizeof(hi->cb), &hi->cb, " ");
+
+ if (hi->invert_value && hi->type < HIDMAP_TYPE_ARR_LIST)
+ data = hi->evtype == EV_REL
+ ? -data
+ : hi->lmin + hi->lmax - data;
+
+ switch (hi->type) {
+ case HIDMAP_TYPE_CALLBACK:
+ if (hi->cb(hm, hi, (union hidmap_cb_ctx){.data = data})
+ != 0)
+ continue;
+ break;
+
+ case HIDMAP_TYPE_VAR_NULLST:
+ /*
+ * 5.10. If the host or the device receives an
+ * out-of-range value then the current value for the
+ * respective control will not be modified.
+ */
+ if (data < hi->lmin || data > hi->lmax)
+ continue;
+ /* FALLTHROUGH */
+ case HIDMAP_TYPE_VARIABLE:
+ /*
+ * Ignore reports for absolute data if the data did not
+ * change and for relative data if data is 0.
+ * Evdev layer filters out them anyway.
+ */
+ if (data == (hi->evtype == EV_REL ? 0 : hi->last_val))
+ continue;
+ if (hi->evtype == EV_KEY)
+ hidmap_push_key(hm, hi->code, data);
+ else
+ evdev_push_event(hm->evdev, hi->evtype,
+ hi->code, data);
+ hi->last_val = data;
+ break;
+
+ case HIDMAP_TYPE_ARR_LIST:
+ key = KEY_RESERVED;
+ /*
+ * 6.2.2.5. An out-of range value in an array field
+ * is considered no controls asserted.
+ */
+ if (data < hi->lmin || data > hi->lmax)
+ goto report_key;
+ /*
+ * 6.2.2.5. Rather than returning a single bit for each
+ * button in the group, an array returns an index in
+ * each field that corresponds to the pressed button.
+ */
+ key = hi->codes[data - hi->lmin];
+ if (key == KEY_RESERVED)
+ DPRINTF(hm, "Can not map unknown HID "
+ "array index: %08x\n", data);
+ goto report_key;
+
+ case HIDMAP_TYPE_ARR_RANGE:
+ key = KEY_RESERVED;
+ /*
+ * 6.2.2.5. An out-of range value in an array field
+ * is considered no controls asserted.
+ */
+ if (data < hi->lmin || data > hi->lmax)
+ goto report_key;
+ /*
+ * When the input field is an array and the usage is
+ * specified with a range instead of an ID, we have to
+ * derive the actual usage by using the item value as
+ * an index in the usage range list.
+ */
+ usage = data - hi->lmin + hi->umin;
+ found = false;
+ HIDMAP_FOREACH_ITEM(hm, mi, uoff) {
+ if (usage == mi->usage + uoff &&
+ mi->type == EV_KEY && !mi->has_cb) {
+ key = mi->code;
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ DPRINTF(hm, "Can not map unknown HID "
+ "usage: %08x\n", usage);
+report_key:
+ if (key == HIDMAP_KEY_NULL || key == hi->last_key)
+ continue;
+ if (hi->last_key != KEY_RESERVED)
+ hidmap_push_key(hm, hi->last_key, 0);
+ if (key != KEY_RESERVED)
+ hidmap_push_key(hm, key, 1);
+ hi->last_key = key;
+ break;
+
+ default:
+ KASSERT(0, ("Unknown map type (%d)", hi->type));
+ }
+ do_sync = true;
+ }
+
+ if (do_sync) {
+ if (HIDMAP_WANT_MERGE_KEYS(hm))
+ hidmap_sync_keys(hm);
+ evdev_sync(hm->evdev);
+ }
+}
+
+static inline bool
+can_map_callback(struct hid_item *hi, const struct hidmap_item *mi,
+ uint16_t usage_offset)
+{
+
+ return (mi->has_cb && !mi->final_cb &&
+ hi->usage == mi->usage + usage_offset &&
+ (mi->relabs == HIDMAP_RELABS_ANY ||
+ !(hi->flags & HIO_RELATIVE) == !(mi->relabs == HIDMAP_RELATIVE)));
+}
+
+static inline bool
+can_map_variable(struct hid_item *hi, const struct hidmap_item *mi,
+ uint16_t usage_offset)
+{
+
+ return ((hi->flags & HIO_VARIABLE) != 0 && !mi->has_cb &&
+ hi->usage == mi->usage + usage_offset &&
+ (mi->relabs == HIDMAP_RELABS_ANY ||
+ !(hi->flags & HIO_RELATIVE) == !(mi->relabs == HIDMAP_RELATIVE)));
+}
+
+static inline bool
+can_map_arr_range(struct hid_item *hi, const struct hidmap_item *mi,
+ uint16_t usage_offset)
+{
+
+ return ((hi->flags & HIO_VARIABLE) == 0 && !mi->has_cb &&
+ hi->usage_minimum <= mi->usage + usage_offset &&
+ hi->usage_maximum >= mi->usage + usage_offset &&
+ mi->type == EV_KEY &&
+ (mi->code != KEY_RESERVED && mi->code != HIDMAP_KEY_NULL));
+}
+
+static inline bool
+can_map_arr_list(struct hid_item *hi, const struct hidmap_item *mi,
+ uint32_t usage, uint16_t usage_offset)
+{
+
+ return ((hi->flags & HIO_VARIABLE) == 0 && !mi->has_cb &&
+ usage == mi->usage + usage_offset &&
+ mi->type == EV_KEY &&
+ (mi->code != KEY_RESERVED && mi->code != HIDMAP_KEY_NULL));
+}
+
+static bool
+hidmap_probe_hid_item(struct hid_item *hi, const struct hidmap_item *map,
+ int nitems_map, hidmap_caps_t caps)
+{
+ u_int i, j;
+ uint16_t uoff;
+ bool found = false;
+
+#define HIDMAP_FOREACH_INDEX(map, nitems, idx, uoff) \
+ for ((idx) = 0, (uoff) = -1; \
+ hidmap_get_next_map_index((map), (nitems), &(idx), &(uoff));)
+
+ HIDMAP_FOREACH_INDEX(map, nitems_map, i, uoff) {
+ if (can_map_callback(hi, map + i, uoff)) {
+ if (map[i].cb(NULL, NULL,
+ (union hidmap_cb_ctx){.hi = hi}) != 0)
+ break;
+ setbit(caps, i);
+ return (true);
+ }
+ }
+
+ if (hi->flags & HIO_VARIABLE) {
+ HIDMAP_FOREACH_INDEX(map, nitems_map, i, uoff) {
+ if (can_map_variable(hi, map + i, uoff)) {
+ KASSERT(map[i].type == EV_KEY ||
+ map[i].type == EV_REL ||
+ map[i].type == EV_ABS ||
+ map[i].type == EV_SW,
+ ("Unsupported event type"));
+ setbit(caps, i);
+ return (true);
+ }
+ }
+ return (false);
+ }
+
+ if (hi->usage_minimum != 0 || hi->usage_maximum != 0) {
+ HIDMAP_FOREACH_INDEX(map, nitems_map, i, uoff) {
+ if (can_map_arr_range(hi, map + i, uoff)) {
+ setbit(caps, i);
+ found = true;
+ }
+ }
+ return (found);
+ }
+
+ for (j = 0; j < hi->nusages; j++) {
+ HIDMAP_FOREACH_INDEX(map, nitems_map, i, uoff) {
+ if (can_map_arr_list(hi, map+i, hi->usages[j], uoff)) {
+ setbit(caps, i);
+ found = true;
+ }
+ }
+ }
+
+ return (found);
+}
+
+static uint32_t
+hidmap_probe_hid_descr(void *d_ptr, hid_size_t d_len, uint8_t tlc_index,
+ const struct hidmap_item *map, int nitems_map, hidmap_caps_t caps)
+{
+ struct hid_data *hd;
+ struct hid_item hi;
+ uint32_t i, items = 0;
+ bool do_free = false;
+
+ if (caps == NULL) {
+ caps = malloc(HIDMAP_CAPS_SZ(nitems_map), M_DEVBUF, M_WAITOK);
+ do_free = true;
+ } else
+ bzero (caps, HIDMAP_CAPS_SZ(nitems_map));
+
+ /* Parse inputs */
+ hd = hid_start_parse(d_ptr, d_len, 1 << hid_input);
+ HIDBUS_FOREACH_ITEM(hd, &hi, tlc_index) {
+ if (hi.kind != hid_input)
+ continue;
+ if (hi.flags & HIO_CONST)
+ continue;
+ for (i = 0; i < hi.loc.count; i++, hi.loc.pos += hi.loc.size)
+ if (hidmap_probe_hid_item(&hi, map, nitems_map, caps))
+ items++;
+ }
+ hid_end_parse(hd);
+
+ /* Take finalizing callbacks in to account */
+ for (i = 0; i < nitems_map; i++) {
+ if (map[i].has_cb && map[i].final_cb &&
+ map[i].cb(NULL, NULL, (union hidmap_cb_ctx){}) == 0) {
+ setbit(caps, i);
+ items++;
+ }
+ }
+
+ /* Check that all mandatory usages are present in report descriptor */
+ if (items != 0) {
+ for (i = 0; i < nitems_map; i++) {
+ if (map[i].required && isclr(caps, i)) {
+ items = 0;
+ break;
+ }
+ }
+ }
+
+ if (do_free)
+ free(caps, M_DEVBUF);
+
+ return (items);
+}
+
+uint32_t
+hidmap_add_map(struct hidmap *hm, const struct hidmap_item *map,
+ int nitems_map, hidmap_caps_t caps)
+{
+ void *d_ptr;
+ uint32_t items;
+ int i, error;
+ hid_size_t d_len;
+ uint8_t tlc_index = hidbus_get_index(hm->dev);
+
+ /* Avoid double-adding of map in probe() handler */
+ for (i = 0; i < hm->nmaps; i++)
+ if (hm->map[i] == map)
+ return (0);
+
+ error = hid_get_report_descr(hm->dev, &d_ptr, &d_len);
+ if (error != 0) {
+ device_printf(hm->dev, "could not retrieve report descriptor "
+ "from device: %d\n", error);
+ return (error);
+ }
+
+ hm->cb_state = HIDMAP_CB_IS_PROBING;
+ items = hidmap_probe_hid_descr(d_ptr, d_len, tlc_index, map,
+ nitems_map, caps);
+ if (items == 0)
+ return (ENXIO);
+
+ KASSERT(hm->nmaps < HIDMAP_MAX_MAPS,
+ ("Not more than %d maps is supported", HIDMAP_MAX_MAPS));
+ hm->nhid_items += items;
+ hm->map[hm->nmaps] = map;
+ hm->nmap_items[hm->nmaps] = nitems_map;
+ hm->nmaps++;
+
+ return (0);
+}
+
+static bool
+hidmap_parse_hid_item(struct hidmap *hm, struct hid_item *hi,
+ struct hidmap_hid_item *item)
+{
+ const struct hidmap_item *mi;
+ struct hidmap_hid_item hi_temp;
+ uint32_t i;
+ uint16_t uoff;
+ bool found = false;
+
+ HIDMAP_FOREACH_ITEM(hm, mi, uoff) {
+ if (can_map_callback(hi, mi, uoff)) {
+ bzero(&hi_temp, sizeof(hi_temp));
+ hi_temp.cb = mi->cb;
+ hi_temp.type = HIDMAP_TYPE_CALLBACK;
+ /*
+ * Values returned by probe- and attach-stage
+ * callbacks MUST be identical.
+ */
+ if (mi->cb(hm, &hi_temp,
+ (union hidmap_cb_ctx){.hi = hi}) != 0)
+ break;
+ bcopy(&hi_temp, item, sizeof(hi_temp));
+ goto mapped;
+ }
+ }
+
+ if (hi->flags & HIO_VARIABLE) {
+ HIDMAP_FOREACH_ITEM(hm, mi, uoff) {
+ if (can_map_variable(hi, mi, uoff)) {
+ item->evtype = mi->type;
+ item->code = mi->code + uoff;
+ item->type = hi->flags & HIO_NULLSTATE
+ ? HIDMAP_TYPE_VAR_NULLST
+ : HIDMAP_TYPE_VARIABLE;
+ item->last_val = 0;
+ item->invert_value = mi->invert_value;
+ switch (mi->type) {
+ case EV_KEY:
+ hidmap_support_key(hm, item->code);
+ break;
+ case EV_REL:
+ evdev_support_event(hm->evdev, EV_REL);
+ evdev_support_rel(hm->evdev,
+ item->code);
+ break;
+ case EV_ABS:
+ evdev_support_event(hm->evdev, EV_ABS);
+ evdev_support_abs(hm->evdev,
+ item->code,
+ hi->logical_minimum,
+ hi->logical_maximum,
+ mi->fuzz,
+ mi->flat,
+ hid_item_resolution(hi));
+ break;
+ case EV_SW:
+ evdev_support_event(hm->evdev, EV_SW);
+ evdev_support_sw(hm->evdev,
+ item->code);
+ break;
+ default:
+ KASSERT(0, ("Unsupported event type"));
+ }
+ goto mapped;
+ }
+ }
+ return (false);
+ }
+
+ if (hi->usage_minimum != 0 || hi->usage_maximum != 0) {
+ HIDMAP_FOREACH_ITEM(hm, mi, uoff) {
+ if (can_map_arr_range(hi, mi, uoff)) {
+ hidmap_support_key(hm, mi->code + uoff);
+ found = true;
+ }
+ }
+ if (!found)
+ return (false);
+ item->umin = hi->usage_minimum;
+ item->type = HIDMAP_TYPE_ARR_RANGE;
+ item->last_key = KEY_RESERVED;
+ goto mapped;
+ }
+
+ for (i = 0; i < hi->nusages; i++) {
+ HIDMAP_FOREACH_ITEM(hm, mi, uoff) {
+ if (can_map_arr_list(hi, mi, hi->usages[i], uoff)) {
+ hidmap_support_key(hm, mi->code + uoff);
+ if (item->codes == NULL)
+ item->codes = malloc(
+ hi->nusages * sizeof(uint16_t),
+ M_DEVBUF, M_WAITOK | M_ZERO);
+ item->codes[i] = mi->code + uoff;
+ found = true;
+ break;
+ }
+ }
+ }
+ if (!found)
+ return (false);
+ item->type = HIDMAP_TYPE_ARR_LIST;
+ item->last_key = KEY_RESERVED;
+
+mapped:
+ item->id = hi->report_ID;
+ item->loc = hi->loc;
+ item->loc.count = 1;
+ item->lmin = hi->logical_minimum;
+ item->lmax = hi->logical_maximum;
+
+ DPRINTFN(hm, 6, "usage=%04x id=%d loc=%u/%u type=%d item=%*D\n",
+ hi->usage, hi->report_ID, hi->loc.pos, hi->loc.size, item->type,
+ (int)sizeof(item->cb), &item->cb, " ");
+
+ return (true);
+}
+
+static int
+hidmap_parse_hid_descr(struct hidmap *hm, uint8_t tlc_index)
+{
+ const struct hidmap_item *map;
+ struct hidmap_hid_item *item = hm->hid_items;
+ void *d_ptr;
+ struct hid_data *hd;
+ struct hid_item hi;
+ int i, error;
+ hid_size_t d_len;
+
+ error = hid_get_report_descr(hm->dev, &d_ptr, &d_len);
+ if (error != 0) {
+ DPRINTF(hm, "could not retrieve report descriptor from "
+ "device: %d\n", error);
+ return (error);
+ }
+
+ /* Parse inputs */
+ hd = hid_start_parse(d_ptr, d_len, 1 << hid_input);
+ HIDBUS_FOREACH_ITEM(hd, &hi, tlc_index) {
+ if (hi.kind != hid_input)
+ continue;
+ if (hi.flags & HIO_CONST)
+ continue;
+ for (i = 0; i < hi.loc.count; i++, hi.loc.pos += hi.loc.size)
+ if (hidmap_parse_hid_item(hm, &hi, item))
+ item++;
+ KASSERT(item <= hm->hid_items + hm->nhid_items,
+ ("Parsed HID item array overflow"));
+ }
+ hid_end_parse(hd);
+
+ /* Add finalizing callbacks to the end of list */
+ for (i = 0; i < hm->nmaps; i++) {
+ for (map = hm->map[i];
+ map < hm->map[i] + hm->nmap_items[i];
+ map++) {
+ if (map->has_cb && map->final_cb &&
+ map->cb(hm, item, (union hidmap_cb_ctx){}) == 0) {
+ item->cb = map->cb;
+ item->type = HIDMAP_TYPE_FINALCB;
+ item++;
+ }
+ }
+ }
+
+ /*
+ * Resulting number of parsed HID items can be less than expected as
+ * map items might be duplicated in different maps. Save real number.
+ */
+ if (hm->nhid_items != item - hm->hid_items)
+ DPRINTF(hm, "Parsed HID item number mismatch: expected=%u "
+ "result=%td\n", hm->nhid_items, item - hm->hid_items);
+ hm->nhid_items = item - hm->hid_items;
+
+ if (HIDMAP_WANT_MERGE_KEYS(hm))
+ bzero(hm->key_press, howmany(KEY_CNT, 8));
+
+ return (0);
+}
+
+int
+hidmap_probe(struct hidmap* hm, device_t dev,
+ const struct hid_device_id *id, int nitems_id,
+ const struct hidmap_item *map, int nitems_map,
+ const char *suffix, hidmap_caps_t caps)
+{
+ int error;
+
+ error = hidbus_lookup_driver_info(dev, id, nitems_id);
+ if (error != 0)
+ return (error);
+
+ hidmap_set_dev(hm, dev);
+
+ error = hidmap_add_map(hm, map, nitems_map, caps);
+ if (error != 0)
+ return (error);
+
+ hidbus_set_desc(dev, suffix);
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+int
+hidmap_attach(struct hidmap* hm)
+{
+ const struct hid_device_info *hw = hid_get_device_info(hm->dev);
+#ifdef HID_DEBUG
+ char tunable[40];
+#endif
+ int error;
+
+#ifdef HID_DEBUG
+ if (hm->debug_var == NULL) {
+ hm->debug_var = &hm->debug_level;
+ snprintf(tunable, sizeof(tunable), "hw.hid.%s.debug",
+ device_get_name(hm->dev));
+ TUNABLE_INT_FETCH(tunable, &hm->debug_level);
+ SYSCTL_ADD_INT(device_get_sysctl_ctx(hm->dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(hm->dev)),
+ OID_AUTO, "debug", CTLTYPE_INT | CTLFLAG_RWTUN,
+ &hm->debug_level, 0, "Verbosity level");
+ }
+#endif
+
+ DPRINTFN(hm, 11, "hm=%p\n", hm);
+
+ hm->cb_state = HIDMAP_CB_IS_ATTACHING;
+
+ hm->hid_items = malloc(hm->nhid_items * sizeof(struct hid_item),
+ M_DEVBUF, M_WAITOK | M_ZERO);
+
+ hidbus_set_intr(hm->dev, hidmap_intr, hm);
+ hm->evdev_methods = (struct evdev_methods) {
+ .ev_open = &hidmap_ev_open,
+ .ev_close = &hidmap_ev_close,
+ };
+
+ hm->evdev = evdev_alloc();
+ evdev_set_name(hm->evdev, device_get_desc(hm->dev));
+ evdev_set_phys(hm->evdev, device_get_nameunit(hm->dev));
+ evdev_set_id(hm->evdev, hw->idBus, hw->idVendor, hw->idProduct,
+ hw->idVersion);
+ evdev_set_serial(hm->evdev, hw->serial);
+ evdev_set_flag(hm->evdev, EVDEV_FLAG_EXT_EPOCH); /* hidbus child */
+ evdev_support_event(hm->evdev, EV_SYN);
+ error = hidmap_parse_hid_descr(hm, hidbus_get_index(hm->dev));
+ if (error) {
+ DPRINTF(hm, "error=%d\n", error);
+ hidmap_detach(hm);
+ return (ENXIO);
+ }
+
+ evdev_set_methods(hm->evdev, hm->dev, &hm->evdev_methods);
+ hm->cb_state = HIDMAP_CB_IS_RUNNING;
+
+ error = evdev_register(hm->evdev);
+ if (error) {
+ DPRINTF(hm, "error=%d\n", error);
+ hidmap_detach(hm);
+ return (ENXIO);
+ }
+
+ return (0);
+}
+
+int
+hidmap_detach(struct hidmap* hm)
+{
+ struct hidmap_hid_item *hi;
+
+ DPRINTFN(hm, 11, "\n");
+
+ hm->cb_state = HIDMAP_CB_IS_DETACHING;
+
+ evdev_free(hm->evdev);
+ if (hm->hid_items != NULL) {
+ for (hi = hm->hid_items;
+ hi < hm->hid_items + hm->nhid_items;
+ hi++)
+ if (hi->type == HIDMAP_TYPE_FINALCB ||
+ hi->type == HIDMAP_TYPE_CALLBACK)
+ hi->cb(hm, hi, (union hidmap_cb_ctx){});
+ else if (hi->type == HIDMAP_TYPE_ARR_LIST)
+ free(hi->codes, M_DEVBUF);
+ free(hm->hid_items, M_DEVBUF);
+ }
+
+ free(hm->key_press, M_DEVBUF);
+ free(hm->key_rel, M_DEVBUF);
+
+ return (0);
+}
+
+MODULE_DEPEND(hidmap, hid, 1, 1, 1);
+MODULE_DEPEND(hidmap, hidbus, 1, 1, 1);
+MODULE_DEPEND(hidmap, evdev, 1, 1, 1);
+MODULE_VERSION(hidmap, 1);
diff --git a/sys/dev/hid/hidmap.h b/sys/dev/hid/hidmap.h
new file mode 100644
index 000000000000..6ac23b3dc4ee
--- /dev/null
+++ b/sys/dev/hid/hidmap.h
@@ -0,0 +1,262 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2020 Vladimir Kondratyev <wulf@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _HIDMAP_H_
+#define _HIDMAP_H_
+
+#include <sys/param.h>
+
+#include <dev/hid/hid.h>
+
+#define HIDMAP_MAX_MAPS 4
+
+struct hid_device_id;
+struct hidmap_hid_item;
+struct hidmap_item;
+struct hidmap;
+
+enum hidmap_cb_state {
+ HIDMAP_CB_IS_PROBING,
+ HIDMAP_CB_IS_ATTACHING,
+ HIDMAP_CB_IS_RUNNING,
+ HIDMAP_CB_IS_DETACHING,
+};
+
+#define HIDMAP_KEY_NULL 0xFF /* Special event code to discard input */
+
+/* Third parameter of hidmap callback has different type depending on state */
+union hidmap_cb_ctx {
+ struct hid_item *hi; /* Probe- and attach-stage callbacks */
+ int32_t data; /* Run-stage callbacks */
+ uint8_t rid; /* Run-stage finalizing callbacks */
+};
+
+#define HIDMAP_CB_ARGS \
+ struct hidmap *hm, struct hidmap_hid_item *hi, union hidmap_cb_ctx ctx
+typedef int hidmap_cb_t(HIDMAP_CB_ARGS);
+
+/* These helpers can be used at any stage of any callbacks */
+#define HIDMAP_CB_GET_STATE(...) \
+ ((hm == NULL) ? HIDMAP_CB_IS_PROBING : hm->cb_state)
+#define HIDMAP_CB_GET_SOFTC(...) \
+ (hm == NULL ? NULL : device_get_softc(hm->dev))
+#define HIDMAP_CB_GET_EVDEV(...) \
+ (hm == NULL ? NULL : hm->evdev)
+#define HIDMAP_CB_UDATA (hi->udata)
+#define HIDMAP_CB_UDATA64 (hi->udata64)
+/* Special helpers for run-stage of finalizing callbacks */
+#define HIDMAP_CB_GET_RID(...) (ctx.rid)
+#define HIDMAP_CB_GET_DATA(loc) \
+ hid_get_data(hm->intr_buf, hm->intr_len, (loc))
+#define HIDMAP_CB_GET_UDATA(loc) \
+ hid_get_udata(hm->intr_buf, hm->intr_len, (loc))
+
+enum hidmap_relabs {
+ HIDMAP_RELABS_ANY = 0,
+ HIDMAP_RELATIVE,
+ HIDMAP_ABSOLUTE,
+};
+
+struct hidmap_item {
+ union {
+ struct {
+ uint16_t type; /* Evdev event type */
+ uint16_t code; /* Evdev event code */
+ uint16_t fuzz; /* Evdev event fuzz */
+ uint16_t flat; /* Evdev event flat */
+ };
+ hidmap_cb_t *cb; /* Reporting callback */
+ };
+ int32_t usage; /* HID usage (base) */
+ uint16_t nusages; /* number of usages */
+ bool required:1; /* Required by driver */
+ enum hidmap_relabs relabs:2;
+ bool has_cb:1;
+ bool final_cb:1;
+ bool invert_value:1;
+ u_int reserved:10;
+};
+
+#define HIDMAP_ANY(_page, _usage, _type, _code) \
+ .usage = HID_USAGE2((_page), (_usage)), \
+ .nusages = 1, \
+ .type = (_type), \
+ .code = (_code)
+#define HIDMAP_ANY_RANGE(_page, _usage_from, _usage_to, _type, _code) \
+ .usage = HID_USAGE2((_page), (_usage_from)), \
+ .nusages = (_usage_to) - (_usage_from) + 1, \
+ .type = (_type), \
+ .code = (_code)
+#define HIDMAP_ANY_CB(_page, _usage, _callback) \
+ .usage = HID_USAGE2((_page), (_usage)), \
+ .nusages = 1, \
+ .cb = (_callback), \
+ .has_cb = true
+#define HIDMAP_ANY_CB_RANGE(_page, _usage_from, _usage_to, _callback) \
+ .usage = HID_USAGE2((_page), (_usage_from)), \
+ .nusages = (_usage_to) - (_usage_from) + 1, \
+ .cb = (_callback), \
+ .has_cb = true
+#define HIDMAP_KEY(_page, _usage, _code) \
+ HIDMAP_ANY((_page), (_usage), EV_KEY, (_code)), \
+ .relabs = HIDMAP_RELABS_ANY
+#define HIDMAP_KEY_RANGE(_page, _ufrom, _uto, _code) \
+ HIDMAP_ANY_RANGE((_page), (_ufrom), (_uto), EV_KEY, (_code)), \
+ .relabs = HIDMAP_RELABS_ANY
+#define HIDMAP_REL(_page, _usage, _code) \
+ HIDMAP_ANY((_page), (_usage), EV_REL, (_code)), \
+ .relabs = HIDMAP_RELATIVE
+#define HIDMAP_ABS(_page, _usage, _code) \
+ HIDMAP_ANY((_page), (_usage), EV_ABS, (_code)), \
+ .relabs = HIDMAP_ABSOLUTE
+#define HIDMAP_SW(_page, _usage, _code) \
+ HIDMAP_ANY((_page), (_usage), EV_SW, (_code)), \
+ .relabs = HIDMAP_RELABS_ANY
+#define HIDMAP_REL_CB(_page, _usage, _callback) \
+ HIDMAP_ANY_CB((_page), (_usage), (_callback)), \
+ .relabs = HIDMAP_RELATIVE
+#define HIDMAP_ABS_CB(_page, _usage, _callback) \
+ HIDMAP_ANY_CB((_page), (_usage), (_callback)), \
+ .relabs = HIDMAP_ABSOLUTE
+/*
+ * Special callback function which is not tied to particular HID input usage
+ * but called at the end evdev properties setting or interrupt handler
+ * just before evdev_register() or evdev_sync() calls.
+ */
+#define HIDMAP_FINAL_CB(_callback) \
+ HIDMAP_ANY_CB(0, 0, (_callback)), .final_cb = true
+
+enum hidmap_type {
+ HIDMAP_TYPE_FINALCB = 0,/* No HID item associated. Runs unconditionally
+ * at the end of other items processing */
+ HIDMAP_TYPE_CALLBACK, /* HID item is reported with user callback */
+ HIDMAP_TYPE_VARIABLE, /* HID item is variable (single usage) */
+ HIDMAP_TYPE_VAR_NULLST, /* HID item is null state variable */
+ HIDMAP_TYPE_ARR_LIST, /* HID item is array with list of usages */
+ HIDMAP_TYPE_ARR_RANGE, /* Array with range (min;max) of usages */
+};
+
+struct hidmap_hid_item {
+ union {
+ hidmap_cb_t *cb; /* Callback */
+ struct { /* Variable */
+ uint16_t evtype; /* Evdev event type */
+ uint16_t code; /* Evdev event code */
+ };
+ uint16_t *codes; /* Array list map type */
+ int32_t umin; /* Array range map type */
+ };
+ union {
+ void *udata; /* Callback private context */
+ uint64_t udata64;
+ int32_t last_val; /* Last reported value (var) */
+ uint16_t last_key; /* Last reported key (array) */
+ };
+ struct hid_location loc; /* HID item location */
+ int32_t lmin; /* HID item logical minimum */
+ int32_t lmax; /* HID item logical maximum */
+ enum hidmap_type type:8;
+ uint8_t id; /* Report ID */
+ bool invert_value;
+};
+
+struct hidmap {
+ device_t dev;
+
+ struct evdev_dev *evdev;
+ struct evdev_methods evdev_methods;
+
+ /* Scatter-gather list of maps */
+ int nmaps;
+ uint32_t nmap_items[HIDMAP_MAX_MAPS];
+ const struct hidmap_item *map[HIDMAP_MAX_MAPS];
+
+ /* List of preparsed HID items */
+ uint32_t nhid_items;
+ struct hidmap_hid_item *hid_items;
+
+ /* Key event merging buffers */
+ uint8_t *key_press;
+ uint8_t *key_rel;
+ uint16_t key_min;
+ uint16_t key_max;
+
+ int *debug_var;
+ int debug_level;
+ enum hidmap_cb_state cb_state;
+ void * intr_buf;
+ hid_size_t intr_len;
+};
+
+typedef uint8_t * hidmap_caps_t;
+#define HIDMAP_CAPS_SZ(nitems) howmany((nitems), 8)
+#define HIDMAP_CAPS(name, map) uint8_t (name)[HIDMAP_CAPS_SZ(nitems(map))]
+static inline bool
+hidmap_test_cap(hidmap_caps_t caps, int cap)
+{
+ return (isset(caps, cap) != 0);
+}
+
+/*
+ * It is safe to call any of following procedures in device_probe context
+ * that makes possible to write probe-only drivers with attach/detach handlers
+ * inherited from hidmap. See hcons and hsctrl drivers for example.
+ */
+static inline void
+hidmap_set_dev(struct hidmap *hm, device_t dev)
+{
+ hm->dev = dev;
+}
+
+/* Hack to avoid #ifdef-ing of hidmap_set_debug_var in hidmap based drivers */
+#ifdef HID_DEBUG
+#define hidmap_set_debug_var(h, d) _hidmap_set_debug_var((h), (d))
+#else
+#define hidmap_set_debug_var(...)
+#endif
+void _hidmap_set_debug_var(struct hidmap *hm, int *debug_var);
+#define HIDMAP_ADD_MAP(hm, map, caps) \
+ hidmap_add_map((hm), (map), nitems(map), (caps))
+uint32_t hidmap_add_map(struct hidmap *hm, const struct hidmap_item *map,
+ int nitems_map, hidmap_caps_t caps);
+
+/* Versions of evdev_* functions capable to merge key events with same codes */
+void hidmap_support_key(struct hidmap *hm, uint16_t key);
+void hidmap_push_key(struct hidmap *hm, uint16_t key, int32_t value);
+
+void hidmap_intr(void *context, void *buf, hid_size_t len);
+#define HIDMAP_PROBE(hm, dev, id, map, suffix) \
+ hidmap_probe((hm), (dev), (id), nitems(id), (map), nitems(map), \
+ (suffix), NULL)
+int hidmap_probe(struct hidmap* hm, device_t dev,
+ const struct hid_device_id *id, int nitems_id,
+ const struct hidmap_item *map, int nitems_map,
+ const char *suffix, hidmap_caps_t caps);
+int hidmap_attach(struct hidmap *hm);
+int hidmap_detach(struct hidmap *hm);
+
+#endif /* _HIDMAP_H_ */
diff --git a/sys/dev/hid/hms.c b/sys/dev/hid/hms.c
new file mode 100644
index 000000000000..7f3455ff2725
--- /dev/null
+++ b/sys/dev/hid/hms.c
@@ -0,0 +1,267 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2020 Vladimir Kondratyev <wulf@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * HID spec: https://www.usb.org/sites/default/files/documents/hid1_11.pdf
+ */
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/sysctl.h>
+
+#include <dev/evdev/input.h>
+#include <dev/evdev/evdev.h>
+
+#include <dev/hid/hid.h>
+#include <dev/hid/hidbus.h>
+#include <dev/hid/hidmap.h>
+#include <dev/hid/hidquirk.h>
+#include <dev/hid/hidrdesc.h>
+
+static const uint8_t hms_boot_desc[] = { HID_MOUSE_BOOTPROTO_DESCR() };
+
+enum {
+ HMS_REL_X,
+ HMS_REL_Y,
+ HMS_REL_Z,
+ HMS_ABS_X,
+ HMS_ABS_Y,
+ HMS_ABS_Z,
+ HMS_HWHEEL,
+ HMS_BTN,
+ HMS_BTN_MS1,
+ HMS_BTN_MS2,
+ HMS_FINAL_CB,
+};
+
+static hidmap_cb_t hms_final_cb;
+
+#define HMS_MAP_BUT_RG(usage_from, usage_to, code) \
+ { HIDMAP_KEY_RANGE(HUP_BUTTON, usage_from, usage_to, code) }
+#define HMS_MAP_BUT_MS(usage, code) \
+ { HIDMAP_KEY(HUP_MICROSOFT, usage, code) }
+#define HMS_MAP_ABS(usage, code) \
+ { HIDMAP_ABS(HUP_GENERIC_DESKTOP, usage, code) }
+#define HMS_MAP_REL(usage, code) \
+ { HIDMAP_REL(HUP_GENERIC_DESKTOP, usage, code) }
+#define HMS_MAP_REL_REV(usage, code) \
+ { HIDMAP_REL(HUP_GENERIC_DESKTOP, usage, code), .invert_value = true }
+#define HMS_MAP_REL_CN(usage, code) \
+ { HIDMAP_REL(HUP_CONSUMER, usage, code) }
+#define HMS_FINAL_CB(cb) \
+ { HIDMAP_FINAL_CB(&cb) }
+
+static const struct hidmap_item hms_map[] = {
+ [HMS_REL_X] = HMS_MAP_REL(HUG_X, REL_X),
+ [HMS_REL_Y] = HMS_MAP_REL(HUG_Y, REL_Y),
+ [HMS_REL_Z] = HMS_MAP_REL(HUG_Z, REL_Z),
+ [HMS_ABS_X] = HMS_MAP_ABS(HUG_X, ABS_X),
+ [HMS_ABS_Y] = HMS_MAP_ABS(HUG_Y, ABS_Y),
+ [HMS_ABS_Z] = HMS_MAP_ABS(HUG_Z, ABS_Z),
+ [HMS_HWHEEL] = HMS_MAP_REL_CN(HUC_AC_PAN, REL_HWHEEL),
+ [HMS_BTN] = HMS_MAP_BUT_RG(1, 16, BTN_MOUSE),
+ [HMS_BTN_MS1] = HMS_MAP_BUT_MS(1, BTN_RIGHT),
+ [HMS_BTN_MS2] = HMS_MAP_BUT_MS(2, BTN_MIDDLE),
+ [HMS_FINAL_CB] = HMS_FINAL_CB(hms_final_cb),
+};
+
+static const struct hidmap_item hms_map_wheel[] = {
+ HMS_MAP_REL(HUG_WHEEL, REL_WHEEL),
+};
+static const struct hidmap_item hms_map_wheel_rev[] = {
+ HMS_MAP_REL_REV(HUG_WHEEL, REL_WHEEL),
+};
+
+/* A match on these entries will load hms */
+static const struct hid_device_id hms_devs[] = {
+ { HID_TLC(HUP_GENERIC_DESKTOP, HUG_MOUSE) },
+};
+
+struct hms_softc {
+ struct hidmap hm;
+ HIDMAP_CAPS(caps, hms_map);
+};
+
+static int
+hms_final_cb(HIDMAP_CB_ARGS)
+{
+ struct hms_softc *sc = HIDMAP_CB_GET_SOFTC();
+ struct evdev_dev *evdev = HIDMAP_CB_GET_EVDEV();
+
+ if (HIDMAP_CB_GET_STATE() == HIDMAP_CB_IS_ATTACHING) {
+ if (hidmap_test_cap(sc->caps, HMS_ABS_X) ||
+ hidmap_test_cap(sc->caps, HMS_ABS_Y))
+ evdev_support_prop(evdev, INPUT_PROP_DIRECT);
+ else
+ evdev_support_prop(evdev, INPUT_PROP_POINTER);
+ }
+
+ /* Do not execute callback at interrupt handler and detach */
+ return (ENOSYS);
+}
+
+static void
+hms_identify(driver_t *driver, device_t parent)
+{
+ const struct hid_device_info *hw = hid_get_device_info(parent);
+ void *d_ptr;
+ hid_size_t d_len;
+ int error;
+
+ /*
+ * If device claimed boot protocol support but do not have report
+ * descriptor, load one defined in "Appendix B.2" of HID1_11.pdf
+ */
+ error = hid_get_report_descr(parent, &d_ptr, &d_len);
+ if ((error != 0 && hid_test_quirk(hw, HQ_HAS_MS_BOOTPROTO)) ||
+ (error == 0 && hid_test_quirk(hw, HQ_MS_BOOTPROTO) &&
+ hid_is_mouse(d_ptr, d_len)))
+ (void)hid_set_report_descr(parent, hms_boot_desc,
+ sizeof(hms_boot_desc));
+}
+
+static int
+hms_probe(device_t dev)
+{
+ struct hms_softc *sc = device_get_softc(dev);
+ int error;
+
+ error = HIDBUS_LOOKUP_DRIVER_INFO(dev, hms_devs);
+ if (error != 0)
+ return (error);
+
+ hidmap_set_dev(&sc->hm, dev);
+
+ /* Check if report descriptor belongs to mouse */
+ error = HIDMAP_ADD_MAP(&sc->hm, hms_map, sc->caps);
+ if (error != 0)
+ return (error);
+
+ /* There should be at least one X or Y axis */
+ if (!hidmap_test_cap(sc->caps, HMS_REL_X) &&
+ !hidmap_test_cap(sc->caps, HMS_REL_X) &&
+ !hidmap_test_cap(sc->caps, HMS_ABS_X) &&
+ !hidmap_test_cap(sc->caps, HMS_ABS_Y))
+ return (ENXIO);
+
+ if (hidmap_test_cap(sc->caps, HMS_ABS_X) ||
+ hidmap_test_cap(sc->caps, HMS_ABS_Y))
+ hidbus_set_desc(dev, "Tablet");
+ else
+ hidbus_set_desc(dev, "Mouse");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+hms_attach(device_t dev)
+{
+ struct hms_softc *sc = device_get_softc(dev);
+ const struct hid_device_info *hw = hid_get_device_info(dev);
+ struct hidmap_hid_item *hi;
+ HIDMAP_CAPS(cap_wheel, hms_map_wheel);
+ void *d_ptr;
+ hid_size_t d_len;
+ bool set_report_proto;
+ int error, nbuttons = 0;
+
+ /*
+ * Set the report (non-boot) protocol if report descriptor has not been
+ * overloaded with boot protocol report descriptor.
+ *
+ * Mice without boot protocol support may choose not to implement
+ * Set_Protocol at all; Ignore any error.
+ */
+ error = hid_get_report_descr(dev, &d_ptr, &d_len);
+ set_report_proto = !(error == 0 && d_len == sizeof(hms_boot_desc) &&
+ memcmp(d_ptr, hms_boot_desc, sizeof(hms_boot_desc)) == 0);
+ (void)hid_set_protocol(dev, set_report_proto ? 1 : 0);
+
+ if (hid_test_quirk(hw, HQ_MS_REVZ))
+ HIDMAP_ADD_MAP(&sc->hm, hms_map_wheel_rev, cap_wheel);
+ else
+ HIDMAP_ADD_MAP(&sc->hm, hms_map_wheel, cap_wheel);
+
+ error = hidmap_attach(&sc->hm);
+ if (error)
+ return (error);
+
+ /* Count number of input usages of variable type mapped to buttons */
+ for (hi = sc->hm.hid_items;
+ hi < sc->hm.hid_items + sc->hm.nhid_items;
+ hi++)
+ if (hi->type == HIDMAP_TYPE_VARIABLE && hi->evtype == EV_KEY)
+ nbuttons++;
+
+ /* announce information about the mouse */
+ device_printf(dev, "%d buttons and [%s%s%s%s%s] coordinates ID=%u\n",
+ nbuttons,
+ (hidmap_test_cap(sc->caps, HMS_REL_X) ||
+ hidmap_test_cap(sc->caps, HMS_ABS_X)) ? "X" : "",
+ (hidmap_test_cap(sc->caps, HMS_REL_Y) ||
+ hidmap_test_cap(sc->caps, HMS_ABS_Y)) ? "Y" : "",
+ (hidmap_test_cap(sc->caps, HMS_REL_Z) ||
+ hidmap_test_cap(sc->caps, HMS_ABS_Z)) ? "Z" : "",
+ hidmap_test_cap(cap_wheel, 0) ? "W" : "",
+ hidmap_test_cap(sc->caps, HMS_HWHEEL) ? "H" : "",
+ sc->hm.hid_items[0].id);
+
+ return (0);
+}
+
+static int
+hms_detach(device_t dev)
+{
+ struct hms_softc *sc = device_get_softc(dev);
+
+ return (hidmap_detach(&sc->hm));
+}
+
+static devclass_t hms_devclass;
+static device_method_t hms_methods[] = {
+ DEVMETHOD(device_identify, hms_identify),
+ DEVMETHOD(device_probe, hms_probe),
+ DEVMETHOD(device_attach, hms_attach),
+ DEVMETHOD(device_detach, hms_detach),
+
+ DEVMETHOD_END
+};
+
+DEFINE_CLASS_0(hms, hms_driver, hms_methods, sizeof(struct hms_softc));
+DRIVER_MODULE(hms, hidbus, hms_driver, hms_devclass, NULL, 0);
+MODULE_DEPEND(hms, hid, 1, 1, 1);
+MODULE_DEPEND(hms, hidbus, 1, 1, 1);
+MODULE_DEPEND(hms, hidmap, 1, 1, 1);
+MODULE_DEPEND(hms, evdev, 1, 1, 1);
+MODULE_VERSION(hms, 1);
+HID_PNP_INFO(hms_devs);
diff --git a/sys/dev/hid/hsctrl.c b/sys/dev/hid/hsctrl.c
new file mode 100644
index 000000000000..2e0a06f547ac
--- /dev/null
+++ b/sys/dev/hid/hsctrl.c
@@ -0,0 +1,110 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2020 Vladimir Kondratyev <wulf@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * General Desktop/System Controls usage page driver
+ * https://www.usb.org/sites/default/files/documents/hut1_12v2.pdf
+ */
+
+#include <sys/param.h>
+#include <sys/bitstring.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/sysctl.h>
+
+#include <dev/evdev/input.h>
+#include <dev/evdev/evdev.h>
+
+#include <dev/hid/hid.h>
+#include <dev/hid/hidbus.h>
+#include <dev/hid/hidmap.h>
+
+#define HSCTRL_MAP(usage, code) \
+ { HIDMAP_KEY(HUP_GENERIC_DESKTOP, HUG_SYSTEM_##usage, code) }
+
+static const struct hidmap_item hsctrl_map[] = {
+ HSCTRL_MAP(POWER_DOWN, KEY_POWER),
+ HSCTRL_MAP(SLEEP, KEY_SLEEP),
+ HSCTRL_MAP(WAKEUP, KEY_WAKEUP),
+ HSCTRL_MAP(CONTEXT_MENU, KEY_CONTEXT_MENU),
+ HSCTRL_MAP(MAIN_MENU, KEY_MENU),
+ HSCTRL_MAP(APP_MENU, KEY_PROG1),
+ HSCTRL_MAP(MENU_HELP, KEY_HELP),
+ HSCTRL_MAP(MENU_EXIT, KEY_EXIT),
+ HSCTRL_MAP(MENU_SELECT, KEY_SELECT),
+ HSCTRL_MAP(MENU_RIGHT, KEY_RIGHT),
+ HSCTRL_MAP(MENU_LEFT, KEY_LEFT),
+ HSCTRL_MAP(MENU_UP, KEY_UP),
+ HSCTRL_MAP(MENU_DOWN, KEY_DOWN),
+ HSCTRL_MAP(POWER_UP, KEY_POWER2),
+ HSCTRL_MAP(RESTART, KEY_RESTART),
+};
+
+static const struct hid_device_id hsctrl_devs[] = {
+ { HID_TLC(HUP_GENERIC_DESKTOP, HUG_SYSTEM_CONTROL) },
+};
+
+static int
+hsctrl_probe(device_t dev)
+{
+ return (HIDMAP_PROBE(device_get_softc(dev), dev,
+ hsctrl_devs, hsctrl_map, "System Control"));
+}
+
+static int
+hsctrl_attach(device_t dev)
+{
+ return (hidmap_attach(device_get_softc(dev)));
+}
+
+static int
+hsctrl_detach(device_t dev)
+{
+ return (hidmap_detach(device_get_softc(dev)));
+}
+
+static devclass_t hsctrl_devclass;
+static device_method_t hsctrl_methods[] = {
+ DEVMETHOD(device_probe, hsctrl_probe),
+ DEVMETHOD(device_attach, hsctrl_attach),
+ DEVMETHOD(device_detach, hsctrl_detach),
+
+ DEVMETHOD_END
+};
+
+DEFINE_CLASS_0(hsctrl, hsctrl_driver, hsctrl_methods, sizeof(struct hidmap));
+DRIVER_MODULE(hsctrl, hidbus, hsctrl_driver, hsctrl_devclass, NULL, 0);
+MODULE_DEPEND(hsctrl, hid, 1, 1, 1);
+MODULE_DEPEND(hsctrl, hidbus, 1, 1, 1);
+MODULE_DEPEND(hsctrl, hidmap, 1, 1, 1);
+MODULE_DEPEND(hsctrl, evdev, 1, 1, 1);
+MODULE_VERSION(hsctrl, 1);
+HID_PNP_INFO(hsctrl_devs);
diff --git a/sys/dev/hid/ps4dshock.c b/sys/dev/hid/ps4dshock.c
new file mode 100644
index 000000000000..e0448947853e
--- /dev/null
+++ b/sys/dev/hid/ps4dshock.c
@@ -0,0 +1,1406 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2020 Vladimir Kondratyev <wulf@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Sony PS4 DualShock 4 driver
+ * https://eleccelerator.com/wiki/index.php?title=DualShock_4
+ * https://gist.github.com/johndrinkwater/7708901
+ * https://www.psdevwiki.com/ps4/DS4-USB
+ */
+
+#include "opt_hid.h"
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/sx.h>
+#include <sys/sysctl.h>
+
+#include <dev/evdev/input.h>
+#include <dev/evdev/evdev.h>
+
+#define HID_DEBUG_VAR ps4dshock_debug
+#include <dev/hid/hid.h>
+#include <dev/hid/hidbus.h>
+#include <dev/hid/hidquirk.h>
+#include <dev/hid/hidmap.h>
+#include "usbdevs.h"
+
+#ifdef HID_DEBUG
+static int ps4dshock_debug = 1;
+
+static SYSCTL_NODE(_hw_hid, OID_AUTO, ps4dshock, CTLFLAG_RW, 0,
+ "Sony PS4 DualShock Gamepad");
+SYSCTL_INT(_hw_hid_ps4dshock, OID_AUTO, debug, CTLFLAG_RWTUN,
+ &ps4dshock_debug, 0, "Debug level");
+#endif
+
+static const uint8_t ps4dshock_rdesc[] = {
+ 0x05, 0x01, /* Usage Page (Generic Desktop Ctrls) */
+ 0x09, 0x05, /* Usage (Game Pad) */
+ 0xA1, 0x01, /* Collection (Application) */
+ 0x85, 0x01, /* Report ID (1) */
+ 0x09, 0x30, /* Usage (X) */
+ 0x09, 0x31, /* Usage (Y) */
+ 0x09, 0x33, /* Usage (Rx) */
+ 0x09, 0x34, /* Usage (Ry) */
+ 0x15, 0x00, /* Logical Minimum (0) */
+ 0x26, 0xFF, 0x00, /* Logical Maximum (255) */
+ 0x75, 0x08, /* Report Size (8) */
+ 0x95, 0x04, /* Report Count (4) */
+ 0x81, 0x02, /* Input (Data,Var,Abs) */
+ 0x09, 0x39, /* Usage (Hat switch) */
+ 0x15, 0x00, /* Logical Minimum (0) */
+ 0x25, 0x07, /* Logical Maximum (7) */
+ 0x35, 0x00, /* Physical Minimum (0) */
+ 0x46, 0x3B, 0x01, /* Physical Maximum (315) */
+ 0x65, 0x14, /* Unit (System: English Rotation, Length: Centimeter) */
+ 0x75, 0x04, /* Report Size (4) */
+ 0x95, 0x01, /* Report Count (1) */
+ 0x81, 0x42, /* Input (Data,Var,Abs,Null State) */
+ 0x65, 0x00, /* Unit (None) */
+ 0x45, 0x00, /* Physical Maximum (0) */
+ 0x05, 0x09, /* Usage Page (Button) */
+ 0x19, 0x01, /* Usage Minimum (0x01) */
+ 0x29, 0x0E, /* Usage Maximum (0x0E) */
+ 0x15, 0x00, /* Logical Minimum (0) */
+ 0x25, 0x01, /* Logical Maximum (1) */
+ 0x75, 0x01, /* Report Size (1) */
+ 0x95, 0x0E, /* Report Count (14) */
+ 0x81, 0x02, /* Input (Data,Var,Abs) */
+ 0x06, 0x00, 0xFF, /* Usage Page (Vendor Defined 0xFF00) */
+ 0x09, 0x20, /* Usage (0x20) */
+ 0x75, 0x06, /* Report Size (6) */
+ 0x95, 0x01, /* Report Count (1) */
+ 0x15, 0x00, /* Logical Minimum (0) */
+ 0x25, 0x3F, /* Logical Maximum (63) */
+ 0x81, 0x02, /* Input (Data,Var,Abs) */
+ 0x05, 0x01, /* Usage Page (Generic Desktop Ctrls) */
+ 0x09, 0x32, /* Usage (Z) */
+ 0x09, 0x35, /* Usage (Rz) */
+ 0x15, 0x00, /* Logical Minimum (0) */
+ 0x26, 0xFF, 0x00, /* Logical Maximum (255) */
+ 0x75, 0x08, /* Report Size (8) */
+ 0x95, 0x02, /* Report Count (2) */
+ 0x81, 0x02, /* Input (Data,Var,Abs) */
+ 0xC0, /* End Collection */
+ 0x05, 0x01, /* Usage Page (Generic Desktop Ctrls) */
+ 0x09, 0x08, /* Usage (Multi-axis Controller) */
+ 0xA1, 0x01, /* Collection (Application) */
+ 0x06, 0x00, 0xFF, /* Usage Page (Vendor Defined 0xFF00) */
+ 0x09, 0x21, /* Usage (0x21) */
+ 0x27, 0xFF, 0xFF, 0x00, 0x00, /* Logical Maximum (65534) */
+ 0x75, 0x10, /* Report Size (16) */
+ 0x95, 0x01, /* Report Count (1) */
+ 0x81, 0x02, /* Input (Data,Var,Abs) */
+ 0x05, 0x06, /* Usage Page (Generic Dev Ctrls) */
+ 0x09, 0x20, /* Usage (Battery Strength) */
+ 0x26, 0xFF, 0x00, /* Logical Maximum (255) */
+ 0x75, 0x08, /* Report Size (8) */
+ 0x95, 0x01, /* Report Count (1) */
+ 0x81, 0x02, /* Input (Data,Var,Abs) */
+ 0x05, 0x01, /* Usage Page (Generic Desktop Ctrls) */
+ 0x19, 0x33, /* Usage Minimum (RX) */
+ 0x29, 0x35, /* Usage Maximum (RZ) */
+ 0x16, 0x00, 0x80, /* Logical Minimum (-32768) */
+ 0x26, 0xFF, 0x7F, /* Logical Maximum (32767) */
+ 0x75, 0x10, /* Report Size (16) */
+ 0x95, 0x03, /* Report Count (3) */
+ 0x81, 0x02, /* Input (Data,Var,Abs) */
+ 0x19, 0x30, /* Usage Minimum (X) */
+ 0x29, 0x32, /* Usage Maximum (Z) */
+ 0x16, 0x00, 0x80, /* Logical Minimum (-32768) */
+ 0x26, 0xFF, 0x7F, /* Logical Maximum (32767) */
+ 0x95, 0x03, /* Report Count (3) */
+ 0x81, 0x02, /* Input (Data,Var,Abs) */
+ 0x06, 0x00, 0xFF, /* Usage Page (Vendor Defined 0xFF00) */
+ 0x09, 0x21, /* Usage (0x21) */
+ 0x15, 0x00, /* Logical Minimum (0) */
+ 0x26, 0xFF, 0x00, /* Logical Maximum (255) */
+ 0x75, 0x08, /* Report Size (8) */
+ 0x95, 0x05, /* Report Count (5) */
+ 0x81, 0x03, /* Input (Const) */
+ 0xC0, /* End Collection */
+ 0x05, 0x0C, /* Usage Page (Consumer) */
+ 0x09, 0x05, /* Usage (Headphone) */
+ 0xA1, 0x01, /* Collection (Application) */
+ 0x75, 0x05, /* Report Size (5) */
+ 0x95, 0x01, /* Report Count (1) */
+ 0x81, 0x03, /* Input (Const) */
+ 0x06, 0x00, 0xFF, /* Usage Page (Vendor Defined 0xFF00) */
+ 0x09, 0x20, /* Usage (0x20) */
+ 0x09, 0x21, /* Usage (0x21) */
+ 0x15, 0x00, /* Logical Minimum (0) */
+ 0x25, 0x01, /* Logical Maximum (1) */
+ 0x75, 0x01, /* Report Size (1) */
+ 0x95, 0x02, /* Report Count (2) */
+ 0x81, 0x02, /* Input (Data,Var,Abs) */
+ 0x75, 0x01, /* Report Size (1) */
+ 0x95, 0x01, /* Report Count (1) */
+ 0x81, 0x03, /* Input (Const) */
+ 0x75, 0x08, /* Report Size (8) */
+ 0x95, 0x02, /* Report Count (2) */
+ 0x81, 0x03, /* Input (Const) */
+ 0xC0, /* End Collection */
+ 0x05, 0x0D, /* Usage Page (Digitizer) */
+ 0x09, 0x05, /* Usage (Touch Pad) */
+ 0xA1, 0x01, /* Collection (Application) */
+ 0x06, 0x00, 0xFF, /* Usage Page (Vendor Defined 0xFF00) */
+ 0x09, 0x21, /* Usage (0x21) */
+ 0x15, 0x00, /* Logical Minimum (0) */
+ 0x25, 0x03, /* Logical Maximum (3) */
+ 0x75, 0x04, /* Report Size (4) */
+ 0x95, 0x01, /* Report Count (1) */
+ 0x81, 0x02, /* Input (Data,Var,Abs) */
+ 0x75, 0x04, /* Report Size (4) */
+ 0x95, 0x01, /* Report Count (1) */
+ 0x81, 0x03, /* Input (Data,Var,Abs) */
+ 0x05, 0x0D, /* Usage Page (Digitizer) */
+ 0x09, 0x56, /* Usage (0x56) */
+ 0x55, 0x0C, /* Unit Exponent (-4) */
+ 0x66, 0x01, 0x10, /* Unit (System: SI Linear, Time: Seconds) */
+ 0x46, 0xCC, 0x06, /* Physical Maximum (1740) */
+ 0x26, 0xFF, 0x00, /* Logical Maximum (255) */
+ 0x75, 0x08, /* Report Size (8) */
+ 0x95, 0x01, /* Report Count (1) */
+ 0x81, 0x02, /* Input (Data,Var,Abs) */
+ 0x65, 0x00, /* Unit (None) */
+ 0x45, 0x00, /* Physical Maximum (0) */
+ 0x05, 0x0D, /* Usage Page (Digitizer) */
+ 0x09, 0x22, /* Usage (Finger) */
+ 0xA1, 0x02, /* Collection (Logical) */
+ 0x09, 0x51, /* Usage (0x51) */
+ 0x25, 0x7F, /* Logical Maximum (127) */
+ 0x75, 0x07, /* Report Size (7) */
+ 0x95, 0x01, /* Report Count (1) */
+ 0x81, 0x02, /* Input (Data,Var,Abs) */
+ 0x09, 0x42, /* Usage (Tip Switch) */
+ 0x25, 0x01, /* Logical Maximum (1) */
+ 0x75, 0x01, /* Report Size (1) */
+ 0x95, 0x01, /* Report Count (1) */
+ 0x81, 0x02, /* Input (Data,Var,Abs) */
+ 0x05, 0x01, /* Usage Page (Generic Desktop Ctrls) */
+ 0x09, 0x30, /* Usage (X) */
+ 0x55, 0x0E, /* Unit Exponent (-2) */
+ 0x65, 0x11, /* Unit (System: SI Linear, Length: Centimeter) */
+ 0x35, 0x00, /* Physical Minimum (0) */
+ 0x46, 0x80, 0x02, /* Physical Maximum (640) */
+ 0x26, 0x80, 0x07, /* Logical Maximum (1920) */
+ 0x75, 0x0C, /* Report Size (12) */
+ 0x81, 0x02, /* Input (Data,Var,Abs) */
+ 0x09, 0x31, /* Usage (Y) */
+ 0x46, 0xC0, 0x00, /* Physical Maximum (192) */
+ 0x26, 0xAE, 0x03, /* Logical Maximum (942) */
+ 0x81, 0x02, /* Input (Data,Var,Abs) */
+ 0x65, 0x00, /* Unit (None) */
+ 0x45, 0x00, /* Physical Maximum (0) */
+ 0xC0, /* End Collection */
+ 0x05, 0x0D, /* Usage Page (Digitizer) */
+ 0x09, 0x22, /* Usage (Finger) */
+ 0xA1, 0x02, /* Collection (Logical) */
+ 0x09, 0x51, /* Usage (0x51) */
+ 0x25, 0x7F, /* Logical Maximum (127) */
+ 0x75, 0x07, /* Report Size (7) */
+ 0x95, 0x01, /* Report Count (1) */
+ 0x81, 0x02, /* Input (Data,Var,Abs) */
+ 0x09, 0x42, /* Usage (Tip Switch) */
+ 0x25, 0x01, /* Logical Maximum (1) */
+ 0x75, 0x01, /* Report Size (1) */
+ 0x95, 0x01, /* Report Count (1) */
+ 0x81, 0x02, /* Input (Data,Var,Abs) */
+ 0x05, 0x01, /* Usage Page (Generic Desktop Ctrls) */
+ 0x09, 0x30, /* Usage (X) */
+ 0x55, 0x0E, /* Unit Exponent (-2) */
+ 0x65, 0x11, /* Unit (System: SI Linear, Length: Centimeter) */
+ 0x35, 0x00, /* Physical Minimum (0) */
+ 0x46, 0x80, 0x02, /* Physical Maximum (640) */
+ 0x26, 0x80, 0x07, /* Logical Maximum (1920) */
+ 0x75, 0x0C, /* Report Size (12) */
+ 0x81, 0x02, /* Input (Data,Var,Abs) */
+ 0x09, 0x31, /* Usage (Y) */
+ 0x46, 0xC0, 0x00, /* Physical Maximum (192) */
+ 0x26, 0xAE, 0x03, /* Logical Maximum (942) */
+ 0x81, 0x02, /* Input (Data,Var,Abs) */
+ 0x65, 0x00, /* Unit (None) */
+ 0x45, 0x00, /* Physical Maximum (0) */
+ 0xC0, /* End Collection */
+ 0x05, 0x0D, /* Usage Page (Digitizer) */
+ 0x09, 0x56, /* Usage (0x56) */
+ 0x55, 0x0C, /* Unit Exponent (-4) */
+ 0x66, 0x01, 0x10, /* Unit (System: SI Linear, Time: Seconds) */
+ 0x46, 0xCC, 0x06, /* Physical Maximum (1740) */
+ 0x26, 0xFF, 0x00, /* Logical Maximum (255) */
+ 0x75, 0x08, /* Report Size (8) */
+ 0x95, 0x01, /* Report Count (1) */
+ 0x81, 0x02, /* Input (Data,Var,Abs) */
+ 0x65, 0x00, /* Unit (None) */
+ 0x45, 0x00, /* Physical Maximum (0) */
+ 0x05, 0x0D, /* Usage Page (Digitizer) */
+ 0x09, 0x22, /* Usage (Finger) */
+ 0xA1, 0x02, /* Collection (Logical) */
+ 0x09, 0x51, /* Usage (0x51) */
+ 0x25, 0x7F, /* Logical Maximum (127) */
+ 0x75, 0x07, /* Report Size (7) */
+ 0x95, 0x01, /* Report Count (1) */
+ 0x81, 0x02, /* Input (Data,Var,Abs) */
+ 0x09, 0x42, /* Usage (Tip Switch) */
+ 0x25, 0x01, /* Logical Maximum (1) */
+ 0x75, 0x01, /* Report Size (1) */
+ 0x95, 0x01, /* Report Count (1) */
+ 0x81, 0x02, /* Input (Data,Var,Abs) */
+ 0x05, 0x01, /* Usage Page (Generic Desktop Ctrls) */
+ 0x09, 0x30, /* Usage (X) */
+ 0x55, 0x0E, /* Unit Exponent (-2) */
+ 0x65, 0x11, /* Unit (System: SI Linear, Length: Centimeter) */
+ 0x35, 0x00, /* Physical Minimum (0) */
+ 0x46, 0x80, 0x02, /* Physical Maximum (640) */
+ 0x26, 0x80, 0x07, /* Logical Maximum (1920) */
+ 0x75, 0x0C, /* Report Size (12) */
+ 0x81, 0x02, /* Input (Data,Var,Abs) */
+ 0x09, 0x31, /* Usage (Y) */
+ 0x46, 0xC0, 0x00, /* Physical Maximum (192) */
+ 0x26, 0xAE, 0x03, /* Logical Maximum (942) */
+ 0x81, 0x02, /* Input (Data,Var,Abs) */
+ 0x65, 0x00, /* Unit (None) */
+ 0x45, 0x00, /* Physical Maximum (0) */
+ 0xC0, /* End Collection */
+ 0x05, 0x0D, /* Usage Page (Digitizer) */
+ 0x09, 0x22, /* Usage (Finger) */
+ 0xA1, 0x02, /* Collection (Logical) */
+ 0x09, 0x51, /* Usage (0x51) */
+ 0x25, 0x7F, /* Logical Maximum (127) */
+ 0x75, 0x07, /* Report Size (7) */
+ 0x95, 0x01, /* Report Count (1) */
+ 0x81, 0x02, /* Input (Data,Var,Abs) */
+ 0x09, 0x42, /* Usage (Tip Switch) */
+ 0x25, 0x01, /* Logical Maximum (1) */
+ 0x75, 0x01, /* Report Size (1) */
+ 0x95, 0x01, /* Report Count (1) */
+ 0x81, 0x02, /* Input (Data,Var,Abs) */
+ 0x05, 0x01, /* Usage Page (Generic Desktop Ctrls) */
+ 0x09, 0x30, /* Usage (X) */
+ 0x55, 0x0E, /* Unit Exponent (-2) */
+ 0x65, 0x11, /* Unit (System: SI Linear, Length: Centimeter) */
+ 0x35, 0x00, /* Physical Minimum (0) */
+ 0x46, 0x80, 0x02, /* Physical Maximum (640) */
+ 0x26, 0x80, 0x07, /* Logical Maximum (1920) */
+ 0x75, 0x0C, /* Report Size (12) */
+ 0x81, 0x02, /* Input (Data,Var,Abs) */
+ 0x09, 0x31, /* Usage (Y) */
+ 0x46, 0xC0, 0x00, /* Physical Maximum (192) */
+ 0x26, 0xAE, 0x03, /* Logical Maximum (942) */
+ 0x81, 0x02, /* Input (Data,Var,Abs) */
+ 0x65, 0x00, /* Unit (None) */
+ 0x45, 0x00, /* Physical Maximum (0) */
+ 0xC0, /* End Collection */
+ 0x05, 0x0D, /* Usage Page (Digitizer) */
+ 0x09, 0x56, /* Usage (0x56) */
+ 0x55, 0x0C, /* Unit Exponent (-4) */
+ 0x66, 0x01, 0x10, /* Unit (System: SI Linear, Time: Seconds) */
+ 0x46, 0xCC, 0x06, /* Physical Maximum (1740) */
+ 0x26, 0xFF, 0x00, /* Logical Maximum (255) */
+ 0x75, 0x08, /* Report Size (8) */
+ 0x95, 0x01, /* Report Count (1) */
+ 0x81, 0x02, /* Input (Data,Var,Abs) */
+ 0x65, 0x00, /* Unit (None) */
+ 0x45, 0x00, /* Physical Maximum (0) */
+ 0x05, 0x0D, /* Usage Page (Digitizer) */
+ 0x09, 0x22, /* Usage (Finger) */
+ 0xA1, 0x02, /* Collection (Logical) */
+ 0x09, 0x51, /* Usage (0x51) */
+ 0x25, 0x7F, /* Logical Maximum (127) */
+ 0x75, 0x07, /* Report Size (7) */
+ 0x95, 0x01, /* Report Count (1) */
+ 0x81, 0x02, /* Input (Data,Var,Abs) */
+ 0x09, 0x42, /* Usage (Tip Switch) */
+ 0x25, 0x01, /* Logical Maximum (1) */
+ 0x75, 0x01, /* Report Size (1) */
+ 0x95, 0x01, /* Report Count (1) */
+ 0x81, 0x02, /* Input (Data,Var,Abs) */
+ 0x05, 0x01, /* Usage Page (Generic Desktop Ctrls) */
+ 0x09, 0x30, /* Usage (X) */
+ 0x55, 0x0E, /* Unit Exponent (-2) */
+ 0x65, 0x11, /* Unit (System: SI Linear, Length: Centimeter) */
+ 0x35, 0x00, /* Physical Minimum (0) */
+ 0x46, 0x80, 0x02, /* Physical Maximum (640) */
+ 0x26, 0x80, 0x07, /* Logical Maximum (1920) */
+ 0x75, 0x0C, /* Report Size (12) */
+ 0x81, 0x02, /* Input (Data,Var,Abs) */
+ 0x09, 0x31, /* Usage (Y) */
+ 0x46, 0xC0, 0x00, /* Physical Maximum (192) */
+ 0x26, 0xAE, 0x03, /* Logical Maximum (942) */
+ 0x81, 0x02, /* Input (Data,Var,Abs) */
+ 0x65, 0x00, /* Unit (None) */
+ 0x45, 0x00, /* Physical Maximum (0) */
+ 0xC0, /* End Collection */
+ 0x05, 0x0D, /* Usage Page (Digitizer) */
+ 0x09, 0x22, /* Usage (Finger) */
+ 0xA1, 0x02, /* Collection (Logical) */
+ 0x09, 0x51, /* Usage (0x51) */
+ 0x25, 0x7F, /* Logical Maximum (127) */
+ 0x75, 0x07, /* Report Size (7) */
+ 0x95, 0x01, /* Report Count (1) */
+ 0x81, 0x02, /* Input (Data,Var,Abs) */
+ 0x09, 0x42, /* Usage (Tip Switch) */
+ 0x25, 0x01, /* Logical Maximum (1) */
+ 0x75, 0x01, /* Report Size (1) */
+ 0x95, 0x01, /* Report Count (1) */
+ 0x81, 0x02, /* Input (Data,Var,Abs) */
+ 0x05, 0x01, /* Usage Page (Generic Desktop Ctrls) */
+ 0x09, 0x30, /* Usage (X) */
+ 0x55, 0x0E, /* Unit Exponent (-2) */
+ 0x65, 0x11, /* Unit (System: SI Linear, Length: Centimeter) */
+ 0x35, 0x00, /* Physical Minimum (0) */
+ 0x46, 0x80, 0x02, /* Physical Maximum (640) */
+ 0x26, 0x80, 0x07, /* Logical Maximum (1920) */
+ 0x75, 0x0C, /* Report Size (12) */
+ 0x81, 0x02, /* Input (Data,Var,Abs) */
+ 0x09, 0x31, /* Usage (Y) */
+ 0x46, 0xC0, 0x00, /* Physical Maximum (192) */
+ 0x26, 0xAE, 0x03, /* Logical Maximum (942) */
+ 0x81, 0x02, /* Input (Data,Var,Abs) */
+ 0x65, 0x00, /* Unit (None) */
+ 0x45, 0x00, /* Physical Maximum (0) */
+ 0xC0, /* End Collection */
+ 0x75, 0x08, /* Report Size (8) */
+ 0x95, 0x03, /* Report Count (3) */
+ 0x81, 0x03, /* Input (Const) */
+ /* Output and feature reports */
+ 0x85, 0x05, /* Report ID (5) */
+ 0x06, 0x00, 0xFF, /* Usage Page (Vendor Defined 0xFF00) */
+ 0x09, 0x22, /* Usage (0x22) */
+ 0x15, 0x00, /* Logical Minimum (0) */
+ 0x26, 0xFF, 0x00, /* Logical Maximum (255) */
+ 0x95, 0x1F, /* Report Count (31) */
+ 0x91, 0x02, /* Output (Data,Var,Abs) */
+ 0x85, 0x04, /* Report ID (4) */
+ 0x09, 0x23, /* Usage (0x23) */
+ 0x95, 0x24, /* Report Count (36) */
+ 0xB1, 0x02, /* Feature (Data,Var,Abs) */
+ 0x85, 0x02, /* Report ID (2) */
+ 0x09, 0x24, /* Usage (0x24) */
+ 0x95, 0x24, /* Report Count (36) */
+ 0xB1, 0x02, /* Feature (Data,Var,Abs) */
+ 0x85, 0x08, /* Report ID (8) */
+ 0x09, 0x25, /* Usage (0x25) */
+ 0x95, 0x03, /* Report Count (3) */
+ 0xB1, 0x02, /* Feature (Data,Var,Abs) */
+ 0x85, 0x10, /* Report ID (16) */
+ 0x09, 0x26, /* Usage (0x26) */
+ 0x95, 0x04, /* Report Count (4) */
+ 0xB1, 0x02, /* Feature (Data,Var,Abs) */
+ 0x85, 0x11, /* Report ID (17) */
+ 0x09, 0x27, /* Usage (0x27) */
+ 0x95, 0x02, /* Report Count (2) */
+ 0xB1, 0x02, /* Feature (Data,Var,Abs) */
+ 0x85, 0x12, /* Report ID (18) */
+ 0x06, 0x02, 0xFF, /* Usage Page (Vendor Defined 0xFF02) */
+ 0x09, 0x21, /* Usage (0x21) */
+ 0x95, 0x0F, /* Report Count (15) */
+ 0xB1, 0x02, /* Feature (Data,Var,Abs) */
+ 0x85, 0x13, /* Report ID (19) */
+ 0x09, 0x22, /* Usage (0x22) */
+ 0x95, 0x16, /* Report Count (22) */
+ 0xB1, 0x02, /* Feature (Data,Var,Abs) */
+ 0x85, 0x14, /* Report ID (20) */
+ 0x06, 0x05, 0xFF, /* Usage Page (Vendor Defined 0xFF05) */
+ 0x09, 0x20, /* Usage (0x20) */
+ 0x95, 0x10, /* Report Count (16) */
+ 0xB1, 0x02, /* Feature (Data,Var,Abs) */
+ 0x85, 0x15, /* Report ID (21) */
+ 0x09, 0x21, /* Usage (0x21) */
+ 0x95, 0x2C, /* Report Count (44) */
+ 0xB1, 0x02, /* Feature (Data,Var,Abs) */
+ 0x06, 0x80, 0xFF, /* Usage Page (Vendor Defined 0xFF80) */
+ 0x85, 0x80, /* Report ID (-128) */
+ 0x09, 0x20, /* Usage (0x20) */
+ 0x95, 0x06, /* Report Count (6) */
+ 0xB1, 0x02, /* Feature (Data,Var,Abs) */
+ 0x85, 0x81, /* Report ID (-127) */
+ 0x09, 0x21, /* Usage (0x21) */
+ 0x95, 0x06, /* Report Count (6) */
+ 0xB1, 0x02, /* Feature (Data,Var,Abs) */
+ 0x85, 0x82, /* Report ID (-126) */
+ 0x09, 0x22, /* Usage (0x22) */
+ 0x95, 0x05, /* Report Count (5) */
+ 0xB1, 0x02, /* Feature (Data,Var,Abs) */
+ 0x85, 0x83, /* Report ID (-125) */
+ 0x09, 0x23, /* Usage (0x23) */
+ 0x95, 0x01, /* Report Count (1) */
+ 0xB1, 0x02, /* Feature (Data,Var,Abs) */
+ 0x85, 0x84, /* Report ID (-124) */
+ 0x09, 0x24, /* Usage (0x24) */
+ 0x95, 0x04, /* Report Count (4) */
+ 0xB1, 0x02, /* Feature (Data,Var,Abs) */
+ 0x85, 0x85, /* Report ID (-123) */
+ 0x09, 0x25, /* Usage (0x25) */
+ 0x95, 0x06, /* Report Count (6) */
+ 0xB1, 0x02, /* Feature (Data,Var,Abs) */
+ 0x85, 0x86, /* Report ID (-122) */
+ 0x09, 0x26, /* Usage (0x26) */
+ 0x95, 0x06, /* Report Count (6) */
+ 0xB1, 0x02, /* Feature (Data,Var,Abs) */
+ 0x85, 0x87, /* Report ID (-121) */
+ 0x09, 0x27, /* Usage (0x27) */
+ 0x95, 0x23, /* Report Count (35) */
+ 0xB1, 0x02, /* Feature (Data,Var,Abs) */
+ 0x85, 0x88, /* Report ID (-120) */
+ 0x09, 0x28, /* Usage (0x28) */
+ 0x95, 0x22, /* Report Count (34) */
+ 0xB1, 0x02, /* Feature (Data,Var,Abs) */
+ 0x85, 0x89, /* Report ID (-119) */
+ 0x09, 0x29, /* Usage (0x29) */
+ 0x95, 0x02, /* Report Count (2) */
+ 0xB1, 0x02, /* Feature (Data,Var,Abs) */
+ 0x85, 0x90, /* Report ID (-112) */
+ 0x09, 0x30, /* Usage (0x30) */
+ 0x95, 0x05, /* Report Count (5) */
+ 0xB1, 0x02, /* Feature (Data,Var,Abs) */
+ 0x85, 0x91, /* Report ID (-111) */
+ 0x09, 0x31, /* Usage (0x31) */
+ 0x95, 0x03, /* Report Count (3) */
+ 0xB1, 0x02, /* Feature (Data,Var,Abs) */
+ 0x85, 0x92, /* Report ID (-110) */
+ 0x09, 0x32, /* Usage (0x32) */
+ 0x95, 0x03, /* Report Count (3) */
+ 0xB1, 0x02, /* Feature (Data,Var,Abs) */
+ 0x85, 0x93, /* Report ID (-109) */
+ 0x09, 0x33, /* Usage (0x33) */
+ 0x95, 0x0C, /* Report Count (12) */
+ 0xB1, 0x02, /* Feature (Data,Var,Abs) */
+ 0x85, 0xA0, /* Report ID (-96) */
+ 0x09, 0x40, /* Usage (0x40) */
+ 0x95, 0x06, /* Report Count (6) */
+ 0xB1, 0x02, /* Feature (Data,Var,Abs) */
+ 0x85, 0xA1, /* Report ID (-95) */
+ 0x09, 0x41, /* Usage (0x41) */
+ 0x95, 0x01, /* Report Count (1) */
+ 0xB1, 0x02, /* Feature (Data,Var,Abs) */
+ 0x85, 0xA2, /* Report ID (-94) */
+ 0x09, 0x42, /* Usage (0x42) */
+ 0x95, 0x01, /* Report Count (1) */
+ 0xB1, 0x02, /* Feature (Data,Var,Abs) */
+ 0x85, 0xA3, /* Report ID (-93) */
+ 0x09, 0x43, /* Usage (0x43) */
+ 0x95, 0x30, /* Report Count (48) */
+ 0xB1, 0x02, /* Feature (Data,Var,Abs) */
+ 0x85, 0xA4, /* Report ID (-92) */
+ 0x09, 0x44, /* Usage (0x44) */
+ 0x95, 0x0D, /* Report Count (13) */
+ 0xB1, 0x02, /* Feature (Data,Var,Abs) */
+ 0x85, 0xA5, /* Report ID (-91) */
+ 0x09, 0x45, /* Usage (0x45) */
+ 0x95, 0x15, /* Report Count (21) */
+ 0xB1, 0x02, /* Feature (Data,Var,Abs) */
+ 0x85, 0xA6, /* Report ID (-90) */
+ 0x09, 0x46, /* Usage (0x46) */
+ 0x95, 0x15, /* Report Count (21) */
+ 0xB1, 0x02, /* Feature (Data,Var,Abs) */
+ 0x85, 0xF0, /* Report ID (-16) */
+ 0x09, 0x47, /* Usage (0x47) */
+ 0x95, 0x3F, /* Report Count (63) */
+ 0xB1, 0x02, /* Feature (Data,Var,Abs) */
+ 0x85, 0xF1, /* Report ID (-15) */
+ 0x09, 0x48, /* Usage (0x48) */
+ 0x95, 0x3F, /* Report Count (63) */
+ 0xB1, 0x02, /* Feature (Data,Var,Abs) */
+ 0x85, 0xF2, /* Report ID (-14) */
+ 0x09, 0x49, /* Usage (0x49) */
+ 0x95, 0x0F, /* Report Count (15) */
+ 0xB1, 0x02, /* Feature (Data,Var,Abs) */
+ 0x85, 0xA7, /* Report ID (-89) */
+ 0x09, 0x4A, /* Usage (0x4A) */
+ 0x95, 0x01, /* Report Count (1) */
+ 0xB1, 0x02, /* Feature (Data,Var,Abs) */
+ 0x85, 0xA8, /* Report ID (-88) */
+ 0x09, 0x4B, /* Usage (0x4B) */
+ 0x95, 0x01, /* Report Count (1) */
+ 0xB1, 0x02, /* Feature (Data,Var,Abs) */
+ 0x85, 0xA9, /* Report ID (-87) */
+ 0x09, 0x4C, /* Usage (0x4C) */
+ 0x95, 0x08, /* Report Count (8) */
+ 0xB1, 0x02, /* Feature (Data,Var,Abs) */
+ 0x85, 0xAA, /* Report ID (-86) */
+ 0x09, 0x4E, /* Usage (0x4E) */
+ 0x95, 0x01, /* Report Count (1) */
+ 0xB1, 0x02, /* Feature (Data,Var,Abs) */
+ 0x85, 0xAB, /* Report ID (-85) */
+ 0x09, 0x4F, /* Usage (0x4F) */
+ 0x95, 0x39, /* Report Count (57) */
+ 0xB1, 0x02, /* Feature (Data,Var,Abs) */
+ 0x85, 0xAC, /* Report ID (-84) */
+ 0x09, 0x50, /* Usage (0x50) */
+ 0x95, 0x39, /* Report Count (57) */
+ 0xB1, 0x02, /* Feature (Data,Var,Abs) */
+ 0x85, 0xAD, /* Report ID (-83) */
+ 0x09, 0x51, /* Usage (0x51) */
+ 0x95, 0x0B, /* Report Count (11) */
+ 0xB1, 0x02, /* Feature (Data,Var,Abs) */
+ 0x85, 0xAE, /* Report ID (-82) */
+ 0x09, 0x52, /* Usage (0x52) */
+ 0x95, 0x01, /* Report Count (1) */
+ 0xB1, 0x02, /* Feature (Data,Var,Abs) */
+ 0x85, 0xAF, /* Report ID (-81) */
+ 0x09, 0x53, /* Usage (0x53) */
+ 0x95, 0x02, /* Report Count (2) */
+ 0xB1, 0x02, /* Feature (Data,Var,Abs) */
+ 0x85, 0xB0, /* Report ID (-80) */
+ 0x09, 0x54, /* Usage (0x54) */
+ 0x95, 0x3F, /* Report Count (63) */
+ 0xB1, 0x02, /* Feature (Data,Var,Abs) */
+ 0xC0, /* End Collection */
+};
+
+#define PS4DS_GYRO_RES_PER_DEG_S 1024
+#define PS4DS_ACC_RES_PER_G 8192
+#define PS4DS_MAX_TOUCHPAD_PACKETS 4
+#define PS4DS_FEATURE_REPORT2_SIZE 37
+#define PS4DS_OUTPUT_REPORT5_SIZE 32
+#define PS4DS_OUTPUT_REPORT11_SIZE 78
+
+static hidmap_cb_t ps4dshock_hat_switch_cb;
+static hidmap_cb_t ps4dshock_final_cb;
+static hidmap_cb_t ps4dsacc_data_cb;
+static hidmap_cb_t ps4dsacc_tstamp_cb;
+static hidmap_cb_t ps4dsacc_final_cb;
+static hidmap_cb_t ps4dsmtp_data_cb;
+static hidmap_cb_t ps4dsmtp_npackets_cb;
+static hidmap_cb_t ps4dsmtp_final_cb;
+
+struct ps4ds_out5 {
+ uint8_t features;
+ uint8_t reserved1;
+ uint8_t reserved2;
+ uint8_t rumble_right;
+ uint8_t rumble_left;
+ uint8_t led_color_r;
+ uint8_t led_color_g;
+ uint8_t led_color_b;
+ uint8_t led_delay_on; /* centiseconds */
+ uint8_t led_delay_off;
+} __attribute__((packed));
+
+static const struct ps4ds_led {
+ int r;
+ int g;
+ int b;
+} ps4ds_leds[] = {
+ /* The first 4 entries match the PS4, other from Linux driver */
+ { 0x00, 0x00, 0x40 }, /* Blue */
+ { 0x40, 0x00, 0x00 }, /* Red */
+ { 0x00, 0x40, 0x00 }, /* Green */
+ { 0x20, 0x00, 0x20 }, /* Pink */
+ { 0x02, 0x01, 0x00 }, /* Orange */
+ { 0x00, 0x01, 0x01 }, /* Teal */
+ { 0x01, 0x01, 0x01 } /* White */
+};
+
+enum ps4ds_led_state {
+ PS4DS_LED_OFF,
+ PS4DS_LED_ON,
+ PS4DS_LED_BLINKING,
+ PD4DS_LED_CNT,
+};
+
+/* Map structure for accelerometer and gyro. */
+struct ps4ds_calib_data {
+ int32_t usage;
+ int32_t code;
+ int32_t res;
+ int32_t range;
+ /* Calibration data for accelerometer and gyro. */
+ int16_t bias;
+ int32_t sens_numer;
+ int32_t sens_denom;
+};
+
+enum {
+ PS4DS_TSTAMP,
+ PS4DS_CID1,
+ PS4DS_TIP1,
+ PS4DS_X1,
+ PS4DS_Y1,
+ PS4DS_CID2,
+ PS4DS_TIP2,
+ PS4DS_X2,
+ PS4DS_Y2,
+ PS4DS_NTPUSAGES,
+};
+
+struct ps4dshock_softc {
+ struct hidmap hm;
+
+ bool is_bluetooth;
+
+ struct sx lock;
+ enum ps4ds_led_state led_state;
+ struct ps4ds_led led_color;
+ int led_delay_on; /* msecs */
+ int led_delay_off;
+
+ int rumble_right;
+ int rumble_left;
+};
+
+struct ps4dsacc_softc {
+ struct hidmap hm;
+
+ uint16_t hw_tstamp;
+ int32_t ev_tstamp;
+
+ struct ps4ds_calib_data calib_data[6];
+};
+
+struct ps4dsmtp_softc {
+ struct hidmap hm;
+
+ struct hid_location btn_loc;
+ u_int npackets;
+ int32_t *data_ptr;
+ int32_t data[PS4DS_MAX_TOUCHPAD_PACKETS * PS4DS_NTPUSAGES];
+
+ bool do_tstamps;
+ uint8_t hw_tstamp;
+ int32_t ev_tstamp;
+ bool touch;
+};
+
+#define PD4DSHOCK_OFFSET(field) offsetof(struct ps4dshock_softc, field)
+enum {
+ PD4DSHOCK_SYSCTL_LED_STATE = PD4DSHOCK_OFFSET(led_state),
+ PD4DSHOCK_SYSCTL_LED_COLOR_R = PD4DSHOCK_OFFSET(led_color.r),
+ PD4DSHOCK_SYSCTL_LED_COLOR_G = PD4DSHOCK_OFFSET(led_color.g),
+ PD4DSHOCK_SYSCTL_LED_COLOR_B = PD4DSHOCK_OFFSET(led_color.b),
+ PD4DSHOCK_SYSCTL_LED_DELAY_ON = PD4DSHOCK_OFFSET(led_delay_on),
+ PD4DSHOCK_SYSCTL_LED_DELAY_OFF= PD4DSHOCK_OFFSET(led_delay_off),
+#define PD4DSHOCK_SYSCTL_LAST PD4DSHOCK_SYSCTL_LED_DELAY_OFF
+};
+
+#define PS4DS_MAP_BTN(number, code) \
+ { HIDMAP_KEY(HUP_BUTTON, number, code) }
+#define PS4DS_MAP_ABS(usage, code) \
+ { HIDMAP_ABS(HUP_GENERIC_DESKTOP, HUG_##usage, code) }
+#define PS4DS_MAP_FLT(usage, code) \
+ { HIDMAP_ABS(HUP_GENERIC_DESKTOP, HUG_##usage, code), .flat = 15 }
+#define PS4DS_MAP_VSW(usage, code) \
+ { HIDMAP_SW(HUP_MICROSOFT, usage, code) }
+#define PS4DS_MAP_GCB(usage, callback) \
+ { HIDMAP_ANY_CB(HUP_GENERIC_DESKTOP, HUG_##usage, callback) }
+#define PS4DS_MAP_VCB(usage, callback) \
+ { HIDMAP_ANY_CB(HUP_MICROSOFT, usage, callback) }
+#define PS4DS_FINALCB(cb) \
+ { HIDMAP_FINAL_CB(&cb) }
+
+static const struct hidmap_item ps4dshock_map[] = {
+ PS4DS_MAP_FLT(X, ABS_X),
+ PS4DS_MAP_FLT(Y, ABS_Y),
+ PS4DS_MAP_ABS(Z, ABS_Z),
+ PS4DS_MAP_FLT(RX, ABS_RX),
+ PS4DS_MAP_FLT(RY, ABS_RY),
+ PS4DS_MAP_ABS(RZ, ABS_RZ),
+ PS4DS_MAP_BTN(1, BTN_WEST),
+ PS4DS_MAP_BTN(2, BTN_SOUTH),
+ PS4DS_MAP_BTN(3, BTN_EAST),
+ PS4DS_MAP_BTN(4, BTN_NORTH),
+ PS4DS_MAP_BTN(5, BTN_TL),
+ PS4DS_MAP_BTN(6, BTN_TR),
+ PS4DS_MAP_BTN(7, BTN_TL2),
+ PS4DS_MAP_BTN(8, BTN_TR2),
+ PS4DS_MAP_BTN(9, BTN_SELECT),
+ PS4DS_MAP_BTN(10, BTN_START),
+ PS4DS_MAP_BTN(11, BTN_THUMBL),
+ PS4DS_MAP_BTN(12, BTN_THUMBR),
+ PS4DS_MAP_BTN(13, BTN_MODE),
+ /* Click button is handled by touchpad driver */
+ /* PS4DS_MAP_BTN(14, BTN_LEFT), */
+ PS4DS_MAP_GCB(HAT_SWITCH, ps4dshock_hat_switch_cb),
+ PS4DS_FINALCB( ps4dshock_final_cb),
+};
+static const struct hidmap_item ps4dsacc_map[] = {
+ PS4DS_MAP_GCB(X, ps4dsacc_data_cb),
+ PS4DS_MAP_GCB(Y, ps4dsacc_data_cb),
+ PS4DS_MAP_GCB(Z, ps4dsacc_data_cb),
+ PS4DS_MAP_GCB(RX, ps4dsacc_data_cb),
+ PS4DS_MAP_GCB(RY, ps4dsacc_data_cb),
+ PS4DS_MAP_GCB(RZ, ps4dsacc_data_cb),
+ PS4DS_MAP_VCB(0x0021, ps4dsacc_tstamp_cb),
+ PS4DS_FINALCB( ps4dsacc_final_cb),
+};
+static const struct hidmap_item ps4dshead_map[] = {
+ PS4DS_MAP_VSW(0x0020, SW_MICROPHONE_INSERT),
+ PS4DS_MAP_VSW(0x0021, SW_HEADPHONE_INSERT),
+};
+static const struct hidmap_item ps4dsmtp_map[] = {
+ { HIDMAP_ABS_CB(HUP_MICROSOFT, 0x0021, ps4dsmtp_npackets_cb)},
+ { HIDMAP_ABS_CB(HUP_DIGITIZERS, HUD_SCAN_TIME, ps4dsmtp_data_cb) },
+ { HIDMAP_ABS_CB(HUP_DIGITIZERS, HUD_CONTACTID, ps4dsmtp_data_cb) },
+ { HIDMAP_ABS_CB(HUP_DIGITIZERS, HUD_TIP_SWITCH, ps4dsmtp_data_cb) },
+ { HIDMAP_ABS_CB(HUP_GENERIC_DESKTOP, HUG_X, ps4dsmtp_data_cb) },
+ { HIDMAP_ABS_CB(HUP_GENERIC_DESKTOP, HUG_Y, ps4dsmtp_data_cb) },
+ { HIDMAP_FINAL_CB( ps4dsmtp_final_cb) },
+};
+
+static const struct hid_device_id ps4dshock_devs[] = {
+ { HID_BVP(BUS_USB, USB_VENDOR_SONY, 0x9cc),
+ HID_TLC(HUP_GENERIC_DESKTOP, HUG_GAME_PAD) },
+};
+static const struct hid_device_id ps4dsacc_devs[] = {
+ { HID_BVP(BUS_USB, USB_VENDOR_SONY, 0x9cc),
+ HID_TLC(HUP_GENERIC_DESKTOP, HUG_MULTIAXIS_CNTROLLER) },
+};
+static const struct hid_device_id ps4dshead_devs[] = {
+ { HID_BVP(BUS_USB, USB_VENDOR_SONY, 0x9cc),
+ HID_TLC(HUP_CONSUMER, HUC_HEADPHONE) },
+};
+static const struct hid_device_id ps4dsmtp_devs[] = {
+ { HID_BVP(BUS_USB, USB_VENDOR_SONY, 0x9cc),
+ HID_TLC(HUP_DIGITIZERS, HUD_TOUCHPAD) },
+};
+
+static int
+ps4dshock_hat_switch_cb(HIDMAP_CB_ARGS)
+{
+ static const struct { int32_t x; int32_t y; } hat_switch_map[] = {
+ {0, -1}, {1, -1}, {1, 0}, {1, 1}, {0, 1}, {-1, 1}, {-1, 0},
+ {-1, -1},{0, 0}
+ };
+ struct evdev_dev *evdev = HIDMAP_CB_GET_EVDEV();
+ u_int idx;
+
+ switch (HIDMAP_CB_GET_STATE()) {
+ case HIDMAP_CB_IS_ATTACHING:
+ evdev_support_event(evdev, EV_ABS);
+ evdev_support_abs(evdev, ABS_HAT0X, -1, 1, 0, 0, 0);
+ evdev_support_abs(evdev, ABS_HAT0Y, -1, 1, 0, 0, 0);
+ break;
+
+ case HIDMAP_CB_IS_RUNNING:
+ idx = MIN(nitems(hat_switch_map) - 1, (u_int)ctx.data);
+ evdev_push_abs(evdev, ABS_HAT0X, hat_switch_map[idx].x);
+ evdev_push_abs(evdev, ABS_HAT0Y, hat_switch_map[idx].y);
+ }
+
+ return (0);
+}
+
+static int
+ps4dshock_final_cb(HIDMAP_CB_ARGS)
+{
+ struct evdev_dev *evdev = HIDMAP_CB_GET_EVDEV();
+
+ if (HIDMAP_CB_GET_STATE() == HIDMAP_CB_IS_ATTACHING)
+ evdev_support_prop(evdev, INPUT_PROP_DIRECT);
+
+ /* Do not execute callback at interrupt handler and detach */
+ return (ENOSYS);
+}
+
+static int
+ps4dsacc_data_cb(HIDMAP_CB_ARGS)
+{
+ struct evdev_dev *evdev = HIDMAP_CB_GET_EVDEV();
+ struct ps4dsacc_softc *sc = HIDMAP_CB_GET_SOFTC();
+ struct ps4ds_calib_data *calib;
+ u_int i;
+
+ switch (HIDMAP_CB_GET_STATE()) {
+ case HIDMAP_CB_IS_ATTACHING:
+ for (i = 0; i < nitems(sc->calib_data); i++) {
+ if (sc->calib_data[i].usage == ctx.hi->usage) {
+ evdev_support_abs(evdev,
+ sc->calib_data[i].code,
+ -sc->calib_data[i].range,
+ sc->calib_data[i].range, 16, 0,
+ sc->calib_data[i].res);
+ HIDMAP_CB_UDATA = &sc->calib_data[i];
+ break;
+ }
+ }
+ break;
+
+ case HIDMAP_CB_IS_RUNNING:
+ calib = HIDMAP_CB_UDATA;
+ evdev_push_abs(evdev, calib->code,
+ ((int64_t)ctx.data - calib->bias) * calib->sens_numer /
+ calib->sens_denom);
+ break;
+ }
+
+ return (0);
+}
+
+static int
+ps4dsacc_tstamp_cb(HIDMAP_CB_ARGS)
+{
+ struct evdev_dev *evdev = HIDMAP_CB_GET_EVDEV();
+ struct ps4dsacc_softc *sc = HIDMAP_CB_GET_SOFTC();
+ uint16_t tstamp;
+
+ switch (HIDMAP_CB_GET_STATE()) {
+ case HIDMAP_CB_IS_ATTACHING:
+ evdev_support_event(evdev, EV_MSC);
+ evdev_support_msc(evdev, MSC_TIMESTAMP);
+ break;
+
+ case HIDMAP_CB_IS_RUNNING:
+ /* Convert timestamp (in 5.33us unit) to timestamp_us */
+ tstamp = (uint16_t)ctx.data;
+ sc->ev_tstamp += (uint16_t)(tstamp - sc->hw_tstamp) * 16 / 3;
+ sc->hw_tstamp = tstamp;
+ evdev_push_msc(evdev, MSC_TIMESTAMP, sc->ev_tstamp);
+ break;
+ }
+
+ return (0);
+}
+
+static int
+ps4dsacc_final_cb(HIDMAP_CB_ARGS)
+{
+ struct evdev_dev *evdev = HIDMAP_CB_GET_EVDEV();
+
+ if (HIDMAP_CB_GET_STATE() == HIDMAP_CB_IS_ATTACHING) {
+ evdev_support_event(evdev, EV_ABS);
+ evdev_support_prop(evdev, INPUT_PROP_ACCELEROMETER);
+ }
+ /* Do not execute callback at interrupt handler and detach */
+ return (ENOSYS);
+}
+
+static int
+ps4dsmtp_npackets_cb(HIDMAP_CB_ARGS)
+{
+ struct ps4dsmtp_softc *sc = HIDMAP_CB_GET_SOFTC();
+
+ if (HIDMAP_CB_GET_STATE() == HIDMAP_CB_IS_RUNNING) {
+ sc->npackets = MIN(PS4DS_MAX_TOUCHPAD_PACKETS,(u_int)ctx.data);
+ /* Reset pointer here as it is first usage in touchpad TLC */
+ sc->data_ptr = sc->data;
+ }
+
+ return (0);
+}
+
+static int
+ps4dsmtp_data_cb(HIDMAP_CB_ARGS)
+{
+ struct ps4dsmtp_softc *sc = HIDMAP_CB_GET_SOFTC();
+
+ if (HIDMAP_CB_GET_STATE() == HIDMAP_CB_IS_RUNNING) {
+ *sc->data_ptr = ctx.data;
+ ++sc->data_ptr;
+ }
+
+ return (0);
+}
+
+static void
+ps4dsmtp_push_packet(struct ps4dsmtp_softc *sc, struct evdev_dev *evdev,
+ int32_t *data)
+{
+ uint8_t hw_tstamp, delta;
+ bool touch;
+
+ evdev_push_abs(evdev, ABS_MT_SLOT, 0);
+ if (data[PS4DS_TIP1] == 0) {
+ evdev_push_abs(evdev, ABS_MT_TRACKING_ID, data[PS4DS_CID1]);
+ evdev_push_abs(evdev, ABS_MT_POSITION_X, data[PS4DS_X1]);
+ evdev_push_abs(evdev, ABS_MT_POSITION_Y, data[PS4DS_Y1]);
+ } else
+ evdev_push_abs(evdev, ABS_MT_TRACKING_ID, -1);
+ evdev_push_abs(evdev, ABS_MT_SLOT, 1);
+ if (data[PS4DS_TIP2] == 0) {
+ evdev_push_abs(evdev, ABS_MT_TRACKING_ID, data[PS4DS_CID2]);
+ evdev_push_abs(evdev, ABS_MT_POSITION_X, data[PS4DS_X2]);
+ evdev_push_abs(evdev, ABS_MT_POSITION_Y, data[PS4DS_Y2]);
+ } else
+ evdev_push_abs(evdev, ABS_MT_TRACKING_ID, -1);
+
+ if (sc->do_tstamps) {
+ /*
+ * Export hardware timestamps in libinput-friendly way.
+ * Make timestamp counter 32-bit, scale up hardware
+ * timestamps to be on per 1usec basis and reset
+ * counter at the start of each touch.
+ */
+ hw_tstamp = (uint8_t)data[PS4DS_TSTAMP];
+ delta = hw_tstamp - sc->hw_tstamp;
+ sc->hw_tstamp = hw_tstamp;
+ touch = data[PS4DS_TIP1] == 0 || data[PS4DS_TIP2] == 0;
+ /* Hardware timestamp counter ticks in 682 usec interval. */
+ if ((touch || sc->touch) && delta != 0) {
+ if (sc->touch)
+ sc->ev_tstamp += delta * 682;
+ evdev_push_msc(evdev, MSC_TIMESTAMP, sc->ev_tstamp);
+ }
+ if (!touch)
+ sc->ev_tstamp = 0;
+ sc->touch = touch;
+ }
+}
+
+static int
+ps4dsmtp_final_cb(HIDMAP_CB_ARGS)
+{
+ struct ps4dsmtp_softc *sc = HIDMAP_CB_GET_SOFTC();
+ struct evdev_dev *evdev = HIDMAP_CB_GET_EVDEV();
+ int32_t *data;
+
+ switch (HIDMAP_CB_GET_STATE()) {
+ case HIDMAP_CB_IS_ATTACHING:
+ if (hid_test_quirk(hid_get_device_info(sc->hm.dev),
+ HQ_MT_TIMESTAMP))
+ sc->do_tstamps = true;
+ /*
+ * Dualshock 4 touchpad TLC contained in fixed report
+ * descriptor is almost compatible with MS precission touchpad
+ * specs and hmt(4) driver. But... for some reasons "Click"
+ * button location was grouped with other GamePad buttons by
+ * touchpad designers so it belongs to GamePad TLC. Fix it with
+ * direct reading of "Click" button value from interrupt frame.
+ */
+ sc->btn_loc = (struct hid_location) { 1, 0, 49 };
+ evdev_support_event(evdev, EV_SYN);
+ evdev_support_event(evdev, EV_KEY);
+ evdev_support_event(evdev, EV_ABS);
+ if (sc->do_tstamps) {
+ evdev_support_event(evdev, EV_MSC);
+ evdev_support_msc(evdev, MSC_TIMESTAMP);
+ }
+ evdev_support_key(evdev, BTN_LEFT);
+ evdev_support_abs(evdev, ABS_MT_SLOT, 0, 1, 0, 0, 0);
+ evdev_support_abs(evdev, ABS_MT_TRACKING_ID, -1, 127, 0, 0, 0);
+ evdev_support_abs(evdev, ABS_MT_POSITION_X, 0, 1920, 0, 0, 30);
+ evdev_support_abs(evdev, ABS_MT_POSITION_Y, 0, 942, 0, 0, 49);
+ evdev_support_prop(evdev, INPUT_PROP_POINTER);
+ evdev_support_prop(evdev, INPUT_PROP_BUTTONPAD);
+ evdev_set_flag(evdev, EVDEV_FLAG_MT_STCOMPAT);
+ break;
+
+ case HIDMAP_CB_IS_RUNNING:
+ /* Only packets with ReportID=1 are accepted */
+ if (HIDMAP_CB_GET_RID() != 1)
+ return (ENOTSUP);
+ evdev_push_key(evdev, BTN_LEFT,
+ HIDMAP_CB_GET_UDATA(&sc->btn_loc));
+ for (data = sc->data;
+ data < sc->data + PS4DS_NTPUSAGES * sc->npackets;
+ data += PS4DS_NTPUSAGES) {
+ ps4dsmtp_push_packet(sc, evdev, data);
+ evdev_sync(evdev);
+ }
+ break;
+ }
+
+ /* Do execute callback at interrupt handler and detach */
+ return (0);
+}
+
+static int
+ps4dshock_write(struct ps4dshock_softc *sc)
+{
+ hid_size_t osize = sc->is_bluetooth ?
+ PS4DS_OUTPUT_REPORT11_SIZE : PS4DS_OUTPUT_REPORT5_SIZE;
+ uint8_t buf[osize];
+ int offset;
+ bool led_on, led_blinks;
+
+ memset(buf, 0, osize);
+ buf[0] = sc->is_bluetooth ? 0x11 : 0x05;
+ offset = sc->is_bluetooth ? 3 : 1;
+ led_on = sc->led_state != PS4DS_LED_OFF;
+ led_blinks = sc->led_state == PS4DS_LED_BLINKING;
+ *(struct ps4ds_out5 *)(buf + offset) = (struct ps4ds_out5) {
+ .features = 0x07, /* blink + LEDs + motor */
+ .rumble_right = sc->rumble_right,
+ .rumble_left = sc->rumble_left,
+ .led_color_r = led_on ? sc->led_color.r : 0,
+ .led_color_g = led_on ? sc->led_color.g : 0,
+ .led_color_b = led_on ? sc->led_color.b : 0,
+ /* convert milliseconds to centiseconds */
+ .led_delay_on = led_blinks ? sc->led_delay_on / 10 : 0,
+ .led_delay_off = led_blinks ? sc->led_delay_off / 10 : 0,
+ };
+
+ return (hid_write(sc->hm.dev, buf, osize));
+}
+
+/* Synaptics Touchpad */
+static int
+ps4dshock_sysctl(SYSCTL_HANDLER_ARGS)
+{
+ struct ps4dshock_softc *sc;
+ int error, arg;
+
+ if (oidp->oid_arg1 == NULL || oidp->oid_arg2 < 0 ||
+ oidp->oid_arg2 > PD4DSHOCK_SYSCTL_LAST)
+ return (EINVAL);
+
+ sc = oidp->oid_arg1;
+ sx_xlock(&sc->lock);
+
+ /* Read the current value. */
+ arg = *(int *)((char *)sc + oidp->oid_arg2);
+ error = sysctl_handle_int(oidp, &arg, 0, req);
+
+ /* Sanity check. */
+ if (error || !req->newptr)
+ goto unlock;
+
+ /*
+ * Check that the new value is in the concerned node's range
+ * of values.
+ */
+ switch (oidp->oid_arg2) {
+ case PD4DSHOCK_SYSCTL_LED_STATE:
+ if (arg < 0 || arg >= PD4DS_LED_CNT)
+ error = EINVAL;
+ break;
+ case PD4DSHOCK_SYSCTL_LED_COLOR_R:
+ case PD4DSHOCK_SYSCTL_LED_COLOR_G:
+ case PD4DSHOCK_SYSCTL_LED_COLOR_B:
+ if (arg < 0 || arg > UINT8_MAX)
+ error = EINVAL;
+ break;
+ case PD4DSHOCK_SYSCTL_LED_DELAY_ON:
+ case PD4DSHOCK_SYSCTL_LED_DELAY_OFF:
+ if (arg < 0 || arg > UINT8_MAX * 10)
+ error = EINVAL;
+ break;
+ default:
+ error = EINVAL;
+ }
+
+ /* Update. */
+ if (error == 0) {
+ *(int *)((char *)sc + oidp->oid_arg2) = arg;
+ ps4dshock_write(sc);
+ }
+unlock:
+ sx_unlock(&sc->lock);
+
+ return (error);
+}
+
+static void
+ps4dshock_identify(driver_t *driver, device_t parent)
+{
+
+ /* Overload PS4 DualShock gamepad rudimentary report descriptor */
+ if (HIDBUS_LOOKUP_ID(parent, ps4dshock_devs) != NULL)
+ hid_set_report_descr(parent, ps4dshock_rdesc,
+ sizeof(ps4dshock_rdesc));
+}
+
+static int
+ps4dshock_probe(device_t dev)
+{
+ struct ps4dshock_softc *sc = device_get_softc(dev);
+
+ hidmap_set_debug_var(&sc->hm, &HID_DEBUG_VAR);
+ return (
+ HIDMAP_PROBE(&sc->hm, dev, ps4dshock_devs, ps4dshock_map, NULL)
+ );
+}
+
+static int
+ps4dsacc_probe(device_t dev)
+{
+ struct ps4dsacc_softc *sc = device_get_softc(dev);
+
+ hidmap_set_debug_var(&sc->hm, &HID_DEBUG_VAR);
+ return (
+ HIDMAP_PROBE(&sc->hm, dev, ps4dsacc_devs, ps4dsacc_map, "Sensors")
+ );
+}
+
+static int
+ps4dshead_probe(device_t dev)
+{
+ struct hidmap *hm = device_get_softc(dev);
+
+ hidmap_set_debug_var(hm, &HID_DEBUG_VAR);
+ return (
+ HIDMAP_PROBE(hm, dev, ps4dshead_devs, ps4dshead_map, "Headset")
+ );
+}
+
+static int
+ps4dsmtp_probe(device_t dev)
+{
+ struct ps4dshock_softc *sc = device_get_softc(dev);
+
+ hidmap_set_debug_var(&sc->hm, &HID_DEBUG_VAR);
+ return (
+ HIDMAP_PROBE(&sc->hm, dev, ps4dsmtp_devs, ps4dsmtp_map, "Touchpad")
+ );
+}
+
+static int
+ps4dshock_attach(device_t dev)
+{
+ struct ps4dshock_softc *sc = device_get_softc(dev);
+ struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(dev);
+ struct sysctl_oid *tree = device_get_sysctl_tree(dev);
+
+ sc->led_state = PS4DS_LED_ON;
+ sc->led_color = ps4ds_leds[device_get_unit(dev) % nitems(ps4ds_leds)];
+ sc->led_delay_on = 500; /* 1 Hz */
+ sc->led_delay_off = 500;
+ ps4dshock_write(sc);
+
+ sx_init(&sc->lock, "ps4dshock");
+
+ SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+ "led_state", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY, sc,
+ PD4DSHOCK_SYSCTL_LED_STATE, ps4dshock_sysctl, "I",
+ "LED state: 0 - off, 1 - on, 2 - blinking.");
+
+ SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+ "led_color_r", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY, sc,
+ PD4DSHOCK_SYSCTL_LED_COLOR_R, ps4dshock_sysctl, "I",
+ "LED color. Red component.");
+
+ SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+ "led_color_g", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY, sc,
+ PD4DSHOCK_SYSCTL_LED_COLOR_G, ps4dshock_sysctl, "I",
+ "LED color. Green component.");
+
+ SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+ "led_color_b", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY, sc,
+ PD4DSHOCK_SYSCTL_LED_COLOR_B, ps4dshock_sysctl, "I",
+ "LED color. Blue component.");
+
+ SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+ "led_delay_on", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY, sc,
+ PD4DSHOCK_SYSCTL_LED_DELAY_ON, ps4dshock_sysctl, "I",
+ "LED blink. On delay, msecs.");
+
+ SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+ "led_delay_off", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY, sc,
+ PD4DSHOCK_SYSCTL_LED_DELAY_OFF, ps4dshock_sysctl, "I",
+ "LED blink. Off delay, msecs.");
+
+ return (hidmap_attach(&sc->hm));
+}
+
+static int
+ps4dsacc_attach(device_t dev)
+{
+ struct ps4dsacc_softc *sc = device_get_softc(dev);
+ uint8_t buf[PS4DS_FEATURE_REPORT2_SIZE];
+ int error, speed_2x, range_2g;
+
+ /* Read accelerometers and gyroscopes calibration data */
+ error = hid_get_report(dev, buf, sizeof(buf), NULL,
+ HID_FEATURE_REPORT, 0x02);
+ if (error)
+ DPRINTF("get feature report failed, error=%d "
+ "(ignored)\n", error);
+
+ DPRINTFN(5, "calibration data: %*D\n", (int)sizeof(buf), buf, " ");
+
+ /*
+ * Set gyroscope calibration and normalization parameters.
+ * Data values will be normalized to 1/ PS4DS_GYRO_RES_PER_DEG_S
+ * degree/s.
+ */
+#define HGETW(w) ((int16_t)((w)[0] | (((uint16_t)((w)[1])) << 8)))
+ speed_2x = HGETW(&buf[19]) + HGETW(&buf[21]);
+ sc->calib_data[0].usage = HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_RX);
+ sc->calib_data[0].code = ABS_RX;
+ sc->calib_data[0].range = PS4DS_GYRO_RES_PER_DEG_S * 2048;
+ sc->calib_data[0].res = PS4DS_GYRO_RES_PER_DEG_S;
+ sc->calib_data[0].bias = HGETW(&buf[1]);
+ sc->calib_data[0].sens_numer = speed_2x * PS4DS_GYRO_RES_PER_DEG_S;
+ sc->calib_data[0].sens_denom = HGETW(&buf[7]) - HGETW(&buf[9]);
+ /* BT case */
+ /* sc->calib_data[0].sens_denom = HGETW(&buf[7]) - HGETW(&buf[13]); */
+
+ sc->calib_data[1].usage = HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_RY);
+ sc->calib_data[1].code = ABS_RY;
+ sc->calib_data[1].range = PS4DS_GYRO_RES_PER_DEG_S * 2048;
+ sc->calib_data[1].res = PS4DS_GYRO_RES_PER_DEG_S;
+ sc->calib_data[1].bias = HGETW(&buf[3]);
+ sc->calib_data[1].sens_numer = speed_2x * PS4DS_GYRO_RES_PER_DEG_S;
+ sc->calib_data[1].sens_denom = HGETW(&buf[11]) - HGETW(&buf[13]);
+ /* BT case */
+ /* sc->calib_data[1].sens_denom = HGETW(&buf[9]) - HGETW(&buf[15]); */
+
+ sc->calib_data[2].usage = HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_RZ);
+ sc->calib_data[2].code = ABS_RZ;
+ sc->calib_data[2].range = PS4DS_GYRO_RES_PER_DEG_S * 2048;
+ sc->calib_data[2].res = PS4DS_GYRO_RES_PER_DEG_S;
+ sc->calib_data[2].bias = HGETW(&buf[5]);
+ sc->calib_data[2].sens_numer = speed_2x * PS4DS_GYRO_RES_PER_DEG_S;
+ sc->calib_data[2].sens_denom = HGETW(&buf[15]) - HGETW(&buf[17]);
+ /* BT case */
+ /* sc->calib_data[2].sens_denom = HGETW(&buf[11]) - HGETW(&buf[17]); */
+
+ /*
+ * Set accelerometer calibration and normalization parameters.
+ * Data values will be normalized to 1 / PS4DS_ACC_RES_PER_G G.
+ */
+ range_2g = HGETW(&buf[23]) - HGETW(&buf[25]);
+ sc->calib_data[3].usage = HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_X);
+ sc->calib_data[3].code = ABS_X;
+ sc->calib_data[3].range = PS4DS_ACC_RES_PER_G * 4;
+ sc->calib_data[3].res = PS4DS_ACC_RES_PER_G;
+ sc->calib_data[3].bias = HGETW(&buf[23]) - range_2g / 2;
+ sc->calib_data[3].sens_numer = 2 * PS4DS_ACC_RES_PER_G;
+ sc->calib_data[3].sens_denom = range_2g;
+
+ range_2g = HGETW(&buf[27]) - HGETW(&buf[29]);
+ sc->calib_data[4].usage = HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Y);
+ sc->calib_data[4].code = ABS_Y;
+ sc->calib_data[4].range = PS4DS_ACC_RES_PER_G * 4;
+ sc->calib_data[4].res = PS4DS_ACC_RES_PER_G;
+ sc->calib_data[4].bias = HGETW(&buf[27]) - range_2g / 2;
+ sc->calib_data[4].sens_numer = 2 * PS4DS_ACC_RES_PER_G;
+ sc->calib_data[4].sens_denom = range_2g;
+
+ range_2g = HGETW(&buf[31]) - HGETW(&buf[33]);
+ sc->calib_data[5].usage = HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Z);
+ sc->calib_data[5].code = ABS_Z;
+ sc->calib_data[5].range = PS4DS_ACC_RES_PER_G * 4;
+ sc->calib_data[5].res = PS4DS_ACC_RES_PER_G;
+ sc->calib_data[5].bias = HGETW(&buf[31]) - range_2g / 2;
+ sc->calib_data[5].sens_numer = 2 * PS4DS_ACC_RES_PER_G;
+ sc->calib_data[5].sens_denom = range_2g;
+
+ return (hidmap_attach(&sc->hm));
+}
+
+static int
+ps4dshead_attach(device_t dev)
+{
+ return (hidmap_attach(device_get_softc(dev)));
+}
+
+static int
+ps4dsmtp_attach(device_t dev)
+{
+ struct ps4dsmtp_softc *sc = device_get_softc(dev);
+
+ return (hidmap_attach(&sc->hm));
+}
+
+static int
+ps4dshock_detach(device_t dev)
+{
+ struct ps4dshock_softc *sc = device_get_softc(dev);
+
+ hidmap_detach(&sc->hm);
+ sc->led_state = PS4DS_LED_OFF;
+ ps4dshock_write(sc);
+ sx_destroy(&sc->lock);
+
+ return (0);
+}
+
+static int
+ps4dsacc_detach(device_t dev)
+{
+ struct ps4dsacc_softc *sc = device_get_softc(dev);
+
+ return (hidmap_detach(&sc->hm));
+}
+
+static int
+ps4dshead_detach(device_t dev)
+{
+ return (hidmap_detach(device_get_softc(dev)));
+}
+
+static int
+ps4dsmtp_detach(device_t dev)
+{
+ struct ps4dsmtp_softc *sc = device_get_softc(dev);
+
+ return (hidmap_detach(&sc->hm));
+}
+
+static devclass_t ps4dshock_devclass;
+static devclass_t ps4dsacc_devclass;
+static devclass_t ps4dshead_devclass;
+static devclass_t ps4dsmtp_devclass;
+
+static device_method_t ps4dshock_methods[] = {
+ DEVMETHOD(device_identify, ps4dshock_identify),
+ DEVMETHOD(device_probe, ps4dshock_probe),
+ DEVMETHOD(device_attach, ps4dshock_attach),
+ DEVMETHOD(device_detach, ps4dshock_detach),
+
+ DEVMETHOD_END
+};
+static device_method_t ps4dsacc_methods[] = {
+ DEVMETHOD(device_probe, ps4dsacc_probe),
+ DEVMETHOD(device_attach, ps4dsacc_attach),
+ DEVMETHOD(device_detach, ps4dsacc_detach),
+
+ DEVMETHOD_END
+};
+static device_method_t ps4dshead_methods[] = {
+ DEVMETHOD(device_probe, ps4dshead_probe),
+ DEVMETHOD(device_attach, ps4dshead_attach),
+ DEVMETHOD(device_detach, ps4dshead_detach),
+
+ DEVMETHOD_END
+};
+static device_method_t ps4dsmtp_methods[] = {
+ DEVMETHOD(device_probe, ps4dsmtp_probe),
+ DEVMETHOD(device_attach, ps4dsmtp_attach),
+ DEVMETHOD(device_detach, ps4dsmtp_detach),
+
+ DEVMETHOD_END
+};
+
+DEFINE_CLASS_0(ps4dsacc, ps4dsacc_driver, ps4dsacc_methods,
+ sizeof(struct ps4dsacc_softc));
+DRIVER_MODULE(ps4dsacc, hidbus, ps4dsacc_driver, ps4dsacc_devclass, NULL, 0);
+DEFINE_CLASS_0(ps4dshead, ps4dshead_driver, ps4dshead_methods,
+ sizeof(struct hidmap));
+DRIVER_MODULE(ps4dshead, hidbus, ps4dshead_driver, ps4dshead_devclass, NULL, 0);
+DEFINE_CLASS_0(ps4dsmtp, ps4dsmtp_driver, ps4dsmtp_methods,
+ sizeof(struct ps4dsmtp_softc));
+DRIVER_MODULE(ps4dsmtp, hidbus, ps4dsmtp_driver, ps4dsmtp_devclass, NULL, 0);
+DEFINE_CLASS_0(ps4dshock, ps4dshock_driver, ps4dshock_methods,
+ sizeof(struct ps4dshock_softc));
+DRIVER_MODULE(ps4dshock, hidbus, ps4dshock_driver, ps4dshock_devclass, NULL, 0);
+
+MODULE_DEPEND(ps4dshock, hid, 1, 1, 1);
+MODULE_DEPEND(ps4dshock, hidbus, 1, 1, 1);
+MODULE_DEPEND(ps4dshock, hidmap, 1, 1, 1);
+MODULE_DEPEND(ps4dshock, evdev, 1, 1, 1);
+MODULE_VERSION(ps4dshock, 1);
+HID_PNP_INFO(ps4dshock_devs);
diff --git a/sys/modules/hid/Makefile b/sys/modules/hid/Makefile
index 0936bae92738..20ae0b3b9302 100644
--- a/sys/modules/hid/Makefile
+++ b/sys/modules/hid/Makefile
@@ -3,12 +3,17 @@
SUBDIR = \
hid \
hidbus \
+ hidmap \
hidquirk \
hidraw
SUBDIR += \
hconf \
+ hcons \
hkbd \
- hmt
+ hms \
+ hmt \
+ hsctrl \
+ ps4dshock
.include <bsd.subdir.mk>
diff --git a/sys/modules/hid/hcons/Makefile b/sys/modules/hid/hcons/Makefile
new file mode 100644
index 000000000000..2c2ad16322af
--- /dev/null
+++ b/sys/modules/hid/hcons/Makefile
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+.PATH: ${SRCTOP}/sys/dev/hid
+
+KMOD= hcons
+SRCS= hcons.c
+SRCS+= bus_if.h device_if.h
+
+.include <bsd.kmod.mk>
diff --git a/sys/modules/hid/hidmap/Makefile b/sys/modules/hid/hidmap/Makefile
new file mode 100644
index 000000000000..8e79f156d9d7
--- /dev/null
+++ b/sys/modules/hid/hidmap/Makefile
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+.PATH: ${SRCTOP}/sys/dev/hid
+
+KMOD= hidmap
+SRCS= hidmap.c
+SRCS+= bus_if.h device_if.h
+
+.include <bsd.kmod.mk>
diff --git a/sys/modules/hid/hms/Makefile b/sys/modules/hid/hms/Makefile
new file mode 100644
index 000000000000..71fbd2a77b89
--- /dev/null
+++ b/sys/modules/hid/hms/Makefile
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+.PATH: ${SRCTOP}/sys/dev/hid
+
+KMOD= hms
+SRCS= hms.c
+SRCS+= bus_if.h device_if.h
+
+.include <bsd.kmod.mk>
diff --git a/sys/modules/hid/hsctrl/Makefile b/sys/modules/hid/hsctrl/Makefile
new file mode 100644
index 000000000000..e0feba2657e5
--- /dev/null
+++ b/sys/modules/hid/hsctrl/Makefile
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+.PATH: ${SRCTOP}/sys/dev/hid
+
+KMOD= hsctrl
+SRCS= hsctrl.c
+SRCS+= bus_if.h device_if.h
+
+.include <bsd.kmod.mk>
diff --git a/sys/modules/hid/ps4dshock/Makefile b/sys/modules/hid/ps4dshock/Makefile
new file mode 100644
index 000000000000..688494f33ac6
--- /dev/null
+++ b/sys/modules/hid/ps4dshock/Makefile
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+.PATH: ${SRCTOP}/sys/dev/hid
+
+KMOD= ps4dshock
+SRCS= ps4dshock.c
+SRCS+= bus_if.h device_if.h usbdevs.h
+
+.include <bsd.kmod.mk>