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