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

1.14    ! ratchov     1: /*     $OpenBSD: sndioctl.c,v 1.13 2020/06/18 05:33:16 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);
1.7       ratchov    51: void print_num(struct info *);
1.8       ratchov    52: void print_ent(struct info *, char *);
1.1       ratchov    53: void print_val(struct info *, int);
1.8       ratchov    54: void print_par(struct info *, int);
1.1       ratchov    55: int parse_name(char **, char *);
1.5       ratchov    56: int parse_unit(char **, int *);
1.1       ratchov    57: int parse_val(char **, float *);
                     58: int parse_node(char **, char *, int *);
                     59: int parse_modeval(char **, int *, float *);
                     60: void dump(void);
                     61: int cmd(char *);
                     62: void commit(void);
                     63: void list(void);
                     64: void ondesc(void *, struct sioctl_desc *, int);
                     65: void onctl(void *, unsigned, unsigned);
                     66:
                     67: struct sioctl_hdl *hdl;
                     68: struct info *infolist;
1.4       ratchov    69: int i_flag = 0, v_flag = 0, m_flag = 0, n_flag = 0, q_flag = 0;
1.1       ratchov    70:
                     71: static inline int
1.12      ratchov    72: isname(int c)
1.1       ratchov    73: {
1.12      ratchov    74:        return (c == '_') ||
                     75:            (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
                     76:            (c >= '0' && c <= '9');
1.1       ratchov    77: }
                     78:
                     79: static int
                     80: ftoi(float f)
                     81: {
                     82:        return f + 0.5;
                     83: }
                     84:
                     85: /*
                     86:  * compare two sioctl_desc structures, used to sort infolist
                     87:  */
                     88: int
                     89: cmpdesc(struct sioctl_desc *d1, struct sioctl_desc *d2)
                     90: {
                     91:        int res;
                     92:
                     93:        res = strcmp(d1->group, d2->group);
                     94:        if (res != 0)
                     95:                return res;
                     96:        res = strcmp(d1->node0.name, d2->node0.name);
                     97:        if (res != 0)
                     98:                return res;
                     99:        res = d1->type - d2->type;
                    100:        if (res != 0)
                    101:                return res;
                    102:        res = strcmp(d1->func, d2->func);
                    103:        if (res != 0)
                    104:                return res;
                    105:        res = d1->node0.unit - d2->node0.unit;
1.14    ! ratchov   106:        if (d1->type == SIOCTL_SEL ||
        !           107:            d1->type == SIOCTL_VEC ||
1.1       ratchov   108:            d1->type == SIOCTL_LIST) {
                    109:                if (res != 0)
                    110:                        return res;
                    111:                res = strcmp(d1->node1.name, d2->node1.name);
                    112:                if (res != 0)
                    113:                        return res;
                    114:                res = d1->node1.unit - d2->node1.unit;
                    115:        }
                    116:        return res;
                    117: }
                    118:
                    119: /*
                    120:  * return true of the vector entry is diagonal
                    121:  */
                    122: int
                    123: isdiag(struct info *e)
                    124: {
                    125:        if (e->desc.node0.unit < 0 || e->desc.node1.unit < 0)
                    126:                return 1;
                    127:        return e->desc.node1.unit == e->desc.node0.unit;
                    128: }
                    129:
                    130: /*
                    131:  * find the selector or vector entry with the given name and channels
                    132:  */
                    133: struct info *
                    134: vecent(struct info *i, char *vstr, int vunit)
                    135: {
                    136:        while (i != NULL) {
                    137:                if ((strcmp(i->desc.node1.name, vstr) == 0) &&
                    138:                    (vunit < 0 || i->desc.node1.unit == vunit))
                    139:                        break;
                    140:                i = i->next;
                    141:        }
                    142:        return i;
                    143: }
                    144:
                    145: /*
                    146:  * skip all parameters with the same group, name, and func
                    147:  */
                    148: struct info *
                    149: nextfunc(struct info *i)
                    150: {
                    151:        char *str, *group, *func;
                    152:
                    153:        group = i->desc.group;
                    154:        func = i->desc.func;
                    155:        str = i->desc.node0.name;
                    156:        for (i = i->next; i != NULL; i = i->next) {
                    157:                if (strcmp(i->desc.group, group) != 0 ||
                    158:                    strcmp(i->desc.node0.name, str) != 0 ||
                    159:                    strcmp(i->desc.func, func) != 0)
                    160:                        return i;
                    161:        }
                    162:        return NULL;
                    163: }
                    164:
                    165: /*
                    166:  * find the next parameter with the same group, name, func
                    167:  */
                    168: struct info *
                    169: nextpar(struct info *i)
                    170: {
                    171:        char *str, *group, *func;
                    172:        int unit;
                    173:
                    174:        group = i->desc.group;
                    175:        func = i->desc.func;
                    176:        str = i->desc.node0.name;
                    177:        unit = i->desc.node0.unit;
                    178:        for (i = i->next; i != NULL; i = i->next) {
                    179:                if (strcmp(i->desc.group, group) != 0 ||
                    180:                    strcmp(i->desc.node0.name, str) != 0 ||
                    181:                    strcmp(i->desc.func, func) != 0)
                    182:                        break;
                    183:                /* XXX: need to check for -1 ? */
                    184:                if (i->desc.node0.unit != unit)
                    185:                        return i;
                    186:        }
                    187:        return NULL;
                    188: }
                    189:
                    190: /*
                    191:  * return the first vector entry with the given name
                    192:  */
                    193: struct info *
                    194: firstent(struct info *g, char *vstr)
                    195: {
                    196:        char *astr, *group, *func;
                    197:        struct info *i;
                    198:
                    199:        group = g->desc.group;
                    200:        astr = g->desc.node0.name;
                    201:        func = g->desc.func;
                    202:        for (i = g; i != NULL; i = i->next) {
                    203:                if (strcmp(i->desc.group, group) != 0 ||
                    204:                    strcmp(i->desc.node0.name, astr) != 0 ||
                    205:                    strcmp(i->desc.func, func) != 0)
                    206:                        break;
                    207:                if (!isdiag(i))
                    208:                        continue;
                    209:                if (strcmp(i->desc.node1.name, vstr) == 0)
                    210:                        return i;
                    211:        }
                    212:        return NULL;
                    213: }
                    214:
                    215: /*
                    216:  * find the next entry of the given vector, if the mono flag
                    217:  * is set then the whole group is searched and off-diagonal entries are
                    218:  * skipped
                    219:  */
                    220: struct info *
                    221: nextent(struct info *i, int mono)
                    222: {
                    223:        char *str, *group, *func;
                    224:        int unit;
                    225:
                    226:        group = i->desc.group;
                    227:        func = i->desc.func;
                    228:        str = i->desc.node0.name;
                    229:        unit = i->desc.node0.unit;
                    230:        for (i = i->next; i != NULL; i = i->next) {
                    231:                if (strcmp(i->desc.group, group) != 0 ||
                    232:                    strcmp(i->desc.node0.name, str) != 0 ||
                    233:                    strcmp(i->desc.func, func) != 0)
                    234:                        return NULL;
                    235:                if (mono)
                    236:                        return i;
                    237:                if (i->desc.node0.unit == unit)
                    238:                        return i;
                    239:        }
                    240:        return NULL;
                    241: }
                    242:
                    243: /*
                    244:  * return true if parameter matches the given name and channel
                    245:  */
                    246: int
                    247: matchpar(struct info *i, char *astr, int aunit)
                    248: {
                    249:        if (strcmp(i->desc.node0.name, astr) != 0)
                    250:                return 0;
                    251:        if (aunit < 0)
                    252:                return 1;
                    253:        else if (i->desc.node0.unit < 0) {
                    254:                fprintf(stderr, "unit used for parameter with no unit\n");
                    255:                exit(1);
                    256:        }
                    257:        return i->desc.node0.unit == aunit;
                    258: }
                    259:
                    260: /*
                    261:  * return true if selector or vector entry matches the given name and
                    262:  * channel range
                    263:  */
                    264: int
                    265: matchent(struct info *i, char *vstr, int vunit)
                    266: {
                    267:        if (strcmp(i->desc.node1.name, vstr) != 0)
                    268:                return 0;
                    269:        if (vunit < 0)
                    270:                return 1;
                    271:        else if (i->desc.node1.unit < 0) {
                    272:                fprintf(stderr, "unit used for parameter with no unit\n");
                    273:                exit(1);
                    274:        }
                    275:        return i->desc.node1.unit == vunit;
                    276: }
                    277:
                    278: /*
                    279:  * return true if the given group can be represented as a signle mono
                    280:  * parameter
                    281:  */
                    282: int
                    283: ismono(struct info *g)
                    284: {
                    285:        struct info *p1, *p2;
                    286:        struct info *e1, *e2;
                    287:
                    288:        p1 = g;
                    289:        switch (g->desc.type) {
                    290:        case SIOCTL_NUM:
                    291:        case SIOCTL_SW:
                    292:                for (p2 = g; p2 != NULL; p2 = nextpar(p2)) {
                    293:                        if (p2->curval != p1->curval)
                    294:                                return 0;
                    295:                }
                    296:                break;
1.14    ! ratchov   297:        case SIOCTL_SEL:
1.1       ratchov   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;
1.14    ! ratchov   346:        case SIOCTL_SEL:
1.1       ratchov   347:        case SIOCTL_VEC:
                    348:        case SIOCTL_LIST:
                    349:                more = 0;
                    350:                for (e = p; e != NULL; e = nextent(e, mono)) {
                    351:                        if (mono) {
                    352:                                if (!isdiag(e))
                    353:                                        continue;
                    354:                                if (e != firstent(p, e->desc.node1.name))
                    355:                                        continue;
                    356:                        }
                    357:                        if (more)
                    358:                                printf(",");
                    359:                        print_node(&e->desc.node1, mono);
1.14    ! ratchov   360:                        if (p->desc.type != SIOCTL_SEL)
        !           361:                                printf(":*");
1.1       ratchov   362:                        more = 1;
                    363:                }
                    364:        }
                    365: }
                    366:
