hid: Import hidmap and bunch of drivers based on it
hidmap is a kernel module that maps HID input usages to evdev events. Following dependent drivers is included in the commit: hms - HID mouse driver. hcons - Consumer page AKA Multimedia keys driver. hsctrl - System Controls page (Power/Sleep keys) driver. ps4dshock - Sony DualShock 4 gamepad driver. Reviewed by: hselasky Differential revision: https://reviews.freebsd.org/D27993
This commit is contained in:
parent
3b2175fdb6
commit
afd590d9e5
@ -180,11 +180,13 @@ MAN= aac.4 \
|
||||
gre.4 \
|
||||
h_ertt.4 \
|
||||
hconf.4 \
|
||||
hcons.4 \
|
||||
hidbus.4 \
|
||||
hidquirk.4 \
|
||||
hidraw.4 \
|
||||
hifn.4 \
|
||||
hkbd.4 \
|
||||
hms.4 \
|
||||
hmt.4 \
|
||||
hpet.4 \
|
||||
${_hpt27xx.4} \
|
||||
@ -192,6 +194,7 @@ MAN= aac.4 \
|
||||
${_hptmv.4} \
|
||||
${_hptnr.4} \
|
||||
${_hptrr.4} \
|
||||
hsctrl.4 \
|
||||
${_hv_kvp.4} \
|
||||
${_hv_netvsc.4} \
|
||||
${_hv_storvsc.4} \
|
||||
@ -432,6 +435,7 @@ MAN= aac.4 \
|
||||
ppi.4 \
|
||||
procdesc.4 \
|
||||
proto.4 \
|
||||
ps4dshock.4 \
|
||||
psm.4 \
|
||||
pst.4 \
|
||||
pt.4 \
|
||||
|
98
share/man/man4/hcons.4
Normal file
98
share/man/man4/hcons.4
Normal file
@ -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 .
|
115
share/man/man4/hms.4
Normal file
115
share/man/man4/hms.4
Normal file
@ -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 .
|
98
share/man/man4/hsctrl.4
Normal file
98
share/man/man4/hsctrl.4
Normal file
@ -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 .
|
109
share/man/man4/ps4dshock.4
Normal file
109
share/man/man4/ps4dshock.4
Normal file
@ -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 .
|
@ -1816,13 +1816,18 @@ dev/gpio/gpiobus_if.m optional gpio
|
||||
dev/gpio/gpiopps.c optional gpiopps fdt
|
||||
dev/gpio/ofw_gpiobus.c optional fdt gpio
|
||||
dev/hid/hconf.c optional hconf
|
||||
dev/hid/hcons.c optional hcons
|
||||
dev/hid/hid.c optional hid
|
||||
dev/hid/hid_if.m optional hid
|
||||
dev/hid/hidbus.c optional hidbus
|
||||
dev/hid/hidmap.c optional hidmap
|
||||
dev/hid/hidquirk.c optional hid
|
||||
dev/hid/hidraw.c optional hidraw
|
||||
dev/hid/hkbd.c optional hkbd
|
||||
dev/hid/hms.c optional hms
|
||||
dev/hid/hmt.c optional hmt hconf
|
||||
dev/hid/hsctrl.c optional hsctrl
|
||||
dev/hid/ps4dshock.c optional ps4dshock
|
||||
dev/hifn/hifn7751.c optional hifn
|
||||
dev/hptiop/hptiop.c optional hptiop scbus
|
||||
dev/hwpmc/hwpmc_logging.c optional hwpmc
|
||||
|
295
sys/dev/hid/hcons.c
Normal file
295
sys/dev/hid/hcons.c
Normal file
@ -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);
|
832
sys/dev/hid/hidmap.c
Normal file
832
sys/dev/hid/hidmap.c
Normal file
@ -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);
|
262
sys/dev/hid/hidmap.h
Normal file
262
sys/dev/hid/hidmap.h
Normal file
@ -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_ */
|
267
sys/dev/hid/hms.c
Normal file
267
sys/dev/hid/hms.c
Normal file
@ -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);
|
110
sys/dev/hid/hsctrl.c
Normal file
110
sys/dev/hid/hsctrl.c
Normal file
@ -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);
|
1406
sys/dev/hid/ps4dshock.c
Normal file
1406
sys/dev/hid/ps4dshock.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -3,12 +3,17 @@
|
||||
SUBDIR = \
|
||||
hid \
|
||||
hidbus \
|
||||
hidmap \
|
||||
hidquirk \
|
||||
hidraw
|
||||
|
||||
SUBDIR += \
|
||||
hconf \
|
||||
hcons \
|
||||
hkbd \
|
||||
hmt
|
||||
hms \
|
||||
hmt \
|
||||
hsctrl \
|
||||
ps4dshock
|
||||
|
||||
.include <bsd.subdir.mk>
|
||||
|
9
sys/modules/hid/hcons/Makefile
Normal file
9
sys/modules/hid/hcons/Makefile
Normal file
@ -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>
|
9
sys/modules/hid/hidmap/Makefile
Normal file
9
sys/modules/hid/hidmap/Makefile
Normal file
@ -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>
|
9
sys/modules/hid/hms/Makefile
Normal file
9
sys/modules/hid/hms/Makefile
Normal file
@ -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>
|
9
sys/modules/hid/hsctrl/Makefile
Normal file
9
sys/modules/hid/hsctrl/Makefile
Normal file
@ -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>
|
9
sys/modules/hid/ps4dshock/Makefile
Normal file
9
sys/modules/hid/ps4dshock/Makefile
Normal file
@ -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>
|
Loading…
Reference in New Issue
Block a user