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