1 /* $OpenBSD: audioctl.c,v 1.10 2003/06/21 01:39:07 deraadt 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 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by the NetBSD 21 * Foundation, Inc. and its contributors. 22 * 4. Neither the name of The NetBSD Foundation nor the names of its 23 * contributors may be used to endorse or promote products derived 24 * from this software without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36 * POSSIBILITY OF SUCH DAMAGE. 37 */ 38 39 /* 40 * audioctl(1) - a program to control audio device. 41 */ 42 43 #include <stdio.h> 44 #include <stdlib.h> 45 #include <fcntl.h> 46 #include <err.h> 47 #include <unistd.h> 48 #include <string.h> 49 #include <sys/types.h> 50 #include <sys/stat.h> 51 #include <sys/ioctl.h> 52 #include <sys/audioio.h> 53 54 struct field *findfield(char *name); 55 void prfield(struct field *p, char *sep); 56 void rdfield(struct field *p, char *q); 57 void getinfo(int fd); 58 void usage(void); 59 int main(int argc, char **argv); 60 61 FILE *out = stdout; 62 63 audio_device_t adev; 64 65 audio_info_t info; 66 67 char encbuf[1000]; 68 69 int properties, fullduplex, rerror; 70 71 struct field { 72 char *name; 73 void *valp; 74 int format; 75 #define STRING 1 76 #define INT 2 77 #define UINT 3 78 #define P_R 4 79 #define ULONG 5 80 #define UCHAR 6 81 #define ENC 7 82 #define PROPS 8 83 #define XINT 9 84 char flags; 85 #define READONLY 1 86 #define ALIAS 2 87 #define SET 4 88 } fields[] = { 89 { "name", &adev.name, STRING, READONLY }, 90 { "version", &adev.version, STRING, READONLY }, 91 { "config", &adev.config, STRING, READONLY }, 92 { "encodings", encbuf, STRING, READONLY }, 93 { "properties", &properties, PROPS, READONLY }, 94 { "full_duplex", &fullduplex, UINT, 0 }, 95 { "fullduplex", &fullduplex, UINT, 0 }, 96 { "blocksize", &info.blocksize, UINT, 0 }, 97 { "hiwat", &info.hiwat, UINT, 0 }, 98 { "lowat", &info.lowat, UINT, 0 }, 99 { "monitor_gain", &info.monitor_gain, UINT, 0 }, 100 { "mode", &info.mode, P_R, READONLY }, 101 { "play.rate", &info.play.sample_rate, UINT, 0 }, 102 { "play.sample_rate", &info.play.sample_rate, UINT, ALIAS }, 103 { "play.channels", &info.play.channels, UINT, 0 }, 104 { "play.precision", &info.play.precision, UINT, 0 }, 105 { "play.encoding", &info.play.encoding, ENC, 0 }, 106 { "play.gain", &info.play.gain, UINT, 0 }, 107 { "play.balance", &info.play.balance, UCHAR, 0 }, 108 { "play.port", &info.play.port, XINT, 0 }, 109 { "play.avail_ports", &info.play.avail_ports, XINT, 0 }, 110 { "play.seek", &info.play.seek, UINT, READONLY }, 111 { "play.samples", &info.play.samples, UINT, READONLY }, 112 { "play.eof", &info.play.eof, UINT, READONLY }, 113 { "play.pause", &info.play.pause, UCHAR, 0 }, 114 { "play.error", &info.play.error, UCHAR, READONLY }, 115 { "play.waiting", &info.play.waiting, UCHAR, READONLY }, 116 { "play.open", &info.play.open, UCHAR, READONLY }, 117 { "play.active", &info.play.active, UCHAR, READONLY }, 118 { "play.buffer_size", &info.play.buffer_size, UINT, 0 }, 119 { "record.rate", &info.record.sample_rate,UINT, 0 }, 120 { "record.sample_rate", &info.record.sample_rate,UINT, ALIAS }, 121 { "record.channels", &info.record.channels, UINT, 0 }, 122 { "record.precision", &info.record.precision, 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.errors", &rerror, INT, READONLY }, 138 { 0 } 139 }; 140 141 struct { 142 char *ename; 143 int eno; 144 } encs[] = { 145 { AudioEmulaw, AUDIO_ENCODING_ULAW }, 146 { "ulaw", AUDIO_ENCODING_ULAW }, 147 { AudioEalaw, AUDIO_ENCODING_ALAW }, 148 { AudioEslinear, AUDIO_ENCODING_SLINEAR }, 149 { "linear", AUDIO_ENCODING_SLINEAR }, 150 { AudioEulinear, AUDIO_ENCODING_ULINEAR }, 151 { AudioEadpcm, AUDIO_ENCODING_ADPCM }, 152 { "ADPCM", AUDIO_ENCODING_ADPCM }, 153 { AudioEslinear_le, AUDIO_ENCODING_SLINEAR_LE }, 154 { "linear_le", AUDIO_ENCODING_SLINEAR_LE }, 155 { AudioEulinear_le, AUDIO_ENCODING_ULINEAR_LE }, 156 { AudioEslinear_be, AUDIO_ENCODING_SLINEAR_BE }, 157 { "linear_be", AUDIO_ENCODING_SLINEAR_BE }, 158 { AudioEulinear_be, AUDIO_ENCODING_ULINEAR_BE }, 159 { AudioEmpeg_l1_stream, AUDIO_ENCODING_MPEG_L1_STREAM }, 160 { AudioEmpeg_l1_packets,AUDIO_ENCODING_MPEG_L1_PACKETS }, 161 { AudioEmpeg_l1_system, AUDIO_ENCODING_MPEG_L1_SYSTEM }, 162 { AudioEmpeg_l2_stream, AUDIO_ENCODING_MPEG_L2_STREAM }, 163 { AudioEmpeg_l2_packets,AUDIO_ENCODING_MPEG_L2_PACKETS }, 164 { AudioEmpeg_l2_system, AUDIO_ENCODING_MPEG_L2_SYSTEM }, 165 { 0 } 166 }; 167 168 static struct { 169 char *name; 170 u_int prop; 171 } props[] = { 172 { "full_duplex", AUDIO_PROP_FULLDUPLEX }, 173 { "mmap", AUDIO_PROP_MMAP }, 174 { "independent", AUDIO_PROP_INDEPENDENT }, 175 { 0 } 176 }; 177 178 struct field * 179 findfield(char *name) 180 { 181 int i; 182 for(i = 0; fields[i].name; i++) 183 if (strcmp(fields[i].name, name) == 0) 184 return &fields[i]; 185 return (0); 186 } 187 188 void 189 prfield(struct field *p, char *sep) 190 { 191 u_int v; 192 char *cm; 193 int i; 194 195 if (sep) 196 fprintf(out, "%s%s", p->name, sep); 197 switch(p->format) { 198 case STRING: 199 fprintf(out, "%s", (char*)p->valp); 200 break; 201 case INT: 202 fprintf(out, "%d", *(int*)p->valp); 203 break; 204 case UINT: 205 fprintf(out, "%u", *(u_int*)p->valp); 206 break; 207 case XINT: 208 fprintf(out, "0x%x", *(u_int*)p->valp); 209 break; 210 case UCHAR: 211 fprintf(out, "%u", *(u_char*)p->valp); 212 break; 213 case ULONG: 214 fprintf(out, "%lu", *(u_long*)p->valp); 215 break; 216 case P_R: 217 v = *(u_int*)p->valp; 218 cm = ""; 219 if (v & AUMODE_PLAY) { 220 if (v & AUMODE_PLAY_ALL) 221 fprintf(out, "play"); 222 else 223 fprintf(out, "playsync"); 224 cm = ","; 225 } 226 if (v & AUMODE_RECORD) 227 fprintf(out, "%srecord", cm); 228 break; 229 case ENC: 230 v = *(u_int*)p->valp; 231 for(i = 0; encs[i].ename; i++) 232 if (encs[i].eno == v) 233 break; 234 if (encs[i].ename) 235 fprintf(out, "%s", encs[i].ename); 236 else 237 fprintf(out, "%u", v); 238 break; 239 case PROPS: 240 v = *(u_int*)p->valp; 241 for (cm = "", i = 0; props[i].name; i++) { 242 if (v & props[i].prop) { 243 fprintf(out, "%s%s", cm, props[i].name); 244 cm = ","; 245 } 246 } 247 break; 248 default: 249 errx(1, "Invalid print format."); 250 } 251 } 252 253 void 254 rdfield(struct field *p, char *q) 255 { 256 int i; 257 u_int u; 258 259 switch(p->format) { 260 case UINT: 261 if (sscanf(q, "%u", (unsigned int *)p->valp) != 1) 262 warnx("Bad number %s", q); 263 break; 264 case UCHAR: 265 if (sscanf(q, "%u", &u) != 1) 266 warnx("Bad number %s", q); 267 else 268 *(u_char *)p->valp = u; 269 break; 270 case XINT: 271 if (sscanf(q, "0x%x", (unsigned int *)p->valp) != 1 && 272 sscanf(q, "%x", (unsigned int *)p->valp) != 1) 273 warnx("Bad number %s", q); 274 break; 275 case ENC: 276 for(i = 0; encs[i].ename; i++) 277 if (strcmp(encs[i].ename, q) == 0) 278 break; 279 if (encs[i].ename) 280 *(u_int*)p->valp = encs[i].eno; 281 else 282 warnx("Unknown encoding: %s", q); 283 break; 284 default: 285 errx(1, "Invalid read format."); 286 } 287 p->flags |= SET; 288 } 289 290 void 291 getinfo(int fd) 292 { 293 int pos = 0, i = 0; 294 295 if (ioctl(fd, AUDIO_GETDEV, &adev) < 0) 296 err(1, "AUDIO_GETDEV"); 297 for(;;) { 298 audio_encoding_t enc; 299 enc.index = i++; 300 if (ioctl(fd, AUDIO_GETENC, &enc) < 0) 301 break; 302 if (pos) 303 encbuf[pos++] = ','; 304 snprintf(encbuf+pos, sizeof(encbuf)-pos, "%s:%d%s", 305 enc.name, enc.precision, 306 enc.flags & AUDIO_ENCODINGFLAG_EMULATED ? "*" : ""); 307 pos += strlen(encbuf+pos); 308 } 309 if (ioctl(fd, AUDIO_GETFD, &fullduplex) < 0) 310 err(1, "AUDIO_GETFD"); 311 if (ioctl(fd, AUDIO_GETPROPS, &properties) < 0) 312 err(1, "AUDIO_GETPROPS"); 313 if (ioctl(fd, AUDIO_RERROR, &rerror) < 0) 314 err(1, "AUDIO_RERROR"); 315 if (ioctl(fd, AUDIO_GETINFO, &info) < 0) 316 err(1, "AUDIO_GETINFO"); 317 } 318 319 void 320 usage(void) 321 { 322 extern char *__progname; /* from crt0.o */ 323 324 fprintf(stderr, 325 "usage: %s [-f file] [-n] -a\n" 326 " %s [-f file] [-n] name [...]\n" 327 " %s [-f file] [-n] -w name=value [...]\n", __progname, 328 __progname, __progname); 329 330 exit(1); 331 } 332 333 int 334 main(int argc, char **argv) 335 { 336 int fd, i, ch; 337 int aflag = 0, wflag = 0; 338 struct stat dstat, ostat; 339 char *file; 340 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 wflag++; 352 break; 353 case 'n': 354 sep = 0; 355 break; 356 case 'f': 357 file = optarg; 358 break; 359 case '?': 360 default: 361 usage(); 362 } 363 } 364 argc -= optind; 365 argv += optind; 366 367 if ((fd = open(file, wflag ? O_RDWR : O_RDONLY)) < 0) 368 err(1, "%s", file); 369 370 /* Check if stdout is the same device as the audio device. */ 371 if (fstat(fd, &dstat) < 0) 372 err(1, "fstat au"); 373 if (fstat(STDOUT_FILENO, &ostat) < 0) 374 err(1, "fstat stdout"); 375 if (S_ISCHR(dstat.st_mode) && S_ISCHR(ostat.st_mode) && 376 major(dstat.st_dev) == major(ostat.st_dev) && 377 minor(dstat.st_dev) == minor(ostat.st_dev)) 378 /* We can't write to stdout so use stderr */ 379 out = stderr; 380 381 if (!wflag) 382 getinfo(fd); 383 384 if (!argc && aflag && !wflag) { 385 for(i = 0; fields[i].name; i++) { 386 if (!(fields[i].flags & ALIAS)) { 387 prfield(&fields[i], sep); 388 fprintf(out, "\n"); 389 } 390 } 391 } else if (argc > 0 && !aflag) { 392 struct field *p; 393 if (wflag) { 394 AUDIO_INITINFO(&info); 395 while(argc--) { 396 char *q; 397 398 if ((q = strchr(*argv, '='))) { 399 *q++ = 0; 400 p = findfield(*argv); 401 if (p == 0) 402 warnx("field `%s' does not exist", *argv); 403 else { 404 if (p->flags & READONLY) 405 warnx("`%s' is read only", *argv); 406 else { 407 rdfield(p, q); 408 if (p->valp == &fullduplex) 409 if (ioctl(fd, AUDIO_SETFD, &fullduplex) < 0) 410 err(1, "set failed"); 411 } 412 } 413 } else 414 warnx("No `=' in %s", *argv); 415 argv++; 416 } 417 if (ioctl(fd, AUDIO_SETINFO, &info) < 0) 418 err(1, "set failed"); 419 if (sep) { 420 getinfo(fd); 421 for(i = 0; fields[i].name; i++) { 422 if (fields[i].flags & SET) { 423 fprintf(out, "%s: -> ", fields[i].name); 424 prfield(&fields[i], 0); 425 fprintf(out, "\n"); 426 } 427 } 428 } 429 } else { 430 while(argc--) { 431 p = findfield(*argv); 432 if (p == 0) { 433 if (strchr(*argv, '=')) 434 warnx("field %s does not exist (use -w to set a variable)", *argv); 435 else 436 warnx("field %s does not exist", *argv); 437 } else { 438 prfield(p, sep); 439 fprintf(out, "\n"); 440 } 441 argv++; 442 } 443 } 444 } else 445 usage(); 446 exit(0); 447 } 448