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