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