1d5add41dSVladimir Kondratyev /*- 24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 3d5add41dSVladimir Kondratyev * 4d5add41dSVladimir Kondratyev * Copyright (c) 2020, 2022 Vladimir Kondratyev <wulf@FreeBSD.org> 5d5add41dSVladimir Kondratyev * 6d5add41dSVladimir Kondratyev * Redistribution and use in source and binary forms, with or without 7d5add41dSVladimir Kondratyev * modification, are permitted provided that the following conditions 8d5add41dSVladimir Kondratyev * are met: 9d5add41dSVladimir Kondratyev * 1. Redistributions of source code must retain the above copyright 10d5add41dSVladimir Kondratyev * notice, this list of conditions and the following disclaimer. 11d5add41dSVladimir Kondratyev * 2. Redistributions in binary form must reproduce the above copyright 12d5add41dSVladimir Kondratyev * notice, this list of conditions and the following disclaimer in the 13d5add41dSVladimir Kondratyev * documentation and/or other materials provided with the distribution. 14d5add41dSVladimir Kondratyev * 15d5add41dSVladimir Kondratyev * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16d5add41dSVladimir Kondratyev * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17d5add41dSVladimir Kondratyev * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18d5add41dSVladimir Kondratyev * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19d5add41dSVladimir Kondratyev * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20d5add41dSVladimir Kondratyev * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21d5add41dSVladimir Kondratyev * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22d5add41dSVladimir Kondratyev * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23d5add41dSVladimir Kondratyev * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24d5add41dSVladimir Kondratyev * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25d5add41dSVladimir Kondratyev * SUCH DAMAGE. 26d5add41dSVladimir Kondratyev */ 27d5add41dSVladimir Kondratyev 28d5add41dSVladimir Kondratyev /* 29d5add41dSVladimir Kondratyev * Elan I2C Touchpad driver. Based on Linux driver. 30d5add41dSVladimir Kondratyev * https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/drivers/input/mouse/elan_i2c_core.c 31d5add41dSVladimir Kondratyev */ 32d5add41dSVladimir Kondratyev 33d5add41dSVladimir Kondratyev #include <sys/cdefs.h> 34d5add41dSVladimir Kondratyev __FBSDID("$FreeBSD$"); 35d5add41dSVladimir Kondratyev 36d5add41dSVladimir Kondratyev #include <sys/param.h> 37d5add41dSVladimir Kondratyev #include <sys/bus.h> 38d5add41dSVladimir Kondratyev #include <sys/endian.h> 39d5add41dSVladimir Kondratyev #include <sys/kernel.h> 40d5add41dSVladimir Kondratyev #include <sys/lock.h> 41d5add41dSVladimir Kondratyev #include <sys/malloc.h> 42d5add41dSVladimir Kondratyev #include <sys/module.h> 43d5add41dSVladimir Kondratyev #include <sys/mutex.h> 44d5add41dSVladimir Kondratyev #include <sys/sysctl.h> 45d5add41dSVladimir Kondratyev #include <sys/systm.h> 46d5add41dSVladimir Kondratyev 47d5add41dSVladimir Kondratyev #include <dev/evdev/evdev.h> 48d5add41dSVladimir Kondratyev #include <dev/evdev/input.h> 49d5add41dSVladimir Kondratyev 50d5add41dSVladimir Kondratyev #include <dev/iicbus/iic.h> 51d5add41dSVladimir Kondratyev #include <dev/iicbus/iicbus.h> 52d5add41dSVladimir Kondratyev 53d5add41dSVladimir Kondratyev #define HID_DEBUG_VAR ietp_debug 54d5add41dSVladimir Kondratyev #include <dev/hid/hid.h> 55d5add41dSVladimir Kondratyev #include <dev/hid/hidbus.h> 56d5add41dSVladimir Kondratyev #include <dev/hid/hidquirk.h> 57d5add41dSVladimir Kondratyev 58d5add41dSVladimir Kondratyev #ifdef HID_DEBUG 59d5add41dSVladimir Kondratyev static SYSCTL_NODE(_hw_hid, OID_AUTO, ietp, CTLFLAG_RW, 0, 60d5add41dSVladimir Kondratyev "Elantech Touchpad"); 61d5add41dSVladimir Kondratyev static int ietp_debug = 1; 62d5add41dSVladimir Kondratyev SYSCTL_INT(_hw_hid_ietp, OID_AUTO, debug, CTLFLAG_RWTUN, 63d5add41dSVladimir Kondratyev &ietp_debug, 1, "Debug level"); 64d5add41dSVladimir Kondratyev #endif 65d5add41dSVladimir Kondratyev 66d5add41dSVladimir Kondratyev #define IETP_PATTERN 0x0100 67d5add41dSVladimir Kondratyev #define IETP_UNIQUEID 0x0101 68d5add41dSVladimir Kondratyev #define IETP_FW_VERSION 0x0102 69d5add41dSVladimir Kondratyev #define IETP_IC_TYPE 0x0103 70d5add41dSVladimir Kondratyev #define IETP_OSM_VERSION 0x0103 71d5add41dSVladimir Kondratyev #define IETP_NSM_VERSION 0x0104 72d5add41dSVladimir Kondratyev #define IETP_TRACENUM 0x0105 73d5add41dSVladimir Kondratyev #define IETP_MAX_X_AXIS 0x0106 74d5add41dSVladimir Kondratyev #define IETP_MAX_Y_AXIS 0x0107 75d5add41dSVladimir Kondratyev #define IETP_RESOLUTION 0x0108 76d5add41dSVladimir Kondratyev #define IETP_PRESSURE 0x010A 77d5add41dSVladimir Kondratyev 78d5add41dSVladimir Kondratyev #define IETP_CONTROL 0x0300 79d5add41dSVladimir Kondratyev #define IETP_CTRL_ABSOLUTE 0x0001 80d5add41dSVladimir Kondratyev #define IETP_CTRL_STANDARD 0x0000 81d5add41dSVladimir Kondratyev 82d5add41dSVladimir Kondratyev #define IETP_REPORT_LEN_LO 32 83d5add41dSVladimir Kondratyev #define IETP_REPORT_LEN_HI 37 84d5add41dSVladimir Kondratyev #define IETP_MAX_FINGERS 5 85d5add41dSVladimir Kondratyev 86d5add41dSVladimir Kondratyev #define IETP_REPORT_ID_LO 0x5D 87d5add41dSVladimir Kondratyev #define IETP_REPORT_ID_HI 0x60 88d5add41dSVladimir Kondratyev 89d5add41dSVladimir Kondratyev #define IETP_TOUCH_INFO 1 90d5add41dSVladimir Kondratyev #define IETP_FINGER_DATA 2 91d5add41dSVladimir Kondratyev #define IETP_FINGER_DATA_LEN 5 92d5add41dSVladimir Kondratyev #define IETP_HOVER_INFO 28 93d5add41dSVladimir Kondratyev #define IETP_WH_DATA 31 94d5add41dSVladimir Kondratyev 95d5add41dSVladimir Kondratyev #define IETP_TOUCH_LMB (1 << 0) 96d5add41dSVladimir Kondratyev #define IETP_TOUCH_RMB (1 << 1) 97d5add41dSVladimir Kondratyev #define IETP_TOUCH_MMB (1 << 2) 98d5add41dSVladimir Kondratyev 99d5add41dSVladimir Kondratyev #define IETP_MAX_PRESSURE 255 100d5add41dSVladimir Kondratyev #define IETP_FWIDTH_REDUCE 90 101d5add41dSVladimir Kondratyev #define IETP_FINGER_MAX_WIDTH 15 102d5add41dSVladimir Kondratyev #define IETP_PRESSURE_BASE 25 103d5add41dSVladimir Kondratyev 104d5add41dSVladimir Kondratyev struct ietp_softc { 105d5add41dSVladimir Kondratyev device_t dev; 106d5add41dSVladimir Kondratyev 107d5add41dSVladimir Kondratyev struct evdev_dev *evdev; 108d5add41dSVladimir Kondratyev uint8_t report_id; 109d5add41dSVladimir Kondratyev hid_size_t report_len; 110d5add41dSVladimir Kondratyev 111d5add41dSVladimir Kondratyev uint16_t product_id; 112d5add41dSVladimir Kondratyev uint16_t ic_type; 113d5add41dSVladimir Kondratyev 114d5add41dSVladimir Kondratyev int32_t pressure_base; 115d5add41dSVladimir Kondratyev uint16_t max_x; 116d5add41dSVladimir Kondratyev uint16_t max_y; 117d5add41dSVladimir Kondratyev uint16_t trace_x; 118d5add41dSVladimir Kondratyev uint16_t trace_y; 119d5add41dSVladimir Kondratyev uint16_t res_x; /* dots per mm */ 120d5add41dSVladimir Kondratyev uint16_t res_y; 1218d5fef85SAymeric Wibo bool hi_precision; 122d5add41dSVladimir Kondratyev bool is_clickpad; 123d5add41dSVladimir Kondratyev bool has_3buttons; 124d5add41dSVladimir Kondratyev }; 125d5add41dSVladimir Kondratyev 126d5add41dSVladimir Kondratyev static evdev_open_t ietp_ev_open; 127d5add41dSVladimir Kondratyev static evdev_close_t ietp_ev_close; 128d5add41dSVladimir Kondratyev static hid_intr_t ietp_intr; 129d5add41dSVladimir Kondratyev 130d5add41dSVladimir Kondratyev static int ietp_probe(struct ietp_softc *); 131d5add41dSVladimir Kondratyev static int ietp_attach(struct ietp_softc *); 132d5add41dSVladimir Kondratyev static int ietp_detach(struct ietp_softc *); 133d5add41dSVladimir Kondratyev static int32_t ietp_res2dpmm(uint8_t, bool); 134d5add41dSVladimir Kondratyev 1358d5fef85SAymeric Wibo static device_identify_t ietp_iic_identify; 136d5add41dSVladimir Kondratyev static device_probe_t ietp_iic_probe; 137d5add41dSVladimir Kondratyev static device_attach_t ietp_iic_attach; 138d5add41dSVladimir Kondratyev static device_detach_t ietp_iic_detach; 139d5add41dSVladimir Kondratyev static device_resume_t ietp_iic_resume; 140d5add41dSVladimir Kondratyev 141d5add41dSVladimir Kondratyev static int ietp_iic_read_reg(device_t, uint16_t, size_t, void *); 142d5add41dSVladimir Kondratyev static int ietp_iic_write_reg(device_t, uint16_t, uint16_t); 143d5add41dSVladimir Kondratyev static int ietp_iic_set_absolute_mode(device_t, bool); 144d5add41dSVladimir Kondratyev 145d5add41dSVladimir Kondratyev #define IETP_IIC_DEV(pnp) \ 146d5add41dSVladimir Kondratyev { HID_TLC(HUP_GENERIC_DESKTOP, HUG_MOUSE), HID_BUS(BUS_I2C), HID_PNP(pnp) } 147d5add41dSVladimir Kondratyev 148d5add41dSVladimir Kondratyev static const struct hid_device_id ietp_iic_devs[] = { 149d5add41dSVladimir Kondratyev IETP_IIC_DEV("ELAN0000"), 150d5add41dSVladimir Kondratyev IETP_IIC_DEV("ELAN0100"), 151d5add41dSVladimir Kondratyev IETP_IIC_DEV("ELAN0600"), 152d5add41dSVladimir Kondratyev IETP_IIC_DEV("ELAN0601"), 153d5add41dSVladimir Kondratyev IETP_IIC_DEV("ELAN0602"), 154d5add41dSVladimir Kondratyev IETP_IIC_DEV("ELAN0603"), 155d5add41dSVladimir Kondratyev IETP_IIC_DEV("ELAN0604"), 156d5add41dSVladimir Kondratyev IETP_IIC_DEV("ELAN0605"), 157d5add41dSVladimir Kondratyev IETP_IIC_DEV("ELAN0606"), 158d5add41dSVladimir Kondratyev IETP_IIC_DEV("ELAN0607"), 159d5add41dSVladimir Kondratyev IETP_IIC_DEV("ELAN0608"), 160d5add41dSVladimir Kondratyev IETP_IIC_DEV("ELAN0609"), 161d5add41dSVladimir Kondratyev IETP_IIC_DEV("ELAN060B"), 162d5add41dSVladimir Kondratyev IETP_IIC_DEV("ELAN060C"), 163d5add41dSVladimir Kondratyev IETP_IIC_DEV("ELAN060F"), 164d5add41dSVladimir Kondratyev IETP_IIC_DEV("ELAN0610"), 165d5add41dSVladimir Kondratyev IETP_IIC_DEV("ELAN0611"), 166d5add41dSVladimir Kondratyev IETP_IIC_DEV("ELAN0612"), 167d5add41dSVladimir Kondratyev IETP_IIC_DEV("ELAN0615"), 168d5add41dSVladimir Kondratyev IETP_IIC_DEV("ELAN0616"), 169d5add41dSVladimir Kondratyev IETP_IIC_DEV("ELAN0617"), 170d5add41dSVladimir Kondratyev IETP_IIC_DEV("ELAN0618"), 171d5add41dSVladimir Kondratyev IETP_IIC_DEV("ELAN0619"), 172d5add41dSVladimir Kondratyev IETP_IIC_DEV("ELAN061A"), 173d5add41dSVladimir Kondratyev IETP_IIC_DEV("ELAN061B"), 174d5add41dSVladimir Kondratyev IETP_IIC_DEV("ELAN061C"), 175d5add41dSVladimir Kondratyev IETP_IIC_DEV("ELAN061D"), 176d5add41dSVladimir Kondratyev IETP_IIC_DEV("ELAN061E"), 177d5add41dSVladimir Kondratyev IETP_IIC_DEV("ELAN061F"), 178d5add41dSVladimir Kondratyev IETP_IIC_DEV("ELAN0620"), 179d5add41dSVladimir Kondratyev IETP_IIC_DEV("ELAN0621"), 180d5add41dSVladimir Kondratyev IETP_IIC_DEV("ELAN0622"), 181d5add41dSVladimir Kondratyev IETP_IIC_DEV("ELAN0623"), 182d5add41dSVladimir Kondratyev IETP_IIC_DEV("ELAN0624"), 183d5add41dSVladimir Kondratyev IETP_IIC_DEV("ELAN0625"), 184d5add41dSVladimir Kondratyev IETP_IIC_DEV("ELAN0626"), 185d5add41dSVladimir Kondratyev IETP_IIC_DEV("ELAN0627"), 186d5add41dSVladimir Kondratyev IETP_IIC_DEV("ELAN0628"), 187d5add41dSVladimir Kondratyev IETP_IIC_DEV("ELAN0629"), 188d5add41dSVladimir Kondratyev IETP_IIC_DEV("ELAN062A"), 189d5add41dSVladimir Kondratyev IETP_IIC_DEV("ELAN062B"), 190d5add41dSVladimir Kondratyev IETP_IIC_DEV("ELAN062C"), 191d5add41dSVladimir Kondratyev IETP_IIC_DEV("ELAN062D"), 192d5add41dSVladimir Kondratyev IETP_IIC_DEV("ELAN062E"), /* Lenovo V340 Whiskey Lake U */ 193d5add41dSVladimir Kondratyev IETP_IIC_DEV("ELAN062F"), /* Lenovo V340 Comet Lake U */ 194d5add41dSVladimir Kondratyev IETP_IIC_DEV("ELAN0631"), 195d5add41dSVladimir Kondratyev IETP_IIC_DEV("ELAN0632"), 196d5add41dSVladimir Kondratyev IETP_IIC_DEV("ELAN0633"), /* Lenovo S145 */ 197d5add41dSVladimir Kondratyev IETP_IIC_DEV("ELAN0634"), /* Lenovo V340 Ice lake */ 198d5add41dSVladimir Kondratyev IETP_IIC_DEV("ELAN0635"), /* Lenovo V1415-IIL */ 199d5add41dSVladimir Kondratyev IETP_IIC_DEV("ELAN0636"), /* Lenovo V1415-Dali */ 200d5add41dSVladimir Kondratyev IETP_IIC_DEV("ELAN0637"), /* Lenovo V1415-IGLR */ 201d5add41dSVladimir Kondratyev IETP_IIC_DEV("ELAN1000"), 202d5add41dSVladimir Kondratyev }; 203d5add41dSVladimir Kondratyev 2048d5fef85SAymeric Wibo static uint8_t const ietp_dummy_rdesc[] = { 2058d5fef85SAymeric Wibo 0x05, HUP_GENERIC_DESKTOP, /* Usage Page (Generic Desktop Ctrls) */ 2068d5fef85SAymeric Wibo 0x09, HUG_MOUSE, /* Usage (Mouse) */ 2078d5fef85SAymeric Wibo 0xA1, 0x01, /* Collection (Application) */ 2088d5fef85SAymeric Wibo 0x09, 0x01, /* Usage (0x01) */ 2098d5fef85SAymeric Wibo 0x95, IETP_REPORT_LEN_LO, /* Report Count (IETP_REPORT_LEN_LO) */ 2108d5fef85SAymeric Wibo 0x75, 0x08, /* Report Size (8) */ 2118d5fef85SAymeric Wibo 0x81, 0x02, /* Input (Data,Var,Abs) */ 2128d5fef85SAymeric Wibo 0xC0, /* End Collection */ 2138d5fef85SAymeric Wibo }; 2148d5fef85SAymeric Wibo 215d5add41dSVladimir Kondratyev static const struct evdev_methods ietp_evdev_methods = { 216d5add41dSVladimir Kondratyev .ev_open = &ietp_ev_open, 217d5add41dSVladimir Kondratyev .ev_close = &ietp_ev_close, 218d5add41dSVladimir Kondratyev }; 219d5add41dSVladimir Kondratyev 220d5add41dSVladimir Kondratyev static int 221d5add41dSVladimir Kondratyev ietp_ev_open(struct evdev_dev *evdev) 222d5add41dSVladimir Kondratyev { 223*4151ac9fSVladimir Kondratyev return (hid_intr_start(evdev_get_softc(evdev))); 224d5add41dSVladimir Kondratyev } 225d5add41dSVladimir Kondratyev 226d5add41dSVladimir Kondratyev static int 227d5add41dSVladimir Kondratyev ietp_ev_close(struct evdev_dev *evdev) 228d5add41dSVladimir Kondratyev { 229*4151ac9fSVladimir Kondratyev return (hid_intr_stop(evdev_get_softc(evdev))); 230d5add41dSVladimir Kondratyev } 231d5add41dSVladimir Kondratyev 232d5add41dSVladimir Kondratyev static int 233d5add41dSVladimir Kondratyev ietp_probe(struct ietp_softc *sc) 234d5add41dSVladimir Kondratyev { 235d5add41dSVladimir Kondratyev if (hidbus_find_child(device_get_parent(sc->dev), 236d5add41dSVladimir Kondratyev HID_USAGE2(HUP_DIGITIZERS, HUD_TOUCHPAD)) != NULL) { 237d5add41dSVladimir Kondratyev DPRINTFN(5, "Ignore HID-compatible touchpad on %s\n", 238d5add41dSVladimir Kondratyev device_get_nameunit(device_get_parent(sc->dev))); 239d5add41dSVladimir Kondratyev return (ENXIO); 240d5add41dSVladimir Kondratyev } 241d5add41dSVladimir Kondratyev 242d5add41dSVladimir Kondratyev device_set_desc(sc->dev, "Elan Touchpad"); 243d5add41dSVladimir Kondratyev 244d5add41dSVladimir Kondratyev return (BUS_PROBE_DEFAULT); 245d5add41dSVladimir Kondratyev } 246d5add41dSVladimir Kondratyev 247d5add41dSVladimir Kondratyev static int 248d5add41dSVladimir Kondratyev ietp_attach(struct ietp_softc *sc) 249d5add41dSVladimir Kondratyev { 250d5add41dSVladimir Kondratyev const struct hid_device_info *hw = hid_get_device_info(sc->dev); 251d5add41dSVladimir Kondratyev void *d_ptr; 252d5add41dSVladimir Kondratyev hid_size_t d_len; 253d5add41dSVladimir Kondratyev int32_t minor, major; 254d5add41dSVladimir Kondratyev int error; 255d5add41dSVladimir Kondratyev 2568d5fef85SAymeric Wibo sc->report_id = sc->hi_precision ? 257d5add41dSVladimir Kondratyev IETP_REPORT_ID_HI : IETP_REPORT_ID_LO; 2588d5fef85SAymeric Wibo sc->report_len = sc->hi_precision ? 259d5add41dSVladimir Kondratyev IETP_REPORT_LEN_HI : IETP_REPORT_LEN_LO; 260d5add41dSVladimir Kondratyev 261d5add41dSVladimir Kondratyev /* Try to detect 3-rd button by relative mouse TLC */ 262d5add41dSVladimir Kondratyev if (!sc->is_clickpad) { 263d5add41dSVladimir Kondratyev error = hid_get_report_descr(sc->dev, &d_ptr, &d_len); 264d5add41dSVladimir Kondratyev if (error != 0) { 265d5add41dSVladimir Kondratyev device_printf(sc->dev, "could not retrieve report " 266d5add41dSVladimir Kondratyev "descriptor from device: %d\n", error); 267d5add41dSVladimir Kondratyev return (ENXIO); 268d5add41dSVladimir Kondratyev } 269d5add41dSVladimir Kondratyev if (hidbus_locate(d_ptr, d_len, HID_USAGE2(HUP_BUTTON, 3), 270d5add41dSVladimir Kondratyev hid_input, hidbus_get_index(sc->dev), 0, NULL, NULL, NULL, 271d5add41dSVladimir Kondratyev NULL)) 272d5add41dSVladimir Kondratyev sc->has_3buttons = true; 273d5add41dSVladimir Kondratyev } 274d5add41dSVladimir Kondratyev 275d5add41dSVladimir Kondratyev sc->evdev = evdev_alloc(); 276d5add41dSVladimir Kondratyev evdev_set_name(sc->evdev, device_get_desc(sc->dev)); 277d5add41dSVladimir Kondratyev evdev_set_phys(sc->evdev, device_get_nameunit(sc->dev)); 278d5add41dSVladimir Kondratyev evdev_set_id(sc->evdev, hw->idBus, hw->idVendor, hw->idProduct, 279d5add41dSVladimir Kondratyev hw->idVersion); 280d5add41dSVladimir Kondratyev evdev_set_serial(sc->evdev, hw->serial); 281d5add41dSVladimir Kondratyev evdev_set_methods(sc->evdev, sc->dev, &ietp_evdev_methods); 282d5add41dSVladimir Kondratyev evdev_set_flag(sc->evdev, EVDEV_FLAG_MT_STCOMPAT); 283d5add41dSVladimir Kondratyev evdev_set_flag(sc->evdev, EVDEV_FLAG_EXT_EPOCH); /* hidbus child */ 284d5add41dSVladimir Kondratyev 285d5add41dSVladimir Kondratyev evdev_support_event(sc->evdev, EV_SYN); 286d5add41dSVladimir Kondratyev evdev_support_event(sc->evdev, EV_ABS); 287d5add41dSVladimir Kondratyev evdev_support_event(sc->evdev, EV_KEY); 288d5add41dSVladimir Kondratyev evdev_support_prop(sc->evdev, INPUT_PROP_POINTER); 289d5add41dSVladimir Kondratyev evdev_support_key(sc->evdev, BTN_LEFT); 290d5add41dSVladimir Kondratyev if (sc->is_clickpad) { 291d5add41dSVladimir Kondratyev evdev_support_prop(sc->evdev, INPUT_PROP_BUTTONPAD); 292d5add41dSVladimir Kondratyev } else { 293d5add41dSVladimir Kondratyev evdev_support_key(sc->evdev, BTN_RIGHT); 294d5add41dSVladimir Kondratyev if (sc->has_3buttons) 295d5add41dSVladimir Kondratyev evdev_support_key(sc->evdev, BTN_MIDDLE); 296d5add41dSVladimir Kondratyev } 297d5add41dSVladimir Kondratyev 298d5add41dSVladimir Kondratyev major = IETP_FINGER_MAX_WIDTH * MAX(sc->trace_x, sc->trace_y); 299d5add41dSVladimir Kondratyev minor = IETP_FINGER_MAX_WIDTH * MIN(sc->trace_x, sc->trace_y); 300d5add41dSVladimir Kondratyev 301d5add41dSVladimir Kondratyev evdev_support_abs(sc->evdev, ABS_MT_SLOT, 302d5add41dSVladimir Kondratyev 0, IETP_MAX_FINGERS - 1, 0, 0, 0); 303d5add41dSVladimir Kondratyev evdev_support_abs(sc->evdev, ABS_MT_TRACKING_ID, 304d5add41dSVladimir Kondratyev -1, IETP_MAX_FINGERS - 1, 0, 0, 0); 305d5add41dSVladimir Kondratyev evdev_support_abs(sc->evdev, ABS_MT_POSITION_X, 306d5add41dSVladimir Kondratyev 0, sc->max_x, 0, 0, sc->res_x); 307d5add41dSVladimir Kondratyev evdev_support_abs(sc->evdev, ABS_MT_POSITION_Y, 308d5add41dSVladimir Kondratyev 0, sc->max_y, 0, 0, sc->res_y); 309d5add41dSVladimir Kondratyev evdev_support_abs(sc->evdev, ABS_MT_PRESSURE, 310d5add41dSVladimir Kondratyev 0, IETP_MAX_PRESSURE, 0, 0, 0); 311d5add41dSVladimir Kondratyev evdev_support_abs(sc->evdev, ABS_MT_ORIENTATION, 0, 1, 0, 0, 0); 312d5add41dSVladimir Kondratyev evdev_support_abs(sc->evdev, ABS_MT_TOUCH_MAJOR, 0, major, 0, 0, 0); 313d5add41dSVladimir Kondratyev evdev_support_abs(sc->evdev, ABS_MT_TOUCH_MINOR, 0, minor, 0, 0, 0); 314d5add41dSVladimir Kondratyev evdev_support_abs(sc->evdev, ABS_DISTANCE, 0, 1, 0, 0, 0); 315d5add41dSVladimir Kondratyev 316d5add41dSVladimir Kondratyev error = evdev_register(sc->evdev); 317d5add41dSVladimir Kondratyev if (error != 0) { 318d5add41dSVladimir Kondratyev ietp_detach(sc); 319d5add41dSVladimir Kondratyev return (ENOMEM); 320d5add41dSVladimir Kondratyev } 321d5add41dSVladimir Kondratyev 322d5add41dSVladimir Kondratyev hidbus_set_intr(sc->dev, ietp_intr, sc); 323d5add41dSVladimir Kondratyev 324d5add41dSVladimir Kondratyev device_printf(sc->dev, "[%d:%d], %s\n", sc->max_x, sc->max_y, 325d5add41dSVladimir Kondratyev sc->is_clickpad ? "clickpad" : 326d5add41dSVladimir Kondratyev sc->has_3buttons ? "3 buttons" : "2 buttons"); 327d5add41dSVladimir Kondratyev 328d5add41dSVladimir Kondratyev return (0); 329d5add41dSVladimir Kondratyev } 330d5add41dSVladimir Kondratyev 331d5add41dSVladimir Kondratyev static int 332d5add41dSVladimir Kondratyev ietp_detach(struct ietp_softc *sc) 333d5add41dSVladimir Kondratyev { 334d5add41dSVladimir Kondratyev evdev_free(sc->evdev); 335d5add41dSVladimir Kondratyev 336d5add41dSVladimir Kondratyev return (0); 337d5add41dSVladimir Kondratyev } 338d5add41dSVladimir Kondratyev 339d5add41dSVladimir Kondratyev static void 340d5add41dSVladimir Kondratyev ietp_intr(void *context, void *buf, hid_size_t len) 341d5add41dSVladimir Kondratyev { 342d5add41dSVladimir Kondratyev struct ietp_softc *sc = context; 343d5add41dSVladimir Kondratyev union evdev_mt_slot slot_data; 344d5add41dSVladimir Kondratyev uint8_t *report, *fdata; 345d5add41dSVladimir Kondratyev int32_t finger; 346d5add41dSVladimir Kondratyev int32_t x, y, w, h, wh; 347d5add41dSVladimir Kondratyev 348d5add41dSVladimir Kondratyev /* we seem to get 0 length reports sometimes, ignore them */ 3498d5fef85SAymeric Wibo if (len == 0) 350d5add41dSVladimir Kondratyev return; 3518d5fef85SAymeric Wibo if (len != sc->report_len) { 3528d5fef85SAymeric Wibo DPRINTF("wrong report length (%d vs %d expected)", len, sc->report_len); 3538d5fef85SAymeric Wibo return; 3548d5fef85SAymeric Wibo } 3558d5fef85SAymeric Wibo 3568d5fef85SAymeric Wibo report = buf; 3578d5fef85SAymeric Wibo if (*report != sc->report_id) 3588d5fef85SAymeric Wibo return; 3598d5fef85SAymeric Wibo 3608d5fef85SAymeric Wibo evdev_push_key(sc->evdev, BTN_LEFT, 3618d5fef85SAymeric Wibo report[IETP_TOUCH_INFO] & IETP_TOUCH_LMB); 3628d5fef85SAymeric Wibo evdev_push_key(sc->evdev, BTN_MIDDLE, 3638d5fef85SAymeric Wibo report[IETP_TOUCH_INFO] & IETP_TOUCH_MMB); 3648d5fef85SAymeric Wibo evdev_push_key(sc->evdev, BTN_RIGHT, 3658d5fef85SAymeric Wibo report[IETP_TOUCH_INFO] & IETP_TOUCH_RMB); 3668d5fef85SAymeric Wibo evdev_push_abs(sc->evdev, ABS_DISTANCE, 3678d5fef85SAymeric Wibo (report[IETP_HOVER_INFO] & 0x40) >> 6); 368d5add41dSVladimir Kondratyev 369d5add41dSVladimir Kondratyev for (finger = 0, fdata = report + IETP_FINGER_DATA; 370d5add41dSVladimir Kondratyev finger < IETP_MAX_FINGERS; 371d5add41dSVladimir Kondratyev finger++, fdata += IETP_FINGER_DATA_LEN) { 372d5add41dSVladimir Kondratyev if ((report[IETP_TOUCH_INFO] & (1 << (finger + 3))) != 0) { 3738d5fef85SAymeric Wibo if (sc->hi_precision) { 374d5add41dSVladimir Kondratyev x = fdata[0] << 8 | fdata[1]; 375d5add41dSVladimir Kondratyev y = fdata[2] << 8 | fdata[3]; 376d5add41dSVladimir Kondratyev wh = report[IETP_WH_DATA + finger]; 377d5add41dSVladimir Kondratyev } else { 378d5add41dSVladimir Kondratyev x = (fdata[0] & 0xf0) << 4 | fdata[1]; 379d5add41dSVladimir Kondratyev y = (fdata[0] & 0x0f) << 8 | fdata[2]; 380d5add41dSVladimir Kondratyev wh = fdata[3]; 381d5add41dSVladimir Kondratyev } 382d5add41dSVladimir Kondratyev 383d5add41dSVladimir Kondratyev if (x > sc->max_x || y > sc->max_y) { 384d5add41dSVladimir Kondratyev DPRINTF("[%d] x=%d y=%d over max (%d, %d)", 385d5add41dSVladimir Kondratyev finger, x, y, sc->max_x, sc->max_y); 386d5add41dSVladimir Kondratyev continue; 387d5add41dSVladimir Kondratyev } 388d5add41dSVladimir Kondratyev 389d5add41dSVladimir Kondratyev /* Reduce trace size to not treat large finger as palm */ 390d5add41dSVladimir Kondratyev w = (wh & 0x0F) * (sc->trace_x - IETP_FWIDTH_REDUCE); 391d5add41dSVladimir Kondratyev h = (wh >> 4) * (sc->trace_y - IETP_FWIDTH_REDUCE); 392d5add41dSVladimir Kondratyev 393d5add41dSVladimir Kondratyev slot_data = (union evdev_mt_slot) { 394d5add41dSVladimir Kondratyev .id = finger, 395d5add41dSVladimir Kondratyev .x = x, 396d5add41dSVladimir Kondratyev .y = sc->max_y - y, 397d5add41dSVladimir Kondratyev .p = MIN((int32_t)fdata[4] + sc->pressure_base, 398d5add41dSVladimir Kondratyev IETP_MAX_PRESSURE), 399d5add41dSVladimir Kondratyev .ori = w > h ? 1 : 0, 400d5add41dSVladimir Kondratyev .maj = MAX(w, h), 401d5add41dSVladimir Kondratyev .min = MIN(w, h), 402d5add41dSVladimir Kondratyev }; 403d5add41dSVladimir Kondratyev evdev_mt_push_slot(sc->evdev, finger, &slot_data); 404d5add41dSVladimir Kondratyev } else { 405d5add41dSVladimir Kondratyev evdev_push_abs(sc->evdev, ABS_MT_SLOT, finger); 406d5add41dSVladimir Kondratyev evdev_push_abs(sc->evdev, ABS_MT_TRACKING_ID, -1); 407d5add41dSVladimir Kondratyev } 408d5add41dSVladimir Kondratyev } 409d5add41dSVladimir Kondratyev 410d5add41dSVladimir Kondratyev evdev_sync(sc->evdev); 411d5add41dSVladimir Kondratyev } 412d5add41dSVladimir Kondratyev 413d5add41dSVladimir Kondratyev static int32_t 4148d5fef85SAymeric Wibo ietp_res2dpmm(uint8_t res, bool hi_precision) 415d5add41dSVladimir Kondratyev { 416d5add41dSVladimir Kondratyev int32_t dpi; 417d5add41dSVladimir Kondratyev 4188d5fef85SAymeric Wibo dpi = hi_precision ? 300 + res * 100 : 790 + res * 10; 419d5add41dSVladimir Kondratyev 420d5add41dSVladimir Kondratyev return (dpi * 10 /254); 421d5add41dSVladimir Kondratyev } 422d5add41dSVladimir Kondratyev 4238d5fef85SAymeric Wibo static void 4248d5fef85SAymeric Wibo ietp_iic_identify(driver_t *driver, device_t parent) 4258d5fef85SAymeric Wibo { 4268d5fef85SAymeric Wibo void *d_ptr; 4278d5fef85SAymeric Wibo hid_size_t d_len; 4288d5fef85SAymeric Wibo int isize; 4298d5fef85SAymeric Wibo uint8_t iid; 4308d5fef85SAymeric Wibo 4318d5fef85SAymeric Wibo if (HIDBUS_LOOKUP_ID(parent, ietp_iic_devs) == NULL) 4328d5fef85SAymeric Wibo return; 4338d5fef85SAymeric Wibo if (hid_get_report_descr(parent, &d_ptr, &d_len) != 0) 4348d5fef85SAymeric Wibo return; 4358d5fef85SAymeric Wibo 4368d5fef85SAymeric Wibo /* 4378d5fef85SAymeric Wibo * Some Elantech trackpads have a mangled HID report descriptor, which 4388d5fef85SAymeric Wibo * reads as having an incorrect input size (i.e. < IETP_REPORT_LEN_LO). 4398d5fef85SAymeric Wibo * If the input size is incorrect, load a dummy report descriptor. 4408d5fef85SAymeric Wibo */ 4418d5fef85SAymeric Wibo 4428d5fef85SAymeric Wibo isize = hid_report_size_max(d_ptr, d_len, hid_input, &iid); 4438d5fef85SAymeric Wibo if (isize >= IETP_REPORT_LEN_LO) 4448d5fef85SAymeric Wibo return; 4458d5fef85SAymeric Wibo 4468d5fef85SAymeric Wibo hid_set_report_descr(parent, ietp_dummy_rdesc, 4478d5fef85SAymeric Wibo sizeof(ietp_dummy_rdesc)); 4488d5fef85SAymeric Wibo } 4498d5fef85SAymeric Wibo 450d5add41dSVladimir Kondratyev static int 451d5add41dSVladimir Kondratyev ietp_iic_probe(device_t dev) 452d5add41dSVladimir Kondratyev { 453d5add41dSVladimir Kondratyev struct ietp_softc *sc = device_get_softc(dev); 454d5add41dSVladimir Kondratyev device_t iichid; 455d5add41dSVladimir Kondratyev int error; 456d5add41dSVladimir Kondratyev 457d5add41dSVladimir Kondratyev error = HIDBUS_LOOKUP_DRIVER_INFO(dev, ietp_iic_devs); 458d5add41dSVladimir Kondratyev if (error != 0) 459d5add41dSVladimir Kondratyev return (error); 460d5add41dSVladimir Kondratyev 461d5add41dSVladimir Kondratyev iichid = device_get_parent(device_get_parent(dev)); 462d5add41dSVladimir Kondratyev if (device_get_devclass(iichid) != devclass_find("iichid")) 463d5add41dSVladimir Kondratyev return (ENXIO); 464d5add41dSVladimir Kondratyev 465d5add41dSVladimir Kondratyev sc->dev = dev; 466d5add41dSVladimir Kondratyev 467d5add41dSVladimir Kondratyev return (ietp_probe(sc)); 468d5add41dSVladimir Kondratyev } 469d5add41dSVladimir Kondratyev 470d5add41dSVladimir Kondratyev static int 471d5add41dSVladimir Kondratyev ietp_iic_attach(device_t dev) 472d5add41dSVladimir Kondratyev { 473d5add41dSVladimir Kondratyev struct ietp_softc *sc = device_get_softc(dev); 474d5add41dSVladimir Kondratyev uint16_t buf, reg; 475d5add41dSVladimir Kondratyev uint8_t *buf8; 476d5add41dSVladimir Kondratyev uint8_t pattern; 477d5add41dSVladimir Kondratyev 478d5add41dSVladimir Kondratyev buf8 = (uint8_t *)&buf; 479d5add41dSVladimir Kondratyev 480d5add41dSVladimir Kondratyev if (ietp_iic_read_reg(dev, IETP_UNIQUEID, sizeof(buf), &buf) != 0) { 481d5add41dSVladimir Kondratyev device_printf(sc->dev, "failed reading product ID\n"); 482d5add41dSVladimir Kondratyev return (EIO); 483d5add41dSVladimir Kondratyev } 484d5add41dSVladimir Kondratyev sc->product_id = le16toh(buf); 485d5add41dSVladimir Kondratyev 486d5add41dSVladimir Kondratyev if (ietp_iic_read_reg(dev, IETP_PATTERN, sizeof(buf), &buf) != 0) { 487d5add41dSVladimir Kondratyev device_printf(sc->dev, "failed reading pattern\n"); 488d5add41dSVladimir Kondratyev return (EIO); 489d5add41dSVladimir Kondratyev } 490d5add41dSVladimir Kondratyev pattern = buf == 0xFFFF ? 0 : buf8[1]; 4918d5fef85SAymeric Wibo sc->hi_precision = pattern >= 0x02; 492d5add41dSVladimir Kondratyev 493d5add41dSVladimir Kondratyev reg = pattern >= 0x01 ? IETP_IC_TYPE : IETP_OSM_VERSION; 494d5add41dSVladimir Kondratyev if (ietp_iic_read_reg(dev, reg, sizeof(buf), &buf) != 0) { 495d5add41dSVladimir Kondratyev device_printf(sc->dev, "failed reading IC type\n"); 496d5add41dSVladimir Kondratyev return (EIO); 497d5add41dSVladimir Kondratyev } 498d5add41dSVladimir Kondratyev sc->ic_type = pattern >= 0x01 ? be16toh(buf) : buf8[1]; 499d5add41dSVladimir Kondratyev 500d5add41dSVladimir Kondratyev if (ietp_iic_read_reg(dev, IETP_NSM_VERSION, sizeof(buf), &buf) != 0) { 501d5add41dSVladimir Kondratyev device_printf(sc->dev, "failed reading SM version\n"); 502d5add41dSVladimir Kondratyev return (EIO); 503d5add41dSVladimir Kondratyev } 504d5add41dSVladimir Kondratyev sc->is_clickpad = (buf8[0] & 0x10) != 0; 505d5add41dSVladimir Kondratyev 506d5add41dSVladimir Kondratyev if (ietp_iic_set_absolute_mode(dev, true) != 0) { 507d5add41dSVladimir Kondratyev device_printf(sc->dev, "failed to set absolute mode\n"); 508d5add41dSVladimir Kondratyev return (EIO); 509d5add41dSVladimir Kondratyev } 510d5add41dSVladimir Kondratyev 511d5add41dSVladimir Kondratyev if (ietp_iic_read_reg(dev, IETP_MAX_X_AXIS, sizeof(buf), &buf) != 0) { 512d5add41dSVladimir Kondratyev device_printf(sc->dev, "failed reading max x\n"); 513d5add41dSVladimir Kondratyev return (EIO); 514d5add41dSVladimir Kondratyev } 515d5add41dSVladimir Kondratyev sc->max_x = le16toh(buf); 516d5add41dSVladimir Kondratyev 517d5add41dSVladimir Kondratyev if (ietp_iic_read_reg(dev, IETP_MAX_Y_AXIS, sizeof(buf), &buf) != 0) { 518d5add41dSVladimir Kondratyev device_printf(sc->dev, "failed reading max y\n"); 519d5add41dSVladimir Kondratyev return (EIO); 520d5add41dSVladimir Kondratyev } 521d5add41dSVladimir Kondratyev sc->max_y = le16toh(buf); 522d5add41dSVladimir Kondratyev 523d5add41dSVladimir Kondratyev if (ietp_iic_read_reg(dev, IETP_TRACENUM, sizeof(buf), &buf) != 0) { 524d5add41dSVladimir Kondratyev device_printf(sc->dev, "failed reading trace info\n"); 525d5add41dSVladimir Kondratyev return (EIO); 526d5add41dSVladimir Kondratyev } 527d5add41dSVladimir Kondratyev sc->trace_x = sc->max_x / buf8[0]; 528d5add41dSVladimir Kondratyev sc->trace_y = sc->max_y / buf8[1]; 529d5add41dSVladimir Kondratyev 530d5add41dSVladimir Kondratyev if (ietp_iic_read_reg(dev, IETP_PRESSURE, sizeof(buf), &buf) != 0) { 531d5add41dSVladimir Kondratyev device_printf(sc->dev, "failed reading pressure format\n"); 532d5add41dSVladimir Kondratyev return (EIO); 533d5add41dSVladimir Kondratyev } 534d5add41dSVladimir Kondratyev sc->pressure_base = (buf8[0] & 0x10) ? 0 : IETP_PRESSURE_BASE; 535d5add41dSVladimir Kondratyev 536d5add41dSVladimir Kondratyev if (ietp_iic_read_reg(dev, IETP_RESOLUTION, sizeof(buf), &buf) != 0) { 537d5add41dSVladimir Kondratyev device_printf(sc->dev, "failed reading resolution\n"); 538d5add41dSVladimir Kondratyev return (EIO); 539d5add41dSVladimir Kondratyev } 540d5add41dSVladimir Kondratyev /* Conversion from internal format to dot per mm */ 5418d5fef85SAymeric Wibo sc->res_x = ietp_res2dpmm(buf8[0], sc->hi_precision); 5428d5fef85SAymeric Wibo sc->res_y = ietp_res2dpmm(buf8[1], sc->hi_precision); 543d5add41dSVladimir Kondratyev 544d5add41dSVladimir Kondratyev return (ietp_attach(sc)); 545d5add41dSVladimir Kondratyev } 546d5add41dSVladimir Kondratyev 547d5add41dSVladimir Kondratyev static int 548d5add41dSVladimir Kondratyev ietp_iic_detach(device_t dev) 549d5add41dSVladimir Kondratyev { 550d5add41dSVladimir Kondratyev struct ietp_softc *sc = device_get_softc(dev); 551d5add41dSVladimir Kondratyev 552d5add41dSVladimir Kondratyev if (ietp_iic_set_absolute_mode(dev, false) != 0) 553d5add41dSVladimir Kondratyev device_printf(dev, "failed setting standard mode\n"); 554d5add41dSVladimir Kondratyev 555d5add41dSVladimir Kondratyev return (ietp_detach(sc)); 556d5add41dSVladimir Kondratyev } 557d5add41dSVladimir Kondratyev 558d5add41dSVladimir Kondratyev static int 559d5add41dSVladimir Kondratyev ietp_iic_resume(device_t dev) 560d5add41dSVladimir Kondratyev { 561d5add41dSVladimir Kondratyev if (ietp_iic_set_absolute_mode(dev, true) != 0) { 562d5add41dSVladimir Kondratyev device_printf(dev, "reset when resuming failed: \n"); 563d5add41dSVladimir Kondratyev return (EIO); 564d5add41dSVladimir Kondratyev } 565d5add41dSVladimir Kondratyev 566d5add41dSVladimir Kondratyev return (0); 567d5add41dSVladimir Kondratyev } 568d5add41dSVladimir Kondratyev 569d5add41dSVladimir Kondratyev static int 570d5add41dSVladimir Kondratyev ietp_iic_set_absolute_mode(device_t dev, bool enable) 571d5add41dSVladimir Kondratyev { 572d5add41dSVladimir Kondratyev struct ietp_softc *sc = device_get_softc(dev); 573d5add41dSVladimir Kondratyev static const struct { 574d5add41dSVladimir Kondratyev uint16_t ic_type; 575d5add41dSVladimir Kondratyev uint16_t product_id; 576d5add41dSVladimir Kondratyev } special_fw[] = { 577d5add41dSVladimir Kondratyev { 0x0E, 0x05 }, { 0x0E, 0x06 }, { 0x0E, 0x07 }, { 0x0E, 0x09 }, 578d5add41dSVladimir Kondratyev { 0x0E, 0x13 }, { 0x08, 0x26 }, 579d5add41dSVladimir Kondratyev }; 580d5add41dSVladimir Kondratyev uint16_t val; 581d5add41dSVladimir Kondratyev int i, error; 582d5add41dSVladimir Kondratyev bool require_wakeup; 583d5add41dSVladimir Kondratyev 584d5add41dSVladimir Kondratyev error = 0; 585d5add41dSVladimir Kondratyev 586d5add41dSVladimir Kondratyev /* 587d5add41dSVladimir Kondratyev * Some ASUS touchpads need to be powered on to enter absolute mode. 588d5add41dSVladimir Kondratyev */ 589d5add41dSVladimir Kondratyev require_wakeup = false; 590d5add41dSVladimir Kondratyev for (i = 0; i < nitems(special_fw); i++) { 591d5add41dSVladimir Kondratyev if (sc->ic_type == special_fw[i].ic_type && 592d5add41dSVladimir Kondratyev sc->product_id == special_fw[i].product_id) { 593d5add41dSVladimir Kondratyev require_wakeup = true; 594d5add41dSVladimir Kondratyev break; 595d5add41dSVladimir Kondratyev } 596d5add41dSVladimir Kondratyev } 597d5add41dSVladimir Kondratyev 598*4151ac9fSVladimir Kondratyev if (require_wakeup && hid_intr_start(dev) != 0) { 599d5add41dSVladimir Kondratyev device_printf(dev, "failed writing poweron command\n"); 600d5add41dSVladimir Kondratyev return (EIO); 601d5add41dSVladimir Kondratyev } 602d5add41dSVladimir Kondratyev 603d5add41dSVladimir Kondratyev val = enable ? IETP_CTRL_ABSOLUTE : IETP_CTRL_STANDARD; 604d5add41dSVladimir Kondratyev if (ietp_iic_write_reg(dev, IETP_CONTROL, val) != 0) { 605d5add41dSVladimir Kondratyev device_printf(dev, "failed setting absolute mode\n"); 606d5add41dSVladimir Kondratyev error = EIO; 607d5add41dSVladimir Kondratyev } 608d5add41dSVladimir Kondratyev 609*4151ac9fSVladimir Kondratyev if (require_wakeup && hid_intr_stop(dev) != 0) { 610d5add41dSVladimir Kondratyev device_printf(dev, "failed writing poweroff command\n"); 611d5add41dSVladimir Kondratyev error = EIO; 612d5add41dSVladimir Kondratyev } 613d5add41dSVladimir Kondratyev 614d5add41dSVladimir Kondratyev return (error); 615d5add41dSVladimir Kondratyev } 616d5add41dSVladimir Kondratyev 617d5add41dSVladimir Kondratyev static int 618d5add41dSVladimir Kondratyev ietp_iic_read_reg(device_t dev, uint16_t reg, size_t len, void *val) 619d5add41dSVladimir Kondratyev { 620d5add41dSVladimir Kondratyev device_t iichid = device_get_parent(device_get_parent(dev)); 621d5add41dSVladimir Kondratyev uint16_t addr = iicbus_get_addr(iichid) << 1; 622d5add41dSVladimir Kondratyev uint8_t cmd[2] = { reg & 0xff, (reg >> 8) & 0xff }; 623d5add41dSVladimir Kondratyev struct iic_msg msgs[2] = { 624d5add41dSVladimir Kondratyev { addr, IIC_M_WR | IIC_M_NOSTOP, sizeof(cmd), cmd }, 625d5add41dSVladimir Kondratyev { addr, IIC_M_RD, len, val }, 626d5add41dSVladimir Kondratyev }; 627d5add41dSVladimir Kondratyev struct iic_rdwr_data ird = { msgs, nitems(msgs) }; 628d5add41dSVladimir Kondratyev int error; 629d5add41dSVladimir Kondratyev 630d5add41dSVladimir Kondratyev DPRINTF("Read reg 0x%04x with size %zu\n", reg, len); 631d5add41dSVladimir Kondratyev 632d5add41dSVladimir Kondratyev error = hid_ioctl(dev, I2CRDWR, (uintptr_t)&ird); 633d5add41dSVladimir Kondratyev if (error != 0) 634d5add41dSVladimir Kondratyev return (error); 635d5add41dSVladimir Kondratyev 636d5add41dSVladimir Kondratyev DPRINTF("Response: %*D\n", (int)len, val, " "); 637d5add41dSVladimir Kondratyev 638d5add41dSVladimir Kondratyev return (0); 639d5add41dSVladimir Kondratyev } 640d5add41dSVladimir Kondratyev 641d5add41dSVladimir Kondratyev static int 642d5add41dSVladimir Kondratyev ietp_iic_write_reg(device_t dev, uint16_t reg, uint16_t val) 643d5add41dSVladimir Kondratyev { 644d5add41dSVladimir Kondratyev device_t iichid = device_get_parent(device_get_parent(dev)); 645d5add41dSVladimir Kondratyev uint16_t addr = iicbus_get_addr(iichid) << 1; 646d5add41dSVladimir Kondratyev uint8_t cmd[4] = { reg & 0xff, (reg >> 8) & 0xff, 647d5add41dSVladimir Kondratyev val & 0xff, (val >> 8) & 0xff }; 648d5add41dSVladimir Kondratyev struct iic_msg msgs[1] = { 649d5add41dSVladimir Kondratyev { addr, IIC_M_WR, sizeof(cmd), cmd }, 650d5add41dSVladimir Kondratyev }; 651d5add41dSVladimir Kondratyev struct iic_rdwr_data ird = { msgs, nitems(msgs) }; 652d5add41dSVladimir Kondratyev 653d5add41dSVladimir Kondratyev DPRINTF("Write reg 0x%04x with value 0x%04x\n", reg, val); 654d5add41dSVladimir Kondratyev 655d5add41dSVladimir Kondratyev return (hid_ioctl(dev, I2CRDWR, (uintptr_t)&ird)); 656d5add41dSVladimir Kondratyev } 657d5add41dSVladimir Kondratyev 658d5add41dSVladimir Kondratyev static device_method_t ietp_methods[] = { 6598d5fef85SAymeric Wibo DEVMETHOD(device_identify, ietp_iic_identify), 660d5add41dSVladimir Kondratyev DEVMETHOD(device_probe, ietp_iic_probe), 661d5add41dSVladimir Kondratyev DEVMETHOD(device_attach, ietp_iic_attach), 662d5add41dSVladimir Kondratyev DEVMETHOD(device_detach, ietp_iic_detach), 663d5add41dSVladimir Kondratyev DEVMETHOD(device_resume, ietp_iic_resume), 664d5add41dSVladimir Kondratyev DEVMETHOD_END 665d5add41dSVladimir Kondratyev }; 666d5add41dSVladimir Kondratyev 667d5add41dSVladimir Kondratyev static driver_t ietp_driver = { 668d5add41dSVladimir Kondratyev .name = "ietp", 669d5add41dSVladimir Kondratyev .methods = ietp_methods, 670d5add41dSVladimir Kondratyev .size = sizeof(struct ietp_softc), 671d5add41dSVladimir Kondratyev }; 672d5add41dSVladimir Kondratyev 6737eeede15SJohn Baldwin DRIVER_MODULE(ietp, hidbus, ietp_driver, NULL, NULL); 674d5add41dSVladimir Kondratyev MODULE_DEPEND(ietp, hidbus, 1, 1, 1); 675d5add41dSVladimir Kondratyev MODULE_DEPEND(ietp, hid, 1, 1, 1); 676d5add41dSVladimir Kondratyev MODULE_DEPEND(ietp, evdev, 1, 1, 1); 677d5add41dSVladimir Kondratyev MODULE_VERSION(ietp, 1); 678d5add41dSVladimir Kondratyev HID_PNP_INFO(ietp_iic_devs); 679