[BACK]Return to usbhid.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / usbhidctl

Annotation of src/usr.bin/usbhidctl/usbhid.c, Revision 1.12

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