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/ietp.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, 2022 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 /*
   29  * Elan I2C Touchpad driver. Based on Linux driver.
   30  * https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/drivers/input/mouse/elan_i2c_core.c
   31  */
   32 
   33 #include <sys/cdefs.h>
   34 __FBSDID("$FreeBSD$");
   35 
   36 #include <sys/param.h>
   37 #include <sys/bus.h>
   38 #include <sys/endian.h>
   39 #include <sys/kernel.h>
   40 #include <sys/lock.h>
   41 #include <sys/malloc.h>
   42 #include <sys/module.h>
   43 #include <sys/mutex.h>
   44 #include <sys/sysctl.h>
   45 #include <sys/systm.h>
   46 
   47 #include <dev/evdev/evdev.h>
   48 #include <dev/evdev/input.h>
   49 
   50 #include <dev/iicbus/iic.h>
   51 #include <dev/iicbus/iicbus.h>
   52 
   53 #define HID_DEBUG_VAR   ietp_debug
   54 #include <dev/hid/hid.h>
   55 #include <dev/hid/hidbus.h>
   56 #include <dev/hid/hidquirk.h>
   57 
   58 #ifdef HID_DEBUG
   59 static SYSCTL_NODE(_hw_hid, OID_AUTO, ietp, CTLFLAG_RW, 0,
   60     "Elantech Touchpad");
   61 static int ietp_debug = 1;
   62 SYSCTL_INT(_hw_hid_ietp, OID_AUTO, debug, CTLFLAG_RWTUN,
   63     &ietp_debug, 1, "Debug level");
   64 #endif
   65 
   66 #define IETP_PATTERN            0x0100
   67 #define IETP_UNIQUEID           0x0101
   68 #define IETP_FW_VERSION         0x0102
   69 #define IETP_IC_TYPE            0x0103
   70 #define IETP_OSM_VERSION        0x0103
   71 #define IETP_NSM_VERSION        0x0104
   72 #define IETP_TRACENUM           0x0105
   73 #define IETP_MAX_X_AXIS         0x0106
   74 #define IETP_MAX_Y_AXIS         0x0107
   75 #define IETP_RESOLUTION         0x0108
   76 #define IETP_PRESSURE           0x010A
   77 
   78 #define IETP_CONTROL            0x0300
   79 #define IETP_CTRL_ABSOLUTE      0x0001
   80 #define IETP_CTRL_STANDARD      0x0000
   81 
   82 #define IETP_REPORT_LEN_LO      32
   83 #define IETP_REPORT_LEN_HI      37
   84 #define IETP_MAX_FINGERS        5
   85 
   86 #define IETP_REPORT_ID_LO       0x5D
   87 #define IETP_REPORT_ID_HI       0x60
   88 
   89 #define IETP_TOUCH_INFO         1
   90 #define IETP_FINGER_DATA        2
   91 #define IETP_FINGER_DATA_LEN    5
   92 #define IETP_HOVER_INFO         28
   93 #define IETP_WH_DATA            31
   94 
   95 #define IETP_TOUCH_LMB          (1 << 0)
   96 #define IETP_TOUCH_RMB          (1 << 1)
   97 #define IETP_TOUCH_MMB          (1 << 2)
   98 
   99 #define IETP_MAX_PRESSURE       255
  100 #define IETP_FWIDTH_REDUCE      90
  101 #define IETP_FINGER_MAX_WIDTH   15
  102 #define IETP_PRESSURE_BASE      25
  103 
  104 struct ietp_softc {
  105         device_t                dev;
  106 
  107         struct evdev_dev        *evdev;
  108         uint8_t                 report_id;
  109         hid_size_t              report_len;
  110 
  111         uint16_t                product_id;
  112         uint16_t                ic_type;
  113 
  114         int32_t                 pressure_base;
  115         uint16_t                max_x;
  116         uint16_t                max_y;
  117         uint16_t                trace_x;
  118         uint16_t                trace_y;
  119         uint16_t                res_x;          /* dots per mm */
  120         uint16_t                res_y;
  121         bool                    hi_precission;
  122         bool                    is_clickpad;
  123         bool                    has_3buttons;
  124 };
  125 
  126 static evdev_open_t     ietp_ev_open;
  127 static evdev_close_t    ietp_ev_close;
  128 static hid_intr_t       ietp_intr;
  129 
  130 static int              ietp_probe(struct ietp_softc *);
  131 static int              ietp_attach(struct ietp_softc *);
  132 static int              ietp_detach(struct ietp_softc *);
  133 static int32_t          ietp_res2dpmm(uint8_t, bool);
  134 
  135 static device_probe_t   ietp_iic_probe;
  136 static device_attach_t  ietp_iic_attach;
  137 static device_detach_t  ietp_iic_detach;
  138 static device_resume_t  ietp_iic_resume;
  139 
  140 static int              ietp_iic_read_reg(device_t, uint16_t, size_t, void *);
  141 static int              ietp_iic_write_reg(device_t, uint16_t, uint16_t);
  142 static int              ietp_iic_set_absolute_mode(device_t, bool);
  143 
  144 #define IETP_IIC_DEV(pnp) \
  145     { HID_TLC(HUP_GENERIC_DESKTOP, HUG_MOUSE), HID_BUS(BUS_I2C), HID_PNP(pnp) }
  146 
  147 static const struct hid_device_id ietp_iic_devs[] = {
  148         IETP_IIC_DEV("ELAN0000"),
  149         IETP_IIC_DEV("ELAN0100"),
  150         IETP_IIC_DEV("ELAN0600"),
  151         IETP_IIC_DEV("ELAN0601"),
  152         IETP_IIC_DEV("ELAN0602"),
  153         IETP_IIC_DEV("ELAN0603"),
  154         IETP_IIC_DEV("ELAN0604"),
  155         IETP_IIC_DEV("ELAN0605"),
  156         IETP_IIC_DEV("ELAN0606"),
  157         IETP_IIC_DEV("ELAN0607"),
  158         IETP_IIC_DEV("ELAN0608"),
  159         IETP_IIC_DEV("ELAN0609"),
  160         IETP_IIC_DEV("ELAN060B"),
  161         IETP_IIC_DEV("ELAN060C"),
  162         IETP_IIC_DEV("ELAN060F"),
  163         IETP_IIC_DEV("ELAN0610"),
  164         IETP_IIC_DEV("ELAN0611"),
  165         IETP_IIC_DEV("ELAN0612"),
  166         IETP_IIC_DEV("ELAN0615"),
  167         IETP_IIC_DEV("ELAN0616"),
  168         IETP_IIC_DEV("ELAN0617"),
  169         IETP_IIC_DEV("ELAN0618"),
  170         IETP_IIC_DEV("ELAN0619"),
  171         IETP_IIC_DEV("ELAN061A"),
  172         IETP_IIC_DEV("ELAN061B"),
  173         IETP_IIC_DEV("ELAN061C"),
  174         IETP_IIC_DEV("ELAN061D"),
  175         IETP_IIC_DEV("ELAN061E"),
  176         IETP_IIC_DEV("ELAN061F"),
  177         IETP_IIC_DEV("ELAN0620"),
  178         IETP_IIC_DEV("ELAN0621"),
  179         IETP_IIC_DEV("ELAN0622"),
  180         IETP_IIC_DEV("ELAN0623"),
  181         IETP_IIC_DEV("ELAN0624"),
  182         IETP_IIC_DEV("ELAN0625"),
  183         IETP_IIC_DEV("ELAN0626"),
  184         IETP_IIC_DEV("ELAN0627"),
  185         IETP_IIC_DEV("ELAN0628"),
  186         IETP_IIC_DEV("ELAN0629"),
  187         IETP_IIC_DEV("ELAN062A"),
  188         IETP_IIC_DEV("ELAN062B"),
  189         IETP_IIC_DEV("ELAN062C"),
  190         IETP_IIC_DEV("ELAN062D"),
  191         IETP_IIC_DEV("ELAN062E"),       /* Lenovo V340 Whiskey Lake U */
  192         IETP_IIC_DEV("ELAN062F"),       /* Lenovo V340 Comet Lake U */
  193         IETP_IIC_DEV("ELAN0631"),
  194         IETP_IIC_DEV("ELAN0632"),
  195         IETP_IIC_DEV("ELAN0633"),       /* Lenovo S145 */
  196         IETP_IIC_DEV("ELAN0634"),       /* Lenovo V340 Ice lake */
  197         IETP_IIC_DEV("ELAN0635"),       /* Lenovo V1415-IIL */
  198         IETP_IIC_DEV("ELAN0636"),       /* Lenovo V1415-Dali */
  199         IETP_IIC_DEV("ELAN0637"),       /* Lenovo V1415-IGLR */
  200         IETP_IIC_DEV("ELAN1000"),
  201 };
  202 
  203 static const struct evdev_methods ietp_evdev_methods = {
  204         .ev_open = &ietp_ev_open,
  205         .ev_close = &ietp_ev_close,
  206 };
  207 
  208 static int
  209 ietp_ev_open(struct evdev_dev *evdev)
  210 {
  211         return (hidbus_intr_start(evdev_get_softc(evdev)));
  212 }
  213 
  214 static int
  215 ietp_ev_close(struct evdev_dev *evdev)
  216 {
  217         return (hidbus_intr_stop(evdev_get_softc(evdev)));
  218 }
  219 
  220 static int
  221 ietp_probe(struct ietp_softc *sc)
  222 {
  223         if (hidbus_find_child(device_get_parent(sc->dev),
  224             HID_USAGE2(HUP_DIGITIZERS, HUD_TOUCHPAD)) != NULL) {
  225                 DPRINTFN(5, "Ignore HID-compatible touchpad on %s\n",
  226                     device_get_nameunit(device_get_parent(sc->dev)));
  227                 return (ENXIO);
  228         }
  229 
  230         device_set_desc(sc->dev, "Elan Touchpad");
  231 
  232         return (BUS_PROBE_DEFAULT);
  233 }
  234 
  235 static int
  236 ietp_attach(struct ietp_softc *sc)
  237 {
  238         const struct hid_device_info *hw = hid_get_device_info(sc->dev);
  239         void *d_ptr;
  240         hid_size_t d_len;
  241         int32_t minor, major;
  242         int error;
  243 
  244         sc->report_id = sc->hi_precission ?
  245             IETP_REPORT_ID_HI : IETP_REPORT_ID_LO;
  246         sc->report_len = sc->hi_precission ?
  247             IETP_REPORT_LEN_HI : IETP_REPORT_LEN_LO;
  248 
  249         /* Try to detect 3-rd button by relative mouse TLC */
  250         if (!sc->is_clickpad) {
  251                 error = hid_get_report_descr(sc->dev, &d_ptr, &d_len);
  252                 if (error != 0) {
  253                         device_printf(sc->dev, "could not retrieve report "
  254                             "descriptor from device: %d\n", error);
  255                         return (ENXIO);
  256                 }
  257                 if (hidbus_locate(d_ptr, d_len, HID_USAGE2(HUP_BUTTON, 3),
  258                     hid_input, hidbus_get_index(sc->dev), 0, NULL, NULL, NULL,
  259                     NULL))
  260                         sc->has_3buttons = true;
  261         }
  262 
  263         sc->evdev = evdev_alloc();
  264         evdev_set_name(sc->evdev, device_get_desc(sc->dev));
  265         evdev_set_phys(sc->evdev, device_get_nameunit(sc->dev));
  266         evdev_set_id(sc->evdev, hw->idBus, hw->idVendor, hw->idProduct,
  267             hw->idVersion);
  268         evdev_set_serial(sc->evdev, hw->serial);
  269         evdev_set_methods(sc->evdev, sc->dev, &ietp_evdev_methods);
  270         evdev_set_flag(sc->evdev, EVDEV_FLAG_MT_STCOMPAT);
  271         evdev_set_flag(sc->evdev, EVDEV_FLAG_EXT_EPOCH); /* hidbus child */
  272 
  273         evdev_support_event(sc->evdev, EV_SYN);
  274         evdev_support_event(sc->evdev, EV_ABS);
  275         evdev_support_event(sc->evdev, EV_KEY);
  276         evdev_support_prop(sc->evdev, INPUT_PROP_POINTER);
  277         evdev_support_key(sc->evdev, BTN_LEFT);
  278         if (sc->is_clickpad) {
  279                 evdev_support_prop(sc->evdev, INPUT_PROP_BUTTONPAD);
  280         } else {
  281                 evdev_support_key(sc->evdev, BTN_RIGHT);
  282                 if (sc->has_3buttons)
  283                         evdev_support_key(sc->evdev, BTN_MIDDLE);
  284         }
  285 
  286         major = IETP_FINGER_MAX_WIDTH * MAX(sc->trace_x, sc->trace_y);
  287         minor = IETP_FINGER_MAX_WIDTH * MIN(sc->trace_x, sc->trace_y);
  288 
  289         evdev_support_abs(sc->evdev, ABS_MT_SLOT,
  290             0, IETP_MAX_FINGERS - 1, 0, 0, 0);
  291         evdev_support_abs(sc->evdev, ABS_MT_TRACKING_ID,
  292             -1, IETP_MAX_FINGERS - 1, 0, 0, 0);
  293         evdev_support_abs(sc->evdev, ABS_MT_POSITION_X,
  294             0, sc->max_x, 0, 0, sc->res_x);
  295         evdev_support_abs(sc->evdev, ABS_MT_POSITION_Y,
  296             0, sc->max_y, 0, 0, sc->res_y);
  297         evdev_support_abs(sc->evdev, ABS_MT_PRESSURE,
  298             0, IETP_MAX_PRESSURE, 0, 0, 0);
  299         evdev_support_abs(sc->evdev, ABS_MT_ORIENTATION, 0, 1, 0, 0, 0);
  300         evdev_support_abs(sc->evdev, ABS_MT_TOUCH_MAJOR, 0, major, 0, 0, 0);
  301         evdev_support_abs(sc->evdev, ABS_MT_TOUCH_MINOR, 0, minor, 0, 0, 0);
  302         evdev_support_abs(sc->evdev, ABS_DISTANCE, 0, 1, 0, 0, 0);
  303 
  304         error = evdev_register(sc->evdev);
  305         if (error != 0) {
  306                 ietp_detach(sc);
  307                 return (ENOMEM);
  308         }
  309 
  310         hidbus_set_intr(sc->dev, ietp_intr, sc);
  311 
  312         device_printf(sc->dev, "[%d:%d], %s\n", sc->max_x, sc->max_y,
  313             sc->is_clickpad ? "clickpad" :
  314             sc->has_3buttons ? "3 buttons" : "2 buttons");
  315 
  316         return (0);
  317 }
  318 
  319 static int
  320 ietp_detach(struct ietp_softc *sc)
  321 {
  322         evdev_free(sc->evdev);
  323 
  324         return (0);
  325 }
  326 
  327 static void
  328 ietp_intr(void *context, void *buf, hid_size_t len)
  329 {
  330         struct ietp_softc *sc = context;
  331         union evdev_mt_slot slot_data;
  332         uint8_t *report, *fdata;
  333         int32_t finger;
  334         int32_t x, y, w, h, wh;
  335 
  336         /* we seem to get 0 length reports sometimes, ignore them */
  337         report = buf;
  338         if (*report != sc->report_id || len < sc->report_len)
  339                 return;
  340 
  341         for (finger = 0, fdata = report + IETP_FINGER_DATA;
  342              finger < IETP_MAX_FINGERS;
  343              finger++, fdata += IETP_FINGER_DATA_LEN) {
  344                 if ((report[IETP_TOUCH_INFO] & (1 << (finger + 3))) != 0) {
  345                         if (sc->hi_precission) {
  346                                 x = fdata[0] << 8 | fdata[1];
  347                                 y = fdata[2] << 8 | fdata[3];
  348                                 wh = report[IETP_WH_DATA + finger];
  349                         } else {
  350                                 x = (fdata[0] & 0xf0) << 4 | fdata[1];
  351                                 y = (fdata[0] & 0x0f) << 8 | fdata[2];
  352                                 wh = fdata[3];
  353                         }
  354 
  355                         if (x > sc->max_x || y > sc->max_y) {
  356                                 DPRINTF("[%d] x=%d y=%d over max (%d, %d)",
  357                                     finger, x, y, sc->max_x, sc->max_y);
  358                                 continue;
  359                         }
  360 
  361                         /* Reduce trace size to not treat large finger as palm */
  362                         w = (wh & 0x0F) * (sc->trace_x - IETP_FWIDTH_REDUCE);
  363                         h = (wh >> 4) * (sc->trace_y - IETP_FWIDTH_REDUCE);
  364 
  365                         slot_data = (union evdev_mt_slot) {
  366                                 .id = finger,
  367                                 .x = x,
  368                                 .y = sc->max_y - y,
  369                                 .p = MIN((int32_t)fdata[4] + sc->pressure_base,
  370                                     IETP_MAX_PRESSURE),
  371                                 .ori = w > h ? 1 : 0,
  372                                 .maj = MAX(w, h),
  373                                 .min = MIN(w, h),
  374                         };
  375                         evdev_mt_push_slot(sc->evdev, finger, &slot_data);
  376                 } else {
  377                         evdev_push_abs(sc->evdev, ABS_MT_SLOT, finger);
  378                         evdev_push_abs(sc->evdev, ABS_MT_TRACKING_ID, -1);
  379                 }
  380         }
  381 
  382         evdev_push_key(sc->evdev, BTN_LEFT,
  383             report[IETP_TOUCH_INFO] & IETP_TOUCH_LMB);
  384         evdev_push_key(sc->evdev, BTN_MIDDLE,
  385             report[IETP_TOUCH_INFO] & IETP_TOUCH_MMB);
  386         evdev_push_key(sc->evdev, BTN_RIGHT,
  387             report[IETP_TOUCH_INFO] & IETP_TOUCH_RMB);
  388         evdev_push_abs(sc->evdev, ABS_DISTANCE,
  389             (report[IETP_HOVER_INFO] & 0x40) >> 6);
  390 
  391         evdev_sync(sc->evdev);
  392 }
  393 
  394 static int32_t
  395 ietp_res2dpmm(uint8_t res, bool hi_precission)
  396 {
  397         int32_t dpi;
  398 
  399         dpi = hi_precission ? 300 + res * 100 : 790 + res * 10;
  400 
  401         return (dpi * 10 /254);
  402 }
  403 
  404 static int
  405 ietp_iic_probe(device_t dev)
  406 {
  407         struct ietp_softc *sc = device_get_softc(dev);
  408         device_t iichid;
  409         int error;
  410 
  411         error = HIDBUS_LOOKUP_DRIVER_INFO(dev, ietp_iic_devs);
  412         if (error != 0)
  413                 return (error);
  414 
  415         iichid = device_get_parent(device_get_parent(dev));
  416         if (device_get_devclass(iichid) != devclass_find("iichid"))
  417                 return (ENXIO);
  418 
  419         sc->dev = dev;
  420 
  421         return (ietp_probe(sc));
  422 }
  423 
  424 static int
  425 ietp_iic_attach(device_t dev)
  426 {
  427         struct ietp_softc *sc = device_get_softc(dev);
  428         uint16_t buf, reg;
  429         uint8_t *buf8;
  430         uint8_t pattern;
  431 
  432         buf8 = (uint8_t *)&buf;
  433 
  434         if (ietp_iic_read_reg(dev, IETP_UNIQUEID, sizeof(buf), &buf) != 0) {
  435                 device_printf(sc->dev, "failed reading product ID\n");
  436                 return (EIO);
  437         }
  438         sc->product_id = le16toh(buf);
  439 
  440         if (ietp_iic_read_reg(dev, IETP_PATTERN, sizeof(buf), &buf) != 0) {
  441                 device_printf(sc->dev, "failed reading pattern\n");
  442                 return (EIO);
  443         }
  444         pattern = buf == 0xFFFF ? 0 : buf8[1];
  445         sc->hi_precission = pattern >= 0x02;
  446 
  447         reg = pattern >= 0x01 ? IETP_IC_TYPE : IETP_OSM_VERSION;
  448         if (ietp_iic_read_reg(dev, reg, sizeof(buf), &buf) != 0) {
  449                 device_printf(sc->dev, "failed reading IC type\n");
  450                 return (EIO);
  451         }
  452         sc->ic_type = pattern >= 0x01 ? be16toh(buf) : buf8[1];
  453 
  454         if (ietp_iic_read_reg(dev, IETP_NSM_VERSION, sizeof(buf), &buf) != 0) {
  455                 device_printf(sc->dev, "failed reading SM version\n");
  456                 return (EIO);
  457         }
  458         sc->is_clickpad = (buf8[0] & 0x10) != 0;
  459 
  460         if (ietp_iic_set_absolute_mode(dev, true) != 0) {
  461                 device_printf(sc->dev, "failed to set absolute mode\n");
  462                 return (EIO);
  463         }
  464 
  465         if (ietp_iic_read_reg(dev, IETP_MAX_X_AXIS, sizeof(buf), &buf) != 0) {
  466                 device_printf(sc->dev, "failed reading max x\n");
  467                 return (EIO);
  468         }
  469         sc->max_x = le16toh(buf);
  470 
  471         if (ietp_iic_read_reg(dev, IETP_MAX_Y_AXIS, sizeof(buf), &buf) != 0) {
  472                 device_printf(sc->dev, "failed reading max y\n");
  473                 return (EIO);
  474         }
  475         sc->max_y = le16toh(buf);
  476 
  477         if (ietp_iic_read_reg(dev, IETP_TRACENUM, sizeof(buf), &buf) != 0) {
  478                 device_printf(sc->dev, "failed reading trace info\n");
  479                 return (EIO);
  480         }
  481         sc->trace_x = sc->max_x / buf8[0];
  482         sc->trace_y = sc->max_y / buf8[1];
  483 
  484         if (ietp_iic_read_reg(dev, IETP_PRESSURE, sizeof(buf), &buf) != 0) {
  485                 device_printf(sc->dev, "failed reading pressure format\n");
  486                 return (EIO);
  487         }
  488         sc->pressure_base = (buf8[0] & 0x10) ? 0 : IETP_PRESSURE_BASE;
  489 
  490         if (ietp_iic_read_reg(dev, IETP_RESOLUTION, sizeof(buf), &buf)  != 0) {
  491                 device_printf(sc->dev, "failed reading resolution\n");
  492                 return (EIO);
  493         }
  494         /* Conversion from internal format to dot per mm */
  495         sc->res_x = ietp_res2dpmm(buf8[0], sc->hi_precission);
  496         sc->res_y = ietp_res2dpmm(buf8[1], sc->hi_precission);
  497 
  498         return (ietp_attach(sc));
  499 }
  500 
  501 static int
  502 ietp_iic_detach(device_t dev)
  503 {
  504         struct ietp_softc *sc = device_get_softc(dev);
  505 
  506         if (ietp_iic_set_absolute_mode(dev, false) != 0)
  507                 device_printf(dev, "failed setting standard mode\n");
  508 
  509         return (ietp_detach(sc));
  510 }
  511 
  512 static int
  513 ietp_iic_resume(device_t dev)
  514 {
  515         if (ietp_iic_set_absolute_mode(dev, true) != 0) {
  516                 device_printf(dev, "reset when resuming failed: \n");
  517                 return (EIO);
  518         }
  519 
  520         return (0);
  521 }
  522 
  523 static int
  524 ietp_iic_set_absolute_mode(device_t dev, bool enable)
  525 {
  526         struct ietp_softc *sc = device_get_softc(dev);
  527         static const struct {
  528                 uint16_t        ic_type;
  529                 uint16_t        product_id;
  530         } special_fw[] = {
  531             { 0x0E, 0x05 }, { 0x0E, 0x06 }, { 0x0E, 0x07 }, { 0x0E, 0x09 },
  532             { 0x0E, 0x13 }, { 0x08, 0x26 },
  533         };
  534         uint16_t val;
  535         int i, error;
  536         bool require_wakeup;
  537 
  538         error = 0;
  539 
  540         /*
  541          * Some ASUS touchpads need to be powered on to enter absolute mode.
  542          */
  543         require_wakeup = false;
  544         for (i = 0; i < nitems(special_fw); i++) {
  545                 if (sc->ic_type == special_fw[i].ic_type &&
  546                     sc->product_id == special_fw[i].product_id) {
  547                         require_wakeup = true;
  548                         break;
  549                 }
  550         }
  551 
  552         if (require_wakeup && hidbus_intr_start(dev) != 0) {
  553                 device_printf(dev, "failed writing poweron command\n");
  554                 return (EIO);
  555         }
  556 
  557         val = enable ? IETP_CTRL_ABSOLUTE : IETP_CTRL_STANDARD;
  558         if (ietp_iic_write_reg(dev, IETP_CONTROL, val) != 0) {
  559                 device_printf(dev, "failed setting absolute mode\n");
  560                 error = EIO;
  561         }
  562 
  563         if (require_wakeup && hidbus_intr_stop(dev) != 0) {
  564                 device_printf(dev, "failed writing poweroff command\n");
  565                 error = EIO;
  566         }
  567 
  568         return (error);
  569 }
  570 
  571 static int
  572 ietp_iic_read_reg(device_t dev, uint16_t reg, size_t len, void *val)
  573 {
  574         device_t iichid = device_get_parent(device_get_parent(dev));
  575         uint16_t addr = iicbus_get_addr(iichid) << 1;
  576         uint8_t cmd[2] = { reg & 0xff, (reg >> 8) & 0xff };
  577         struct iic_msg msgs[2] = {
  578             { addr, IIC_M_WR | IIC_M_NOSTOP,  sizeof(cmd), cmd },
  579             { addr, IIC_M_RD, len, val },
  580         };
  581         struct iic_rdwr_data ird = { msgs, nitems(msgs) };
  582         int error;
  583 
  584         DPRINTF("Read reg 0x%04x with size %zu\n", reg, len);
  585 
  586         error = hid_ioctl(dev, I2CRDWR, (uintptr_t)&ird);
  587         if (error != 0)
  588                 return (error);
  589 
  590         DPRINTF("Response: %*D\n", (int)len, val, " ");
  591 
  592         return (0);
  593 }
  594 
  595 static int
  596 ietp_iic_write_reg(device_t dev, uint16_t reg, uint16_t val)
  597 {
  598         device_t iichid = device_get_parent(device_get_parent(dev));
  599         uint16_t addr = iicbus_get_addr(iichid) << 1;
  600         uint8_t cmd[4] = { reg & 0xff, (reg >> 8) & 0xff,
  601                            val & 0xff, (val >> 8) & 0xff };
  602         struct iic_msg msgs[1] = {
  603             { addr, IIC_M_WR, sizeof(cmd), cmd },
  604         };
  605         struct iic_rdwr_data ird = { msgs, nitems(msgs) };
  606 
  607         DPRINTF("Write reg 0x%04x with value 0x%04x\n", reg, val);
  608 
  609         return (hid_ioctl(dev, I2CRDWR, (uintptr_t)&ird));
  610 }
  611 
  612 static device_method_t ietp_methods[] = {
  613         DEVMETHOD(device_probe,         ietp_iic_probe),
  614         DEVMETHOD(device_attach,        ietp_iic_attach),
  615         DEVMETHOD(device_detach,        ietp_iic_detach),
  616         DEVMETHOD(device_resume,        ietp_iic_resume),
  617         DEVMETHOD_END
  618 };
  619 
  620 static driver_t ietp_driver = {
  621         .name = "ietp",
  622         .methods = ietp_methods,
  623         .size = sizeof(struct ietp_softc),
  624 };
  625 
  626 DRIVER_MODULE(ietp, hidbus, ietp_driver, NULL, NULL);
  627 MODULE_DEPEND(ietp, hidbus, 1, 1, 1);
  628 MODULE_DEPEND(ietp, hid, 1, 1, 1);
  629 MODULE_DEPEND(ietp, evdev, 1, 1, 1);
  630 MODULE_VERSION(ietp, 1);
  631 HID_PNP_INFO(ietp_iic_devs);

Cache object: 2410fe0fe180979d6133b0bfe3f199ad


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