The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/dev/hid/hidmap.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

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

Cache object: 33a068968896ca779a5f8a0aba5a9770


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.