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

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