File: | dev/i2c/ihidev.c |
Warning: | line 648, column 2 Value stored to 'res' is never read |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* $OpenBSD: ihidev.c,v 1.29 2023/08/12 10:03:05 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 | #define SLOW_POLL_MS200 200 |
42 | #define FAST_POLL_MS10 10 |
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 | int ihidev_activate(struct device *, int); |
67 | |
68 | int ihidev_hid_command(struct ihidev_softc *, int, void *); |
69 | int ihidev_intr(void *); |
70 | int ihidev_reset(struct ihidev_softc *); |
71 | int ihidev_hid_desc_parse(struct ihidev_softc *); |
72 | |
73 | int ihidev_maxrepid(void *buf, int len); |
74 | int ihidev_print(void *aux, const char *pnp); |
75 | int ihidev_submatch(struct device *parent, void *cf, void *aux); |
76 | |
77 | const struct cfattach ihidev_ca = { |
78 | sizeof(struct ihidev_softc), |
79 | ihidev_match, |
80 | ihidev_attach, |
81 | ihidev_detach, |
82 | ihidev_activate, |
83 | }; |
84 | |
85 | struct cfdriver ihidev_cd = { |
86 | NULL((void *)0), "ihidev", DV_DULL |
87 | }; |
88 | |
89 | int |
90 | ihidev_match(struct device *parent, void *match, void *aux) |
91 | { |
92 | struct i2c_attach_args *ia = aux; |
93 | |
94 | if (strcmp(ia->ia_name, "ihidev") == 0) |
95 | return (1); |
96 | |
97 | return (0); |
98 | } |
99 | |
100 | void |
101 | ihidev_attach(struct device *parent, struct device *self, void *aux) |
102 | { |
103 | struct ihidev_softc *sc = (struct ihidev_softc *)self; |
104 | struct i2c_attach_args *ia = aux; |
105 | struct ihidev_attach_arg iha; |
106 | struct device *dev; |
107 | int repid, repsz; |
108 | int repsizes[256]; |
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 | if (ihidev_hid_command(sc, I2C_HID_CMD_DESCR, NULL((void *)0)) || |
115 | ihidev_hid_desc_parse(sc)) { |
116 | printf(", failed fetching initial HID descriptor\n"); |
117 | return; |
118 | } |
119 | |
120 | if (ia->ia_intr) { |
121 | printf(" %s", iic_intr_string(sc->sc_tag, ia->ia_intr)(*(sc->sc_tag)->ic_intr_string)((sc->sc_tag)->ic_cookie , (ia->ia_intr))); |
122 | |
123 | sc->sc_ih = iic_intr_establish(sc->sc_tag, ia->ia_intr,(*(sc->sc_tag)->ic_intr_establish)((sc->sc_tag)-> ic_cookie, (ia->ia_intr), (0x9), (ihidev_intr), (sc), (sc-> sc_dev.dv_xname)) |
124 | IPL_TTY, ihidev_intr, sc, sc->sc_dev.dv_xname)(*(sc->sc_tag)->ic_intr_establish)((sc->sc_tag)-> ic_cookie, (ia->ia_intr), (0x9), (ihidev_intr), (sc), (sc-> sc_dev.dv_xname)); |
125 | if (sc->sc_ih == NULL((void *)0)) |
126 | printf(", can't establish interrupt"); |
127 | } |
128 | |
129 | if (ia->ia_poll || !sc->sc_ih) { |
130 | printf(" (polling)"); |
131 | sc->sc_poll = 1; |
132 | sc->sc_fastpoll = 1; |
133 | } |
134 | |
135 | printf(", vendor 0x%x product 0x%x, %s\n", |
136 | letoh16(sc->hid_desc.wVendorID)((__uint16_t)(sc->hid_desc.wVendorID)), letoh16(sc->hid_desc.wProductID)((__uint16_t)(sc->hid_desc.wProductID)), |
137 | (char *)ia->ia_cookie); |
138 | |
139 | sc->sc_nrepid = ihidev_maxrepid(sc->sc_report, sc->sc_reportlen); |
140 | if (sc->sc_nrepid < 0) |
141 | return; |
142 | |
143 | printf("%s: %d report id%s\n", sc->sc_dev.dv_xname, sc->sc_nrepid, |
144 | sc->sc_nrepid > 1 ? "s" : ""); |
145 | |
146 | sc->sc_nrepid++; |
147 | sc->sc_subdevs = mallocarray(sc->sc_nrepid, sizeof(struct ihidev *), |
148 | M_DEVBUF2, M_NOWAIT0x0002 | M_ZERO0x0008); |
149 | if (sc->sc_subdevs == NULL((void *)0)) { |
150 | printf("%s: failed allocating memory\n", sc->sc_dev.dv_xname); |
151 | return; |
152 | } |
153 | |
154 | /* find largest report size and allocate memory for input buffer */ |
155 | sc->sc_isize = letoh16(sc->hid_desc.wMaxInputLength)((__uint16_t)(sc->hid_desc.wMaxInputLength)); |
156 | for (repid = 0; repid < sc->sc_nrepid; repid++) { |
157 | repsz = hid_report_size(sc->sc_report, sc->sc_reportlen, |
158 | hid_input, repid); |
159 | repsizes[repid] = repsz; |
160 | if (repsz > sc->sc_isize) |
161 | sc->sc_isize = repsz; |
162 | if (repsz != 0) |
163 | DPRINTF(("%s: repid %d size %d\n", sc->sc_dev.dv_xname, |
164 | repid, repsz)); |
165 | } |
166 | sc->sc_ibuf = malloc(sc->sc_isize, M_DEVBUF2, M_NOWAIT0x0002 | M_ZERO0x0008); |
167 | |
168 | iha.iaa = ia; |
169 | iha.parent = sc; |
170 | |
171 | /* Look for a driver claiming multiple report IDs first. */ |
172 | iha.reportid = IHIDEV_CLAIM_MULTIPLEID255; |
173 | iha.nclaims = 0; |
174 | dev = config_found_sm((struct device *)sc, &iha, NULL((void *)0), |
175 | ihidev_submatch); |
176 | if (dev != NULL((void *)0)) { |
177 | for (repid = 0; repid < iha.nclaims; repid++) { |
178 | sc->sc_subdevs[iha.claims[repid]] = |
179 | (struct ihidev *)dev; |
180 | } |
181 | } |
182 | |
183 | for (repid = 0; repid < sc->sc_nrepid; repid++) { |
184 | if (sc->sc_subdevs[repid] != NULL((void *)0)) |
185 | continue; |
186 | |
187 | if (hid_report_size(sc->sc_report, sc->sc_reportlen, hid_input, |
188 | repid) == 0 && |
189 | hid_report_size(sc->sc_report, sc->sc_reportlen, |
190 | hid_output, repid) == 0 && |
191 | hid_report_size(sc->sc_report, sc->sc_reportlen, |
192 | hid_feature, repid) == 0) |
193 | continue; |
194 | |
195 | iha.reportid = repid; |
196 | dev = config_found_sm(self, &iha, ihidev_print, |
197 | ihidev_submatch); |
198 | sc->sc_subdevs[repid] = (struct ihidev *)dev; |
199 | } |
200 | |
201 | if (sc->sc_refcnt > 0) |
202 | return; |
203 | |
204 | /* power down until we're opened */ |
205 | if (ihidev_hid_command(sc, I2C_HID_CMD_SET_POWER, &I2C_HID_POWER_OFF)) { |
206 | printf("%s: failed to power down\n", sc->sc_dev.dv_xname); |
207 | return; |
208 | } |
209 | } |
210 | |
211 | int |
212 | ihidev_detach(struct device *self, int flags) |
213 | { |
214 | struct ihidev_softc *sc = (struct ihidev_softc *)self; |
215 | |
216 | if (sc->sc_ih != NULL((void *)0)) { |
217 | iic_intr_disestablish(sc->sc_tag, sc->sc_ih)(*(sc->sc_tag)->ic_intr_disestablish)((sc->sc_tag)-> ic_cookie, (sc->sc_ih)); |
218 | sc->sc_ih = NULL((void *)0); |
219 | } |
220 | |
221 | if (sc->sc_ibuf != NULL((void *)0)) { |
222 | free(sc->sc_ibuf, M_DEVBUF2, sc->sc_isize); |
223 | sc->sc_ibuf = NULL((void *)0); |
224 | } |
225 | |
226 | if (sc->sc_report != NULL((void *)0)) |
227 | free(sc->sc_report, M_DEVBUF2, sc->sc_reportlen); |
228 | |
229 | return (0); |
230 | } |
231 | |
232 | int |
233 | ihidev_activate(struct device *self, int act) |
234 | { |
235 | struct ihidev_softc *sc = (struct ihidev_softc *)self; |
236 | |
237 | DPRINTF(("%s(%d)\n", __func__, act)); |
238 | |
239 | switch (act) { |
240 | case DVACT_QUIESCE2: |
241 | sc->sc_dying = 1; |
242 | if (sc->sc_poll && timeout_initialized(&sc->sc_timer)((&sc->sc_timer)->to_flags & 0x04)) { |
243 | DPRINTF(("%s: cancelling polling\n", |
244 | sc->sc_dev.dv_xname)); |
245 | timeout_del_barrier(&sc->sc_timer); |
246 | } |
247 | if (ihidev_hid_command(sc, I2C_HID_CMD_SET_POWER, |
248 | &I2C_HID_POWER_OFF)) |
249 | printf("%s: failed to power down\n", |
250 | sc->sc_dev.dv_xname); |
251 | break; |
252 | case DVACT_WAKEUP5: |
253 | ihidev_reset(sc); |
254 | sc->sc_dying = 0; |
255 | if (sc->sc_poll && timeout_initialized(&sc->sc_timer)((&sc->sc_timer)->to_flags & 0x04)) |
256 | timeout_add(&sc->sc_timer, 2000); |
257 | break; |
258 | } |
259 | |
260 | config_activate_children(self, act); |
261 | |
262 | return 0; |
263 | } |
264 | |
265 | void |
266 | ihidev_sleep(struct ihidev_softc *sc, int ms) |
267 | { |
268 | if (cold) |
269 | delay(ms * 1000)(*delay_func)(ms * 1000); |
270 | else |
271 | tsleep_nsec(&sc, PWAIT32, "ihidev", MSEC_TO_NSEC(ms)); |
272 | } |
273 | |
274 | int |
275 | ihidev_hid_command(struct ihidev_softc *sc, int hidcmd, void *arg) |
276 | { |
277 | int i, res = 1; |
278 | |
279 | iic_acquire_bus(sc->sc_tag, 0)(*(sc->sc_tag)->ic_acquire_bus)((sc->sc_tag)->ic_cookie , (0)); |
280 | |
281 | switch (hidcmd) { |
282 | case I2C_HID_CMD_DESCR: { |
283 | /* |
284 | * 5.2.2 - HID Descriptor Retrieval |
285 | * register is passed from the controller |
286 | */ |
287 | uint8_t cmd[] = { |
288 | htole16(sc->sc_hid_desc_addr)((__uint16_t)(sc->sc_hid_desc_addr)) & 0xff, |
289 | htole16(sc->sc_hid_desc_addr)((__uint16_t)(sc->sc_hid_desc_addr)) >> 8, |
290 | }; |
291 | |
292 | DPRINTF(("%s: HID command I2C_HID_CMD_DESCR at 0x%x\n", |
293 | sc->sc_dev.dv_xname, htole16(sc->sc_hid_desc_addr))); |
294 | |
295 | /* 20 00 */ |
296 | res = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr, |
297 | &cmd, sizeof(cmd), &sc->hid_desc_buf, |
298 | sizeof(struct i2c_hid_desc), 0); |
299 | |
300 | DPRINTF(("%s: HID descriptor:", sc->sc_dev.dv_xname)); |
301 | for (i = 0; i < sizeof(struct i2c_hid_desc); i++) |
302 | DPRINTF((" %.2x", sc->hid_desc_buf[i])); |
303 | DPRINTF(("\n")); |
304 | |
305 | break; |
306 | } |
307 | case I2C_HID_CMD_RESET: { |
308 | uint8_t cmd[] = { |
309 | htole16(sc->hid_desc.wCommandRegister)((__uint16_t)(sc->hid_desc.wCommandRegister)) & 0xff, |
310 | htole16(sc->hid_desc.wCommandRegister)((__uint16_t)(sc->hid_desc.wCommandRegister)) >> 8, |
311 | 0, |
312 | I2C_HID_CMD_RESET, |
313 | }; |
314 | |
315 | DPRINTF(("%s: HID command I2C_HID_CMD_RESET\n", |
316 | sc->sc_dev.dv_xname)); |
317 | |
318 | /* 22 00 00 01 */ |
319 | res = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr, |
320 | &cmd, sizeof(cmd), NULL((void *)0), 0, 0); |
321 | |
322 | break; |
323 | } |
324 | case I2C_HID_CMD_GET_REPORT: { |
325 | struct i2c_hid_report_request *rreq = |
326 | (struct i2c_hid_report_request *)arg; |
327 | |
328 | uint8_t cmd[] = { |
329 | htole16(sc->hid_desc.wCommandRegister)((__uint16_t)(sc->hid_desc.wCommandRegister)) & 0xff, |
330 | htole16(sc->hid_desc.wCommandRegister)((__uint16_t)(sc->hid_desc.wCommandRegister)) >> 8, |
331 | 0, |
332 | I2C_HID_CMD_GET_REPORT, |
333 | 0, 0, 0, |
334 | }; |
335 | int cmdlen = 7; |
336 | int dataoff = 4; |
337 | int report_id = rreq->id; |
338 | int report_id_len = 1; |
339 | int report_len = rreq->len + 2; |
340 | int d; |
341 | uint8_t *tmprep; |
342 | |
343 | DPRINTF(("%s: HID command I2C_HID_CMD_GET_REPORT %d " |
344 | "(type %d, len %d)\n", sc->sc_dev.dv_xname, report_id, |
345 | rreq->type, rreq->len)); |
346 | |
347 | /* |
348 | * 7.2.2.4 - "The protocol is optimized for Report < 15. If a |
349 | * report ID >= 15 is necessary, then the Report ID in the Low |
350 | * Byte must be set to 1111 and a Third Byte is appended to the |
351 | * protocol. This Third Byte contains the entire/actual report |
352 | * ID." |
353 | */ |
354 | if (report_id >= 15) { |
355 | cmd[dataoff++] = report_id; |
356 | report_id = 15; |
357 | report_id_len = 2; |
358 | } else |
359 | cmdlen--; |
360 | |
361 | cmd[2] = report_id | rreq->type << 4; |
362 | |
363 | cmd[dataoff++] = sc->hid_desc.wDataRegister & 0xff; |
364 | cmd[dataoff] = sc->hid_desc.wDataRegister >> 8; |
365 | |
366 | /* |
367 | * 7.2.2.2 - Response will be a 2-byte length value, the report |
368 | * id with length determined above, and then the report. |
369 | * Allocate rreq->len + 2 + 2 bytes, read into that temporary |
370 | * buffer, and then copy only the report back out to |
371 | * rreq->data. |
372 | */ |
373 | report_len += report_id_len; |
374 | tmprep = malloc(report_len, M_DEVBUF2, M_NOWAIT0x0002 | M_ZERO0x0008); |
375 | |
376 | /* type 3 id 8: 22 00 38 02 23 00 */ |
377 | res = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr, |
378 | &cmd, cmdlen, tmprep, report_len, 0); |
379 | |
380 | d = tmprep[0] | tmprep[1] << 8; |
381 | if (d != report_len) |
382 | DPRINTF(("%s: response size %d != expected length %d\n", |
383 | sc->sc_dev.dv_xname, d, report_len)); |
384 | |
385 | if (report_id_len == 2) |
386 | d = tmprep[2] | tmprep[3] << 8; |
387 | else |
388 | d = tmprep[2]; |
389 | |
390 | if (d != rreq->id) { |
391 | DPRINTF(("%s: response report id %d != %d\n", |
392 | sc->sc_dev.dv_xname, d, rreq->id)); |
393 | iic_release_bus(sc->sc_tag, 0)(*(sc->sc_tag)->ic_release_bus)((sc->sc_tag)->ic_cookie , (0)); |
394 | free(tmprep, M_DEVBUF2, report_len); |
395 | return (1); |
396 | } |
397 | |
398 | DPRINTF(("%s: response:", sc->sc_dev.dv_xname)); |
399 | for (i = 0; i < report_len; i++) |
400 | DPRINTF((" %.2x", tmprep[i])); |
401 | DPRINTF(("\n")); |
402 | |
403 | memcpy(rreq->data, tmprep + 2 + report_id_len, rreq->len)__builtin_memcpy((rreq->data), (tmprep + 2 + report_id_len ), (rreq->len)); |
404 | free(tmprep, M_DEVBUF2, report_len); |
405 | |
406 | break; |
407 | } |
408 | case I2C_HID_CMD_SET_REPORT: { |
409 | struct i2c_hid_report_request *rreq = |
410 | (struct i2c_hid_report_request *)arg; |
411 | |
412 | uint8_t cmd[] = { |
413 | htole16(sc->hid_desc.wCommandRegister)((__uint16_t)(sc->hid_desc.wCommandRegister)) & 0xff, |
414 | htole16(sc->hid_desc.wCommandRegister)((__uint16_t)(sc->hid_desc.wCommandRegister)) >> 8, |
415 | 0, |
416 | I2C_HID_CMD_SET_REPORT, |
417 | 0, 0, 0, 0, 0, 0, |
418 | }; |
419 | int cmdlen = sizeof(cmd); |
420 | int report_id = rreq->id; |
421 | int report_len = 2 + (report_id ? 1 : 0) + rreq->len; |
422 | int dataoff; |
423 | uint8_t *finalcmd; |
424 | |
425 | DPRINTF(("%s: HID command I2C_HID_CMD_SET_REPORT %d " |
426 | "(type %d, len %d):", sc->sc_dev.dv_xname, report_id, |
427 | rreq->type, rreq->len)); |
428 | for (i = 0; i < rreq->len; i++) |
429 | DPRINTF((" %.2x", ((uint8_t *)rreq->data)[i])); |
430 | DPRINTF(("\n")); |
431 | |
432 | /* |
433 | * 7.2.3.4 - "The protocol is optimized for Report < 15. If a |
434 | * report ID >= 15 is necessary, then the Report ID in the Low |
435 | * Byte must be set to 1111 and a Third Byte is appended to the |
436 | * protocol. This Third Byte contains the entire/actual report |
437 | * ID." |
438 | */ |
439 | dataoff = 4; |
440 | if (report_id >= 15) { |
441 | cmd[dataoff++] = report_id; |
442 | report_id = 15; |
443 | } else |
444 | cmdlen--; |
445 | |
446 | cmd[2] = report_id | rreq->type << 4; |
447 | |
448 | if (rreq->type == I2C_HID_REPORT_TYPE_FEATURE0x3) { |
449 | cmd[dataoff++] = htole16(sc->hid_desc.wDataRegister)((__uint16_t)(sc->hid_desc.wDataRegister)) |
450 | & 0xff; |
451 | cmd[dataoff++] = htole16(sc->hid_desc.wDataRegister)((__uint16_t)(sc->hid_desc.wDataRegister)) |
452 | >> 8; |
453 | } else { |
454 | cmd[dataoff++] = htole16(sc->hid_desc.wOutputRegister)((__uint16_t)(sc->hid_desc.wOutputRegister)) |
455 | & 0xff; |
456 | cmd[dataoff++] = htole16(sc->hid_desc.wOutputRegister)((__uint16_t)(sc->hid_desc.wOutputRegister)) |
457 | >> 8; |
458 | } |
459 | |
460 | cmd[dataoff++] = report_len & 0xff; |
461 | cmd[dataoff++] = report_len >> 8; |
462 | cmd[dataoff] = rreq->id; |
463 | |
464 | finalcmd = malloc(cmdlen + rreq->len, M_DEVBUF2, |
465 | M_NOWAIT0x0002 | M_ZERO0x0008); |
466 | |
467 | memcpy(finalcmd, cmd, cmdlen)__builtin_memcpy((finalcmd), (cmd), (cmdlen)); |
468 | memcpy(finalcmd + cmdlen, rreq->data, rreq->len)__builtin_memcpy((finalcmd + cmdlen), (rreq->data), (rreq-> len)); |
469 | |
470 | /* type 3 id 4: 22 00 34 03 23 00 04 00 04 03 */ |
471 | res = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr, |
472 | finalcmd, cmdlen + rreq->len, NULL((void *)0), 0, 0); |
473 | |
474 | free(finalcmd, M_DEVBUF2, cmdlen + rreq->len); |
475 | |
476 | break; |
477 | } |
478 | |
479 | case I2C_HID_CMD_SET_POWER: { |
480 | int power = *(int *)arg; |
481 | uint8_t cmd[] = { |
482 | htole16(sc->hid_desc.wCommandRegister)((__uint16_t)(sc->hid_desc.wCommandRegister)) & 0xff, |
483 | htole16(sc->hid_desc.wCommandRegister)((__uint16_t)(sc->hid_desc.wCommandRegister)) >> 8, |
484 | power, |
485 | I2C_HID_CMD_SET_POWER, |
486 | }; |
487 | |
488 | DPRINTF(("%s: HID command I2C_HID_CMD_SET_POWER(%d)\n", |
489 | sc->sc_dev.dv_xname, power)); |
490 | |
491 | /* 22 00 00 08 */ |
492 | res = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr, |
493 | &cmd, sizeof(cmd), NULL((void *)0), 0, 0); |
494 | |
495 | break; |
496 | } |
497 | case I2C_HID_REPORT_DESCR: { |
498 | uint8_t cmd[] = { |
499 | htole16(sc->hid_desc.wReportDescRegister)((__uint16_t)(sc->hid_desc.wReportDescRegister)) & 0xff, |
500 | htole16(sc->hid_desc.wReportDescRegister)((__uint16_t)(sc->hid_desc.wReportDescRegister)) >> 8, |
501 | }; |
502 | |
503 | DPRINTF(("%s: HID command I2C_HID_REPORT_DESCR at 0x%x with " |
504 | "size %d\n", sc->sc_dev.dv_xname, cmd[0], |
505 | sc->sc_reportlen)); |
506 | |
507 | /* 20 00 */ |
508 | res = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr, |
509 | &cmd, sizeof(cmd), sc->sc_report, sc->sc_reportlen, 0); |
510 | |
511 | DPRINTF(("%s: HID report descriptor:", sc->sc_dev.dv_xname)); |
512 | for (i = 0; i < sc->sc_reportlen; i++) |
513 | DPRINTF((" %.2x", sc->sc_report[i])); |
514 | DPRINTF(("\n")); |
515 | |
516 | break; |
517 | } |
518 | default: |
519 | printf("%s: unknown command %d\n", sc->sc_dev.dv_xname, |
520 | hidcmd); |
521 | } |
522 | |
523 | iic_release_bus(sc->sc_tag, 0)(*(sc->sc_tag)->ic_release_bus)((sc->sc_tag)->ic_cookie , (0)); |
524 | |
525 | return (res); |
526 | } |
527 | |
528 | int |
529 | ihidev_reset(struct ihidev_softc *sc) |
530 | { |
531 | DPRINTF(("%s: resetting\n", sc->sc_dev.dv_xname)); |
532 | |
533 | if (ihidev_hid_command(sc, I2C_HID_CMD_SET_POWER, &I2C_HID_POWER_ON)) { |
534 | printf("%s: failed to power on\n", sc->sc_dev.dv_xname); |
535 | return (1); |
536 | } |
537 | |
538 | ihidev_sleep(sc, 100); |
539 | |
540 | if (ihidev_hid_command(sc, I2C_HID_CMD_RESET, 0)) { |
541 | printf("%s: failed to reset hardware\n", sc->sc_dev.dv_xname); |
542 | |
543 | ihidev_hid_command(sc, I2C_HID_CMD_SET_POWER, |
544 | &I2C_HID_POWER_OFF); |
545 | |
546 | return (1); |
547 | } |
548 | |
549 | ihidev_sleep(sc, 100); |
550 | |
551 | return (0); |
552 | } |
553 | |
554 | /* |
555 | * 5.2.2 - HID Descriptor Retrieval |
556 | * |
557 | * parse HID Descriptor that has already been read into hid_desc with |
558 | * I2C_HID_CMD_DESCR |
559 | */ |
560 | int |
561 | ihidev_hid_desc_parse(struct ihidev_softc *sc) |
562 | { |
563 | int retries = 3; |
564 | |
565 | /* must be v01.00 */ |
566 | if (letoh16(sc->hid_desc.bcdVersion)((__uint16_t)(sc->hid_desc.bcdVersion)) != 0x0100) { |
567 | printf("%s: bad HID descriptor bcdVersion (0x%x)\n", |
568 | sc->sc_dev.dv_xname, |
569 | letoh16(sc->hid_desc.bcdVersion)((__uint16_t)(sc->hid_desc.bcdVersion))); |
570 | return (1); |
571 | } |
572 | |
573 | /* must be 30 bytes for v1.00 */ |
574 | if (letoh16(sc->hid_desc.wHIDDescLength !=((__uint16_t)(sc->hid_desc.wHIDDescLength != sizeof(struct i2c_hid_desc))) |
575 | sizeof(struct i2c_hid_desc))((__uint16_t)(sc->hid_desc.wHIDDescLength != sizeof(struct i2c_hid_desc)))) { |
576 | printf("%s: bad HID descriptor size (%d != %zu)\n", |
577 | sc->sc_dev.dv_xname, |
578 | letoh16(sc->hid_desc.wHIDDescLength)((__uint16_t)(sc->hid_desc.wHIDDescLength)), |
579 | sizeof(struct i2c_hid_desc)); |
580 | return (1); |
581 | } |
582 | |
583 | if (letoh16(sc->hid_desc.wReportDescLength)((__uint16_t)(sc->hid_desc.wReportDescLength)) <= 0) { |
584 | printf("%s: bad HID report descriptor size (%d)\n", |
585 | sc->sc_dev.dv_xname, |
586 | letoh16(sc->hid_desc.wReportDescLength)((__uint16_t)(sc->hid_desc.wReportDescLength))); |
587 | return (1); |
588 | } |
589 | |
590 | while (retries-- > 0) { |
591 | if (ihidev_reset(sc)) { |
592 | if (retries == 0) |
593 | return(1); |
594 | |
595 | ihidev_sleep(sc, 10); |
596 | } |
597 | else |
598 | break; |
599 | } |
600 | |
601 | sc->sc_reportlen = letoh16(sc->hid_desc.wReportDescLength)((__uint16_t)(sc->hid_desc.wReportDescLength)); |
602 | sc->sc_report = malloc(sc->sc_reportlen, M_DEVBUF2, M_NOWAIT0x0002 | M_ZERO0x0008); |
603 | |
604 | if (ihidev_hid_command(sc, I2C_HID_REPORT_DESCR, 0)) { |
605 | printf("%s: failed fetching HID report\n", |
606 | sc->sc_dev.dv_xname); |
607 | return (1); |
608 | } |
609 | |
610 | return (0); |
611 | } |
612 | |
613 | void |
614 | ihidev_poll(void *arg) |
615 | { |
616 | struct ihidev_softc *sc = arg; |
617 | |
618 | sc->sc_frompoll = 1; |
619 | ihidev_intr(sc); |
620 | sc->sc_frompoll = 0; |
621 | } |
622 | |
623 | int |
624 | ihidev_intr(void *arg) |
625 | { |
626 | struct ihidev_softc *sc = arg; |
627 | struct ihidev *scd; |
628 | int psize, res, i, fast = 0; |
629 | u_char *p; |
630 | u_int rep = 0; |
631 | |
632 | if (sc->sc_dying) |
633 | return 1; |
634 | |
635 | if (sc->sc_poll && !sc->sc_frompoll) { |
636 | DPRINTF(("%s: received interrupt while polling, disabling " |
637 | "polling\n", sc->sc_dev.dv_xname)); |
638 | sc->sc_poll = 0; |
639 | timeout_del_barrier(&sc->sc_timer); |
640 | } |
641 | |
642 | /* |
643 | * XXX: force I2C_F_POLL for now to avoid dwiic interrupting |
644 | * while we are interrupting |
645 | */ |
646 | |
647 | iic_acquire_bus(sc->sc_tag, I2C_F_POLL)(*(sc->sc_tag)->ic_acquire_bus)((sc->sc_tag)->ic_cookie , (0x08)); |
648 | res = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr, NULL((void *)0), 0, |
Value stored to 'res' is never read | |
649 | sc->sc_ibuf, letoh16(sc->hid_desc.wMaxInputLength)((__uint16_t)(sc->hid_desc.wMaxInputLength)), I2C_F_POLL0x08); |
650 | iic_release_bus(sc->sc_tag, I2C_F_POLL)(*(sc->sc_tag)->ic_release_bus)((sc->sc_tag)->ic_cookie , (0x08)); |
651 | |
652 | /* |
653 | * 6.1.1 - First two bytes are the packet length, which must be less |
654 | * than or equal to wMaxInputLength |
655 | */ |
656 | psize = sc->sc_ibuf[0] | sc->sc_ibuf[1] << 8; |
657 | if (psize <= 2 || psize > sc->sc_isize) { |
658 | if (sc->sc_poll) { |
659 | /* |
660 | * TODO: all fingers are up, should we pass to hid |
661 | * layer? |
662 | */ |
663 | sc->sc_fastpoll = 0; |
664 | goto more_polling; |
665 | } else |
666 | DPRINTF(("%s: %s: invalid packet size (%d vs. %d)\n", |
667 | sc->sc_dev.dv_xname, __func__, psize, |
668 | sc->sc_isize)); |
669 | return (1); |
670 | } |
671 | |
672 | /* 3rd byte is the report id */ |
673 | p = sc->sc_ibuf + 2; |
674 | psize -= 2; |
675 | if (sc->sc_nrepid != 1) |
676 | rep = *p++, psize--; |
677 | |
678 | if (rep >= sc->sc_nrepid) { |
679 | printf("%s: %s: bad report id %d\n", sc->sc_dev.dv_xname, |
680 | __func__, rep); |
681 | if (sc->sc_poll) { |
682 | sc->sc_fastpoll = 0; |
683 | goto more_polling; |
684 | } |
685 | return (1); |
686 | } |
687 | |
688 | DPRINTF(("%s: %s: hid input (rep %d):", sc->sc_dev.dv_xname, __func__, |
689 | rep)); |
690 | for (i = 0; i < psize; i++) { |
691 | if (i > 0 && p[i] != 0 && p[i] != 0xff) { |
692 | fast = 1; |
693 | } |
694 | DPRINTF((" %.2x", p[i])); |
695 | } |
696 | DPRINTF(("\n")); |
697 | |
698 | scd = sc->sc_subdevs[rep]; |
699 | if (scd == NULL((void *)0) || !(scd->sc_state & IHIDEV_OPEN0x01)) { |
700 | if (sc->sc_poll) { |
701 | if (sc->sc_fastpoll) { |
702 | DPRINTF(("%s: fast->slow polling\n", |
703 | sc->sc_dev.dv_xname)); |
704 | sc->sc_fastpoll = 0; |
705 | } |
706 | goto more_polling; |
707 | } |
708 | return (1); |
709 | } |
710 | |
711 | scd->sc_intr(scd, p, psize); |
712 | |
713 | if (sc->sc_poll && (fast != sc->sc_fastpoll)) { |
714 | DPRINTF(("%s: %s->%s polling\n", sc->sc_dev.dv_xname, |
715 | sc->sc_fastpoll ? "fast" : "slow", |
716 | fast ? "fast" : "slow")); |
717 | sc->sc_fastpoll = fast; |
718 | } |
719 | |
720 | more_polling: |
721 | if (sc->sc_poll && sc->sc_refcnt && !sc->sc_dying && |
722 | !timeout_pending(&sc->sc_timer)((&sc->sc_timer)->to_flags & 0x02)) |
723 | timeout_add_msec(&sc->sc_timer, |
724 | sc->sc_fastpoll ? FAST_POLL_MS10 : SLOW_POLL_MS200); |
725 | |
726 | return (1); |
727 | } |
728 | |
729 | int |
730 | ihidev_maxrepid(void *buf, int len) |
731 | { |
732 | struct hid_data *d; |
733 | struct hid_item h; |
734 | int maxid; |
735 | |
736 | maxid = -1; |
737 | h.report_ID = 0; |
738 | for (d = hid_start_parse(buf, len, hid_all); hid_get_item(d, &h);) |
739 | if (h.report_ID > maxid) |
740 | maxid = h.report_ID; |
741 | hid_end_parse(d); |
742 | |
743 | return (maxid); |
744 | } |
745 | |
746 | int |
747 | ihidev_print(void *aux, const char *pnp) |
748 | { |
749 | struct ihidev_attach_arg *iha = aux; |
750 | |
751 | if (pnp) |
752 | printf("hid at %s", pnp); |
753 | |
754 | if (iha->reportid != 0) |
755 | printf(" reportid %d", iha->reportid); |
756 | |
757 | return (UNCONF1); |
758 | } |
759 | |
760 | int |
761 | ihidev_submatch(struct device *parent, void *match, void *aux) |
762 | { |
763 | struct ihidev_attach_arg *iha = aux; |
764 | struct cfdata *cf = match; |
765 | |
766 | if (cf->ihidevcf_reportidcf_loc[0] != IHIDEV_UNK_REPORTID-1 && |
767 | cf->ihidevcf_reportidcf_loc[0] != iha->reportid) |
768 | return (0); |
769 | |
770 | return ((*cf->cf_attach->ca_match)(parent, cf, aux)); |
771 | } |
772 | |
773 | int |
774 | ihidev_open(struct ihidev *scd) |
775 | { |
776 | struct ihidev_softc *sc = scd->sc_parent; |
777 | |
778 | DPRINTF(("%s: %s: state=%d refcnt=%d\n", sc->sc_dev.dv_xname, |
779 | __func__, scd->sc_state, sc->sc_refcnt)); |
780 | |
781 | if (scd->sc_state & IHIDEV_OPEN0x01) |
782 | return (EBUSY16); |
783 | |
784 | scd->sc_state |= IHIDEV_OPEN0x01; |
785 | |
786 | if (sc->sc_refcnt++ || sc->sc_isize == 0) |
787 | return (0); |
788 | |
789 | /* power on */ |
790 | ihidev_reset(sc); |
791 | |
792 | if (sc->sc_poll) { |
793 | if (!timeout_initialized(&sc->sc_timer)((&sc->sc_timer)->to_flags & 0x04)) |
794 | timeout_set(&sc->sc_timer, (void *)ihidev_poll, sc); |
795 | if (!timeout_pending(&sc->sc_timer)((&sc->sc_timer)->to_flags & 0x02)) |
796 | timeout_add(&sc->sc_timer, FAST_POLL_MS10); |
797 | } |
798 | |
799 | return (0); |
800 | } |
801 | |
802 | void |
803 | ihidev_close(struct ihidev *scd) |
804 | { |
805 | struct ihidev_softc *sc = scd->sc_parent; |
806 | |
807 | DPRINTF(("%s: %s: state=%d refcnt=%d\n", sc->sc_dev.dv_xname, |
808 | __func__, scd->sc_state, sc->sc_refcnt)); |
809 | |
810 | if (!(scd->sc_state & IHIDEV_OPEN0x01)) |
811 | return; |
812 | |
813 | scd->sc_state &= ~IHIDEV_OPEN0x01; |
814 | |
815 | if (--sc->sc_refcnt) |
816 | return; |
817 | |
818 | /* no sub-devices open, conserve power */ |
819 | |
820 | if (sc->sc_poll && timeout_pending(&sc->sc_timer)((&sc->sc_timer)->to_flags & 0x02)) |
821 | timeout_del(&sc->sc_timer); |
822 | |
823 | if (ihidev_hid_command(sc, I2C_HID_CMD_SET_POWER, &I2C_HID_POWER_OFF)) |
824 | printf("%s: failed to power down\n", sc->sc_dev.dv_xname); |
825 | } |
826 | |
827 | int |
828 | ihidev_ioctl(struct ihidev *sc, u_long cmd, caddr_t addr, int flag, |
829 | struct proc *p) |
830 | { |
831 | return -1; |
832 | } |
833 | |
834 | void |
835 | ihidev_get_report_desc(struct ihidev_softc *sc, void **desc, int *size) |
836 | { |
837 | *desc = sc->sc_report; |
838 | *size = sc->sc_reportlen; |
839 | } |
840 | |
841 | int |
842 | ihidev_report_type_conv(int hid_type_id) |
843 | { |
844 | switch (hid_type_id) { |
845 | case hid_input: |
846 | return I2C_HID_REPORT_TYPE_INPUT0x1; |
847 | case hid_output: |
848 | return I2C_HID_REPORT_TYPE_OUTPUT0x2; |
849 | case hid_feature: |
850 | return I2C_HID_REPORT_TYPE_FEATURE0x3; |
851 | default: |
852 | return -1; |
853 | } |
854 | } |
855 | |
856 | int |
857 | ihidev_get_report(struct device *dev, int type, int id, void *data, int len) |
858 | { |
859 | struct ihidev_softc *sc = (struct ihidev_softc *)dev; |
860 | struct i2c_hid_report_request rreq; |
861 | |
862 | rreq.type = type; |
863 | rreq.id = id; |
864 | rreq.data = data; |
865 | rreq.len = len; |
866 | |
867 | if (ihidev_hid_command(sc, I2C_HID_CMD_GET_REPORT, &rreq)) { |
868 | printf("%s: failed fetching report\n", sc->sc_dev.dv_xname); |
869 | return (1); |
870 | } |
871 | |
872 | return 0; |
873 | } |
874 | |
875 | int |
876 | ihidev_set_report(struct device *dev, int type, int id, void *data, int len) |
877 | { |
878 | struct ihidev_softc *sc = (struct ihidev_softc *)dev; |
879 | struct i2c_hid_report_request rreq; |
880 | |
881 | rreq.type = type; |
882 | rreq.id = id; |
883 | rreq.data = data; |
884 | rreq.len = len; |
885 | |
886 | if (ihidev_hid_command(sc, I2C_HID_CMD_SET_REPORT, &rreq)) { |
887 | printf("%s: failed setting report\n", sc->sc_dev.dv_xname); |
888 | return (1); |
889 | } |
890 | |
891 | return 0; |
892 | } |