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