1.7       ratchov   367: void
                    368: print_num(struct info *p)
                    369: {
                    370:        if (p->desc.maxval == 1)
                    371:                printf("%d", p->curval);
                    372:        else {
                    373:                /*
                    374:                 * For now, maxval is always 127 or 255,
                    375:                 * so three decimals is always ideal.
                    376:                 */
                    377:                printf("%.3f", p->curval / (float)p->desc.maxval);
                    378:        }
                    379: }
                    380:
1.1       ratchov   381: /*
1.8       ratchov   382:  * print a single control
                    383:  */
                    384: void
                    385: print_ent(struct info *e, char *comment)
                    386: {
                    387:        if (e->desc.group[0] != 0) {
                    388:                printf("%s", e->desc.group);
                    389:                printf("/");
                    390:        }
                    391:        print_node(&e->desc.node0, 0);
                    392:        printf(".%s=", e->desc.func);
                    393:        switch (e->desc.type) {
                    394:        case SIOCTL_NONE:
                    395:                printf("<removed>\n");
                    396:                break;
1.14    ! ratchov   397:        case SIOCTL_SEL:
1.8       ratchov   398:        case SIOCTL_VEC:
                    399:        case SIOCTL_LIST:
                    400:                print_node(&e->desc.node1, 0);
                    401:                printf(":");
                    402:                /* FALLTHROUGH */
                    403:        case SIOCTL_SW:
                    404:        case SIOCTL_NUM:
                    405:                print_num(e);
                    406:        }
                    407:        if (comment)
                    408:                printf("\t# %s", comment);
                    409:        printf("\n");
                    410: }
                    411:
                    412: /*
1.1       ratchov   413:  * print parameter value
                    414:  */
                    415: void
                    416: print_val(struct info *p, int mono)
                    417: {
                    418:        struct info *e;
                    419:        int more;
                    420:
                    421:        switch (p->desc.type) {
                    422:        case SIOCTL_NUM:
                    423:        case SIOCTL_SW:
1.7       ratchov   424:                print_num(p);
1.1       ratchov   425:                break;
1.14    ! ratchov   426:        case SIOCTL_SEL:
1.1       ratchov   427:        case SIOCTL_VEC:
                    428:        case SIOCTL_LIST:
                    429:                more = 0;
                    430:                for (e = p; e != NULL; e = nextent(e, mono)) {
                    431:                        if (mono) {
                    432:                                if (!isdiag(e))
                    433:                                        continue;
                    434:                                if (e != firstent(p, e->desc.node1.name))
                    435:                                        continue;
                    436:                        }
1.10      ratchov   437:                        if (e->desc.maxval == 1) {
                    438:                                if (e->curval) {
                    439:                                        if (more)
                    440:                                                printf(",");
                    441:                                        print_node(&e->desc.node1, mono);
                    442:                                        more = 1;
                    443:                                }
                    444:                        } else {
                    445:                                if (more)
                    446:                                        printf(",");
                    447:                                print_node(&e->desc.node1, mono);
                    448:                                printf(":");
                    449:                                print_num(e);
                    450:                                more = 1;
                    451:                        }
1.1       ratchov   452:                }
                    453:        }
                    454: }
                    455:
                    456: /*
                    457:  * print ``<parameter>=<value>'' string (including '\n')
                    458:  */
                    459: void
