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