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