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