hid: Import functions and constants required by new subsystem

This does an import of quirk stubs, debugging macros from USB code and
numerous usage constants used by dependent drivers.

Besides, this change renames some functions to get a better matching
with userland library and NetBSD/OpenBSD HID code. Namely:

- Old hid_report_size() renamed to hid_report_size_max()
- New hid_report_size() calculates size of given report rather than
  maximum size of all reports.
- hid_get_data_unsigned() renamed to hid_get_udata()
- hid_put_data_unsigned() renamed to hid_put_udata()

Compat shim functions are provided in usbhid.h to make possible compile
of legacy code unmodified after this change.

Reviewed by:	manu, hselasky
Differential revision:	https://reviews.freebsd.org/D27887
This commit is contained in:
Vladimir Kondratyev 2020-10-05 12:38:11 +03:00
parent 67de2db262
commit 1975878673
19 changed files with 343 additions and 49 deletions

View File

@ -382,4 +382,5 @@ device evdev # input event device support
device uinput # install /dev/uinput cdev
# HID support
options HID_DEBUG # enable debug msgs
device hid # Generic HID support

View File

@ -382,4 +382,5 @@ device acpi
makeoptions MODULES_EXTRA="dtb/allwinner dtb/freescale dtb/imx8 dtb/nvidia dtb/mv dtb/rockchip dtb/rpi"
# HID support
options HID_DEBUG # enable debug msgs
device hid # Generic HID support

View File

@ -36,6 +36,7 @@ nooptions WITNESS
nooptions WITNESS_SKIPSPIN
nooptions DEADLKRES
nooptions USB_DEBUG
nooptions HID_DEBUG
nooptions COVERAGE
nooptions KCOV
nooptions MALLOC_DEBUG_MAXZONES

View File

@ -1013,3 +1013,6 @@ AMDSBWD_DEBUG opt_amdsbwd.h
# gcov support
GCOV opt_global.h
LINDEBUGFS
# options for HID support
HID_DEBUG opt_hid.h

View File

