1*07e9317cSderaadt /* $OpenBSD: ietp.c,v 1.3 2024/08/18 03:25:04 deraadt Exp $ */ 2593d792cSjcs /* 38b995cd9Sjcs * Elan I2C Touchpad driver 4593d792cSjcs * 5593d792cSjcs * Copyright (c) 2015, 2016 joshua stein <jcs@openbsd.org> 6593d792cSjcs * Copyright (c) 2020, 2022 Vladimir Kondratyev <wulf@FreeBSD.org> 7593d792cSjcs * Copyright (c) 2023 vladimir serbinenko <phcoder@gmail.com> 8593d792cSjcs * 9593d792cSjcs * Permission to use, copy, modify, and distribute this software for any 10593d792cSjcs * purpose with or without fee is hereby granted, provided that the above 11593d792cSjcs * copyright notice and this permission notice appear in all copies. 12593d792cSjcs * 13593d792cSjcs * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 14593d792cSjcs * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 15593d792cSjcs * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 16593d792cSjcs * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 17593d792cSjcs * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 18593d792cSjcs * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 19593d792cSjcs * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 20593d792cSjcs */ 21593d792cSjcs 228b995cd9Sjcs /* Protocol documentation: 238b995cd9Sjcs * https://lkml.indiana.edu/hypermail/linux/kernel/1205.0/02551.html 248b995cd9Sjcs * Based on FreeBSD ietp driver. 25593d792cSjcs */ 26593d792cSjcs 27593d792cSjcs #include <sys/param.h> 28593d792cSjcs #include <sys/systm.h> 29593d792cSjcs #include <sys/device.h> 30593d792cSjcs #include <sys/malloc.h> 31593d792cSjcs #include <sys/stdint.h> 32593d792cSjcs 33593d792cSjcs #include <dev/i2c/i2cvar.h> 34593d792cSjcs #include <dev/i2c/ietp.h> 35593d792cSjcs 36593d792cSjcs #include <dev/wscons/wsconsio.h> 37593d792cSjcs #include <dev/wscons/wsmousevar.h> 38593d792cSjcs 39593d792cSjcs /* #define IETP_DEBUG */ 40593d792cSjcs 41593d792cSjcs #ifdef IETP_DEBUG 42593d792cSjcs #define DPRINTF(x) printf x 43593d792cSjcs #else 44593d792cSjcs #define DPRINTF(x) 45593d792cSjcs #endif 46593d792cSjcs 47593d792cSjcs enum { 48593d792cSjcs I2C_HID_CMD_DESCR = 0x0, 49593d792cSjcs I2C_HID_CMD_RESET = 0x1, 50593d792cSjcs I2C_HID_CMD_SET_POWER = 0x8, 51593d792cSjcs }; 52593d792cSjcs 53593d792cSjcs #define I2C_HID_POWER_ON 0x0 54593d792cSjcs #define I2C_HID_POWER_OFF 0x1 55593d792cSjcs 56593d792cSjcs #define IETP_PATTERN 0x0100 57593d792cSjcs #define IETP_UNIQUEID 0x0101 58593d792cSjcs #define IETP_IC_TYPE 0x0103 59593d792cSjcs #define IETP_OSM_VERSION 0x0103 60593d792cSjcs #define IETP_NSM_VERSION 0x0104 61593d792cSjcs #define IETP_TRACENUM 0x0105 62593d792cSjcs #define IETP_MAX_X_AXIS 0x0106 63593d792cSjcs #define IETP_MAX_Y_AXIS 0x0107 64593d792cSjcs #define IETP_RESOLUTION 0x0108 65593d792cSjcs #define IETP_PRESSURE 0x010A 66593d792cSjcs 67593d792cSjcs #define IETP_CONTROL 0x0300 68593d792cSjcs #define IETP_CTRL_ABSOLUTE 0x0001 69593d792cSjcs #define IETP_CTRL_STANDARD 0x0000 70593d792cSjcs 71593d792cSjcs #define IETP_REPORT_LEN_LO 31 72593d792cSjcs #define IETP_REPORT_LEN_HI 36 73593d792cSjcs #define IETP_MAX_FINGERS 5 74593d792cSjcs 75593d792cSjcs #define IETP_REPORT_ID_LO 0x5D 76593d792cSjcs #define IETP_REPORT_ID_HI 0x60 77593d792cSjcs 78593d792cSjcs #define IETP_TOUCH_INFO 0 79593d792cSjcs #define IETP_FINGER_DATA 1 80593d792cSjcs #define IETP_FINGER_DATA_LEN 5 81593d792cSjcs #define IETP_WH_DATA 31 82593d792cSjcs 83593d792cSjcs #define IETP_TOUCH_LMB (1 << 0) 84593d792cSjcs #define IETP_TOUCH_RMB (1 << 1) 85593d792cSjcs #define IETP_TOUCH_MMB (1 << 2) 86593d792cSjcs 87593d792cSjcs #define IETP_MAX_PRESSURE 255 88593d792cSjcs #define IETP_FWIDTH_REDUCE 90 89593d792cSjcs #define IETP_PRESSURE_BASE 25 90593d792cSjcs 91593d792cSjcs int ietp_match(struct device *, void *, void *); 92593d792cSjcs void ietp_attach(struct device *, struct device *, void *); 93593d792cSjcs int ietp_detach(struct device *, int); 94593d792cSjcs int ietp_activate(struct device *, int); 95593d792cSjcs 96593d792cSjcs int ietp_intr(void *); 97593d792cSjcs int ietp_reset(struct ietp_softc *); 98593d792cSjcs 99593d792cSjcs int ietp_fetch_descriptor(struct ietp_softc *sc); 100593d792cSjcs int ietp_set_power(struct ietp_softc *sc, int power); 101593d792cSjcs int ietp_reset_cmd(struct ietp_softc *sc); 102593d792cSjcs 103593d792cSjcs int32_t ietp_res2dpmm(uint8_t, bool); 104593d792cSjcs 105593d792cSjcs int ietp_iic_read_reg(struct ietp_softc *, uint16_t, size_t, void *); 106593d792cSjcs int ietp_iic_write_reg(struct ietp_softc *, uint16_t, uint16_t); 107593d792cSjcs int ietp_iic_set_absolute_mode(struct ietp_softc *, bool); 108593d792cSjcs 109593d792cSjcs const struct cfattach ietp_ca = { 110593d792cSjcs sizeof(struct ietp_softc), 111593d792cSjcs ietp_match, 112593d792cSjcs ietp_attach, 113593d792cSjcs ietp_detach, 114593d792cSjcs ietp_activate, 115593d792cSjcs }; 116593d792cSjcs 117593d792cSjcs const struct wsmouse_accessops ietp_mouse_access = { 118593d792cSjcs ietp_enable, 119593d792cSjcs ietp_ioctl, 120593d792cSjcs ietp_disable 121593d792cSjcs }; 122593d792cSjcs 123593d792cSjcs struct cfdriver ietp_cd = { 124593d792cSjcs NULL, "ietp", DV_DULL 125593d792cSjcs }; 126593d792cSjcs 127593d792cSjcs int 128593d792cSjcs ietp_match(struct device *parent, void *match, void *aux) 129593d792cSjcs { 130593d792cSjcs struct i2c_attach_args *ia = aux; 131593d792cSjcs 132593d792cSjcs if (strcmp(ia->ia_name, "ietp") == 0) 133593d792cSjcs return (1); 134593d792cSjcs 135593d792cSjcs return (0); 136593d792cSjcs } 137593d792cSjcs 138593d792cSjcs int32_t 139593d792cSjcs ietp_res2dpmm(uint8_t res, bool hi_precision) 140593d792cSjcs { 141593d792cSjcs int32_t dpi; 142593d792cSjcs 143593d792cSjcs dpi = hi_precision ? 300 + res * 100 : 790 + res * 10; 144593d792cSjcs 145593d792cSjcs return (dpi * 10 /254); 146593d792cSjcs } 147593d792cSjcs 148593d792cSjcs void 149593d792cSjcs ietp_attach(struct device *parent, struct device *self, void *aux) 150593d792cSjcs { 151593d792cSjcs struct ietp_softc *sc = (struct ietp_softc *)self; 152593d792cSjcs struct i2c_attach_args *ia = aux; 153593d792cSjcs uint16_t buf, reg; 154593d792cSjcs uint8_t *buf8; 155593d792cSjcs uint8_t pattern; 156593d792cSjcs struct wsmousedev_attach_args a; 157593d792cSjcs struct wsmousehw *hw; 158593d792cSjcs 159593d792cSjcs sc->sc_tag = ia->ia_tag; 160593d792cSjcs sc->sc_addr = ia->ia_addr; 161593d792cSjcs 162593d792cSjcs ietp_fetch_descriptor(sc); 163593d792cSjcs 164593d792cSjcs if (ia->ia_intr) { 165593d792cSjcs printf(" %s", iic_intr_string(sc->sc_tag, ia->ia_intr)); 166593d792cSjcs 167593d792cSjcs sc->sc_ih = iic_intr_establish(sc->sc_tag, ia->ia_intr, 168593d792cSjcs IPL_TTY, ietp_intr, sc, sc->sc_dev.dv_xname); 169593d792cSjcs if (sc->sc_ih == NULL) { 1708b995cd9Sjcs printf(", can't establish interrupt\n"); 171593d792cSjcs return; 172593d792cSjcs } 173593d792cSjcs } 174593d792cSjcs 175593d792cSjcs sc->sc_buttons = 0; 176593d792cSjcs sc->sc_refcnt = 0; 177593d792cSjcs 178593d792cSjcs buf8 = (uint8_t *)&buf; 179593d792cSjcs 180593d792cSjcs if (ietp_iic_read_reg(sc, IETP_UNIQUEID, sizeof(buf), &buf) != 0) { 1818b995cd9Sjcs printf(": failed reading product ID\n"); 182593d792cSjcs return; 183593d792cSjcs } 184593d792cSjcs sc->product_id = le16toh(buf); 185593d792cSjcs 186593d792cSjcs if (ietp_iic_read_reg(sc, IETP_PATTERN, sizeof(buf), &buf) != 0) { 1878b995cd9Sjcs printf(": failed reading pattern\n"); 188593d792cSjcs return; 189593d792cSjcs } 190593d792cSjcs pattern = buf == 0xFFFF ? 0 : buf8[1]; 191593d792cSjcs sc->hi_precision = pattern >= 0x02; 192593d792cSjcs 193593d792cSjcs reg = pattern >= 0x01 ? IETP_IC_TYPE : IETP_OSM_VERSION; 194593d792cSjcs if (ietp_iic_read_reg(sc, reg, sizeof(buf), &buf) != 0) { 1958b995cd9Sjcs printf(": failed reading IC type\n"); 196593d792cSjcs return; 197593d792cSjcs } 198593d792cSjcs sc->ic_type = pattern >= 0x01 ? be16toh(buf) : buf8[1]; 199593d792cSjcs 200593d792cSjcs if (ietp_iic_read_reg(sc, IETP_NSM_VERSION, sizeof(buf), &buf) != 0) { 2018b995cd9Sjcs printf(": failed reading SM version\n"); 202593d792cSjcs return; 203593d792cSjcs } 204593d792cSjcs sc->is_clickpad = (buf8[0] & 0x10) != 0; 205593d792cSjcs 206593d792cSjcs if (ietp_iic_set_absolute_mode(sc, true) != 0) { 2078b995cd9Sjcs printf(": failed to set absolute mode\n"); 208593d792cSjcs return; 209593d792cSjcs } 210593d792cSjcs 211593d792cSjcs if (ietp_iic_read_reg(sc, IETP_MAX_X_AXIS, sizeof(buf), &buf) != 0) { 2128b995cd9Sjcs printf(": failed reading max x\n"); 213593d792cSjcs return; 214593d792cSjcs } 215593d792cSjcs sc->max_x = le16toh(buf); 216593d792cSjcs 217593d792cSjcs if (ietp_iic_read_reg(sc, IETP_MAX_Y_AXIS, sizeof(buf), &buf) != 0) { 2188b995cd9Sjcs printf(": failed reading max y\n"); 219593d792cSjcs return; 220593d792cSjcs } 221593d792cSjcs sc->max_y = le16toh(buf); 222593d792cSjcs 223593d792cSjcs if (ietp_iic_read_reg(sc, IETP_TRACENUM, sizeof(buf), &buf) != 0) { 2248b995cd9Sjcs printf(": failed reading trace info\n"); 225593d792cSjcs return; 226593d792cSjcs } 227593d792cSjcs sc->trace_x = sc->max_x / buf8[0]; 228593d792cSjcs sc->trace_y = sc->max_y / buf8[1]; 229593d792cSjcs 230593d792cSjcs if (ietp_iic_read_reg(sc, IETP_PRESSURE, sizeof(buf), &buf) != 0) { 2318b995cd9Sjcs printf(": failed reading pressure format\n"); 232593d792cSjcs return; 233593d792cSjcs } 234593d792cSjcs sc->pressure_base = (buf8[0] & 0x10) ? 0 : IETP_PRESSURE_BASE; 235593d792cSjcs 236593d792cSjcs if (ietp_iic_read_reg(sc, IETP_RESOLUTION, sizeof(buf), &buf) != 0) { 2378b995cd9Sjcs printf(": failed reading resolution\n"); 238593d792cSjcs return; 239593d792cSjcs } 240593d792cSjcs /* Conversion from internal format to dot per mm */ 241593d792cSjcs sc->res_x = ietp_res2dpmm(buf8[0], sc->hi_precision); 242593d792cSjcs sc->res_y = ietp_res2dpmm(buf8[1], sc->hi_precision); 243593d792cSjcs 244593d792cSjcs sc->report_id = sc->hi_precision ? 245593d792cSjcs IETP_REPORT_ID_HI : IETP_REPORT_ID_LO; 246593d792cSjcs sc->report_len = sc->hi_precision ? 247593d792cSjcs IETP_REPORT_LEN_HI : IETP_REPORT_LEN_LO; 248593d792cSjcs 2498b995cd9Sjcs sc->sc_ibuf = malloc(IETP_REPORT_LEN_HI + 12, M_DEVBUF, 2508b995cd9Sjcs M_NOWAIT | M_ZERO); 251593d792cSjcs sc->sc_isize = sc->report_len + 3; 252593d792cSjcs 253593d792cSjcs a.accessops = &ietp_mouse_access; 254593d792cSjcs a.accesscookie = sc; 255593d792cSjcs sc->sc_wsmousedev = config_found(self, &a, wsmousedevprint); 256593d792cSjcs 257593d792cSjcs hw = wsmouse_get_hw(sc->sc_wsmousedev); 258593d792cSjcs hw->type = WSMOUSE_TYPE_TOUCHPAD; 259593d792cSjcs hw->hw_type = sc->is_clickpad ? WSMOUSEHW_CLICKPAD : WSMOUSEHW_TOUCHPAD; 260593d792cSjcs hw->x_min = 0; 261593d792cSjcs hw->x_max = sc->max_x; 262593d792cSjcs hw->y_min = 0; 263593d792cSjcs hw->y_max = sc->max_y; 264593d792cSjcs hw->h_res = sc->res_x; 265593d792cSjcs hw->v_res = sc->res_y; 266593d792cSjcs hw->mt_slots = IETP_MAX_FINGERS; 267593d792cSjcs 268593d792cSjcs wsmouse_configure(sc->sc_wsmousedev, NULL, 0); 269593d792cSjcs 270593d792cSjcs /* power down until we're opened */ 271593d792cSjcs if (ietp_set_power(sc, I2C_HID_POWER_OFF)) { 2728b995cd9Sjcs printf(": failed to power down\n"); 273593d792cSjcs return; 274593d792cSjcs } 275593d792cSjcs 2768b995cd9Sjcs printf("\n"); 2778b995cd9Sjcs 278593d792cSjcs DPRINTF(("%s: max_x=%d, max_y=%d, %s\n", sc->sc_dev.dv_xname, 279593d792cSjcs sc->max_x, sc->max_y, 280593d792cSjcs sc->is_clickpad ? "clickpad" : "touchpad")); 281593d792cSjcs 282593d792cSjcs return; 283593d792cSjcs } 284593d792cSjcs 285593d792cSjcs int 286593d792cSjcs ietp_detach(struct device *self, int flags) 287593d792cSjcs { 288593d792cSjcs struct ietp_softc *sc = (struct ietp_softc *)self; 289593d792cSjcs 290593d792cSjcs if (sc->sc_ih != NULL) { 291593d792cSjcs iic_intr_disestablish(sc->sc_tag, sc->sc_ih); 292593d792cSjcs sc->sc_ih = NULL; 293593d792cSjcs } 294593d792cSjcs 295593d792cSjcs if (sc->sc_ibuf != NULL) { 296593d792cSjcs free(sc->sc_ibuf, M_DEVBUF, sc->sc_isize); 297593d792cSjcs sc->sc_ibuf = NULL; 298593d792cSjcs } 299593d792cSjcs 300593d792cSjcs return (0); 301593d792cSjcs } 302593d792cSjcs 303593d792cSjcs int 304593d792cSjcs ietp_activate(struct device *self, int act) 305593d792cSjcs { 306593d792cSjcs struct ietp_softc *sc = (struct ietp_softc *)self; 307*07e9317cSderaadt int rv; 308593d792cSjcs 309593d792cSjcs DPRINTF(("%s(%d)\n", __func__, act)); 310593d792cSjcs 311593d792cSjcs switch (act) { 312593d792cSjcs case DVACT_QUIESCE: 313*07e9317cSderaadt rv = config_activate_children(self, act); 314593d792cSjcs sc->sc_dying = 1; 315593d792cSjcs if (ietp_set_power(sc, I2C_HID_POWER_OFF)) 316593d792cSjcs printf("%s: failed to power down\n", 317593d792cSjcs sc->sc_dev.dv_xname); 318593d792cSjcs break; 319593d792cSjcs case DVACT_WAKEUP: 320593d792cSjcs ietp_reset(sc); 321593d792cSjcs sc->sc_dying = 0; 322*07e9317cSderaadt rv = config_activate_children(self, act); 323*07e9317cSderaadt break; 324*07e9317cSderaadt default: 325*07e9317cSderaadt rv = config_activate_children(self, act); 326593d792cSjcs break; 327593d792cSjcs } 328*07e9317cSderaadt return rv; 329593d792cSjcs } 330593d792cSjcs 331593d792cSjcs void 332593d792cSjcs ietp_sleep(struct ietp_softc *sc, int ms) 333593d792cSjcs { 334593d792cSjcs if (cold) 335593d792cSjcs delay(ms * 1000); 336593d792cSjcs else 337593d792cSjcs tsleep_nsec(&sc, PWAIT, "ietp", MSEC_TO_NSEC(ms)); 338593d792cSjcs } 339593d792cSjcs 340593d792cSjcs int 341593d792cSjcs ietp_iic_set_absolute_mode(struct ietp_softc *sc, bool enable) 342593d792cSjcs { 343593d792cSjcs static const struct { 344593d792cSjcs uint16_t ic_type; 345593d792cSjcs uint16_t product_id; 346593d792cSjcs } special_fw[] = { 347593d792cSjcs { 0x0E, 0x05 }, { 0x0E, 0x06 }, { 0x0E, 0x07 }, { 0x0E, 0x09 }, 348593d792cSjcs { 0x0E, 0x13 }, { 0x08, 0x26 }, 349593d792cSjcs }; 350593d792cSjcs uint16_t val; 351593d792cSjcs int i, error; 352593d792cSjcs bool require_wakeup; 353593d792cSjcs 354593d792cSjcs error = 0; 355593d792cSjcs 356593d792cSjcs /* 357593d792cSjcs * Some ASUS touchpads need to be powered on to enter absolute mode. 358593d792cSjcs */ 359593d792cSjcs require_wakeup = false; 360593d792cSjcs for (i = 0; i < nitems(special_fw); i++) { 361593d792cSjcs if (sc->ic_type == special_fw[i].ic_type && 362593d792cSjcs sc->product_id == special_fw[i].product_id) { 363593d792cSjcs require_wakeup = true; 364593d792cSjcs break; 365593d792cSjcs } 366593d792cSjcs } 367593d792cSjcs 368593d792cSjcs if (require_wakeup && ietp_set_power(sc, I2C_HID_POWER_ON) != 0) { 3698b995cd9Sjcs printf("%s: failed writing poweron command\n", 3708b995cd9Sjcs sc->sc_dev.dv_xname); 371593d792cSjcs return (EIO); 372593d792cSjcs } 373593d792cSjcs 374593d792cSjcs val = enable ? IETP_CTRL_ABSOLUTE : IETP_CTRL_STANDARD; 375593d792cSjcs if (ietp_iic_write_reg(sc, IETP_CONTROL, val) != 0) { 3768b995cd9Sjcs printf("%s: failed setting absolute mode\n", 3778b995cd9Sjcs sc->sc_dev.dv_xname); 378593d792cSjcs error = EIO; 379593d792cSjcs } 380593d792cSjcs 381593d792cSjcs if (require_wakeup && ietp_set_power(sc, I2C_HID_POWER_OFF) != 0) { 3828b995cd9Sjcs printf("%s: failed writing poweroff command\n", 3838b995cd9Sjcs sc->sc_dev.dv_xname); 384593d792cSjcs error = EIO; 385593d792cSjcs } 386593d792cSjcs 387593d792cSjcs return (error); 388593d792cSjcs } 389593d792cSjcs 390593d792cSjcs int 391593d792cSjcs ietp_iic_read_reg(struct ietp_softc *sc, uint16_t reg, size_t len, void *val) 392593d792cSjcs { 393593d792cSjcs uint8_t cmd[] = { 394593d792cSjcs reg & 0xff, 395593d792cSjcs reg >> 8, 396593d792cSjcs }; 397593d792cSjcs 398593d792cSjcs return iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr, 399593d792cSjcs &cmd, 2, val, len, 0); 400593d792cSjcs } 401593d792cSjcs 402593d792cSjcs int 403593d792cSjcs ietp_iic_write_reg(struct ietp_softc *sc, uint16_t reg, uint16_t val) 404593d792cSjcs { 405593d792cSjcs uint8_t cmd[] = { 406593d792cSjcs reg & 0xff, 407593d792cSjcs reg >> 8, 408593d792cSjcs val & 0xff, 409593d792cSjcs val >> 8, 410593d792cSjcs }; 411593d792cSjcs 412593d792cSjcs return iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr, 413593d792cSjcs &cmd, 4, NULL, 0, 0); 414593d792cSjcs } 415593d792cSjcs 416593d792cSjcs int 417593d792cSjcs ietp_set_power(struct ietp_softc *sc, int power) 418593d792cSjcs { 419593d792cSjcs int res = 1; 420593d792cSjcs uint8_t cmd[] = { 421593d792cSjcs htole16(sc->hid_desc.wCommandRegister) & 0xff, 422593d792cSjcs htole16(sc->hid_desc.wCommandRegister) >> 8, 423593d792cSjcs power, 424593d792cSjcs I2C_HID_CMD_SET_POWER, 425593d792cSjcs }; 426593d792cSjcs 427593d792cSjcs iic_acquire_bus(sc->sc_tag, 0); 428593d792cSjcs 429593d792cSjcs DPRINTF(("%s: HID command I2C_HID_CMD_SET_POWER(%d)\n", 430593d792cSjcs sc->sc_dev.dv_xname, power)); 431593d792cSjcs 432593d792cSjcs /* 22 00 00 08 */ 433593d792cSjcs res = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr, 434593d792cSjcs &cmd, sizeof(cmd), NULL, 0, 0); 435593d792cSjcs 436593d792cSjcs iic_release_bus(sc->sc_tag, 0); 437593d792cSjcs 438593d792cSjcs return (res); 439593d792cSjcs } 440593d792cSjcs 441593d792cSjcs int 442593d792cSjcs ietp_reset_cmd(struct ietp_softc *sc) 443593d792cSjcs { 444593d792cSjcs int res = 1; 445593d792cSjcs uint8_t cmd[] = { 446593d792cSjcs htole16(sc->hid_desc.wCommandRegister) & 0xff, 447593d792cSjcs htole16(sc->hid_desc.wCommandRegister) >> 8, 448593d792cSjcs 0, 449593d792cSjcs I2C_HID_CMD_RESET, 450593d792cSjcs }; 451593d792cSjcs 452593d792cSjcs iic_acquire_bus(sc->sc_tag, 0); 453593d792cSjcs 454593d792cSjcs DPRINTF(("%s: HID command I2C_HID_CMD_RESET\n", 455593d792cSjcs sc->sc_dev.dv_xname)); 456593d792cSjcs 457593d792cSjcs /* 22 00 00 01 */ 458593d792cSjcs res = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr, 459593d792cSjcs &cmd, sizeof(cmd), NULL, 0, 0); 460593d792cSjcs 461593d792cSjcs iic_release_bus(sc->sc_tag, 0); 462593d792cSjcs 463593d792cSjcs return (res); 464593d792cSjcs } 465593d792cSjcs 466593d792cSjcs int 467593d792cSjcs ietp_fetch_descriptor(struct ietp_softc *sc) 468593d792cSjcs { 469593d792cSjcs int i, res = 1; 470593d792cSjcs /* 471593d792cSjcs * 5.2.2 - HID Descriptor Retrieval 472593d792cSjcs * register is passed from the controller 473593d792cSjcs */ 474593d792cSjcs uint8_t cmd[] = { 475593d792cSjcs 1, 476593d792cSjcs 0, 477593d792cSjcs }; 478593d792cSjcs 479593d792cSjcs iic_acquire_bus(sc->sc_tag, 0); 480593d792cSjcs 481593d792cSjcs DPRINTF(("%s: HID command I2C_HID_CMD_DESCR at 0x1\n", 482593d792cSjcs sc->sc_dev.dv_xname)); 483593d792cSjcs 484593d792cSjcs /* 20 00 */ 485593d792cSjcs res = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr, 486593d792cSjcs &cmd, sizeof(cmd), &sc->hid_desc_buf, 487593d792cSjcs sizeof(struct i2c_hid_desc), 0); 488593d792cSjcs 489593d792cSjcs DPRINTF(("%s: HID descriptor:", sc->sc_dev.dv_xname)); 490593d792cSjcs for (i = 0; i < sizeof(struct i2c_hid_desc); i++) 491593d792cSjcs DPRINTF((" %.2x", sc->hid_desc_buf[i])); 492593d792cSjcs DPRINTF(("\n")); 493593d792cSjcs 494593d792cSjcs iic_release_bus(sc->sc_tag, 0); 495593d792cSjcs 496593d792cSjcs return (res); 497593d792cSjcs } 498593d792cSjcs 499593d792cSjcs int 500593d792cSjcs ietp_reset(struct ietp_softc *sc) 501593d792cSjcs { 502593d792cSjcs DPRINTF(("%s: resetting\n", sc->sc_dev.dv_xname)); 503593d792cSjcs 504593d792cSjcs if (ietp_set_power(sc, I2C_HID_POWER_ON)) { 505593d792cSjcs printf("%s: failed to power on\n", sc->sc_dev.dv_xname); 506593d792cSjcs return (1); 507593d792cSjcs } 508593d792cSjcs 509593d792cSjcs ietp_sleep(sc, 100); 510593d792cSjcs 511593d792cSjcs if (ietp_reset_cmd(sc)) { 512593d792cSjcs printf("%s: failed to reset hardware\n", sc->sc_dev.dv_xname); 513593d792cSjcs 514593d792cSjcs ietp_set_power(sc, I2C_HID_POWER_OFF); 515593d792cSjcs 516593d792cSjcs return (1); 517593d792cSjcs } 518593d792cSjcs 519593d792cSjcs ietp_sleep(sc, 100); 520593d792cSjcs 521593d792cSjcs return (0); 522593d792cSjcs } 523593d792cSjcs 524593d792cSjcs void 525593d792cSjcs parse_input(struct ietp_softc *sc, u_char *report, int len) 526593d792cSjcs { 527593d792cSjcs uint8_t *fdata; 528593d792cSjcs int32_t finger; 529593d792cSjcs int32_t x, y, p; 530593d792cSjcs int buttons = 0; 531593d792cSjcs int s; 532593d792cSjcs 533593d792cSjcs /* we seem to get 0 length reports sometimes, ignore them */ 534593d792cSjcs if (len == 0) 535593d792cSjcs return; 536593d792cSjcs if (len != sc->report_len) { 5378b995cd9Sjcs printf("%s: wrong report length (%d vs %d expected)", 5388b995cd9Sjcs sc->sc_dev.dv_xname, len, (int) sc->report_len); 539593d792cSjcs return; 540593d792cSjcs } 541593d792cSjcs 542593d792cSjcs s = spltty(); 543593d792cSjcs 544593d792cSjcs buttons = report[IETP_TOUCH_INFO] & 7; 545593d792cSjcs 546593d792cSjcs if (sc->sc_buttons != buttons) { 547593d792cSjcs wsmouse_buttons(sc->sc_wsmousedev, buttons); 548593d792cSjcs sc->sc_buttons = buttons; 549593d792cSjcs } 550593d792cSjcs 551593d792cSjcs for (finger = 0, fdata = report + IETP_FINGER_DATA; 552593d792cSjcs finger < IETP_MAX_FINGERS; 553593d792cSjcs finger++, fdata += IETP_FINGER_DATA_LEN) { 554593d792cSjcs if ((report[IETP_TOUCH_INFO] & (1 << (finger + 3))) != 0) { 555593d792cSjcs if (sc->hi_precision) { 556593d792cSjcs x = fdata[0] << 8 | fdata[1]; 557593d792cSjcs y = fdata[2] << 8 | fdata[3]; 558593d792cSjcs } else { 559593d792cSjcs x = (fdata[0] & 0xf0) << 4 | fdata[1]; 560593d792cSjcs y = (fdata[0] & 0x0f) << 8 | fdata[2]; 561593d792cSjcs } 562593d792cSjcs 563593d792cSjcs if (x > sc->max_x || y > sc->max_y) { 564593d792cSjcs printf("%s: [%d] x=%d y=%d over max (%d, %d)\n", 5658b995cd9Sjcs sc->sc_dev.dv_xname, finger, x, y, 5668b995cd9Sjcs sc->max_x, sc->max_y); 567593d792cSjcs continue; 568593d792cSjcs } 569593d792cSjcs 570593d792cSjcs 571593d792cSjcs p = MIN((int32_t)fdata[4] + sc->pressure_base, 572593d792cSjcs IETP_MAX_PRESSURE); 573593d792cSjcs 574593d792cSjcs } else { 575593d792cSjcs x = 0; 576593d792cSjcs y = 0; 577593d792cSjcs p = 0; 578593d792cSjcs } 579593d792cSjcs 5808b995cd9Sjcs DPRINTF(("position: [finger=%d, x=%d, y=%d, p=%d]\n", finger, 5818b995cd9Sjcs x, y, p)); 582593d792cSjcs wsmouse_mtstate(sc->sc_wsmousedev, finger, x, y, p); 583593d792cSjcs } 584593d792cSjcs 585593d792cSjcs wsmouse_input_sync(sc->sc_wsmousedev); 586593d792cSjcs 587593d792cSjcs splx(s); 588593d792cSjcs } 589593d792cSjcs 590593d792cSjcs int 591593d792cSjcs ietp_intr(void *arg) 592593d792cSjcs { 593593d792cSjcs struct ietp_softc *sc = arg; 594593d792cSjcs int psize, i; 595593d792cSjcs u_char *p; 596593d792cSjcs u_int rep = 0; 597593d792cSjcs 598593d792cSjcs if (sc->sc_dying) 599593d792cSjcs return 1; 600593d792cSjcs 601593d792cSjcs /* 602593d792cSjcs * XXX: force I2C_F_POLL for now to avoid dwiic interrupting 603593d792cSjcs * while we are interrupting 604593d792cSjcs */ 605593d792cSjcs 606593d792cSjcs iic_acquire_bus(sc->sc_tag, I2C_F_POLL); 607593d792cSjcs iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr, NULL, 0, 608593d792cSjcs sc->sc_ibuf, letoh16(sc->hid_desc.wMaxInputLength), I2C_F_POLL); 609593d792cSjcs iic_release_bus(sc->sc_tag, I2C_F_POLL); 610593d792cSjcs 611593d792cSjcs /* 612593d792cSjcs * 6.1.1 - First two bytes are the packet length, which must be less 613593d792cSjcs * than or equal to wMaxInputLength 614593d792cSjcs */ 615593d792cSjcs psize = sc->sc_ibuf[0] | sc->sc_ibuf[1] << 8; 616593d792cSjcs if (psize <= 2 || psize > sc->sc_isize) { 617593d792cSjcs DPRINTF(("%s: %s: invalid packet size (%d vs. %d)\n", 618593d792cSjcs sc->sc_dev.dv_xname, __func__, psize, 619593d792cSjcs sc->sc_isize)); 620593d792cSjcs return (1); 621593d792cSjcs } 622593d792cSjcs 623593d792cSjcs /* 3rd byte is the report id */ 624593d792cSjcs p = sc->sc_ibuf + 2; 625593d792cSjcs psize -= 2; 626593d792cSjcs rep = *p++; 627593d792cSjcs psize--; 628593d792cSjcs 629593d792cSjcs DPRINTF(("%s: %s: hid input (rep 0x%x):", sc->sc_dev.dv_xname, __func__, 630593d792cSjcs rep)); 631593d792cSjcs for (i = 0; i < psize; i++) { 632593d792cSjcs DPRINTF((" %.2x", p[i])); 633593d792cSjcs } 634593d792cSjcs DPRINTF(("\n")); 635593d792cSjcs 636593d792cSjcs if (sc->sc_refcnt && rep == sc->report_id) { 637593d792cSjcs parse_input(sc, p, psize); 638593d792cSjcs } 639593d792cSjcs 640593d792cSjcs return (1); 641593d792cSjcs } 642593d792cSjcs 643593d792cSjcs int 644593d792cSjcs ietp_enable(void *dev) 645593d792cSjcs { 646593d792cSjcs struct ietp_softc *sc = dev; 647593d792cSjcs 648593d792cSjcs DPRINTF(("%s: %s: refcnt=%d\n", sc->sc_dev.dv_xname, 649593d792cSjcs __func__, sc->sc_refcnt)); 650593d792cSjcs 651593d792cSjcs if (sc->sc_refcnt++ || sc->sc_isize == 0) 652593d792cSjcs return (0); 653593d792cSjcs 654593d792cSjcs /* power on */ 655593d792cSjcs ietp_reset(sc); 656593d792cSjcs 657593d792cSjcs return (0); 658593d792cSjcs } 659593d792cSjcs 660593d792cSjcs void 661593d792cSjcs ietp_disable(void *dev) 662593d792cSjcs { 663593d792cSjcs struct ietp_softc *sc = dev; 664593d792cSjcs DPRINTF(("%s: %s: refcnt=%d\n", sc->sc_dev.dv_xname, 665593d792cSjcs __func__, sc->sc_refcnt)); 666593d792cSjcs 667593d792cSjcs if (--sc->sc_refcnt) 668593d792cSjcs return; 669593d792cSjcs 670593d792cSjcs /* no sub-devices open, conserve power */ 671593d792cSjcs 672593d792cSjcs if (ietp_set_power(sc, I2C_HID_POWER_OFF)) 673593d792cSjcs printf("%s: failed to power down\n", sc->sc_dev.dv_xname); 674593d792cSjcs } 675593d792cSjcs 676593d792cSjcs int 677593d792cSjcs ietp_ioctl(void *dev, u_long cmd, caddr_t data, int flag, 678593d792cSjcs struct proc *p) 679593d792cSjcs { 680593d792cSjcs struct ietp_softc *sc = dev; 681593d792cSjcs struct wsmouse_calibcoords *wsmc = (struct wsmouse_calibcoords *)data; 682593d792cSjcs 683593d792cSjcs switch (cmd) { 684593d792cSjcs case WSMOUSEIO_GTYPE: 685593d792cSjcs *(u_int *)data = WSMOUSE_TYPE_TOUCHPAD; 686593d792cSjcs return 0; 687593d792cSjcs 688593d792cSjcs case WSMOUSEIO_GCALIBCOORDS: 689593d792cSjcs wsmc->minx = 0; 690593d792cSjcs wsmc->maxx = sc->max_x; 691593d792cSjcs wsmc->miny = 0; 692593d792cSjcs wsmc->maxy = sc->max_y; 693593d792cSjcs wsmc->swapxy = 0; 694593d792cSjcs wsmc->resx = sc->res_x; 695593d792cSjcs wsmc->resy = sc->res_y; 696593d792cSjcs return 0; 697593d792cSjcs } 698593d792cSjcs return -1; 699593d792cSjcs } 700