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