Home | History | Annotate | Line # | Download | only in hid
hid.c revision 1.1
      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 (at) 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