1 /* $NetBSD: ctl.c,v 1.12 1997/10/19 07:44:12 augustss Exp $ */ 2 3 /* 4 * Copyright (c) 1997 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * Author: Lennart Augustsson 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. All advertising materials mentioning features or use of this software 18 * must display the following acknowledgement: 19 * This product includes software developed by the NetBSD 20 * Foundation, Inc. and its contributors. 21 * 4. Neither the name of The NetBSD Foundation nor the names of its 22 * contributors may be used to endorse or promote products derived 23 * from this software without specific prior written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 26 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 27 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 28 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 29 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 30 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 31 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 32 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 35 * POSSIBILITY OF SUCH DAMAGE. 36 */ 37 38 #include <stdio.h> 39 #include <fcntl.h> 40 #include <err.h> 41 #include <unistd.h> 42 #include <string.h> 43 #include <sys/types.h> 44 #include <sys/stat.h> 45 #include <sys/ioctl.h> 46 #include <sys/audioio.h> 47 48 struct field *findfield __P((char *name)); 49 void prfield __P((struct field *p, char *sep)); 50 void rdfield __P((struct field *p, char *q)); 51 void getinfo __P((int fd)); 52 void usage __P((void)); 53 int main __P((int argc, char **argv)); 54 55 FILE *out = stdout; 56 57 char *prog; 58 59 audio_device_t adev; 60 61 audio_info_t info; 62 63 char encbuf[1000]; 64 65 int properties, fullduplex, rerror; 66 67 struct field { 68 char *name; 69 void *valp; 70 int format; 71 #define STRING 1 72 #define INT 2 73 #define UINT 3 74 #define P_R 4 75 #define ULONG 5 76 #define UCHAR 6 77 #define ENC 7 78 #define PROPS 8 79 #define XINT 9 80 char flags; 81 #define READONLY 1 82 #define ALIAS 2 83 #define SET 4 84 } fields[] = { 85 { "name", &adev.name, STRING, READONLY }, 86 { "version", &adev.version, STRING, READONLY }, 87 { "config", &adev.config, STRING, READONLY }, 88 { "encodings", encbuf, STRING, READONLY }, 89 { "properties", &properties, PROPS, READONLY }, 90 { "full_duplex", &fullduplex, INT, 0 }, 91 { "blocksize", &info.blocksize, UINT, 0 }, 92 { "hiwat", &info.hiwat, UINT, 0 }, 93 { "lowat", &info.lowat, UINT, 0 }, 94 { "monitor_gain", &info.monitor_gain, UINT, 0 }, 95 { "mode", &info.mode, P_R, READONLY }, 96 { "play.rate", &info.play.sample_rate, UINT, 0 }, 97 { "play.sample_rate", &info.play.sample_rate, UINT, ALIAS }, 98 { "play.channels", &info.play.channels, UINT, 0 }, 99 { "play.precision", &info.play.precision, UINT, 0 }, 100 { "play.encoding", &info.play.encoding, ENC, 0 }, 101 { "play.gain", &info.play.gain, UINT, 0 }, 102 { "play.balance", &info.play.balance, UCHAR, 0 }, 103 { "play.port", &info.play.port, XINT, 0 }, 104 { "play.avail_ports", &info.play.avail_ports, XINT, 0 }, 105 { "play.seek", &info.play.seek, ULONG, READONLY }, 106 { "play.samples", &info.play.samples, UINT, READONLY }, 107 { "play.eof", &info.play.eof, UINT, READONLY }, 108 { "play.pause", &info.play.pause, UCHAR, 0 }, 109 { "play.error", &info.play.error, UCHAR, READONLY }, 110 { "play.waiting", &info.play.waiting, UCHAR, READONLY }, 111 { "play.open", &info.play.open, UCHAR, READONLY }, 112 { "play.active", &info.play.active, UCHAR, READONLY }, 113 { "play.buffer_size", &info.play.buffer_size, UINT, 0 }, 114 { "record.rate", &info.record.sample_rate,UINT, 0 }, 115 { "record.sample_rate", &info.record.sample_rate,UINT, ALIAS }, 116 { "record.channels", &info.record.channels, UINT, 0 }, 117 { "record.precision", &info.record.precision, UINT, 0 }, 118 { "record.encoding", &info.record.encoding, ENC, 0 }, 119 { "record.gain", &info.record.gain, UINT, 0 }, 120 { "record.balance", &info.record.balance, UCHAR, 0 }, 121 { "record.port", &info.record.port, XINT, 0 }, 122 { "record.avail_ports", &info.record.avail_ports,XINT, 0 }, 123 { "record.seek", &info.record.seek, ULONG, READONLY }, 124 { "record.samples", &info.record.samples, UINT, READONLY }, 125 { "record.eof", &info.record.eof, UINT, READONLY }, 126 { "record.pause", &info.record.pause, UCHAR, 0 }, 127 { "record.error", &info.record.error, UCHAR, READONLY }, 128 { "record.waiting", &info.record.waiting, UCHAR, READONLY }, 129 { "record.open", &info.record.open, UCHAR, READONLY }, 130 { "record.active", &info.record.active, UCHAR, READONLY }, 131 { "record.buffer_size", &info.record.buffer_size,UINT, 0 }, 132 { "record.errors", &rerror, INT, READONLY }, 133 { 0 } 134 }; 135 136 struct { 137 char *ename; 138 int eno; 139 } encs[] = { 140 { AudioEmulaw, AUDIO_ENCODING_ULAW }, 141 { "ulaw", AUDIO_ENCODING_ULAW }, 142 { AudioEalaw, AUDIO_ENCODING_ALAW }, 143 { AudioEslinear, AUDIO_ENCODING_SLINEAR }, 144 { "linear", AUDIO_ENCODING_SLINEAR }, 145 { AudioEulinear, AUDIO_ENCODING_ULINEAR }, 146 { AudioEadpcm, AUDIO_ENCODING_ADPCM }, 147 { "ADPCM", AUDIO_ENCODING_ADPCM }, 148 { AudioEslinear_le, AUDIO_ENCODING_SLINEAR_LE }, 149 { "linear_le", AUDIO_ENCODING_SLINEAR_LE }, 150 { AudioEulinear_le, AUDIO_ENCODING_ULINEAR_LE }, 151 { AudioEslinear_be, AUDIO_ENCODING_SLINEAR_BE }, 152 { "linear_be", AUDIO_ENCODING_SLINEAR_BE }, 153 { AudioEulinear_be, AUDIO_ENCODING_ULINEAR_BE }, 154 { AudioEmpeg_l1_stream, AUDIO_ENCODING_MPEG_L1_STREAM }, 155 { AudioEmpeg_l1_packets,AUDIO_ENCODING_MPEG_L1_PACKETS }, 156 { AudioEmpeg_l1_system, AUDIO_ENCODING_MPEG_L1_SYSTEM }, 157 { AudioEmpeg_l2_stream, AUDIO_ENCODING_MPEG_L2_STREAM }, 158 { AudioEmpeg_l2_packets,AUDIO_ENCODING_MPEG_L2_PACKETS }, 159 { AudioEmpeg_l2_system, AUDIO_ENCODING_MPEG_L2_SYSTEM }, 160 { 0 } 161 }; 162 163 static struct { 164 char *name; 165 u_int prop; 166 } props[] = { 167 { "full_duplex", AUDIO_PROP_FULLDUPLEX }, 168 { "mmap", AUDIO_PROP_MMAP }, 169 { "independent", AUDIO_PROP_INDEPENDENT }, 170 { 0 } 171 }; 172 173 struct field * 174 findfield(name) 175 char *name; 176 { 177 int i; 178 for(i = 0; fields[i].name; i++) 179 if (strcmp(fields[i].name, name) == 0) 180 return &fields[i]; 181 return 0; 182 } 183 184 void 185 prfield(p, sep) 186 struct field *p; 187 char *sep; 188 { 189 u_int v; 190 char *cm; 191 int i; 192 193 if (sep) 194 fprintf(out, "%s%s", p->name, sep); 195 switch(p->format) { 196 case STRING: 197 fprintf(out, "%s", (char*)p->valp); 198 break; 199 case INT: 200 fprintf(out, "%d", *(int*)p->valp); 201 break; 202 case UINT: 203 fprintf(out, "%u", *(u_int*)p->valp); 204 break; 205 case XINT: 206 fprintf(out, "0x%x", *(u_int*)p->valp); 207 break; 208 case UCHAR: 209 fprintf(out, "%u", *(u_char*)p->valp); 210 break; 211 case ULONG: 212 fprintf(out, "%lu", *(u_long*)p->valp); 213 break; 214 case P_R: 215 v = *(u_int*)p->valp; 216 cm = ""; 217 if (v & AUMODE_PLAY) { 218 if (v & AUMODE_PLAY_ALL) 219 fprintf(out, "play"); 220 else 221 fprintf(out, "playsync"); 222 cm = ","; 223 } 224 if (v & AUMODE_RECORD) 225 fprintf(out, "%srecord", cm); 226 break; 227 case ENC: 228 v = *(u_int*)p->valp; 229 for(i = 0; encs[i].ename; i++) 230 if (encs[i].eno == v) 231 break; 232 if (encs[i].ename) 233 fprintf(out, "%s", encs[i].ename); 234 else 235 fprintf(out, "%u", v); 236 break; 237 case PROPS: 238 v = *(u_int*)p->valp; 239 for (cm = "", i = 0; props[i].name; i++) { 240 if (v & props[i].prop) { 241 fprintf(out, "%s%s", cm, props[i].name); 242 cm = ","; 243 } 244 } 245 break; 246 default: 247 errx(1, "Invalid print format."); 248 } 249 } 250 251 void 252 rdfield(p, q) 253 struct field *p; 254 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(fd) 292 int fd; 293 { 294 int pos, i; 295 296 if (ioctl(fd, AUDIO_GETDEV, &adev) < 0) 297 err(1, "AUDIO_GETDEV"); 298 for(pos = 0, i = 0; ; i++) { 299 audio_encoding_t enc; 300 enc.index = i; 301 if (ioctl(fd, AUDIO_GETENC, &enc) < 0) 302 break; 303 if (pos) 304 encbuf[pos++] = ','; 305 sprintf(encbuf+pos, "%s:%d%s", enc.name, 306 enc.precision, 307 enc.flags & AUDIO_ENCODINGFLAG_EMULATED ? "*" : ""); 308 pos += strlen(encbuf+pos); 309 } 310 if (ioctl(fd, AUDIO_GETFD, &fullduplex) < 0) 311 err(1, "AUDIO_GETFD"); 312 if (ioctl(fd, AUDIO_GETPROPS, &properties) < 0) 313 err(1, "AUDIO_GETPROPS"); 314 if (ioctl(fd, AUDIO_RERROR, &rerror) < 0) 315 err(1, "AUDIO_RERROR"); 316 if (ioctl(fd, AUDIO_GETINFO, &info) < 0) 317 err(1, "AUDIO_GETINFO"); 318 } 319 320 void 321 usage() 322 { 323 fprintf(out, "%s [-f file] [-n] name ...\n", prog); 324 fprintf(out, "%s [-f file] [-n] -w name=value ...\n", prog); 325 fprintf(out, "%s [-f file] [-n] -a\n", prog); 326 exit(1); 327 } 328 329 int 330 main(argc, argv) 331 int argc; 332 char **argv; 333 { 334 int fd, i, ch; 335 int aflag = 0, wflag = 0; 336 struct stat dstat, ostat; 337 char *file = "/dev/audioctl"; 338 char *sep = "="; 339 340 prog = *argv; 341 342 while ((ch = getopt(argc, argv, "af:nw")) != -1) { 343 switch(ch) { 344 case 'a': 345 aflag++; 346 break; 347 case 'w': 348 wflag++; 349 break; 350 case 'n': 351 sep = 0; 352 break; 353 case 'f': 354 file = optarg; 355 break; 356 case '?': 357 default: 358 usage(); 359 } 360 } 361 argc -= optind; 362 argv += optind; 363 364 fd = open(file, O_WRONLY); 365 if (fd < 0) 366 fd = open(file, O_RDONLY); 367 if (fd < 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 == 0 && 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 q = strchr(*argv, '='); 399 if (q) { 400 *q++ = 0; 401 p = findfield(*argv); 402 if (p == 0) 403 warnx("field `%s' does not exist", *argv); 404 else { 405 if (p->flags & READONLY) 406 warnx("`%s' is read only", *argv); 407 else { 408 rdfield(p, q); 409 if (p->valp == &fullduplex) 410 if (ioctl(fd, AUDIO_SETFD, &fullduplex) < 0) 411 err(1, "set failed"); 412 } 413 } 414 } else 415 warnx("No `=' in %s", *argv); 416 argv++; 417 } 418 if (ioctl(fd, AUDIO_SETINFO, &info) < 0) 419 err(1, "set failed"); 420 if (sep) { 421 getinfo(fd); 422 for(i = 0; fields[i].name; i++) { 423 if (fields[i].flags & SET) { 424 fprintf(out, "%s: -> ", fields[i].name); 425 prfield(&fields[i], 0); 426 fprintf(out, "\n"); 427 } 428 } 429 } 430 } else { 431 while(argc--) { 432 p = findfield(*argv); 433 if (p == 0) { 434 if (strchr(*argv, '=')) 435 warnx("field %s does not exist (use -w to set a variable)", *argv); 436 else 437 warnx("field %s does not exist", *argv); 438 } else { 439 prfield(p, sep); 440 fprintf(out, "\n"); 441 } 442 argv++; 443 } 444 } 445 } else 446 usage(); 447 exit(0); 448 } 449