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