1.8       ratchov   460: print_par(struct info *p, int mono)
1.1       ratchov   461: {
1.3       ratchov   462:        if (!n_flag) {
                    463:                if (p->desc.group[0] != 0) {
                    464:                        printf("%s", p->desc.group);
                    465:                        printf("/");
                    466:                }
                    467:                print_node(&p->desc.node0, mono);
                    468:                printf(".%s=", p->desc.func);
1.1       ratchov   469:        }
                    470:        if (i_flag)
                    471:                print_desc(p, mono);
                    472:        else
                    473:                print_val(p, mono);
                    474:        printf("\n");
                    475: }
                    476:
                    477: /*
                    478:  * parse a stream name or parameter name
                    479:  */
                    480: int
                    481: parse_name(char **line, char *name)
                    482: {
                    483:        char *p = *line;
                    484:        unsigned len = 0;
                    485:
1.12      ratchov   486:        if (!isname(*p)) {
                    487:                fprintf(stderr, "letter or digit expected near '%s'\n", p);
1.1       ratchov   488:                return 0;
                    489:        }
1.12      ratchov   490:        while (isname(*p)) {
1.1       ratchov   491:                if (len >= SIOCTL_NAMEMAX - 1) {
                    492:                        name[SIOCTL_NAMEMAX - 1] = '\0';
                    493:                        fprintf(stderr, "%s...: too long\n", name);
                    494:                        return 0;
                    495:                }
                    496:                name[len++] = *p;
                    497:                p++;
                    498:        }
                    499:        name[len] = '\0';
                    500:        *line = p;
                    501:        return 1;
                    502: }
                    503:
                    504: /*
                    505:  * parse a decimal integer
                    506:  */
                    507: int
