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