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

Annotation of src/usr.bin/audioctl/audioctl.c, Revision 1.45

1.45    ! ratchov     1: /*     $OpenBSD: audioctl.c,v 1.44 2022/12/26 19:16:00 jmc Exp $       */
1.1       provos      2: /*
1.31      ratchov     3:  * Copyright (c) 2016 Alexandre Ratchov <alex@caoua.org>
1.1       provos      4:  *
1.31      ratchov     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.
1.1       provos      8:  *
1.31      ratchov     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.
1.1       provos     16:  */
1.31      ratchov    17: #include <sys/types.h>
                     18: #include <sys/ioctl.h>
                     19: #include <sys/audioio.h>
                     20: #include <fcntl.h>
                     21: #include <limits.h>
1.1       provos     22: #include <stdio.h>
1.3       provos     23: #include <stdlib.h>
1.1       provos     24: #include <unistd.h>
                     25: #include <string.h>
1.31      ratchov    26: #include <err.h>
1.1       provos     27:
1.31      ratchov    28: /*
                     29:  * Default bytes per sample for the given bits per sample.
                     30:  */
                     31: #define BPS(bits) (((bits) <= 8) ? 1 : (((bits) <= 16) ? 2 : 4))
1.1       provos     32:
1.31      ratchov    33: struct audio_device rname;
                     34: struct audio_status rstatus;
                     35: struct audio_swpar rpar, wpar;
                     36: struct audio_pos rpos;
1.30      ratchov    37:
1.1       provos     38: struct field {
1.31      ratchov    39:        char *name;
                     40:        void *raddr, *waddr;
                     41: #define MODE   0
                     42: #define NUM    1
                     43: #define STR    2
                     44: #define ENC    3
                     45:        int type;
1.45    ! ratchov    46:        int show;
1.31      ratchov    47:        int set;
1.1       provos     48: } fields[] = {
1.31      ratchov    49:        {"name",                &rname.name,            NULL,           STR},
                     50:        {"mode",                &rstatus.mode,          NULL,           MODE},
                     51:        {"pause",               &rstatus.pause,         NULL,           NUM},
                     52:        {"active",              &rstatus.active,        NULL,           NUM},
                     53:        {"nblks",               &rpar.nblks,            &wpar.nblks,    NUM},
                     54:        {"blksz",               &rpar.round,            &wpar.round,    NUM},
                     55:        {"rate",                &rpar.rate,             &wpar.rate,     NUM},
                     56:        {"encoding",            &rpar,                  &wpar,          ENC},
                     57:        {"play.channels",       &rpar.pchan,            &wpar.pchan,    NUM},
                     58:        {"play.bytes",          &rpos.play_pos,         NULL,           NUM},
                     59:        {"play.errors",         &rpos.play_xrun,        NULL,           NUM},
                     60:        {"record.channels",     &rpar.rchan,            &wpar.rchan,    NUM},
                     61:        {"record.bytes",        &rpos.rec_pos,          NULL,           NUM},
                     62:        {"record.errors",       &rpos.rec_xrun,         NULL,           NUM},
                     63:        {NULL,                  NULL,                   0}
1.1       provos     64: };
                     65:
1.32      ratchov    66: const char usagestr[] =
1.45    ! ratchov    67:        "usage: audioctl [-f file] [-w wait_sec]\n"
        !            68:        "       audioctl [-n] [-f file] [-w wait_sec] name ...\n"
1.33      jmc        69:        "       audioctl [-nq] [-f file] name=value ...\n";
1.1       provos     70:
1.45    ! ratchov    71: int fd, show_names = 1, quiet = 0, wait_sec = 0;
1.39      ratchov    72:
1.31      ratchov    73: /*
                     74:  * parse encoding string (examples: s8, u8, s16, s16le, s24be ...)
1.44      jmc        75:  * and fill encoding fields of audio_swpar structure
1.31      ratchov    76:  */
                     77: int
                     78: strtoenc(struct audio_swpar *ap, char *p)
