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

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