xref: /netbsd-src/sys/dev/hid/hid.c (revision bdc22b2e01993381dcefeff2bc9b56ca75a4235c)
1 /*	$NetBSD: hid.c,v 1.1 2017/12/10 17:03:07 bouyer 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.1 2017/12/10 17:03:07 bouyer 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/hid/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
54 extern int uhidevdebug;
55 #else
56 #define DPRINTF(x)
57 #define DPRINTFN(n,x)
58 #endif
59 
60 Static void hid_clear_local(struct hid_item *);
61 
62 #define MAXUSAGE 256
63 struct 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 
76 Static void
77 hid_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 
93 struct hid_data *
94 hid_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 	s->start = s->p = d;
100 	s->end = (const char *)d + len;
101 	s->kind = kind;
102 	return s;
103 }
104 
105 void
106 hid_end_parse(struct hid_data *s)
107 {
108 
109 	while (s->cur.next != NULL) {
110 		struct hid_item *hi = s->cur.next->next;
111 		kmem_free(s->cur.next, sizeof(*s->cur.next));
112 		s->cur.next = hi;
113 	}
114 	kmem_free(s, sizeof(*s));
115 }
116 
117 int
118 hid_get_item(struct hid_data *s, struct hid_item *h)
119 {
120 	struct hid_item *c = &s->cur;
121 	unsigned int bTag, bType, bSize;
122 	uint32_t oldpos;
123 	const u_char *data;
124 	int32_t dval;
125 	const u_char *p;
126 	struct hid_item *hi;
127 	int i;
128 	enum hid_kind retkind;
129 
130  top:
131 	DPRINTFN(5,("hid_get_item: multi=%d multimax=%d\n",
132 		    s->multi, s->multimax));
133 	if (s->multimax != 0) {
134 		if (s->multi < s->multimax) {
135 			c->usage = s->usages[min(s->multi, s->nu-1)];
136 			s->multi++;
137 			*h = *c;
138 			c->loc.pos += c->loc.size;
139 			h->next = NULL;
140 			DPRINTFN(5,("return multi\n"));
141 			return 1;
142 		} else {
143 			c->loc.count = s->multimax;
144 			s->multimax = 0;
145 			s->nu = 0;
146 			hid_clear_local(c);
147 		}
148 	}
149 	for (;;) {
150 		p = s->p;
151 		if (p >= s->end)
152 			return 0;
153 
154 		bSize = *p++;
155 		if (bSize == 0xfe) {
156 			/* long item */
157 			bSize = *p++;
158 			bSize |= *p++ << 8;
159 			bTag = *p++;
160 			data = p;
161 			p += bSize;
162 			bType = 0xff; /* XXX what should it be */
163 		} else {
164 			/* short item */
165 			bTag = bSize >> 4;
166 			bType = (bSize >> 2) & 3;
167 			bSize &= 3;
168 			if (bSize == 3) bSize = 4;
169 			data = p;
170 			p += bSize;
171 		}
172 		s->p = p;
173 		switch(bSize) {
174 		case 0:
175 			dval = 0;
176 			break;
177 		case 1:
178 			dval = (int8_t)*data++;
179 			break;
180 		case 2:
181 			dval = *data++;
182 			dval |= *data++ << 8;
183 			dval = (int16_t)dval;
184 			break;
185 		case 4:
186 			dval = *data++;
187 			dval |= *data++ << 8;
188 			dval |= *data++ << 16;
189 			dval |= *data++ << 24;
190 			dval = (int32_t)dval;
191 			break;
192 		default:
193 			aprint_normal("BAD LENGTH %d\n", bSize);
194 			continue;
195 		}
196 
197 		DPRINTFN(5,("hid_get_item: bType=%d bTag=%d dval=%d\n",
198 			 bType, bTag, dval));
199 		switch (bType) {
200 		case 0:			/* Main */
201 			switch (bTag) {
202 			case 8:		/* Input */
203 				retkind = hid_input;
204 			ret:
205 				if (s->kind != retkind) {
206 					s->minset = 0;
207 					s->nu = 0;
208 					hid_clear_local(c);
209 					continue;
210 				}
211 				c->kind = retkind;
212 				c->flags = dval;
213 				if (c->flags & HIO_VARIABLE) {
214 					s->multimax = c->loc.count;
215 					s->multi = 0;
216 					c->loc.count = 1;
217 					if (s->minset) {
218 						for (i = c->usage_minimum;
219 						     i <= c->usage_maximum;
220 						     i++) {
221 							s->usages[s->nu] = i;
222 							if (s->nu < MAXUSAGE-1)
223 								s->nu++;
224 						}
225 						s->minset = 0;
226 					}
227 					goto top;
228 				} else {
229 					if (s->minset)
230 						c->usage = c->usage_minimum;
231 					*h = *c;
232 					h->next = NULL;
233 					c->loc.pos +=
234 					    c->loc.size * c->loc.count;
235 					s->minset = 0;
236 					s->nu = 0;
237 					hid_clear_local(c);
238 					return 1;
239 				}
240 			case 9:		/* Output */
241 				retkind = hid_output;
242 				goto ret;
243 			case 10:	/* Collection */
244 				c->kind = hid_collection;
245 				c->collection = dval;
246 				c->collevel++;
247 				*h = *c;
248 				hid_clear_local(c);
249 				s->nu = 0;
250 				return 1;
251 			case 11:	/* Feature */
252 				retkind = hid_feature;
253 				goto ret;
254 			case 12:	/* End collection */
255 				c->kind = hid_endcollection;
256 				c->collevel--;
257 				*h = *c;
258 				s->nu = 0;
259 				return 1;
260 			default:
261 				aprint_normal("Main bTag=%d\n", bTag);
262 				break;
263 			}
264 			break;
265 		case 1:		/* Global */
266 			switch (bTag) {
267 			case 0:
268 				c->_usage_page = dval << 16;
269 				break;
270 			case 1:
271 				c->logical_minimum = dval;
272 				break;
273 			case 2:
274 				c->logical_maximum = dval;
275 				break;
276 			case 3:
277 				c->physical_minimum = dval;
278 				break;
279 			case 4:
280 				c->physical_maximum = dval;
281 				break;
282 			case 5:
283 				c->unit_exponent = dval;
284 				break;
285 			case 6:
286 				c->unit = dval;
287 				break;
288 			case 7:
289 				c->loc.size = dval;
290 				break;
291 			case 8:
292 				c->report_ID = dval;
293 				c->loc.pos = 0;
294 				break;
295 			case 9:
296 				c->loc.count = dval;
297 				break;
298 			case 10: /* Push */
299 				hi = kmem_alloc(sizeof(*hi), KM_SLEEP);
300 				*hi = *c;
301 				c->next = hi;
302 				break;
303 			case 11: /* Pop */
304 				hi = c->next;
305 				if (hi == NULL)
306 					break;
307 				oldpos = c->loc.pos;
308 				*c = *hi;
309 				c->loc.pos = oldpos;
310 				kmem_free(hi, sizeof(*hi));
311 				break;
312 			default:
313 				aprint_normal("Global bTag=%d\n", bTag);
314 				break;
315 			}
316 			break;
317 		case 2:		/* Local */
318 			switch (bTag) {
319 			case 0:
320 				if (bSize == 1)
321 					dval = c->_usage_page | (dval&0xff);
322 				else if (bSize == 2)
323 					dval = c->_usage_page | (dval&0xffff);
324 				c->usage = dval;
325 				if (s->nu < MAXUSAGE)
326 					s->usages[s->nu++] = dval;
327 				/* else XXX */
328 				break;
329 			case 1:
330 				s->minset = 1;
331 				if (bSize == 1)
332 					dval = c->_usage_page | (dval&0xff);
333 				else if (bSize == 2)
334 					dval = c->_usage_page | (dval&0xffff);
335 				c->usage_minimum = dval;
336 				break;
337 			case 2:
338 				if (bSize == 1)
339 					dval = c->_usage_page | (dval&0xff);
340 				else if (bSize == 2)
341 					dval = c->_usage_page | (dval&0xffff);
342 				c->usage_maximum = dval;
343 				break;
344 			case 3:
345 				c->designator_index = dval;
346 				break;
347 			case 4:
348 				c->designator_minimum = dval;
349 				break;
350 			case 5:
351 				c->designator_maximum = dval;
352 				break;
353 			case 7:
354 				c->string_index = dval;
355 				break;
356 			case 8:
357 				c->string_minimum = dval;
358 				break;
359 			case 9:
360 				c->string_maximum = dval;
361 				break;
362 			case 10:
363 				c->set_delimiter = dval;
364 				break;
365 			default:
366 				aprint_normal("Local bTag=%d\n", bTag);
367 				break;
368 			}
369 			break;
370 		default:
371 			aprint_normal("default bType=%d\n", bType);
372 			break;
373 		}
374 	}
375 }
376 
377 int
378 hid_report_size(const void *buf, int len, enum hid_kind k, uint8_t id)
379 {
380 	struct hid_data *d;
381 	struct hid_item h;
382 	int lo, hi;
383 
384 	h.report_ID = 0;
385 	lo = hi = -1;
386 	DPRINTFN(2,("hid_report_size: kind=%d id=%d\n", k, id));
387 	for (d = hid_start_parse(buf, len, k); hid_get_item(d, &h); ) {
388 		DPRINTFN(2,("hid_report_size: item kind=%d id=%d pos=%d "
389 			    "size=%d count=%d\n",
390 			    h.kind, h.report_ID, h.loc.pos, h.loc.size,
391 			    h.loc.count));
392 		if (h.report_ID == id && h.kind == k) {
393 			if (lo < 0) {
394 				lo = h.loc.pos;
395 #ifdef DIAGNOSTIC
396 				if (lo != 0) {
397 					aprint_normal("hid_report_size:"
398 					   " 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(const void *desc, int size, uint32_t u, uint8_t id, enum hid_kind k,
412 	   struct hid_location *loc, uint32_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 	if (loc != NULL)
434 		loc->size = 0;
435 	return 0;
436 }
437 
438 long
439 hid_get_data(const u_char *buf, const struct hid_location *loc)
440 {
441 	u_int hsize = loc->size;
442 	u_long data;
443 
444 	if (hsize == 0)
445 		return 0;
446 
447 	data = hid_get_udata(buf, loc);
448 	if (data < (1UL << (hsize - 1)) || hsize == sizeof(data) * NBBY)
449 		return data;
450 	return data - (1UL << hsize);
451 }
452 
453 u_long
454 hid_get_udata(const u_char *buf, const struct hid_location *loc)
455 {
456 	u_int hpos = loc->pos;
457 	u_int hsize = loc->size;
458 	u_int i, num, off;
459 	u_long data;
460 
461 	if (hsize == 0)
462 		return 0;
463 
464 	data = 0;
465 	off = hpos / 8;
466 	num = (hpos + hsize + 7) / 8 - off;
467 
468 	for (i = 0; i < num; i++)
469 		data |= (unsigned long)buf[off + i] << (i * 8);
470 
471 	data >>= hpos % 8;
472 	if (hsize < sizeof(data) * NBBY)
473 		data &= (1UL << hsize) - 1;
474 
475 	DPRINTFN(10,("hid_get_udata: loc %d/%d = %lu\n", hpos, hsize, data));
476 	return data;
477 }
478 
479 /*
480  * hid_is_collection(desc, size, id, usage)
481  *
482  * This function is broken in the following way.
483  *
484  * It is used to discover if the given 'id' is part of 'usage' collection
485  * in the descriptor in order to match report id against device type.
486  *
487  * The semantics of hid_start_parse() means though, that only a single
488  * kind of report is considered. The current HID code that uses this for
489  * matching is actually only looking for input reports, so this works
490  * for now.
491  *
492  * This function could try all report kinds (input, output and feature)
493  * consecutively if necessary, but it may be better to integrate the
494  * libusbhid code which can consider multiple report kinds simultaneously
495  *
496  * Needs some thought.
497  */
498 int
499 hid_is_collection(const void *desc, int size, uint8_t id, uint32_t usage)
500 {
501 	struct hid_data *hd;
502 	struct hid_item hi;
503 	uint32_t coll_usage = ~0;
504 
505 	hd = hid_start_parse(desc, size, hid_input);
506 	if (hd == NULL)
507 		return 0;
508 
509 	DPRINTFN(2,("hid_is_collection: id=%d usage=0x%x\n", id, usage));
510 	while (hid_get_item(hd, &hi)) {
511 		DPRINTFN(2,("hid_is_collection: kind=%d id=%d usage=0x%x"
512 			    "(0x%x)\n",
513 			    hi.kind, hi.report_ID, hi.usage, coll_usage));
514 
515 		if (hi.kind == hid_collection &&
516 		    hi.collection == HCOLL_APPLICATION)
517 			coll_usage = hi.usage;
518 
519 		if (hi.kind == hid_endcollection)
520 			coll_usage = ~0;
521 
522 		if (hi.kind == hid_input &&
523 		    coll_usage == usage &&
524 		    hi.report_ID == id) {
525 			DPRINTFN(2,("hid_is_collection: found\n"));
526 			hid_end_parse(hd);
527 			return 1;
528 		}
529 	}
530 	DPRINTFN(2,("hid_is_collection: not found\n"));
531 	hid_end_parse(hd);
532 	return 0;
533 }
534