1.1       provos     79: {
1.31      ratchov    80:        /* expect "s" or "u" (signedness) */
                     81:        if (*p == 's')
                     82:                ap->sig = 1;
                     83:        else if (*p == 'u')
                     84:                ap->sig = 0;
                     85:        else
                     86:                return 0;
                     87:        p++;
                     88:
                     89:        /* expect 1-2 decimal digits (bits per sample) */
                     90:        ap->bits = 0;
                     91:        while (*p >= '0' && *p <= '9') {
                     92:                ap->bits = (ap->bits * 10) + *p++ - '0';
                     93:                if (ap->bits > 32)
                     94:                        return 0;
                     95:        }
                     96:        if (ap->bits < 8)
                     97:                return 0;
1.1       provos     98:
1.31      ratchov    99:        /* set defaults as next tokens are optional */
                    100:        ap->bps = BPS(ap->bits);
                    101:        ap->le = (BYTE_ORDER == LITTLE_ENDIAN);
                    102:        ap->msb = 1;
                    103:        if (*p == '\0')
                    104:                return 1;
                    105:
                    106:        /* expect "le" or "be" (endianness) */
                    107:        if (p[0] == 'l' && p[1] == 'e')
                    108:                ap->le = 1;
                    109:        else if (p[0] == 'b' && p[1] == 'e')
                    110:                ap->le = 0;
                    111:        else
                    112:                return 0;
                    113:        p += 2;
                    114:        if (*p == '\0')
                    115:                return 1;
                    116:
                    117:        /* expect 1 decimal digit (number of bytes) */
                    118:        if (*p < '0' || *p > '9')
                    119:                return 0;
                    120:        ap->bps = *p - '0';
                    121:        if (ap->bps < ((ap->bits + 7) >> 3) || ap->bps > 4)
                    122:                return 0;
                    123:        if (*++p == '\0')
                    124:                return 1;
                    125:
                    126:        /* expect "msb" or "lsb" (alignment) */
                    127:        if (p[0] == 'm' && p[1] == 's' && p[2] == 'b')
                    128:                ap->msb = 1;
                    129:        else if (p[0] == 'l' && p[1] == 's' && p[2] == 'b')
                    130:                ap->msb = 0;
                    131:        else if (*p == '\0')
                    132:                return 1;
                    133:        p += 3;
                    134:        if (*p == '\0')
                    135:                return 1;
1.1       provos    136:
1.31      ratchov   137:        /* must be no additional junk */
                    138:        return 0;
1.20      ratchov   139: }
                    140:
                    141: void
1.41      ratchov   142: print_field(struct field *p, void *addr)
1.1       provos    143: {
1.31      ratchov   144:        int mode;
                    145:        struct audio_swpar *ap;
1.1       provos    146:
1.31      ratchov   147:        switch (p->type) {
                    148:        case NUM:
                    149:                printf("%u", *(unsigned int *)addr);
                    150:                break;
                    151:        case STR:
                    152:                printf("%s", (char *)addr);
                    153:                break;
                    154:        case MODE:
                    155:                mode = *(unsigned int *)addr;
                    156:                if (mode & AUMODE_PLAY)
                    157:                        printf("play");
                    158:                if (mode & AUMODE_RECORD) {
                    159:                        if (mode & AUMODE_PLAY)
                    160:                                printf(",");
                    161:                        printf("record");
1.20      ratchov   162:                }
1.1       provos    163:                break;
                    164:        case ENC:
1.31      ratchov   165:                ap = addr;
                    166:                printf("%s%u", ap->sig ? "s" : "u", ap->bits);
                    167:                if (ap->bps == 1)
                    168:                        break;
                    169:                printf("%s", ap->le ? "le" : "be");
                    170:                if (ap->bps != BPS(ap->bits) || ap->bits < ap->bps * 8) {
                    171:                        printf("%u", ap->bps);
                    172:                        if (ap->bits < ap->bps * 8)
                    173:                                printf("%s", ap->msb ? "msb" : "lsb");
1.20      ratchov   174:                }
1.1       provos    175:        }
                    176: }
                    177:
                    178: void
1.41      ratchov   179: parse_field(struct field *f, void *addr, char *p)
1.1       provos    180: {
1.31      ratchov   181:        const char *strerr;
1.1       provos    182:
1.31      ratchov   183:        switch (f->type) {
                    184:        case NUM:
                    185:                *(unsigned int *)addr = strtonum(p, 0, UINT_MAX, &strerr);
                    186:                if (strerr)
                    187:                        errx(1, "%s: %s", p, strerr);
                    188:                break;
                    189:        case ENC:
                    190:                if (!strtoenc((struct audio_swpar *)addr, p))
                    191:                        errx(1, "%s: bad encoding", p);
1.1       provos    192:        }
                    193: }
                    194:
1.39      ratchov   195: void
                    196: audio_main(int argc, char **argv)
