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