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