diff --git a/sys/amd64/conf/GENERIC b/sys/amd64/conf/GENERIC --- a/sys/amd64/conf/GENERIC +++ b/sys/amd64/conf/GENERIC @@ -382,4 +382,5 @@ device uinput # install /dev/uinput cdev # HID support +options HID_DEBUG # enable debug msgs device hid # Generic HID support diff --git a/sys/arm64/conf/GENERIC b/sys/arm64/conf/GENERIC --- a/sys/arm64/conf/GENERIC +++ b/sys/arm64/conf/GENERIC @@ -382,4 +382,5 @@ 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 diff --git a/sys/arm64/conf/GENERIC-NODEBUG b/sys/arm64/conf/GENERIC-NODEBUG --- a/sys/arm64/conf/GENERIC-NODEBUG +++ b/sys/arm64/conf/GENERIC-NODEBUG @@ -36,6 +36,7 @@ nooptions WITNESS_SKIPSPIN nooptions DEADLKRES nooptions USB_DEBUG +nooptions HID_DEBUG nooptions COVERAGE nooptions KCOV nooptions MALLOC_DEBUG_MAXZONES diff --git a/sys/conf/options b/sys/conf/options --- a/sys/conf/options +++ b/sys/conf/options @@ -1013,3 +1013,6 @@ # gcov support GCOV opt_global.h LINDEBUGFS + +# options for HID support +HID_DEBUG opt_hid.h diff --git a/sys/dev/hid/hid.h b/sys/dev/hid/hid.h --- a/sys/dev/hid/hid.h +++ b/sys/dev/hid/hid.h @@ -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_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_ */ diff --git a/sys/dev/hid/hid.c b/sys/dev/hid/hid.c --- a/sys/dev/hid/hid.c +++ b/sys/dev/hid/hid.c @@ -32,45 +32,36 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#ifdef USB_GLOBAL_INCLUDE_FILE -#include USB_GLOBAL_INCLUDE_FILE -#else -#include -#include +#include "opt_hid.h" + #include -#include -#include -#include -#include #include +#include +#include +#include #include -#include -#include -#include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#define HID_DEBUG_VAR hid_debug +#include +#include -#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 -#include -#include -#include -#include -#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_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_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_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_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 @@ } 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_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_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 @@ 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); diff --git a/sys/dev/hid/hidquirk.h b/sys/dev/hid/hidquirk.h new file mode 100644 --- /dev/null +++ b/sys/dev/hid/hidquirk.h @@ -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 + * + * 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 */ diff --git a/sys/dev/usb/usbhid.h b/sys/dev/usb/usbhid.h --- a/sys/dev/usb/usbhid.h +++ b/sys/dev/usb/usbhid.h @@ -63,9 +63,29 @@ #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); diff --git a/sys/i386/conf/GENERIC b/sys/i386/conf/GENERIC --- a/sys/i386/conf/GENERIC +++ b/sys/i386/conf/GENERIC @@ -350,4 +350,5 @@ device uinput # install /dev/uinput cdev # HID support +options HID_DEBUG # enable debug msgs device hid # Generic HID support diff --git a/sys/mips/conf/ERL b/sys/mips/conf/ERL --- a/sys/mips/conf/ERL +++ b/sys/mips/conf/ERL @@ -214,4 +214,5 @@ #device hwpmc # HID support +options HID_DEBUG # enable debug msgs device hid # Generic HID support diff --git a/sys/mips/conf/JZ4780 b/sys/mips/conf/JZ4780 --- a/sys/mips/conf/JZ4780 +++ b/sys/mips/conf/JZ4780 @@ -110,6 +110,7 @@ device ums # Mouse # HID support +options HID_DEBUG # enable debug msgs device hid # Generic HID support # FDT support diff --git a/sys/mips/conf/OCTEON1 b/sys/mips/conf/OCTEON1 --- a/sys/mips/conf/OCTEON1 +++ b/sys/mips/conf/OCTEON1 @@ -239,4 +239,5 @@ #device hwpmc # HID support +options HID_DEBUG # enable debug msgs device hid # Generic HID support diff --git a/sys/modules/hid/hid/Makefile b/sys/modules/hid/hid/Makefile --- a/sys/modules/hid/hid/Makefile +++ b/sys/modules/hid/hid/Makefile @@ -4,6 +4,6 @@ KMOD= hid SRCS= hid.c -SRCS+= opt_usb.h +SRCS+= opt_hid.h .include diff --git a/sys/powerpc/conf/GENERIC b/sys/powerpc/conf/GENERIC --- a/sys/powerpc/conf/GENERIC +++ b/sys/powerpc/conf/GENERIC @@ -241,4 +241,5 @@ device virtio_balloon # VirtIO Memory Balloon device # HID support +options HID_DEBUG # enable debug msgs device hid # Generic HID support diff --git a/sys/powerpc/conf/GENERIC64 b/sys/powerpc/conf/GENERIC64 --- a/sys/powerpc/conf/GENERIC64 +++ b/sys/powerpc/conf/GENERIC64 @@ -271,4 +271,5 @@ device virtio_balloon # VirtIO Memory Balloon device # HID support +options HID_DEBUG # enable debug msgs device hid # Generic HID support diff --git a/sys/powerpc/conf/GENERIC64LE b/sys/powerpc/conf/GENERIC64LE --- a/sys/powerpc/conf/GENERIC64LE +++ b/sys/powerpc/conf/GENERIC64LE @@ -252,4 +252,5 @@ device virtio_balloon # VirtIO Memory Balloon device # HID support +options HID_DEBUG # enable debug msgs device hid # Generic HID support diff --git a/sys/powerpc/conf/MPC85XX b/sys/powerpc/conf/MPC85XX --- a/sys/powerpc/conf/MPC85XX +++ b/sys/powerpc/conf/MPC85XX @@ -119,4 +119,5 @@ device fbd # HID support +options HID_DEBUG # enable debug msgs device hid # Generic HID support diff --git a/sys/powerpc/conf/MPC85XXSPE b/sys/powerpc/conf/MPC85XXSPE --- a/sys/powerpc/conf/MPC85XXSPE +++ b/sys/powerpc/conf/MPC85XXSPE @@ -120,4 +120,5 @@ device fbd # HID support +options HID_DEBUG # enable debug msgs device hid # Generic HID support diff --git a/sys/powerpc/conf/QORIQ64 b/sys/powerpc/conf/QORIQ64 --- a/sys/powerpc/conf/QORIQ64 +++ b/sys/powerpc/conf/QORIQ64 @@ -122,4 +122,5 @@ device ums # HID support +options HID_DEBUG # enable debug msgs device hid # Generic HID support