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/hidmt.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 /* $OpenBSD: hidmt.c,v 1.13 2022/10/16 20:17:08 bru Exp $ */
    2 /*
    3  * HID multitouch driver for devices conforming to Windows Precision Touchpad
    4  * standard
    5  *
    6  * https://msdn.microsoft.com/en-us/library/windows/hardware/dn467314%28v=vs.85%29.aspx
    7  * https://docs.microsoft.com/en-us/windows-hardware/design/component-guidelines/touchscreen-packet-reporting-modes
    8  *
    9  * Copyright (c) 2016 joshua stein <jcs@openbsd.org>
   10  *
   11  * Permission to use, copy, modify, and distribute this software for any
   12  * purpose with or without fee is hereby granted, provided that the above
   13  * copyright notice and this permission notice appear in all copies.
   14  *
   15  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
   16  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
   17  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
   18  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
   19  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
   20  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
   21  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
   22  */
   23 
   24 #include <sys/param.h>
   25 #include <sys/systm.h>
   26 #include <sys/kernel.h>
   27 #include <sys/device.h>
   28 #include <sys/ioctl.h>
   29 #include <sys/malloc.h>
   30 
   31 #include <dev/wscons/wsconsio.h>
   32 #include <dev/wscons/wsmousevar.h>
   33 
   34 #include <dev/hid/hid.h>
   35 #include <dev/hid/hidmtvar.h>
   36 
   37 /* #define HIDMT_DEBUG */
   38 
   39 #ifdef HIDMT_DEBUG
   40 #define DPRINTF(x) printf x
   41 #else
   42 #define DPRINTF(x)
   43 #endif
   44 
   45 #define HID_UNIT_CM     0x11
   46 #define HID_UNIT_INCH   0x13
   47 
   48 /*
   49  * Calculate the horizontal or vertical resolution, in device units per
   50  * millimeter.
   51  *
   52  * With the length unit specified by the descriptor (centimeter or inch),
   53  * the result is:
   54  *     (logical_maximum - logical_minimum) / ((physical_maximum -
   55  *         physical_minimum) * 10^unit_exponent)
   56  *
   57  * The descriptors should encode the unit exponent as a signed half-byte.
   58  * However, this function accepts the values from -8 to -1 in both the
   59  * 4-bit format and the usual encoding.  Other values beyond the 4-bit
   60  * range are treated as undefined.  Possibly a misinterpretation of
   61  * section 6.2.2.7 of the HID specification (v1.11) has been turned into
   62  * a standard here, see (from www.usb.org)
   63  *     HUTRR39: "HID Sensor Usage Tables", sect. 3.9, 3.10, 4.2.1
   64  * for an official exegesis and
   65  *     https://patchwork.kernel.org/patch/3033191
   66  * for details and a different view.
   67  */
   68 int
   69 hidmt_get_resolution(struct hid_item *h)
   70 {
   71         int log_extent, phy_extent, exponent;
   72 
   73         if (h->unit != HID_UNIT_CM && h->unit != HID_UNIT_INCH)
   74                 return (0);
   75 
   76         log_extent = h->logical_maximum - h->logical_minimum;
   77         phy_extent = h->physical_maximum - h->physical_minimum;
   78         if (log_extent <= 0 || phy_extent <= 0)
   79                 return (0);
   80 
   81         exponent = h->unit_exponent;
   82         if (exponent < -8 || exponent > 15)             /* See above. */
   83                 return (0);
   84         if (exponent > 7)
   85                 exponent -= 16;
   86 
   87         for (; exponent < 0 && log_extent <= INT_MAX / 10; exponent++)
   88                 log_extent *= 10;
   89         for (; exponent > 0 && phy_extent <= INT_MAX / 10; exponent--)
   90                 phy_extent *= 10;
   91         if (exponent != 0)
   92                 return (0);
   93 
   94         if (h->unit == HID_UNIT_INCH) {                 /* Map inches to mm. */
   95                 if ((phy_extent > INT_MAX / 127)
   96                     || (log_extent > INT_MAX / 5))
   97                         return (0);
   98                 log_extent *= 5;
   99                 phy_extent *= 127;
  100         } else {                                        /* Map cm to mm. */
  101                 if (phy_extent > INT_MAX / 10)
  102                         return (0);
  103                 phy_extent *= 10;
  104         }
  105 
  106         return (log_extent / phy_extent);
  107 }
  108 
  109 int
  110 hidmt_setup(struct device *self, struct hidmt *mt, void *desc, int dlen)
  111 {
  112         struct hid_location cap;
  113         int32_t d;
  114         uint8_t *rep;
  115         int capsize;
  116 
  117         struct hid_data *hd;
  118         struct hid_item h;
  119 
  120         mt->sc_device = self;
  121         mt->sc_rep_input_size = hid_report_size(desc, dlen, hid_input,
  122             mt->sc_rep_input);
  123 
  124         mt->sc_minx = mt->sc_miny = mt->sc_maxx = mt->sc_maxy = 0;
  125 
  126         capsize = hid_report_size(desc, dlen, hid_feature, mt->sc_rep_cap);
  127         rep = malloc(capsize, M_DEVBUF, M_NOWAIT | M_ZERO);
  128 
  129         if (mt->hidev_report_type_conv == NULL)
  130                 panic("no report type conversion function");
  131 
  132         if (mt->hidev_get_report(mt->sc_device,
  133             mt->hidev_report_type_conv(hid_feature), mt->sc_rep_cap,
  134             rep, capsize)) {
  135                 printf("\n%s: failed getting capability report\n",
  136                     self->dv_xname);
  137                 return 1;
  138         }
  139 
  140         /* find maximum number of contacts being reported per input report */
  141         mt->sc_num_contacts = HIDMT_MAX_CONTACTS;
  142         if (hid_locate(desc, dlen, HID_USAGE2(HUP_DIGITIZERS, HUD_CONTACT_MAX),
  143             mt->sc_rep_cap, hid_feature, &cap, NULL)) {
  144                 d = hid_get_udata(rep, capsize, &cap);
  145                 if (d > HIDMT_MAX_CONTACTS)
  146                         printf("\n%s: contacts %d > max %d\n", self->dv_xname,
  147                             d, HIDMT_MAX_CONTACTS);
  148                 else
  149                         mt->sc_num_contacts = d;
  150         }
  151 
  152         /* find whether this is a clickpad or not */
  153         if (hid_locate(desc, dlen, HID_USAGE2(HUP_DIGITIZERS, HUD_BUTTON_TYPE),
  154             mt->sc_rep_cap, hid_feature, &cap, NULL)) {
  155                 d = hid_get_udata(rep, capsize, &cap);
  156                 mt->sc_clickpad = (d == 0);
  157         } else if (hid_locate(desc, dlen, HID_USAGE2(HUP_BUTTON, 1),
  158             mt->sc_rep_input, hid_input, &cap, NULL) ||
  159             !hid_locate(desc, dlen, HID_USAGE2(HUP_BUTTON, 2),
  160             mt->sc_rep_input, hid_input, &cap, NULL) ||
  161             !hid_locate(desc, dlen, HID_USAGE2(HUP_BUTTON, 3),
  162             mt->sc_rep_input, hid_input, &cap, NULL)) {
  163                 mt->sc_clickpad = 1;
  164         }
  165         /*
  166          * Walk HID descriptor and store usages we care about to know what to
  167          * pluck out of input reports.
  168          */
  169 
  170         SIMPLEQ_INIT(&mt->sc_inputs);
  171 
  172         hd = hid_start_parse(desc, dlen, hid_input);
  173         while (hid_get_item(hd, &h)) {
  174                 struct hidmt_data *input;
  175 
  176                 if (h.report_ID != mt->sc_rep_input)
  177                         continue;
  178                 if (h.kind != hid_input)
  179                         continue;
  180 
  181                 switch (h.usage) {
  182                 /* contact level usages */
  183                 case HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_X):
  184                         if (h.logical_maximum - h.logical_minimum) {
  185                                 mt->sc_minx = h.logical_minimum;
  186                                 mt->sc_maxx = h.logical_maximum;
  187                                 mt->sc_resx = hidmt_get_resolution(&h);
  188                         }
  189                         break;
  190                 case HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Y):
  191                         if (h.logical_maximum - h.logical_minimum) {
  192                                 mt->sc_miny = h.logical_minimum;
  193                                 mt->sc_maxy = h.logical_maximum;
  194                                 mt->sc_resy = hidmt_get_resolution(&h);
  195                         }
  196                         break;
  197                 case HID_USAGE2(HUP_DIGITIZERS, HUD_TIP_SWITCH):
  198                 case HID_USAGE2(HUP_DIGITIZERS, HUD_CONFIDENCE):
  199                 case HID_USAGE2(HUP_DIGITIZERS, HUD_WIDTH):
  200                 case HID_USAGE2(HUP_DIGITIZERS, HUD_HEIGHT):
  201                 case HID_USAGE2(HUP_DIGITIZERS, HUD_CONTACTID):
  202 
  203                 /* report level usages */
  204                 case HID_USAGE2(HUP_DIGITIZERS, HUD_CONTACTCOUNT):
  205                 case HID_USAGE2(HUP_BUTTON, 0x01):
  206                 case HID_USAGE2(HUP_BUTTON, 0x02):
  207                 case HID_USAGE2(HUP_BUTTON, 0x03):
  208                         break;
  209                 default:
  210                         continue;
  211                 }
  212 
  213                 input = malloc(sizeof(*input), M_DEVBUF, M_NOWAIT | M_ZERO);
  214                 memcpy(&input->loc, &h.loc, sizeof(struct hid_location));
  215                 input->usage = h.usage;
  216 
  217                 SIMPLEQ_INSERT_TAIL(&mt->sc_inputs, input, entry);
  218         }
  219         hid_end_parse(hd);
  220 
  221         if (mt->sc_maxx <= 0 || mt->sc_maxy <= 0) {
  222                 printf("\n%s: invalid max X/Y %d/%d\n", self->dv_xname,
  223                     mt->sc_maxx, mt->sc_maxy);
  224                 return 1;
  225         }
  226 
  227         if (hidmt_set_input_mode(mt, HIDMT_INPUT_MODE_MT_TOUCHPAD)) {
  228                 printf("\n%s: switch to multitouch mode failed\n",
  229                     self->dv_xname);
  230                 return 1;
  231         }
  232 
  233         return 0;
  234 }
  235 
  236 void
  237 hidmt_configure(struct hidmt *mt)
  238 {
  239         struct wsmousehw *hw;
  240 
  241         if (mt->sc_wsmousedev == NULL)
  242                 return;
  243 
  244         hw = wsmouse_get_hw(mt->sc_wsmousedev);
  245         hw->type = WSMOUSE_TYPE_TOUCHPAD;
  246         hw->hw_type = (mt->sc_clickpad
  247             ? WSMOUSEHW_CLICKPAD : WSMOUSEHW_TOUCHPAD);
  248         hw->x_min = mt->sc_minx;
  249         hw->x_max = mt->sc_maxx;
  250         hw->y_min = mt->sc_miny;
  251         hw->y_max = mt->sc_maxy;
  252         hw->h_res = mt->sc_resx;
  253         hw->v_res = mt->sc_resy;
  254         hw->mt_slots = HIDMT_MAX_CONTACTS;
  255 
  256         wsmouse_configure(mt->sc_wsmousedev, NULL, 0);
  257 }
  258 
  259 void
  260 hidmt_attach(struct hidmt *mt, const struct wsmouse_accessops *ops)
  261 {
  262         struct wsmousedev_attach_args a;
  263 
  264         printf(": %spad, %d contact%s\n",
  265             (mt->sc_clickpad ? "click" : "touch"), mt->sc_num_contacts,
  266             (mt->sc_num_contacts == 1 ? "" : "s"));
  267 
  268         a.accessops = ops;
  269         a.accesscookie = mt->sc_device;
  270         mt->sc_wsmousedev = config_found(mt->sc_device, &a, wsmousedevprint);
  271         hidmt_configure(mt);
  272 }
  273 
  274 int
  275 hidmt_detach(struct hidmt *mt, int flags)
  276 {
  277         int rv = 0;
  278 
  279         if (mt->sc_wsmousedev != NULL)
  280                 rv = config_detach(mt->sc_wsmousedev, flags);
  281 
  282         return (rv);
  283 }
  284 
  285 int
  286 hidmt_set_input_mode(struct hidmt *mt, uint16_t mode)
  287 {
  288         if (mt->hidev_report_type_conv == NULL)
  289                 panic("no report type conversion function");
  290 
  291         return mt->hidev_set_report(mt->sc_device,
  292             mt->hidev_report_type_conv(hid_feature),
  293             mt->sc_rep_config, &mode, sizeof(mode));
  294 }
  295 
  296 void
  297 hidmt_input(struct hidmt *mt, uint8_t *data, u_int len)
  298 {
  299         struct hidmt_data *hi;
  300         struct hidmt_contact hc;
  301         int32_t d, firstu = 0;
  302         int contactcount = 0, seencontacts = 0, tips = 0, buttons = 0, i, s, z;
  303 
  304         if (len != mt->sc_rep_input_size) {
  305                 DPRINTF(("%s: %s: length %d not %d, ignoring\n",
  306                     mt->sc_device->dv_xname, __func__, len,
  307                     mt->sc_rep_input_size));
  308                 return;
  309         }
  310 
  311         /*
  312          * "In Parallel mode, devices report all contact information in a
  313          * single packet. Each physical contact is represented by a logical
  314          * collection that is embedded in the top-level collection."
  315          *
  316          * Since additional contacts that were not present will still be in the
  317          * report with contactid=0 but contactids are zero-based, find
  318          * contactcount first.
  319          */
  320         SIMPLEQ_FOREACH(hi, &mt->sc_inputs, entry) {
  321                 if (hi->usage == HID_USAGE2(HUP_DIGITIZERS, HUD_CONTACTCOUNT))
  322                         contactcount = hid_get_udata(data, len, &hi->loc);
  323         }
  324 
  325         if (contactcount)
  326                 mt->sc_cur_contactcount = contactcount;
  327         else {
  328                 /*
  329                 * "In Hybrid mode, the number of contacts that can be reported
  330                 * in one report is less than the maximum number of contacts
  331                 * that the device supports. For example, a device that supports
  332                 * a maximum of 4 concurrent physical contacts, can set up its
  333                 * top-level collection to deliver a maximum of two contacts in
  334                 * one report. If four contact points are present, the device
  335                 * can break these up into two serial reports that deliver two
  336                 * contacts each.
  337                 *
  338                 * "When a device delivers data in this manner, the Contact
  339                 * Count usage value in the first report should reflect the
  340                 * total number of contacts that are being delivered in the
  341                 * hybrid reports. The other serial reports should have a
  342                 * contact count of zero (0)."
  343                 */
  344                 contactcount = mt->sc_cur_contactcount;
  345         }
  346 
  347         if (!contactcount) {
  348                 DPRINTF(("%s: %s: no contactcount in report\n",
  349                     mt->sc_device->dv_xname, __func__));
  350                 return;
  351         }
  352 
  353         /*
  354          * Walk through each input we know about and fetch its data from the
  355          * report, storing it in a temporary contact.  Once we see our first
  356          * usage again, we'll know we saw all usages being presented for that
  357          * contact.
  358          */
  359         bzero(&hc, sizeof(struct hidmt_contact));
  360         SIMPLEQ_FOREACH(hi, &mt->sc_inputs, entry) {
  361                 d = hid_get_udata(data, len, &hi->loc);
  362 
  363                 if (firstu && hi->usage == firstu) {
  364                         if (seencontacts < contactcount) {
  365                                 hc.seen = 1;
  366                                 i = wsmouse_id_to_slot(
  367                                     mt->sc_wsmousedev, hc.contactid);
  368                                 if (i >= 0)
  369                                         memcpy(&mt->sc_contacts[i], &hc,
  370                                             sizeof(struct hidmt_contact));
  371                                 seencontacts++;
  372                         }
  373 
  374                         bzero(&hc, sizeof(struct hidmt_contact));
  375                 }
  376                 else if (!firstu)
  377                         firstu = hi->usage;
  378 
  379                 switch (hi->usage) {
  380                 case HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_X):
  381                         hc.x = d;
  382                         break;
  383                 case HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Y):
  384                         if (mt->sc_flags & HIDMT_REVY)
  385                                 hc.y = mt->sc_maxy - d;
  386                         else
  387                                 hc.y = d;
  388                         break;
  389                 case HID_USAGE2(HUP_DIGITIZERS, HUD_TIP_SWITCH):
  390                         hc.tip = d;
  391                         if (d)
  392                                 tips++;
  393                         break;
  394                 case HID_USAGE2(HUP_DIGITIZERS, HUD_CONFIDENCE):
  395                         hc.confidence = d;
  396                         break;
  397                 case HID_USAGE2(HUP_DIGITIZERS, HUD_WIDTH):
  398                         hc.width = d;
  399                         break;
  400                 case HID_USAGE2(HUP_DIGITIZERS, HUD_HEIGHT):
  401                         hc.height = d;
  402                         break;
  403                 case HID_USAGE2(HUP_DIGITIZERS, HUD_CONTACTID):
  404                         hc.contactid = d;
  405                         break;
  406 
  407                 /* these will only appear once per report */
  408                 case HID_USAGE2(HUP_DIGITIZERS, HUD_CONTACTCOUNT):
  409                         if (d)
  410                                 contactcount = d;
  411                         break;
  412                 case HID_USAGE2(HUP_BUTTON, 0x01):
  413                 case HID_USAGE2(HUP_BUTTON, 0x02):
  414                         if (d != 0)
  415                                 buttons |= 1;
  416                         break;
  417                 case HID_USAGE2(HUP_BUTTON, 0x03):
  418                         if (d != 0)
  419                                 buttons |= 1 << 2;
  420                         break;
  421                 }
  422         }
  423         if (seencontacts < contactcount) {
  424                 hc.seen = 1;
  425                 i = wsmouse_id_to_slot(mt->sc_wsmousedev, hc.contactid);
  426                 if (i >= 0)
  427                         memcpy(&mt->sc_contacts[i], &hc,
  428                             sizeof(struct hidmt_contact));
  429                 seencontacts++;
  430         }
  431 
  432         s = spltty();
  433         if (mt->sc_buttons != buttons) {
  434                 wsmouse_buttons(mt->sc_wsmousedev, buttons);
  435                 mt->sc_buttons = buttons;
  436         }
  437         for (i = 0; i < HIDMT_MAX_CONTACTS; i++) {
  438                 if (!mt->sc_contacts[i].seen)
  439                         continue;
  440 
  441                 mt->sc_contacts[i].seen = 0;
  442 
  443                 DPRINTF(("%s: %s: contact %d of %d: id %d, x %d, y %d, "
  444                     "touch %d, confidence %d, width %d, height %d "
  445                     "(button 0x%x)\n",
  446                     mt->sc_device->dv_xname, __func__,
  447                     i + 1, contactcount,
  448                     mt->sc_contacts[i].contactid,
  449                     mt->sc_contacts[i].x,
  450                     mt->sc_contacts[i].y,
  451                     mt->sc_contacts[i].tip,
  452                     mt->sc_contacts[i].confidence,
  453                     mt->sc_contacts[i].width,
  454                     mt->sc_contacts[i].height,
  455                     mt->sc_buttons));
  456 
  457                 if (mt->sc_contacts[i].tip && !mt->sc_contacts[i].confidence)
  458                         continue;
  459 
  460                 /* Report width as pressure. */
  461                 z = (mt->sc_contacts[i].tip
  462                     ? imax(mt->sc_contacts[i].width, 50) : 0);
  463 
  464                 wsmouse_mtstate(mt->sc_wsmousedev,
  465                     i, mt->sc_contacts[i].x, mt->sc_contacts[i].y, z);
  466         }
  467         wsmouse_input_sync(mt->sc_wsmousedev);
  468 
  469         splx(s);
  470 }
  471 
  472 int
  473 hidmt_enable(struct hidmt *mt)
  474 {
  475         if (mt->sc_enabled)
  476                 return EBUSY;
  477 
  478         mt->sc_enabled = 1;
  479 
  480         return 0;
  481 }
  482 
  483 int
  484 hidmt_ioctl(struct hidmt *mt, u_long cmd, caddr_t data, int flag,
  485     struct proc *p)
  486 {
  487         struct wsmouse_calibcoords *wsmc = (struct wsmouse_calibcoords *)data;
  488         int wsmode;
  489 
  490         switch (cmd) {
  491         case WSMOUSEIO_GTYPE: {
  492                 struct wsmousehw *hw = wsmouse_get_hw(mt->sc_wsmousedev);
  493                 *(u_int *)data = hw->type;
  494                 break;
  495         }
  496 
  497         case WSMOUSEIO_GCALIBCOORDS:
  498                 wsmc->minx = mt->sc_minx;
  499                 wsmc->maxx = mt->sc_maxx;
  500                 wsmc->miny = mt->sc_miny;
  501                 wsmc->maxy = mt->sc_maxy;
  502                 wsmc->swapxy = 0;
  503                 wsmc->resx = mt->sc_resx;
  504                 wsmc->resy = mt->sc_resy;
  505                 break;
  506 
  507         case WSMOUSEIO_SETMODE:
  508                 wsmode = *(u_int *)data;
  509                 if (wsmode != WSMOUSE_COMPAT && wsmode != WSMOUSE_NATIVE) {
  510                         printf("%s: invalid mode %d\n",
  511                             mt->sc_device->dv_xname, wsmode);
  512                         return (EINVAL);
  513                 }
  514 
  515                 DPRINTF(("%s: changing mode to %s\n", mt->sc_device->dv_xname,
  516                     (wsmode == WSMOUSE_COMPAT ? "compat" : "native")));
  517 
  518                 wsmouse_set_mode(mt->sc_wsmousedev, wsmode);
  519 
  520                 break;
  521 
  522         default:
  523                 return -1;
  524         }
  525 
  526         return 0;
  527 }
  528 
  529 void
  530 hidmt_disable(struct hidmt *mt)
  531 {
  532         mt->sc_enabled = 0;
  533 }

Cache object: e8b68b2e701aa5c085e2b4b4fb585854


[ 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.