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