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

Annotation of src/usr.bin/sndioctl/sndioctl.c, Revision 1.3

1.3     ! ratchov     1: /*     $OpenBSD: sndioctl.c,v 1.2 2020/02/26 14:47:48 ratchov Exp $    */
1.1       ratchov     2: /*
                      3:  * Copyright (c) 2014-2020 Alexandre Ratchov <alex@caoua.org>
                      4:  *
                      5:  * Permission to use, copy, modify, and distribute this software for any
                      6:  * purpose with or without fee is hereby granted, provided that the above
                      7:  * copyright notice and this permission notice appear in all copies.
                      8:  *
                      9:  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
                     10:  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
                     11:  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
                     12:  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
                     13:  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
                     14:  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
                     15:  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
                     16:  */
                     17: #include <errno.h>
                     18: #include <poll.h>
                     19: #include <sndio.h>
                     20: #include <stdlib.h>
                     21: #include <stdio.h>
                     22: #include <string.h>
                     23: #include <unistd.h>
                     24:
                     25: struct info {
                     26:        struct info *next;
                     27:        struct sioctl_desc desc;
                     28:        unsigned ctladdr;
                     29: #define MODE_IGNORE    0       /* ignore this value */
                     30: #define MODE_PRINT     1       /* print-only, don't change value */
                     31: #define MODE_SET       2       /* set to newval value */
                     32: #define MODE_ADD       3       /* increase current value by newval */
                     33: #define MODE_SUB       4       /* decrease current value by newval */
                     34: #define MODE_TOGGLE    5       /* toggle current value */
                     35:        unsigned mode;
                     36:        int curval, newval;
                     37: };
                     38:
                     39: int cmpdesc(struct sioctl_desc *, struct sioctl_desc *);
                     40: int isdiag(struct info *);
                     41: struct info *vecent(struct info *, char *, int);
                     42: struct info *nextfunc(struct info *);
                     43: struct info *nextpar(struct info *);
                     44: struct info *firstent(struct info *, char *);
                     45: struct info *nextent(struct info *, int);
                     46: int matchpar(struct info *, char *, int);
                     47: int matchent(struct info *, char *, int);
                     48: int ismono(struct info *);
                     49: void print_node(struct sioctl_node *, int);
                     50: void print_desc(struct info *, int);
                     51: void print_val(struct info *, int);
                     52: void print_par(struct info *, int, char *);
                     53: int parse_name(char **, char *);
                     54: int parse_unit(char **, unsigned int *);
                     55: int parse_val(char **, float *);
                     56: int parse_node(char **, char *, int *);
                     57: int parse_modeval(char **, int *, float *);
                     58: void dump(void);
                     59: int cmd(char *);
                     60: void commit(void);
                     61: void list(void);
                     62: void ondesc(void *, struct sioctl_desc *, int);
                     63: void onctl(void *, unsigned, unsigned);
                     64:
                     65: struct sioctl_hdl *hdl;
                     66: struct info *infolist;
