Home | History | Annotate | Line # | Download | only in usbhidctl
usbhid.c revision 1.15
      1 /*      $NetBSD: usbhid.c,v 1.15 2000/09/24 02:27:12 augustss Exp $ */
      2 
      3 /*
      4  * Copyright (c) 2000 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 <usb.h>
     54 
     55 /* Parser tokens */
     56 #define DELIM_USAGE '.'
     57 #define DELIM_PAGE ':'
     58 #define DELIM_SET '='
     59 
     60 /* Zero if not in a verbose mode.  Greater levels of verbosity are
     61    indicated by values larger than one. */
     62 static unsigned int verbose;
     63 
     64 struct Susbvar {
     65 	/* Variable name, not NUL terminated */
     66 	char const *variable;
     67 	size_t varlen;
     68 
     69 	char const *value; /* Value to set variable to */
     70 
     71 #define MATCH_ALL		(1 << 0)
     72 #define MATCH_COLLECTIONS	(1 << 1)
     73 #define MATCH_NODATA		(1 << 2)
     74 #define MATCH_CONSTANTS		(1 << 3)
     75 #define MATCH_WASMATCHED	(1 << 4)
     76 #define MATCH_SHOWPAGENAME	(1 << 5)
     77 #define MATCH_SHOWNUMERIC	(1 << 6)
     78 #define MATCH_WRITABLE		(1 << 7)
     79 	unsigned int mflags;
     80 
     81 	/* Workspace for hidmatch() */
     82 	ssize_t matchindex;
     83 
     84 	int (*opfunc)(struct hid_item *item, struct Susbvar *var,
     85 		      u_int32_t const *collist, size_t collen, u_char *buf);
     86 };
     87 
     88 struct Sreport {
     89 	struct usb_ctl_report *buffer;
     90 
     91 	enum {srs_uninit, srs_clean, srs_dirty} status;
     92 	int report_id;
     93 	size_t size;
     94 };
     95 
     96 static struct {
     97 	int uhid_report;
     98 	hid_kind_t hid_kind;
     99 	char const *name;
    100 } const reptoparam[] = {
    101 #define REPORT_INPUT 0
    102 	{ UHID_INPUT_REPORT, hid_input, "input" },
    103 #define REPORT_OUTPUT 1
    104 	{ UHID_OUTPUT_REPORT, hid_output, "output" },
    105 #define REPORT_FEATURE 2
    106 	{ UHID_FEATURE_REPORT, hid_feature, "feature" }
    107 #define REPORT_MAXVAL 2
    108 };
    109 
    110 static struct Susbvar*
    111 hidmatch(u_int32_t const *collist, size_t collen, struct hid_item *item,
    112 	 struct Susbvar *varlist, size_t vlsize)
    113 {
    114 	size_t vlind, colind, vlactive;
    115 	int iscollection;
    116 
    117 	/*
    118 	 * Keep track of how many variables are still "active".  When
    119 	 * the active count reaches zero, don't bother to continue
    120 	 * looking for matches.
    121 	 */
    122 	vlactive = vlsize;
    123 
    124 	iscollection = item->kind == hid_collection ||
    125 		item->kind == hid_endcollection;
    126 
    127 	for (vlind = 0; vlind < vlsize; vlind++) {
    128 		struct Susbvar *var;
    129 
    130 		var = &varlist[vlind];
    131 
    132 		var->matchindex = 0;
    133 
    134 		if (!(var->mflags & MATCH_COLLECTIONS) && iscollection) {
    135 			/* Don't match collections for this variable */
    136 			var->matchindex = -1;
    137 			vlactive--;
    138 		} else if (!iscollection && !(var->mflags & MATCH_CONSTANTS) &&
    139 			   (item->flags & HIO_CONST)) {
    140 			/*
    141 			 * Don't match constants for this variable,
    142 			 * but ignore the constant bit on collections.
    143 			 */
    144 			var->matchindex = -1;
    145 			vlactive--;
    146 		} else if ((var->mflags & MATCH_WRITABLE) &&
    147 			   ((item->kind != hid_output &&
    148 			     item->kind != hid_feature) ||
    149 			    (item->flags & HIO_CONST))) {
    150 			/*
    151 			 * If we are only matching writable items, if
    152 			 * this is not an output or feature kind, or
    153 			 * it is a constant, reject it.
    154 			 */
    155 			var->matchindex = -1;
    156 			vlactive--;
    157 		} else if (var->mflags & MATCH_ALL) {
    158 			/* Match immediately */
    159 			return &varlist[vlind];
    160 		}
    161 	}
    162 
    163 	for (colind = 0; vlactive > 0 && colind <= collen; colind++) {
    164 		char const *usage_name, *page_name;
    165 		size_t usage_len, page_len;
    166 		int final;
    167 		u_int32_t usage_id;
    168 
    169 		final = (colind == collen);
    170 
    171 		if (final)
    172 			usage_id = item->usage;
    173 		else
    174 			usage_id = collist[colind];
    175 
    176 		usage_name = hid_usage_in_page(usage_id);
    177 		usage_len = strlen(usage_name);
    178 
    179 		page_name = NULL;
    180 
    181 		for (vlind = 0; vlind < vlsize; vlind++) {
    182 			ssize_t matchindex, pagesplit;
    183 			size_t varlen, strind;
    184 			char const *varname;
    185 			struct Susbvar *var;
    186 
    187 			var = &varlist[vlind];
    188 
    189 			matchindex = var->matchindex;
    190 			varname = var->variable;
    191 			varlen = var->varlen;
    192 
    193 			if (matchindex < 0)
    194 				/* Mismatch at a previous level */
    195 				continue;
    196 
    197 			pagesplit = -1;
    198 			for (strind = matchindex; strind < varlen; strind++) {
    199 				if (varname[strind] == DELIM_USAGE)
    200 					break;
    201 				if (varname[strind] == DELIM_PAGE)
    202 					pagesplit = strind;
    203 			}
    204 
    205 			if (final && strind != varlen) {
    206 				/*
    207 				 * Variable name is too long (hit
    208 				 * delimiter instead of
    209 				 * end-of-variable)
    210 				 */
    211 				var->matchindex = -1;
    212 				vlactive--;
    213 				continue;
    214 			}
    215 
    216 			if (pagesplit >= 0) {
    217 				if (page_name == NULL) {
    218 					page_name = hid_usage_page(HID_PAGE(usage_id));
    219 					page_len = strlen(page_name);
    220 				}
    221 				if (page_len !=
    222 				    (size_t)(pagesplit - matchindex) ||
    223 				    memcmp(page_name, &varname[matchindex],
    224 					   page_len) != 0) {
    225 					/* Mismatch, page name wrong */
    226 					var->matchindex = -1;
    227 					vlactive--;
    228 					continue;
    229 				}
    230 
    231 				/* Page matches, discard page name */
    232 				matchindex = pagesplit + 1;
    233 			}
    234 
    235 			if (usage_len != strind - matchindex ||
    236 			    memcmp(usage_name, &varname[matchindex],
    237 				   usage_len) != 0) {
    238 				/* Mismatch, usage name wrong */
    239 				var->matchindex = -1;
    240 				vlactive--;
    241 				continue;
    242 			}
    243 
    244 			if (final)
    245 				/* Match */
    246 				return var;
    247 
    248 			/*
    249 			 * Partial match: Move index past this usage
    250 			 * string + delimiter
    251 			 */
    252 			var->matchindex = matchindex + usage_len + 1;
    253 		}
    254 	}
    255 
    256 	return NULL;
    257 }
    258 
    259 static void
    260 allocreport(struct Sreport *report, report_desc_t rd, int repindex)
    261 {
    262 	int reptsize;
    263 
    264 	reptsize = hid_report_size(rd, reptoparam[repindex].hid_kind,
    265 				   &report->report_id);
    266 	if (reptsize < 0)
    267 		errx(1, "Negative report size");
    268 	report->size = reptsize;
    269 
    270 	if (report->size > 0) {
    271 		/*
    272 		 * Allocate a buffer with enough space for the
    273 		 * report in the variable-sized data field.
    274 		 */
    275 		report->buffer = malloc(sizeof(*report->buffer) -
    276 					sizeof(report->buffer->data) +
    277 					report->size);
    278 		if (report->buffer == NULL)
    279 			err(1, NULL);
    280 	} else
    281 		report->buffer = NULL;
    282 
    283 	report->status = srs_clean;
    284 }
    285 
    286 static void
    287 freereport(struct Sreport *report)
    288 {
    289 	if (report->buffer != NULL)
    290 		free(report->buffer);
    291 	report->status = srs_uninit;
    292 }
    293 
    294 static void
    295 getreport(struct Sreport *report, int hidfd, report_desc_t rd, int repindex)
    296 {
    297 	if (report->status == srs_uninit) {
    298 		allocreport(report, rd, repindex);
    299 		if (report->size == 0)
    300 			return;
    301 
    302 		report->buffer->report = reptoparam[repindex].uhid_report;
    303 		if (ioctl(hidfd, USB_GET_REPORT, report->buffer) < 0)
    304 			err(1, "USB_GET_REPORT");
    305 	}
    306 }
    307 
    308 static void
    309 setreport(struct Sreport *report, int hidfd, int repindex)
    310 {
    311 	if (report->status == srs_dirty) {
    312 		report->buffer->report = reptoparam[repindex].uhid_report;
    313 
    314 		if (ioctl(hidfd, USB_SET_REPORT, report->buffer) < 0)
    315 			err(1, "USB_SET_REPORT(%s)",
    316 			    reptoparam[repindex].name);
    317 
    318 		report->status = srs_clean;
    319 	}
    320 }
    321 
    322 /* ARGSUSED1 */
    323 static int
    324 varop_value(struct hid_item *item, struct Susbvar *var,
    325 	    u_int32_t const *collist, size_t collen, u_char *buf)
    326 {
    327 	printf("%d\n", hid_get_data(buf, item));
    328 	return 0;
    329 }
    330 
    331 /* ARGSUSED1 */
    332 static int
    333 varop_display(struct hid_item *item, struct Susbvar *var,
    334 	      u_int32_t const *collist, size_t collen, u_char *buf)
    335 {
    336 	size_t colitem;
    337 
    338 	for (colitem = 0; colitem < collen; colitem++) {
    339 		if (var->mflags & MATCH_SHOWPAGENAME)
    340 			printf("%s:",
    341 			       hid_usage_page(HID_PAGE(collist[colitem])));
    342 		printf("%s.", hid_usage_in_page(collist[colitem]));
    343 	}
    344 
    345 	if (var->mflags & MATCH_SHOWPAGENAME)
    346 		printf("%s:", hid_usage_page(HID_PAGE(item->usage)));
    347 	printf("%s=%d%s\n", hid_usage_in_page(item->usage),
    348 	       hid_get_data(buf, item),
    349 	       (item->flags & HIO_CONST) ? " (const)" : "");
    350 	return 0;
    351 }
    352 
    353 /* ARGSUSED1 */
    354 static int
    355 varop_modify(struct hid_item *item, struct Susbvar *var,
    356 	     u_int32_t const *collist, size_t collen, u_char *buf)
    357 {
    358 	u_int dataval;
    359 
    360 	dataval = (u_int)strtol(var->value, NULL, 10);
    361 
    362 	hid_set_data(buf, item, dataval);
    363 
    364 	if (verbose >= 1)
    365 		/*
    366 		 * Allow displaying of set value in verbose mode.
    367 		 * This isn't particularly useful though, so don't
    368 		 * bother documenting it.
    369 		 */
    370 		varop_display(item, var, collist, collen, buf);
    371 
    372 	return 1;
    373 }
    374 
    375 static void
    376 reportitem(char const *label, struct hid_item const *item, unsigned int mflags)
    377 {
    378 	printf("%s size=%d count=%d page=%s usage=%s%s", label,
    379 	       item->report_size, item->report_count,
    380 	       hid_usage_page(HID_PAGE(item->usage)),
    381 	       hid_usage_in_page(item->usage),
    382 	       item->flags & HIO_CONST ? " Const" : "");
    383 	if (mflags & MATCH_SHOWNUMERIC)
    384 		printf(" (%u/0x%x)",
    385 		       HID_PAGE(item->usage), HID_USAGE(item->usage));
    386 	printf(", logical range %d..%d",
    387 	       item->logical_minimum, item->logical_maximum);
    388 	if (item->physical_minimum != item->physical_maximum)
    389 		printf(", physical range %d..%d",
    390 		       item->physical_minimum, item->physical_maximum);
    391 	if (item->unit)
    392 		printf(", unit=0x%02x exp=%d", item->unit,
    393 		       item->unit_exponent);
    394 	printf("\n");
    395 }
    396 
    397 /* ARGSUSED1 */
    398 static int
    399 varop_report(struct hid_item *item, struct Susbvar *var,
    400 	     u_int32_t const *collist, size_t collen, u_char *buf)
    401 {
    402 	switch (item->kind) {
    403 	case hid_collection:
    404 		printf("Collection page=%s usage=%s\n",
    405 		       hid_usage_page(HID_PAGE(item->usage)),
    406 		       hid_usage_in_page(item->usage));
    407 		break;
    408 	case hid_endcollection:
    409 		printf("End collection\n");
    410 		break;
    411 	case hid_input:
    412 		reportitem("Input  ", item, var->mflags);
    413 		break;
    414 	case hid_output:
    415 		reportitem("Output ", item, var->mflags);
    416 		break;
    417 	case hid_feature:
    418 		reportitem("Feature", item, var->mflags);
    419 		break;
    420 	}
    421 
    422 	return 0;
    423 }
    424 
    425 static void
    426 devloop(int hidfd, report_desc_t rd, struct Susbvar *varlist, size_t vlsize)
    427 {
    428 	struct hid_data *hdata;
    429 	struct hid_item hitem;
    430 	u_int32_t colls[128];
    431 	struct Sreport inreport;
    432 	size_t dlen;
    433 	u_char *dbuf;
    434 	size_t collind;
    435 
    436 	allocreport(&inreport, rd, REPORT_INPUT);
    437 
    438 	if (inreport.size <= 0)
    439 		errx(1, "Input report descriptor invalid length");
    440 
    441 	dlen = inreport.size;
    442 	dbuf = inreport.buffer->data;
    443 
    444 	for (;;) {
    445 		ssize_t readlen;
    446 
    447 		readlen = read(hidfd, dbuf, dlen);
    448 		if (readlen < 0 || dlen != (size_t)readlen)
    449 			err(1, "bad read %ld != %ld",
    450 			    (long)readlen, (long)dlen);
    451 
    452 		collind = 0;
    453 		hdata = hid_start_parse(rd, 1 << hid_input);
    454 		if (hdata == NULL)
    455 			errx(1, "Failed to start parser");
    456 
    457 		while (hid_get_item(hdata, &hitem)) {
    458 			struct Susbvar *matchvar;
    459 
    460 			switch (hitem.kind) {
    461 			case hid_collection:
    462 				if (collind >= (sizeof(colls) / sizeof(*colls)))
    463 					errx(1, "Excessive nested collections");
    464 				colls[collind++] = hitem.usage;
    465 				break;
    466 			case hid_endcollection:
    467 				if (collind == 0)
    468 					errx(1, "Excessive collection ends");
    469 				collind--;
    470 				break;
    471 			case hid_input:
    472 				break;
    473 			case hid_output:
    474 			case hid_feature:
    475 				errx(1, "Unexpected non-input item returned");
    476 			}
    477 
    478 			matchvar = hidmatch(colls, collind, &hitem,
    479 					    varlist, vlsize);
    480 
    481 			if (matchvar != NULL)
    482 				matchvar->opfunc(&hitem, matchvar,
    483 						 colls, collind,
    484 						 inreport.buffer->data);
    485 		}
    486 		hid_end_parse(hdata);
    487 		printf("\n");
    488 	}
    489 	/* NOTREACHED */
    490 }
    491 
    492 static void
    493 devshow(int hidfd, report_desc_t rd, struct Susbvar *varlist, size_t vlsize,
    494 	int kindset)
    495 {
    496 	struct hid_data *hdata;
    497 	struct hid_item hitem;
    498 	u_int32_t colls[128];
    499 	size_t collind, repind, vlind;
    500 
    501 	struct Sreport reports[REPORT_MAXVAL + 1];
    502 
    503 
    504 	for (repind = 0; repind < (sizeof(reports) / sizeof(*reports));
    505 	     repind++) {
    506 		reports[repind].status = srs_uninit;
    507 		reports[repind].buffer = NULL;
    508 	}
    509 
    510 	collind = 0;
    511 	hdata = hid_start_parse(rd, kindset |
    512 				(1 << hid_collection) |
    513 				(1 << hid_endcollection));
    514 	if (hdata == NULL)
    515 		errx(1, "Failed to start parser");
    516 
    517 	while (hid_get_item(hdata, &hitem)) {
    518 		struct Susbvar *matchvar;
    519 		int repindex;
    520 
    521 		repindex = -1;
    522 		switch (hitem.kind) {
    523 		case hid_collection:
    524 			if (collind >= (sizeof(colls) / sizeof(*colls)))
    525 				errx(1, "Excessive nested collections");
    526 			colls[collind++] = hitem.usage;
    527 			break;
    528 		case hid_endcollection:
    529 			if (collind == 0)
    530 				errx(1, "Excessive collection ends");
    531 			collind--;
    532 			break;
    533 		case hid_input:
    534 			repindex = REPORT_INPUT;
    535 			break;
    536 		case hid_output:
    537 			repindex = REPORT_OUTPUT;
    538 			break;
    539 		case hid_feature:
    540 			repindex = REPORT_FEATURE;
    541 			break;
    542 		}
    543 
    544 		matchvar = hidmatch(colls, collind, &hitem, varlist, vlsize);
    545 
    546 		if (matchvar != NULL) {
    547 			u_char *bufdata;
    548 			struct Sreport *repptr;
    549 
    550 			matchvar->mflags |= MATCH_WASMATCHED;
    551 
    552 			if (repindex >= 0)
    553 				repptr = &reports[repindex];
    554 			else
    555 				repptr = NULL;
    556 
    557 			if (repptr != NULL &&
    558 			    !(matchvar->mflags & MATCH_NODATA))
    559 				getreport(repptr, hidfd, rd, repindex);
    560 
    561 			bufdata = (repptr == NULL || repptr->buffer == NULL) ?
    562 				NULL : repptr->buffer->data;
    563 
    564 			if (matchvar->opfunc(&hitem, matchvar, colls, collind,
    565 					     bufdata))
    566 				repptr->status = srs_dirty;
    567 		}
    568 	}
    569 	hid_end_parse(hdata);
    570 
    571 	for (repind = 0; repind < (sizeof(reports) / sizeof(*reports));
    572 	     repind++) {
    573 		setreport(&reports[repind], hidfd, repind);
    574 		freereport(&reports[repind]);
    575 	}
    576 
    577 	/* Warn about any items that we couldn't find a match for */
    578 	for (vlind = 0; vlind < vlsize; vlind++) {
    579 		struct Susbvar *var;
    580 
    581 		var = &varlist[vlind];
    582 
    583 		if (var->variable != NULL &&
    584 		    !(var->mflags & MATCH_WASMATCHED))
    585 			warnx("Failed to match: %.*s", (int)var->varlen,
    586 			      var->variable);
    587 	}
    588 }
    589 
    590 static void
    591 usage(void)
    592 {
    593 	extern char *__progname;
    594 
    595 	fprintf(stderr, "Usage: %s -f device [-t tablefile] [-l] [-v] -a\n", __progname);
    596 	fprintf(stderr, "       %s -f device [-t tablefile] [-v] -r\n", __progname);
    597 	fprintf(stderr, "       %s -f device [-t tablefile] [-l] [-n] [-v] name ...\n", __progname);
    598 	fprintf(stderr, "       %s -f device [-t tablefile] -w name=value ...\n", __progname);
    599 	exit(1);
    600 }
    601 
    602 int
    603 main(int argc, char **argv)
    604 {
    605 	int hidfd;
    606 	report_desc_t repdesc;
    607 	char devnamebuf[PATH_MAX];
    608 	char const *dev;
    609 	int ch, wflag, aflag, nflag, rflag, lflag;
    610 	size_t varnum;
    611 	char const *table;
    612 	struct Susbvar variables[128];
    613 
    614 	wflag = aflag = nflag = verbose = rflag = lflag = 0;
    615 	dev = NULL;
    616 	table = NULL;
    617 	while ((ch = getopt(argc, argv, "?af:lnrt:vw")) != -1) {
    618 		switch (ch) {
    619 		case 'a':
    620 			aflag = 1;
    621 			break;
    622 		case 'f':
    623 			dev = optarg;
    624 			break;
    625 		case 'l':
    626 			lflag = 1;
    627 			break;
    628 		case 'n':
    629 			nflag = 1;
    630 			break;
    631 		case 'r':
    632 			rflag = 1;
    633 			break;
    634 		case 't':
    635 			table = optarg;
    636 			break;
    637 		case 'v':
    638 			verbose++;
    639 			break;
    640 		case 'w':
    641 			wflag = 1;
    642 			break;
    643 		case '?':
    644 		default:
    645 			usage();
    646 			/* NOTREACHED */
    647 		}
    648 	}
    649 	argc -= optind;
    650 	argv += optind;
    651 	if (dev == NULL || (lflag && (wflag || rflag))) {
    652 		/*
    653 		 * No device specified, or attempting to loop and set
    654 		 * or dump report at the same time
    655 		 */
    656 		usage();
    657 		/* NOTREACHED */
    658 	}
    659 
    660 	for (varnum = 0; varnum < (size_t)argc; varnum++) {
    661 		char const *name, *valuesep;
    662 		struct Susbvar *svar;
    663 
    664 		svar = &variables[varnum];
    665 		name = argv[varnum];
    666 		valuesep = strchr(name, DELIM_SET);
    667 
    668 		svar->variable = name;
    669 		svar->mflags = 0;
    670 
    671 		if (valuesep == NULL) {
    672 			/* Read variable */
    673 			if (wflag)
    674 				errx(1, "Must not specify -w to read variables");
    675 			svar->value = NULL;
    676 			svar->varlen = strlen(name);
    677 
    678 			if (nflag) {
    679 				/* Display value of variable only */
    680 				svar->opfunc = varop_value;
    681 			} else {
    682 				/* Display name and value of variable */
    683 				svar->opfunc = varop_display;
    684 
    685 				if (verbose >= 1)
    686 					/* Show page names in verbose modes */
    687 					svar->mflags |= MATCH_SHOWPAGENAME;
    688 			}
    689 		} else {
    690 			/* Write variable */
    691 			if (!wflag)
    692 				errx(2, "Must specify -w to set variables");
    693 			svar->mflags |= MATCH_WRITABLE;
    694 			svar->varlen = valuesep - name;
    695 			svar->value = valuesep + 1;
    696 			svar->opfunc = varop_modify;
    697 		}
    698 	}
    699 
    700 	if (aflag || rflag) {
    701 		struct Susbvar *svar;
    702 
    703 		svar = &variables[varnum++];
    704 
    705 		svar->variable = NULL;
    706 		svar->mflags = MATCH_ALL;
    707 
    708 		if (rflag) {
    709 			/*
    710 			 * Dump report descriptor.  Do dump collection
    711 			 * items also, and hint that it won't be
    712 			 * necessary to get the item status.
    713 			 */
    714 			svar->opfunc = varop_report;
    715 			svar->mflags |= MATCH_COLLECTIONS | MATCH_NODATA;
    716 
    717 			switch (verbose) {
    718 			default:
    719 				/* Level 2: Show item numerics and constants */
    720 				svar->mflags |= MATCH_SHOWNUMERIC;
    721 				/* FALLTHROUGH */
    722 			case 1:
    723 				/* Level 1: Just show constants */
    724 				svar->mflags |= MATCH_CONSTANTS;
    725 				/* FALLTHROUGH */
    726 			case 0:
    727 				break;
    728 			}
    729 		} else {
    730 			/* Display name and value of variable */
    731 			svar->opfunc = varop_display;
    732 
    733 			switch (verbose) {
    734 			default:
    735 				/* Level 2: Show constants and page names */
    736 				svar->mflags |= MATCH_CONSTANTS;
    737 				/* FALLTHROUGH */
    738 			case 1:
    739 				/* Level 1: Just show page names */
    740 				svar->mflags |= MATCH_SHOWPAGENAME;
    741 				/* FALLTHROUGH */
    742 			case 0:
    743 				break;
    744 			}
    745 		}
    746 	}
    747 
    748 	if (varnum == 0) {
    749 		/* Nothing to do...  Display usage information. */
    750 		usage();
    751 		/* NOTREACHED */
    752 	}
    753 
    754 	hid_init(table);
    755 
    756 	if (dev[0] != '/') {
    757 		snprintf(devnamebuf, sizeof(devnamebuf), "/dev/%s%s",
    758 			 isdigit(dev[0]) ? "uhid" : "", dev);
    759 		dev = devnamebuf;
    760 	}
    761 
    762 	hidfd = open(dev, O_RDWR);
    763 	if (hidfd < 0)
    764 		err(1, "%s", dev);
    765 
    766 	repdesc = hid_get_report_desc(hidfd);
    767 	if (repdesc == 0)
    768 		errx(1, "USB_GET_REPORT_DESC");
    769 
    770 	if (lflag) {
    771 		devloop(hidfd, repdesc, variables, varnum);
    772 		/* NOTREACHED */
    773 	}
    774 
    775 	if (rflag)
    776 		/* Report mode header */
    777 		printf("Report descriptor:\n");
    778 
    779 	devshow(hidfd, repdesc, variables, varnum,
    780 		1 << hid_input |
    781 		1 << hid_output |
    782 		1 << hid_feature);
    783 
    784 #if 0
    785 	{
    786 		size_t repindex;
    787 		for (repindex = 0;
    788 		     repindex < (sizeof(reptoparam) / sizeof(*reptoparam));
    789 		     repindex++)
    790 			devshow(hidfd, repdesc, variables, varnum,
    791 				1 << reptoparam[repindex].hid_kind);
    792 	}
    793 #endif
    794 
    795 	if (rflag) {
    796 		/* Report mode trailer */
    797 		size_t repindex;
    798 		for (repindex = 0;
    799 		     repindex < (sizeof(reptoparam) / sizeof(*reptoparam));
    800 		     repindex++) {
    801 			int report_id, size;
    802 			size = hid_report_size(repdesc,
    803 					       reptoparam[repindex].hid_kind,
    804 					       &report_id);
    805 			size -= report_id != 0;
    806 			printf("Total %7s size %s%d bytes\n",
    807 			       reptoparam[repindex].name,
    808 			       report_id && size ? "1+" : "", size);
    809 		}
    810 	}
    811 
    812 	hid_dispose_report_desc(repdesc);
    813 	exit(0);
    814 	/* NOTREACHED */
    815 }
    816