1/* $NetBSD: hid.c,v 1.46 2016/10/15 07:10:15 nat Exp $ */
2/* $FreeBSD: src/sys/dev/usb/hid.c,v 1.11 1999/11/17 22:33:39 n_hibma Exp $ */
3
4/*
5 * Copyright (c) 1998 The NetBSD Foundation, Inc.
6 * All rights reserved.
7 *
8 * This code is derived from software contributed to The NetBSD Foundation
9 * by Lennart Augustsson (lennart@augustsson.net) at
10 * Carlstedt Research & Technology.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
23 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
25 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 * POSSIBILITY OF SUCH DAMAGE.
32 */
33
34#include <sys/cdefs.h>
35__KERNEL_RCSID(0, "$NetBSD: hid.c,v 1.46 2016/10/15 07:10:15 nat Exp $");
36
37#ifdef _KERNEL_OPT
38#include "opt_usb.h"
39#endif
40
41#include <sys/param.h>
42#include <sys/systm.h>
43#include <sys/kernel.h>
44#include <sys/kmem.h>
45
46#include <dev/usb/usb.h>
47#include <dev/usb/usbhid.h>
48
49#include <dev/usb/hid.h>
50
51#ifdef UHIDEV_DEBUG
52#define DPRINTF(x) if (uhidevdebug) printf x
53#define DPRINTFN(n,x) if (uhidevdebug>(n)) printf x
54extern int uhidevdebug;
55#else
56#define DPRINTF(x)
57#define DPRINTFN(n,x)
58#endif
59
60Static void hid_clear_local(struct hid_item *);
61
62#define MAXUSAGE 256
63struct hid_data {
64 const u_char *start;
65 const u_char *end;
66 const u_char *p;
67 struct hid_item cur;
68 int32_t usages[MAXUSAGE];
69 int nu;
70 int minset;
71 int multi;
72 int multimax;
73 enum hid_kind kind;
74};
75
76Static void
77hid_clear_local(struct hid_item *c)
78{
79
80 DPRINTFN(5,("hid_clear_local\n"));
81 c->usage = 0;
82 c->usage_minimum = 0;
83 c->usage_maximum = 0;
84 c->designator_index = 0;
85 c->designator_minimum = 0;
86 c->designator_maximum = 0;
87 c->string_index = 0;
88 c->string_minimum = 0;
89 c->string_maximum = 0;
90 c->set_delimiter = 0;
91}
92
93struct hid_data *
94hid_start_parse(const void *d, int len, enum hid_kind kind)
95{
96 struct hid_data *s;
97
98 s = kmem_zalloc(sizeof(*s), KM_SLEEP);
99 if (s == NULL)
100 return s;
101 s->start = s->p = d;
102 s->end = (const char *)d + len;
103 s->kind = kind;
104 return s;
105}
106
107void
108hid_end_parse(struct hid_data *s)
109{
110
111 while (s->cur.next != NULL) {
112 struct hid_item *hi = s->cur.next->next;
113 kmem_free(s->cur.next, sizeof(*s->cur.next));
114 s->cur.next = hi;
115 }
116 kmem_free(s, sizeof(*s));
117}
118
119int
120hid_get_item(struct hid_data *s, struct hid_item *h)
121{
122 struct hid_item *c = &s->cur;
123 unsigned int bTag, bType, bSize;
124 uint32_t oldpos;
125 const u_char *data;
126 int32_t dval;
127 const u_char *p;
128 struct hid_item *hi;
129 int i;
130 enum hid_kind retkind;
131
132 top:
133 DPRINTFN(5,("hid_get_item: multi=%d multimax=%d\n",
134 s->multi, s->multimax));
135 if (s->multimax != 0) {
136 if (s->multi < s->multimax) {
137 c->usage = s->usages[min(s->multi, s->nu-1)];
138 s->multi++;
139 *h = *c;
140 c->loc.pos += c->loc.size;
141 h->next = NULL;
142 DPRINTFN(5,("return multi\n"));
143 return 1;
144 } else {
145 c->loc.count = s->multimax;
146 s->multimax = 0;
147 s->nu = 0;
148 hid_clear_local(c);
149 }
150 }
151 for (;;) {
152 p = s->p;
153 if (p >= s->end)
154 return 0;
155
156 bSize = *p++;
157 if (bSize == 0xfe) {
158 /* long item */
159 bSize = *p++;
160 bSize |= *p++ << 8;
161 bTag = *p++;
162 data = p;
163 p += bSize;
164 bType = 0xff; /* XXX what should it be */
165 } else {
166 /* short item */
167 bTag = bSize >> 4;
168 bType = (bSize >> 2) & 3;
169 bSize &= 3;
170 if (bSize == 3) bSize = 4;
171 data = p;
172 p += bSize;
173 }
174 s->p = p;
175 switch(bSize) {
176 case 0:
177 dval = 0;
178 break;
179 case 1:
180 dval = (int8_t)*data++;
181 break;
182 case 2:
183 dval = *data++;
184 dval |= *data++ << 8;
185 dval = (int16_t)dval;
186 break;
187 case 4:
188 dval = *data++;
189 dval |= *data++ << 8;
190 dval |= *data++ << 16;
191 dval |= *data++ << 24;
192 dval = (int32_t)dval;
193 break;
194 default:
195 aprint_normal("BAD LENGTH %d\n", bSize);
196 continue;
197 }
198
199 DPRINTFN(5,("hid_get_item: bType=%d bTag=%d dval=%d\n",
200 bType, bTag, dval));
201 switch (bType) {
202 case 0: /* Main */
203 switch (bTag) {
204 case 8: /* Input */
205 retkind = hid_input;
206 ret:
207 if (s->kind != retkind) {
208 s->minset = 0;
209 s->nu = 0;
210 hid_clear_local(c);
211 continue;
212 }
213 c->kind = retkind;
214 c->flags = dval;
215 if (c->flags & HIO_VARIABLE) {
216 s->multimax = c->loc.count;
217 s->multi = 0;
218 c->loc.count = 1;
219 if (s->minset) {
220 for (i = c->usage_minimum;
221 i <= c->usage_maximum;
222 i++) {
223 s->usages[s->nu] = i;
224 if (s->nu < MAXUSAGE-1)
225 s->nu++;
226 }
227 s->minset = 0;
228 }
229 goto top;
230 } else {
231 if (s->minset)
232 c->usage = c->usage_minimum;
233 *h = *c;
234 h->next = NULL;
235 c->loc.pos +=
236 c->loc.size * c->loc.count;
237 s->minset = 0;
238 s->nu = 0;
239 hid_clear_local(c);
240 return 1;
241 }
242 case 9: /* Output */
243 retkind = hid_output;
244 goto ret;
245 case 10: /* Collection */
246 c->kind = hid_collection;
247 c->collection = dval;
248 c->collevel++;
249 *h = *c;
250 hid_clear_local(c);
251 s->nu = 0;
252 return 1;
253 case 11: /* Feature */
254 retkind = hid_feature;
255 goto ret;
256 case 12: /* End collection */
257 c->kind = hid_endcollection;
258 c->collevel--;
259 *h = *c;
260 s->nu = 0;
261 return 1;
262 default:
263 aprint_normal("Main bTag=%d\n", bTag);
264 break;
265 }
266 break;
267 case 1: /* Global */
268 switch (bTag) {
269 case 0:
270 c->_usage_page = dval << 16;
271 break;
272 case 1:
273 c->logical_minimum = dval;
274 break;
275 case 2:
276 c->logical_maximum = dval;
277 break;
278 case 3:
279 c->physical_minimum = dval;
280 break;
281 case 4:
282 c->physical_maximum = dval;
283 break;
284 case 5:
285 c->unit_exponent = dval;
286 break;
287 case 6:
288 c->unit = dval;
289 break;
290 case 7:
291 c->loc.size = dval;
292 break;
293 case 8:
294 c->report_ID = dval;
295 c->loc.pos = 0;
296 break;
297 case 9:
298 c->loc.count = dval;
299 break;
300 case 10: /* Push */
301 hi = kmem_alloc(sizeof(*hi), KM_SLEEP);
302 *hi = *c;
303 c->next = hi;
304 break;
305 case 11: /* Pop */
306 hi = c->next;
307 if (hi == NULL)
308 break;
309 oldpos = c->loc.pos;
310 *c = *hi;
311 c->loc.pos = oldpos;
312 kmem_free(hi, sizeof(*hi));
313 break;
314 default:
315 aprint_normal("Global bTag=%d\n", bTag);
316 break;
317 }
318 break;
319 case 2: /* Local */
320 switch (bTag) {
321 case 0:
322 if (bSize == 1)
323 dval = c->_usage_page | (dval&0xff);
324 else if (bSize == 2)
325 dval = c->_usage_page | (dval&0xffff);
326 c->usage = dval;
327 if (s->nu < MAXUSAGE)
328 s->usages[s->nu++] = dval;
329 /* else XXX */
330 break;
331 case 1:
332 s->minset = 1;
333 if (bSize == 1)
334 dval = c->_usage_page | (dval&0xff);
335 else if (bSize == 2)
336 dval = c->_usage_page | (dval&0xffff);
337 c->usage_minimum = dval;
338 break;
339 case 2:
340 if (bSize == 1)
341 dval = c->_usage_page | (dval&0xff);
342 else if (bSize == 2)
343 dval = c->_usage_page | (dval&0xffff);
344 c->usage_maximum = dval;
345 break;
346 case 3:
347 c->designator_index = dval;
348 break;
349 case 4:
350 c->designator_minimum = dval;
351 break;
352 case 5:
353 c->designator_maximum = dval;
354 break;
355 case 7:
356 c->string_index = dval;
357 break;
358 case 8:
359 c->string_minimum = dval;
360 break;
361 case 9:
362 c->string_maximum = dval;
363 break;
364 case 10:
365 c->set_delimiter = dval;
366 break;
367 default:
368 aprint_normal("Local bTag=%d\n", bTag);
369 break;
370 }
371 break;
372 default:
373 aprint_normal("default bType=%d\n", bType);
374 break;
375 }
376 }
377}
378
379int
380hid_report_size(const void *buf, int len, enum hid_kind k, uint8_t id)
381{
382 struct hid_data *d;
383 struct hid_item h;
384 int lo, hi;
385
386 h.report_ID = 0;
387 lo = hi = -1;
388 DPRINTFN(2,("hid_report_size: kind=%d id=%d\n", k, id));
389 for (d = hid_start_parse(buf, len, k); hid_get_item(d, &h); ) {
390 DPRINTFN(2,("hid_report_size: item kind=%d id=%d pos=%d "
391 "size=%d count=%d\n",
392 h.kind, h.report_ID, h.loc.pos, h.loc.size,
393 h.loc.count));
394 if (h.report_ID == id && h.kind == k) {
395 if (lo < 0) {
396 lo = h.loc.pos;
397#ifdef DIAGNOSTIC
398 if (lo != 0) {
399 aprint_normal("hid_report_size:"
400 " lo != 0\n");
401 }
402#endif
403 }
404 hi = h.loc.pos + h.loc.size * h.loc.count;
405 DPRINTFN(2,("hid_report_size: lo=%d hi=%d\n", lo, hi));
406 }
407 }
408 hid_end_parse(d);
409 return (hi - lo + 7) / 8;
410}
411
412int
413hid_locate(const void *desc, int size, uint32_t u, uint8_t id, enum hid_kind k,
414 struct hid_location *loc, uint32_t *flags)
415{
416 struct hid_data *d;
417 struct hid_item h;
418
419 h.report_ID = 0;
420 DPRINTFN(5,("hid_locate: enter usage=0x%x kind=%d id=%d\n", u, k, id));
421 for (d = hid_start_parse(desc, size, k); hid_get_item(d, &h); ) {
422 DPRINTFN(5,("hid_locate: usage=0x%x kind=%d id=%d flags=0x%x\n",
423 h.usage, h.kind, h.report_ID, h.flags));
424 if (h.kind == k && !(h.flags & HIO_CONST) &&
425 h.usage == u && h.report_ID == id) {
426 if (loc != NULL)
427 *loc = h.loc;
428 if (flags != NULL)
429 *flags = h.flags;
430 hid_end_parse(d);
431 return 1;
432 }
433 }
434 hid_end_parse(d);
435 if (loc != NULL)
436 loc->size = 0;
437 return 0;
438}
439
440long
441hid_get_data(const u_char *buf, const struct hid_location *loc)
442{
443 u_int hsize = loc->size;
444 u_long data;
445
446 if (hsize == 0)
447 return 0;
448
449 data = hid_get_udata(buf, loc);
450 if (data < (1UL << (hsize - 1)) || hsize == sizeof(data) * NBBY)
451 return data;
452 return data - (1UL << hsize);
453}
454
455u_long
456hid_get_udata(const u_char *buf, const struct hid_location *loc)
457{
458 u_int hpos = loc->pos;
459 u_int hsize = loc->size;
460 u_int i, num, off;
461 u_long data;
462
463 if (hsize == 0)
464 return 0;
465
466 data = 0;
467 off = hpos / 8;
468 num = (hpos + hsize + 7) / 8 - off;
469
470 for (i = 0; i < num; i++)
471 data |= (unsigned long)buf[off + i] << (i * 8);
472
473 data >>= hpos % 8;
474 if (hsize < sizeof(data) * NBBY)
475 data &= (1UL << hsize) - 1;
476
477 DPRINTFN(10,("hid_get_udata: loc %d/%d = %lu\n", hpos, hsize, data));
478 return data;
479}
480
481/*
482 * hid_is_collection(desc, size, id, usage)
483 *
484 * This function is broken in the following way.
485 *
486 * It is used to discover if the given 'id' is part of 'usage' collection
487 * in the descriptor in order to match report id against device type.
488 *
489 * The semantics of hid_start_parse() means though, that only a single
490 * kind of report is considered. The current HID code that uses this for
491 * matching is actually only looking for input reports, so this works
492 * for now.
493 *
494 * This function could try all report kinds (input, output and feature)
495 * consecutively if necessary, but it may be better to integrate the
496 * libusbhid code which can consider multiple report kinds simultaneously
497 *
498 * Needs some thought.
499 */
500int
501hid_is_collection(const void *desc, int size, uint8_t id, uint32_t usage)
502{
503 struct hid_data *hd;
504 struct hid_item hi;
505 uint32_t coll_usage = ~0;
506
507 hd = hid_start_parse(desc, size, hid_input);
508 if (hd == NULL)
509 return 0;
510
511 DPRINTFN(2,("hid_is_collection: id=%d usage=0x%x\n", id, usage));
512 while (hid_get_item(hd, &hi)) {
513 DPRINTFN(2,("hid_is_collection: kind=%d id=%d usage=0x%x"
514 "(0x%x)\n",
515 hi.kind, hi.report_ID, hi.usage, coll_usage));
516
517 if (hi.kind == hid_collection &&
518 hi.collection == HCOLL_APPLICATION)
519 coll_usage = hi.usage;
520
521 if (hi.kind == hid_endcollection)
522 coll_usage = ~0;
523
524 if (hi.kind == hid_input &&
525 coll_usage == usage &&
526 hi.report_ID == id) {
527 DPRINTFN(2,("hid_is_collection: found\n"));
528 hid_end_parse(hd);
529 return 1;
530 }
531 }
532 DPRINTFN(2,("hid_is_collection: not found\n"));
533 hid_end_parse(hd);
534 return 0;
535}
536