xref: /freebsd/sys/dev/hid/hidmap.c (revision 685dc743)
1afd590d9SVladimir Kondratyev /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
3afd590d9SVladimir Kondratyev  *
4afd590d9SVladimir Kondratyev  * Copyright (c) 2020 Vladimir Kondratyev <wulf@FreeBSD.org>
5afd590d9SVladimir Kondratyev  *
6afd590d9SVladimir Kondratyev  * Redistribution and use in source and binary forms, with or without
7afd590d9SVladimir Kondratyev  * modification, are permitted provided that the following conditions
8afd590d9SVladimir Kondratyev  * are met:
9afd590d9SVladimir Kondratyev  * 1. Redistributions of source code must retain the above copyright
10afd590d9SVladimir Kondratyev  *    notice, this list of conditions and the following disclaimer.
11afd590d9SVladimir Kondratyev  * 2. Redistributions in binary form must reproduce the above copyright
12afd590d9SVladimir Kondratyev  *    notice, this list of conditions and the following disclaimer in the
13afd590d9SVladimir Kondratyev  *    documentation and/or other materials provided with the distribution.
14afd590d9SVladimir Kondratyev  *
15afd590d9SVladimir Kondratyev  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16afd590d9SVladimir Kondratyev  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17afd590d9SVladimir Kondratyev  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18afd590d9SVladimir Kondratyev  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19afd590d9SVladimir Kondratyev  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20afd590d9SVladimir Kondratyev  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21afd590d9SVladimir Kondratyev  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22afd590d9SVladimir Kondratyev  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23afd590d9SVladimir Kondratyev  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24afd590d9SVladimir Kondratyev  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25afd590d9SVladimir Kondratyev  * SUCH DAMAGE.
26afd590d9SVladimir Kondratyev  */
27afd590d9SVladimir Kondratyev 
28afd590d9SVladimir Kondratyev #include <sys/cdefs.h>
29afd590d9SVladimir Kondratyev /*
30afd590d9SVladimir Kondratyev  * Abstract 1 to 1 HID input usage to evdev event mapper driver.
31afd590d9SVladimir Kondratyev  */
32afd590d9SVladimir Kondratyev 
33afd590d9SVladimir Kondratyev #include "opt_hid.h"
34afd590d9SVladimir Kondratyev 
35afd590d9SVladimir Kondratyev #include <sys/param.h>
36afd590d9SVladimir Kondratyev #include <sys/bus.h>
37afd590d9SVladimir Kondratyev #include <sys/kernel.h>
38afd590d9SVladimir Kondratyev #include <sys/lock.h>
39afd590d9SVladimir Kondratyev #include <sys/malloc.h>
40afd590d9SVladimir Kondratyev #include <sys/module.h>
41afd590d9SVladimir Kondratyev #include <sys/sysctl.h>
42afd590d9SVladimir Kondratyev #include <sys/systm.h>
43afd590d9SVladimir Kondratyev 
44afd590d9SVladimir Kondratyev #include <dev/evdev/input.h>
45afd590d9SVladimir Kondratyev #include <dev/evdev/evdev.h>
46afd590d9SVladimir Kondratyev 
47afd590d9SVladimir Kondratyev #include <dev/hid/hid.h>
48afd590d9SVladimir Kondratyev #include <dev/hid/hidbus.h>
49afd590d9SVladimir Kondratyev #include <dev/hid/hidmap.h>
50afd590d9SVladimir Kondratyev 
51afd590d9SVladimir Kondratyev #ifdef HID_DEBUG
52afd590d9SVladimir Kondratyev #define DPRINTFN(hm, n, fmt, ...) do {					\
53afd590d9SVladimir Kondratyev 	if ((hm)->debug_var != NULL && *(hm)->debug_var >= (n)) {	\
54afd590d9SVladimir Kondratyev 		device_printf((hm)->dev, "%s: " fmt,			\
55afd590d9SVladimir Kondratyev 		    __FUNCTION__ ,##__VA_ARGS__);			\
56afd590d9SVladimir Kondratyev 	}								\
57afd590d9SVladimir Kondratyev } while (0)
58afd590d9SVladimir Kondratyev #define DPRINTF(hm, ...)	DPRINTFN(hm, 1, __VA_ARGS__)
59afd590d9SVladimir Kondratyev #else
60afd590d9SVladimir Kondratyev #define DPRINTF(...) do { } while (0)
61afd590d9SVladimir Kondratyev #define DPRINTFN(...) do { } while (0)
62afd590d9SVladimir Kondratyev #endif
63afd590d9SVladimir Kondratyev 
64afd590d9SVladimir Kondratyev static evdev_open_t hidmap_ev_open;
65afd590d9SVladimir Kondratyev static evdev_close_t hidmap_ev_close;
66afd590d9SVladimir Kondratyev 
67afd590d9SVladimir Kondratyev #define	HIDMAP_WANT_MERGE_KEYS(hm)	((hm)->key_rel != NULL)
68afd590d9SVladimir Kondratyev 
69afd590d9SVladimir Kondratyev #define HIDMAP_FOREACH_ITEM(hm, mi, uoff)				\
70afd590d9SVladimir Kondratyev 	for (u_int _map = 0, _item = 0, _uoff_priv = -1;		\
71afd590d9SVladimir Kondratyev 	    ((mi) = hidmap_get_next_map_item(				\
72afd590d9SVladimir Kondratyev 		(hm), &_map, &_item, &_uoff_priv, &(uoff))) != NULL;)
73afd590d9SVladimir Kondratyev 
74afd590d9SVladimir Kondratyev static inline bool
hidmap_get_next_map_index(const struct hidmap_item * map,int nmap_items,uint32_t * index,uint16_t * usage_offset)75afd590d9SVladimir Kondratyev hidmap_get_next_map_index(const struct hidmap_item *map, int nmap_items,
76afd590d9SVladimir Kondratyev     uint32_t *index, uint16_t *usage_offset)
77afd590d9SVladimir Kondratyev {
78afd590d9SVladimir Kondratyev 
79afd590d9SVladimir Kondratyev 	++*usage_offset;
80afd590d9SVladimir Kondratyev 	if ((*index != 0 || *usage_offset != 0) &&
81afd590d9SVladimir Kondratyev 	    *usage_offset >= map[*index].nusages) {
82afd590d9SVladimir Kondratyev 		++*index;
83afd590d9SVladimir Kondratyev 		*usage_offset = 0;
84afd590d9SVladimir Kondratyev 	}
85afd590d9SVladimir Kondratyev 
86afd590d9SVladimir Kondratyev 	return (*index < nmap_items);
87afd590d9SVladimir Kondratyev }
88afd590d9SVladimir Kondratyev 
89afd590d9SVladimir Kondratyev 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)90afd590d9SVladimir Kondratyev hidmap_get_next_map_item(struct hidmap *hm, u_int *map, u_int *item,
91afd590d9SVladimir Kondratyev     u_int *uoff_priv, uint16_t *uoff)
92afd590d9SVladimir Kondratyev {
93afd590d9SVladimir Kondratyev 
94afd590d9SVladimir Kondratyev 	*uoff = *uoff_priv;
95afd590d9SVladimir Kondratyev 	while (!hidmap_get_next_map_index(
96afd590d9SVladimir Kondratyev 	   hm->map[*map], hm->nmap_items[*map], item, uoff)) {
97afd590d9SVladimir Kondratyev 		++*map;
98afd590d9SVladimir Kondratyev 		*item = 0;
99afd590d9SVladimir Kondratyev 		*uoff = -1;
100afd590d9SVladimir Kondratyev 		if (*map >= hm->nmaps)
101afd590d9SVladimir Kondratyev 			return (NULL);
102afd590d9SVladimir Kondratyev 	}
103afd590d9SVladimir Kondratyev 	*uoff_priv = *uoff;
104afd590d9SVladimir Kondratyev 
105afd590d9SVladimir Kondratyev 	return (hm->map[*map] + *item);
106afd590d9SVladimir Kondratyev }
107afd590d9SVladimir Kondratyev 
108afd590d9SVladimir Kondratyev void
_hidmap_set_debug_var(struct hidmap * hm,int * debug_var)109afd590d9SVladimir Kondratyev _hidmap_set_debug_var(struct hidmap *hm, int *debug_var)
110afd590d9SVladimir Kondratyev {
111afd590d9SVladimir Kondratyev #ifdef HID_DEBUG
112afd590d9SVladimir Kondratyev 	hm->debug_var = debug_var;
113afd590d9SVladimir Kondratyev #endif
114afd590d9SVladimir Kondratyev }
115afd590d9SVladimir Kondratyev 
116afd590d9SVladimir Kondratyev static int
hidmap_ev_close(struct evdev_dev * evdev)117afd590d9SVladimir Kondratyev hidmap_ev_close(struct evdev_dev *evdev)
118afd590d9SVladimir Kondratyev {
1194151ac9fSVladimir Kondratyev 	return (hid_intr_stop(evdev_get_softc(evdev)));
120afd590d9SVladimir Kondratyev }
121afd590d9SVladimir Kondratyev 
122afd590d9SVladimir Kondratyev static int
hidmap_ev_open(struct evdev_dev * evdev)123afd590d9SVladimir Kondratyev hidmap_ev_open(struct evdev_dev *evdev)
124afd590d9SVladimir Kondratyev {
1254151ac9fSVladimir Kondratyev 	return (hid_intr_start(evdev_get_softc(evdev)));
126afd590d9SVladimir Kondratyev }
127afd590d9SVladimir Kondratyev 
128afd590d9SVladimir Kondratyev void
hidmap_support_key(struct hidmap * hm,uint16_t key)129afd590d9SVladimir Kondratyev hidmap_support_key(struct hidmap *hm, uint16_t key)
130afd590d9SVladimir Kondratyev {
131afd590d9SVladimir Kondratyev 	if (hm->key_press == NULL) {
132afd590d9SVladimir Kondratyev 		hm->key_press = malloc(howmany(KEY_CNT, 8), M_DEVBUF,
133afd590d9SVladimir Kondratyev 		    M_ZERO | M_WAITOK);
134afd590d9SVladimir Kondratyev 		evdev_support_event(hm->evdev, EV_KEY);
135afd590d9SVladimir Kondratyev 		hm->key_min = key;
136afd590d9SVladimir Kondratyev 		hm->key_max = key;
137afd590d9SVladimir Kondratyev 	}
138afd590d9SVladimir Kondratyev 	hm->key_min = MIN(hm->key_min, key);
139afd590d9SVladimir Kondratyev 	hm->key_max = MAX(hm->key_max, key);
140afd590d9SVladimir Kondratyev 	if (isset(hm->key_press, key)) {
141afd590d9SVladimir Kondratyev 		if (hm->key_rel == NULL)
142afd590d9SVladimir Kondratyev 			hm->key_rel = malloc(howmany(KEY_CNT, 8), M_DEVBUF,
143afd590d9SVladimir Kondratyev 			    M_ZERO | M_WAITOK);
144afd590d9SVladimir Kondratyev 	} else {
145afd590d9SVladimir Kondratyev 		setbit(hm->key_press, key);
146afd590d9SVladimir Kondratyev 		evdev_support_key(hm->evdev, key);
147afd590d9SVladimir Kondratyev 	}
148afd590d9SVladimir Kondratyev }
149afd590d9SVladimir Kondratyev 
150afd590d9SVladimir Kondratyev void
hidmap_push_key(struct hidmap * hm,uint16_t key,int32_t value)151afd590d9SVladimir Kondratyev hidmap_push_key(struct hidmap *hm, uint16_t key, int32_t value)
152afd590d9SVladimir Kondratyev {
153afd590d9SVladimir Kondratyev 	if (HIDMAP_WANT_MERGE_KEYS(hm))
154afd590d9SVladimir Kondratyev 		setbit(value != 0 ? hm->key_press : hm->key_rel, key);
155afd590d9SVladimir Kondratyev 	else
156afd590d9SVladimir Kondratyev 		evdev_push_key(hm->evdev, key, value);
157afd590d9SVladimir Kondratyev }
158afd590d9SVladimir Kondratyev 
159afd590d9SVladimir Kondratyev static void
hidmap_sync_keys(struct hidmap * hm)160afd590d9SVladimir Kondratyev hidmap_sync_keys(struct hidmap *hm)
161afd590d9SVladimir Kondratyev {
162afd590d9SVladimir Kondratyev 	int i, j;
163afd590d9SVladimir Kondratyev 	bool press, rel;
164afd590d9SVladimir Kondratyev 
165afd590d9SVladimir Kondratyev 	for (j = hm->key_min / 8; j <= hm->key_max / 8; j++) {
166afd590d9SVladimir Kondratyev 		if (hm->key_press[j] != hm->key_rel[j]) {
167afd590d9SVladimir Kondratyev 			for (i = j * 8; i < j * 8 + 8; i++) {
168afd590d9SVladimir Kondratyev 				press = isset(hm->key_press, i);
169afd590d9SVladimir Kondratyev 				rel = isset(hm->key_rel, i);
170afd590d9SVladimir Kondratyev 				if (press != rel)
171afd590d9SVladimir Kondratyev 					evdev_push_key(hm->evdev, i, press);
172afd590d9SVladimir Kondratyev 			}
173afd590d9SVladimir Kondratyev 		}
174afd590d9SVladimir Kondratyev 	}
175afd590d9SVladimir Kondratyev 	bzero(hm->key_press, howmany(KEY_CNT, 8));
176afd590d9SVladimir Kondratyev 	bzero(hm->key_rel, howmany(KEY_CNT, 8));
177afd590d9SVladimir Kondratyev }
178afd590d9SVladimir Kondratyev 
179afd590d9SVladimir Kondratyev void
hidmap_intr(void * context,void * buf,hid_size_t len)180afd590d9SVladimir Kondratyev hidmap_intr(void *context, void *buf, hid_size_t len)
181afd590d9SVladimir Kondratyev {
182afd590d9SVladimir Kondratyev 	struct hidmap *hm = context;
183afd590d9SVladimir Kondratyev 	struct hidmap_hid_item *hi;
184afd590d9SVladimir Kondratyev 	const struct hidmap_item *mi;
185afd590d9SVladimir Kondratyev 	int32_t usage;
186afd590d9SVladimir Kondratyev 	int32_t data;
187afd590d9SVladimir Kondratyev 	uint16_t key, uoff;
188afd590d9SVladimir Kondratyev 	uint8_t id = 0;
189afd590d9SVladimir Kondratyev 	bool found, do_sync = false;
190afd590d9SVladimir Kondratyev 
191afd590d9SVladimir Kondratyev 	DPRINTFN(hm, 6, "hm=%p len=%d\n", hm, len);
192afd590d9SVladimir Kondratyev 	DPRINTFN(hm, 6, "data = %*D\n", len, buf, " ");
193afd590d9SVladimir Kondratyev 
194afd590d9SVladimir Kondratyev 	/* Strip leading "report ID" byte */
195afd590d9SVladimir Kondratyev 	if (hm->hid_items[0].id) {
196afd590d9SVladimir Kondratyev 		id = *(uint8_t *)buf;
197afd590d9SVladimir Kondratyev 		len--;
198afd590d9SVladimir Kondratyev 		buf = (uint8_t *)buf + 1;
199afd590d9SVladimir Kondratyev 	}
200afd590d9SVladimir Kondratyev 
201afd590d9SVladimir Kondratyev 	hm->intr_buf = buf;
202afd590d9SVladimir Kondratyev 	hm->intr_len = len;
203afd590d9SVladimir Kondratyev 
204afd590d9SVladimir Kondratyev 	for (hi = hm->hid_items; hi < hm->hid_items + hm->nhid_items; hi++) {
205afd590d9SVladimir Kondratyev 		/* At first run callbacks that not tied to HID items */
206afd590d9SVladimir Kondratyev 		if (hi->type == HIDMAP_TYPE_FINALCB) {
207afd590d9SVladimir Kondratyev 			DPRINTFN(hm, 6, "type=%d item=%*D\n", hi->type,
208afd590d9SVladimir Kondratyev 			    (int)sizeof(hi->cb), &hi->cb, " ");
209afd590d9SVladimir Kondratyev 			if (hi->cb(hm, hi, (union hidmap_cb_ctx){.rid = id})
210afd590d9SVladimir Kondratyev 			    == 0)
211afd590d9SVladimir Kondratyev 				do_sync = true;
212afd590d9SVladimir Kondratyev 			continue;
213afd590d9SVladimir Kondratyev 		}
214afd590d9SVladimir Kondratyev 
215afd590d9SVladimir Kondratyev 		/* Ignore irrelevant reports */
216afd590d9SVladimir Kondratyev 		if (id != hi->id)
217afd590d9SVladimir Kondratyev 			continue;
218afd590d9SVladimir Kondratyev 
219afd590d9SVladimir Kondratyev 		/*
220afd590d9SVladimir Kondratyev 		 * 5.8. If Logical Minimum and Logical Maximum are both
221afd590d9SVladimir Kondratyev 		 * positive values then the contents of a field can be assumed
222afd590d9SVladimir Kondratyev 		 * to be an unsigned value. Otherwise, all integer values are
223afd590d9SVladimir Kondratyev 		 * signed values represented in 2’s complement format.
224afd590d9SVladimir Kondratyev 		 */
225afd590d9SVladimir Kondratyev 		data = hi->lmin < 0 || hi->lmax < 0
226afd590d9SVladimir Kondratyev 		    ? hid_get_data(buf, len, &hi->loc)
227afd590d9SVladimir Kondratyev 		    : hid_get_udata(buf, len, &hi->loc);
228afd590d9SVladimir Kondratyev 
229afd590d9SVladimir Kondratyev 		DPRINTFN(hm, 6, "type=%d data=%d item=%*D\n", hi->type, data,
230afd590d9SVladimir Kondratyev 		    (int)sizeof(hi->cb), &hi->cb, " ");
231afd590d9SVladimir Kondratyev 
232afd590d9SVladimir Kondratyev 		if (hi->invert_value && hi->type < HIDMAP_TYPE_ARR_LIST)
233afd590d9SVladimir Kondratyev 			data = hi->evtype == EV_REL
234afd590d9SVladimir Kondratyev 			    ? -data
235afd590d9SVladimir Kondratyev 			    : hi->lmin + hi->lmax - data;
236afd590d9SVladimir Kondratyev 
237afd590d9SVladimir Kondratyev 		switch (hi->type) {
238afd590d9SVladimir Kondratyev 		case HIDMAP_TYPE_CALLBACK:
239afd590d9SVladimir Kondratyev 			if (hi->cb(hm, hi, (union hidmap_cb_ctx){.data = data})
240afd590d9SVladimir Kondratyev 			    != 0)
241afd590d9SVladimir Kondratyev 				continue;
242afd590d9SVladimir Kondratyev 			break;
243afd590d9SVladimir Kondratyev 
244afd590d9SVladimir Kondratyev 		case HIDMAP_TYPE_VAR_NULLST:
245afd590d9SVladimir Kondratyev 			/*
246afd590d9SVladimir Kondratyev 			 * 5.10. If the host or the device receives an
247afd590d9SVladimir Kondratyev 			 * out-of-range value then the current value for the
248afd590d9SVladimir Kondratyev 			 * respective control will not be modified.
249afd590d9SVladimir Kondratyev 			 */
250afd590d9SVladimir Kondratyev 			if (data < hi->lmin || data > hi->lmax)
251afd590d9SVladimir Kondratyev 				continue;
252afd590d9SVladimir Kondratyev 			/* FALLTHROUGH */
253afd590d9SVladimir Kondratyev 		case HIDMAP_TYPE_VARIABLE:
254afd590d9SVladimir Kondratyev 			/*
255afd590d9SVladimir Kondratyev 			 * Ignore reports for absolute data if the data did not
256afd590d9SVladimir Kondratyev 			 * change and for relative data if data is 0.
257afd590d9SVladimir Kondratyev 			 * Evdev layer filters out them anyway.
258afd590d9SVladimir Kondratyev 			 */
259afd590d9SVladimir Kondratyev 			if (data == (hi->evtype == EV_REL ? 0 : hi->last_val))
260afd590d9SVladimir Kondratyev 				continue;
261afd590d9SVladimir Kondratyev 			if (hi->evtype == EV_KEY)
262afd590d9SVladimir Kondratyev 				hidmap_push_key(hm, hi->code, data);
263afd590d9SVladimir Kondratyev 			else
264afd590d9SVladimir Kondratyev 				evdev_push_event(hm->evdev, hi->evtype,
265afd590d9SVladimir Kondratyev 				    hi->code, data);
266afd590d9SVladimir Kondratyev 			hi->last_val = data;
267afd590d9SVladimir Kondratyev 			break;
268afd590d9SVladimir Kondratyev 
269afd590d9SVladimir Kondratyev 		case HIDMAP_TYPE_ARR_LIST:
270afd590d9SVladimir Kondratyev 			key = KEY_RESERVED;
271afd590d9SVladimir Kondratyev 			/*
272afd590d9SVladimir Kondratyev 			 * 6.2.2.5. An out-of range value in an array field
273afd590d9SVladimir Kondratyev 			 * is considered no controls asserted.
274afd590d9SVladimir Kondratyev 			 */
275afd590d9SVladimir Kondratyev 			if (data < hi->lmin || data > hi->lmax)
276afd590d9SVladimir Kondratyev 				goto report_key;
277afd590d9SVladimir Kondratyev 			/*
278afd590d9SVladimir Kondratyev 			 * 6.2.2.5. Rather than returning a single bit for each
279afd590d9SVladimir Kondratyev 			 * button in the group, an array returns an index in
280afd590d9SVladimir Kondratyev 			 * each field that corresponds to the pressed button.
281afd590d9SVladimir Kondratyev 			 */
282afd590d9SVladimir Kondratyev 			key = hi->codes[data - hi->lmin];
283afd590d9SVladimir Kondratyev 			if (key == KEY_RESERVED)
284afd590d9SVladimir Kondratyev 				DPRINTF(hm, "Can not map unknown HID "
285afd590d9SVladimir Kondratyev 				    "array index: %08x\n", data);
286afd590d9SVladimir Kondratyev 			goto report_key;
287afd590d9SVladimir Kondratyev 
288afd590d9SVladimir Kondratyev 		case HIDMAP_TYPE_ARR_RANGE:
289afd590d9SVladimir Kondratyev 			key = KEY_RESERVED;
290afd590d9SVladimir Kondratyev 			/*
291afd590d9SVladimir Kondratyev 			 * 6.2.2.5. An out-of range value in an array field
292afd590d9SVladimir Kondratyev 			 * is considered no controls asserted.
293afd590d9SVladimir Kondratyev 			 */
294afd590d9SVladimir Kondratyev 			if (data < hi->lmin || data > hi->lmax)
295afd590d9SVladimir Kondratyev 				goto report_key;
296afd590d9SVladimir Kondratyev 			/*
297afd590d9SVladimir Kondratyev 			 * When the input field is an array and the usage is
298afd590d9SVladimir Kondratyev 			 * specified with a range instead of an ID, we have to
299afd590d9SVladimir Kondratyev 			 * derive the actual usage by using the item value as
300afd590d9SVladimir Kondratyev 			 * an index in the usage range list.
301afd590d9SVladimir Kondratyev 			 */
302afd590d9SVladimir Kondratyev 			usage = data - hi->lmin + hi->umin;
303afd590d9SVladimir Kondratyev 			found = false;
304afd590d9SVladimir Kondratyev 			HIDMAP_FOREACH_ITEM(hm, mi, uoff) {
305afd590d9SVladimir Kondratyev 				if (usage == mi->usage + uoff &&
306afd590d9SVladimir Kondratyev 				    mi->type == EV_KEY && !mi->has_cb) {
307afd590d9SVladimir Kondratyev 					key = mi->code;
308afd590d9SVladimir Kondratyev 					found = true;
309afd590d9SVladimir Kondratyev 					break;
310afd590d9SVladimir Kondratyev 				}
311afd590d9SVladimir Kondratyev 			}
312afd590d9SVladimir Kondratyev 			if (!found)
313afd590d9SVladimir Kondratyev 				DPRINTF(hm, "Can not map unknown HID "
314afd590d9SVladimir Kondratyev 				    "usage: %08x\n", usage);
315afd590d9SVladimir Kondratyev report_key:
316afd590d9SVladimir Kondratyev 			if (key == HIDMAP_KEY_NULL || key == hi->last_key)
317afd590d9SVladimir Kondratyev 				continue;
318afd590d9SVladimir Kondratyev 			if (hi->last_key != KEY_RESERVED)
319afd590d9SVladimir Kondratyev 				hidmap_push_key(hm, hi->last_key, 0);
320afd590d9SVladimir Kondratyev 			if (key != KEY_RESERVED)
321afd590d9SVladimir Kondratyev 				hidmap_push_key(hm, key, 1);
322afd590d9SVladimir Kondratyev 			hi->last_key = key;
323afd590d9SVladimir Kondratyev 			break;
324afd590d9SVladimir Kondratyev 
325afd590d9SVladimir Kondratyev 		default:
326afd590d9SVladimir Kondratyev 			KASSERT(0, ("Unknown map type (%d)", hi->type));
327afd590d9SVladimir Kondratyev 		}
328afd590d9SVladimir Kondratyev 		do_sync = true;
329afd590d9SVladimir Kondratyev 	}
330afd590d9SVladimir Kondratyev 
331afd590d9SVladimir Kondratyev 	if (do_sync) {
332afd590d9SVladimir Kondratyev 		if (HIDMAP_WANT_MERGE_KEYS(hm))
333afd590d9SVladimir Kondratyev 			hidmap_sync_keys(hm);
334afd590d9SVladimir Kondratyev 		evdev_sync(hm->evdev);
335afd590d9SVladimir Kondratyev 	}
336afd590d9SVladimir Kondratyev }
337afd590d9SVladimir Kondratyev 
338afd590d9SVladimir Kondratyev static inline bool
can_map_callback(struct hid_item * hi,const struct hidmap_item * mi,uint16_t usage_offset)339afd590d9SVladimir Kondratyev can_map_callback(struct hid_item *hi, const struct hidmap_item *mi,
340afd590d9SVladimir Kondratyev     uint16_t usage_offset)
341afd590d9SVladimir Kondratyev {
342afd590d9SVladimir Kondratyev 
343afd590d9SVladimir Kondratyev 	return (mi->has_cb && !mi->final_cb &&
344afd590d9SVladimir Kondratyev 	    hi->usage == mi->usage + usage_offset &&
345afd590d9SVladimir Kondratyev 	    (mi->relabs == HIDMAP_RELABS_ANY ||
346afd590d9SVladimir Kondratyev 	    !(hi->flags & HIO_RELATIVE) == !(mi->relabs == HIDMAP_RELATIVE)));
347afd590d9SVladimir Kondratyev }
348afd590d9SVladimir Kondratyev 
349afd590d9SVladimir Kondratyev static inline bool
can_map_variable(struct hid_item * hi,const struct hidmap_item * mi,uint16_t usage_offset)350afd590d9SVladimir Kondratyev can_map_variable(struct hid_item *hi, const struct hidmap_item *mi,
351afd590d9SVladimir Kondratyev     uint16_t usage_offset)
352afd590d9SVladimir Kondratyev {
353afd590d9SVladimir Kondratyev 
354afd590d9SVladimir Kondratyev 	return ((hi->flags & HIO_VARIABLE) != 0 && !mi->has_cb &&
355afd590d9SVladimir Kondratyev 	    hi->usage == mi->usage + usage_offset &&
356afd590d9SVladimir Kondratyev 	    (mi->relabs == HIDMAP_RELABS_ANY ||
357afd590d9SVladimir Kondratyev 	    !(hi->flags & HIO_RELATIVE) == !(mi->relabs == HIDMAP_RELATIVE)));
358afd590d9SVladimir Kondratyev }
359afd590d9SVladimir Kondratyev 
360afd590d9SVladimir Kondratyev static inline bool
can_map_arr_range(struct hid_item * hi,const struct hidmap_item * mi,uint16_t usage_offset)361afd590d9SVladimir Kondratyev can_map_arr_range(struct hid_item *hi, const struct hidmap_item *mi,
362afd590d9SVladimir Kondratyev     uint16_t usage_offset)
363afd590d9SVladimir Kondratyev {
364afd590d9SVladimir Kondratyev 
365afd590d9SVladimir Kondratyev 	return ((hi->flags & HIO_VARIABLE) == 0 && !mi->has_cb &&
366afd590d9SVladimir Kondratyev 	    hi->usage_minimum <= mi->usage + usage_offset &&
367afd590d9SVladimir Kondratyev 	    hi->usage_maximum >= mi->usage + usage_offset &&
368afd590d9SVladimir Kondratyev 	    mi->type == EV_KEY &&
369afd590d9SVladimir Kondratyev 	    (mi->code != KEY_RESERVED && mi->code != HIDMAP_KEY_NULL));
370afd590d9SVladimir Kondratyev }
371afd590d9SVladimir Kondratyev 
372afd590d9SVladimir Kondratyev static inline bool
can_map_arr_list(struct hid_item * hi,const struct hidmap_item * mi,uint32_t usage,uint16_t usage_offset)373afd590d9SVladimir Kondratyev can_map_arr_list(struct hid_item *hi, const struct hidmap_item *mi,
374afd590d9SVladimir Kondratyev     uint32_t usage, uint16_t usage_offset)
375afd590d9SVladimir Kondratyev {
376afd590d9SVladimir Kondratyev 
377afd590d9SVladimir Kondratyev 	return ((hi->flags & HIO_VARIABLE) == 0 && !mi->has_cb &&
378afd590d9SVladimir Kondratyev 	    usage == mi->usage + usage_offset &&
379afd590d9SVladimir Kondratyev 	    mi->type == EV_KEY &&
380afd590d9SVladimir Kondratyev 	    (mi->code != KEY_RESERVED && mi->code != HIDMAP_KEY_NULL));
381afd590d9SVladimir Kondratyev }
382afd590d9SVladimir Kondratyev 
383afd590d9SVladimir Kondratyev static bool
hidmap_probe_hid_item(struct hid_item * hi,const struct hidmap_item * map,int nitems_map,hidmap_caps_t caps)384afd590d9SVladimir Kondratyev hidmap_probe_hid_item(struct hid_item *hi, const struct hidmap_item *map,
385afd590d9SVladimir Kondratyev     int nitems_map, hidmap_caps_t caps)
386afd590d9SVladimir Kondratyev {
387afd590d9SVladimir Kondratyev 	u_int i, j;
388afd590d9SVladimir Kondratyev 	uint16_t uoff;
389afd590d9SVladimir Kondratyev 	bool found = false;
390afd590d9SVladimir Kondratyev 
391afd590d9SVladimir Kondratyev #define	HIDMAP_FOREACH_INDEX(map, nitems, idx, uoff)	\
392afd590d9SVladimir Kondratyev 	for ((idx) = 0, (uoff) = -1;			\
393afd590d9SVladimir Kondratyev 	     hidmap_get_next_map_index((map), (nitems), &(idx), &(uoff));)
394afd590d9SVladimir Kondratyev 
395afd590d9SVladimir Kondratyev 	HIDMAP_FOREACH_INDEX(map, nitems_map, i, uoff) {
396afd590d9SVladimir Kondratyev 		if (can_map_callback(hi, map + i, uoff)) {
397afd590d9SVladimir Kondratyev 			if (map[i].cb(NULL, NULL,
398afd590d9SVladimir Kondratyev 			    (union hidmap_cb_ctx){.hi = hi}) != 0)
399afd590d9SVladimir Kondratyev 				break;
400afd590d9SVladimir Kondratyev 			setbit(caps, i);
401afd590d9SVladimir Kondratyev 			return (true);
402afd590d9SVladimir Kondratyev 		}
403afd590d9SVladimir Kondratyev 	}
404afd590d9SVladimir Kondratyev 
405afd590d9SVladimir Kondratyev 	if (hi->flags & HIO_VARIABLE) {
406afd590d9SVladimir Kondratyev 		HIDMAP_FOREACH_INDEX(map, nitems_map, i, uoff) {
407afd590d9SVladimir Kondratyev 			if (can_map_variable(hi, map + i, uoff)) {
408afd590d9SVladimir Kondratyev 				KASSERT(map[i].type == EV_KEY ||
409afd590d9SVladimir Kondratyev 					map[i].type == EV_REL ||
410afd590d9SVladimir Kondratyev 					map[i].type == EV_ABS ||
411afd590d9SVladimir Kondratyev 					map[i].type == EV_SW,
412afd590d9SVladimir Kondratyev 				    ("Unsupported event type"));
413afd590d9SVladimir Kondratyev 				setbit(caps, i);
414afd590d9SVladimir Kondratyev 				return (true);
415afd590d9SVladimir Kondratyev 			}
416afd590d9SVladimir Kondratyev 		}
417afd590d9SVladimir Kondratyev 		return (false);
418afd590d9SVladimir Kondratyev 	}
419afd590d9SVladimir Kondratyev 
420afd590d9SVladimir Kondratyev 	if (hi->usage_minimum != 0 || hi->usage_maximum != 0) {
421afd590d9SVladimir Kondratyev 		HIDMAP_FOREACH_INDEX(map, nitems_map, i, uoff) {
422afd590d9SVladimir Kondratyev 			if (can_map_arr_range(hi, map + i, uoff)) {
423afd590d9SVladimir Kondratyev 				setbit(caps, i);
424afd590d9SVladimir Kondratyev 				found = true;
425afd590d9SVladimir Kondratyev 			}
426afd590d9SVladimir Kondratyev 		}
427afd590d9SVladimir Kondratyev 		return (found);
428afd590d9SVladimir Kondratyev 	}
429afd590d9SVladimir Kondratyev 
430afd590d9SVladimir Kondratyev 	for (j = 0; j < hi->nusages; j++) {
431afd590d9SVladimir Kondratyev 		HIDMAP_FOREACH_INDEX(map, nitems_map, i, uoff) {
432afd590d9SVladimir Kondratyev 			if (can_map_arr_list(hi, map+i, hi->usages[j], uoff)) {
433afd590d9SVladimir Kondratyev 				setbit(caps, i);
434afd590d9SVladimir Kondratyev 				found = true;
435afd590d9SVladimir Kondratyev 			}
436afd590d9SVladimir Kondratyev 		}
437afd590d9SVladimir Kondratyev 	}
438afd590d9SVladimir Kondratyev 
439afd590d9SVladimir Kondratyev 	return (found);
440afd590d9SVladimir Kondratyev }
441afd590d9SVladimir Kondratyev 
442afd590d9SVladimir Kondratyev 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)443afd590d9SVladimir Kondratyev hidmap_probe_hid_descr(void *d_ptr, hid_size_t d_len, uint8_t tlc_index,
444afd590d9SVladimir Kondratyev     const struct hidmap_item *map, int nitems_map, hidmap_caps_t caps)
445afd590d9SVladimir Kondratyev {
446afd590d9SVladimir Kondratyev 	struct hid_data *hd;
447afd590d9SVladimir Kondratyev 	struct hid_item hi;
448afd590d9SVladimir Kondratyev 	uint32_t i, items = 0;
449afd590d9SVladimir Kondratyev 	bool do_free = false;
450afd590d9SVladimir Kondratyev 
451afd590d9SVladimir Kondratyev 	if (caps == NULL) {
452bbed4b41SVladimir Kondratyev 		caps = malloc(HIDMAP_CAPS_SZ(nitems_map), M_DEVBUF,
453bbed4b41SVladimir Kondratyev 		    M_WAITOK | M_ZERO);
454afd590d9SVladimir Kondratyev 		do_free = true;
455afd590d9SVladimir Kondratyev 	} else
456afd590d9SVladimir Kondratyev 		bzero (caps, HIDMAP_CAPS_SZ(nitems_map));
457afd590d9SVladimir Kondratyev 
458afd590d9SVladimir Kondratyev 	/* Parse inputs */
459afd590d9SVladimir Kondratyev 	hd = hid_start_parse(d_ptr, d_len, 1 << hid_input);
460afd590d9SVladimir Kondratyev 	HIDBUS_FOREACH_ITEM(hd, &hi, tlc_index) {
461afd590d9SVladimir Kondratyev 		if (hi.kind != hid_input)
462afd590d9SVladimir Kondratyev 			continue;
463afd590d9SVladimir Kondratyev 		if (hi.flags & HIO_CONST)
464afd590d9SVladimir Kondratyev 			continue;
465afd590d9SVladimir Kondratyev 		for (i = 0; i < hi.loc.count; i++, hi.loc.pos += hi.loc.size)
466afd590d9SVladimir Kondratyev 			if (hidmap_probe_hid_item(&hi, map, nitems_map, caps))
467afd590d9SVladimir Kondratyev 				items++;
468afd590d9SVladimir Kondratyev 	}
469afd590d9SVladimir Kondratyev 	hid_end_parse(hd);
470afd590d9SVladimir Kondratyev 
471afd590d9SVladimir Kondratyev 	/* Take finalizing callbacks in to account */
472afd590d9SVladimir Kondratyev 	for (i = 0; i < nitems_map; i++) {
473afd590d9SVladimir Kondratyev 		if (map[i].has_cb && map[i].final_cb &&
474afd590d9SVladimir Kondratyev 		    map[i].cb(NULL, NULL, (union hidmap_cb_ctx){}) == 0) {
475afd590d9SVladimir Kondratyev 			setbit(caps, i);
476afd590d9SVladimir Kondratyev 			items++;
477afd590d9SVladimir Kondratyev 		}
478afd590d9SVladimir Kondratyev 	}
479afd590d9SVladimir Kondratyev 
480afd590d9SVladimir Kondratyev 	/* Check that all mandatory usages are present in report descriptor */
481afd590d9SVladimir Kondratyev 	if (items != 0) {
482afd590d9SVladimir Kondratyev 		for (i = 0; i < nitems_map; i++) {
483cded1fdbSVladimir Kondratyev 			KASSERT(!(map[i].required && map[i].forbidden),
484cded1fdbSVladimir Kondratyev 			    ("both required & forbidden item flags are set"));
485cded1fdbSVladimir Kondratyev 			if ((map[i].required && isclr(caps, i)) ||
486cded1fdbSVladimir Kondratyev 			    (map[i].forbidden && isset(caps, i))) {
487afd590d9SVladimir Kondratyev 				items = 0;
488afd590d9SVladimir Kondratyev 				break;
489afd590d9SVladimir Kondratyev 			}
490afd590d9SVladimir Kondratyev 		}
491afd590d9SVladimir Kondratyev 	}
492afd590d9SVladimir Kondratyev 
493afd590d9SVladimir Kondratyev 	if (do_free)
494afd590d9SVladimir Kondratyev 		free(caps, M_DEVBUF);
495afd590d9SVladimir Kondratyev 
496afd590d9SVladimir Kondratyev 	return (items);
497afd590d9SVladimir Kondratyev }
498afd590d9SVladimir Kondratyev 
499afd590d9SVladimir Kondratyev uint32_t
hidmap_add_map(struct hidmap * hm,const struct hidmap_item * map,int nitems_map,hidmap_caps_t caps)500afd590d9SVladimir Kondratyev hidmap_add_map(struct hidmap *hm, const struct hidmap_item *map,
501afd590d9SVladimir Kondratyev     int nitems_map, hidmap_caps_t caps)
502afd590d9SVladimir Kondratyev {
503afd590d9SVladimir Kondratyev 	void *d_ptr;
504afd590d9SVladimir Kondratyev 	uint32_t items;
505afd590d9SVladimir Kondratyev 	int i, error;
506afd590d9SVladimir Kondratyev 	hid_size_t d_len;
507afd590d9SVladimir Kondratyev 	uint8_t tlc_index = hidbus_get_index(hm->dev);
508afd590d9SVladimir Kondratyev 
509afd590d9SVladimir Kondratyev 	/* Avoid double-adding of map in probe() handler */
510afd590d9SVladimir Kondratyev 	for (i = 0; i < hm->nmaps; i++)
511afd590d9SVladimir Kondratyev 		if (hm->map[i] == map)
512afd590d9SVladimir Kondratyev 			return (0);
513afd590d9SVladimir Kondratyev 
514afd590d9SVladimir Kondratyev 	error = hid_get_report_descr(hm->dev, &d_ptr, &d_len);
515afd590d9SVladimir Kondratyev 	if (error != 0) {
516afd590d9SVladimir Kondratyev 		device_printf(hm->dev, "could not retrieve report descriptor "
517afd590d9SVladimir Kondratyev 		     "from device: %d\n", error);
518afd590d9SVladimir Kondratyev 		return (error);
519afd590d9SVladimir Kondratyev 	}
520afd590d9SVladimir Kondratyev 
521afd590d9SVladimir Kondratyev 	hm->cb_state = HIDMAP_CB_IS_PROBING;
522afd590d9SVladimir Kondratyev 	items = hidmap_probe_hid_descr(d_ptr, d_len, tlc_index, map,
523afd590d9SVladimir Kondratyev 	    nitems_map, caps);
524afd590d9SVladimir Kondratyev 	if (items == 0)
525afd590d9SVladimir Kondratyev 		return (ENXIO);
526afd590d9SVladimir Kondratyev 
527afd590d9SVladimir Kondratyev 	KASSERT(hm->nmaps < HIDMAP_MAX_MAPS,
528afd590d9SVladimir Kondratyev 	    ("Not more than %d maps is supported", HIDMAP_MAX_MAPS));
529afd590d9SVladimir Kondratyev 	hm->nhid_items += items;
530afd590d9SVladimir Kondratyev 	hm->map[hm->nmaps] = map;
531afd590d9SVladimir Kondratyev 	hm->nmap_items[hm->nmaps] = nitems_map;
532afd590d9SVladimir Kondratyev 	hm->nmaps++;
533afd590d9SVladimir Kondratyev 
534afd590d9SVladimir Kondratyev 	return (0);
535afd590d9SVladimir Kondratyev }
536afd590d9SVladimir Kondratyev 
537afd590d9SVladimir Kondratyev static bool
hidmap_parse_hid_item(struct hidmap * hm,struct hid_item * hi,struct hidmap_hid_item * item)538afd590d9SVladimir Kondratyev hidmap_parse_hid_item(struct hidmap *hm, struct hid_item *hi,
539afd590d9SVladimir Kondratyev     struct hidmap_hid_item *item)
540afd590d9SVladimir Kondratyev {
541afd590d9SVladimir Kondratyev 	const struct hidmap_item *mi;
542afd590d9SVladimir Kondratyev 	struct hidmap_hid_item hi_temp;
543afd590d9SVladimir Kondratyev 	uint32_t i;
544afd590d9SVladimir Kondratyev 	uint16_t uoff;
545afd590d9SVladimir Kondratyev 	bool found = false;
546afd590d9SVladimir Kondratyev 
547afd590d9SVladimir Kondratyev 	HIDMAP_FOREACH_ITEM(hm, mi, uoff) {
548afd590d9SVladimir Kondratyev 		if (can_map_callback(hi, mi, uoff)) {
549afd590d9SVladimir Kondratyev 			bzero(&hi_temp, sizeof(hi_temp));
550afd590d9SVladimir Kondratyev 			hi_temp.cb = mi->cb;
551afd590d9SVladimir Kondratyev 			hi_temp.type = HIDMAP_TYPE_CALLBACK;
552afd590d9SVladimir Kondratyev 			/*
553afd590d9SVladimir Kondratyev 			 * Values returned by probe- and attach-stage
554afd590d9SVladimir Kondratyev 			 * callbacks MUST be identical.
555afd590d9SVladimir Kondratyev 			 */
556afd590d9SVladimir Kondratyev 			if (mi->cb(hm, &hi_temp,
557afd590d9SVladimir Kondratyev 			    (union hidmap_cb_ctx){.hi = hi}) != 0)
558afd590d9SVladimir Kondratyev 				break;
559afd590d9SVladimir Kondratyev 			bcopy(&hi_temp, item, sizeof(hi_temp));
560afd590d9SVladimir Kondratyev 			goto mapped;
561afd590d9SVladimir Kondratyev 		}
562afd590d9SVladimir Kondratyev 	}
563afd590d9SVladimir Kondratyev 
564afd590d9SVladimir Kondratyev 	if (hi->flags & HIO_VARIABLE) {
565afd590d9SVladimir Kondratyev 		HIDMAP_FOREACH_ITEM(hm, mi, uoff) {
566afd590d9SVladimir Kondratyev 			if (can_map_variable(hi, mi, uoff)) {
567afd590d9SVladimir Kondratyev 				item->evtype = mi->type;
568afd590d9SVladimir Kondratyev 				item->code = mi->code + uoff;
569afd590d9SVladimir Kondratyev 				item->type = hi->flags & HIO_NULLSTATE
570afd590d9SVladimir Kondratyev 				    ? HIDMAP_TYPE_VAR_NULLST
571afd590d9SVladimir Kondratyev 				    : HIDMAP_TYPE_VARIABLE;
572afd590d9SVladimir Kondratyev 				item->last_val = 0;
573afd590d9SVladimir Kondratyev 				item->invert_value = mi->invert_value;
574afd590d9SVladimir Kondratyev 				switch (mi->type) {
575afd590d9SVladimir Kondratyev 				case EV_KEY:
576afd590d9SVladimir Kondratyev 					hidmap_support_key(hm, item->code);
577afd590d9SVladimir Kondratyev 					break;
578afd590d9SVladimir Kondratyev 				case EV_REL:
579afd590d9SVladimir Kondratyev 					evdev_support_event(hm->evdev, EV_REL);
580afd590d9SVladimir Kondratyev 					evdev_support_rel(hm->evdev,
581afd590d9SVladimir Kondratyev 					    item->code);
582afd590d9SVladimir Kondratyev 					break;
583afd590d9SVladimir Kondratyev 				case EV_ABS:
584afd590d9SVladimir Kondratyev 					evdev_support_event(hm->evdev, EV_ABS);
585afd590d9SVladimir Kondratyev 					evdev_support_abs(hm->evdev,
586afd590d9SVladimir Kondratyev 					    item->code,
587afd590d9SVladimir Kondratyev 					    hi->logical_minimum,
588afd590d9SVladimir Kondratyev 					    hi->logical_maximum,
589afd590d9SVladimir Kondratyev 					    mi->fuzz,
590afd590d9SVladimir Kondratyev 					    mi->flat,
591afd590d9SVladimir Kondratyev 					    hid_item_resolution(hi));
592afd590d9SVladimir Kondratyev 					break;
593afd590d9SVladimir Kondratyev 				case EV_SW:
594afd590d9SVladimir Kondratyev 					evdev_support_event(hm->evdev, EV_SW);
595afd590d9SVladimir Kondratyev 					evdev_support_sw(hm->evdev,
596afd590d9SVladimir Kondratyev 					    item->code);
597afd590d9SVladimir Kondratyev 					break;
598afd590d9SVladimir Kondratyev 				default:
599afd590d9SVladimir Kondratyev 					KASSERT(0, ("Unsupported event type"));
600afd590d9SVladimir Kondratyev 				}
601afd590d9SVladimir Kondratyev 				goto mapped;
602afd590d9SVladimir Kondratyev 			}
603afd590d9SVladimir Kondratyev 		}
604afd590d9SVladimir Kondratyev 		return (false);
605afd590d9SVladimir Kondratyev 	}
606afd590d9SVladimir Kondratyev 
607afd590d9SVladimir Kondratyev 	if (hi->usage_minimum != 0 || hi->usage_maximum != 0) {
608afd590d9SVladimir Kondratyev 		HIDMAP_FOREACH_ITEM(hm, mi, uoff) {
609afd590d9SVladimir Kondratyev 			if (can_map_arr_range(hi, mi, uoff)) {
610afd590d9SVladimir Kondratyev 				hidmap_support_key(hm, mi->code + uoff);
611afd590d9SVladimir Kondratyev 				found = true;
612afd590d9SVladimir Kondratyev 			}
613afd590d9SVladimir Kondratyev 		}
614afd590d9SVladimir Kondratyev 		if (!found)
615afd590d9SVladimir Kondratyev 			return (false);
616afd590d9SVladimir Kondratyev 		item->umin = hi->usage_minimum;
617afd590d9SVladimir Kondratyev 		item->type = HIDMAP_TYPE_ARR_RANGE;
618afd590d9SVladimir Kondratyev 		item->last_key = KEY_RESERVED;
619afd590d9SVladimir Kondratyev 		goto mapped;
620afd590d9SVladimir Kondratyev 	}
621afd590d9SVladimir Kondratyev 
622afd590d9SVladimir Kondratyev 	for (i = 0; i < hi->nusages; i++) {
623afd590d9SVladimir Kondratyev 		HIDMAP_FOREACH_ITEM(hm, mi, uoff) {
624afd590d9SVladimir Kondratyev 			if (can_map_arr_list(hi, mi, hi->usages[i], uoff)) {
625afd590d9SVladimir Kondratyev 				hidmap_support_key(hm, mi->code + uoff);
626afd590d9SVladimir Kondratyev 				if (item->codes == NULL)
627afd590d9SVladimir Kondratyev 					item->codes = malloc(
628afd590d9SVladimir Kondratyev 					    hi->nusages * sizeof(uint16_t),
629afd590d9SVladimir Kondratyev 					    M_DEVBUF, M_WAITOK | M_ZERO);
630afd590d9SVladimir Kondratyev 				item->codes[i] = mi->code + uoff;
631afd590d9SVladimir Kondratyev 				found = true;
632afd590d9SVladimir Kondratyev 				break;
633afd590d9SVladimir Kondratyev 			}
634afd590d9SVladimir Kondratyev 		}
635afd590d9SVladimir Kondratyev 	}
636afd590d9SVladimir Kondratyev 	if (!found)
637afd590d9SVladimir Kondratyev 		return (false);
638afd590d9SVladimir Kondratyev 	item->type = HIDMAP_TYPE_ARR_LIST;
639afd590d9SVladimir Kondratyev 	item->last_key = KEY_RESERVED;
640afd590d9SVladimir Kondratyev 
641afd590d9SVladimir Kondratyev mapped:
642afd590d9SVladimir Kondratyev 	item->id = hi->report_ID;
643afd590d9SVladimir Kondratyev 	item->loc = hi->loc;
644afd590d9SVladimir Kondratyev 	item->loc.count = 1;
645afd590d9SVladimir Kondratyev 	item->lmin = hi->logical_minimum;
646afd590d9SVladimir Kondratyev 	item->lmax = hi->logical_maximum;
647afd590d9SVladimir Kondratyev 
648afd590d9SVladimir Kondratyev 	DPRINTFN(hm, 6, "usage=%04x id=%d loc=%u/%u type=%d item=%*D\n",
649afd590d9SVladimir Kondratyev 	    hi->usage, hi->report_ID, hi->loc.pos, hi->loc.size, item->type,
650afd590d9SVladimir Kondratyev 	    (int)sizeof(item->cb), &item->cb, " ");
651afd590d9SVladimir Kondratyev 
652afd590d9SVladimir Kondratyev 	return (true);
653afd590d9SVladimir Kondratyev }
654afd590d9SVladimir Kondratyev 
655afd590d9SVladimir Kondratyev static int
hidmap_parse_hid_descr(struct hidmap * hm,uint8_t tlc_index)656afd590d9SVladimir Kondratyev hidmap_parse_hid_descr(struct hidmap *hm, uint8_t tlc_index)
657afd590d9SVladimir Kondratyev {
658afd590d9SVladimir Kondratyev 	const struct hidmap_item *map;
659afd590d9SVladimir Kondratyev 	struct hidmap_hid_item *item = hm->hid_items;
660afd590d9SVladimir Kondratyev 	void *d_ptr;
661afd590d9SVladimir Kondratyev 	struct hid_data *hd;
662afd590d9SVladimir Kondratyev 	struct hid_item hi;
663afd590d9SVladimir Kondratyev 	int i, error;
664afd590d9SVladimir Kondratyev 	hid_size_t d_len;
665afd590d9SVladimir Kondratyev 
666afd590d9SVladimir Kondratyev 	error = hid_get_report_descr(hm->dev, &d_ptr, &d_len);
667afd590d9SVladimir Kondratyev 	if (error != 0) {
668afd590d9SVladimir Kondratyev 		DPRINTF(hm, "could not retrieve report descriptor from "
669afd590d9SVladimir Kondratyev 		     "device: %d\n", error);
670afd590d9SVladimir Kondratyev 		return (error);
671afd590d9SVladimir Kondratyev 	}
672afd590d9SVladimir Kondratyev 
673afd590d9SVladimir Kondratyev 	/* Parse inputs */
674afd590d9SVladimir Kondratyev 	hd = hid_start_parse(d_ptr, d_len, 1 << hid_input);
675afd590d9SVladimir Kondratyev 	HIDBUS_FOREACH_ITEM(hd, &hi, tlc_index) {
676afd590d9SVladimir Kondratyev 		if (hi.kind != hid_input)
677afd590d9SVladimir Kondratyev 			continue;
678afd590d9SVladimir Kondratyev 		if (hi.flags & HIO_CONST)
679afd590d9SVladimir Kondratyev 			continue;
680afd590d9SVladimir Kondratyev 		for (i = 0; i < hi.loc.count; i++, hi.loc.pos += hi.loc.size)
681afd590d9SVladimir Kondratyev 			if (hidmap_parse_hid_item(hm, &hi, item))
682afd590d9SVladimir Kondratyev 				item++;
683afd590d9SVladimir Kondratyev 		KASSERT(item <= hm->hid_items + hm->nhid_items,
684afd590d9SVladimir Kondratyev 		    ("Parsed HID item array overflow"));
685afd590d9SVladimir Kondratyev 	}
686afd590d9SVladimir Kondratyev 	hid_end_parse(hd);
687afd590d9SVladimir Kondratyev 
688afd590d9SVladimir Kondratyev 	/* Add finalizing callbacks to the end of list */
689afd590d9SVladimir Kondratyev 	for (i = 0; i < hm->nmaps; i++) {
690afd590d9SVladimir Kondratyev 		for (map = hm->map[i];
691afd590d9SVladimir Kondratyev 		     map < hm->map[i] + hm->nmap_items[i];
692afd590d9SVladimir Kondratyev 		     map++) {
693afd590d9SVladimir Kondratyev 			if (map->has_cb && map->final_cb &&
694afd590d9SVladimir Kondratyev 			    map->cb(hm, item, (union hidmap_cb_ctx){}) == 0) {
695afd590d9SVladimir Kondratyev 				item->cb = map->cb;
696afd590d9SVladimir Kondratyev 				item->type = HIDMAP_TYPE_FINALCB;
697afd590d9SVladimir Kondratyev 				item++;
698afd590d9SVladimir Kondratyev 			}
699afd590d9SVladimir Kondratyev 		}
700afd590d9SVladimir Kondratyev 	}
701afd590d9SVladimir Kondratyev 
702afd590d9SVladimir Kondratyev 	/*
703afd590d9SVladimir Kondratyev 	 * Resulting number of parsed HID items can be less than expected as
704afd590d9SVladimir Kondratyev 	 * map items might be duplicated in different maps. Save real number.
705afd590d9SVladimir Kondratyev 	 */
706afd590d9SVladimir Kondratyev 	if (hm->nhid_items != item - hm->hid_items)
707afd590d9SVladimir Kondratyev 		DPRINTF(hm, "Parsed HID item number mismatch: expected=%u "
708afd590d9SVladimir Kondratyev 		    "result=%td\n", hm->nhid_items, item - hm->hid_items);
709afd590d9SVladimir Kondratyev 	hm->nhid_items = item - hm->hid_items;
710afd590d9SVladimir Kondratyev 
711afd590d9SVladimir Kondratyev 	if (HIDMAP_WANT_MERGE_KEYS(hm))
712afd590d9SVladimir Kondratyev 		bzero(hm->key_press, howmany(KEY_CNT, 8));
713afd590d9SVladimir Kondratyev 
714afd590d9SVladimir Kondratyev 	return (0);
715afd590d9SVladimir Kondratyev }
716afd590d9SVladimir Kondratyev 
717afd590d9SVladimir Kondratyev 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)718afd590d9SVladimir Kondratyev hidmap_probe(struct hidmap* hm, device_t dev,
719afd590d9SVladimir Kondratyev     const struct hid_device_id *id, int nitems_id,
720afd590d9SVladimir Kondratyev     const struct hidmap_item *map, int nitems_map,
721afd590d9SVladimir Kondratyev     const char *suffix, hidmap_caps_t caps)
722afd590d9SVladimir Kondratyev {
723afd590d9SVladimir Kondratyev 	int error;
724afd590d9SVladimir Kondratyev 
725afd590d9SVladimir Kondratyev 	error = hidbus_lookup_driver_info(dev, id, nitems_id);
726afd590d9SVladimir Kondratyev 	if (error != 0)
727afd590d9SVladimir Kondratyev 		return (error);
728afd590d9SVladimir Kondratyev 
729afd590d9SVladimir Kondratyev 	hidmap_set_dev(hm, dev);
730afd590d9SVladimir Kondratyev 
731afd590d9SVladimir Kondratyev 	error = hidmap_add_map(hm, map, nitems_map, caps);
732afd590d9SVladimir Kondratyev 	if (error != 0)
733afd590d9SVladimir Kondratyev 		return (error);
734afd590d9SVladimir Kondratyev 
735afd590d9SVladimir Kondratyev 	hidbus_set_desc(dev, suffix);
736afd590d9SVladimir Kondratyev 
737afd590d9SVladimir Kondratyev 	return (BUS_PROBE_DEFAULT);
738afd590d9SVladimir Kondratyev }
739afd590d9SVladimir Kondratyev 
740afd590d9SVladimir Kondratyev int
hidmap_attach(struct hidmap * hm)741afd590d9SVladimir Kondratyev hidmap_attach(struct hidmap* hm)
742afd590d9SVladimir Kondratyev {
743afd590d9SVladimir Kondratyev 	const struct hid_device_info *hw = hid_get_device_info(hm->dev);
744afd590d9SVladimir Kondratyev #ifdef HID_DEBUG
745afd590d9SVladimir Kondratyev 	char tunable[40];
746afd590d9SVladimir Kondratyev #endif
747afd590d9SVladimir Kondratyev 	int error;
748afd590d9SVladimir Kondratyev 
749afd590d9SVladimir Kondratyev #ifdef HID_DEBUG
750afd590d9SVladimir Kondratyev 	if (hm->debug_var == NULL) {
751afd590d9SVladimir Kondratyev 		hm->debug_var = &hm->debug_level;
752afd590d9SVladimir Kondratyev 		snprintf(tunable, sizeof(tunable), "hw.hid.%s.debug",
753afd590d9SVladimir Kondratyev 		    device_get_name(hm->dev));
754afd590d9SVladimir Kondratyev 		TUNABLE_INT_FETCH(tunable, &hm->debug_level);
755afd590d9SVladimir Kondratyev 		SYSCTL_ADD_INT(device_get_sysctl_ctx(hm->dev),
756afd590d9SVladimir Kondratyev 			SYSCTL_CHILDREN(device_get_sysctl_tree(hm->dev)),
7578ffcde25SVladimir Kondratyev 			OID_AUTO, "debug", CTLFLAG_RWTUN,
758afd590d9SVladimir Kondratyev 			&hm->debug_level, 0, "Verbosity level");
759afd590d9SVladimir Kondratyev 	}
760afd590d9SVladimir Kondratyev #endif
761afd590d9SVladimir Kondratyev 
762afd590d9SVladimir Kondratyev 	DPRINTFN(hm, 11, "hm=%p\n", hm);
763afd590d9SVladimir Kondratyev 
764afd590d9SVladimir Kondratyev 	hm->cb_state = HIDMAP_CB_IS_ATTACHING;
765afd590d9SVladimir Kondratyev 
766afd590d9SVladimir Kondratyev 	hm->hid_items = malloc(hm->nhid_items * sizeof(struct hid_item),
767afd590d9SVladimir Kondratyev 	    M_DEVBUF, M_WAITOK | M_ZERO);
768afd590d9SVladimir Kondratyev 
769afd590d9SVladimir Kondratyev 	hidbus_set_intr(hm->dev, hidmap_intr, hm);
770afd590d9SVladimir Kondratyev 	hm->evdev_methods = (struct evdev_methods) {
771afd590d9SVladimir Kondratyev 		.ev_open = &hidmap_ev_open,
772afd590d9SVladimir Kondratyev 		.ev_close = &hidmap_ev_close,
773afd590d9SVladimir Kondratyev 	};
774afd590d9SVladimir Kondratyev 
775afd590d9SVladimir Kondratyev 	hm->evdev = evdev_alloc();
776afd590d9SVladimir Kondratyev 	evdev_set_name(hm->evdev, device_get_desc(hm->dev));
777afd590d9SVladimir Kondratyev 	evdev_set_phys(hm->evdev, device_get_nameunit(hm->dev));
778afd590d9SVladimir Kondratyev 	evdev_set_id(hm->evdev, hw->idBus, hw->idVendor, hw->idProduct,
779afd590d9SVladimir Kondratyev 	    hw->idVersion);
780afd590d9SVladimir Kondratyev 	evdev_set_serial(hm->evdev, hw->serial);
781afd590d9SVladimir Kondratyev 	evdev_set_flag(hm->evdev, EVDEV_FLAG_EXT_EPOCH); /* hidbus child */
782afd590d9SVladimir Kondratyev 	evdev_support_event(hm->evdev, EV_SYN);
783afd590d9SVladimir Kondratyev 	error = hidmap_parse_hid_descr(hm, hidbus_get_index(hm->dev));
784afd590d9SVladimir Kondratyev 	if (error) {
785afd590d9SVladimir Kondratyev 		DPRINTF(hm, "error=%d\n", error);
786afd590d9SVladimir Kondratyev 		hidmap_detach(hm);
787afd590d9SVladimir Kondratyev 		return (ENXIO);
788afd590d9SVladimir Kondratyev 	}
789afd590d9SVladimir Kondratyev 
790afd590d9SVladimir Kondratyev 	evdev_set_methods(hm->evdev, hm->dev, &hm->evdev_methods);
791afd590d9SVladimir Kondratyev 	hm->cb_state = HIDMAP_CB_IS_RUNNING;
792afd590d9SVladimir Kondratyev 
793afd590d9SVladimir Kondratyev 	error = evdev_register(hm->evdev);
794afd590d9SVladimir Kondratyev 	if (error) {
795afd590d9SVladimir Kondratyev 		DPRINTF(hm, "error=%d\n", error);
796afd590d9SVladimir Kondratyev 		hidmap_detach(hm);
797afd590d9SVladimir Kondratyev 		return (ENXIO);
798afd590d9SVladimir Kondratyev 	}
799afd590d9SVladimir Kondratyev 
800afd590d9SVladimir Kondratyev 	return (0);
801afd590d9SVladimir Kondratyev }
802afd590d9SVladimir Kondratyev 
803afd590d9SVladimir Kondratyev int
hidmap_detach(struct hidmap * hm)804afd590d9SVladimir Kondratyev hidmap_detach(struct hidmap* hm)
805afd590d9SVladimir Kondratyev {
806afd590d9SVladimir Kondratyev 	struct hidmap_hid_item *hi;
807afd590d9SVladimir Kondratyev 
808afd590d9SVladimir Kondratyev 	DPRINTFN(hm, 11, "\n");
809afd590d9SVladimir Kondratyev 
810afd590d9SVladimir Kondratyev 	hm->cb_state = HIDMAP_CB_IS_DETACHING;
811afd590d9SVladimir Kondratyev 
812afd590d9SVladimir Kondratyev 	evdev_free(hm->evdev);
813afd590d9SVladimir Kondratyev 	if (hm->hid_items != NULL) {
814afd590d9SVladimir Kondratyev 		for (hi = hm->hid_items;
815afd590d9SVladimir Kondratyev 		     hi < hm->hid_items + hm->nhid_items;
816afd590d9SVladimir Kondratyev 		     hi++)
817afd590d9SVladimir Kondratyev 			if (hi->type == HIDMAP_TYPE_FINALCB ||
818afd590d9SVladimir Kondratyev 			    hi->type == HIDMAP_TYPE_CALLBACK)
819afd590d9SVladimir Kondratyev 				hi->cb(hm, hi, (union hidmap_cb_ctx){});
820afd590d9SVladimir Kondratyev 			else if (hi->type == HIDMAP_TYPE_ARR_LIST)
821afd590d9SVladimir Kondratyev 				free(hi->codes, M_DEVBUF);
822afd590d9SVladimir Kondratyev 		free(hm->hid_items, M_DEVBUF);
823afd590d9SVladimir Kondratyev 	}
824afd590d9SVladimir Kondratyev 
825afd590d9SVladimir Kondratyev 	free(hm->key_press, M_DEVBUF);
826afd590d9SVladimir Kondratyev 	free(hm->key_rel, M_DEVBUF);
827afd590d9SVladimir Kondratyev 
828afd590d9SVladimir Kondratyev 	return (0);
829afd590d9SVladimir Kondratyev }
830afd590d9SVladimir Kondratyev 
831afd590d9SVladimir Kondratyev MODULE_DEPEND(hidmap, hid, 1, 1, 1);
832afd590d9SVladimir Kondratyev MODULE_DEPEND(hidmap, hidbus, 1, 1, 1);
833afd590d9SVladimir Kondratyev MODULE_DEPEND(hidmap, evdev, 1, 1, 1);
834afd590d9SVladimir Kondratyev MODULE_VERSION(hidmap, 1);
835