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