usbhid.c revision 1.26 1 /* $NetBSD: usbhid.c,v 1.26 2004/10/31 07:36:19 dsainty 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.26 2004/10/31 07:36:19 dsainty 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 hierarchy 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(%s) [probably not supported by "
442 "device]",
443 reptoparam[repindex].name);
444 }
445 }
446
447 static void
448 setreport(struct Sreport *report, int hidfd, int repindex)
449 {
450 if (report->status == srs_dirty) {
451 report->buffer->ucr_report = reptoparam[repindex].uhid_report;
452
453 if (ioctl(hidfd, USB_SET_REPORT, report->buffer) < 0)
454 err(1, "USB_SET_REPORT(%s)",
455 reptoparam[repindex].name);
456
457 report->status = srs_clean;
458 }
459 }
460
461 /* ARGSUSED1 */
462 static int
463 varop_value(struct hid_item *item, struct Susbvar *var,
464 u_int32_t const *collist, size_t collen, u_char *buf)
465 {
466 printf("%d\n", hid_get_data(buf, item));
467 return 0;
468 }
469
470 /* ARGSUSED1 */
471 static int
472 varop_display(struct hid_item *item, struct Susbvar *var,
473 u_int32_t const *collist, size_t collen, u_char *buf)
474 {
475 size_t colitem;
476 int val, i;
477
478 for (i = 0; i < item->report_count; i++) {
479 for (colitem = 0; colitem < collen; colitem++) {
480 if (var->mflags & MATCH_SHOWPAGENAME)
481 printf("%s:",
482 hid_usage_page(HID_PAGE(collist[colitem])));
483 printf("%s.", hid_usage_in_page(collist[colitem]));
484 }
485 if (var->mflags & MATCH_SHOWPAGENAME)
486 printf("%s:", hid_usage_page(HID_PAGE(item->usage)));
487 val = hid_get_data(buf, item);
488 item->pos += item->report_size;
489 if (item->usage_minimum != 0 || item->usage_maximum != 0) {
490 val += item->usage_minimum;
491 printf("%s=1", hid_usage_in_page(val));
492 } else {
493 printf("%s=%d%s", hid_usage_in_page(item->usage),
494 val, item->flags & HIO_CONST ? " (const)" : "");
495 }
496 if (item->report_count > 1)
497 printf(" [%d]", i);
498 printf("\n");
499 }
500 return 0;
501 }
502
503 /* ARGSUSED1 */
504 static int
505 varop_modify(struct hid_item *item, struct Susbvar *var,
506 u_int32_t const *collist, size_t collen, u_char *buf)
507 {
508 u_int dataval;
509
510 dataval = (u_int)strtol(var->value, NULL, 10);
511
512 hid_set_data(buf, item, dataval);
513
514 if (var->mflags & MATCH_SHOWVALUES)
515 /* Display set value */
516 varop_display(item, var, collist, collen, buf);
517
518 return 1;
519 }
520
521 static void
522 reportitem(char const *label, struct hid_item const *item, unsigned int mflags)
523 {
524 int isconst = item->flags & HIO_CONST,
525 isvar = item->flags & HIO_VARIABLE;
526 printf("%s size=%d count=%d%s%s page=%s", label,
527 item->report_size, item->report_count,
528 isconst ? " Const" : "",
529 !isvar && !isconst ? " Array" : "",
530 hid_usage_page(HID_PAGE(item->usage)));
531 if (item->usage_minimum != 0 || item->usage_maximum != 0) {
532 printf(" usage=%s..%s", hid_usage_in_page(item->usage_minimum),
533 hid_usage_in_page(item->usage_maximum));
534 if (mflags & MATCH_SHOWNUMERIC)
535 printf(" (%u:0x%x..%u:0x%x)",
536 HID_PAGE(item->usage_minimum),
537 HID_USAGE(item->usage_minimum),
538 HID_PAGE(item->usage_maximum),
539 HID_USAGE(item->usage_maximum));
540 } else {
541 printf(" usage=%s", hid_usage_in_page(item->usage));
542 if (mflags & MATCH_SHOWNUMERIC)
543 printf(" (%u:0x%x)",
544 HID_PAGE(item->usage), HID_USAGE(item->usage));
545 }
546 printf(", logical range %d..%d",
547 item->logical_minimum, item->logical_maximum);
548 if (item->physical_minimum != item->physical_maximum)
549 printf(", physical range %d..%d",
550 item->physical_minimum, item->physical_maximum);
551 if (item->unit)
552 printf(", unit=0x%02x exp=%d", item->unit,
553 item->unit_exponent);
554 printf("\n");
555 }
556
557 /* ARGSUSED1 */
558 static int
559 varop_report(struct hid_item *item, struct Susbvar *var,
560 u_int32_t const *collist, size_t collen, u_char *buf)
561 {
562 switch (item->kind) {
563 case hid_collection:
564 printf("Collection page=%s usage=%s",
565 hid_usage_page(HID_PAGE(item->usage)),
566 hid_usage_in_page(item->usage));
567 if (var->mflags & MATCH_SHOWNUMERIC)
568 printf(" (%u:0x%x)\n",
569 HID_PAGE(item->usage), HID_USAGE(item->usage));
570 else
571 printf("\n");
572 break;
573 case hid_endcollection:
574 printf("End collection\n");
575 break;
576 case hid_input:
577 reportitem("Input ", item, var->mflags);
578 break;
579 case hid_output:
580 reportitem("Output ", item, var->mflags);
581 break;
582 case hid_feature:
583 reportitem("Feature", item, var->mflags);
584 break;
585 }
586
587 return 0;
588 }
589
590 static void
591 devloop(int hidfd, report_desc_t rd, struct Susbvar *varlist, size_t vlsize)
592 {
593 u_char *dbuf;
594 struct hid_data *hdata;
595 size_t collind, dlen;
596 struct hid_item hitem;
597 u_int32_t colls[128];
598 struct Sreport inreport;
599
600 allocreport(&inreport, rd, REPORT_INPUT);
601
602 if (inreport.size <= 0)
603 errx(1, "Input report descriptor invalid length");
604
605 dlen = inreport.size;
606 dbuf = inreport.buffer->ucr_data;
607
608 for (;;) {
609 ssize_t readlen;
610
611 readlen = read(hidfd, dbuf, dlen);
612 if (readlen < 0)
613 err(1, "Device read error");
614 if (dlen != (size_t)readlen)
615 errx(1, "Unexpected response length: %lu != %lu",
616 (unsigned long)readlen, (unsigned long)dlen);
617
618 collind = 0;
619 hdata = hid_start_parse(rd, 1 << hid_input, reportid);
620 if (hdata == NULL)
621 errx(1, "Failed to start parser");
622
623 while (hid_get_item(hdata, &hitem)) {
624 struct Susbvar *matchvar;
625
626 switch (hitem.kind) {
627 case hid_collection:
628 if (collind >= (sizeof(colls) / sizeof(*colls)))
629 errx(1, "Excessive nested collections");
630 colls[collind++] = hitem.usage;
631 break;
632 case hid_endcollection:
633 if (collind == 0)
634 errx(1, "Excessive collection ends");
635 collind--;
636 break;
637 case hid_input:
638 break;
639 case hid_output:
640 case hid_feature:
641 errx(1, "Unexpected non-input item returned");
642 }
643
644 if (reportid != -1 && hitem.report_ID != reportid)
645 continue;
646
647 matchvar = hidmatch(colls, collind, &hitem,
648 varlist, vlsize);
649
650 if (matchvar != NULL)
651 matchvar->opfunc(&hitem, matchvar,
652 colls, collind,
653 inreport.buffer->ucr_data);
654 }
655 hid_end_parse(hdata);
656 printf("\n");
657 }
658 /* NOTREACHED */
659 }
660
661 static void
662 devshow(int hidfd, report_desc_t rd, struct Susbvar *varlist, size_t vlsize,
663 int kindset)
664 {
665 struct hid_data *hdata;
666 size_t collind, repind, vlind;
667 struct hid_item hitem;
668 u_int32_t colls[128];
669 struct Sreport reports[REPORT_MAXVAL + 1];
670
671
672 for (repind = 0; repind < (sizeof(reports) / sizeof(*reports));
673 repind++) {
674 reports[repind].status = srs_uninit;
675 reports[repind].buffer = NULL;
676 }
677
678 collind = 0;
679 hdata = hid_start_parse(rd, kindset, reportid);
680 if (hdata == NULL)
681 errx(1, "Failed to start parser");
682
683 while (hid_get_item(hdata, &hitem)) {
684 struct Susbvar *matchvar;
685 int repindex;
686
687 if (verbose > 3)
688 printf("item: kind=%d repid=%d usage=0x%x\n",
689 hitem.kind, hitem.report_ID, hitem.usage);
690 repindex = -1;
691 switch (hitem.kind) {
692 case hid_collection:
693 if (collind >= (sizeof(colls) / sizeof(*colls)))
694 errx(1, "Excessive nested collections");
695 colls[collind++] = hitem.usage;
696 break;
697 case hid_endcollection:
698 if (collind == 0)
699 errx(1, "Excessive collection ends");
700 collind--;
701 break;
702 case hid_input:
703 repindex = REPORT_INPUT;
704 break;
705 case hid_output:
706 repindex = REPORT_OUTPUT;
707 break;
708 case hid_feature:
709 repindex = REPORT_FEATURE;
710 break;
711 }
712
713 if (reportid != -1 && hitem.report_ID != reportid)
714 continue;
715
716 matchvar = hidmatch(colls, collind, &hitem, varlist, vlsize);
717
718 if (matchvar != NULL) {
719 u_char *bufdata;
720 struct Sreport *repptr;
721
722 matchvar->mflags |= MATCH_WASMATCHED;
723
724 if (repindex >= 0)
725 repptr = &reports[repindex];
726 else
727 repptr = NULL;
728
729 if (repptr != NULL &&
730 !(matchvar->mflags & MATCH_NODATA))
731 getreport(repptr, hidfd, rd, repindex);
732
733 bufdata = (repptr == NULL || repptr->buffer == NULL) ?
734 NULL : repptr->buffer->ucr_data;
735
736 if (matchvar->opfunc(&hitem, matchvar, colls, collind,
737 bufdata))
738 repptr->status = srs_dirty;
739 }
740 }
741 hid_end_parse(hdata);
742
743 for (repind = 0; repind < (sizeof(reports) / sizeof(*reports));
744 repind++) {
745 setreport(&reports[repind], hidfd, repind);
746 freereport(&reports[repind]);
747 }
748
749 /* Warn about any items that we couldn't find a match for */
750 for (vlind = 0; vlind < vlsize; vlind++) {
751 struct Susbvar *var;
752
753 var = &varlist[vlind];
754
755 if (var->variable != NULL &&
756 !(var->mflags & MATCH_WASMATCHED))
757 warnx("Failed to match: %.*s", (int)var->varlen,
758 var->variable);
759 }
760 }
761
762 static void
763 usage(void)
764 {
765 const char *progname = getprogname();
766
767 fprintf(stderr, "usage: %s -f device [-t tablefile] [-l] [-v] -a\n",
768 progname);
769 fprintf(stderr, " %s -f device [-t tablefile] [-v] -r\n",
770 progname);
771 fprintf(stderr,
772 " %s -f device [-t tablefile] [-l] [-n] [-v] name ...\n",
773 progname);
774 fprintf(stderr,
775 " %s -f device [-t tablefile] -w name=value ...\n",
776 progname);
777 exit(1);
778 }
779
780 int
781 main(int argc, char **argv)
782 {
783 char const *dev;
784 char const *table;
785 size_t varnum;
786 int aflag, lflag, nflag, rflag, wflag;
787 int ch, hidfd;
788 report_desc_t repdesc;
789 char devnamebuf[PATH_MAX];
790 struct Susbvar variables[128];
791
792 wflag = aflag = nflag = verbose = rflag = lflag = 0;
793 dev = NULL;
794 table = NULL;
795 while ((ch = getopt(argc, argv, "?af:lnrt:vw")) != -1) {
796 switch (ch) {
797 case 'a':
798 aflag = 1;
799 break;
800 case 'f':
801 dev = optarg;
802 break;
803 case 'l':
804 lflag = 1;
805 break;
806 case 'n':
807 nflag = 1;
808 break;
809 case 'r':
810 rflag = 1;
811 break;
812 case 't':
813 table = optarg;
814 break;
815 case 'v':
816 verbose++;
817 break;
818 case 'w':
819 wflag = 1;
820 break;
821 case '?':
822 default:
823 usage();
824 /* NOTREACHED */
825 }
826 }
827 argc -= optind;
828 argv += optind;
829 if (dev == NULL || (lflag && (wflag || rflag))) {
830 /*
831 * No device specified, or attempting to loop and set
832 * or dump report at the same time
833 */
834 usage();
835 /* NOTREACHED */
836 }
837
838 for (varnum = 0; varnum < (size_t)argc; varnum++) {
839 char const *name, *valuesep;
840 struct Susbvar *svar;
841
842 svar = &variables[varnum];
843 name = argv[varnum];
844 valuesep = strchr(name, DELIM_SET);
845
846 svar->variable = name;
847 svar->mflags = 0;
848
849 if (valuesep == NULL) {
850 /* Read variable */
851 if (wflag)
852 errx(1, "Must not specify -w to read variables");
853 svar->value = NULL;
854 svar->varlen = strlen(name);
855
856 if (nflag) {
857 /* Display value of variable only */
858 svar->opfunc = varop_value;
859 } else {
860 /* Display name and value of variable */
861 svar->opfunc = varop_display;
862
863 if (verbose >= 1)
864 /* Show page names in verbose modes */
865 svar->mflags |= MATCH_SHOWPAGENAME;
866 }
867 } else {
868 /* Write variable */
869 if (!wflag)
870 errx(2, "Must specify -w to set variables");
871 svar->mflags |= MATCH_WRITABLE;
872 if (verbose >= 1)
873 /*
874 * Allow displaying of set value in
875 * verbose mode. This isn't
876 * particularly useful though, so
877 * don't bother documenting it.
878 */
879 svar->mflags |= MATCH_SHOWVALUES;
880 svar->varlen = valuesep - name;
881 svar->value = valuesep + 1;
882 svar->opfunc = varop_modify;
883 }
884 }
885
886 if (aflag || rflag) {
887 struct Susbvar *svar;
888
889 svar = &variables[varnum++];
890
891 svar->variable = NULL;
892 svar->mflags = MATCH_ALL;
893
894 if (rflag) {
895 /*
896 * Dump report descriptor. Do dump collection
897 * items also, and hint that it won't be
898 * necessary to get the item status.
899 */
900 svar->opfunc = varop_report;
901 svar->mflags |= MATCH_COLLECTIONS | MATCH_NODATA;
902
903 switch (verbose) {
904 default:
905 /* Level 2: Show item numerics and constants */
906 svar->mflags |= MATCH_SHOWNUMERIC;
907 /* FALLTHROUGH */
908 case 1:
909 /* Level 1: Just show constants */
910 svar->mflags |= MATCH_CONSTANTS;
911 /* FALLTHROUGH */
912 case 0:
913 break;
914 }
915 } else {
916 /* Display name and value of variable */
917 svar->opfunc = varop_display;
918
919 switch (verbose) {
920 default:
921 /* Level 2: Show constants and page names */
922 svar->mflags |= MATCH_CONSTANTS;
923 /* FALLTHROUGH */
924 case 1:
925 /* Level 1: Just show page names */
926 svar->mflags |= MATCH_SHOWPAGENAME;
927 /* FALLTHROUGH */
928 case 0:
929 break;
930 }
931 }
932 }
933
934 if (varnum == 0) {
935 /* Nothing to do... Display usage information. */
936 usage();
937 /* NOTREACHED */
938 }
939
940 hid_init(table);
941
942 if (dev[0] != '/') {
943 snprintf(devnamebuf, sizeof(devnamebuf), "/dev/%s%s",
944 isdigit(dev[0]) ? "uhid" : "", dev);
945 dev = devnamebuf;
946 }
947
948 hidfd = open(dev, O_RDWR);
949 if (hidfd < 0)
950 err(1, "%s", dev);
951
952 if (ioctl(hidfd, USB_GET_REPORT_ID, &reportid) < 0)
953 reportid = -1;
954 if (verbose > 1)
955 printf("report ID=%d\n", reportid);
956 repdesc = hid_get_report_desc(hidfd);
957 if (repdesc == 0)
958 errx(1, "USB_GET_REPORT_DESC");
959
960 if (lflag) {
961 devloop(hidfd, repdesc, variables, varnum);
962 /* NOTREACHED */
963 }
964
965 if (rflag)
966 /* Report mode header */
967 printf("Report descriptor:\n");
968
969 devshow(hidfd, repdesc, variables, varnum,
970 1 << hid_input |
971 1 << hid_output |
972 1 << hid_feature);
973
974 if (rflag) {
975 /* Report mode trailer */
976 size_t repindex;
977 for (repindex = 0;
978 repindex < (sizeof(reptoparam) / sizeof(*reptoparam));
979 repindex++) {
980 int size;
981 size = hid_report_size(repdesc,
982 reptoparam[repindex].hid_kind,
983 reportid);
984 printf("Total %7s size %d bytes\n",
985 reptoparam[repindex].name, size);
986 }
987 }
988
989 hid_dispose_report_desc(repdesc);
990 exit(0);
991 /* NOTREACHED */
992 }
993