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