GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/usbhidctl/usbhid.c Lines: 0 376 0.0 %
Date: 2017-11-07 Branches: 0 275 0.0 %

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