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