Home | History | Annotate | Line # | Download | only in usbhidctl
usbhid.c revision 1.21
      1 /*      $NetBSD: usbhid.c,v 1.21 2001/12/29 21:23:24 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 	int val, i;
    471 
    472 	for (i = 0; i < item->report_count; i++) {
    473 		for (colitem = 0; colitem < collen; colitem++) {
    474 			if (var->mflags & MATCH_SHOWPAGENAME)
    475 				printf("%s:",
    476 				    hid_usage_page(HID_PAGE(collist[colitem])));
    477 			printf("%s.", hid_usage_in_page(collist[colitem]));
    478 		}
    479 		if (var->mflags & MATCH_SHOWPAGENAME)
    480 			printf("%s:", hid_usage_page(HID_PAGE(item->usage)));
    481 		val = hid_get_data(buf, item);
    482 		item->pos += item->report_size;
    483 		if (item->usage_minimum != 0 || item->usage_maximum != 0) {
    484 			val += item->usage_minimum;
    485 			printf("%s=1", hid_usage_in_page(val));
    486 		} else {
    487 			printf("%s=%d%s", hid_usage_in_page(item->usage),
    488 			       val, item->flags & HIO_CONST ? " (const)" : "");
    489 		}
    490 		if (item->report_count > 1)
    491 			printf(" [%d]", i);
    492 		printf("\n");
    493 	}
    494 	return 0;
    495 }
    496 
    497 /* ARGSUSED1 */
    498 static int
    499 varop_modify(struct hid_item *item, struct Susbvar *var,
    500 	     u_int32_t const *collist, size_t collen, u_char *buf)
    501 {
    502 	u_int dataval;
    503 
    504 	dataval = (u_int)strtol(var->value, NULL, 10);
    505 
    506 	hid_set_data(buf, item, dataval);
    507 
    508 	if (var->mflags & MATCH_SHOWVALUES)
    509 		/* Display set value */
    510 		varop_display(item, var, collist, collen, buf);
    511 
    512 	return 1;
    513 }
    514 
    515 static void
    516 reportitem(char const *label, struct hid_item const *item, unsigned int mflags)
    517 {
    518 	int isconst = item->flags & HIO_CONST,
    519 	    isvar = item->flags & HIO_VARIABLE;
    520 	printf("%s size=%d count=%d%s%s page=%s", label,
    521 	       item->report_size, item->report_count,
    522 	       isconst ? " Const" : "",
    523 	       !isvar && !isconst ? " Array" : "",
    524 	       hid_usage_page(HID_PAGE(item->usage)));
    525 	if (item->usage_minimum != 0 || item->usage_maximum != 0) {
    526 		printf(" usage=%s..%s", hid_usage_in_page(item->usage_minimum),
    527 		       hid_usage_in_page(item->usage_maximum));
    528 		if (mflags & MATCH_SHOWNUMERIC)
    529 			printf(" (%u:0x%x..%u:0x%x)",
    530 			       HID_PAGE(item->usage_minimum),
    531 			       HID_USAGE(item->usage_minimum),
    532 			       HID_PAGE(item->usage_maximum),
    533 			       HID_USAGE(item->usage_maximum));
    534 	} else {
    535 		printf(" usage=%s", hid_usage_in_page(item->usage));
    536 		if (mflags & MATCH_SHOWNUMERIC)
    537 			printf(" (%u:0x%x)",
    538 			       HID_PAGE(item->usage), HID_USAGE(item->usage));
    539 	}
    540 	printf(", logical range %d..%d",
    541 	       item->logical_minimum, item->logical_maximum);
    542 	if (item->physical_minimum != item->physical_maximum)
    543 		printf(", physical range %d..%d",
    544 		       item->physical_minimum, item->physical_maximum);
    545 	if (item->unit)
    546 		printf(", unit=0x%02x exp=%d", item->unit,
    547 		       item->unit_exponent);
    548 	printf("\n");
    549 }
    550 
    551 /* ARGSUSED1 */
    552 static int
    553 varop_report(struct hid_item *item, struct Susbvar *var,
    554 	     u_int32_t const *collist, size_t collen, u_char *buf)
    555 {
    556 	switch (item->kind) {
    557 	case hid_collection:
    558 		printf("Collection page=%s usage=%s",
    559 		       hid_usage_page(HID_PAGE(item->usage)),
    560 		       hid_usage_in_page(item->usage));
    561 		if (var->mflags & MATCH_SHOWNUMERIC)
    562 			printf(" (%u:0x%x)\n",
    563 			       HID_PAGE(item->usage), HID_USAGE(item->usage));
    564 		else
    565 			printf("\n");
    566 		break;
    567 	case hid_endcollection:
    568 		printf("End collection\n");
    569 		break;
    570 	case hid_input:
    571 		reportitem("Input  ", item, var->mflags);
    572 		break;
    573 	case hid_output:
    574 		reportitem("Output ", item, var->mflags);
    575 		break;
    576 	case hid_feature:
    577 		reportitem("Feature", item, var->mflags);
    578 		break;
    579 	}
    580 
    581 	return 0;
    582 }
    583 
    584 static void
    585 devloop(int hidfd, report_desc_t rd, struct Susbvar *varlist, size_t vlsize)
    586 {
    587 	u_char *dbuf;
    588 	struct hid_data *hdata;
    589 	size_t collind, dlen;
    590 	struct hid_item hitem;
    591 	u_int32_t colls[128];
    592 	struct Sreport inreport;
    593 
    594 	allocreport(&inreport, rd, REPORT_INPUT);
    595 
    596 	if (inreport.size <= 0)
    597 		errx(1, "Input report descriptor invalid length");
    598 
    599 	dlen = inreport.size;
    600 	dbuf = inreport.buffer->data;
    601 
    602 	for (;;) {
    603 		ssize_t readlen;
    604 
    605 		readlen = read(hidfd, dbuf, dlen);
    606 		if (readlen < 0)
    607 			err(1, "Device read error");
    608 		if (dlen != (size_t)readlen)
    609 			errx(1, "Unexpected response length: %lu != %lu",
    610 			     (unsigned long)readlen, (unsigned long)dlen);
    611 
    612 		collind = 0;
    613 		hdata = hid_start_parse(rd, 1 << hid_input, reportid);
    614 		if (hdata == NULL)
    615 			errx(1, "Failed to start parser");
    616 
    617 		while (hid_get_item(hdata, &hitem)) {
    618 			struct Susbvar *matchvar;
    619 
    620 			switch (hitem.kind) {
    621 			case hid_collection:
    622 				if (collind >= (sizeof(colls) / sizeof(*colls)))
    623 					errx(1, "Excessive nested collections");
    624 				colls[collind++] = hitem.usage;
    625 				break;
    626 			case hid_endcollection:
    627 				if (collind == 0)
    628 					errx(1, "Excessive collection ends");
    629 				collind--;
    630 				break;
    631 			case hid_input:
    632 				break;
    633 			case hid_output:
    634 			case hid_feature:
    635 				errx(1, "Unexpected non-input item returned");
    636 			}
    637 
    638 			if (reportid != -1 && hitem.report_ID != reportid)
    639 				continue;
    640 
    641 			matchvar = hidmatch(colls, collind, &hitem,
    642 					    varlist, vlsize);
    643 
    644 			if (matchvar != NULL)
    645 				matchvar->opfunc(&hitem, matchvar,
    646 						 colls, collind,
    647 						 inreport.buffer->data);
    648 		}
    649 		hid_end_parse(hdata);
    650 		printf("\n");
    651 	}
    652 	/* NOTREACHED */
    653 }
    654 
    655 static void
    656 devshow(int hidfd, report_desc_t rd, struct Susbvar *varlist, size_t vlsize,
    657 	int kindset)
    658 {
    659 	struct hid_data *hdata;
    660 	size_t collind, repind, vlind;
    661 	struct hid_item hitem;
    662 	u_int32_t colls[128];
    663 	struct Sreport reports[REPORT_MAXVAL + 1];
    664 
    665 
    666 	for (repind = 0; repind < (sizeof(reports) / sizeof(*reports));
    667 	     repind++) {
    668 		reports[repind].status = srs_uninit;
    669 		reports[repind].buffer = NULL;
    670 	}
    671 
    672 	collind = 0;
    673 	hdata = hid_start_parse(rd, kindset, reportid);
    674 	if (hdata == NULL)
    675 		errx(1, "Failed to start parser");
    676 
    677 	while (hid_get_item(hdata, &hitem)) {
    678 		struct Susbvar *matchvar;
    679 		int repindex;
    680 
    681 		if (verbose > 3)
    682 			printf("item: kind=%d repid=%d usage=0x%x\n",
    683 			       hitem.kind, hitem.report_ID, hitem.usage);
    684 		repindex = -1;
    685 		switch (hitem.kind) {
    686 		case hid_collection:
    687 			if (collind >= (sizeof(colls) / sizeof(*colls)))
    688 				errx(1, "Excessive nested collections");
    689 			colls[collind++] = hitem.usage;
    690 			break;
    691 		case hid_endcollection:
    692 			if (collind == 0)
    693 				errx(1, "Excessive collection ends");
    694 			collind--;
    695 			break;
    696 		case hid_input:
    697 			repindex = REPORT_INPUT;
    698 			break;
    699 		case hid_output:
    700 			repindex = REPORT_OUTPUT;
    701 			break;
    702 		case hid_feature:
    703 			repindex = REPORT_FEATURE;
    704 			break;
    705 		}
    706 
    707 		if (reportid != -1 && hitem.report_ID != reportid)
    708 			continue;
    709 
    710 		matchvar = hidmatch(colls, collind, &hitem, varlist, vlsize);
    711 
    712 		if (matchvar != NULL) {
    713 			u_char *bufdata;
    714 			struct Sreport *repptr;
    715 
    716 			matchvar->mflags |= MATCH_WASMATCHED;
    717 
    718 			if (repindex >= 0)
    719 				repptr = &reports[repindex];
    720 			else
    721 				repptr = NULL;
    722 
    723 			if (repptr != NULL &&
    724 			    !(matchvar->mflags & MATCH_NODATA))
    725 				getreport(repptr, hidfd, rd, repindex);
    726 
    727 			bufdata = (repptr == NULL || repptr->buffer == NULL) ?
    728 				NULL : repptr->buffer->data;
    729 
    730 			if (matchvar->opfunc(&hitem, matchvar, colls, collind,
    731 					     bufdata))
    732 				repptr->status = srs_dirty;
    733 		}
    734 	}
    735 	hid_end_parse(hdata);
    736 
    737 	for (repind = 0; repind < (sizeof(reports) / sizeof(*reports));
    738 	     repind++) {
    739 		setreport(&reports[repind], hidfd, repind);
    740 		freereport(&reports[repind]);
    741 	}
    742 
    743 	/* Warn about any items that we couldn't find a match for */
    744 	for (vlind = 0; vlind < vlsize; vlind++) {
    745 		struct Susbvar *var;
    746 
    747 		var = &varlist[vlind];
    748 
    749 		if (var->variable != NULL &&
    750 		    !(var->mflags & MATCH_WASMATCHED))
    751 			warnx("Failed to match: %.*s", (int)var->varlen,
    752 			      var->variable);
    753 	}
    754 }
    755 
    756 static void
    757 usage(void)
    758 {
    759 	const char *progname = getprogname();
    760 
    761 	fprintf(stderr, "Usage: %s -f device [-t tablefile] [-l] [-v] -a\n",
    762 	    progname);
    763 	fprintf(stderr, "       %s -f device [-t tablefile] [-v] -r\n",
    764 	    progname);
    765 	fprintf(stderr,
    766 	    "       %s -f device [-t tablefile] [-l] [-n] [-v] name ...\n",
    767 	    progname);
    768 	fprintf(stderr,
    769 	    "       %s -f device [-t tablefile] -w name=value ...\n",
    770 	    progname);
    771 	exit(1);
    772 }
    773 
    774 int
    775 main(int argc, char **argv)
    776 {
    777 	char const *dev;
    778 	char const *table;
    779 	size_t varnum;
    780 	int aflag, lflag, nflag, rflag, wflag;
    781 	int ch, hidfd;
    782 	report_desc_t repdesc;
    783 	char devnamebuf[PATH_MAX];
    784 	struct Susbvar variables[128];
    785 
    786 	wflag = aflag = nflag = verbose = rflag = lflag = 0;
    787 	dev = NULL;
    788 	table = NULL;
    789 	while ((ch = getopt(argc, argv, "?af:lnrt:vw")) != -1) {
    790 		switch (ch) {
    791 		case 'a':
    792 			aflag = 1;
    793 			break;
    794 		case 'f':
    795 			dev = optarg;
    796 			break;
    797 		case 'l':
    798 			lflag = 1;
    799 			break;
    800 		case 'n':
    801 			nflag = 1;
    802 			break;
    803 		case 'r':
    804 			rflag = 1;
    805 			break;
    806 		case 't':
    807 			table = optarg;
    808 			break;
    809 		case 'v':
    810 			verbose++;
    811 			break;
    812 		case 'w':
    813 			wflag = 1;
    814 			break;
    815 		case '?':
    816 		default:
    817 			usage();
    818 			/* NOTREACHED */
    819 		}
    820 	}
    821 	argc -= optind;
    822 	argv += optind;
    823 	if (dev == NULL || (lflag && (wflag || rflag))) {
    824 		/*
    825 		 * No device specified, or attempting to loop and set
    826 		 * or dump report at the same time
    827 		 */
    828 		usage();
    829 		/* NOTREACHED */
    830 	}
    831 
    832 	for (varnum = 0; varnum < (size_t)argc; varnum++) {
    833 		char const *name, *valuesep;
    834 		struct Susbvar *svar;
    835 
    836 		svar = &variables[varnum];
    837 		name = argv[varnum];
    838 		valuesep = strchr(name, DELIM_SET);
    839 
    840 		svar->variable = name;
    841 		svar->mflags = 0;
    842 
    843 		if (valuesep == NULL) {
    844 			/* Read variable */
    845 			if (wflag)
    846 				errx(1, "Must not specify -w to read variables");
    847 			svar->value = NULL;
    848 			svar->varlen = strlen(name);
    849 
    850 			if (nflag) {
    851 				/* Display value of variable only */
    852 				svar->opfunc = varop_value;
    853 			} else {
    854 				/* Display name and value of variable */
    855 				svar->opfunc = varop_display;
    856 
    857 				if (verbose >= 1)
    858 					/* Show page names in verbose modes */
    859 					svar->mflags |= MATCH_SHOWPAGENAME;
    860 			}
    861 		} else {
    862 			/* Write variable */
    863 			if (!wflag)
    864 				errx(2, "Must specify -w to set variables");
    865 			svar->mflags |= MATCH_WRITABLE;
    866 			if (verbose >= 1)
    867 				/*
    868 				 * Allow displaying of set value in
    869 				 * verbose mode.  This isn't
    870 				 * particularly useful though, so
    871 				 * don't bother documenting it.
    872 				 */
    873 				svar->mflags |= MATCH_SHOWVALUES;
    874 			svar->varlen = valuesep - name;
    875 			svar->value = valuesep + 1;
    876 			svar->opfunc = varop_modify;
    877 		}
    878 	}
    879 
    880 	if (aflag || rflag) {
    881 		struct Susbvar *svar;
    882 
    883 		svar = &variables[varnum++];
    884 
    885 		svar->variable = NULL;
    886 		svar->mflags = MATCH_ALL;
    887 
    888 		if (rflag) {
    889 			/*
    890 			 * Dump report descriptor.  Do dump collection
    891 			 * items also, and hint that it won't be
    892 			 * necessary to get the item status.
    893 			 */
    894 			svar->opfunc = varop_report;
    895 			svar->mflags |= MATCH_COLLECTIONS | MATCH_NODATA;
    896 
    897 			switch (verbose) {
    898 			default:
    899 				/* Level 2: Show item numerics and constants */
    900 				svar->mflags |= MATCH_SHOWNUMERIC;
    901 				/* FALLTHROUGH */
    902 			case 1:
    903 				/* Level 1: Just show constants */
    904 				svar->mflags |= MATCH_CONSTANTS;
    905 				/* FALLTHROUGH */
    906 			case 0:
    907 				break;
    908 			}
    909 		} else {
    910 			/* Display name and value of variable */
    911 			svar->opfunc = varop_display;
    912 
    913 			switch (verbose) {
    914 			default:
    915 				/* Level 2: Show constants and page names */
    916 				svar->mflags |= MATCH_CONSTANTS;
    917 				/* FALLTHROUGH */
    918 			case 1:
    919 				/* Level 1: Just show page names */
    920 				svar->mflags |= MATCH_SHOWPAGENAME;
    921 				/* FALLTHROUGH */
    922 			case 0:
    923 				break;
    924 			}
    925 		}
    926 	}
    927 
    928 	if (varnum == 0) {
    929 		/* Nothing to do...  Display usage information. */
    930 		usage();
    931 		/* NOTREACHED */
    932 	}
    933 
    934 	hid_init(table);
    935 
    936 	if (dev[0] != '/') {
    937 		snprintf(devnamebuf, sizeof(devnamebuf), "/dev/%s%s",
    938 			 isdigit(dev[0]) ? "uhid" : "", dev);
    939 		dev = devnamebuf;
    940 	}
    941 
    942 	hidfd = open(dev, O_RDWR);
    943 	if (hidfd < 0)
    944 		err(1, "%s", dev);
    945 
    946 	if (ioctl(hidfd, USB_GET_REPORT_ID, &reportid) < 0)
    947 		reportid = -1;
    948 	if (verbose > 1)
    949 		printf("report ID=%d\n", reportid);
    950 	repdesc = hid_get_report_desc(hidfd);
    951 	if (repdesc == 0)
    952 		errx(1, "USB_GET_REPORT_DESC");
    953 
    954 	if (lflag) {
    955 		devloop(hidfd, repdesc, variables, varnum);
    956 		/* NOTREACHED */
    957 	}
    958 
    959 	if (rflag)
    960 		/* Report mode header */
    961 		printf("Report descriptor:\n");
    962 
    963 	devshow(hidfd, repdesc, variables, varnum,
    964 		1 << hid_input |
    965 		1 << hid_output |
    966 		1 << hid_feature);
    967 
    968 	if (rflag) {
    969 		/* Report mode trailer */
    970 		size_t repindex;
    971 		for (repindex = 0;
    972 		     repindex < (sizeof(reptoparam) / sizeof(*reptoparam));
    973 		     repindex++) {
    974 			int size;
    975 			size = hid_report_size(repdesc,
    976 					       reptoparam[repindex].hid_kind,
    977 					       reportid);
    978 			printf("Total %7s size %d bytes\n",
    979 			       reptoparam[repindex].name, size);
    980 		}
    981 	}
    982 
    983 	hid_dispose_report_desc(repdesc);
    984 	exit(0);
    985 	/* NOTREACHED */
    986 }
    987