xref: /openbsd/sys/dev/i2c/ihidev.c (revision 5af055cd)
1 /* $OpenBSD: ihidev.c,v 1.9 2016/01/29 17:11:58 jcs Exp $ */
2 /*
3  * HID-over-i2c driver
4  *
5  * https://msdn.microsoft.com/en-us/library/windows/hardware/dn642101%28v=vs.85%29.aspx
6  *
7  * Copyright (c) 2015, 2016 joshua stein <jcs@openbsd.org>
8  *
9  * Permission to use, copy, modify, and distribute this software for any
10  * purpose with or without fee is hereby granted, provided that the above
11  * copyright notice and this permission notice appear in all copies.
12  *
13  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
14  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
16  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
19  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20  */
21 
22 #include <sys/param.h>
23 #include <sys/systm.h>
24 #include <sys/device.h>
25 #include <sys/malloc.h>
26 #include <sys/stdint.h>
27 
28 #include <dev/i2c/i2cvar.h>
29 #include <dev/i2c/ihidev.h>
30 
31 #include <dev/hid/hid.h>
32 
33 /* XXX */
34 #include <dev/acpi/acpivar.h>
35 
36 /* #define IHIDEV_DEBUG */
37 
38 #ifdef IHIDEV_DEBUG
39 #define DPRINTF(x) printf x
40 #else
41 #define DPRINTF(x)
42 #endif
43 
44 /* 7.2 */
45 enum {
46 	I2C_HID_CMD_DESCR	= 0x0,
47 	I2C_HID_CMD_RESET	= 0x1,
48 	I2C_HID_CMD_GET_REPORT	= 0x2,
49 	I2C_HID_CMD_SET_REPORT	= 0x3,
50 	I2C_HID_CMD_GET_IDLE	= 0x4,
51 	I2C_HID_CMD_SET_IDLE	= 0x5,
52 	I2C_HID_CMD_GET_PROTO	= 0x6,
53 	I2C_HID_CMD_SET_PROTO	= 0x7,
54 	I2C_HID_CMD_SET_POWER	= 0x8,
55 
56 	/* pseudo commands */
57 	I2C_HID_REPORT_DESCR	= 0x100,
58 };
59 
60 static int I2C_HID_POWER_ON	= 0x0;
61 static int I2C_HID_POWER_OFF	= 0x1;
62 
63 int	ihidev_match(struct device *, void *, void *);
64 void	ihidev_attach(struct device *, struct device *, void *);
65 int	ihidev_detach(struct device *, int);
66 
67 int	ihidev_hid_command(struct ihidev_softc *, int, void *);
68 int	ihidev_intr(void *);
69 int	ihidev_reset(struct ihidev_softc *);
70 int	ihidev_hid_desc_parse(struct ihidev_softc *);
71 
72 int	ihidev_maxrepid(void *buf, int len);
73 int	ihidev_print(void *aux, const char *pnp);
74 int	ihidev_submatch(struct device *parent, void *cf, void *aux);
75 
76 struct cfattach ihidev_ca = {
77 	sizeof(struct ihidev_softc),
78 	ihidev_match,
79 	ihidev_attach,
80 	ihidev_detach,
81 	NULL
82 };
83 
84 struct cfdriver ihidev_cd = {
85 	NULL, "ihidev", DV_DULL
86 };
87 
88 int
89 ihidev_match(struct device *parent, void *match, void *aux)
90 {
91 	struct i2c_attach_args *ia = aux;
92 
93 	if (strcmp(ia->ia_name, "ihidev") == 0)
94 		return (1);
95 
96 	return (0);
97 }
98 
99 void
100 ihidev_attach(struct device *parent, struct device *self, void *aux)
101 {
102 	struct ihidev_softc *sc = (struct ihidev_softc *)self;
103 	struct i2c_attach_args *ia = aux;
104 	struct ihidev_attach_arg iha;
105 	struct device *dev;
106 	int repid, repsz;
107 	int repsizes[256];
108 	int isize;
109 
110 	sc->sc_tag = ia->ia_tag;
111 	sc->sc_addr = ia->ia_addr;
112 	sc->sc_hid_desc_addr = ia->ia_size;
113 
114 	printf(": int %d", ia->ia_int);
115 
116 	if (ihidev_hid_command(sc, I2C_HID_CMD_DESCR, NULL) ||
117 	    ihidev_hid_desc_parse(sc)) {
118 		printf(", failed fetching initial HID descriptor\n");
119 		return;
120 	}
121 
122 	printf(", vendor 0x%x product 0x%x, %s\n",
123 	    letoh16(sc->hid_desc.wVendorID), letoh16(sc->hid_desc.wProductID),
124 	    (char *)ia->ia_cookie);
125 
126 	sc->sc_nrepid = ihidev_maxrepid(sc->sc_report, sc->sc_reportlen);
127 	if (sc->sc_nrepid < 0)
128 		return;
129 
130 	printf("%s: %d report id%s\n", sc->sc_dev.dv_xname, sc->sc_nrepid,
131 	    sc->sc_nrepid > 1 ? "s" : "");
132 
133 	sc->sc_nrepid++;
134 	sc->sc_subdevs = mallocarray(sc->sc_nrepid, sizeof(struct ihidev *),
135 	    M_DEVBUF, M_NOWAIT | M_ZERO);
136 	if (sc->sc_subdevs == NULL) {
137 		printf("%s: failed allocating memory\n", sc->sc_dev.dv_xname);
138 		return;
139 	}
140 
141 	/* find largest report size and allocate memory for input buffer */
142 	sc->sc_isize = letoh16(sc->hid_desc.wMaxInputLength);
143 	for (repid = 0; repid < sc->sc_nrepid; repid++) {
144 		repsz = hid_report_size(sc->sc_report, sc->sc_reportlen,
145 		    hid_input, repid);
146 		repsizes[repid] = repsz;
147 
148 		isize = repsz + 2; /* two bytes for the length */
149 		isize += (sc->sc_nrepid != 1); /* one byte for the report ID */
150 		if (isize > sc->sc_isize)
151 			sc->sc_isize = isize;
152 
153 		DPRINTF(("%s: repid %d size %d\n", sc->sc_dev.dv_xname, repid,
154 		    repsz));
155 	}
156 	sc->sc_ibuf = malloc(sc->sc_isize, M_DEVBUF, M_NOWAIT | M_ZERO);
157 
158 	/* register interrupt with system */
159 	if (ia->ia_int > 0) {
160 		/* XXX: don't assume this uses acpi_intr_establish */
161 		sc->sc_ih = acpi_intr_establish(ia->ia_int, ia->ia_int_flags,
162 		    IPL_BIO, ihidev_intr, sc, sc->sc_dev.dv_xname);
163 		if (sc->sc_ih == NULL) {
164 			printf(", failed establishing intr\n");
165 			return;
166 		}
167 	}
168 
169 	iha.iaa = ia;
170 	iha.parent = sc;
171 
172 	/* Look for a driver claiming all report IDs first. */
173 	iha.reportid = IHIDEV_CLAIM_ALLREPORTID;
174 	dev = config_found_sm((struct device *)sc, &iha, NULL,
175 	    ihidev_submatch);
176 	if (dev != NULL) {
177 		for (repid = 0; repid < sc->sc_nrepid; repid++)
178 			sc->sc_subdevs[repid] = (struct ihidev *)dev;
179 		return;
180 	}
181 
182 	for (repid = 0; repid < sc->sc_nrepid; repid++) {
183 		if (hid_report_size(sc->sc_report, sc->sc_reportlen, hid_input,
184 		    repid) == 0 &&
185 		    hid_report_size(sc->sc_report, sc->sc_reportlen,
186 		    hid_output, repid) == 0 &&
187 		    hid_report_size(sc->sc_report, sc->sc_reportlen,
188 		    hid_feature, repid) == 0)
189 			continue;
190 
191 		iha.reportid = repid;
192 		dev = config_found_sm(self, &iha, ihidev_print,
193 		    ihidev_submatch);
194 		sc->sc_subdevs[repid] = (struct ihidev *)dev;
195 	}
196 
197 	/* power down until we're opened */
198 	if (ihidev_hid_command(sc, I2C_HID_CMD_SET_POWER, &I2C_HID_POWER_OFF)) {
199 		printf("%s: failed to power down\n", sc->sc_dev.dv_xname);
200 		return;
201 	}
202 }
203 
204 int
205 ihidev_detach(struct device *self, int flags)
206 {
207 	struct ihidev_softc *sc = (struct ihidev_softc *)self;
208 
209 	if (sc->sc_ih != NULL) {
210 		intr_disestablish(sc->sc_ih);
211 		sc->sc_ih = NULL;
212 	}
213 
214 	if (sc->sc_ibuf != NULL) {
215 		free(sc->sc_ibuf, M_DEVBUF, 0);
216 		sc->sc_ibuf = NULL;
217 	}
218 
219 	if (sc->sc_report != NULL)
220 		free(sc->sc_report, M_DEVBUF, sc->sc_reportlen);
221 
222 	return (0);
223 }
224 
225 int
226 ihidev_hid_command(struct ihidev_softc *sc, int hidcmd, void *arg)
227 {
228 	int i, res = 1;
229 
230 	iic_acquire_bus(sc->sc_tag, 0);
231 
232 	switch (hidcmd) {
233 	case I2C_HID_CMD_DESCR: {
234 		/*
235 		 * 5.2.2 - HID Descriptor Retrieval
236 		 * register is passed from the controller
237 		 */
238 		uint8_t cmd[] = {
239 			htole16(sc->sc_hid_desc_addr) & 0xff,
240 			htole16(sc->sc_hid_desc_addr) >> 8,
241 		};
242 
243 		DPRINTF(("%s: HID command I2C_HID_CMD_DESCR at 0x%x\n",
244 		    sc->sc_dev.dv_xname, htole16(sc->sc_hid_desc_addr)));
245 
246 		/* 20 00 */
247 		res = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr,
248 		    &cmd, sizeof(cmd), &sc->hid_desc_buf,
249 		    sizeof(struct i2c_hid_desc), 0);
250 
251 		DPRINTF(("%s: HID descriptor:", sc->sc_dev.dv_xname));
252 		for (i = 0; i < sizeof(struct i2c_hid_desc); i++)
253 			DPRINTF((" %.2x", sc->hid_desc_buf[i]));
254 		DPRINTF(("\n"));
255 
256 		break;
257 	}
258 	case I2C_HID_CMD_RESET: {
259 		uint8_t cmd[] = {
260 			htole16(sc->hid_desc.wCommandRegister) & 0xff,
261 			htole16(sc->hid_desc.wCommandRegister) >> 8,
262 			0,
263 			I2C_HID_CMD_RESET,
264 		};
265 
266 		DPRINTF(("%s: HID command I2C_HID_CMD_RESET\n",
267 		    sc->sc_dev.dv_xname));
268 
269 		/* 22 00 00 01 */
270 		res = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr,
271 		    &cmd, sizeof(cmd), NULL, 0, 0);
272 
273 		break;
274 	}
275 	case I2C_HID_CMD_GET_REPORT: {
276 		struct i2c_hid_report_request *rreq =
277 		    (struct i2c_hid_report_request *)arg;
278 
279 		uint8_t cmd[] = {
280 			htole16(sc->hid_desc.wCommandRegister) & 0xff,
281 			htole16(sc->hid_desc.wCommandRegister) >> 8,
282 			0,
283 			I2C_HID_CMD_GET_REPORT,
284 			0, 0, 0,
285 		};
286 		int cmdlen = 7;
287 		int dataoff = 4;
288 		int report_id = rreq->id;
289 		int report_id_len = 1;
290 		int report_len = rreq->len + 2;
291 		int d;
292 		uint8_t *tmprep;
293 
294 		DPRINTF(("%s: HID command I2C_HID_CMD_GET_REPORT %d "
295 		    "(type %d, len %d)\n", sc->sc_dev.dv_xname, report_id,
296 		    rreq->type, rreq->len));
297 
298 		/*
299 		 * 7.2.2.4 - "The protocol is optimized for Report < 15.  If a
300 		 * report ID >= 15 is necessary, then the Report ID in the Low
301 		 * Byte must be set to 1111 and a Third Byte is appended to the
302 		 * protocol.  This Third Byte contains the entire/actual report
303 		 * ID."
304 		 */
305 		if (report_id >= 15) {
306 			cmd[dataoff++] = report_id;
307 			report_id = 15;
308 			report_id_len = 2;
309 		} else
310 			cmdlen--;
311 
312 		cmd[2] = report_id | rreq->type << 4;
313 
314 		cmd[dataoff++] = sc->hid_desc.wDataRegister & 0xff;
315 		cmd[dataoff] = sc->hid_desc.wDataRegister >> 8;
316 
317 		/*
318 		 * 7.2.2.2 - Response will be a 2-byte length value, the report
319 		 * id with length determined above, and then the report.
320 		 * Allocate rreq->len + 2 + 2 bytes, read into that temporary
321 		 * buffer, and then copy only the report back out to
322 		 * rreq->data.
323 		 */
324 		report_len += report_id_len;
325 		tmprep = malloc(report_len, M_DEVBUF, M_NOWAIT | M_ZERO);
326 
327 		/* type 3 id 8: 22 00 38 02 23 00 */
328 		res = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr,
329 		    &cmd, cmdlen, tmprep, report_len, 0);
330 
331 		d = tmprep[0] | tmprep[1] << 8;
332 		if (d != report_len)
333 			DPRINTF(("%s: response size %d != expected length %d\n",
334 			    sc->sc_dev.dv_xname, d, report_len));
335 
336 		if (report_id_len == 2)
337 			d = tmprep[2] | tmprep[3] << 8;
338 		else
339 			d = tmprep[2];
340 
341 		if (d != rreq->id) {
342 			DPRINTF(("%s: response report id %d != %d\n",
343 			    sc->sc_dev.dv_xname, d, rreq->id));
344 			iic_release_bus(sc->sc_tag, 0);
345 			return (1);
346 		}
347 
348 		DPRINTF(("%s: response:", sc->sc_dev.dv_xname));
349 		for (i = 0; i < report_len; i++)
350 			DPRINTF((" %.2x", tmprep[i]));
351 		DPRINTF(("\n"));
352 
353 		memcpy(rreq->data, tmprep + 2 + report_id_len, rreq->len);
354 		free(tmprep, M_DEVBUF, report_len);
355 
356 		break;
357 	}
358 	case I2C_HID_CMD_SET_REPORT: {
359 		struct i2c_hid_report_request *rreq =
360 		    (struct i2c_hid_report_request *)arg;
361 
362 		uint8_t cmd[] = {
363 			htole16(sc->hid_desc.wCommandRegister) & 0xff,
364 			htole16(sc->hid_desc.wCommandRegister) >> 8,
365 			0,
366 			I2C_HID_CMD_SET_REPORT,
367 			0, 0, 0, 0, 0, 0,
368 		};
369 		int cmdlen = 10;
370 		int report_id = rreq->id;
371 		int report_len = 2 + (report_id ? 1 : 0) + rreq->len;
372 		int dataoff;
373 		uint8_t *finalcmd;
374 
375 		DPRINTF(("%s: HID command I2C_HID_CMD_SET_REPORT %d "
376 		    "(type %d, len %d):", sc->sc_dev.dv_xname, report_id,
377 		    rreq->type, rreq->len));
378 		for (i = 0; i < rreq->len; i++)
379 			DPRINTF((" %.2x", ((uint8_t *)rreq->data)[i]));
380 		DPRINTF(("\n"));
381 
382 		/*
383 		 * 7.2.2.4 - "The protocol is optimized for Report < 15.  If a
384 		 * report ID >= 15 is necessary, then the Report ID in the Low
385 		 * Byte must be set to 1111 and a Third Byte is appended to the
386 		 * protocol.  This Third Byte contains the entire/actual report
387 		 * ID."
388 		 */
389 		dataoff = 4;
390 		if (report_id >= 15) {
391 			cmd[dataoff++] = report_id;
392 			report_id = 15;
393 		} else
394 			cmdlen--;
395 
396 		cmd[2] = report_id | rreq->type << 4;
397 
398 		if (rreq->type == I2C_HID_REPORT_TYPE_FEATURE) {
399 			cmd[dataoff++] = htole16(sc->hid_desc.wDataRegister)
400 			    & 0xff;
401 			cmd[dataoff++] = htole16(sc->hid_desc.wDataRegister)
402 			    >> 8;
403 		} else {
404 			cmd[dataoff++] = htole16(sc->hid_desc.wOutputRegister)
405 			    & 0xff;
406 			cmd[dataoff++] = htole16(sc->hid_desc.wOutputRegister)
407 			    >> 8;
408 		}
409 
410 		cmd[dataoff++] = report_len & 0xff;
411 		cmd[dataoff++] = report_len >> 8;
412 		cmd[dataoff] = rreq->id;
413 
414 		finalcmd = malloc(cmdlen + rreq->len, M_DEVBUF,
415 		    M_NOWAIT | M_ZERO);
416 
417 		memcpy(finalcmd, cmd, cmdlen);
418 		memcpy(finalcmd + cmdlen, rreq->data, rreq->len);
419 
420 		/* type 3 id 4: 22 00 34 03 23 00 04 00 04 03 */
421 		res = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr,
422 		    finalcmd, cmdlen + rreq->len, NULL, 0, 0);
423 
424 		free(finalcmd, M_DEVBUF, cmdlen + rreq->len);
425 
426  		break;
427  	}
428 
429 	case I2C_HID_CMD_SET_POWER: {
430 		int power = *(int *)arg;
431 		uint8_t cmd[] = {
432 			htole16(sc->hid_desc.wCommandRegister) & 0xff,
433 			htole16(sc->hid_desc.wCommandRegister) >> 8,
434 			power,
435 			I2C_HID_CMD_SET_POWER,
436 		};
437 
438 		DPRINTF(("%s: HID command I2C_HID_CMD_SET_POWER(%d)\n",
439 		    sc->sc_dev.dv_xname, power));
440 
441 		/* 22 00 00 08 */
442 		res = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr,
443 		    &cmd, sizeof(cmd), NULL, 0, 0);
444 
445 		break;
446 	}
447 	case I2C_HID_REPORT_DESCR: {
448 		uint8_t cmd[] = {
449 			htole16(sc->hid_desc.wReportDescRegister) & 0xff,
450 			htole16(sc->hid_desc.wReportDescRegister) >> 8,
451 		};
452 
453 		DPRINTF(("%s: HID command I2C_HID_REPORT_DESCR at 0x%x with "
454 		    "size %d\n", sc->sc_dev.dv_xname, cmd[0],
455 		    sc->sc_reportlen));
456 
457 		/* 20 00 */
458 		res = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr,
459 		    &cmd, sizeof(cmd), sc->sc_report, sc->sc_reportlen, 0);
460 
461 		DPRINTF(("%s: HID report descriptor:", sc->sc_dev.dv_xname));
462 		for (i = 0; i < sc->sc_reportlen; i++)
463 			DPRINTF((" %.2x", sc->sc_report[i]));
464 		DPRINTF(("\n"));
465 
466 		break;
467 	}
468 	default:
469 		printf("%s: unknown command %d\n", sc->sc_dev.dv_xname,
470 		    hidcmd);
471 	}
472 
473 	iic_release_bus(sc->sc_tag, 0);
474 
475 	return (res);
476 }
477 
478 int
479 ihidev_reset(struct ihidev_softc *sc)
480 {
481 	DPRINTF(("%s: resetting\n", sc->sc_dev.dv_xname));
482 
483 	if (ihidev_hid_command(sc, I2C_HID_CMD_SET_POWER, &I2C_HID_POWER_ON)) {
484 		printf("%s: failed to power on\n", sc->sc_dev.dv_xname);
485 		return (1);
486 	}
487 
488 	DELAY(1000);
489 
490 	if (ihidev_hid_command(sc, I2C_HID_CMD_RESET, 0)) {
491 		printf("%s: failed to reset hardware\n", sc->sc_dev.dv_xname);
492 
493 		ihidev_hid_command(sc, I2C_HID_CMD_SET_POWER,
494 		    &I2C_HID_POWER_OFF);
495 
496 		return (1);
497 	}
498 
499 	DELAY(1000);
500 
501 	return (0);
502 }
503 
504 /*
505  * 5.2.2 - HID Descriptor Retrieval
506  *
507  * parse HID Descriptor that has already been read into hid_desc with
508  * I2C_HID_CMD_DESCR
509  */
510 int
511 ihidev_hid_desc_parse(struct ihidev_softc *sc)
512 {
513 	int retries = 3;
514 
515 	/* must be v01.00 */
516 	if (letoh16(sc->hid_desc.bcdVersion) != 0x0100) {
517 		printf("%s: bad HID descriptor bcdVersion (0x%x)\n",
518 		    sc->sc_dev.dv_xname,
519 		    letoh16(sc->hid_desc.bcdVersion));
520 		return (1);
521 	}
522 
523 	/* must be 30 bytes for v1.00 */
524 	if (letoh16(sc->hid_desc.wHIDDescLength !=
525 	    sizeof(struct i2c_hid_desc))) {
526 		printf("%s: bad HID descriptor size (%d != %zu)\n",
527 		    sc->sc_dev.dv_xname,
528 		    letoh16(sc->hid_desc.wHIDDescLength),
529 		    sizeof(struct i2c_hid_desc));
530 		return (1);
531 	}
532 
533 	if (letoh16(sc->hid_desc.wReportDescLength) <= 0) {
534 		printf("%s: bad HID report descriptor size (%d)\n",
535 		    sc->sc_dev.dv_xname,
536 		    letoh16(sc->hid_desc.wReportDescLength));
537 		return (1);
538 	}
539 
540 	while (retries-- > 0) {
541 		if (ihidev_reset(sc)) {
542 			if (retries == 0)
543 				return(1);
544 
545 			DELAY(1000);
546 		}
547 		else
548 			break;
549 	}
550 
551 	sc->sc_reportlen = letoh16(sc->hid_desc.wReportDescLength);
552 	sc->sc_report = malloc(sc->sc_reportlen, M_DEVBUF, M_NOWAIT | M_ZERO);
553 
554 	if (ihidev_hid_command(sc, I2C_HID_REPORT_DESCR, 0)) {
555 		printf("%s: failed fetching HID report\n",
556 		    sc->sc_dev.dv_xname);
557 		return (1);
558 	}
559 
560 	return (0);
561 }
562 
563 int
564 ihidev_intr(void *arg)
565 {
566 	struct ihidev_softc *sc = arg;
567 	struct ihidev *scd;
568 	u_int psize;
569 	int res, i;
570 	u_char *p;
571 	u_int rep = 0;
572 
573 	/*
574 	 * XXX: force I2C_F_POLL for now to avoid dwiic interrupting
575 	 * while we are interrupting
576 	 */
577 
578 	iic_acquire_bus(sc->sc_tag, I2C_F_POLL);
579 
580 	res = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr, NULL, 0,
581 	    sc->sc_ibuf, sc->sc_isize, I2C_F_POLL);
582 
583 	iic_release_bus(sc->sc_tag, I2C_F_POLL);
584 
585 	/*
586 	 * 6.1.1 - First two bytes are the packet length, which must be less
587 	 * than or equal to wMaxInputLength
588 	 */
589 	psize = sc->sc_ibuf[0] | sc->sc_ibuf[1] << 8;
590 	if (!psize || psize > sc->sc_isize) {
591 		DPRINTF(("%s: %s: invalid packet size (%d vs. %d)\n",
592 		    sc->sc_dev.dv_xname, __func__, psize, sc->sc_isize));
593 		return (1);
594 	}
595 
596 	/* 3rd byte is the report id */
597 	p = sc->sc_ibuf + 2;
598 	psize -= 2;
599 	if (sc->sc_nrepid != 1)
600 		rep = *p++, psize--;
601 
602 	if (rep >= sc->sc_nrepid) {
603 		printf("%s: %s: bad report id %d\n", sc->sc_dev.dv_xname,
604 		    __func__, rep);
605 		return (1);
606 	}
607 
608 	DPRINTF(("%s: ihidev_intr: hid input (rep %d):", sc->sc_dev.dv_xname,
609 	    rep));
610 	for (i = 0; i < sc->sc_isize; i++)
611 		DPRINTF((" %.2x", sc->sc_ibuf[i]));
612 	DPRINTF(("\n"));
613 
614 	scd = sc->sc_subdevs[rep];
615 	if (scd == NULL || !(scd->sc_state & IHIDEV_OPEN))
616 		return (1);
617 
618 	scd->sc_intr(scd, p, psize);
619 
620 	return (1);
621 }
622 
623 int
624 ihidev_maxrepid(void *buf, int len)
625 {
626 	struct hid_data *d;
627 	struct hid_item h;
628 	int maxid;
629 
630 	maxid = -1;
631 	h.report_ID = 0;
632 	for (d = hid_start_parse(buf, len, hid_none); hid_get_item(d, &h); )
633 		if (h.report_ID > maxid)
634 			maxid = h.report_ID;
635 	hid_end_parse(d);
636 
637 	return (maxid);
638 }
639 
640 int
641 ihidev_print(void *aux, const char *pnp)
642 {
643 	struct ihidev_attach_arg *iha = aux;
644 
645 	if (pnp)
646 		printf("hid at %s", pnp);
647 
648 	if (iha->reportid != 0 && iha->reportid != IHIDEV_CLAIM_ALLREPORTID)
649 		printf(" reportid %d", iha->reportid);
650 
651 	return (UNCONF);
652 }
653 
654 int
655 ihidev_submatch(struct device *parent, void *match, void *aux)
656 {
657 	struct ihidev_attach_arg *iha = aux;
658         struct cfdata *cf = match;
659 
660 	if (cf->ihidevcf_reportid != IHIDEV_UNK_REPORTID &&
661 	    cf->ihidevcf_reportid != iha->reportid)
662 		return (0);
663 
664 	return ((*cf->cf_attach->ca_match)(parent, cf, aux));
665 }
666 
667 int
668 ihidev_open(struct ihidev *scd)
669 {
670 	struct ihidev_softc *sc = scd->sc_parent;
671 
672 	DPRINTF(("%s: %s: state=%d refcnt=%d\n", sc->sc_dev.dv_xname,
673 	    __func__, scd->sc_state, sc->sc_refcnt));
674 
675 	if (scd->sc_state & IHIDEV_OPEN)
676 		return (EBUSY);
677 
678 	scd->sc_state |= IHIDEV_OPEN;
679 
680 	if (sc->sc_refcnt++ || sc->sc_isize == 0)
681 		return (0);
682 
683 	/* power on */
684 	ihidev_reset(sc);
685 
686 	return (0);
687 }
688 
689 void
690 ihidev_close(struct ihidev *scd)
691 {
692 	struct ihidev_softc *sc = scd->sc_parent;
693 
694 	DPRINTF(("%s: %s: state=%d refcnt=%d\n", sc->sc_dev.dv_xname,
695 	    __func__, scd->sc_state, sc->sc_refcnt));
696 
697 	if (!(scd->sc_state & IHIDEV_OPEN))
698 		return;
699 
700 	scd->sc_state &= ~IHIDEV_OPEN;
701 
702 	if (--sc->sc_refcnt)
703 		return;
704 
705 	if (ihidev_hid_command(sc, I2C_HID_CMD_SET_POWER, &I2C_HID_POWER_OFF))
706 		printf("%s: failed to power down\n", sc->sc_dev.dv_xname);
707 }
708 
709 int
710 ihidev_ioctl(struct ihidev *sc, u_long cmd, caddr_t addr, int flag,
711     struct proc *p)
712 {
713 	return -1;
714 }
715 
716 void
717 ihidev_get_report_desc(struct ihidev_softc *sc, void **desc, int *size)
718 {
719 	*desc = sc->sc_report;
720 	*size = sc->sc_reportlen;
721 }
722 
723 /* convert hid_* constants used throughout HID code to i2c HID equivalents */
724 int
725 ihidev_report_type_conv(int hid_type_id)
726 {
727 	switch (hid_type_id) {
728 	case hid_input:
729 		return I2C_HID_REPORT_TYPE_INPUT;
730 	case hid_output:
731 		return I2C_HID_REPORT_TYPE_OUTPUT;
732 	case hid_feature:
733 		return I2C_HID_REPORT_TYPE_FEATURE;
734 	default:
735 		return -1;
736 	}
737 }
738 
739 int
740 ihidev_get_report(struct device *dev, int type, int id, void *data, int len)
741 {
742 	struct ihidev_softc *sc = (struct ihidev_softc *)dev;
743 	struct i2c_hid_report_request rreq;
744 	int ctype;
745 
746 	if ((ctype = ihidev_report_type_conv(type)) < 0)
747 		return (1);
748 
749 	rreq.type = ctype;
750 	rreq.id = id;
751 	rreq.data = data;
752 	rreq.len = len;
753 
754 	if (ihidev_hid_command(sc, I2C_HID_CMD_GET_REPORT, &rreq)) {
755 		printf("%s: failed fetching report\n", sc->sc_dev.dv_xname);
756 		return (1);
757 	}
758 
759 	return 0;
760 }
761 
762 int
763 ihidev_set_report(struct device *dev, int type, int id, void *data,
764     int len)
765 {
766 	struct ihidev_softc *sc = (struct ihidev_softc *)dev;
767 	struct i2c_hid_report_request rreq;
768 	int ctype;
769 
770 	if ((ctype = ihidev_report_type_conv(type)) < 0)
771 		return (1);
772 
773 	rreq.type = ctype;
774 	rreq.id = id;
775 	rreq.data = data;
776 	rreq.len = len;
777 
778 	if (ihidev_hid_command(sc, I2C_HID_CMD_SET_REPORT, &rreq)) {
779 		printf("%s: failed setting report\n", sc->sc_dev.dv_xname);
780 		return (1);
781 	}
782 
783 	return 0;
784 }
785