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