1 /* $OpenBSD: audioctl.c,v 1.27 2015/05/16 12:51:24 ratchov Exp $ */ 2 /* $NetBSD: audioctl.c,v 1.14 1998/04/27 16:55:23 augustss Exp $ */ 3 4 /* 5 * Copyright (c) 1997 The NetBSD Foundation, Inc. 6 * All rights reserved. 7 * 8 * Author: Lennart Augustsson 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 /* 33 * audioctl(1) - a program to control audio device. 34 */ 35 36 #include <stdio.h> 37 #include <stdlib.h> 38 #include <fcntl.h> 39 #include <err.h> 40 #include <unistd.h> 41 #include <string.h> 42 #include <sys/types.h> 43 #include <sys/stat.h> 44 #include <sys/ioctl.h> 45 #include <sys/audioio.h> 46 47 struct field *findfield(char *name); 48 void prfield(struct field *p, const char *sep); 49 void rdfield(struct field *p, char *q); 50 void getinfo(int fd); 51 void usage(void); 52 int main(int argc, char **argv); 53 54 FILE *out = stdout; 55 56 audio_device_t adev; 57 58 audio_info_t info; 59 60 char encbuf[1000]; 61 62 int properties, fullduplex, perrors, rerrors; 63 64 struct field { 65 const char *name; 66 void *valp; 67 int format; 68 #define STRING 1 69 #define INT 2 70 #define UINT 3 71 #define P_R 4 72 #define UCHAR 6 73 #define ENC 7 74 #define PROPS 8 75 #define XINT 9 76 char flags; 77 #define READONLY 1 78 #define ALIAS 2 79 #define SET 4 80 u_int oldval; 81 } fields[] = { 82 { "name", &adev.name, STRING, READONLY }, 83 { "encodings", encbuf, STRING, READONLY }, 84 { "properties", &properties, PROPS, READONLY }, 85 { "hiwat", &info.hiwat, UINT, 0 }, 86 { "lowat", &info.lowat, UINT, 0 }, 87 { "mode", &info.mode, P_R, READONLY }, 88 { "play.rate", &info.play.sample_rate, UINT, 0 }, 89 { "play.sample_rate", &info.play.sample_rate, UINT, ALIAS }, 90 { "play.channels", &info.play.channels, UINT, 0 }, 91 { "play.precision", &info.play.precision, UINT, 0 }, 92 { "play.bps", &info.play.bps, UINT, 0 }, 93 { "play.msb", &info.play.msb, UINT, 0 }, 94 { "play.encoding", &info.play.encoding, ENC, 0 }, 95 { "play.samples", &info.play.samples, UINT, READONLY }, 96 { "play.pause", &info.play.pause, UCHAR, 0 }, 97 { "play.active", &info.play.active, UCHAR, READONLY }, 98 { "play.block_size", &info.play.block_size, UINT, 0 }, 99 { "play.errors", &perrors, INT, READONLY }, 100 { "record.rate", &info.record.sample_rate,UINT, 0 }, 101 { "record.sample_rate", &info.record.sample_rate,UINT, ALIAS }, 102 { "record.channels", &info.record.channels, UINT, 0 }, 103 { "record.precision", &info.record.precision, UINT, 0 }, 104 { "record.bps", &info.record.bps, UINT, 0 }, 105 { "record.msb", &info.record.msb, UINT, 0 }, 106 { "record.encoding", &info.record.encoding, ENC, 0 }, 107 { "record.samples", &info.record.samples, UINT, READONLY }, 108 { "record.pause", &info.record.pause, UCHAR, 0 }, 109 { "record.active", &info.record.active, UCHAR, READONLY }, 110 { "record.block_size", &info.record.block_size,UINT, 0 }, 111 { "record.errors", &rerrors, INT, READONLY }, 112 { 0 } 113 }; 114 115 struct { 116 const char *ename; 117 u_int eno; 118 } encs[] = { 119 { AudioEmulaw, AUDIO_ENCODING_ULAW }, 120 { "ulaw", AUDIO_ENCODING_ULAW }, 121 { AudioEalaw, AUDIO_ENCODING_ALAW }, 122 { AudioEslinear, AUDIO_ENCODING_SLINEAR }, 123 { "linear", AUDIO_ENCODING_SLINEAR }, 124 { AudioEulinear, AUDIO_ENCODING_ULINEAR }, 125 { AudioEslinear_le, AUDIO_ENCODING_SLINEAR_LE }, 126 { "linear_le", AUDIO_ENCODING_SLINEAR_LE }, 127 { AudioEulinear_le, AUDIO_ENCODING_ULINEAR_LE }, 128 { AudioEslinear_be, AUDIO_ENCODING_SLINEAR_BE }, 129 { "linear_be", AUDIO_ENCODING_SLINEAR_BE }, 130 { AudioEulinear_be, AUDIO_ENCODING_ULINEAR_BE }, 131 { 0 } 132 }; 133 134 static struct { 135 const char *name; 136 u_int prop; 137 } props[] = { 138 { "full_duplex", AUDIO_PROP_FULLDUPLEX }, 139 { "mmap", AUDIO_PROP_MMAP }, 140 { "independent", AUDIO_PROP_INDEPENDENT }, 141 { 0 } 142 }; 143 144 struct field * 145 findfield(char *name) 146 { 147 int i; 148 for (i = 0; fields[i].name; i++) 149 if (strcmp(fields[i].name, name) == 0) 150 return &fields[i]; 151 return (0); 152 } 153 154 static void 155 prval(u_int format, void *valp) 156 { 157 u_int v; 158 const char *cm; 159 int i; 160 161 switch (format) { 162 case STRING: 163 fprintf(out, "%s", (char *)valp); 164 break; 165 case INT: 166 fprintf(out, "%d", *(int *)valp); 167 break; 168 case UINT: 169 fprintf(out, "%u", *(u_int *)valp); 170 break; 171 case XINT: 172 fprintf(out, "0x%x", *(u_int *)valp); 173 break; 174 case UCHAR: 175 fprintf(out, "%u", *(u_char *)valp); 176 break; 177 case P_R: 178 v = *(u_int *)valp; 179 cm = ""; 180 if (v & AUMODE_PLAY) { 181 fprintf(out, "play"); 182 cm = ","; 183 } 184 if (v & AUMODE_RECORD) 185 fprintf(out, "%srecord", cm); 186 break; 187 case ENC: 188 v = *(u_int *)valp; 189 for (i = 0; encs[i].ename; i++) 190 if (encs[i].eno == v) 191 break; 192 if (encs[i].ename) 193 fprintf(out, "%s", encs[i].ename); 194 else 195 fprintf(out, "%u", v); 196 break; 197 case PROPS: 198 v = *(u_int *)valp; 199 for (cm = "", i = 0; props[i].name; i++) { 200 if (v & props[i].prop) { 201 fprintf(out, "%s%s", cm, props[i].name); 202 cm = ","; 203 } 204 } 205 break; 206 default: 207 errx(1, "Invalid print format."); 208 } 209 } 210 211 void 212 prfield(struct field *p, const char *sep) 213 { 214 if (sep) { 215 fprintf(out, "%s", p->name); 216 if (p->flags & SET) { 217 fprintf(out, "%s", ": "); 218 prval(p->format, &p->oldval); 219 fprintf(out, " -> "); 220 } else 221 fprintf(out, "%s", sep); 222 } 223 prval(p->format, p->valp); 224 fprintf(out, "\n"); 225 } 226 227 void 228 rdfield(struct field *p, char *q) 229 { 230 int i; 231 u_int u; 232 233 switch (p->format) { 234 case UINT: 235 p->oldval = *(u_int *)p->valp; 236 if (sscanf(q, "%u", (unsigned int *)p->valp) != 1) { 237 warnx("Bad number %s", q); 238 return; 239 } 240 break; 241 case UCHAR: 242 *(char *)&p->oldval = *(u_char *)p->valp; 243 if (sscanf(q, "%u", &u) != 1) { 244 warnx("Bad number %s", q); 245 return; 246 } 247 *(u_char *)p->valp = u; 248 break; 249 case XINT: 250 p->oldval = *(u_int *)p->valp; 251 if (sscanf(q, "0x%x", (unsigned int *)p->valp) != 1 && 252 sscanf(q, "%x", (unsigned int *)p->valp) != 1) { 253 warnx("Bad number %s", q); 254 return; 255 } 256 break; 257 case ENC: 258 p->oldval = *(u_int *)p->valp; 259 for (i = 0; encs[i].ename; i++) 260 if (strcmp(encs[i].ename, q) == 0) 261 break; 262 if (encs[i].ename) 263 *(u_int*)p->valp = encs[i].eno; 264 else { 265 warnx("Unknown encoding: %s", q); 266 return; 267 } 268 break; 269 default: 270 errx(1, "Invalid read format."); 271 } 272 p->flags |= SET; 273 } 274 275 void 276 getinfo(int fd) 277 { 278 int pos = 0, i = 0; 279 280 if (ioctl(fd, AUDIO_GETDEV, &adev) < 0) 281 err(1, "AUDIO_GETDEV"); 282 for (;;) { 283 audio_encoding_t enc; 284 enc.index = i++; 285 if (ioctl(fd, AUDIO_GETENC, &enc) < 0) 286 break; 287 if (pos) 288 encbuf[pos++] = ','; 289 snprintf(encbuf+pos, sizeof(encbuf)-pos, "%s:%d:%d:%d%s", 290 enc.name, enc.precision, enc.bps, enc.msb, 291 enc.flags & AUDIO_ENCODINGFLAG_EMULATED ? "*" : ""); 292 pos += strlen(encbuf+pos); 293 } 294 if (ioctl(fd, AUDIO_GETFD, &fullduplex) < 0) 295 err(1, "AUDIO_GETFD"); 296 if (ioctl(fd, AUDIO_GETPROPS, &properties) < 0) 297 err(1, "AUDIO_GETPROPS"); 298 if (ioctl(fd, AUDIO_PERROR, &perrors) < 0) 299 err(1, "AUDIO_PERROR"); 300 if (ioctl(fd, AUDIO_RERROR, &rerrors) < 0) 301 err(1, "AUDIO_RERROR"); 302 if (ioctl(fd, AUDIO_GETINFO, &info) < 0) 303 err(1, "AUDIO_GETINFO"); 304 } 305 306 void 307 usage(void) 308 { 309 extern char *__progname; /* from crt0.o */ 310 311 fprintf(stderr, 312 "usage: %s [-an] [-f file]\n" 313 " %s [-n] [-f file] name ...\n" 314 " %s [-n] [-f file] name=value ...\n", 315 __progname, __progname, __progname); 316 317 exit(1); 318 } 319 320 int 321 main(int argc, char **argv) 322 { 323 int fd, i, ch; 324 int aflag = 0, canwrite, writeinfo = 0; 325 struct stat dstat, ostat; 326 struct field *p; 327 const char *file; 328 const char *sep = "="; 329 330 if ((file = getenv("AUDIOCTLDEVICE")) == 0 || *file == '\0') 331 file = "/dev/audioctl"; 332 333 while ((ch = getopt(argc, argv, "af:nw")) != -1) { 334 switch (ch) { 335 case 'a': 336 aflag = 1; 337 break; 338 case 'w': 339 /* backward compatibility */ 340 break; 341 case 'n': 342 sep = 0; 343 break; 344 case 'f': 345 file = optarg; 346 break; 347 default: 348 usage(); 349 } 350 } 351 argc -= optind; 352 argv += optind; 353 354 if (argc == 0) 355 aflag = 1; 356 357 if ((fd = open(file, O_RDWR)) < 0) { 358 if ((fd = open(file, O_RDONLY)) < 0) 359 err(1, "%s", file); 360 canwrite = 0; 361 } else 362 canwrite = 1; 363 364 /* Check if stdout is the same device as the audio device. */ 365 if (fstat(fd, &dstat) < 0) 366 err(1, "fstat au"); 367 if (fstat(STDOUT_FILENO, &ostat) < 0) 368 err(1, "fstat stdout"); 369 if (S_ISCHR(dstat.st_mode) && S_ISCHR(ostat.st_mode) && 370 major(dstat.st_dev) == major(ostat.st_dev) && 371 minor(dstat.st_dev) == minor(ostat.st_dev)) 372 /* We can't write to stdout so use stderr */ 373 out = stderr; 374 375 if (!argc && !aflag) 376 usage(); 377 378 getinfo(fd); 379 380 if (aflag) { 381 for (i = 0; fields[i].name; i++) { 382 if (!(fields[i].flags & ALIAS)) { 383 prfield(&fields[i], sep); 384 } 385 } 386 } else { 387 while (argc--) { 388 char *q; 389 390 if ((q = strchr(*argv, '=')) != NULL) { 391 *q++ = 0; 392 p = findfield(*argv); 393 if (p == 0) 394 warnx("field `%s' does not exist", *argv); 395 else { 396 if (!canwrite) 397 errx(1, "%s: permission denied", 398 *argv); 399 if (p->flags & READONLY) 400 warnx("`%s' is read only", *argv); 401 else { 402 rdfield(p, q); 403 if (p->valp == &fullduplex) 404 if (ioctl(fd, AUDIO_SETFD, 405 &fullduplex) < 0) 406 err(1, "set failed"); 407 } 408 writeinfo = 1; 409 } 410 } else { 411 p = findfield(*argv); 412 if (p == 0) 413 warnx("field %s does not exist", *argv); 414 else { 415 prfield(p, sep); 416 } 417 } 418 argv++; 419 } 420 if (writeinfo && ioctl(fd, AUDIO_SETINFO, &info) < 0) 421 err(1, "set failed"); 422 getinfo(fd); 423 for (i = 0; fields[i].name; i++) { 424 if (fields[i].flags & SET) { 425 prfield(&fields[i], sep); 426 } 427 } 428 } 429 exit(0); 430 } 431