1 /* $OpenBSD: audioctl.c,v 1.21 2010/07/15 03:43:12 jakemsr 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 u_int oldval; 69 #define STRING 1 70 #define INT 2 71 #define UINT 3 72 #define P_R 4 73 #define UCHAR 6 74 #define ENC 7 75 #define PROPS 8 76 #define XINT 9 77 char flags; 78 #define READONLY 1 79 #define ALIAS 2 80 #define SET 4 81 } fields[] = { 82 { "name", &adev.name, STRING, READONLY }, 83 { "version", &adev.version, STRING, READONLY }, 84 { "config", &adev.config, STRING, READONLY }, 85 { "encodings", encbuf, STRING, READONLY }, 86 { "properties", &properties, PROPS, READONLY }, 87 { "full_duplex", &fullduplex, UINT, 0 }, 88 { "fullduplex", &fullduplex, UINT, 0 }, 89 { "blocksize", &info.blocksize, UINT, 0 }, 90 { "hiwat", &info.hiwat, UINT, 0 }, 91 { "lowat", &info.lowat, UINT, 0 }, 92 { "output_muted", &info.output_muted, UCHAR, 0 }, 93 { "monitor_gain", &info.monitor_gain, UINT, 0 }, 94 { "mode", &info.mode, P_R, READONLY }, 95 { "play.rate", &info.play.sample_rate, UINT, 0 }, 96 { "play.sample_rate", &info.play.sample_rate, UINT, ALIAS }, 97 { "play.channels", &info.play.channels, UINT, 0 }, 98 { "play.precision", &info.play.precision, UINT, 0 }, 99 { "play.bps", &info.play.bps, UINT, 0 }, 100 { "play.msb", &info.play.msb, UINT, 0 }, 101 { "play.encoding", &info.play.encoding, ENC, 0 }, 102 { "play.gain", &info.play.gain, UINT, 0 }, 103 { "play.balance", &info.play.balance, UCHAR, 0 }, 104 { "play.port", &info.play.port, XINT, 0 }, 105 { "play.avail_ports", &info.play.avail_ports, XINT, 0 }, 106 { "play.seek", &info.play.seek, UINT, READONLY }, 107 { "play.samples", &info.play.samples, UINT, READONLY }, 108 { "play.eof", &info.play.eof, UINT, READONLY }, 109 { "play.pause", &info.play.pause, UCHAR, 0 }, 110 { "play.error", &info.play.error, UCHAR, READONLY }, 111 { "play.waiting", &info.play.waiting, UCHAR, READONLY }, 112 { "play.open", &info.play.open, UCHAR, READONLY }, 113 { "play.active", &info.play.active, UCHAR, READONLY }, 114 { "play.buffer_size", &info.play.buffer_size, UINT, 0 }, 115 { "play.block_size", &info.play.block_size, UINT, 0 }, 116 { "play.errors", &perrors, INT, READONLY }, 117 { "record.rate", &info.record.sample_rate,UINT, 0 }, 118 { "record.sample_rate", &info.record.sample_rate,UINT, ALIAS }, 119 { "record.channels", &info.record.channels, UINT, 0 }, 120 { "record.precision", &info.record.precision, UINT, 0 }, 121 { "record.bps", &info.record.bps, UINT, 0 }, 122 { "record.msb", &info.record.msb, UINT, 0 }, 123 { "record.encoding", &info.record.encoding, ENC, 0 }, 124 { "record.gain", &info.record.gain, UINT, 0 }, 125 { "record.balance", &info.record.balance, UCHAR, 0 }, 126 { "record.port", &info.record.port, XINT, 0 }, 127 { "record.avail_ports", &info.record.avail_ports,XINT, 0 }, 128 { "record.seek", &info.record.seek, UINT, READONLY }, 129 { "record.samples", &info.record.samples, UINT, READONLY }, 130 { "record.eof", &info.record.eof, UINT, READONLY }, 131 { "record.pause", &info.record.pause, UCHAR, 0 }, 132 { "record.error", &info.record.error, UCHAR, READONLY }, 133 { "record.waiting", &info.record.waiting, UCHAR, READONLY }, 134 { "record.open", &info.record.open, UCHAR, READONLY }, 135 { "record.active", &info.record.active, UCHAR, READONLY }, 136 { "record.buffer_size", &info.record.buffer_size,UINT, 0 }, 137 { "record.block_size", &info.record.block_size,UINT, 0 }, 138 { "record.errors", &rerrors, INT, READONLY }, 139 { 0 } 140 }; 141 142 struct { 143 const char *ename; 144 u_int eno; 145 } encs[] = { 146 { AudioEmulaw, AUDIO_ENCODING_ULAW }, 147 { "ulaw", AUDIO_ENCODING_ULAW }, 148 { AudioEalaw, AUDIO_ENCODING_ALAW }, 149 { AudioEslinear, AUDIO_ENCODING_SLINEAR }, 150 { "linear", AUDIO_ENCODING_SLINEAR }, 151 { AudioEulinear, AUDIO_ENCODING_ULINEAR }, 152 { AudioEadpcm, AUDIO_ENCODING_ADPCM }, 153 { "ADPCM", AUDIO_ENCODING_ADPCM }, 154 { AudioEslinear_le, AUDIO_ENCODING_SLINEAR_LE }, 155 { "linear_le", AUDIO_ENCODING_SLINEAR_LE }, 156 { AudioEulinear_le, AUDIO_ENCODING_ULINEAR_LE }, 157 { AudioEslinear_be, AUDIO_ENCODING_SLINEAR_BE }, 158 { "linear_be", AUDIO_ENCODING_SLINEAR_BE }, 159 { AudioEulinear_be, AUDIO_ENCODING_ULINEAR_BE }, 160 { AudioEmpeg_l1_stream, AUDIO_ENCODING_MPEG_L1_STREAM }, 161 { AudioEmpeg_l1_packets,AUDIO_ENCODING_MPEG_L1_PACKETS }, 162 { AudioEmpeg_l1_system, AUDIO_ENCODING_MPEG_L1_SYSTEM }, 163 { AudioEmpeg_l2_stream, AUDIO_ENCODING_MPEG_L2_STREAM }, 164 { AudioEmpeg_l2_packets,AUDIO_ENCODING_MPEG_L2_PACKETS }, 165 { AudioEmpeg_l2_system, AUDIO_ENCODING_MPEG_L2_SYSTEM }, 166 { 0 } 167 }; 168 169 static struct { 170 const char *name; 171 u_int prop; 172 } props[] = { 173 { "full_duplex", AUDIO_PROP_FULLDUPLEX }, 174 { "mmap", AUDIO_PROP_MMAP }, 175 { "independent", AUDIO_PROP_INDEPENDENT }, 176 { 0 } 177 }; 178 179 struct field * 180 findfield(char *name) 181 { 182 int i; 183 for (i = 0; fields[i].name; i++) 184 if (strcmp(fields[i].name, name) == 0) 185 return &fields[i]; 186 return (0); 187 } 188 189 void 190 prval(u_int format, void *valp) 191 { 192 u_int v; 193 const char *cm; 194 int i; 195 196 switch (format) { 197 case STRING: 198 fprintf(out, "%s", (char *)valp); 199 break; 200 case INT: 201 fprintf(out, "%d", *(int *)valp); 202 break; 203 case UINT: 204 fprintf(out, "%u", *(u_int *)valp); 205 break; 206 case XINT: 207 fprintf(out, "0x%x", *(u_int *)valp); 208 break; 209 case UCHAR: 210 fprintf(out, "%u", *(u_char *)valp); 211 break; 212 case P_R: 213 v = *(u_int *)valp; 214 cm = ""; 215 if (v & AUMODE_PLAY) { 216 if (v & AUMODE_PLAY_ALL) 217 fprintf(out, "play"); 218 else 219 fprintf(out, "playsync"); 220 cm = ","; 221 } 222 if (v & AUMODE_RECORD) 223 fprintf(out, "%srecord", cm); 224 break; 225 case ENC: 226 v = *(u_int *)valp; 227 for (i = 0; encs[i].ename; i++) 228 if (encs[i].eno == v) 229 break; 230 if (encs[i].ename) 231 fprintf(out, "%s", encs[i].ename); 232 else 233 fprintf(out, "%u", v); 234 break; 235 case PROPS: 236 v = *(u_int *)valp; 237 for (cm = "", i = 0; props[i].name; i++) { 238 if (v & props[i].prop) { 239 fprintf(out, "%s%s", cm, props[i].name); 240 cm = ","; 241 } 242 } 243 break; 244 default: 245 errx(1, "Invalid print format."); 246 } 247 } 248 249 void 250 prfield(struct field *p, const char *sep) 251 { 252 if (sep) { 253 fprintf(out, "%s", p->name); 254 if (p->flags & SET) { 255 fprintf(out, "%s", ": "); 256 prval(p->format, &p->oldval); 257 fprintf(out, " -> "); 258 } else 259 fprintf(out, "%s", sep); 260 } 261 prval(p->format, p->valp); 262 fprintf(out, "\n"); 263 } 264 265 void 266 rdfield(struct field *p, char *q) 267 { 268 int i; 269 u_int u; 270 271 switch (p->format) { 272 case UINT: 273 p->oldval = *(u_int *)p->valp; 274 if (sscanf(q, "%u", (unsigned int *)p->valp) != 1) { 275 warnx("Bad number %s", q); 276 return; 277 } 278 break; 279 case UCHAR: 280 *(char *)&p->oldval = *(u_char *)p->valp; 281 if (sscanf(q, "%u", &u) != 1) { 282 warnx("Bad number %s", q); 283 return; 284 } 285 *(u_char *)p->valp = u; 286 break; 287 case XINT: 288 p->oldval = *(u_int *)p->valp; 289 if (sscanf(q, "0x%x", (unsigned int *)p->valp) != 1 && 290 sscanf(q, "%x", (unsigned int *)p->valp) != 1) { 291 warnx("Bad number %s", q); 292 return; 293 } 294 break; 295 case ENC: 296 p->oldval = *(u_int *)p->valp; 297 for (i = 0; encs[i].ename; i++) 298 if (strcmp(encs[i].ename, q) == 0) 299 break; 300 if (encs[i].ename) 301 *(u_int*)p->valp = encs[i].eno; 302 else { 303 warnx("Unknown encoding: %s", q); 304 return; 305 } 306 break; 307 default: 308 errx(1, "Invalid read format."); 309 } 310 p->flags |= SET; 311 } 312 313 void 314 getinfo(int fd) 315 { 316 int pos = 0, i = 0; 317 318 if (ioctl(fd, AUDIO_GETDEV, &adev) < 0) 319 err(1, "AUDIO_GETDEV"); 320 for (;;) { 321 audio_encoding_t enc; 322 enc.index = i++; 323 if (ioctl(fd, AUDIO_GETENC, &enc) < 0) 324 break; 325 if (pos) 326 encbuf[pos++] = ','; 327 snprintf(encbuf+pos, sizeof(encbuf)-pos, "%s:%d:%d:%d%s", 328 enc.name, enc.precision, enc.bps, enc.msb, 329 enc.flags & AUDIO_ENCODINGFLAG_EMULATED ? "*" : ""); 330 pos += strlen(encbuf+pos); 331 } 332 if (ioctl(fd, AUDIO_GETFD, &fullduplex) < 0) 333 err(1, "AUDIO_GETFD"); 334 if (ioctl(fd, AUDIO_GETPROPS, &properties) < 0) 335 err(1, "AUDIO_GETPROPS"); 336 if (ioctl(fd, AUDIO_PERROR, &perrors) < 0) 337 err(1, "AUDIO_PERROR"); 338 if (ioctl(fd, AUDIO_RERROR, &rerrors) < 0) 339 err(1, "AUDIO_RERROR"); 340 if (ioctl(fd, AUDIO_GETINFO, &info) < 0) 341 err(1, "AUDIO_GETINFO"); 342 } 343 344 void 345 usage(void) 346 { 347 extern char *__progname; /* from crt0.o */ 348 349 fprintf(stderr, 350 "usage: %s [-an] [-f file]\n" 351 " %s [-n] [-f file] name ...\n" 352 " %s [-n] [-f file] name=value ...\n", 353 __progname, __progname, __progname); 354 355 exit(1); 356 } 357 358 int 359 main(int argc, char **argv) 360 { 361 int fd, i, ch; 362 int aflag = 0, canwrite, writeinfo = 0; 363 struct stat dstat, ostat; 364 struct field *p; 365 const char *file; 366 const char *sep = "="; 367 368 if ((file = getenv("AUDIOCTLDEVICE")) == 0 || *file == '\0') 369 file = "/dev/audioctl"; 370 371 while ((ch = getopt(argc, argv, "af:nw")) != -1) { 372 switch (ch) { 373 case 'a': 374 aflag++; 375 break; 376 case 'w': 377 /* backward compatibility */ 378 break; 379 case 'n': 380 sep = 0; 381 break; 382 case 'f': 383 file = optarg; 384 break; 385 default: 386 usage(); 387 } 388 } 389 argc -= optind; 390 argv += optind; 391 392 if (argc == 0) 393 aflag++; 394 395 if ((fd = open(file, O_RDWR)) < 0) { 396 if ((fd = open(file, O_RDONLY)) < 0) 397 err(1, "%s", file); 398 canwrite = 0; 399 } else 400 canwrite = 1; 401 402 /* Check if stdout is the same device as the audio device. */ 403 if (fstat(fd, &dstat) < 0) 404 err(1, "fstat au"); 405 if (fstat(STDOUT_FILENO, &ostat) < 0) 406 err(1, "fstat stdout"); 407 if (S_ISCHR(dstat.st_mode) && S_ISCHR(ostat.st_mode) && 408 major(dstat.st_dev) == major(ostat.st_dev) && 409 minor(dstat.st_dev) == minor(ostat.st_dev)) 410 /* We can't write to stdout so use stderr */ 411 out = stderr; 412 413 if (!argc && !aflag) 414 usage(); 415 416 getinfo(fd); 417 418 if (aflag) { 419 for (i = 0; fields[i].name; i++) { 420 if (!(fields[i].flags & ALIAS)) { 421 prfield(&fields[i], sep); 422 } 423 } 424 } else { 425 while (argc--) { 426 char *q; 427 428 if ((q = strchr(*argv, '=')) != NULL) { 429 *q++ = 0; 430 p = findfield(*argv); 431 if (p == 0) 432 warnx("field `%s' does not exist", *argv); 433 else { 434 if (!canwrite) 435 errx(1, "%s: permission denied", 436 *argv); 437 if (p->flags & READONLY) 438 warnx("`%s' is read only", *argv); 439 else { 440 rdfield(p, q); 441 if (p->valp == &fullduplex) 442 if (ioctl(fd, AUDIO_SETFD, 443 &fullduplex) < 0) 444 err(1, "set failed"); 445 } 446 writeinfo = 1; 447 } 448 } else { 449 p = findfield(*argv); 450 if (p == 0) 451 warnx("field %s does not exist", *argv); 452 else { 453 prfield(p, sep); 454 } 455 } 456 argv++; 457 } 458 if (writeinfo && ioctl(fd, AUDIO_SETINFO, &info) < 0) 459 err(1, "set failed"); 460 getinfo(fd); 461 for (i = 0; fields[i].name; i++) { 462 if (fields[i].flags & SET) { 463 prfield(&fields[i], sep); 464 } 465 } 466 } 467 exit(0); 468 } 469