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