Home | History | Annotate | Line # | Download | only in usbhidctl
usbhid.c revision 1.20
      1 /*      $NetBSD: usbhid.c,v 1.20 2001/12/28 17:49:32 augustss Exp $ */
      2 
      3 /*
      4  * Copyright (c) 2001 The NetBSD Foundation, Inc.
      5  * All rights reserved.
      6  *
      7  * This code is derived from software contributed to The NetBSD Foundation
      8  * by David Sainty <David.Sainty (at) dtsp.co.nz>
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  * 1. Redistributions of source code must retain the above copyright
     14  *    notice, this list of conditions and the following disclaimer.
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  * 3. All advertising materials mentioning features or use of this software
     19  *    must display the following acknowledgement:
     20  *        This product includes software developed by the NetBSD
     21  *        Foundation, Inc. and its contributors.
     22  * 4. Neither the name of The NetBSD Foundation nor the names of its
     23  *    contributors may be used to endorse or promote products derived
     24  *    from this software without specific prior written permission.
     25  *
     26  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     27  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     28  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     29  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     30  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     36  * POSSIBILITY OF SUCH DAMAGE.
     37  */
     38 
     39 #include <sys/types.h>
     40 
     41 #include <dev/usb/usb.h>
     42 #include <dev/usb/usbhid.h>
     43 
     44 #include <ctype.h>
     45 #include <err.h>
     46 #include <errno.h>
     47 #include <fcntl.h>
     48 #include <limits.h>
     49 #include <stdio.h>
     50 #include <stdlib.h>
     51 #include <string.h>
     52 #include <unistd.h>
     53 #include <usbhid.h>
     54 
     55 /*
     56  * Zero if not in a verbose mode.  Greater levels of verbosity
     57  * are indicated by values larger than one.
     58  */
     59 unsigned int verbose;
     60 
     61 /* Parser tokens */
     62 #define DELIM_USAGE '.'
     63 #define DELIM_PAGE ':'
     64 #define DELIM_SET '='
     65 
     66 static int reportid;
     67 
     68 struct Susbvar {
     69 	/* Variable name, not NUL terminated */
     70 	char const *variable;
     71 	size_t varlen;
     72 
     73 	char const *value; /* Value to set variable to */
     74 
     75 #define MATCH_ALL		(1 << 0)
     76 #define MATCH_COLLECTIONS	(1 << 1)
     77 #define MATCH_NODATA		(1 << 2)
     78 #define MATCH_CONSTANTS		(1 << 3)
     79 #define MATCH_WASMATCHED	(1 << 4)
     80 #define MATCH_SHOWPAGENAME	(1 << 5)
     81 #define MATCH_SHOWNUMERIC	(1 << 6)
     82 #define MATCH_WRITABLE		(1 << 7)
     83 #define MATCH_SHOWVALUES	(1 << 8)
     84 	unsigned int mflags;
     85 
     86 	/* Workspace for hidmatch() */
     87 	ssize_t matchindex;
     88 
     89 	int (*opfunc)(struct hid_item *item, struct Susbvar *var,
     90 		      u_int32_t const *collist, size_t collen, u_char *buf);
     91 };
     92 
     93 struct Sreport {
     94 	struct usb_ctl_report *buffer;
     95 
     96 	enum {srs_uninit, srs_clean, srs_dirty} status;
     97 	int report_id;
     98 	size_t size;
     99 };
    100 
    101 static struct {
    102 	int uhid_report;
    103 	hid_kind_t hid_kind;
    104 	char const *name;
    105 } const reptoparam[] = {
    106 #define REPORT_INPUT 0
    107 	{ UHID_INPUT_REPORT, hid_input, "input" },
    108 #define REPORT_OUTPUT 1
    109 	{ UHID_OUTPUT_REPORT, hid_output, "output" },
    110 #define REPORT_FEATURE 2
    111 	{ UHID_FEATURE_REPORT, hid_feature, "feature" }
    112 #define REPORT_MAXVAL 2
    113 };
    114 
    115 /*
    116  * Extract 16-bit unsigned usage ID from a numeric string.  Returns -1
    117  * if string failed to parse correctly.
    118  */
    119 static int
    120 strtousage(const char *nptr, size_t nlen)
    121 {
    122 	char *endptr;
    123 	long result;
    124 	char numstr[16];
    125 
    126 	if (nlen >= sizeof(numstr) || !isdigit((unsigned char)*nptr))
    127 		return -1;
    128 
    129 	/*
    130 	 * We use strtol() here, but unfortunately strtol() requires a
    131 	 * NUL terminated string - which we don't have - at least not
    132 	 * officially.
    133 	 */
    134 	memcpy(numstr, nptr, nlen);
    135 	numstr[nlen] = '\0';
    136 
    137 	result = strtol(numstr, &endptr, 0);
    138 
    139 	if (result < 0 || result > 0xffff || endptr != &numstr[nlen])
    140 		return -1;
    141 
    142 	return result;
    143 }
    144 
    145 struct usagedata {
    146 	char const *page_name;
    147 	char const *usage_name;
    148 	size_t page_len;
    149 	size_t usage_len;
    150 	int isfinal;
    151 	u_int32_t usage_id;
    152 };
    153 
    154 /*
    155  * Test a rule against the current usage data.  Returns -1 on no
    156  * match, 0 on partial match and 1 on complete match.
    157  */
    158 static int
    159 hidtestrule(struct Susbvar *var, struct usagedata *cache)
    160 {
    161 	char const *varname;
    162 	ssize_t matchindex, pagesplit;
    163 	size_t strind, varlen;
    164 	int numusage;
    165 	u_int32_t usage_id;
    166 
    167 	matchindex = var->matchindex;
    168 	varname = var->variable;
    169 	varlen = var->varlen;
    170 
    171 	usage_id = cache->usage_id;
    172 
    173 	/*
    174 	 * Parse the current variable name, locating the end of the
    175 	 * current 'usage', and possibly where the usage page name
    176 	 * ends.
    177 	 */
    178 	pagesplit = -1;
    179 	for (strind = matchindex; strind < varlen; strind++) {
    180 		if (varname[strind] == DELIM_USAGE)
    181 			break;
    182 		if (varname[strind] == DELIM_PAGE)
    183 			pagesplit = strind;
    184 	}
    185 
    186 	if (cache->isfinal && strind != varlen)
    187 		/*
    188 		 * Variable name is too long (hit delimiter instead of
    189 		 * end-of-variable).
    190 		 */
    191 		return -1;
    192 
    193 	if (pagesplit >= 0) {
    194 		/*
    195 		 * Page name was specified, determine whether it was
    196 		 * symbolic or numeric.
    197 		 */
    198 		char const *strstart;
    199 		int numpage;
    200 
    201 		strstart = &varname[matchindex];
    202 
    203 		numpage = strtousage(strstart, pagesplit - matchindex);
    204 
    205 		if (numpage >= 0) {
    206 			/* Valid numeric */
    207 
    208 			if (numpage != HID_PAGE(usage_id))
    209 				/* Numeric didn't match page ID */
    210 				return -1;
    211 		} else {
    212 			/* Not a valid numeric */
    213 
    214 			/*
    215 			 * Load and cache the page name if and only if
    216 			 * it hasn't already been loaded (it's a
    217 			 * fairly expensive operation).
    218 			 */
    219 			if (cache->page_name == NULL) {
    220 				cache->page_name = hid_usage_page(HID_PAGE(usage_id));
    221 				cache->page_len = strlen(cache->page_name);
    222 			}
    223 
    224 			/*
    225 			 * Compare specified page name to actual page
    226 			 * name.
    227 			 */
    228 			if (cache->page_len !=
    229 			    (size_t)(pagesplit - matchindex) ||
    230 			    memcmp(cache->page_name,
    231 				   &varname[matchindex],
    232 				   cache->page_len) != 0)
    233 				/* Mismatch, page name wrong */
    234 				return -1;
    235 		}
    236 
    237 		/* Page matches, discard page name */
    238 		matchindex = pagesplit + 1;
    239 	}
    240 
    241 	numusage = strtousage(&varname[matchindex], strind - matchindex);
    242 
    243 	if (numusage >= 0) {
    244 		/* Valid numeric */
    245 
    246 		if (numusage != HID_USAGE(usage_id))
    247 			/* Numeric didn't match usage ID */
    248 			return -1;
    249 	} else {
    250 		/* Not a valid numeric */
    251 
    252 		/* Load and cache the usage name */
    253 		if (cache->usage_name == NULL) {
    254 			cache->usage_name = hid_usage_in_page(usage_id);
    255 			cache->usage_len = strlen(cache->usage_name);
    256 		}
    257 
    258 		/*
    259 		 * Compare specified usage name to actual usage name
    260 		 */
    261 		if (cache->usage_len != (size_t)(strind - matchindex) ||
    262 		    memcmp(cache->usage_name, &varname[matchindex],
    263 			   cache->usage_len) != 0)
    264 			/* Mismatch, usage name wrong */
    265 			return -1;
    266 	}
    267 
    268 	if (cache->isfinal)
    269 		/* Match */
    270 		return 1;
    271 
    272 	/*
    273 	 * Partial match: Move index past this usage string +
    274 	 * delimiter
    275 	 */
    276 	var->matchindex = strind + 1;
    277 
    278 	return 0;
    279 }
    280 
    281 /*
    282  * hidmatch() determines whether the item specified in 'item', and
    283  * nested within a heirarchy of collections specified in 'collist'
    284  * matches any of the rules in the list 'varlist'.  Returns the
    285  * matching rule on success, or NULL on no match.
    286  */
    287 static struct Susbvar*
    288 hidmatch(u_int32_t const *collist, size_t collen, struct hid_item *item,
    289 	 struct Susbvar *varlist, size_t vlsize)
    290 {
    291 	size_t colind, vlactive, vlind;
    292 	int iscollection;
    293 
    294 	/*
    295 	 * Keep track of how many variables are still "active".  When
    296 	 * the active count reaches zero, don't bother to continue
    297 	 * looking for matches.
    298 	 */
    299 	vlactive = vlsize;
    300 
    301 	iscollection = item->kind == hid_collection ||
    302 		item->kind == hid_endcollection;
    303 
    304 	for (vlind = 0; vlind < vlsize; vlind++) {
    305 		struct Susbvar *var;
    306 
    307 		var = &varlist[vlind];
    308 
    309 		var->matchindex = 0;
    310 
    311 		if (!(var->mflags & MATCH_COLLECTIONS) && iscollection) {
    312 			/* Don't match collections for this variable */
    313 			var->matchindex = -1;
    314 			vlactive--;
    315 		} else if (!iscollection && !(var->mflags & MATCH_CONSTANTS) &&
    316 			   (item->flags & HIO_CONST)) {
    317 			/*
    318 			 * Don't match constants for this variable,
    319 			 * but ignore the constant bit on collections.
    320 			 */
    321 			var->matchindex = -1;
    322 			vlactive--;
    323 		} else if ((var->mflags & MATCH_WRITABLE) &&
    324 			   ((item->kind != hid_output &&
    325 			     item->kind != hid_feature) ||
    326 			    (item->flags & HIO_CONST))) {
    327 			/*
    328 			 * If we are only matching writable items, if
    329 			 * this is not an output or feature kind, or
    330 			 * it is a constant, reject it.
    331 			 */
    332 			var->matchindex = -1;
    333 			vlactive--;
    334 		} else if (var->mflags & MATCH_ALL) {
    335 			/* Match immediately */
    336 			return &varlist[vlind];
    337 		}
    338 	}
    339 
    340 	/*
    341 	 * Loop through each usage in the collection list, including
    342 	 * the 'item' itself on the final iteration.  For each usage,
    343 	 * test which variables named in the rule list are still
    344 	 * applicable - if any.
    345 	 */
    346 	for (colind = 0; vlactive > 0 && colind <= collen; colind++) {
    347 		struct usagedata cache;
    348 
    349 		cache.isfinal = (colind == collen);
    350 		if (cache.isfinal)
    351 			cache.usage_id = item->usage;
    352 		else
    353 			cache.usage_id = collist[colind];
    354 
    355 		cache.usage_name = NULL;
    356 		cache.page_name = NULL;
    357 
    358 		/*
    359 		 * Loop through each rule, testing whether the rule is
    360 		 * still applicable or not.  For each rule,
    361 		 * 'matchindex' retains the current match state as an
    362 		 * index into the variable name string, or -1 if this
    363 		 * rule has been proven not to match.
    364 		 */
    365 		for (vlind = 0; vlind < vlsize; vlind++) {
    366 			struct Susbvar *var;
    367 			int matchres;
    368 
    369 			var = &varlist[vlind];
    370 
    371 			if (var->matchindex < 0)
    372 				/* Mismatch at a previous level */
    373 				continue;
    374 
    375 			matchres = hidtestrule(var, &cache);
    376 
    377 			if (matchres < 0) {
    378 				/* Bad match */
    379 				var->matchindex = -1;
    380 				vlactive--;
    381 				continue;
    382 			} else if (matchres > 0) {
    383 				/* Complete match */
    384 				return var;
    385 			}
    386 		}
    387 	}
    388 
    389 	return NULL;
    390 }
    391 
    392 static void
    393 allocreport(struct Sreport *report, report_desc_t rd, int repindex)
    394 {
    395 	int reptsize;
    396 
    397 	reptsize = hid_report_size(rd, reptoparam[repindex].hid_kind, reportid);
    398 	if (reptsize < 0)
    399 		errx(1, "Negative report size");
    400 	report->size = reptsize;
    401 
    402 	if (report->size > 0) {
    403 		/*
    404 		 * Allocate a buffer with enough space for the
    405 		 * report in the variable-sized data field.
    406 		 */
    407 		report->buffer = malloc(sizeof(*report->buffer) -
    408 					sizeof(report->buffer->data) +
    409 					report->size);
    410 		if (report->buffer == NULL)
    411 			err(1, NULL);
    412 	} else
    413 		report->buffer = NULL;
    414 
    415 	report->status = srs_clean;
    416 }
    417 
    418 static void
    419 freereport(struct Sreport *report)
    420 {
    421 	if (report->buffer != NULL)
    422 		free(report->buffer);
    423 	report->status = srs_uninit;
    424 }
    425 
    426 static void
    427 getreport(struct Sreport *report, int hidfd, report_desc_t rd, int repindex)
    428 {
    429 	if (report->status == srs_uninit) {
    430 		allocreport(report, rd, repindex);
    431 		if (report->size == 0)
    432 			return;
    433 
    434 		report->buffer->report = reptoparam[repindex].uhid_report;
    435 		if (ioctl(hidfd, USB_GET_REPORT, report->buffer) < 0)
    436 			err(1, "USB_GET_REPORT (probably not supported by "
    437 			    "device)");
    438 	}
    439 }
    440 
    441 static void
    442 setreport(struct Sreport *report, int hidfd, int repindex)
    443 {
    444 	if (report->status == srs_dirty) {
    445 		report->buffer->report = reptoparam[repindex].uhid_report;
    446 
    447 		if (ioctl(hidfd, USB_SET_REPORT, report->buffer) < 0)
    448 			err(1, "USB_SET_REPORT(%s)",
    449 			    reptoparam[repindex].name);
    450 
    451 		report->status = srs_clean;
    452 	}
    453 }
    454 
    455 /* ARGSUSED1 */
    456 static int
    457 varop_value(struct hid_item *item, struct Susbvar *var,
    458 	    u_int32_t const *collist, size_t collen, u_char *buf)
    459 {
    460 	printf("%d\n", hid_get_data(buf, item));
    461 	return 0;
    462 }
    463 
    464 /* ARGSUSED1 */
    465 static int
    466 varop_display(struct hid_item *item, struct Susbvar *var,
    467 	      u_int32_t const *collist, size_t collen, u_char *buf)
    468 {
    469 	size_t colitem;
    470 
    471 	for (colitem = 0; colitem < collen; colitem++) {
    472 		if (var->mflags & MATCH_SHOWPAGENAME)
    473 			printf("%s:",
    474 			       hid_usage_page(HID_PAGE(collist[colitem])));
    475 		printf("%s.", hid_usage_in_page(collist[colitem]));
    476 	}
    477 
    478 	if (var->mflags & MATCH_SHOWPAGENAME)
    479 		printf("%s:", hid_usage_page(HID_PAGE(item->usage)));
    480 	printf("%s=%d%s\n", hid_usage_in_page(item->usage),
    481 	       hid_get_data(buf, item),
    482 	       (item->flags & HIO_CONST) ? " (const)" : "");
    483 	return 0;
    484 }
    485 
    486 /* ARGSUSED1 */
    487 static int
    488 varop_modify(struct hid_item *item, struct Susbvar *var,
    489 	     u_int32_t const *collist, size_t collen, u_char *buf)
    490 {
    491 	u_int dataval;
    492 
    493 	dataval = (u_int)strtol(var->value, NULL, 10);
    494 
    495 	hid_set_data(buf, item, dataval);
    496 
    497 	if (var->mflags & MATCH_SHOWVALUES)
    498 		/* Display set value */
    499 		varop_display(item, var, collist, collen, buf);
    500 
    501 	return 1;
    502 }
    503 
    504 static void
    505 reportitem(char const *label, struct hid_item const *item, unsigned int mflags)
    506 {
    507 	int isconst = item->flags & HIO_CONST,
    508 	    isvar = item->flags & HIO_VARIABLE;
    509 	printf("%s size=%d count=%d%s%s page=%s", label,
    510 	       item->report_size, item->report_count,
    511 	       isconst ? " Const" : "",
    512 	       !isvar && !isconst ? " Array" : "",
    513 	       hid_usage_page(HID_PAGE(item->usage)));
    514 	if (item->usage_minimum != 0 || item->usage_maximum != 0) {
    515 		printf(" usage=%s..%s", hid_usage_in_page(item->usage_minimum),
    516 		       hid_usage_in_page(item->usage_maximum));
    517 		if (mflags & MATCH_SHOWNUMERIC)
    518 			printf(" (%u:0x%x..%u:0x%x)",
    519 			       HID_PAGE(item->usage_minimum),
    520 			       HID_USAGE(item->usage_minimum),
    521 			       HID_PAGE(item->usage_maximum),
    522 			       HID_USAGE(item->usage_maximum));
    523 	} else {
    524 		printf(" usage=%s", hid_usage_in_page(item->usage));
    525 		if (mflags & MATCH_SHOWNUMERIC)
    526 			printf(" (%u:0x%x)",
    527 			       HID_PAGE(item->usage), HID_USAGE(item->usage));
    528 	}
    529 	printf(", logical range %d..%d",
    530 	       item->logical_minimum, item->logical_maximum);
    531 	if (item->physical_minimum != item->physical_maximum)
    532 		printf(", physical range %d..%d",
    533 		       item->physical_minimum, item->physical_maximum);
    534 	if (item->unit)
    535 		printf(", unit=0x%02x exp=%d", item->unit,
    536 		       item->unit_exponent);
    537 	printf("\n");
    538 }
    539 
    540 /* ARGSUSED1 */
    541 static int
    542 varop_report(struct hid_item *item, struct Susbvar *var,
    543 	     u_int32_t const *collist, size_t collen, u_char *buf)
    544 {
    545 	switch (item->kind) {
    546 	case hid_collection:
    547 		printf("Collection page=%s usage=%s",
    548 		       hid_usage_page(HID_PAGE(item->usage)),
    549 		       hid_usage_in_page(item->usage));
    550 		if (var->mflags & MATCH_SHOWNUMERIC)
    551 			printf(" (%u:0x%x)\n",
    552 			       HID_PAGE(item->usage), HID_USAGE(item->usage));
    553 		else
    554 			printf("\n");
    555 		break;
    556 	case hid_endcollection:
    557 		printf("End collection\n");
    558 		break;
    559 	case hid_input:
    560 		reportitem("Input  ", item, var->mflags);
    561 		break;
    562 	case hid_output:
    563 		reportitem("Output ", item, var->mflags);
    564 		break;
    565 	case hid_feature:
    566 		reportitem("Feature", item, var->mflags);
    567 		break;
    568 	}
    569 
    570 	return 0;
    571 }
    572 
    573 static void
    574 devloop(int hidfd, report_desc_t rd, struct Susbvar *varlist, size_t vlsize)
    575 {
    576 	u_char *dbuf;
    577 	struct hid_data *hdata;
    578 	size_t collind, dlen;
    579 	struct hid_item hitem;
    580 	u_int32_t colls[128];
    581 	struct Sreport inreport;
    582 
    583 	allocreport(&inreport, rd, REPORT_INPUT);
    584 
    585 	if (inreport.size <= 0)
    586 		errx(1, "Input report descriptor invalid length");
    587 
    588 	dlen = inreport.size;
    589 	dbuf = inreport.buffer->data;
    590 
    591 	for (;;) {
    592 		ssize_t readlen;
    593 
    594 		readlen = read(hidfd, dbuf, dlen);
    595 		if (readlen < 0)
    596 			err(1, "Device read error");
    597 		if (dlen != (size_t)readlen)
    598 			errx(1, "Unexpected response length: %lu != %lu",
    599 			     (unsigned long)readlen, (unsigned long)dlen);
    600 
    601 		collind = 0;
    602 		hdata = hid_start_parse(rd, 1 << hid_input, reportid);
    603 		if (hdata == NULL)
    604 			errx(1, "Failed to start parser");
    605 
    606 		while (hid_get_item(hdata, &hitem)) {
    607 			struct Susbvar *matchvar;
    608 
    609 			switch (hitem.kind) {
    610 			case hid_collection:
    611 				if (collind >= (sizeof(colls) / sizeof(*colls)))
    612 					errx(1, "Excessive nested collections");
    613 				colls[collind++] = hitem.usage;
    614 				break;
    615 			case hid_endcollection:
    616 				if (collind == 0)
    617 					errx(1, "Excessive collection ends");
    618 				collind--;
    619 				break;
    620 			case hid_input:
    621 				break;
    622 			case hid_output:
    623 			case hid_feature:
    624 				errx(1, "Unexpected non-input item returned");
    625 			}
    626 
    627 			if (reportid != -1 && hitem.report_ID != reportid)
    628 				continue;
    629 
    630 			matchvar = hidmatch(colls, collind, &hitem,
    631 					    varlist, vlsize);
    632 
    633 			if (matchvar != NULL)
    634 				matchvar->opfunc(&hitem, matchvar,
    635 						 colls, collind,
    636 						 inreport.buffer->data);
    637 		}
    638 		hid_end_parse(hdata);
    639 		printf("\n");
    640 	}
    641 	/* NOTREACHED */
    642 }
    643 
    644 static void
    645 devshow(int hidfd, report_desc_t rd, struct Susbvar *varlist, size_t vlsize,
    646 	int kindset)
    647 {
    648 	struct hid_data *hdata;
    649 	size_t collind, repind, vlind;
    650 	struct hid_item hitem;
    651 	u_int32_t colls[128];
    652 	struct Sreport reports[REPORT_MAXVAL + 1];
    653 
    654 
    655 	for (repind = 0; repind < (sizeof(reports) / sizeof(*reports));
    656 	     repind++) {
    657 		reports[repind].status = srs_uninit;
    658 		reports[repind].buffer = NULL;
    659 	}
    660 
    661 	collind = 0;
    662 	hdata = hid_start_parse(rd, kindset, reportid);
    663 	if (hdata == NULL)
    664 		errx(1, "Failed to start parser");
    665 
    666 	while (hid_get_item(hdata, &hitem)) {
    667 		struct Susbvar *matchvar;
    668 		int repindex;
    669 
    670 		if (verbose > 3)
    671 			printf("item: kind=%d repid=%d usage=0x%x\n",
    672 			       hitem.kind, hitem.report_ID, hitem.usage);
    673 		repindex = -1;
    674 		switch (hitem.kind) {
    675 		case hid_collection:
    676 			if (collind >= (sizeof(colls) / sizeof(*colls)))
    677 				errx(1, "Excessive nested collections");
    678 			colls[collind++] = hitem.usage;
    679 			break;
    680 		case hid_endcollection:
    681 			if (collind == 0)
    682 				errx(1, "Excessive collection ends");
    683 			collind--;
    684 			break;
    685 		case hid_input:
    686 			repindex = REPORT_INPUT;
    687 			break;
    688 		case hid_output:
    689 			repindex = REPORT_OUTPUT;
    690 			break;
    691 		case hid_feature:
    692 			repindex = REPORT_FEATURE;
    693 			break;
    694 		}
    695 
    696 		if (reportid != -1 && hitem.report_ID != reportid)
    697 			continue;
    698 
    699 		matchvar = hidmatch(colls, collind, &hitem, varlist, vlsize);
    700 
    701 		if (matchvar != NULL) {
    702 			u_char *bufdata;
    703 			struct Sreport *repptr;
    704 
    705 			matchvar->mflags |= MATCH_WASMATCHED;
    706 
    707 			if (repindex >= 0)
    708 				repptr = &reports[repindex];
    709 			else
    710 				repptr = NULL;
    711 
    712 			if (repptr != NULL &&
    713 			    !(matchvar->mflags & MATCH_NODATA))
    714 				getreport(repptr, hidfd, rd, repindex);
    715 
    716 			bufdata = (repptr == NULL || repptr->buffer == NULL) ?
    717 				NULL : repptr->buffer->data;
    718 
    719 			if (matchvar->opfunc(&hitem, matchvar, colls, collind,
    720 					     bufdata))
    721 				repptr->status = srs_dirty;
    722 		}
    723 	}
    724 	hid_end_parse(hdata);
    725 
    726 	for (repind = 0; repind < (sizeof(reports) / sizeof(*reports));
    727 	     repind++) {
    728 		setreport(&reports[repind], hidfd, repind);
    729 		freereport(&reports[repind]);
    730 	}
    731 
    732 	/* Warn about any items that we couldn't find a match for */
    733 	for (vlind = 0; vlind < vlsize; vlind++) {
    734 		struct Susbvar *var;
    735 
    736 		var = &varlist[vlind];
    737 
    738 		if (var->variable != NULL &&
    739 		    !(var->mflags & MATCH_WASMATCHED))
    740 			warnx("Failed to match: %.*s", (int)var->varlen,
    741 			      var->variable);
    742 	}
    743 }
    744 
    745 static void
    746 usage(void)
    747 {
    748 	const char *progname = getprogname();
    749 
    750 	fprintf(stderr, "Usage: %s -f device [-t tablefile] [-l] [-v] -a\n",
    751 	    progname);
    752 	fprintf(stderr, "       %s -f device [-t tablefile] [-v] -r\n",
    753 	    progname);
    754 	fprintf(stderr,
    755 	    "       %s -f device [-t tablefile] [-l] [-n] [-v] name ...\n",
    756 	    progname);
    757 	fprintf(stderr,
    758 	    "       %s -f device [-t tablefile] -w name=value ...\n",
    759 	    progname);
    760 	exit(1);
    761 }
    762 
    763 int
    764 main(int argc, char **argv)
    765 {
    766 	char const *dev;
    767 	char const *table;
    768 	size_t varnum;
    769 	int aflag, lflag, nflag, rflag, wflag;
    770 	int ch, hidfd;
    771 	report_desc_t repdesc;
    772 	char devnamebuf[PATH_MAX];
    773 	struct Susbvar variables[128];
    774 
    775 	wflag = aflag = nflag = verbose = rflag = lflag = 0;
    776 	dev = NULL;
    777 	table = NULL;
    778 	while ((ch = getopt(argc, argv, "?af:lnrt:vw")) != -1) {
    779 		switch (ch) {
    780 		case 'a':
    781 			aflag = 1;
    782 			break;
    783 		case 'f':
    784 			dev = optarg;
    785 			break;
    786 		case 'l':
    787 			lflag = 1;
    788 			break;
    789 		case 'n':
    790 			nflag = 1;
    791 			break;
    792 		case 'r':
    793 			rflag = 1;
    794 			break;
    795 		case 't':
    796 			table = optarg;
    797 			break;
    798 		case 'v':
    799 			verbose++;
    800 			break;
    801 		case 'w':
    802 			wflag = 1;
    803 			break;
    804 		case '?':
    805 		default:
    806 			usage();
    807 			/* NOTREACHED */
    808 		}
    809 	}
    810 	argc -= optind;
    811 	argv += optind;
    812 	if (dev == NULL || (lflag && (wflag || rflag))) {
    813 		/*
    814 		 * No device specified, or attempting to loop and set
    815 		 * or dump report at the same time
    816 		 */
    817 		usage();
    818 		/* NOTREACHED */
    819 	}
    820 
    821 	for (varnum = 0; varnum < (size_t)argc; varnum++) {
    822 		char const *name, *valuesep;
    823 		struct Susbvar *svar;
    824 
    825 		svar = &variables[varnum];
    826 		name = argv[varnum];
    827 		valuesep = strchr(name, DELIM_SET);
    828 
    829 		svar->variable = name;
    830 		svar->mflags = 0;
    831 
    832 		if (valuesep == NULL) {
    833 			/* Read variable */
    834 			if (wflag)
    835 				errx(1, "Must not specify -w to read variables");
    836 			svar->value = NULL;
    837 			svar->varlen = strlen(name);
    838 
    839 			if (nflag) {
    840 				/* Display value of variable only */
    841 				svar->opfunc = varop_value;
    842 			} else {
    843 				/* Display name and value of variable */
    844 				svar->opfunc = varop_display;
    845 
    846 				if (verbose >= 1)
    847 					/* Show page names in verbose modes */
    848 					svar->mflags |= MATCH_SHOWPAGENAME;
    849 			}
    850 		} else {
    851 			/* Write variable */
    852 			if (!wflag)
    853 				errx(2, "Must specify -w to set variables");
    854 			svar->mflags |= MATCH_WRITABLE;
    855 			if (verbose >= 1)
    856 				/*
    857 				 * Allow displaying of set value in
    858 				 * verbose mode.  This isn't
    859 				 * particularly useful though, so
    860 				 * don't bother documenting it.
    861 				 */
    862 				svar->mflags |= MATCH_SHOWVALUES;
    863 			svar->varlen = valuesep - name;
    864 			svar->value = valuesep + 1;
    865 			svar->opfunc = varop_modify;
    866 		}
    867 	}
    868 
    869 	if (aflag || rflag) {
    870 		struct Susbvar *svar;
    871 
    872 		svar = &variables[varnum++];
    873 
    874 		svar->variable = NULL;
    875 		svar->mflags = MATCH_ALL;
    876 
    877 		if (rflag) {
    878 			/*
    879 			 * Dump report descriptor.  Do dump collection
    880 			 * items also, and hint that it won't be
    881 			 * necessary to get the item status.
    882 			 */
    883 			svar->opfunc = varop_report;
    884 			svar->mflags |= MATCH_COLLECTIONS | MATCH_NODATA;
    885 
    886 			switch (verbose) {
    887 			default:
    888 				/* Level 2: Show item numerics and constants */
    889 				svar->mflags |= MATCH_SHOWNUMERIC;
    890 				/* FALLTHROUGH */
    891 			case 1:
    892 				/* Level 1: Just show constants */
    893 				svar->mflags |= MATCH_CONSTANTS;
    894 				/* FALLTHROUGH */
    895 			case 0:
    896 				break;
    897 			}
    898 		} else {
    899 			/* Display name and value of variable */
    900 			svar->opfunc = varop_display;
    901 
    902 			switch (verbose) {
    903 			default:
    904 				/* Level 2: Show constants and page names */
    905 				svar->mflags |= MATCH_CONSTANTS;
    906 				/* FALLTHROUGH */
    907 			case 1:
    908 				/* Level 1: Just show page names */
    909 				svar->mflags |= MATCH_SHOWPAGENAME;
    910 				/* FALLTHROUGH */
    911 			case 0:
    912 				break;
    913 			}
    914 		}
    915 	}
    916 
    917 	if (varnum == 0) {
    918 		/* Nothing to do...  Display usage information. */
    919 		usage();
    920 		/* NOTREACHED */
    921 	}
    922 
    923 	hid_init(table);
    924 
    925 	if (dev[0] != '/') {
    926 		snprintf(devnamebuf, sizeof(devnamebuf), "/dev/%s%s",
    927 			 isdigit(dev[0]) ? "uhid" : "", dev);
    928 		dev = devnamebuf;
    929 	}
    930 
    931 	hidfd = open(dev, O_RDWR);
    932 	if (hidfd < 0)
    933 		err(1, "%s", dev);
    934 
    935 	if (ioctl(hidfd, USB_GET_REPORT_ID, &reportid) < 0)
    936 		reportid = -1;
    937 	if (verbose > 1)
    938 		printf("report ID=%d\n", reportid);
    939 	repdesc = hid_get_report_desc(hidfd);
    940 	if (repdesc == 0)
    941 		errx(1, "USB_GET_REPORT_DESC");
    942 
    943 	if (lflag) {
    944 		devloop(hidfd, repdesc, variables, varnum);
    945 		/* NOTREACHED */
    946 	}
    947 
    948 	if (rflag)
    949 		/* Report mode header */
    950 		printf("Report descriptor:\n");
    951 
    952 	devshow(hidfd, repdesc, variables, varnum,
    953 		1 << hid_input |
    954 		1 << hid_output |
    955 		1 << hid_feature);
    956 
    957 	if (rflag) {
    958 		/* Report mode trailer */
    959 		size_t repindex;
    960 		for (repindex = 0;
    961 		     repindex < (sizeof(reptoparam) / sizeof(*reptoparam));
    962 		     repindex++) {
    963 			int size;
    964 			size = hid_report_size(repdesc,
    965 					       reptoparam[repindex].hid_kind,
    966 					       reportid);
    967 			printf("Total %7s size %d bytes\n",
    968 			       reptoparam[repindex].name, size);
    969 		}
    970 	}
    971 
    972 	hid_dispose_report_desc(repdesc);
    973 	exit(0);
    974 	/* NOTREACHED */
    975 }
    976