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