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