[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.10

1.10    ! miod        1: /*     $OpenBSD: usbhid.c,v 1.9 2008/10/16 18:37:19 jakemsr 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: {
                    415:        if (report->buffer != NULL)
                    416:                free(report->buffer);
                    417:        report->status = srs_uninit;
                    418: }
                    419:
                    420: static void
                    421: getreport(struct Sreport *report, int hidfd, report_desc_t rd, int repindex)
                    422: {
                    423:        if (report->status == srs_uninit) {
                    424:                allocreport(report, rd, repindex);
                    425:                if (report->size == 0)
                    426:                        return;
                    427:
1.3       nate      428:                report->buffer->ucr_report = reptoparam[repindex].uhid_report;
1.1       pvalchev  429:                if (ioctl(hidfd, USB_GET_REPORT, report->buffer) < 0)
1.4       nate      430:                        err(1, "USB_GET_REPORT (probably not supported by "
                    431:                            "device)");
1.1       pvalchev  432:        }
                    433: }
                    434:
                    435: static void
                    436: setreport(struct Sreport *report, int hidfd, int repindex)
                    437: {
                    438:        if (report->status == srs_dirty) {
1.3       nate      439:                report->buffer->ucr_report = reptoparam[repindex].uhid_report;
1.1       pvalchev  440:
                    441:                if (ioctl(hidfd, USB_SET_REPORT, report->buffer) < 0)
                    442:                        err(1, "USB_SET_REPORT(%s)",
                    443:                            reptoparam[repindex].name);
                    444:
                    445:                report->status = srs_clean;
                    446:        }
                    447: }
                    448:
                    449: /* ARGSUSED1 */
                    450: static int
                    451: varop_value(struct hid_item *item, struct Susbvar *var,
                    452:            u_int32_t const *collist, size_t collen, u_char *buf)
                    453: {
                    454:        printf("%d\n", hid_get_data(buf, item));
                    455:        return 0;
                    456: }
                    457:
                    458: /* ARGSUSED1 */
                    459: static int
                    460: varop_display(struct hid_item *item, struct Susbvar *var,
                    461:              u_int32_t const *collist, size_t collen, u_char *buf)
                    462: {
                    463:        size_t colitem;
1.4       nate      464:        int val, i;
1.1       pvalchev  465:
1.4       nate      466:        for (i = 0; i < item->report_count; i++) {
                    467:                for (colitem = 0; colitem < collen; colitem++) {
                    468:                        if (var->mflags & MATCH_SHOWPAGENAME)
                    469:                                printf("%s:",
                    470:                                    hid_usage_page(HID_PAGE(collist[colitem])));
                    471:                        printf("%s.", hid_usage_in_page(collist[colitem]));
                    472:                }
1.1       pvalchev  473:                if (var->mflags & MATCH_SHOWPAGENAME)
1.4       nate      474:                        printf("%s:", hid_usage_page(HID_PAGE(item->usage)));
                    475:                val = hid_get_data(buf, item);
                    476:                item->pos += item->report_size;
                    477:                if (item->usage_minimum != 0 || item->usage_maximum != 0) {
                    478:                        val += item->usage_minimum;
                    479:                        printf("%s=1", hid_usage_in_page(val));
                    480:                } else {
                    481:                        printf("%s=%d%s", hid_usage_in_page(item->usage),
                    482:                               val, item->flags & HIO_CONST ? " (const)" : "");
                    483:                }
                    484:                if (item->report_count > 1)
                    485:                        printf(" [%d]", i);
                    486:                printf("\n");
1.1       pvalchev  487:        }
                    488:        return 0;
                    489: }
                    490:
                    491: /* ARGSUSED1 */
                    492: static int
                    493: varop_modify(struct hid_item *item, struct Susbvar *var,
                    494:             u_int32_t const *collist, size_t collen, u_char *buf)
                    495: {
                    496:        u_int dataval;
                    497:
                    498:        dataval = (u_int)strtol(var->value, NULL, 10);
                    499:
                    500:        hid_set_data(buf, item, dataval);
                    501:
1.4       nate      502:        if (var->mflags & MATCH_SHOWVALUES)
                    503:                /* Display set value */
1.1       pvalchev  504:                varop_display(item, var, collist, collen, buf);
                    505:
                    506:        return 1;
                    507: }
                    508:
                    509: static void
                    510: reportitem(char const *label, struct hid_item const *item, unsigned int mflags)
                    511: {
1.4       nate      512:        int isconst = item->flags & HIO_CONST,
                    513:            isvar = item->flags & HIO_VARIABLE;
                    514:        printf("%s size=%d count=%d%s%s page=%s", label,
1.1       pvalchev  515:               item->report_size, item->report_count,
1.4       nate      516:               isconst ? " Const" : "",
                    517:               !isvar && !isconst ? " Array" : "",
                    518:               hid_usage_page(HID_PAGE(item->usage)));
                    519:        if (item->usage_minimum != 0 || item->usage_maximum != 0) {
                    520:                printf(" usage=%s..%s", hid_usage_in_page(item->usage_minimum),
                    521:                       hid_usage_in_page(item->usage_maximum));
                    522:                if (mflags & MATCH_SHOWNUMERIC)
                    523:                        printf(" (%u:0x%x..%u:0x%x)",
                    524:                               HID_PAGE(item->usage_minimum),
                    525:                               HID_USAGE(item->usage_minimum),
                    526:                               HID_PAGE(item->usage_maximum),
                    527:                               HID_USAGE(item->usage_maximum));
                    528:        } else {
                    529:                printf(" usage=%s", hid_usage_in_page(item->usage));
                    530:                if (mflags & MATCH_SHOWNUMERIC)
                    531:                        printf(" (%u:0x%x)",
                    532:                               HID_PAGE(item->usage), HID_USAGE(item->usage));
                    533:        }
1.1       pvalchev  534:        printf(", logical range %d..%d",
                    535:               item->logical_minimum, item->logical_maximum);
                    536:        if (item->physical_minimum != item->physical_maximum)
                    537:                printf(", physical range %d..%d",
                    538:                       item->physical_minimum, item->physical_maximum);
                    539:        if (item->unit)
                    540:                printf(", unit=0x%02x exp=%d", item->unit,
                    541:                       item->unit_exponent);
                    542:        printf("\n");
                    543: }
                    544:
                    545: /* ARGSUSED1 */
                    546: static int
                    547: varop_report(struct hid_item *item, struct Susbvar *var,
                    548:             u_int32_t const *collist, size_t collen, u_char *buf)
                    549: {
                    550:        switch (item->kind) {
                    551:        case hid_collection:
1.4       nate      552:                printf("Collection page=%s usage=%s",
1.1       pvalchev  553:                       hid_usage_page(HID_PAGE(item->usage)),
                    554:                       hid_usage_in_page(item->usage));
1.4       nate      555:                if (var->mflags & MATCH_SHOWNUMERIC)
                    556:                        printf(" (%u:0x%x)\n",
                    557:                               HID_PAGE(item->usage), HID_USAGE(item->usage));
                    558:                else
                    559:                        printf("\n");
1.1       pvalchev  560:                break;
                    561:        case hid_endcollection:
                    562:                printf("End collection\n");
                    563:                break;
                    564:        case hid_input:
                    565:                reportitem("Input  ", item, var->mflags);
                    566:                break;
                    567:        case hid_output:
                    568:                reportitem("Output ", item, var->mflags);
                    569:                break;
                    570:        case hid_feature:
                    571:                reportitem("Feature", item, var->mflags);
                    572:                break;
                    573:        }
                    574:
                    575:        return 0;
                    576: }
                    577:
                    578: static void
                    579: devloop(int hidfd, report_desc_t rd, struct Susbvar *varlist, size_t vlsize)
                    580: {
1.4       nate      581:        u_char *dbuf;
1.1       pvalchev  582:        struct hid_data *hdata;
1.4       nate      583:        size_t collind, dlen;
1.1       pvalchev  584:        struct hid_item hitem;
                    585:        u_int32_t colls[128];
                    586:        struct Sreport inreport;
                    587:
                    588:        allocreport(&inreport, rd, REPORT_INPUT);
                    589:
                    590:        if (inreport.size <= 0)
                    591:                errx(1, "Input report descriptor invalid length");
                    592:
                    593:        dlen = inreport.size;
1.3       nate      594:        dbuf = inreport.buffer->ucr_data;
1.1       pvalchev  595:
                    596:        for (;;) {
                    597:                ssize_t readlen;
                    598:
                    599:                readlen = read(hidfd, dbuf, dlen);
1.4       nate      600:                if (readlen < 0)
                    601:                        err(1, "Device read error");
                    602:                if (dlen != (size_t)readlen)
                    603:                        errx(1, "Unexpected response length: %lu != %lu",
                    604:                             (unsigned long)readlen, (unsigned long)dlen);
1.1       pvalchev  605:
                    606:                collind = 0;
1.4       nate      607:                hdata = hid_start_parse(rd, 1 << hid_input, reportid);
1.1       pvalchev  608:                if (hdata == NULL)
                    609:                        errx(1, "Failed to start parser");
                    610:
                    611:                while (hid_get_item(hdata, &hitem)) {
                    612:                        struct Susbvar *matchvar;
                    613:
                    614:                        switch (hitem.kind) {
                    615:                        case hid_collection:
                    616:                                if (collind >= (sizeof(colls) / sizeof(*colls)))
                    617:                                        errx(1, "Excessive nested collections");
                    618:                                colls[collind++] = hitem.usage;
                    619:                                break;
                    620:                        case hid_endcollection:
                    621:                                if (collind == 0)
                    622:                                        errx(1, "Excessive collection ends");
                    623:                                collind--;
                    624:                                break;
                    625:                        case hid_input:
                    626:                                break;
                    627:                        case hid_output:
                    628:                        case hid_feature:
                    629:                                errx(1, "Unexpected non-input item returned");
                    630:                        }
                    631:
1.4       nate      632:                        if (reportid != -1 && hitem.report_ID != reportid)
                    633:                                continue;
                    634:
1.1       pvalchev  635:                        matchvar = hidmatch(colls, collind, &hitem,
                    636:                                            varlist, vlsize);
                    637:
                    638:                        if (matchvar != NULL)
                    639:                                matchvar->opfunc(&hitem, matchvar,
                    640:                                                 colls, collind,
1.3       nate      641:                                                 inreport.buffer->ucr_data);
1.1       pvalchev  642:                }
                    643:                hid_end_parse(hdata);
                    644:                printf("\n");
                    645:        }
                    646:        /* NOTREACHED */
                    647: }
                    648:
                    649: static void
                    650: devshow(int hidfd, report_desc_t rd, struct Susbvar *varlist, size_t vlsize,
                    651:        int kindset)
                    652: {
                    653:        struct hid_data *hdata;
1.4       nate      654:        size_t collind, repind, vlind;
1.1       pvalchev  655:        struct hid_item hitem;
                    656:        u_int32_t colls[128];
                    657:        struct Sreport reports[REPORT_MAXVAL + 1];
                    658:
                    659:
                    660:        for (repind = 0; repind < (sizeof(reports) / sizeof(*reports));
                    661:             repind++) {
                    662:                reports[repind].status = srs_uninit;
                    663:                reports[repind].buffer = NULL;
                    664:        }
                    665:
                    666:        collind = 0;
1.4       nate      667:        hdata = hid_start_parse(rd, kindset, reportid);
1.1       pvalchev  668:        if (hdata == NULL)
                    669:                errx(1, "Failed to start parser");
                    670:
                    671:        while (hid_get_item(hdata, &hitem)) {
                    672:                struct Susbvar *matchvar;
                    673:                int repindex;
                    674:
1.4       nate      675:                if (verbose > 3)
                    676:                        printf("item: kind=%d repid=%d usage=0x%x\n",
                    677:                               hitem.kind, hitem.report_ID, hitem.usage);
1.1       pvalchev  678:                repindex = -1;
                    679:                switch (hitem.kind) {
                    680:                case hid_collection:
                    681:                        if (collind >= (sizeof(colls) / sizeof(*colls)))
                    682:                                errx(1, "Excessive nested collections");
                    683:                        colls[collind++] = hitem.usage;
                    684:                        break;
                    685:                case hid_endcollection:
                    686:                        if (collind == 0)
                    687:                                errx(1, "Excessive collection ends");
                    688:                        collind--;
                    689:                        break;
                    690:                case hid_input:
                    691:                        repindex = REPORT_INPUT;
                    692:                        break;
                    693:                case hid_output:
                    694:                        repindex = REPORT_OUTPUT;
                    695:                        break;
                    696:                case hid_feature:
                    697:                        repindex = REPORT_FEATURE;
                    698:                        break;
                    699:                }
                    700:
1.4       nate      701:                if (reportid != -1 && hitem.report_ID != reportid)
                    702:                        continue;
                    703:
1.1       pvalchev  704:                matchvar = hidmatch(colls, collind, &hitem, varlist, vlsize);
                    705:
                    706:                if (matchvar != NULL) {
                    707:                        u_char *bufdata;
                    708:                        struct Sreport *repptr;
                    709:
                    710:                        matchvar->mflags |= MATCH_WASMATCHED;
                    711:
                    712:                        if (repindex >= 0)
                    713:                                repptr = &reports[repindex];
                    714:                        else
                    715:                                repptr = NULL;
                    716:
                    717:                        if (repptr != NULL &&
                    718:                            !(matchvar->mflags & MATCH_NODATA))
                    719:                                getreport(repptr, hidfd, rd, repindex);
                    720:
                    721:                        bufdata = (repptr == NULL || repptr->buffer == NULL) ?
1.3       nate      722:                                NULL : repptr->buffer->ucr_data;
1.1       pvalchev  723:
                    724:                        if (matchvar->opfunc(&hitem, matchvar, colls, collind,
                    725:                                             bufdata))
                    726:                                repptr->status = srs_dirty;
                    727:                }
                    728:        }
                    729:        hid_end_parse(hdata);
                    730:
                    731:        for (repind = 0; repind < (sizeof(reports) / sizeof(*reports));
                    732:             repind++) {
                    733:                setreport(&reports[repind], hidfd, repind);
                    734:                freereport(&reports[repind]);
                    735:        }
                    736:
                    737:        /* Warn about any items that we couldn't find a match for */
                    738:        for (vlind = 0; vlind < vlsize; vlind++) {
                    739:                struct Susbvar *var;
                    740:
                    741:                var = &varlist[vlind];
                    742:
                    743:                if (var->variable != NULL &&
                    744:                    !(var->mflags & MATCH_WASMATCHED))
                    745:                        warnx("Failed to match: %.*s", (int)var->varlen,
                    746:                              var->variable);
                    747:        }
                    748: }
                    749:
                    750: static void
                    751: usage(void)
                    752: {
                    753:        extern char *__progname;
                    754:
1.9       jakemsr   755:        fprintf(stderr, "usage: %s -f device [-t table] [-alv]\n",
1.1       pvalchev  756:            __progname);
1.5       jmc       757:        fprintf(stderr, "       %s -f device [-t table] [-v] -r\n",
1.1       pvalchev  758:            __progname);
                    759:        fprintf(stderr,
1.5       jmc       760:            "       %s -f device [-t table] [-lnv] name ...\n",
1.1       pvalchev  761:            __progname);
                    762:        fprintf(stderr,
1.5       jmc       763:            "       %s -f device [-t table] -w name=value ...\n",
1.1       pvalchev  764:            __progname);
                    765:        exit(1);
                    766: }
                    767:
                    768: int
                    769: main(int argc, char **argv)
                    770: {
1.4       nate      771:        char const *dev;
                    772:        char const *table;
                    773:        size_t varnum;
                    774:        int aflag, lflag, nflag, rflag, wflag;
                    775:        int ch, hidfd;
1.1       pvalchev  776:        report_desc_t repdesc;
                    777:        char devnamebuf[PATH_MAX];
                    778:        struct Susbvar variables[128];
                    779:
                    780:        wflag = aflag = nflag = verbose = rflag = lflag = 0;
                    781:        dev = NULL;
                    782:        table = NULL;
                    783:        while ((ch = getopt(argc, argv, "?af:lnrt:vw")) != -1) {
                    784:                switch (ch) {
                    785:                case 'a':
                    786:                        aflag = 1;
                    787:                        break;
                    788:                case 'f':
                    789:                        dev = optarg;
                    790:                        break;
                    791:                case 'l':
                    792:                        lflag = 1;
                    793:                        break;
                    794:                case 'n':
                    795:                        nflag = 1;
                    796:                        break;
                    797:                case 'r':
                    798:                        rflag = 1;
                    799:                        break;
                    800:                case 't':
                    801:                        table = optarg;
                    802:                        break;
                    803:                case 'v':
                    804:                        verbose++;
                    805:                        break;
                    806:                case 'w':
                    807:                        wflag = 1;
                    808:                        break;
                    809:                case '?':
                    810:                default:
                    811:                        usage();
                    812:                        /* NOTREACHED */
                    813:                }
                    814:        }
                    815:        argc -= optind;
                    816:        argv += optind;
                    817:        if (dev == NULL || (lflag && (wflag || rflag))) {
                    818:                /*
                    819:                 * No device specified, or attempting to loop and set
                    820:                 * or dump report at the same time
                    821:                 */
                    822:                usage();
                    823:                /* NOTREACHED */
                    824:        }
1.9       jakemsr   825:
                    826:        if (argc == 0 && rflag == 0)
                    827:                aflag = 1;
1.1       pvalchev  828:
                    829:        for (varnum = 0; varnum < (size_t)argc; varnum++) {
                    830:                char const *name, *valuesep;
                    831:                struct Susbvar *svar;
                    832:
                    833:                svar = &variables[varnum];
                    834:                name = argv[varnum];
                    835:                valuesep = strchr(name, DELIM_SET);
                    836:
                    837:                svar->variable = name;
                    838:                svar->mflags = 0;
                    839:
                    840:                if (valuesep == NULL) {
                    841:                        /* Read variable */
                    842:                        if (wflag)
                    843:                                errx(1, "Must not specify -w to read variables");
                    844:                        svar->value = NULL;
                    845:                        svar->varlen = strlen(name);
                    846:
                    847:                        if (nflag) {
                    848:                                /* Display value of variable only */
                    849:                                svar->opfunc = varop_value;
                    850:                        } else {
                    851:                                /* Display name and value of variable */
                    852:                                svar->opfunc = varop_display;
                    853:
                    854:                                if (verbose >= 1)
                    855:                                        /* Show page names in verbose modes */
                    856:                                        svar->mflags |= MATCH_SHOWPAGENAME;
                    857:                        }
                    858:                } else {
                    859:                        /* Write variable */
                    860:                        if (!wflag)
                    861:                                errx(2, "Must specify -w to set variables");
                    862:                        svar->mflags |= MATCH_WRITABLE;
1.4       nate      863:                        if (verbose >= 1)
                    864:                                /*
                    865:                                 * Allow displaying of set value in
                    866:                                 * verbose mode.  This isn't
                    867:                                 * particularly useful though, so
                    868:                                 * don't bother documenting it.
                    869:                                 */
                    870:                                svar->mflags |= MATCH_SHOWVALUES;
1.1       pvalchev  871:                        svar->varlen = valuesep - name;
                    872:                        svar->value = valuesep + 1;
                    873:                        svar->opfunc = varop_modify;
                    874:                }
                    875:        }
                    876:
                    877:        if (aflag || rflag) {
                    878:                struct Susbvar *svar;
                    879:
                    880:                svar = &variables[varnum++];
                    881:
                    882:                svar->variable = NULL;
                    883:                svar->mflags = MATCH_ALL;
                    884:
                    885:                if (rflag) {
                    886:                        /*
                    887:                         * Dump report descriptor.  Do dump collection
                    888:                         * items also, and hint that it won't be
                    889:                         * necessary to get the item status.
                    890:                         */
                    891:                        svar->opfunc = varop_report;
                    892:                        svar->mflags |= MATCH_COLLECTIONS | MATCH_NODATA;
                    893:
                    894:                        switch (verbose) {
                    895:                        default:
                    896:                                /* Level 2: Show item numerics and constants */
                    897:                                svar->mflags |= MATCH_SHOWNUMERIC;
                    898:                                /* FALLTHROUGH */
                    899:                        case 1:
                    900:                                /* Level 1: Just show constants */
                    901:                                svar->mflags |= MATCH_CONSTANTS;
                    902:                                /* FALLTHROUGH */
                    903:                        case 0:
                    904:                                break;
                    905:                        }
                    906:                } else {
                    907:                        /* Display name and value of variable */
                    908:                        svar->opfunc = varop_display;
                    909:
                    910:                        switch (verbose) {
                    911:                        default:
                    912:                                /* Level 2: Show constants and page names */
                    913:                                svar->mflags |= MATCH_CONSTANTS;
                    914:                                /* FALLTHROUGH */
                    915:                        case 1:
                    916:                                /* Level 1: Just show page names */
                    917:                                svar->mflags |= MATCH_SHOWPAGENAME;
                    918:                                /* FALLTHROUGH */
                    919:                        case 0:
                    920:                                break;
                    921:                        }
                    922:                }
                    923:        }
                    924:
                    925:        if (varnum == 0) {
                    926:                /* Nothing to do...  Display usage information. */
                    927:                usage();
                    928:                /* NOTREACHED */
                    929:        }
                    930:
1.6       deraadt   931:        if (hid_start(table) == -1)
                    932:                errx(1, "hid_init");
1.1       pvalchev  933:
                    934:        if (dev[0] != '/') {
                    935:                snprintf(devnamebuf, sizeof(devnamebuf), "/dev/%s%s",
                    936:                         isdigit(dev[0]) ? "uhid" : "", dev);
                    937:                dev = devnamebuf;
                    938:        }
                    939:
1.10    ! miod      940:        hidfd = open(dev, wflag ? O_RDWR : O_RDONLY);
1.1       pvalchev  941:        if (hidfd < 0)
                    942:                err(1, "%s", dev);
                    943:
1.4       nate      944:        if (ioctl(hidfd, USB_GET_REPORT_ID, &reportid) < 0)
                    945:                reportid = -1;
                    946:        if (verbose > 1)
                    947:                printf("report ID=%d\n", reportid);
1.1       pvalchev  948:        repdesc = hid_get_report_desc(hidfd);
                    949:        if (repdesc == 0)
                    950:                errx(1, "USB_GET_REPORT_DESC");
                    951:
                    952:        if (lflag) {
                    953:                devloop(hidfd, repdesc, variables, varnum);
                    954:                /* NOTREACHED */
                    955:        }
                    956:
                    957:        if (rflag)
                    958:                /* Report mode header */
                    959:                printf("Report descriptor:\n");
                    960:
                    961:        devshow(hidfd, repdesc, variables, varnum,
                    962:                1 << hid_input |
                    963:                1 << hid_output |
                    964:                1 << hid_feature);
                    965:
                    966:        if (rflag) {
                    967:                /* Report mode trailer */
                    968:                size_t repindex;
                    969:                for (repindex = 0;
                    970:                     repindex < (sizeof(reptoparam) / sizeof(*reptoparam));
                    971:                     repindex++) {
1.4       nate      972:                        int size;
1.1       pvalchev  973:                        size = hid_report_size(repdesc,
                    974:                                               reptoparam[repindex].hid_kind,
1.4       nate      975:                                               reportid);
                    976:                        printf("Total %7s size %d bytes\n",
                    977:                               reptoparam[repindex].name, size);
1.1       pvalchev  978:                }
                    979:        }
                    980:
                    981:        hid_dispose_report_desc(repdesc);
                    982:        exit(0);
                    983:        /* NOTREACHED */
                    984: }