Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F102186921
D27993.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
118 KB
Referenced Files
None
Subscribers
None
D27993.diff
View Options
diff --git a/share/man/man4/Makefile b/share/man/man4/Makefile
--- a/share/man/man4/Makefile
+++ b/share/man/man4/Makefile
@@ -180,11 +180,13 @@
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 @@
${_hptmv.4} \
${_hptnr.4} \
${_hptrr.4} \
+ hsctrl.4 \
${_hv_kvp.4} \
${_hv_netvsc.4} \
${_hv_storvsc.4} \
@@ -432,6 +435,7 @@
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
--- /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
--- /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
--- /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
--- /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
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -1816,13 +1816,18 @@
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
--- /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.h b/sys/dev/hid/hidmap.h
new file mode 100644
--- /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/hidmap.c b/sys/dev/hid/hidmap.c
new file mode 100644
--- /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/hms.c b/sys/dev/hid/hms.c
new file mode 100644
--- /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
--- /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
--- /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
--- 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
--- /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
--- /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
--- /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
--- /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
--- /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>
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sat, Nov 9, 4:31 PM (21 h, 39 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
14562972
Default Alt Text
D27993.diff (118 KB)
Attached To
Mode
D27993: hid: Import hidmap and bunch of drivers based on it.
Attached
Detach File
Event Timeline
Log In to Comment