2020-10-15 00:02:15 +02:00
|
|
|
/*-
|
|
|
|
* 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)
|
2021-09-02 21:33:24 +02:00
|
|
|
#define HIDMAP_CB_GET_DEV(...) \
|
|
|
|
(hm == NULL ? NULL : hm->dev)
|
2020-10-15 00:02:15 +02:00
|
|
|
#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;
|
2021-09-02 21:31:51 +02:00
|
|
|
bool forbidden:1; /* Forbidden by driver */
|
|
|
|
u_int reserved:9;
|
2020-10-15 00:02:15 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
#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_ */
|