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