1.5       ratchov   508: parse_unit(char **line, int *num)
1.1       ratchov   509: {
                    510:        char *p = *line;
                    511:        unsigned int val;
                    512:        int n;
                    513:
                    514:        if (sscanf(p, "%u%n", &val, &n) != 1) {
                    515:                fprintf(stderr, "number expected near '%s'\n", p);
                    516:                return 0;
                    517:        }
                    518:        if (val >= 255) {
                    519:                fprintf(stderr, "%d: too large\n", val);
                    520:                return 0;
                    521:        }
                    522:        *num = val;
                    523:        *line = p + n;
                    524:        return 1;
                    525: }
                    526:
                    527: int
                    528: parse_val(char **line, float *num)
                    529: {
                    530:        char *p = *line;
                    531:        float val;
                    532:        int n;
                    533:
                    534:        if (sscanf(p, "%g%n", &val, &n) != 1) {
                    535:                fprintf(stderr, "number expected near '%s'\n", p);
                    536:                return 0;
                    537:        }
                    538:        if (val < 0 || val > 1) {
                    539:                fprintf(stderr, "%g: expected number between 0 and 1\n", val);
                    540:                return 0;
                    541:        }
                    542:        *num = val;
                    543:        *line = p + n;
                    544:        return 1;
                    545: }
                    546:
                    547: /*
                    548:  * parse a sub-stream, eg. "spkr[7]"
                    549:  */
                    550: int
                    551: parse_node(char **line, char *str, int *unit)
                    552: {
                    553:        char *p = *line;
                    554:
                    555:        if (!parse_name(&p, str))
                    556:                return 0;
                    557:        if (*p != '[') {
                    558:                *unit = -1;
                    559:                *line = p;
                    560:                return 1;
                    561:        }
                    562:        p++;
                    563:        if (!parse_unit(&p, unit))
                    564:                return 0;
                    565:        if (*p != ']') {
                    566:                fprintf(stderr, "']' expected near '%s'\n", p);
                    567:                return 0;
                    568:        }
                    569:        p++;
                    570:        *line = p;
                    571:        return 1;
                    572: }
                    573:
                    574: /*
                    575:  * parse a decimal prefixed by the optional mode
                    576:  */
                    577: int
                    578: parse_modeval(char **line, int *rmode, float *rval)
                    579: {
                    580:        char *p = *line;
                    581:        unsigned mode;
                    582:
                    583:        switch (*p) {
                    584:        case '+':
                    585:                mode = MODE_ADD;
                    586:                p++;
                    587:                break;
                    588:        case '-':
                    589:                mode = MODE_SUB;
                    590:                p++;
                    591:                break;
                    592:        case '!':
                    593:                mode = MODE_TOGGLE;
                    594:                p++;
                    595:                break;
                    596:        default:
                    597:                mode = MODE_SET;
                    598:        }
                    599:        if (mode != MODE_TOGGLE) {
                    600:                if (!parse_val(&p, rval))
                    601:                        return 0;
                    602:        }
                    603:        *line = p;
                    604:        *rmode = mode;
                    605:        return 1;
                    606: }
                    607:
                    608: /*
                    609:  * dump the whole controls list, useful for debugging
                    610:  */
                    611: void
                    612: dump(void)
                    613: {
                    614:        struct info *i;
                    615:
                    616:        for (i = infolist; i != NULL; i = i->next) {
                    617:                printf("%03u:", i->ctladdr);
                    618:                print_node(&i->desc.node0, 0);
                    619:                printf(".%s", i->desc.func);
                    620:                printf("=");
                    621:                switch (i->desc.type) {
                    622:                case SIOCTL_NUM:
                    623:                case SIOCTL_SW:
                    624:                        printf("0..%d (%u)", i->desc.maxval, i->curval);
                    625:                        break;
1.14    ! ratchov   626:                case SIOCTL_SEL:
        !           627:                        print_node(&i->desc.node1, 0);
        !           628:                        break;
1.1       ratchov   629:                case SIOCTL_VEC:
                    630:                case SIOCTL_LIST:
                    631:                        print_node(&i->desc.node1, 0);
                    632:                        printf(":0..%d (%u)", i->desc.maxval, i->curval);
                    633:                }
                    634:                printf("\n");
                    635:        }
                    636: }
                    637:
                    638: /*
                    639:  * parse and execute a command ``<parameter>[=<value>]''
                    640:  */
                    641: int
                    642: cmd(char *line)
                    643: {
                    644:        char *pos, *group;
                    645:        struct info *i, *e, *g;
                    646:        char func[SIOCTL_NAMEMAX];
                    647:        char astr[SIOCTL_NAMEMAX], vstr[SIOCTL_NAMEMAX];
                    648:        int aunit, vunit;
                    649:        unsigned npar = 0, nent = 0;
                    650:        int comma, mode;
                    651:        float val;
                    652:
                    653:        pos = strrchr(line, '/');
                    654:        if (pos != NULL) {
                    655:                group = line;
                    656:                pos[0] = 0;
                    657:                pos++;
                    658:        } else {
                    659:                group = "";
                    660:                pos = line;
                    661:        }
                    662:        if (!parse_node(&pos, astr, &aunit))
                    663:                return 0;
                    664:        if (*pos != '.') {
                    665:                fprintf(stderr, "'.' expected near '%s'\n", pos);
                    666:                return 0;
                    667:        }
                    668:        pos++;
                    669:        if (!parse_name(&pos, func))
                    670:                return 0;
                    671:        for (g = infolist;; g = g->next) {
                    672:                if (g == NULL) {
                    673:                        fprintf(stderr, "%s.%s: no such control\n", astr, func);
                    674:                        return 0;
                    675:                }
                    676:                if (strcmp(g->desc.group, group) == 0 &&
                    677:                    strcmp(g->desc.func, func) == 0 &&
                    678:                    strcmp(g->desc.node0.name, astr) == 0)
                    679:                        break;
                    680:        }
                    681:        g->mode = MODE_PRINT;
                    682:        if (*pos != '=') {
                    683:                if (*pos != '\0') {
                    684:                        fprintf(stderr, "junk at end of command\n");
                    685:                        return 0;
                    686:                }
                    687:                return 1;
                    688:        }
                    689:        pos++;
                    690:        if (i_flag) {
                    691:                printf("can't set values in info mode\n");
                    692:                return 0;
                    693:        }
                    694:        npar = 0;
                    695:        switch (g->desc.type) {
                    696:        case SIOCTL_NUM:
                    697:        case SIOCTL_SW:
                    698:                if (!parse_modeval(&pos, &mode, &val))
                    699:                        return 0;
                    700:                for (i = g; i != NULL; i = nextpar(i)) {
                    701:                        if (!matchpar(i, astr, aunit))
                    702:                                continue;
                    703:                        i->mode = mode;
                    704:                        i->newval = ftoi(val * i->desc.maxval);
                    705:                        npar++;
                    706:                }
                    707:                break;
1.14    ! ratchov   708:        case SIOCTL_SEL:
1.1       ratchov   709:        case SIOCTL_VEC:
                    710:        case SIOCTL_LIST:
                    711:                for (i = g; i != NULL; i = nextpar(i)) {
                    712:                        if (!matchpar(i, astr, aunit))
                    713:                                continue;
                    714:                        for (e = i; e != NULL; e = nextent(e, 0)) {
                    715:                                e->newval = 0;
                    716:                                e->mode = MODE_SET;
                    717:                        }
                    718:                        npar++;
                    719:                }
                    720:                comma = 0;
                    721:                for (;;) {
                    722:                        if (*pos == '\0')
                    723:                                break;
                    724:                        if (comma) {
                    725:                                if (*pos != ',')
                    726:                                        break;
                    727:                                pos++;
                    728:                        }
                    729:                        if (!parse_node(&pos, vstr, &vunit))
                    730:                                return 0;
                    731:                        if (*pos == ':') {
                    732:                                pos++;
                    733:                                if (!parse_modeval(&pos, &mode, &val))
                    734:                                        return 0;
                    735:                        } else {
                    736:                                val = 1.;
                    737:                                mode = MODE_SET;
                    738:                        }
                    739:                        nent = 0;
                    740:                        for (i = g; i != NULL; i = nextpar(i)) {
                    741:                                if (!matchpar(i, astr, aunit))
                    742:                                        continue;
                    743:                                for (e = i; e != NULL; e = nextent(e, 0)) {
                    744:                                        if (matchent(e, vstr, vunit)) {
                    745:                                                e->newval = ftoi(val * e->desc.maxval);
                    746:                                                e->mode = mode;
                    747:                                                nent++;
                    748:                                        }
                    749:                                }
                    750:                        }
                    751:                        if (nent == 0) {
                    752:                                /* XXX: use print_node()-like routine */
                    753:                                fprintf(stderr, "%s[%d]: invalid value\n", vstr, vunit);
1.8       ratchov   754:                                print_par(g, 0);
1.1       ratchov   755:                                exit(1);
                    756:                        }
                    757:                        comma = 1;
                    758:                }
                    759:        }
                    760:        if (npar == 0) {
                    761:                fprintf(stderr, "%s: invalid parameter\n", line);
                    762:                exit(1);
                    763:        }
                    764:        if (*pos != '\0') {
                    765:                printf("%s: junk at end of command\n", pos);
                    766:                exit(1);
                    767:        }
                    768:        return 1;
                    769: }
                    770:
                    771: /*
                    772:  * write the controls with the ``set'' flag on the device
                    773:  */
                    774: void
                    775: commit(void)
                    776: {
                    777:        struct info *i;
                    778:        int val;
                    779:
                    780:        for (i = infolist; i != NULL; i = i->next) {
                    781:                val = 0xdeadbeef;
                    782:                switch (i->mode) {
                    783:                case MODE_IGNORE:
                    784:                case MODE_PRINT:
                    785:                        continue;
                    786:                case MODE_SET:
                    787:                        val = i->newval;
                    788:                        break;
                    789:                case MODE_ADD:
                    790:                        val = i->curval + i->newval;
                    791:                        if (val > i->desc.maxval)
                    792:                                val = i->desc.maxval;
                    793:                        break;
                    794:                case MODE_SUB:
                    795:                        val = i->curval - i->newval;
                    796:                        if (val < 0)
                    797:                                val = 0;
                    798:                        break;
                    799:                case MODE_TOGGLE:
                    800:                        val = i->curval ? 0 : i->desc.maxval;
                    801:                }
                    802:                sioctl_setval(hdl, i->ctladdr, val);
                    803:                i->curval = val;
                    804:        }
                    805: }
                    806:
                    807: /*
                    808:  * print all parameters
                    809:  */
                    810: void
                    811: list(void)
                    812: {
                    813:        struct info *p, *g;
                    814:
                    815:        for (g = infolist; g != NULL; g = nextfunc(g)) {
                    816:                if (g->mode == MODE_IGNORE)
                    817:                        continue;
                    818:                if (i_flag) {
                    819:                        if (v_flag) {
                    820:                                for (p = g; p != NULL; p = nextpar(p))
1.8       ratchov   821:                                        print_par(p, 0);
1.1       ratchov   822:                        } else
1.8       ratchov   823:                                print_par(g, 1);
1.1       ratchov   824:                } else {
                    825:                        if (v_flag || !ismono(g)) {
                    826:                                for (p = g; p != NULL; p = nextpar(p))
1.8       ratchov   827:                                        print_par(p, 0);
1.1       ratchov   828:                        } else
1.8       ratchov   829:                                print_par(g, 1);
1.1       ratchov   830:                }
                    831:        }
                    832: }
                    833:
                    834: /*
                    835:  * register a new knob/button, called from the poll() loop.  this may be
                    836:  * called when label string changes, in which case we update the
                    837:  * existing label widged rather than inserting a new one.
                    838:  */
                    839: void
                    840: ondesc(void *arg, struct sioctl_desc *d, int curval)
                    841: {
                    842:        struct info *i, **pi;
                    843:        int cmp;
                    844:
                    845:        if (d == NULL)
                    846:                return;
                    847:
                    848:        /*
                    849:         * delete control
                    850:         */
                    851:        for (pi = &infolist; (i = *pi) != NULL; pi = &i->next) {
                    852:                if (d->addr == i->desc.addr) {
                    853:                        if (m_flag)
1.8       ratchov   854:                                print_ent(i, "deleted");
1.1       ratchov   855:                        *pi = i->next;
                    856:                        free(i);
                    857:                        break;
                    858:                }
                    859:        }
                    860:
1.13      ratchov   861:        switch (d->type) {
                    862:        case SIOCTL_NUM:
                    863:        case SIOCTL_SW:
                    864:        case SIOCTL_VEC:
                    865:        case SIOCTL_LIST:
1.14    ! ratchov   866:        case SIOCTL_SEL:
1.13      ratchov   867:                break;
                    868:        default:
1.1       ratchov   869:                return;
1.13      ratchov   870:        }
1.1       ratchov   871:
                    872:        /*
                    873:         * find the right position to insert the new widget
                    874:         */
                    875:        for (pi = &infolist; (i = *pi) != NULL; pi = &i->next) {
                    876:                cmp = cmpdesc(d, &i->desc);
                    877:                if (cmp == 0) {
                    878:                        fprintf(stderr, "fatal: duplicate control:\n");
1.8       ratchov   879:                        print_ent(i, "duplicate");
1.1       ratchov   880:                        exit(1);
                    881:                }
                    882:                if (cmp < 0)
                    883:                        break;
                    884:        }
                    885:        i = malloc(sizeof(struct info));
                    886:        if (i == NULL) {
                    887:                perror("malloc");
                    888:                exit(1);
                    889:        }
                    890:        i->desc = *d;
                    891:        i->ctladdr = d->addr;
                    892:        i->curval = i->newval = curval;
                    893:        i->mode = MODE_IGNORE;
                    894:        i->next = *pi;
                    895:        *pi = i;
                    896:        if (m_flag)
1.8       ratchov   897:                print_ent(i, "added");
1.1       ratchov   898: }
                    899:
                    900: /*
                    901:  * update a knob/button state, called from the poll() loop
                    902:  */
                    903: void
                    904: onctl(void *arg, unsigned addr, unsigned val)
                    905: {
1.14    ! ratchov   906:        struct info *i, *j;
        !           907:
        !           908:        i = infolist;
        !           909:        for (;;) {
        !           910:                if (i == NULL)
        !           911:                        return;
        !           912:                if (i->ctladdr == addr)
        !           913:                        break;
        !           914:                i = i->next;
        !           915:        }
        !           916:
        !           917:        if (i->curval == val) {
        !           918:                print_ent(i, "eq");
        !           919:                return;
        !           920:        }
1.1       ratchov   921:
1.14    ! ratchov   922:        if (i->desc.type == SIOCTL_SEL) {
        !           923:                for (j = infolist; j != NULL; j = j->next) {
        !           924:                        if (strcmp(i->desc.group, j->desc.group) != 0 ||
        !           925:                            strcmp(i->desc.node0.name, j->desc.node0.name) != 0 ||
        !           926:                            strcmp(i->desc.func, j->desc.func) != 0 ||
        !           927:                            i->desc.node0.unit != j->desc.node0.unit)
        !           928:                                continue;
        !           929:                        j->curval = (i->ctladdr == j->ctladdr);
1.9       ratchov   930:                }
1.14    ! ratchov   931:        } else
        !           932:                i->curval = val;
        !           933:
        !           934:        if (m_flag)
        !           935:                print_ent(i, "changed");
1.1       ratchov   936: }
                    937:
                    938: int
                    939: main(int argc, char **argv)
                    940: {
                    941:        char *devname = SIO_DEVANY;
                    942:        int i, c, d_flag = 0;
                    943:        struct info *g;
                    944:        struct pollfd *pfds;
                    945:        int nfds, revents;
                    946:
1.4       ratchov   947:        while ((c = getopt(argc, argv, "df:imnqv")) != -1) {
1.1       ratchov   948:                switch (c) {
                    949:                case 'd':
                    950:                        d_flag = 1;
                    951:                        break;
                    952:                case 'f':
                    953:                        devname = optarg;
                    954:                        break;
                    955:                case 'i':
                    956:                        i_flag = 1;
                    957:                        break;
                    958:                case 'm':
                    959:                        m_flag = 1;
                    960:                        break;
1.3       ratchov   961:                case 'n':
                    962:                        n_flag = 1;
                    963:                        break;
1.4       ratchov   964:                case 'q':
                    965:                        q_flag = 1;
                    966:                        break;
1.1       ratchov   967:                case 'v':
                    968:                        v_flag++;
                    969:                        break;
                    970:                default:
                    971:                        fprintf(stderr, "usage: sndioctl "
1.4       ratchov   972:                            "[-dimnqv] [-f device] [command ...]\n");
1.1       ratchov   973:                        exit(1);
                    974:                }
                    975:        }
                    976:        argc -= optind;
                    977:        argv += optind;
                    978:
                    979:        hdl = sioctl_open(devname, SIOCTL_READ | SIOCTL_WRITE, 0);
                    980:        if (hdl == NULL) {
                    981:                fprintf(stderr, "%s: can't open control device\n", devname);
                    982:                exit(1);
                    983:        }
1.11      mestre    984:
                    985:        if (pledge("stdio audio", NULL) == -1) {
                    986:                fprintf(stderr, "%s: pledge: %s\n", getprogname(),
                    987:                    strerror(errno));
                    988:                exit(1);
                    989:        }
                    990:
1.1       ratchov   991:        if (!sioctl_ondesc(hdl, ondesc, NULL)) {
                    992:                fprintf(stderr, "%s: can't get device description\n", devname);
                    993:                exit(1);
                    994:        }
                    995:        sioctl_onval(hdl, onctl, NULL);
                    996:
                    997:        if (d_flag) {
                    998:                if (argc > 0) {
                    999:                        fprintf(stderr,
                   1000:                            "commands are not allowed with -d option\n");
                   1001:                        exit(1);
                   1002:                }
                   1003:                dump();
                   1004:        } else {
                   1005:                if (argc == 0) {
                   1006:                        for (g = infolist; g != NULL; g = nextfunc(g))
                   1007:                                g->mode = MODE_PRINT;
                   1008:                } else {
                   1009:                        for (i = 0; i < argc; i++) {
                   1010:                                if (!cmd(argv[i]))
                   1011:                                        return 1;
                   1012:                        }
                   1013:                }
                   1014:                commit();
1.4       ratchov  1015:                if (!q_flag)
                   1016:                        list();
1.1       ratchov  1017:        }
                   1018:        if (m_flag) {
                   1019:                pfds = malloc(sizeof(struct pollfd) * sioctl_nfds(hdl));
                   1020:                if (pfds == NULL) {
                   1021:                        perror("malloc");
                   1022:                        exit(1);
                   1023:                }
                   1024:                for (;;) {
                   1025:                        nfds = sioctl_pollfd(hdl, pfds, POLLIN);
                   1026:                        if (nfds == 0)
                   1027:                                break;
                   1028:                        while (poll(pfds, nfds, -1) < 0) {
                   1029:                                if (errno != EINTR) {
                   1030:                                        perror("poll");
                   1031:                                        exit(1);
                   1032:                                }
                   1033:                        }
                   1034:                        revents = sioctl_revents(hdl, pfds);
                   1035:                        if (revents & POLLHUP) {
                   1036:                                fprintf(stderr, "disconnected\n");
                   1037:                                break;
                   1038:                        }
                   1039:                }
                   1040:                free(pfds);
                   1041:        }
                   1042:        sioctl_close(hdl);
                   1043:        return 0;
                   1044: }