xref: /openbsd-src/sys/dev/i2c/ihidev.c (revision 51cb4255bd7b73bf7c1aae348579ef28e79bc5f5)
1*51cb4255Skirill /* $OpenBSD: ihidev.c,v 1.39 2025/01/13 15:33:34 kirill Exp $ */
2b037e2e6Sjcs /*
3b037e2e6Sjcs  * HID-over-i2c driver
4b037e2e6Sjcs  *
5fc330ebdSjcs  * https://msdn.microsoft.com/en-us/library/windows/hardware/dn642101%28v=vs.85%29.aspx
6b037e2e6Sjcs  *
7b037e2e6Sjcs  * Copyright (c) 2015, 2016 joshua stein <jcs@openbsd.org>
8b037e2e6Sjcs  *
9b037e2e6Sjcs  * Permission to use, copy, modify, and distribute this software for any
10b037e2e6Sjcs  * purpose with or without fee is hereby granted, provided that the above
11b037e2e6Sjcs  * copyright notice and this permission notice appear in all copies.
12b037e2e6Sjcs  *
13b037e2e6Sjcs  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
14b037e2e6Sjcs  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15b037e2e6Sjcs  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
16b037e2e6Sjcs  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17b037e2e6Sjcs  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18b037e2e6Sjcs  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
19b037e2e6Sjcs  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20b037e2e6Sjcs  */
21b037e2e6Sjcs 
22b037e2e6Sjcs #include <sys/param.h>
23b037e2e6Sjcs #include <sys/systm.h>
24b037e2e6Sjcs #include <sys/device.h>
25b037e2e6Sjcs #include <sys/malloc.h>
26b037e2e6Sjcs #include <sys/stdint.h>
27b037e2e6Sjcs 
28b037e2e6Sjcs #include <dev/i2c/i2cvar.h>
29b037e2e6Sjcs #include <dev/i2c/ihidev.h>
30b037e2e6Sjcs 
31b037e2e6Sjcs #include <dev/hid/hid.h>
32b037e2e6Sjcs 
33b037e2e6Sjcs /* #define IHIDEV_DEBUG */
34b037e2e6Sjcs 
35b037e2e6Sjcs #ifdef IHIDEV_DEBUG
36b037e2e6Sjcs #define DPRINTF(x) printf x
37b037e2e6Sjcs #else
38b037e2e6Sjcs #define DPRINTF(x)
39b037e2e6Sjcs #endif
40b037e2e6Sjcs 
41372bf416Sjcs #define SLOW_POLL_MS	200
42372bf416Sjcs #define FAST_POLL_MS	10
43372bf416Sjcs 
44b037e2e6Sjcs /* 7.2 */
45b037e2e6Sjcs enum {
46b037e2e6Sjcs 	I2C_HID_CMD_DESCR	= 0x0,
47b037e2e6Sjcs 	I2C_HID_CMD_RESET	= 0x1,
48b037e2e6Sjcs 	I2C_HID_CMD_GET_REPORT	= 0x2,
49b037e2e6Sjcs 	I2C_HID_CMD_SET_REPORT	= 0x3,
50b037e2e6Sjcs 	I2C_HID_CMD_GET_IDLE	= 0x4,
51b037e2e6Sjcs 	I2C_HID_CMD_SET_IDLE	= 0x5,
52b037e2e6Sjcs 	I2C_HID_CMD_GET_PROTO	= 0x6,
53b037e2e6Sjcs 	I2C_HID_CMD_SET_PROTO	= 0x7,
54b037e2e6Sjcs 	I2C_HID_CMD_SET_POWER	= 0x8,
55b037e2e6Sjcs 
56b037e2e6Sjcs 	/* pseudo commands */
57b037e2e6Sjcs 	I2C_HID_REPORT_DESCR	= 0x100,
582e24d660Skirill 	I2C_HID_RESET_RESPONSE	= 0x101,
59b037e2e6Sjcs };
60b037e2e6Sjcs 
61b037e2e6Sjcs static int I2C_HID_POWER_ON	= 0x0;
62b037e2e6Sjcs static int I2C_HID_POWER_OFF	= 0x1;
63b037e2e6Sjcs 
64b037e2e6Sjcs int	ihidev_match(struct device *, void *, void *);
65b037e2e6Sjcs void	ihidev_attach(struct device *, struct device *, void *);
66b037e2e6Sjcs int	ihidev_detach(struct device *, int);
67d03221a8Sjcs int	ihidev_activate(struct device *, int);
68b037e2e6Sjcs 
69b037e2e6Sjcs int	ihidev_hid_command(struct ihidev_softc *, int, void *);
70b037e2e6Sjcs int	ihidev_intr(void *);
71e773e7e3Stobhe int	ihidev_poweron(struct ihidev_softc *);
72b037e2e6Sjcs int	ihidev_reset(struct ihidev_softc *);
73b037e2e6Sjcs int	ihidev_hid_desc_parse(struct ihidev_softc *);
74b037e2e6Sjcs 
75b037e2e6Sjcs int	ihidev_maxrepid(void *buf, int len);
76b037e2e6Sjcs int	ihidev_print(void *aux, const char *pnp);
77b037e2e6Sjcs int	ihidev_submatch(struct device *parent, void *cf, void *aux);
78b037e2e6Sjcs 
79895a6fb3Skirill #define IHIDEV_QUIRK_RE_POWER_ON	0x1
80895a6fb3Skirill 
81895a6fb3Skirill const struct ihidev_quirks {
82895a6fb3Skirill 	uint16_t		ihq_vid;
83895a6fb3Skirill 	uint16_t		ihq_pid;
84895a6fb3Skirill 	int			ihq_quirks;
85895a6fb3Skirill } ihidev_devs[] = {
86895a6fb3Skirill 	/* HONOR MagicBook Art 14 Touchpad (QTEC0002) */
87895a6fb3Skirill 	{ 0x35cc, 0x0104, IHIDEV_QUIRK_RE_POWER_ON },
88895a6fb3Skirill };
89895a6fb3Skirill 
90471aeecfSnaddy const struct cfattach ihidev_ca = {
91b037e2e6Sjcs 	sizeof(struct ihidev_softc),
92b037e2e6Sjcs 	ihidev_match,
93b037e2e6Sjcs 	ihidev_attach,
94b037e2e6Sjcs 	ihidev_detach,
95d03221a8Sjcs 	ihidev_activate,
96b037e2e6Sjcs };
97b037e2e6Sjcs 
98b037e2e6Sjcs struct cfdriver ihidev_cd = {
99b037e2e6Sjcs 	NULL, "ihidev", DV_DULL
100b037e2e6Sjcs };
101b037e2e6Sjcs 
102b037e2e6Sjcs int
103b037e2e6Sjcs ihidev_match(struct device *parent, void *match, void *aux)
104b037e2e6Sjcs {
105b037e2e6Sjcs 	struct i2c_attach_args *ia = aux;
106b037e2e6Sjcs 
107b037e2e6Sjcs 	if (strcmp(ia->ia_name, "ihidev") == 0)
108b037e2e6Sjcs 		return (1);
109b037e2e6Sjcs 
110b037e2e6Sjcs 	return (0);
111b037e2e6Sjcs }
112b037e2e6Sjcs 
113895a6fb3Skirill int
114895a6fb3Skirill ihidev_quirks(struct ihidev_softc *sc)
115895a6fb3Skirill {
116895a6fb3Skirill 	const struct ihidev_quirks	*q;
117895a6fb3Skirill 	uint16_t			 vid, pid;
118895a6fb3Skirill 	int 				 i, nent;
119895a6fb3Skirill 
120895a6fb3Skirill 	nent = nitems(ihidev_devs);
121895a6fb3Skirill 
122895a6fb3Skirill 	vid = letoh16(sc->hid_desc.wVendorID);
123895a6fb3Skirill 	pid = letoh16(sc->hid_desc.wProductID);
124895a6fb3Skirill 
125895a6fb3Skirill 	for (i = 0, q = ihidev_devs; i < nent; i++, q++)
126895a6fb3Skirill 		if (vid == q->ihq_vid && pid == q->ihq_pid)
127895a6fb3Skirill 			return (q->ihq_quirks);
128895a6fb3Skirill 
129895a6fb3Skirill 	return (0);
130895a6fb3Skirill }
131895a6fb3Skirill 
132b037e2e6Sjcs void
133b037e2e6Sjcs ihidev_attach(struct device *parent, struct device *self, void *aux)
134b037e2e6Sjcs {
135b037e2e6Sjcs 	struct ihidev_softc *sc = (struct ihidev_softc *)self;
136b037e2e6Sjcs 	struct i2c_attach_args *ia = aux;
137b037e2e6Sjcs 	struct ihidev_attach_arg iha;
138b037e2e6Sjcs 	struct device *dev;
139b037e2e6Sjcs 	int repid, repsz;
140b037e2e6Sjcs 	int repsizes[256];
141b037e2e6Sjcs 
142b037e2e6Sjcs 	sc->sc_tag = ia->ia_tag;
143b037e2e6Sjcs 	sc->sc_addr = ia->ia_addr;
144e08b9924Skettenis 	sc->sc_hid_desc_addr = ia->ia_size;
145b037e2e6Sjcs 
146b037e2e6Sjcs 	if (ihidev_hid_command(sc, I2C_HID_CMD_DESCR, NULL) ||
147b037e2e6Sjcs 	    ihidev_hid_desc_parse(sc)) {
148b037e2e6Sjcs 		printf(", failed fetching initial HID descriptor\n");
149b037e2e6Sjcs 		return;
150b037e2e6Sjcs 	}
151b037e2e6Sjcs 
152b037e2e6Sjcs 	sc->sc_nrepid = ihidev_maxrepid(sc->sc_report, sc->sc_reportlen);
153b037e2e6Sjcs 	if (sc->sc_nrepid < 0)
154b037e2e6Sjcs 		return;
155b037e2e6Sjcs 
156b037e2e6Sjcs 	sc->sc_nrepid++;
157b037e2e6Sjcs 	sc->sc_subdevs = mallocarray(sc->sc_nrepid, sizeof(struct ihidev *),
158a1868ea7Skettenis 	    M_DEVBUF, M_WAITOK | M_ZERO);
159b037e2e6Sjcs 
160fc330ebdSjcs 	/* find largest report size and allocate memory for input buffer */
16159d1b13fSkettenis 	sc->sc_isize = letoh16(sc->hid_desc.wMaxInputLength);
162b037e2e6Sjcs 	for (repid = 0; repid < sc->sc_nrepid; repid++) {
163b037e2e6Sjcs 		repsz = hid_report_size(sc->sc_report, sc->sc_reportlen,
164b037e2e6Sjcs 		    hid_input, repid);
165b037e2e6Sjcs 		repsizes[repid] = repsz;
166e8835b17Sjcs 		if (repsz > sc->sc_isize)
167e8835b17Sjcs 			sc->sc_isize = repsz;
168372bf416Sjcs 		if (repsz != 0)
169372bf416Sjcs 			DPRINTF(("%s: repid %d size %d\n", sc->sc_dev.dv_xname,
170372bf416Sjcs 			    repid, repsz));
171fc330ebdSjcs 	}
172a1868ea7Skettenis 	sc->sc_ibuf = malloc(sc->sc_isize, M_DEVBUF, M_WAITOK | M_ZERO);
173b037e2e6Sjcs 
174a91662ffSmglocker 	if (ia->ia_intr) {
175a91662ffSmglocker 		printf(" %s", iic_intr_string(sc->sc_tag, ia->ia_intr));
176a91662ffSmglocker 
177a91662ffSmglocker 		sc->sc_ih = iic_intr_establish(sc->sc_tag, ia->ia_intr,
178a91662ffSmglocker 		    IPL_TTY, ihidev_intr, sc, sc->sc_dev.dv_xname);
179a91662ffSmglocker 		if (sc->sc_ih == NULL)
180a91662ffSmglocker 			printf("%s: can't establish interrupt\n",
181a91662ffSmglocker 			    sc->sc_dev.dv_xname);
182a91662ffSmglocker 	}
183a91662ffSmglocker 
184a91662ffSmglocker 	if (ia->ia_poll || !sc->sc_ih) {
185a91662ffSmglocker 		printf(" (polling)");
186a91662ffSmglocker 		sc->sc_poll = 1;
187a91662ffSmglocker 		sc->sc_fastpoll = 1;
188a91662ffSmglocker 	}
189a91662ffSmglocker 
190a91662ffSmglocker 	printf(", vendor 0x%x product 0x%x, %s\n",
191a91662ffSmglocker 	    letoh16(sc->hid_desc.wVendorID), letoh16(sc->hid_desc.wProductID),
192a91662ffSmglocker 	    (char *)ia->ia_cookie);
193a91662ffSmglocker 
194a91662ffSmglocker 	printf("%s: %d report id%s\n", sc->sc_dev.dv_xname, (sc->sc_nrepid - 1),
195a91662ffSmglocker 	    (sc->sc_nrepid - 1) > 1 ? "s" : "");
196a91662ffSmglocker 
197fc330ebdSjcs 	iha.iaa = ia;
198fc330ebdSjcs 	iha.parent = sc;
199fc330ebdSjcs 
2008fabbf99Sjcs 	/* Look for a driver claiming multiple report IDs first. */
2018fabbf99Sjcs 	iha.reportid = IHIDEV_CLAIM_MULTIPLEID;
2028fabbf99Sjcs 	iha.nclaims = 0;
203fc330ebdSjcs 	dev = config_found_sm((struct device *)sc, &iha, NULL,
204fc330ebdSjcs 	    ihidev_submatch);
205fc330ebdSjcs 	if (dev != NULL) {
2068fabbf99Sjcs 		for (repid = 0; repid < iha.nclaims; repid++) {
2078fabbf99Sjcs 			sc->sc_subdevs[iha.claims[repid]] =
2088fabbf99Sjcs 			    (struct ihidev *)dev;
2098fabbf99Sjcs 		}
210fc330ebdSjcs 	}
211fc330ebdSjcs 
212fc330ebdSjcs 	for (repid = 0; repid < sc->sc_nrepid; repid++) {
2138fabbf99Sjcs 		if (sc->sc_subdevs[repid] != NULL)
2148fabbf99Sjcs 			continue;
2158fabbf99Sjcs 
216b037e2e6Sjcs 		if (hid_report_size(sc->sc_report, sc->sc_reportlen, hid_input,
217b037e2e6Sjcs 		    repid) == 0 &&
218b037e2e6Sjcs 		    hid_report_size(sc->sc_report, sc->sc_reportlen,
219b037e2e6Sjcs 		    hid_output, repid) == 0 &&
220b037e2e6Sjcs 		    hid_report_size(sc->sc_report, sc->sc_reportlen,
221b037e2e6Sjcs 		    hid_feature, repid) == 0)
222b037e2e6Sjcs 			continue;
223b037e2e6Sjcs 
224b037e2e6Sjcs 		iha.reportid = repid;
225b037e2e6Sjcs 		dev = config_found_sm(self, &iha, ihidev_print,
226b037e2e6Sjcs 		    ihidev_submatch);
227b037e2e6Sjcs 		sc->sc_subdevs[repid] = (struct ihidev *)dev;
228b037e2e6Sjcs 	}
229b037e2e6Sjcs 
230b9d12f9eSkettenis 	if (sc->sc_refcnt > 0)
231b9d12f9eSkettenis 		return;
232b9d12f9eSkettenis 
233b037e2e6Sjcs 	/* power down until we're opened */
234b037e2e6Sjcs 	if (ihidev_hid_command(sc, I2C_HID_CMD_SET_POWER, &I2C_HID_POWER_OFF)) {
235b037e2e6Sjcs 		printf("%s: failed to power down\n", sc->sc_dev.dv_xname);
236b037e2e6Sjcs 		return;
237b037e2e6Sjcs 	}
238b037e2e6Sjcs }
239b037e2e6Sjcs 
240b037e2e6Sjcs int
241b037e2e6Sjcs ihidev_detach(struct device *self, int flags)
242b037e2e6Sjcs {
243b037e2e6Sjcs 	struct ihidev_softc *sc = (struct ihidev_softc *)self;
244b037e2e6Sjcs 
245b037e2e6Sjcs 	if (sc->sc_ih != NULL) {
246d3689130Skettenis 		iic_intr_disestablish(sc->sc_tag, sc->sc_ih);
247b037e2e6Sjcs 		sc->sc_ih = NULL;
248b037e2e6Sjcs 	}
249b037e2e6Sjcs 
250b037e2e6Sjcs 	if (sc->sc_ibuf != NULL) {
251234dfda1Sderaadt 		free(sc->sc_ibuf, M_DEVBUF, sc->sc_isize);
252b037e2e6Sjcs 		sc->sc_ibuf = NULL;
253b037e2e6Sjcs 	}
254b037e2e6Sjcs 
255b037e2e6Sjcs 	if (sc->sc_report != NULL)
256b037e2e6Sjcs 		free(sc->sc_report, M_DEVBUF, sc->sc_reportlen);
257b037e2e6Sjcs 
258b037e2e6Sjcs 	return (0);
259b037e2e6Sjcs }
260b037e2e6Sjcs 
261d03221a8Sjcs int
262d03221a8Sjcs ihidev_activate(struct device *self, int act)
263d03221a8Sjcs {
264d03221a8Sjcs 	struct ihidev_softc *sc = (struct ihidev_softc *)self;
26507e9317cSderaadt 	int rv;
266d03221a8Sjcs 
267d03221a8Sjcs 	DPRINTF(("%s(%d)\n", __func__, act));
268d03221a8Sjcs 
269d03221a8Sjcs 	switch (act) {
270d03221a8Sjcs 	case DVACT_QUIESCE:
27107e9317cSderaadt 		rv = config_activate_children(self, act);
272d03221a8Sjcs 		sc->sc_dying = 1;
273d03221a8Sjcs 		if (sc->sc_poll && timeout_initialized(&sc->sc_timer)) {
2744b1a56afSjsg 			DPRINTF(("%s: cancelling polling\n",
275d03221a8Sjcs 			    sc->sc_dev.dv_xname));
276d03221a8Sjcs 			timeout_del_barrier(&sc->sc_timer);
277d03221a8Sjcs 		}
278d03221a8Sjcs 		if (ihidev_hid_command(sc, I2C_HID_CMD_SET_POWER,
279d03221a8Sjcs 		    &I2C_HID_POWER_OFF))
280d03221a8Sjcs 			printf("%s: failed to power down\n",
281d03221a8Sjcs 			    sc->sc_dev.dv_xname);
282d03221a8Sjcs 		break;
283d03221a8Sjcs 	case DVACT_WAKEUP:
284e773e7e3Stobhe 		ihidev_poweron(sc);
285d03221a8Sjcs 		sc->sc_dying = 0;
286d03221a8Sjcs 		if (sc->sc_poll && timeout_initialized(&sc->sc_timer))
287d03221a8Sjcs 			timeout_add(&sc->sc_timer, 2000);
28807e9317cSderaadt 		rv = config_activate_children(self, act);
28907e9317cSderaadt 		break;
29007e9317cSderaadt 	default:
29107e9317cSderaadt 		rv = config_activate_children(self, act);
292d03221a8Sjcs 		break;
293d03221a8Sjcs 	}
29407e9317cSderaadt 	return rv;
295d03221a8Sjcs }
296d03221a8Sjcs 
297372bf416Sjcs void
298372bf416Sjcs ihidev_sleep(struct ihidev_softc *sc, int ms)
299372bf416Sjcs {
300372bf416Sjcs 	if (cold)
301372bf416Sjcs 		delay(ms * 1000);
302deacac3eScheloha 	else
303deacac3eScheloha 		tsleep_nsec(&sc, PWAIT, "ihidev", MSEC_TO_NSEC(ms));
304372bf416Sjcs }
305372bf416Sjcs 
306b037e2e6Sjcs int
307b037e2e6Sjcs ihidev_hid_command(struct ihidev_softc *sc, int hidcmd, void *arg)
308b037e2e6Sjcs {
309b037e2e6Sjcs 	int i, res = 1;
310b037e2e6Sjcs 
311b037e2e6Sjcs 	iic_acquire_bus(sc->sc_tag, 0);
312b037e2e6Sjcs 
313b037e2e6Sjcs 	switch (hidcmd) {
314b037e2e6Sjcs 	case I2C_HID_CMD_DESCR: {
315b037e2e6Sjcs 		/*
316b037e2e6Sjcs 		 * 5.2.2 - HID Descriptor Retrieval
317fc330ebdSjcs 		 * register is passed from the controller
318b037e2e6Sjcs 		 */
319fc330ebdSjcs 		uint8_t cmd[] = {
320bb27db76Sjcs 			htole16(sc->sc_hid_desc_addr) & 0xff,
321bb27db76Sjcs 			htole16(sc->sc_hid_desc_addr) >> 8,
322fc330ebdSjcs 		};
323b037e2e6Sjcs 
324b037e2e6Sjcs 		DPRINTF(("%s: HID command I2C_HID_CMD_DESCR at 0x%x\n",
325e08b9924Skettenis 		    sc->sc_dev.dv_xname, htole16(sc->sc_hid_desc_addr)));
326b037e2e6Sjcs 
327b037e2e6Sjcs 		/* 20 00 */
328146fcb02Skettenis 		res = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr,
329fc330ebdSjcs 		    &cmd, sizeof(cmd), &sc->hid_desc_buf,
330b037e2e6Sjcs 		    sizeof(struct i2c_hid_desc), 0);
331b037e2e6Sjcs 
332b037e2e6Sjcs 		DPRINTF(("%s: HID descriptor:", sc->sc_dev.dv_xname));
333b037e2e6Sjcs 		for (i = 0; i < sizeof(struct i2c_hid_desc); i++)
334b037e2e6Sjcs 			DPRINTF((" %.2x", sc->hid_desc_buf[i]));
335b037e2e6Sjcs 		DPRINTF(("\n"));
336b037e2e6Sjcs 
337b037e2e6Sjcs 		break;
338b037e2e6Sjcs 	}
339b037e2e6Sjcs 	case I2C_HID_CMD_RESET: {
340fc330ebdSjcs 		uint8_t cmd[] = {
341bb27db76Sjcs 			htole16(sc->hid_desc.wCommandRegister) & 0xff,
342bb27db76Sjcs 			htole16(sc->hid_desc.wCommandRegister) >> 8,
343fc330ebdSjcs 			0,
344fc330ebdSjcs 			I2C_HID_CMD_RESET,
345fc330ebdSjcs 		};
346b037e2e6Sjcs 
347b037e2e6Sjcs 		DPRINTF(("%s: HID command I2C_HID_CMD_RESET\n",
348b037e2e6Sjcs 		    sc->sc_dev.dv_xname));
349b037e2e6Sjcs 
350b037e2e6Sjcs 		/* 22 00 00 01 */
351146fcb02Skettenis 		res = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr,
352fc330ebdSjcs 		    &cmd, sizeof(cmd), NULL, 0, 0);
353b037e2e6Sjcs 
354b037e2e6Sjcs 		break;
355b037e2e6Sjcs 	}
356fc330ebdSjcs 	case I2C_HID_CMD_GET_REPORT: {
357fc330ebdSjcs 		struct i2c_hid_report_request *rreq =
358fc330ebdSjcs 		    (struct i2c_hid_report_request *)arg;
359fc330ebdSjcs 
360fc330ebdSjcs 		uint8_t cmd[] = {
361bb27db76Sjcs 			htole16(sc->hid_desc.wCommandRegister) & 0xff,
362bb27db76Sjcs 			htole16(sc->hid_desc.wCommandRegister) >> 8,
363fc330ebdSjcs 			0,
364fc330ebdSjcs 			I2C_HID_CMD_GET_REPORT,
365fc330ebdSjcs 			0, 0, 0,
366fc330ebdSjcs 		};
367fc330ebdSjcs 		int cmdlen = 7;
368fc330ebdSjcs 		int dataoff = 4;
369fc330ebdSjcs 		int report_id = rreq->id;
370fc330ebdSjcs 		int report_id_len = 1;
371fc330ebdSjcs 		int report_len = rreq->len + 2;
372fc330ebdSjcs 		int d;
373fc330ebdSjcs 		uint8_t *tmprep;
374fc330ebdSjcs 
375fc330ebdSjcs 		DPRINTF(("%s: HID command I2C_HID_CMD_GET_REPORT %d "
376fc330ebdSjcs 		    "(type %d, len %d)\n", sc->sc_dev.dv_xname, report_id,
377fc330ebdSjcs 		    rreq->type, rreq->len));
378fc330ebdSjcs 
379fc330ebdSjcs 		/*
380fc330ebdSjcs 		 * 7.2.2.4 - "The protocol is optimized for Report < 15.  If a
381fc330ebdSjcs 		 * report ID >= 15 is necessary, then the Report ID in the Low
382fc330ebdSjcs 		 * Byte must be set to 1111 and a Third Byte is appended to the
383fc330ebdSjcs 		 * protocol.  This Third Byte contains the entire/actual report
384fc330ebdSjcs 		 * ID."
385fc330ebdSjcs 		 */
386fc330ebdSjcs 		if (report_id >= 15) {
387fc330ebdSjcs 			cmd[dataoff++] = report_id;
388fc330ebdSjcs 			report_id = 15;
389fc330ebdSjcs 			report_id_len = 2;
390fc330ebdSjcs 		} else
391fc330ebdSjcs 			cmdlen--;
392fc330ebdSjcs 
393fc330ebdSjcs 		cmd[2] = report_id | rreq->type << 4;
394fc330ebdSjcs 
395bb27db76Sjcs 		cmd[dataoff++] = sc->hid_desc.wDataRegister & 0xff;
396bb27db76Sjcs 		cmd[dataoff] = sc->hid_desc.wDataRegister >> 8;
397fc330ebdSjcs 
398fc330ebdSjcs 		/*
399fc330ebdSjcs 		 * 7.2.2.2 - Response will be a 2-byte length value, the report
400fc330ebdSjcs 		 * id with length determined above, and then the report.
401fc330ebdSjcs 		 * Allocate rreq->len + 2 + 2 bytes, read into that temporary
402fc330ebdSjcs 		 * buffer, and then copy only the report back out to
403fc330ebdSjcs 		 * rreq->data.
404fc330ebdSjcs 		 */
405fc330ebdSjcs 		report_len += report_id_len;
406a1868ea7Skettenis 		tmprep = malloc(report_len, M_DEVBUF, M_WAITOK | M_ZERO);
407fc330ebdSjcs 
408fc330ebdSjcs 		/* type 3 id 8: 22 00 38 02 23 00 */
409fc330ebdSjcs 		res = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr,
410fc330ebdSjcs 		    &cmd, cmdlen, tmprep, report_len, 0);
411fc330ebdSjcs 
412fc330ebdSjcs 		d = tmprep[0] | tmprep[1] << 8;
413fc330ebdSjcs 		if (d != report_len)
414fc330ebdSjcs 			DPRINTF(("%s: response size %d != expected length %d\n",
415fc330ebdSjcs 			    sc->sc_dev.dv_xname, d, report_len));
416fc330ebdSjcs 
417fc330ebdSjcs 		if (report_id_len == 2)
418fc330ebdSjcs 			d = tmprep[2] | tmprep[3] << 8;
419fc330ebdSjcs 		else
420fc330ebdSjcs 			d = tmprep[2];
421fc330ebdSjcs 
422fc330ebdSjcs 		if (d != rreq->id) {
423fc330ebdSjcs 			DPRINTF(("%s: response report id %d != %d\n",
424fc330ebdSjcs 			    sc->sc_dev.dv_xname, d, rreq->id));
425fc330ebdSjcs 			iic_release_bus(sc->sc_tag, 0);
4261937a0caSjsg 			free(tmprep, M_DEVBUF, report_len);
427fc330ebdSjcs 			return (1);
428fc330ebdSjcs 		}
429fc330ebdSjcs 
430fc330ebdSjcs 		DPRINTF(("%s: response:", sc->sc_dev.dv_xname));
431fc330ebdSjcs 		for (i = 0; i < report_len; i++)
432fc330ebdSjcs 			DPRINTF((" %.2x", tmprep[i]));
433fc330ebdSjcs 		DPRINTF(("\n"));
434fc330ebdSjcs 
435fc330ebdSjcs 		memcpy(rreq->data, tmprep + 2 + report_id_len, rreq->len);
436fc330ebdSjcs 		free(tmprep, M_DEVBUF, report_len);
437fc330ebdSjcs 
438fc330ebdSjcs 		break;
439fc330ebdSjcs 	}
440fc330ebdSjcs 	case I2C_HID_CMD_SET_REPORT: {
441fc330ebdSjcs 		struct i2c_hid_report_request *rreq =
442fc330ebdSjcs 		    (struct i2c_hid_report_request *)arg;
443fc330ebdSjcs 
444fc330ebdSjcs 		uint8_t cmd[] = {
445bb27db76Sjcs 			htole16(sc->hid_desc.wCommandRegister) & 0xff,
446bb27db76Sjcs 			htole16(sc->hid_desc.wCommandRegister) >> 8,
447fc330ebdSjcs 			0,
448fc330ebdSjcs 			I2C_HID_CMD_SET_REPORT,
449fc330ebdSjcs 			0, 0, 0, 0, 0, 0,
450fc330ebdSjcs 		};
451372bf416Sjcs 		int cmdlen = sizeof(cmd);
452fc330ebdSjcs 		int report_id = rreq->id;
453fc330ebdSjcs 		int report_len = 2 + (report_id ? 1 : 0) + rreq->len;
454fc330ebdSjcs 		int dataoff;
455fc330ebdSjcs 		uint8_t *finalcmd;
456fc330ebdSjcs 
457fc330ebdSjcs 		DPRINTF(("%s: HID command I2C_HID_CMD_SET_REPORT %d "
458fc330ebdSjcs 		    "(type %d, len %d):", sc->sc_dev.dv_xname, report_id,
459fc330ebdSjcs 		    rreq->type, rreq->len));
460fc330ebdSjcs 		for (i = 0; i < rreq->len; i++)
461fc330ebdSjcs 			DPRINTF((" %.2x", ((uint8_t *)rreq->data)[i]));
462fc330ebdSjcs 		DPRINTF(("\n"));
463fc330ebdSjcs 
464fc330ebdSjcs 		/*
465372bf416Sjcs 		 * 7.2.3.4 - "The protocol is optimized for Report < 15.  If a
466fc330ebdSjcs 		 * report ID >= 15 is necessary, then the Report ID in the Low
467fc330ebdSjcs 		 * Byte must be set to 1111 and a Third Byte is appended to the
468fc330ebdSjcs 		 * protocol.  This Third Byte contains the entire/actual report
469fc330ebdSjcs 		 * ID."
470fc330ebdSjcs 		 */
471fc330ebdSjcs 		dataoff = 4;
472fc330ebdSjcs 		if (report_id >= 15) {
473fc330ebdSjcs 			cmd[dataoff++] = report_id;
474fc330ebdSjcs 			report_id = 15;
475fc330ebdSjcs 		} else
476fc330ebdSjcs 			cmdlen--;
477fc330ebdSjcs 
478fc330ebdSjcs 		cmd[2] = report_id | rreq->type << 4;
479fc330ebdSjcs 
480650700c9Smglocker 		cmd[dataoff++] = htole16(sc->hid_desc.wDataRegister) & 0xff;
481650700c9Smglocker 		cmd[dataoff++] = htole16(sc->hid_desc.wDataRegister) >> 8;
482fc330ebdSjcs 
483fc330ebdSjcs 		cmd[dataoff++] = report_len & 0xff;
484fc330ebdSjcs 		cmd[dataoff++] = report_len >> 8;
485fc330ebdSjcs 		cmd[dataoff] = rreq->id;
486fc330ebdSjcs 
487fc330ebdSjcs 		finalcmd = malloc(cmdlen + rreq->len, M_DEVBUF,
488a1868ea7Skettenis 		    M_WAITOK | M_ZERO);
489fc330ebdSjcs 
490fc330ebdSjcs 		memcpy(finalcmd, cmd, cmdlen);
491fc330ebdSjcs 		memcpy(finalcmd + cmdlen, rreq->data, rreq->len);
492fc330ebdSjcs 
493fc330ebdSjcs 		/* type 3 id 4: 22 00 34 03 23 00 04 00 04 03 */
494fc330ebdSjcs 		res = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr,
495fc330ebdSjcs 		    finalcmd, cmdlen + rreq->len, NULL, 0, 0);
496fc330ebdSjcs 
497fc330ebdSjcs 		free(finalcmd, M_DEVBUF, cmdlen + rreq->len);
498fc330ebdSjcs 
499fc330ebdSjcs  		break;
500fc330ebdSjcs  	}
501fc330ebdSjcs 
502b037e2e6Sjcs 	case I2C_HID_CMD_SET_POWER: {
503b037e2e6Sjcs 		int power = *(int *)arg;
504fc330ebdSjcs 		uint8_t cmd[] = {
505bb27db76Sjcs 			htole16(sc->hid_desc.wCommandRegister) & 0xff,
506bb27db76Sjcs 			htole16(sc->hid_desc.wCommandRegister) >> 8,
507fc330ebdSjcs 			power,
508fc330ebdSjcs 			I2C_HID_CMD_SET_POWER,
509fc330ebdSjcs 		};
510b037e2e6Sjcs 
511b037e2e6Sjcs 		DPRINTF(("%s: HID command I2C_HID_CMD_SET_POWER(%d)\n",
512b037e2e6Sjcs 		    sc->sc_dev.dv_xname, power));
513b037e2e6Sjcs 
514b037e2e6Sjcs 		/* 22 00 00 08 */
515146fcb02Skettenis 		res = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr,
516fc330ebdSjcs 		    &cmd, sizeof(cmd), NULL, 0, 0);
517b037e2e6Sjcs 
518b037e2e6Sjcs 		break;
519b037e2e6Sjcs 	}
520b037e2e6Sjcs 	case I2C_HID_REPORT_DESCR: {
521fc330ebdSjcs 		uint8_t cmd[] = {
522bb27db76Sjcs 			htole16(sc->hid_desc.wReportDescRegister) & 0xff,
523bb27db76Sjcs 			htole16(sc->hid_desc.wReportDescRegister) >> 8,
524fc330ebdSjcs 		};
525b037e2e6Sjcs 
526b037e2e6Sjcs 		DPRINTF(("%s: HID command I2C_HID_REPORT_DESCR at 0x%x with "
527fc330ebdSjcs 		    "size %d\n", sc->sc_dev.dv_xname, cmd[0],
528b037e2e6Sjcs 		    sc->sc_reportlen));
529b037e2e6Sjcs 
530b037e2e6Sjcs 		/* 20 00 */
531146fcb02Skettenis 		res = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr,
532fc330ebdSjcs 		    &cmd, sizeof(cmd), sc->sc_report, sc->sc_reportlen, 0);
533b037e2e6Sjcs 
534b037e2e6Sjcs 		DPRINTF(("%s: HID report descriptor:", sc->sc_dev.dv_xname));
535b037e2e6Sjcs 		for (i = 0; i < sc->sc_reportlen; i++)
536b037e2e6Sjcs 			DPRINTF((" %.2x", sc->sc_report[i]));
537b037e2e6Sjcs 		DPRINTF(("\n"));
538b037e2e6Sjcs 
539b037e2e6Sjcs 		break;
540b037e2e6Sjcs 	}
5412e24d660Skirill 	case I2C_HID_RESET_RESPONSE: {
5422e24d660Skirill 		int i;
5432e24d660Skirill 		uint8_t buf[2] = { 0xff, 0xff };
5442e24d660Skirill 
5452e24d660Skirill 		DPRINTF(("%s: HID command I2C_HID_RESET_RESPONSE\n",
5462e24d660Skirill 		    sc->sc_dev.dv_xname));
5472e24d660Skirill 
5482e24d660Skirill 		/*
5492e24d660Skirill 		 * 7.2.1 states that a device should response for RESET
5502e24d660Skirill 		 * in less than 5 seconds. It uses poll instead of
5512e24d660Skirill 		 * tsleep because interrupts are blocked during autoconf.
5522e24d660Skirill 		 */
5532e24d660Skirill 		for (i = 0; i < 50; i++) {
5542e24d660Skirill 			ihidev_sleep(sc, 100);
5552e24d660Skirill 			res = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
5562e24d660Skirill 			    sc->sc_addr, NULL, 0, buf, sizeof(buf), 0);
5572e24d660Skirill 			DPRINTF(("%s: read attempt %d: 0x%x, 0x%x, res: %d\n",
5582e24d660Skirill 			    sc->sc_dev.dv_xname, i, buf[0], buf[1], res));
5592e24d660Skirill 			if (!res)
5602e24d660Skirill 				res = (buf[0] != 0x00 || buf[1] != 0x00);
5612e24d660Skirill 			if (!res)
5622e24d660Skirill 				break;
5632e24d660Skirill 		}
5642e24d660Skirill 
5652e24d660Skirill 		break;
5662e24d660Skirill 	}
567b037e2e6Sjcs 	default:
568b037e2e6Sjcs 		printf("%s: unknown command %d\n", sc->sc_dev.dv_xname,
569b037e2e6Sjcs 		    hidcmd);
570b037e2e6Sjcs 	}
571b037e2e6Sjcs 
572b037e2e6Sjcs 	iic_release_bus(sc->sc_tag, 0);
573b037e2e6Sjcs 
574b037e2e6Sjcs 	return (res);
575b037e2e6Sjcs }
576b037e2e6Sjcs 
577b037e2e6Sjcs int
578e773e7e3Stobhe ihidev_poweron(struct ihidev_softc *sc)
579b037e2e6Sjcs {
580b037e2e6Sjcs 	if (ihidev_hid_command(sc, I2C_HID_CMD_SET_POWER, &I2C_HID_POWER_ON)) {
581b037e2e6Sjcs 		printf("%s: failed to power on\n", sc->sc_dev.dv_xname);
582b037e2e6Sjcs 		return (1);
583b037e2e6Sjcs 	}
584b037e2e6Sjcs 
585372bf416Sjcs 	ihidev_sleep(sc, 100);
586b037e2e6Sjcs 
587e773e7e3Stobhe 	return 0;
588e773e7e3Stobhe }
589e773e7e3Stobhe 
590e773e7e3Stobhe 
591e773e7e3Stobhe int
592e773e7e3Stobhe ihidev_reset(struct ihidev_softc *sc)
593e773e7e3Stobhe {
594*51cb4255Skirill 	DPRINTF(("%s: resetting\n", sc->sc_dev.dv_xname));
595*51cb4255Skirill 
596e773e7e3Stobhe 	if (ihidev_poweron(sc))
597e773e7e3Stobhe 		return (1);
598e773e7e3Stobhe 
599b037e2e6Sjcs 	if (ihidev_hid_command(sc, I2C_HID_CMD_RESET, 0)) {
600b037e2e6Sjcs 		printf("%s: failed to reset hardware\n", sc->sc_dev.dv_xname);
601b037e2e6Sjcs 
602b037e2e6Sjcs 		ihidev_hid_command(sc, I2C_HID_CMD_SET_POWER,
603b037e2e6Sjcs 		    &I2C_HID_POWER_OFF);
604b037e2e6Sjcs 
605b037e2e6Sjcs 		return (1);
606b037e2e6Sjcs 	}
607b037e2e6Sjcs 
6082e24d660Skirill 	if (ihidev_hid_command(sc, I2C_HID_RESET_RESPONSE, 0)) {
6092e24d660Skirill 		printf("%s: unexpected reset response\n",
6102e24d660Skirill 		    sc->sc_dev.dv_xname);
6112e24d660Skirill 		return (1);
6122e24d660Skirill 	}
613b037e2e6Sjcs 
614b037e2e6Sjcs 	return (0);
615b037e2e6Sjcs }
616b037e2e6Sjcs 
617b037e2e6Sjcs /*
618b037e2e6Sjcs  * 5.2.2 - HID Descriptor Retrieval
619b037e2e6Sjcs  *
620b037e2e6Sjcs  * parse HID Descriptor that has already been read into hid_desc with
621b037e2e6Sjcs  * I2C_HID_CMD_DESCR
622b037e2e6Sjcs  */
623b037e2e6Sjcs int
624b037e2e6Sjcs ihidev_hid_desc_parse(struct ihidev_softc *sc)
625b037e2e6Sjcs {
6263bd8062fSkirill 	sc->sc_quirks = ihidev_quirks(sc);
6273bd8062fSkirill 
628b037e2e6Sjcs 	/* must be v01.00 */
629b037e2e6Sjcs 	if (letoh16(sc->hid_desc.bcdVersion) != 0x0100) {
630b037e2e6Sjcs 		printf("%s: bad HID descriptor bcdVersion (0x%x)\n",
631b037e2e6Sjcs 		    sc->sc_dev.dv_xname,
632b037e2e6Sjcs 		    letoh16(sc->hid_desc.bcdVersion));
633b037e2e6Sjcs 		return (1);
634b037e2e6Sjcs 	}
635b037e2e6Sjcs 
636b037e2e6Sjcs 	/* must be 30 bytes for v1.00 */
637b037e2e6Sjcs 	if (letoh16(sc->hid_desc.wHIDDescLength !=
638b037e2e6Sjcs 	    sizeof(struct i2c_hid_desc))) {
639b037e2e6Sjcs 		printf("%s: bad HID descriptor size (%d != %zu)\n",
640b037e2e6Sjcs 		    sc->sc_dev.dv_xname,
641b037e2e6Sjcs 		    letoh16(sc->hid_desc.wHIDDescLength),
642b037e2e6Sjcs 		    sizeof(struct i2c_hid_desc));
643b037e2e6Sjcs 		return (1);
644b037e2e6Sjcs 	}
645b037e2e6Sjcs 
646b037e2e6Sjcs 	if (letoh16(sc->hid_desc.wReportDescLength) <= 0) {
647b037e2e6Sjcs 		printf("%s: bad HID report descriptor size (%d)\n",
648b037e2e6Sjcs 		    sc->sc_dev.dv_xname,
649b037e2e6Sjcs 		    letoh16(sc->hid_desc.wReportDescLength));
650b037e2e6Sjcs 		return (1);
651b037e2e6Sjcs 	}
652b037e2e6Sjcs 
6532e24d660Skirill 	if (ihidev_reset(sc))
654b037e2e6Sjcs 		return (1);
655b037e2e6Sjcs 
656b037e2e6Sjcs 	sc->sc_reportlen = letoh16(sc->hid_desc.wReportDescLength);
657a1868ea7Skettenis 	sc->sc_report = malloc(sc->sc_reportlen, M_DEVBUF, M_WAITOK | M_ZERO);
658b037e2e6Sjcs 
659b037e2e6Sjcs 	if (ihidev_hid_command(sc, I2C_HID_REPORT_DESCR, 0)) {
660b037e2e6Sjcs 		printf("%s: failed fetching HID report\n",
661b037e2e6Sjcs 		    sc->sc_dev.dv_xname);
662b037e2e6Sjcs 		return (1);
663b037e2e6Sjcs 	}
664b037e2e6Sjcs 
665895a6fb3Skirill 	if (sc->sc_quirks & IHIDEV_QUIRK_RE_POWER_ON) {
666895a6fb3Skirill 		if (ihidev_poweron(sc))
667895a6fb3Skirill 			return (1);
668895a6fb3Skirill 
669895a6fb3Skirill 		/*
670895a6fb3Skirill 		 * 7.2.8 states that a device shall not respond back
671895a6fb3Skirill 		 * after receiving the power on command, and must ensure
672895a6fb3Skirill 		 * that it transitions to power on state in less than 1
673895a6fb3Skirill 		 * second. The ihidev_poweron function uses a shorter
674895a6fb3Skirill 		 * sleep, sufficient for the ON-RESET sequence. Here,
675895a6fb3Skirill 		 * however, it sleeps for the full second to accommodate
676895a6fb3Skirill 		 * cold boot scenarios on affected devices.
677895a6fb3Skirill 		 */
678895a6fb3Skirill 
679895a6fb3Skirill 		ihidev_sleep(sc, 1000);
680895a6fb3Skirill 	}
681895a6fb3Skirill 
682b037e2e6Sjcs 	return (0);
683b037e2e6Sjcs }
684b037e2e6Sjcs 
685d03221a8Sjcs void
686d03221a8Sjcs ihidev_poll(void *arg)
687d03221a8Sjcs {
688d03221a8Sjcs 	struct ihidev_softc *sc = arg;
689d03221a8Sjcs 
690d03221a8Sjcs 	sc->sc_frompoll = 1;
691d03221a8Sjcs 	ihidev_intr(sc);
692d03221a8Sjcs 	sc->sc_frompoll = 0;
693d03221a8Sjcs }
694d03221a8Sjcs 
695b037e2e6Sjcs int
696b037e2e6Sjcs ihidev_intr(void *arg)
697b037e2e6Sjcs {
698b037e2e6Sjcs 	struct ihidev_softc *sc = arg;
699b037e2e6Sjcs 	struct ihidev *scd;
700f6f15de3Sjcs 	int psize, res, i, fast = 0;
701b037e2e6Sjcs 	u_char *p;
702b037e2e6Sjcs 	u_int rep = 0;
703b037e2e6Sjcs 
704d03221a8Sjcs 	if (sc->sc_poll && !sc->sc_frompoll) {
705d03221a8Sjcs 		DPRINTF(("%s: received interrupt while polling, disabling "
706d03221a8Sjcs 		    "polling\n", sc->sc_dev.dv_xname));
707d03221a8Sjcs 		sc->sc_poll = 0;
708d03221a8Sjcs 		timeout_del_barrier(&sc->sc_timer);
709d03221a8Sjcs 	}
710d03221a8Sjcs 
711e6f4df9dSkettenis 	/*
712e6f4df9dSkettenis 	 * XXX: force I2C_F_POLL for now to avoid dwiic interrupting
713e6f4df9dSkettenis 	 * while we are interrupting
714e6f4df9dSkettenis 	 */
715b037e2e6Sjcs 
716e6f4df9dSkettenis 	iic_acquire_bus(sc->sc_tag, I2C_F_POLL);
717146fcb02Skettenis 	res = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr, NULL, 0,
718e8835b17Sjcs 	    sc->sc_ibuf, letoh16(sc->hid_desc.wMaxInputLength), I2C_F_POLL);
719e6f4df9dSkettenis 	iic_release_bus(sc->sc_tag, I2C_F_POLL);
720b037e2e6Sjcs 
721fc330ebdSjcs 	/*
722fc330ebdSjcs 	 * 6.1.1 - First two bytes are the packet length, which must be less
723fc330ebdSjcs 	 * than or equal to wMaxInputLength
724fc330ebdSjcs 	 */
725b037e2e6Sjcs 	psize = sc->sc_ibuf[0] | sc->sc_ibuf[1] << 8;
726f6f15de3Sjcs 	if (psize <= 2 || psize > sc->sc_isize) {
727372bf416Sjcs 		if (sc->sc_poll) {
728372bf416Sjcs 			/*
729372bf416Sjcs 			 * TODO: all fingers are up, should we pass to hid
730372bf416Sjcs 			 * layer?
731372bf416Sjcs 			 */
732372bf416Sjcs 			sc->sc_fastpoll = 0;
733372bf416Sjcs 			goto more_polling;
734372bf416Sjcs 		} else
735fc330ebdSjcs 			DPRINTF(("%s: %s: invalid packet size (%d vs. %d)\n",
736372bf416Sjcs 			    sc->sc_dev.dv_xname, __func__, psize,
737372bf416Sjcs 			    sc->sc_isize));
738b037e2e6Sjcs 		return (1);
739b037e2e6Sjcs 	}
740b037e2e6Sjcs 
741fc330ebdSjcs 	/* 3rd byte is the report id */
742b037e2e6Sjcs 	p = sc->sc_ibuf + 2;
743bf610880Sjcs 	psize -= 2;
744b037e2e6Sjcs 	if (sc->sc_nrepid != 1)
745fc330ebdSjcs 		rep = *p++, psize--;
746b037e2e6Sjcs 
747b037e2e6Sjcs 	if (rep >= sc->sc_nrepid) {
748fc330ebdSjcs 		printf("%s: %s: bad report id %d\n", sc->sc_dev.dv_xname,
749fc330ebdSjcs 		    __func__, rep);
750372bf416Sjcs 		if (sc->sc_poll) {
751372bf416Sjcs 			sc->sc_fastpoll = 0;
752372bf416Sjcs 			goto more_polling;
753372bf416Sjcs 		}
754b037e2e6Sjcs 		return (1);
755b037e2e6Sjcs 	}
756b037e2e6Sjcs 
757372bf416Sjcs 	DPRINTF(("%s: %s: hid input (rep %d):", sc->sc_dev.dv_xname, __func__,
758fc330ebdSjcs 	    rep));
759372bf416Sjcs 	for (i = 0; i < psize; i++) {
760372bf416Sjcs 		if (i > 0 && p[i] != 0 && p[i] != 0xff) {
761372bf416Sjcs 			fast = 1;
762372bf416Sjcs 		}
763372bf416Sjcs 		DPRINTF((" %.2x", p[i]));
764372bf416Sjcs 	}
765fc330ebdSjcs 	DPRINTF(("\n"));
766fc330ebdSjcs 
767b037e2e6Sjcs 	scd = sc->sc_subdevs[rep];
768372bf416Sjcs 	if (scd == NULL || !(scd->sc_state & IHIDEV_OPEN)) {
769372bf416Sjcs 		if (sc->sc_poll) {
770372bf416Sjcs 			if (sc->sc_fastpoll) {
771372bf416Sjcs 				DPRINTF(("%s: fast->slow polling\n",
772372bf416Sjcs 				    sc->sc_dev.dv_xname));
773372bf416Sjcs 				sc->sc_fastpoll = 0;
774372bf416Sjcs 			}
775372bf416Sjcs 			goto more_polling;
776372bf416Sjcs 		}
777b037e2e6Sjcs 		return (1);
778372bf416Sjcs 	}
779b037e2e6Sjcs 
7805959e33bSkettenis 	if (!sc->sc_dying)
781bf610880Sjcs 		scd->sc_intr(scd, p, psize);
782b037e2e6Sjcs 
783d03221a8Sjcs 	if (sc->sc_poll && (fast != sc->sc_fastpoll)) {
784372bf416Sjcs 		DPRINTF(("%s: %s->%s polling\n", sc->sc_dev.dv_xname,
785372bf416Sjcs 		    sc->sc_fastpoll ? "fast" : "slow",
786372bf416Sjcs 		    fast ? "fast" : "slow"));
787372bf416Sjcs 		sc->sc_fastpoll = fast;
788372bf416Sjcs 	}
789372bf416Sjcs 
790372bf416Sjcs more_polling:
791d03221a8Sjcs 	if (sc->sc_poll && sc->sc_refcnt && !sc->sc_dying &&
792d03221a8Sjcs 	    !timeout_pending(&sc->sc_timer))
793372bf416Sjcs 		timeout_add_msec(&sc->sc_timer,
794372bf416Sjcs 		    sc->sc_fastpoll ? FAST_POLL_MS : SLOW_POLL_MS);
795372bf416Sjcs 
796b037e2e6Sjcs 	return (1);
797b037e2e6Sjcs }
798b037e2e6Sjcs 
799b037e2e6Sjcs int
800b037e2e6Sjcs ihidev_maxrepid(void *buf, int len)
801b037e2e6Sjcs {
802b037e2e6Sjcs 	struct hid_data *d;
803b037e2e6Sjcs 	struct hid_item h;
804b037e2e6Sjcs 	int maxid;
805b037e2e6Sjcs 
806b037e2e6Sjcs 	maxid = -1;
807b037e2e6Sjcs 	h.report_ID = 0;
808fdc486edSanton 	for (d = hid_start_parse(buf, len, hid_all); hid_get_item(d, &h);)
809b037e2e6Sjcs 		if (h.report_ID > maxid)
810b037e2e6Sjcs 			maxid = h.report_ID;
811b037e2e6Sjcs 	hid_end_parse(d);
812b037e2e6Sjcs 
813b037e2e6Sjcs 	return (maxid);
814b037e2e6Sjcs }
815b037e2e6Sjcs 
816b037e2e6Sjcs int
817b037e2e6Sjcs ihidev_print(void *aux, const char *pnp)
818b037e2e6Sjcs {
819b037e2e6Sjcs 	struct ihidev_attach_arg *iha = aux;
820b037e2e6Sjcs 
821b037e2e6Sjcs 	if (pnp)
822b037e2e6Sjcs 		printf("hid at %s", pnp);
823b037e2e6Sjcs 
8248fabbf99Sjcs 	if (iha->reportid != 0)
825b037e2e6Sjcs 		printf(" reportid %d", iha->reportid);
826b037e2e6Sjcs 
827b037e2e6Sjcs 	return (UNCONF);
828b037e2e6Sjcs }
829b037e2e6Sjcs 
830b037e2e6Sjcs int
831b037e2e6Sjcs ihidev_submatch(struct device *parent, void *match, void *aux)
832b037e2e6Sjcs {
833b037e2e6Sjcs 	struct ihidev_attach_arg *iha = aux;
834b037e2e6Sjcs         struct cfdata *cf = match;
835b037e2e6Sjcs 
836b037e2e6Sjcs 	if (cf->ihidevcf_reportid != IHIDEV_UNK_REPORTID &&
837b037e2e6Sjcs 	    cf->ihidevcf_reportid != iha->reportid)
838b037e2e6Sjcs 		return (0);
839b037e2e6Sjcs 
840b037e2e6Sjcs 	return ((*cf->cf_attach->ca_match)(parent, cf, aux));
841b037e2e6Sjcs }
842b037e2e6Sjcs 
843b037e2e6Sjcs int
844b037e2e6Sjcs ihidev_open(struct ihidev *scd)
845b037e2e6Sjcs {
846b037e2e6Sjcs 	struct ihidev_softc *sc = scd->sc_parent;
847b037e2e6Sjcs 
848b037e2e6Sjcs 	DPRINTF(("%s: %s: state=%d refcnt=%d\n", sc->sc_dev.dv_xname,
849b037e2e6Sjcs 	    __func__, scd->sc_state, sc->sc_refcnt));
850b037e2e6Sjcs 
851b037e2e6Sjcs 	if (scd->sc_state & IHIDEV_OPEN)
852b037e2e6Sjcs 		return (EBUSY);
853b037e2e6Sjcs 
854b037e2e6Sjcs 	scd->sc_state |= IHIDEV_OPEN;
855b037e2e6Sjcs 
856b037e2e6Sjcs 	if (sc->sc_refcnt++ || sc->sc_isize == 0)
857b037e2e6Sjcs 		return (0);
858b037e2e6Sjcs 
859b037e2e6Sjcs 	/* power on */
860e773e7e3Stobhe 	ihidev_poweron(sc);
861b037e2e6Sjcs 
862372bf416Sjcs 	if (sc->sc_poll) {
863372bf416Sjcs 		if (!timeout_initialized(&sc->sc_timer))
864d03221a8Sjcs 			timeout_set(&sc->sc_timer, (void *)ihidev_poll, sc);
865372bf416Sjcs 		if (!timeout_pending(&sc->sc_timer))
866372bf416Sjcs 			timeout_add(&sc->sc_timer, FAST_POLL_MS);
867372bf416Sjcs 	}
868372bf416Sjcs 
869b037e2e6Sjcs 	return (0);
870b037e2e6Sjcs }
871b037e2e6Sjcs 
872b037e2e6Sjcs void
873b037e2e6Sjcs ihidev_close(struct ihidev *scd)
874b037e2e6Sjcs {
875b037e2e6Sjcs 	struct ihidev_softc *sc = scd->sc_parent;
876b037e2e6Sjcs 
877b037e2e6Sjcs 	DPRINTF(("%s: %s: state=%d refcnt=%d\n", sc->sc_dev.dv_xname,
878b037e2e6Sjcs 	    __func__, scd->sc_state, sc->sc_refcnt));
879b037e2e6Sjcs 
880b037e2e6Sjcs 	if (!(scd->sc_state & IHIDEV_OPEN))
881b037e2e6Sjcs 		return;
882b037e2e6Sjcs 
883b037e2e6Sjcs 	scd->sc_state &= ~IHIDEV_OPEN;
884b037e2e6Sjcs 
885b037e2e6Sjcs 	if (--sc->sc_refcnt)
886b037e2e6Sjcs 		return;
887b037e2e6Sjcs 
888372bf416Sjcs 	/* no sub-devices open, conserve power */
889372bf416Sjcs 
890d03221a8Sjcs 	if (sc->sc_poll && timeout_pending(&sc->sc_timer))
891372bf416Sjcs 		timeout_del(&sc->sc_timer);
892372bf416Sjcs 
893b037e2e6Sjcs 	if (ihidev_hid_command(sc, I2C_HID_CMD_SET_POWER, &I2C_HID_POWER_OFF))
894b037e2e6Sjcs 		printf("%s: failed to power down\n", sc->sc_dev.dv_xname);
895b037e2e6Sjcs }
896b037e2e6Sjcs 
897b037e2e6Sjcs int
898b037e2e6Sjcs ihidev_ioctl(struct ihidev *sc, u_long cmd, caddr_t addr, int flag,
899b037e2e6Sjcs     struct proc *p)
900b037e2e6Sjcs {
901b037e2e6Sjcs 	return -1;
902b037e2e6Sjcs }
903b037e2e6Sjcs 
904b037e2e6Sjcs void
905b037e2e6Sjcs ihidev_get_report_desc(struct ihidev_softc *sc, void **desc, int *size)
906b037e2e6Sjcs {
907b037e2e6Sjcs 	*desc = sc->sc_report;
908b037e2e6Sjcs 	*size = sc->sc_reportlen;
909b037e2e6Sjcs }
910fc330ebdSjcs 
911fc330ebdSjcs int
912fc330ebdSjcs ihidev_report_type_conv(int hid_type_id)
913fc330ebdSjcs {
914fc330ebdSjcs 	switch (hid_type_id) {
915fc330ebdSjcs 	case hid_input:
916fc330ebdSjcs 		return I2C_HID_REPORT_TYPE_INPUT;
917fc330ebdSjcs 	case hid_output:
918fc330ebdSjcs 		return I2C_HID_REPORT_TYPE_OUTPUT;
919fc330ebdSjcs 	case hid_feature:
920fc330ebdSjcs 		return I2C_HID_REPORT_TYPE_FEATURE;
921fc330ebdSjcs 	default:
922fc330ebdSjcs 		return -1;
923fc330ebdSjcs 	}
924fc330ebdSjcs }
925fc330ebdSjcs 
926fc330ebdSjcs int
927fc330ebdSjcs ihidev_get_report(struct device *dev, int type, int id, void *data, int len)
928fc330ebdSjcs {
929fc330ebdSjcs 	struct ihidev_softc *sc = (struct ihidev_softc *)dev;
930fc330ebdSjcs 	struct i2c_hid_report_request rreq;
931fc330ebdSjcs 
932253b28d0Sjcs 	rreq.type = type;
933fc330ebdSjcs 	rreq.id = id;
934fc330ebdSjcs 	rreq.data = data;
935fc330ebdSjcs 	rreq.len = len;
936fc330ebdSjcs 
937fc330ebdSjcs 	if (ihidev_hid_command(sc, I2C_HID_CMD_GET_REPORT, &rreq)) {
938fc330ebdSjcs 		printf("%s: failed fetching report\n", sc->sc_dev.dv_xname);
939fc330ebdSjcs 		return (1);
940fc330ebdSjcs 	}
941fc330ebdSjcs 
942fc330ebdSjcs 	return 0;
943fc330ebdSjcs }
944fc330ebdSjcs 
945fc330ebdSjcs int
946372bf416Sjcs ihidev_set_report(struct device *dev, int type, int id, void *data, int len)
947fc330ebdSjcs {
948fc330ebdSjcs 	struct ihidev_softc *sc = (struct ihidev_softc *)dev;
949fc330ebdSjcs 	struct i2c_hid_report_request rreq;
950fc330ebdSjcs 
951253b28d0Sjcs 	rreq.type = type;
952fc330ebdSjcs 	rreq.id = id;
953fc330ebdSjcs 	rreq.data = data;
954fc330ebdSjcs 	rreq.len = len;
955fc330ebdSjcs 
956fc330ebdSjcs 	if (ihidev_hid_command(sc, I2C_HID_CMD_SET_REPORT, &rreq)) {
957fc330ebdSjcs 		printf("%s: failed setting report\n", sc->sc_dev.dv_xname);
958fc330ebdSjcs 		return (1);
959fc330ebdSjcs 	}
960fc330ebdSjcs 
961fc330ebdSjcs 	return 0;
962fc330ebdSjcs }
963650700c9Smglocker 
964650700c9Smglocker int
965650700c9Smglocker ihidev_send_report(struct device *dev, int repid, void *data, int data_len)
966650700c9Smglocker {
967650700c9Smglocker 	struct ihidev_softc *sc = (struct ihidev_softc *)dev;
968650700c9Smglocker 	uint8_t *finalcmd, cmd[5];
969650700c9Smglocker 	int cmd_len, report_len, res;
970650700c9Smglocker 
971650700c9Smglocker 	cmd_len = sizeof(cmd);
972650700c9Smglocker 	report_len = 2 + 1 + data_len;
973650700c9Smglocker 
974650700c9Smglocker 	cmd[0] = htole16(sc->hid_desc.wOutputRegister) & 0xff;
975650700c9Smglocker 	cmd[1] = htole16(sc->hid_desc.wOutputRegister) >> 8;
976650700c9Smglocker 	cmd[2] = report_len & 0xff;
977650700c9Smglocker 	cmd[3] = report_len >> 8;
978650700c9Smglocker 	cmd[4] = repid;
979650700c9Smglocker 
980650700c9Smglocker 	finalcmd = malloc(cmd_len + data_len, M_DEVBUF, M_NOWAIT | M_ZERO);
981650700c9Smglocker 	if (finalcmd == NULL)
982650700c9Smglocker 		return ENOMEM;
983650700c9Smglocker 
984650700c9Smglocker 	memcpy(finalcmd, cmd, cmd_len);
985650700c9Smglocker 	memcpy(finalcmd + cmd_len, data, data_len);
986650700c9Smglocker 
987650700c9Smglocker 	res = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr,
988650700c9Smglocker 	    finalcmd, cmd_len + data_len, NULL, 0, 0);
989650700c9Smglocker 
990650700c9Smglocker 	free(finalcmd, M_DEVBUF, cmd_len + data_len);
991650700c9Smglocker 
992650700c9Smglocker 	return res;
993650700c9Smglocker }
994