1.3     ! ratchov    67: int i_flag = 0, v_flag = 0, m_flag = 0, n_flag = 0;
1.1       ratchov    68:
                     69: static inline int
                     70: isname_first(int c)
                     71: {
                     72:        return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
                     73: }
                     74:
                     75: static inline int
                     76: isname_next(int c)
                     77: {
                     78:        return isname_first(c) || (c >= '0' && c <= '9') || (c == '_');
                     79: }
                     80:
                     81: static int
                     82: ftoi(float f)
                     83: {
                     84:        return f + 0.5;
                     85: }
                     86:
                     87: /*
                     88:  * compare two sioctl_desc structures, used to sort infolist
                     89:  */
                     90: int
                     91: cmpdesc(struct sioctl_desc *d1, struct sioctl_desc *d2)
                     92: {
                     93:        int res;
                     94:
                     95:        res = strcmp(d1->group, d2->group);
                     96:        if (res != 0)
                     97:                return res;
                     98:        res = strcmp(d1->node0.name, d2->node0.name);
                     99:        if (res != 0)
                    100:                return res;
                    101:        res = d1->type - d2->type;
                    102:        if (res != 0)
                    103:                return res;
                    104:        res = strcmp(d1->func, d2->func);
                    105:        if (res != 0)
                    106:                return res;
                    107:        res = d1->node0.unit - d2->node0.unit;
                    108:        if (d1->type == SIOCTL_VEC ||
                    109:            d1->type == SIOCTL_LIST) {
                    110:                if (res != 0)
                    111:                        return res;
                    112:                res = strcmp(d1->node1.name, d2->node1.name);
                    113:                if (res != 0)
                    114:                        return res;
                    115:                res = d1->node1.unit - d2->node1.unit;
                    116:        }
                    117:        return res;
                    118: }
                    119:
                    120: /*
                    121:  * return true of the vector entry is diagonal
                    122:  */
                    123: int
                    124: isdiag(struct info *e)
                    125: {
                    126:        if (e->desc.node0.unit < 0 || e->desc.node1.unit < 0)
                    127:                return 1;
                    128:        return e->desc.node1.unit == e->desc.node0.unit;
                    129: }
                    130:
                    131: /*
                    132:  * find the selector or vector entry with the given name and channels
                    133:  */
                    134: struct info *
                    135: vecent(struct info *i, char *vstr, int vunit)
                    136: {
                    137:        while (i != NULL) {
                    138:                if ((strcmp(i->desc.node1.name, vstr) == 0) &&
                    139:                    (vunit < 0 || i->desc.node1.unit == vunit))
                    140:                        break;
                    141:                i = i->next;
                    142:        }
                    143:        return i;
                    144: }
                    145:
                    146: /*
                    147:  * skip all parameters with the same group, name, and func
                    148:  */
                    149: struct info *
                    150: nextfunc(struct info *i)
                    151: {
                    152:        char *str, *group, *func;
                    153:
                    154:        group = i->desc.group;
                    155:        func = i->desc.func;
                    156:        str = i->desc.node0.name;
                    157:        for (i = i->next; i != NULL; i = i->next) {
                    158:                if (strcmp(i->desc.group, group) != 0 ||
                    159:                    strcmp(i->desc.node0.name, str) != 0 ||
                    160:                    strcmp(i->desc.func, func) != 0)
                    161:                        return i;
                    162:        }
                    163:        return NULL;
                    164: }
                    165:
                    166: /*
                    167:  * find the next parameter with the same group, name, func
                    168:  */
                    169: struct info *
                    170: nextpar(struct info *i)
                    171: {
                    172:        char *str, *group, *func;
                    173:        int unit;
                    174:
                    175:        group = i->desc.group;
                    176:        func = i->desc.func;
                    177:        str = i->desc.node0.name;
                    178:        unit = i->desc.node0.unit;
                    179:        for (i = i->next; i != NULL; i = i->next) {
                    180:                if (strcmp(i->desc.group, group) != 0 ||
                    181:                    strcmp(i->desc.node0.name, str) != 0 ||
                    182:                    strcmp(i->desc.func, func) != 0)
                    183:                        break;
                    184:                /* XXX: need to check for -1 ? */
                    185:                if (i->desc.node0.unit != unit)
                    186:                        return i;
                    187:        }
                    188:        return NULL;
                    189: }
                    190:
                    191: /*
                    192:  * return the first vector entry with the given name
                    193:  */
                    194: struct info *
                    195: firstent(struct info *g, char *vstr)
                    196: {
                    197:        char *astr, *group, *func;
                    198:        struct info *i;
                    199:
                    200:        group = g->desc.group;
                    201:        astr = g->desc.node0.name;
                    202:        func = g->desc.func;
                    203:        for (i = g; i != NULL; i = i->next) {
                    204:                if (strcmp(i->desc.group, group) != 0 ||
                    205:                    strcmp(i->desc.node0.name, astr) != 0 ||
                    206:                    strcmp(i->desc.func, func) != 0)
                    207:                        break;
                    208:                if (!isdiag(i))
                    209:                        continue;
                    210:                if (strcmp(i->desc.node1.name, vstr) == 0)
                    211:                        return i;
                    212:        }
                    213:        return NULL;
                    214: }
                    215:
                    216: /*
                    217:  * find the next entry of the given vector, if the mono flag
                    218:  * is set then the whole group is searched and off-diagonal entries are
                    219:  * skipped
                    220:  */
                    221: struct info *
                    222: nextent(struct info *i, int mono)
                    223: {
                    224:        char *str, *group, *func;
                    225:        int unit;
                    226:
                    227:        group = i->desc.group;
                    228:        func = i->desc.func;
                    229:        str = i->desc.node0.name;
                    230:        unit = i->desc.node0.unit;
                    231:        for (i = i->next; i != NULL; i = i->next) {
                    232:                if (strcmp(i->desc.group, group) != 0 ||
                    233:                    strcmp(i->desc.node0.name, str) != 0 ||
                    234:                    strcmp(i->desc.func, func) != 0)
                    235:                        return NULL;
                    236:                if (mono)
                    237:                        return i;
                    238:                if (i->desc.node0.unit == unit)
                    239:                        return i;
                    240:        }
                    241:        return NULL;
                    242: }
                    243:
                    244: /*
                    245:  * return true if parameter matches the given name and channel
                    246:  */
                    247: int
                    248: matchpar(struct info *i, char *astr, int aunit)
                    249: {
                    250:        if (strcmp(i->desc.node0.name, astr) != 0)
                    251:                return 0;
                    252:        if (aunit < 0)
                    253:                return 1;
                    254:        else if (i->desc.node0.unit < 0) {
                    255:                fprintf(stderr, "unit used for parameter with no unit\n");
                    256:                exit(1);
                    257:        }
                    258:        return i->desc.node0.unit == aunit;
                    259: }
                    260:
                    261: /*
                    262:  * return true if selector or vector entry matches the given name and
                    263:  * channel range
                    264:  */
                    265: int
                    266: matchent(struct info *i, char *vstr, int vunit)
                    267: {
                    268:        if (strcmp(i->desc.node1.name, vstr) != 0)
                    269:                return 0;
                    270:        if (vunit < 0)
                    271:                return 1;
                    272:        else if (i->desc.node1.unit < 0) {
                    273:                fprintf(stderr, "unit used for parameter with no unit\n");
                    274:                exit(1);
                    275:        }
                    276:        return i->desc.node1.unit == vunit;
                    277: }
                    278:
                    279: /*
                    280:  * return true if the given group can be represented as a signle mono
                    281:  * parameter
                    282:  */
                    283: int
                    284: ismono(struct info *g)
                    285: {
                    286:        struct info *p1, *p2;
                    287:        struct info *e1, *e2;
                    288:
                    289:        p1 = g;
                    290:        switch (g->desc.type) {
                    291:        case SIOCTL_NUM:
                    292:        case SIOCTL_SW:
                    293:                for (p2 = g; p2 != NULL; p2 = nextpar(p2)) {
                    294:                        if (p2->curval != p1->curval)
                    295:                                return 0;
                    296:                }
                    297:                break;
                    298:        case SIOCTL_VEC:
                    299:        case SIOCTL_LIST:
                    300:                for (p2 = g; p2 != NULL; p2 = nextpar(p2)) {
                    301:                        for (e2 = p2; e2 != NULL; e2 = nextent(e2, 0)) {
                    302:                                if (!isdiag(e2)) {
                    303:                                        if (e2->curval != 0)
                    304:                                                return 0;
                    305:                                } else {
                    306:                                        e1 = vecent(p1,
                    307:                                            e2->desc.node1.name,
                    308:                                            p1->desc.node0.unit);
                    309:                                        if (e1 == NULL)
                    310:                                                continue;
                    311:                                        if (e1->curval != e2->curval)
                    312:                                                return 0;
                    313:                                }
                    314:                        }
                    315:                }
                    316:                break;
                    317:        }
                    318:        return 1;
                    319: }
                    320:
                    321: /*
                    322:  * print a sub-stream, eg. "spkr[4]"
                    323:  */
                    324: void
                    325: print_node(struct sioctl_node *c, int mono)
                    326: {
                    327:        printf("%s", c->name);
                    328:        if (!mono && c->unit >= 0)
                    329:                printf("[%d]", c->unit);
                    330: }
                    331:
                    332: /*
                    333:  * print info about the parameter
                    334:  */
                    335: void
                    336: print_desc(struct info *p, int mono)
                    337: {
                    338:        struct info *e;
                    339:        int more;
                    340:
                    341:        switch (p->desc.type) {
                    342:        case SIOCTL_NUM:
                    343:        case SIOCTL_SW:
                    344:                printf("*");
                    345:                break;
                    346:        case SIOCTL_VEC:
                    347:        case SIOCTL_LIST:
                    348:                more = 0;
                    349:                for (e = p; e != NULL; e = nextent(e, mono)) {
                    350:                        if (mono) {
                    351:                                if (!isdiag(e))
                    352:                                        continue;
                    353:                                if (e != firstent(p, e->desc.node1.name))
                    354:                                        continue;
                    355:                        }
                    356:                        if (more)
                    357:                                printf(",");
                    358:                        print_node(&e->desc.node1, mono);
                    359:                        printf(":*");
                    360:                        more = 1;
                    361:                }
                    362:        }
                    363: }
                    364:
                    365: /*
                    366:  * print parameter value
                    367:  */
                    368: void
                    369: print_val(struct info *p, int mono)
                    370: {
                    371:        struct info *e;
                    372:        int more;
                    373:
                    374:        switch (p->desc.type) {
                    375:        case SIOCTL_NUM:
                    376:        case SIOCTL_SW:
                    377:                printf("%.2g", p->curval / (float)p->desc.maxval);
                    378:                break;
                    379:        case SIOCTL_VEC:
                    380:        case SIOCTL_LIST:
                    381:                more = 0;
                    382:                for (e = p; e != NULL; e = nextent(e, mono)) {
                    383:                        if (mono) {
                    384:                                if (!isdiag(e))
                    385:                                        continue;
                    386:                                if (e != firstent(p, e->desc.node1.name))
                    387:                                        continue;
                    388:                        }
                    389:                        if (more)
                    390:                                printf(",");
                    391:                        print_node(&e->desc.node1, mono);
                    392:                        printf(":%.2g", e->curval / (float)e->desc.maxval);
                    393:                        more = 1;
                    394:                }
                    395:        }
                    396: }
                    397:
                    398: /*
                    399:  * print ``<parameter>=<value>'' string (including '\n')
                    400:  */
                    401: void
                    402: print_par(struct info *p, int mono, char *comment)
                    403: {
1.3     ! ratchov   404:        if (!n_flag) {
        !           405:                if (p->desc.group[0] != 0) {
        !           406:                        printf("%s", p->desc.group);
        !           407:                        printf("/");
        !           408:                }
        !           409:                print_node(&p->desc.node0, mono);
        !           410:                printf(".%s=", p->desc.func);
1.1       ratchov   411:        }
                    412:        if (i_flag)
                    413:                print_desc(p, mono);
                    414:        else
                    415:                print_val(p, mono);
                    416:        if (comment)
                    417:                printf(" # %s", comment);
                    418:        printf("\n");
                    419: }
                    420:
                    421: /*
                    422:  * parse a stream name or parameter name
                    423:  */
                    424: int
                    425: parse_name(char **line, char *name)
                    426: {
                    427:        char *p = *line;
                    428:        unsigned len = 0;
                    429:
                    430:        if (!isname_first(*p)) {
                    431:                fprintf(stderr, "letter expected near '%s'\n", p);
                    432:                return 0;
                    433:        }
                    434:        while (isname_next(*p)) {
                    435:                if (len >= SIOCTL_NAMEMAX - 1) {
                    436:                        name[SIOCTL_NAMEMAX - 1] = '\0';
                    437:                        fprintf(stderr, "%s...: too long\n", name);
                    438:                        return 0;
                    439:                }
                    440:                name[len++] = *p;
                    441:                p++;
                    442:        }
                    443:        name[len] = '\0';
                    444:        *line = p;
                    445:        return 1;
                    446: }
                    447:
                    448: /*
                    449:  * parse a decimal integer
                    450:  */
                    451: int
                    452: parse_unit(char **line, unsigned int *num)
                    453: {
                    454:        char *p = *line;
                    455:        unsigned int val;
                    456:        int n;
                    457:
                    458:        if (sscanf(p, "%u%n", &val, &n) != 1) {
                    459:                fprintf(stderr, "number expected near '%s'\n", p);
                    460:                return 0;
                    461:        }
                    462:        if (val >= 255) {
                    463:                fprintf(stderr, "%d: too large\n", val);
                    464:                return 0;
                    465:        }
                    466:        *num = val;
                    467:        *line = p + n;
                    468:        return 1;
                    469: }
                    470:
                    471: int
                    472: parse_val(char **line, float *num)
                    473: {
                    474:        char *p = *line;
                    475:        float val;
                    476:        int n;
                    477:
                    478:        if (sscanf(p, "%g%n", &val, &n) != 1) {
                    479:                fprintf(stderr, "number expected near '%s'\n", p);
                    480:                return 0;
                    481:        }
                    482:        if (val < 0 || val > 1) {
                    483:                fprintf(stderr, "%g: expected number between 0 and 1\n", val);
                    484:                return 0;
                    485:        }
                    486:        *num = val;
                    487:        *line = p + n;
                    488:        return 1;
                    489: }
                    490:
                    491: /*
                    492:  * parse a sub-stream, eg. "spkr[7]"
                    493:  */
                    494: int
                    495: parse_node(char **line, char *str, int *unit)
                    496: {
                    497:        char *p = *line;
                    498:
                    499:        if (!parse_name(&p, str))
                    500:                return 0;
                    501:        if (*p != '[') {
                    502:                *unit = -1;
                    503:                *line = p;
                    504:                return 1;
                    505:        }
                    506:        p++;
                    507:        if (!parse_unit(&p, unit))
                    508:                return 0;
                    509:        if (*p != ']') {
                    510:                fprintf(stderr, "']' expected near '%s'\n", p);
                    511:                return 0;
                    512:        }
                    513:        p++;
                    514:        *line = p;
                    515:        return 1;
                    516: }
                    517:
                    518: /*
                    519:  * parse a decimal prefixed by the optional mode
                    520:  */
                    521: int
                    522: parse_modeval(char **line, int *rmode, float *rval)
                    523: {
                    524:        char *p = *line;
                    525:        unsigned mode;
                    526:
                    527:        switch (*p) {
                    528:        case '+':
                    529:                mode = MODE_ADD;
                    530:                p++;
                    531:                break;
                    532:        case '-':
                    533:                mode = MODE_SUB;
                    534:                p++;
                    535:                break;
                    536:        case '!':
                    537:                mode = MODE_TOGGLE;
                    538:                p++;
                    539:                break;
                    540:        default:
                    541:                mode = MODE_SET;
                    542:        }
                    543:        if (mode != MODE_TOGGLE) {
                    544:                if (!parse_val(&p, rval))
                    545:                        return 0;
                    546:        }
                    547:        *line = p;
                    548:        *rmode = mode;
                    549:        return 1;
                    550: }
                    551:
                    552: /*
                    553:  * dump the whole controls list, useful for debugging
                    554:  */
                    555: void
                    556: dump(void)
                    557: {
                    558:        struct info *i;
                    559:
                    560:        for (i = infolist; i != NULL; i = i->next) {
                    561:                printf("%03u:", i->ctladdr);
                    562:                print_node(&i->desc.node0, 0);
                    563:                printf(".%s", i->desc.func);
                    564:                printf("=");
                    565:                switch (i->desc.type) {
                    566:                case SIOCTL_NUM:
                    567:                case SIOCTL_SW:
                    568:                        printf("0..%d (%u)", i->desc.maxval, i->curval);
                    569:                        break;
                    570:                case SIOCTL_VEC:
                    571:                case SIOCTL_LIST:
                    572:                        print_node(&i->desc.node1, 0);
                    573:                        printf(":0..%d (%u)", i->desc.maxval, i->curval);
                    574:                }
                    575:                printf("\n");
                    576:        }
                    577: }
                    578:
                    579: /*
                    580:  * parse and execute a command ``<parameter>[=<value>]''
                    581:  */
                    582: int
                    583: cmd(char *line)
                    584: {
                    585:        char *pos, *group;
                    586:        struct info *i, *e, *g;
                    587:        char func[SIOCTL_NAMEMAX];
                    588:        char astr[SIOCTL_NAMEMAX], vstr[SIOCTL_NAMEMAX];
                    589:        int aunit, vunit;
                    590:        unsigned npar = 0, nent = 0;
                    591:        int comma, mode;
                    592:        float val;
                    593:
                    594:        pos = strrchr(line, '/');
                    595:        if (pos != NULL) {
                    596:                group = line;
                    597:                pos[0] = 0;
                    598:                pos++;
                    599:        } else {
                    600:                group = "";
                    601:                pos = line;
                    602:        }
                    603:        if (!parse_node(&pos, astr, &aunit))
                    604:                return 0;
                    605:        if (*pos != '.') {
                    606:                fprintf(stderr, "'.' expected near '%s'\n", pos);
                    607:                return 0;
                    608:        }
                    609:        pos++;
                    610:        if (!parse_name(&pos, func))
                    611:                return 0;
                    612:        for (g = infolist;; g = g->next) {
                    613:                if (g == NULL) {
                    614:                        fprintf(stderr, "%s.%s: no such control\n", astr, func);
                    615:                        return 0;
                    616:                }
                    617:                if (strcmp(g->desc.group, group) == 0 &&
                    618:                    strcmp(g->desc.func, func) == 0 &&
                    619:                    strcmp(g->desc.node0.name, astr) == 0)
                    620:                        break;
                    621:        }
                    622:        g->mode = MODE_PRINT;
                    623:        if (*pos != '=') {
                    624:                if (*pos != '\0') {
                    625:                        fprintf(stderr, "junk at end of command\n");
                    626:                        return 0;
                    627:                }
                    628:                return 1;
                    629:        }
                    630:        pos++;
                    631:        if (i_flag) {
                    632:                printf("can't set values in info mode\n");
                    633:                return 0;
                    634:        }
                    635:        npar = 0;
                    636:        switch (g->desc.type) {
                    637:        case SIOCTL_NUM:
                    638:        case SIOCTL_SW:
                    639:                if (!parse_modeval(&pos, &mode, &val))
                    640:                        return 0;
                    641:                for (i = g; i != NULL; i = nextpar(i)) {
                    642:                        if (!matchpar(i, astr, aunit))
                    643:                                continue;
                    644:                        i->mode = mode;
                    645:                        i->newval = ftoi(val * i->desc.maxval);
                    646:                        npar++;
                    647:                }
                    648:                break;
                    649:        case SIOCTL_VEC:
                    650:        case SIOCTL_LIST:
                    651:                for (i = g; i != NULL; i = nextpar(i)) {
                    652:                        if (!matchpar(i, astr, aunit))
                    653:                                continue;
                    654:                        for (e = i; e != NULL; e = nextent(e, 0)) {
                    655:                                e->newval = 0;
                    656:                                e->mode = MODE_SET;
                    657:                        }
                    658:                        npar++;
                    659:                }
                    660:                comma = 0;
                    661:                for (;;) {
                    662:                        if (*pos == '\0')
                    663:                                break;
                    664:                        if (comma) {
                    665:                                if (*pos != ',')
                    666:                                        break;
                    667:                                pos++;
                    668:                        }
                    669:                        if (!parse_node(&pos, vstr, &vunit))
                    670:                                return 0;
                    671:                        if (*pos == ':') {
                    672:                                pos++;
                    673:                                if (!parse_modeval(&pos, &mode, &val))
                    674:                                        return 0;
                    675:                        } else {
                    676:                                val = 1.;
                    677:                                mode = MODE_SET;
                    678:                        }
                    679:                        nent = 0;
                    680:                        for (i = g; i != NULL; i = nextpar(i)) {
                    681:                                if (!matchpar(i, astr, aunit))
                    682:                                        continue;
                    683:                                for (e = i; e != NULL; e = nextent(e, 0)) {
                    684:                                        if (matchent(e, vstr, vunit)) {
                    685:                                                e->newval = ftoi(val * e->desc.maxval);
                    686:                                                e->mode = mode;
                    687:                                                nent++;
                    688:                                        }
                    689:                                }
                    690:                        }
                    691:                        if (nent == 0) {
                    692:                                /* XXX: use print_node()-like routine */
                    693:                                fprintf(stderr, "%s[%d]: invalid value\n", vstr, vunit);
                    694:                                print_par(g, 0, NULL);
                    695:                                exit(1);
                    696:                        }
                    697:                        comma = 1;
                    698:                }
                    699:        }
                    700:        if (npar == 0) {
                    701:                fprintf(stderr, "%s: invalid parameter\n", line);
                    702:                exit(1);
                    703:        }
                    704:        if (*pos != '\0') {
                    705:                printf("%s: junk at end of command\n", pos);
                    706:                exit(1);
                    707:        }
                    708:        return 1;
                    709: }
                    710:
                    711: /*
                    712:  * write the controls with the ``set'' flag on the device
                    713:  */
                    714: void
                    715: commit(void)
                    716: {
                    717:        struct info *i;
                    718:        int val;
                    719:
                    720:        for (i = infolist; i != NULL; i = i->next) {
                    721:                val = 0xdeadbeef;
                    722:                switch (i->mode) {
                    723:                case MODE_IGNORE:
                    724:                case MODE_PRINT:
                    725:                        continue;
                    726:                case MODE_SET:
                    727:                        val = i->newval;
                    728:                        break;
                    729:                case MODE_ADD:
                    730:                        val = i->curval + i->newval;
                    731:                        if (val > i->desc.maxval)
                    732:                                val = i->desc.maxval;
                    733:                        break;
                    734:                case MODE_SUB:
                    735:                        val = i->curval - i->newval;
                    736:                        if (val < 0)
                    737:                                val = 0;
                    738:                        break;
                    739:                case MODE_TOGGLE:
                    740:                        val = i->curval ? 0 : i->desc.maxval;
                    741:                }
                    742:                sioctl_setval(hdl, i->ctladdr, val);
                    743:                i->curval = val;
                    744:        }
                    745: }
                    746:
                    747: /*
                    748:  * print all parameters
                    749:  */
                    750: void
                    751: list(void)
                    752: {
                    753:        struct info *p, *g;
                    754:
                    755:        for (g = infolist; g != NULL; g = nextfunc(g)) {
                    756:                if (g->mode == MODE_IGNORE)
                    757:                        continue;
                    758:                if (i_flag) {
                    759:                        if (v_flag) {
                    760:                                for (p = g; p != NULL; p = nextpar(p))
                    761:                                        print_par(p, 0, NULL);
                    762:                        } else
                    763:                                print_par(g, 1, NULL);
                    764:                } else {
                    765:                        if (v_flag || !ismono(g)) {
                    766:                                for (p = g; p != NULL; p = nextpar(p))
                    767:                                        print_par(p, 0, NULL);
                    768:                        } else
                    769:                                print_par(g, 1, NULL);
                    770:                }
                    771:        }
                    772: }
                    773:
                    774: /*
                    775:  * register a new knob/button, called from the poll() loop.  this may be
                    776:  * called when label string changes, in which case we update the
                    777:  * existing label widged rather than inserting a new one.
                    778:  */
                    779: void
                    780: ondesc(void *arg, struct sioctl_desc *d, int curval)
                    781: {
                    782:        struct info *i, **pi;
                    783:        int cmp;
                    784:
                    785:        if (d == NULL)
                    786:                return;
                    787:
                    788:        /*
                    789:         * delete control
                    790:         */
                    791:        for (pi = &infolist; (i = *pi) != NULL; pi = &i->next) {
                    792:                if (d->addr == i->desc.addr) {
                    793:                        if (m_flag)
                    794:                                print_par(i, 0, "deleted");
                    795:                        *pi = i->next;
                    796:                        free(i);
                    797:                        break;
                    798:                }
                    799:        }
                    800:
                    801:        if (d->type == SIOCTL_NONE)
                    802:                return;
                    803:
                    804:        /*
                    805:         * find the right position to insert the new widget
                    806:         */
                    807:        for (pi = &infolist; (i = *pi) != NULL; pi = &i->next) {
                    808:                cmp = cmpdesc(d, &i->desc);
                    809:                if (cmp == 0) {
                    810:                        fprintf(stderr, "fatal: duplicate control:\n");
                    811:                        print_par(i, 0, "duplicate");
                    812:                        exit(1);
                    813:                }
                    814:                if (cmp < 0)
                    815:                        break;
                    816:        }
                    817:        i = malloc(sizeof(struct info));
                    818:        if (i == NULL) {
                    819:                perror("malloc");
                    820:                exit(1);
                    821:        }
                    822:        i->desc = *d;
                    823:        i->ctladdr = d->addr;
                    824:        i->curval = i->newval = curval;
                    825:        i->mode = MODE_IGNORE;
                    826:        i->next = *pi;
                    827:        *pi = i;
                    828:        if (m_flag)
                    829:                print_par(i, 0, "added");
                    830: }
                    831:
                    832: /*
                    833:  * update a knob/button state, called from the poll() loop
                    834:  */
                    835: void
                    836: onctl(void *arg, unsigned addr, unsigned val)
                    837: {
                    838:        struct info *i;
                    839:
                    840:        for (i = infolist; i != NULL; i = i->next) {
                    841:                if (i->ctladdr != addr)
                    842:                        continue;
                    843:                i->curval = val;
                    844:                if (m_flag)
                    845:                        print_par(i, 0, "changed");
                    846:        }
                    847: }
                    848:
                    849: int
                    850: main(int argc, char **argv)
                    851: {
                    852:        char *devname = SIO_DEVANY;
                    853:        int i, c, d_flag = 0;
                    854:        struct info *g;
                    855:        struct pollfd *pfds;
                    856:        int nfds, revents;
                    857:
1.3     ! ratchov   858:        while ((c = getopt(argc, argv, "df:imnv")) != -1) {
1.1       ratchov   859:                switch (c) {
                    860:                case 'd':
                    861:                        d_flag = 1;
                    862:                        break;
                    863:                case 'f':
                    864:                        devname = optarg;
                    865:                        break;
                    866:                case 'i':
                    867:                        i_flag = 1;
                    868:                        break;
                    869:                case 'm':
                    870:                        m_flag = 1;
                    871:                        break;
1.3     ! ratchov   872:                case 'n':
        !           873:                        n_flag = 1;
        !           874:                        break;
1.1       ratchov   875:                case 'v':
                    876:                        v_flag++;
                    877:                        break;
                    878:                default:
                    879:                        fprintf(stderr, "usage: sndioctl "
1.3     ! ratchov   880:                            "[-dimnv] [-f device] [command ...]\n");
1.1       ratchov   881:                        exit(1);
                    882:                }
                    883:        }
                    884:        argc -= optind;
                    885:        argv += optind;
                    886:
                    887:        hdl = sioctl_open(devname, SIOCTL_READ | SIOCTL_WRITE, 0);
                    888:        if (hdl == NULL) {
                    889:                fprintf(stderr, "%s: can't open control device\n", devname);
                    890:                exit(1);
                    891:        }
                    892:        if (!sioctl_ondesc(hdl, ondesc, NULL)) {
                    893:                fprintf(stderr, "%s: can't get device description\n", devname);
                    894:                exit(1);
                    895:        }
                    896:        sioctl_onval(hdl, onctl, NULL);
                    897:
                    898:        if (d_flag) {
                    899:                if (argc > 0) {
                    900:                        fprintf(stderr,
                    901:                            "commands are not allowed with -d option\n");
                    902:                        exit(1);
                    903:                }
                    904:                dump();
                    905:        } else {
                    906:                if (argc == 0) {
                    907:                        for (g = infolist; g != NULL; g = nextfunc(g))
                    908:                                g->mode = MODE_PRINT;
                    909:                } else {
                    910:                        for (i = 0; i < argc; i++) {
                    911:                                if (!cmd(argv[i]))
                    912:                                        return 1;
                    913:                        }
                    914:                }
                    915:                commit();
                    916:                list();
                    917:        }
                    918:        if (m_flag) {
                    919:                pfds = malloc(sizeof(struct pollfd) * sioctl_nfds(hdl));
                    920:                if (pfds == NULL) {
                    921:                        perror("malloc");
                    922:                        exit(1);
                    923:                }
                    924:                for (;;) {
                    925:                        nfds = sioctl_pollfd(hdl, pfds, POLLIN);
                    926:                        if (nfds == 0)
                    927:                                break;
                    928:                        while (poll(pfds, nfds, -1) < 0) {
                    929:                                if (errno != EINTR) {
                    930:                                        perror("poll");
                    931:                                        exit(1);
                    932:                                }
                    933:                        }
                    934:                        revents = sioctl_revents(hdl, pfds);
                    935:                        if (revents & POLLHUP) {
                    936:                                fprintf(stderr, "disconnected\n");
                    937:                                break;
                    938:                        }
                    939:                }
                    940:                free(pfds);
                    941:        }
                    942:        sioctl_close(hdl);
                    943:        return 0;
                    944: }