1.1       provos    197: {
1.31      ratchov   198:        struct field *f;
1.39      ratchov   199:        char *lhs, *rhs;
                    200:        int set = 0;
1.31      ratchov   201:
                    202:        if (argc == 0) {
1.45    ! ratchov   203:                for (f = fields; f->name != NULL; f++)
        !           204:                        f->show = 1;
1.31      ratchov   205:        }
                    206:        AUDIO_INITPAR(&wpar);
                    207:        for (; argc > 0; argc--, argv++) {
                    208:                lhs = *argv;
                    209:                rhs = strchr(*argv, '=');
                    210:                if (rhs)
                    211:                        *rhs++ = '\0';
                    212:                for (f = fields;; f++) {
                    213:                        if (f->name == NULL)
                    214:                                errx(1, "%s: unknown parameter", lhs);
                    215:                        if (strcmp(f->name, lhs) == 0)
                    216:                                break;
1.11      vincent   217:                }
1.31      ratchov   218:                if (rhs) {
                    219:                        if (f->waddr == NULL)
                    220:                                errx(1, "%s: is read only", f->name);
1.41      ratchov   221:                        parse_field(f, f->waddr, rhs);
1.31      ratchov   222:                        f->set = 1;
                    223:                        set = 1;
1.45    ! ratchov   224:                } else
        !           225:                        f->show = 1;
        !           226:        }
        !           227:
        !           228:        if (set && wait_sec)
        !           229:                errx(1, "Can't set variables wait_secically");
        !           230:
        !           231:        while (1) {
        !           232:                if (ioctl(fd, AUDIO_GETSTATUS, &rstatus) == -1)
        !           233:                        err(1, "AUDIO_GETSTATUS");
        !           234:                if (ioctl(fd, AUDIO_GETDEV, &rname) == -1)
        !           235:                        err(1, "AUDIO_GETDEV");
        !           236:                if (ioctl(fd, AUDIO_GETPAR, &rpar) == -1)
        !           237:                        err(1, "AUDIO_GETPAR");
        !           238:                if (ioctl(fd, AUDIO_GETPOS, &rpos) == -1)
        !           239:                        err(1, "AUDIO_GETPOS");
        !           240:                for (f = fields; f->name != NULL; f++) {
        !           241:                        if (!f->show)
        !           242:                                continue;
1.40      ratchov   243:                        if (show_names)
1.31      ratchov   244:                                printf("%s=", f->name);
1.41      ratchov   245:                        print_field(f, f->raddr);
1.31      ratchov   246:                        printf("\n");
1.30      ratchov   247:                }
1.45    ! ratchov   248:
        !           249:                if (wait_sec == 0)
        !           250:                        break;
        !           251:
        !           252:                /* ioctls are fast, we neglect drift from real-time clock */
        !           253:                sleep(wait_sec);
1.31      ratchov   254:        }
1.45    ! ratchov   255:
1.39      ratchov   256:        if (!set)
                    257:                return;
1.45    ! ratchov   258:
1.37      deraadt   259:        if (ioctl(fd, AUDIO_SETPAR, &wpar) == -1)
1.31      ratchov   260:                err(1, "AUDIO_SETPAR");
1.37      deraadt   261:        if (ioctl(fd, AUDIO_GETPAR, &wpar) == -1)
1.31      ratchov   262:                err(1, "AUDIO_GETPAR");
                    263:        for (f = fields; f->name != NULL; f++) {
                    264:                if (!f->set || quiet)
                    265:                        continue;
1.40      ratchov   266:                if (show_names) {
1.31      ratchov   267:                        printf("%s: ", f->name);
1.41      ratchov   268:                        print_field(f, f->raddr);
1.31      ratchov   269:                        printf(" -> ");
1.1       provos    270:                }
1.41      ratchov   271:                print_field(f, f->waddr);
1.31      ratchov   272:                printf("\n");
1.11      vincent   273:        }
1.39      ratchov   274: }
                    275:
                    276: int
                    277: main(int argc, char **argv)
                    278: {
                    279:        char *path = "/dev/audioctl0";
1.45    ! ratchov   280:        const char *errstr;
1.42      ratchov   281:        int c;
1.39      ratchov   282:
1.45    ! ratchov   283:        while ((c = getopt(argc, argv, "anf:qw:")) != -1) {
1.39      ratchov   284:                switch (c) {
                    285:                case 'a':       /* ignored, compat */
                    286:                        break;
                    287:                case 'n':
1.40      ratchov   288:                        show_names = 0;
1.39      ratchov   289:                        break;
                    290:                case 'f':
                    291:                        path = optarg;
                    292:                        break;
                    293:                case 'q':
                    294:                        quiet = 1;
1.45    ! ratchov   295:                        break;
        !           296:                case 'w':
        !           297:                        wait_sec = strtonum(optarg, 1, INT_MAX, &errstr);
        !           298:                        if (errstr != NULL)
        !           299:                                errx(1, "wait is %s: %s", errstr, optarg);
1.39      ratchov   300:                        break;
                    301:                default:
                    302:                        fputs(usagestr, stderr);
                    303:                        return 1;
                    304:                }
                    305:        }
                    306:        argc -= optind;
                    307:        argv += optind;
                    308:
                    309:        if (unveil(path, "w") == -1)
1.43      beck      310:                err(1, "unveil %s", path);
1.39      ratchov   311:        if (unveil(NULL, NULL) == -1)
                    312:                err(1, "unveil");
                    313:
                    314:        fd = open(path, O_WRONLY);
                    315:        if (fd == -1)
                    316:                err(1, "%s", path);
                    317:
                    318:        audio_main(argc, argv);
                    319:
1.34      jsg       320:        close(fd);
1.43      beck      321:        return 0;
1.1       provos    322: }