@ -32,45 +32,36 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifdef USB_GLOBAL_INCLUDE_FILE
#include USB_GLOBAL_INCLUDE_FILE
#else
#include <sys/stdint.h>
#include <sys/stddef.h>
#include "opt_hid.h"
#include <sys/param.h>
#include <sys/queue.h>
#include <sys/types.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/bus.h>
#include <sys/module.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/condvar.h>
#include <sys/sysctl.h>
#include <sys/sx.h>
#include <sys/unistd.h>
#include <sys/callout.h>
#include <sys/kdb.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/priv.h>
#include <sys/module.h>
#include <sys/sysctl.h>
#include <dev/usb/usb.h>
#include <dev/usb/usbdi.h>
#include <dev/usb/usbdi_util.h>
#include <dev/usb/usbhid.h>
#define HID_DEBUG_VAR hid_debug
#include <dev/hid/hid.h>
#include <dev/hid/hidquirk.h>
#define USB_DEBUG_VAR usb_debug
/*
* Define this unconditionally in case a kernel module is loaded that
* has been compiled with debugging options.
*/
int hid_debug = 0;
#include <dev/usb/usb_core.h>
#include <dev/usb/usb_debug.h>
#include <dev/usb/usb_process.h>
#include <dev/usb/usb_device.h>
#include <dev/usb/usb_request.h>
#endif /* USB_GLOBAL_INCLUDE_FILE */
SYSCTL_NODE(_hw, OID_AUTO, hid, CTLFLAG_RW, 0, "HID debugging");
SYSCTL_INT(_hw_hid, OID_AUTO, debug, CTLFLAG_RWTUN,
&hid_debug, 0, "Debug level");
static void hid_clear_local(struct hid_item *);
static uint8_t hid_get_byte(struct hid_data *s, const uint16_t wSize);
static hid_test_quirk_t hid_test_quirk_w;
hid_test_quirk_t *hid_test_quirk_p = &hid_test_quirk_w;
#define MAXUSAGE 64
#define MAXPUSH 4
#define MAXID 16
@ -180,7 +171,7 @@ hid_switch_rid(struct hid_data *s, struct hid_item *c, int32_t next_rID)
* hid_start_parse
*------------------------------------------------------------------------*/
struct hid_data *
hid_start_parse(const void *d, usb_size_t len, int kindset)
hid_start_parse(const void *d, hid_size_t len, int kindset)
{
struct hid_data *s;
@ -574,7 +565,47 @@ hid_get_item(struct hid_data *s, struct hid_item *h)
* hid_report_size
*------------------------------------------------------------------------*/
int
hid_report_size(const void *buf, usb_size_t len, enum hid_kind k, uint8_t *id)
hid_report_size(const void *buf, hid_size_t len, enum hid_kind k, uint8_t id)
{
struct hid_data *d;
struct hid_item h;
uint32_t temp;
uint32_t hpos;
uint32_t lpos;
int report_id = 0;
hpos = 0;
lpos = 0xFFFFFFFF;
for (d = hid_start_parse(buf, len, 1 << k); hid_get_item(d, &h);) {
if (h.kind == k && h.report_ID == id) {
/* compute minimum */
if (lpos > h.loc.pos)
lpos = h.loc.pos;
/* compute end position */
temp = h.loc.pos + (h.loc.size * h.loc.count);
/* compute maximum */
if (hpos < temp)
hpos = temp;
if (h.report_ID != 0)
report_id = 1;
}
}
hid_end_parse(d);
/* safety check - can happen in case of currupt descriptors */
if (lpos > hpos)
temp = 0;
else
temp = hpos - lpos;
/* return length in bytes rounded up */
return ((temp + 7) / 8 + report_id);
}
int
hid_report_size_max(const void *buf, hid_size_t len, enum hid_kind k,
uint8_t *id)
{
struct hid_data *d;
struct hid_item h;
@ -627,7 +658,7 @@ hid_report_size(const void *buf, usb_size_t len, enum hid_kind k, uint8_t *id)
* hid_locate
*------------------------------------------------------------------------*/
int
hid_locate(const void *desc, usb_size_t size, int32_t u, enum hid_kind k,
hid_locate(const void *desc, hid_size_t size, int32_t u, enum hid_kind k,
uint8_t index, struct hid_location *loc, uint32_t *flags, uint8_t *id)
{
struct hid_data *d;
@ -664,7 +695,7 @@ hid_locate(const void *desc, usb_size_t size, int32_t u, enum hid_kind k,
* hid_get_data
*------------------------------------------------------------------------*/
static uint32_t
hid_get_data_sub(const uint8_t *buf, usb_size_t len, struct hid_location *loc,
hid_get_data_sub(const uint8_t *buf, hid_size_t len, struct hid_location *loc,
int is_signed)
{
uint32_t hpos = loc->pos;
@ -708,13 +739,13 @@ hid_get_data_sub(const uint8_t *buf, usb_size_t len, struct hid_location *loc,
}
int32_t
hid_get_data(const uint8_t *buf, usb_size_t len, struct hid_location *loc)
hid_get_data(const uint8_t *buf, hid_size_t len, struct hid_location *loc)
{
return (hid_get_data_sub(buf, len, loc, 1));
}
uint32_t
hid_get_data_unsigned(const uint8_t *buf, usb_size_t len, struct hid_location *loc)
hid_get_udata(const uint8_t *buf, hid_size_t len, struct hid_location *loc)
{
return (hid_get_data_sub(buf, len, loc, 0));
}
@ -723,7 +754,7 @@ hid_get_data_unsigned(const uint8_t *buf, usb_size_t len, struct hid_location *l
* hid_put_data
*------------------------------------------------------------------------*/
void
hid_put_data_unsigned(uint8_t *buf, usb_size_t len,
hid_put_udata(uint8_t *buf, hid_size_t len,
struct hid_location *loc, unsigned int value)
{
uint32_t hpos = loc->pos;
@ -760,7 +791,7 @@ hid_put_data_unsigned(uint8_t *buf, usb_size_t len,
* hid_is_collection
*------------------------------------------------------------------------*/
int
hid_is_collection(const void *desc, usb_size_t size, int32_t usage)
hid_is_collection(const void *desc, hid_size_t size, int32_t usage)
{
struct hid_data *hd;
struct hid_item hi;
@ -929,4 +960,69 @@ hid_is_keyboard(const void *d_ptr, uint16_t d_len)
return (0);
}
/*------------------------------------------------------------------------*
* hid_test_quirk - test a device for a given quirk
*
* Return values:
* false: The HID device does not have the given quirk.
* true: The HID device has the given quirk.
*------------------------------------------------------------------------*/
bool
hid_test_quirk(const struct hid_device_info *dev_info, uint16_t quirk)
{
bool found;
uint8_t x;
if (quirk == HQ_NONE)
return (false);
/* search the automatic per device quirks first */
for (x = 0; x != HID_MAX_AUTO_QUIRK; x++) {
if (dev_info->autoQuirk[x] == quirk)
return (true);
}
/* search global quirk table, if any */
found = (hid_test_quirk_p) (dev_info, quirk);
return (found);
}
static bool
hid_test_quirk_w(const struct hid_device_info *dev_info, uint16_t quirk)
{
return (false); /* no match */
}
int
hid_add_dynamic_quirk(struct hid_device_info *dev_info, uint16_t quirk)
{
uint8_t x;
for (x = 0; x != HID_MAX_AUTO_QUIRK; x++) {
if (dev_info->autoQuirk[x] == 0 ||
dev_info->autoQuirk[x] == quirk) {
dev_info->autoQuirk[x] = quirk;
return (0); /* success */
}
}
return (ENOSPC);
}
void
hid_quirk_unload(void *arg)
{
/* reset function pointer */
hid_test_quirk_p = &hid_test_quirk_w;
#ifdef NOT_YET
hidquirk_ioctl_p = &hidquirk_ioctl_w;
#endif
/* wait for CPU to exit the loaded functions, if any */
/* XXX this is a tradeoff */
pause("WAIT", hz);
}
MODULE_VERSION(hid, 1);

View File

@ -67,6 +67,7 @@
#define HUG_GAME_PAD 0x0005
#define HUG_KEYBOARD 0x0006
#define HUG_KEYPAD 0x0007
#define HUG_MULTIAXIS_CNTROLLER 0x0008
#define HUG_X 0x0030
#define HUG_Y 0x0031
#define HUG_Z 0x0032
@ -102,6 +103,12 @@
#define HUG_SYSTEM_MENU_LEFT 0x008b
#define HUG_SYSTEM_MENU_UP 0x008c
#define HUG_SYSTEM_MENU_DOWN 0x008d
#define HUG_SYSTEM_POWER_UP 0x008e
#define HUG_SYSTEM_RESTART 0x008f
#define HUG_D_PAD_UP 0x0090
#define HUG_D_PAD_DOWN 0x0091
#define HUG_D_PAD_RIGHT 0x0092
#define HUG_D_PAD_LEFT 0x0093
#define HUG_APPLE_EJECT 0x00b8
/* Usages Digitizers */
@ -147,16 +154,21 @@
#define HUD_SURFACE_SWITCH 0x0057
#define HUD_BUTTONS_SWITCH 0x0058
#define HUD_BUTTON_TYPE 0x0059
#define HUD_SEC_BARREL_SWITCH 0x005a
#define HUD_LATENCY_MODE 0x0060
/* Usages, Consumer */
#define HUC_CONTROL 0x0001
#define HUC_HEADPHONE 0x0005
#define HUC_AC_PAN 0x0238
#define HID_USAGE2(p,u) (((p) << 16) | (u))
#define HID_USAGE2(p,u) (((p) << 16) | (u))
#define HID_GET_USAGE(u) ((u) & 0xffff)
#define HID_GET_USAGE_PAGE(u) (((u) >> 16) & 0xffff)
#define UHID_INPUT_REPORT 0x01
#define UHID_OUTPUT_REPORT 0x02
#define UHID_FEATURE_REPORT 0x03
#define HID_INPUT_REPORT 0x01
#define HID_OUTPUT_REPORT 0x02
#define HID_FEATURE_REPORT 0x03
/* Bits in the input/output/feature items */
#define HIO_CONST 0x001
@ -178,6 +190,36 @@
#if defined(_KERNEL) || defined(_STANDALONE)
#define HID_ITEM_MAXUSAGE 4
#define HID_MAX_AUTO_QUIRK 8 /* maximum number of dynamic quirks */
#define HID_PNP_ID_SIZE 20 /* includes null terminator */
/* Declare global HID debug variable. */
extern int hid_debug;
/* Check if HID debugging is enabled. */
#ifdef HID_DEBUG_VAR
#ifdef HID_DEBUG
#define DPRINTFN(n,fmt,...) do { \
if ((HID_DEBUG_VAR) >= (n)) { \
printf("%s: " fmt, \
__FUNCTION__ ,##__VA_ARGS__); \
} \
} while (0)
#define DPRINTF(...) DPRINTFN(1, __VA_ARGS__)
#else
#define DPRINTF(...) do { } while (0)
#define DPRINTFN(...) do { } while (0)
#endif
#endif
/* Declare parent SYSCTL HID node. */
#ifdef SYSCTL_DECL
SYSCTL_DECL(_hw_hid);
#endif
typedef uint32_t hid_size_t;
#define HID_IN_POLLING_MODE() (SCHEDULER_STOPPED() || kdb_active)
enum hid_kind {
hid_input, hid_output, hid_feature, hid_collection, hid_endcollection
@ -223,25 +265,71 @@ struct hid_item {
struct hid_location loc;
};
struct hid_absinfo {
int32_t min;
int32_t max;
int32_t res;
};
struct hid_device_info {
char name[80];
char serial[80];
char idPnP[HID_PNP_ID_SIZE];
uint16_t idBus;
uint16_t idVendor;
uint16_t idProduct;
uint16_t idVersion;
hid_size_t rdescsize; /* Report descriptor size */
uint8_t autoQuirk[HID_MAX_AUTO_QUIRK];
};
struct hid_rdesc_info {
void *data;
hid_size_t len;
hid_size_t isize;
hid_size_t osize;
hid_size_t fsize;
uint8_t iid;
uint8_t oid;
uint8_t fid;
/* Max sizes for HID requests supported by transport backend */
hid_size_t rdsize;
hid_size_t wrsize;
hid_size_t grsize;
hid_size_t srsize;
};
typedef void hid_intr_t(void *context, void *data, hid_size_t len);
typedef bool hid_test_quirk_t(const struct hid_device_info *dev_info,
uint16_t quirk);
extern hid_test_quirk_t *hid_test_quirk_p;
/* prototypes from "usb_hid.c" */
struct hid_data *hid_start_parse(const void *d, usb_size_t len, int kindset);
struct hid_data *hid_start_parse(const void *d, hid_size_t len, int kindset);
void hid_end_parse(struct hid_data *s);
int hid_get_item(struct hid_data *s, struct hid_item *h);
int hid_report_size(const void *buf, usb_size_t len, enum hid_kind k,
int hid_report_size(const void *buf, hid_size_t len, enum hid_kind k,
uint8_t id);
int hid_report_size_max(const void *buf, hid_size_t len, enum hid_kind k,
uint8_t *id);
int hid_locate(const void *desc, usb_size_t size, int32_t usage,
int hid_locate(const void *desc, hid_size_t size, int32_t usage,
enum hid_kind kind, uint8_t index, struct hid_location *loc,
uint32_t *flags, uint8_t *id);
int32_t hid_get_data(const uint8_t *buf, usb_size_t len,
int32_t hid_get_data(const uint8_t *buf, hid_size_t len,
struct hid_location *loc);
uint32_t hid_get_data_unsigned(const uint8_t *buf, usb_size_t len,
uint32_t hid_get_udata(const uint8_t *buf, hid_size_t len,
struct hid_location *loc);
void hid_put_data_unsigned(uint8_t *buf, usb_size_t len,
void hid_put_udata(uint8_t *buf, hid_size_t len,
struct hid_location *loc, unsigned int value);
int hid_is_collection(const void *desc, usb_size_t size, int32_t usage);
int hid_is_collection(const void *desc, hid_size_t size, int32_t usage);
int32_t hid_item_resolution(struct hid_item *hi);
int hid_is_mouse(const void *d_ptr, uint16_t d_len);
int hid_is_keyboard(const void *d_ptr, uint16_t d_len);
bool hid_test_quirk(const struct hid_device_info *dev_info, uint16_t quirk);
int hid_add_dynamic_quirk(struct hid_device_info *dev_info,
uint16_t quirk);
void hid_quirk_unload(void *arg);
#endif /* _KERNEL || _STANDALONE */
#endif /* _HID_HID_H_ */

74
sys/dev/hid/hidquirk.h Normal file
View File

@ -0,0 +1,74 @@
/* $FreeBSD$ */
/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright (c) 2008 Hans Petter Selasky. All rights reserved.
* 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.
*/
/*
* Screening of all content of this file except HID_QUIRK_LIST is a kind of
* hack that allows multiple HID_QUIRK_LIST inclusion with different HQ()
* wrappers. That save us splitting hidquirk.h on two header files.
*/
#ifndef HQ
#ifndef _HID_QUIRK_H_
#define _HID_QUIRK_H_
#endif
/*
* Keep in sync with share/man/man4/hidquirk.4
*/
#define HID_QUIRK_LIST(...) \
HQ(NONE), /* not a valid quirk */ \
\
HQ(MATCH_VENDOR_ONLY), /* match quirk on vendor only */ \
\
/* Autoquirks */ \
HQ(HAS_KBD_BOOTPROTO), /* device supports keyboard boot protocol */ \
HQ(HAS_MS_BOOTPROTO), /* device supports mouse boot protocol */ \
HQ(IS_XBOX360GP), /* device is XBox 360 GamePad */ \
HQ(NOWRITE), /* device does not support writes */ \
HQ(IICHID_SAMPLING), /* IIC backend runs in sampling mode */ \
\
/* Various quirks */ \
HQ(HID_IGNORE), /* device should be ignored by hid class */ \
HQ(KBD_BOOTPROTO), /* device should set the boot protocol */ \
HQ(MS_BOOTPROTO), /* device should set the boot protocol */ \
HQ(MS_BAD_CLASS), /* doesn't identify properly */ \
HQ(MS_LEADING_BYTE), /* mouse sends an unknown leading byte */ \
HQ(MS_REVZ), /* mouse has Z-axis reversed */ \
HQ(SPUR_BUT_UP), /* spurious mouse button up events */ \
HQ(MT_TIMESTAMP) /* Multitouch device exports HW timestamps */
#ifndef HQ
#define HQ(x) HQ_##x
enum {
HID_QUIRK_LIST(),
HID_QUIRK_MAX
};
#undef HQ
#endif /* _HID_QUIRK_H_ */
#endif /* HQ */

View File

@ -63,9 +63,29 @@ struct usb_hid_descriptor {
#define USB_HID_DESCRIPTOR_SIZE(n) (9+((n)*3))
#define UHID_INPUT_REPORT HID_INPUT_REPORT
#define UHID_OUTPUT_REPORT HID_OUTPUT_REPORT
#define UHID_FEATURE_REPORT HID_FEATURE_REPORT
#if defined(_KERNEL) || defined(_STANDALONE)
struct usb_config_descriptor;
/* FreeBSD <= 12 compat shims */
#define hid_report_size(buf, len, kind, id) \
hid_report_size_max(buf, len, kind, id)
static __inline uint32_t
hid_get_data_unsigned(const uint8_t *buf, hid_size_t len,
struct hid_location *loc)
{
return (hid_get_udata(buf, len, loc));
}
static __inline void
hid_put_data_unsigned(uint8_t *buf, hid_size_t len, struct hid_location *loc,
unsigned int value)
{
return (hid_put_udata(buf, len, loc, value));
}
struct usb_hid_descriptor *hid_get_descriptor_from_usb(
struct usb_config_descriptor *cd,
struct usb_interface_descriptor *id);

View File

@ -350,4 +350,5 @@ device evdev # input event device support
device uinput # install /dev/uinput cdev
# HID support
options HID_DEBUG # enable debug msgs
device hid # Generic HID support

View File

@ -214,4 +214,5 @@ device cryptocteon # Octeon coprocessor 2 crypto offload
#device hwpmc
# HID support
options HID_DEBUG # enable debug msgs
device hid # Generic HID support

View File

@ -110,6 +110,7 @@ device umass # Disks/Mass storage - Requires scbus and da
device ums # Mouse
# HID support
options HID_DEBUG # enable debug msgs
device hid # Generic HID support
# FDT support

View File

@ -239,4 +239,5 @@ device cryptocteon # Octeon coprocessor 2 crypto offload
#device hwpmc
# HID support
options HID_DEBUG # enable debug msgs
device hid # Generic HID support

View File

@ -4,6 +4,6 @@
KMOD= hid
SRCS= hid.c
SRCS+= opt_usb.h
SRCS+= opt_hid.h
.include <bsd.kmod.mk>

View File

@ -241,4 +241,5 @@ device virtio_scsi # VirtIO SCSI device
device virtio_balloon # VirtIO Memory Balloon device
# HID support
options HID_DEBUG # enable debug msgs
device hid # Generic HID support

View File

@ -271,4 +271,5 @@ device virtio_scsi # VirtIO SCSI device
device virtio_balloon # VirtIO Memory Balloon device
# HID support
options HID_DEBUG # enable debug msgs
device hid # Generic HID support

View File

@ -252,4 +252,5 @@ device virtio_scsi # VirtIO SCSI device
device virtio_balloon # VirtIO Memory Balloon device
# HID support
options HID_DEBUG # enable debug msgs
device hid # Generic HID support

View File

@ -119,4 +119,5 @@ device vt
device fbd
# HID support
options HID_DEBUG # enable debug msgs
device hid # Generic HID support

View File

@ -120,4 +120,5 @@ device vt
device fbd
# HID support
options HID_DEBUG # enable debug msgs
device hid # Generic HID support

View File

@ -122,4 +122,5 @@ device ukbd
device ums
# HID support
options HID_DEBUG # enable debug msgs
device hid # Generic HID support