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 */
57static unsigned int verbose;
58
59/* Parser tokens */
60#define DELIM_USAGE '.'
61#define DELIM_PAGE ':'
62#define DELIM_SET '='
63#define DELIM_INSTANCE '#'
64
65static int reportid;
66
67struct 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
100struct 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
109static 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 */
127static int
128strtousage(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
153struct 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 */
166static int
167hidtestrule(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 */
292static void
293resethidvars(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 */
306static struct Susbvar*
307hidmatch(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
426static void
427allocreport(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
452static void
453freereport(struct Sreport *report)
454{
455	if (report->buffer != NULL)
456		free(report->buffer);
457	report->status = srs_uninit;
458}
459
460static void
461getreport(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
481static void
482setreport(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 */
496static int
497varop_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 */
505static int
506varop_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 */
538static int
539varop_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
555static void
556reportitem(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 */
592static int
593varop_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__dead static void
625devloop(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
697static void
698devshow(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__dead static void
802usage(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
819int
820main(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