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