1 /* $NetBSD: ctl.c,v 1.23 1999/03/26 14:05:41 mrg Exp $ */ 2 3 /* 4 * Copyright (c) 1997 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Lennart Augustsson (augustss@netbsd.org). 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 <sys/types.h> 40 #include <sys/stat.h> 41 #include <sys/ioctl.h> 42 #include <sys/audioio.h> 43 44 #include <err.h> 45 #include <fcntl.h> 46 #include <stdio.h> 47 #include <stdlib.h> 48 #include <string.h> 49 #include <unistd.h> 50 51 #include <paths.h> 52 53 #include "libaudio.h" 54 55 struct field *findfield __P((char *name)); 56 void prfield __P((struct field *p, char *sep)); 57 void rdfield __P((struct field *p, char *q)); 58 void getinfo __P((int fd)); 59 void audioctl_write __P((int, int, char *[])); 60 void usage __P((void)); 61 int main __P((int argc, char **argv)); 62 63 FILE *out = stdout; 64 65 char *prog; 66 67 audio_device_t adev; 68 69 audio_info_t info; 70 71 char encbuf[1000]; 72 73 int properties, fullduplex, rerror; 74 75 struct field { 76 char *name; 77 void *valp; 78 int format; 79 #define STRING 1 80 #define INT 2 81 #define UINT 3 82 #define P_R 4 83 #define ULONG 5 /* XXX obsolete now */ 84 #define UCHAR 6 85 #define ENC 7 86 #define PROPS 8 87 #define XINT 9 88 #define FORMAT 10 89 char flags; 90 #define READONLY 1 91 #define ALIAS 2 92 #define SET 4 93 } fields[] = { 94 { "name", &adev.name, STRING, READONLY }, 95 { "version", &adev.version, STRING, READONLY }, 96 { "config", &adev.config, STRING, READONLY }, 97 { "encodings", encbuf, STRING, READONLY }, 98 { "properties", &properties, PROPS, READONLY }, 99 { "full_duplex", &fullduplex, UINT, 0 }, 100 { "fullduplex", &fullduplex, UINT, 0 }, 101 { "blocksize", &info.blocksize, UINT, 0 }, 102 { "hiwat", &info.hiwat, UINT, 0 }, 103 { "lowat", &info.lowat, UINT, 0 }, 104 { "monitor_gain", &info.monitor_gain, UINT, 0 }, 105 { "mode", &info.mode, P_R, READONLY }, 106 { "play", &info.play, FORMAT, ALIAS }, 107 { "play.rate", &info.play.sample_rate, UINT, 0 }, 108 { "play.sample_rate", &info.play.sample_rate, UINT, ALIAS }, 109 { "play.channels", &info.play.channels, UINT, 0 }, 110 { "play.precision", &info.play.precision, UINT, 0 }, 111 { "play.encoding", &info.play.encoding, ENC, 0 }, 112 { "play.gain", &info.play.gain, UINT, 0 }, 113 { "play.balance", &info.play.balance, UCHAR, 0 }, 114 { "play.port", &info.play.port, XINT, 0 }, 115 { "play.avail_ports", &info.play.avail_ports, XINT, 0 }, 116 { "play.seek", &info.play.seek, UINT, READONLY }, 117 { "play.samples", &info.play.samples, UINT, READONLY }, 118 { "play.eof", &info.play.eof, UINT, READONLY }, 119 { "play.pause", &info.play.pause, UCHAR, 0 }, 120 { "play.error", &info.play.error, UCHAR, READONLY }, 121 { "play.waiting", &info.play.waiting, UCHAR, READONLY }, 122 { "play.open", &info.play.open, UCHAR, READONLY }, 123 { "play.active", &info.play.active, UCHAR, READONLY }, 124 { "play.buffer_size", &info.play.buffer_size, UINT, 0 }, 125 { "record", &info.record, FORMAT, ALIAS }, 126 { "record.rate", &info.record.sample_rate,UINT, 0 }, 127 { "record.sample_rate", &info.record.sample_rate,UINT, ALIAS }, 128 { "record.channels", &info.record.channels, UINT, 0 }, 129 { "record.precision", &info.record.precision, UINT, 0 }, 130 { "record.encoding", &info.record.encoding, ENC, 0 }, 131 { "record.gain", &info.record.gain, UINT, 0 }, 132 { "record.balance", &info.record.balance, UCHAR, 0 }, 133 { "record.port", &info.record.port, XINT, 0 }, 134 { "record.avail_ports", &info.record.avail_ports,XINT, 0 }, 135 { "record.seek", &info.record.seek, UINT, READONLY }, 136 { "record.samples", &info.record.samples, UINT, READONLY }, 137 { "record.eof", &info.record.eof, UINT, READONLY }, 138 { "record.pause", &info.record.pause, UCHAR, 0 }, 139 { "record.error", &info.record.error, UCHAR, READONLY }, 140 { "record.waiting", &info.record.waiting, UCHAR, READONLY }, 141 { "record.open", &info.record.open, UCHAR, READONLY }, 142 { "record.active", &info.record.active, UCHAR, READONLY }, 143 { "record.buffer_size", &info.record.buffer_size,UINT, 0 }, 144 { "record.errors", &rerror, INT, READONLY }, 145 { 0 } 146 }; 147 148 static struct { 149 char *name; 150 u_int prop; 151 } props[] = { 152 { "full_duplex", AUDIO_PROP_FULLDUPLEX }, 153 { "mmap", AUDIO_PROP_MMAP }, 154 { "independent", AUDIO_PROP_INDEPENDENT }, 155 { 0 } 156 }; 157 158 struct field * 159 findfield(name) 160 char *name; 161 { 162 int i; 163 for (i = 0; fields[i].name; i++) 164 if (strcmp(fields[i].name, name) == 0) 165 return &fields[i]; 166 return 0; 167 } 168 169 void 170 prfield(p, sep) 171 struct field *p; 172 char *sep; 173 { 174 u_int v; 175 char *cm, *encstr; 176 int i; 177 178 if (sep) 179 fprintf(out, "%s%s", p->name, sep); 180 switch(p->format) { 181 case STRING: 182 fprintf(out, "%s", (char*)p->valp); 183 break; 184 case INT: 185 fprintf(out, "%d", *(int*)p->valp); 186 break; 187 case UINT: 188 fprintf(out, "%u", *(u_int*)p->valp); 189 break; 190 case XINT: 191 fprintf(out, "0x%x", *(u_int*)p->valp); 192 break; 193 case UCHAR: 194 fprintf(out, "%u", *(u_char*)p->valp); 195 break; 196 case ULONG: 197 fprintf(out, "%lu", *(u_long*)p->valp); 198 break; 199 case P_R: 200 v = *(u_int*)p->valp; 201 cm = ""; 202 if (v & AUMODE_PLAY) { 203 if (v & AUMODE_PLAY_ALL) 204 fprintf(out, "play"); 205 else 206 fprintf(out, "playsync"); 207 cm = ","; 208 } 209 if (v & AUMODE_RECORD) 210 fprintf(out, "%srecord", cm); 211 break; 212 case ENC: 213 v = *(u_int*)p->valp; 214 encstr = audio_enc_from_val(v); 215 if (encstr) 216 fprintf(out, "%s", encstr); 217 else 218 fprintf(out, "%u", v); 219 break; 220 case PROPS: 221 v = *(u_int*)p->valp; 222 for (cm = "", i = 0; props[i].name; i++) { 223 if (v & props[i].prop) { 224 fprintf(out, "%s%s", cm, props[i].name); 225 cm = ","; 226 } 227 } 228 break; 229 case FORMAT: 230 prfield(p + 1, 0); 231 fprintf(out, ","); 232 prfield(p + 3, 0); 233 fprintf(out, ","); 234 prfield(p + 4, 0); 235 fprintf(out, ","); 236 prfield(p + 5, 0); 237 break; 238 default: 239 errx(1, "Invalid print format."); 240 } 241 } 242 243 void 244 rdfield(p, q) 245 struct field *p; 246 char *q; 247 { 248 int enc; 249 u_int u; 250 char *s; 251 252 switch(p->format) { 253 case UINT: 254 if (sscanf(q, "%u", (unsigned int *)p->valp) != 1) 255 errx(1, "Bad number: %s", q); 256 break; 257 case UCHAR: 258 if (sscanf(q, "%u", &u) != 1) 259 errx(1, "Bad number: %s", q); 260 else 261 *(u_char *)p->valp = u; 262 break; 263 case XINT: 264 if (sscanf(q, "0x%x", (unsigned int *)p->valp) != 1 && 265 sscanf(q, "%x", (unsigned int *)p->valp) != 1) 266 errx(1, "Bad number: %s", q); 267 break; 268 case ENC: 269 enc = audio_enc_to_val(q); 270 if (enc >= 0) 271 *(u_int*)p->valp = enc; 272 else 273 errx(1, "Unknown encoding: %s", q); 274 break; 275 case FORMAT: 276 s = strsep(&q, ","); 277 if (s) 278 rdfield(p + 1, s); 279 s = strsep(&q, ","); 280 if (s) 281 rdfield(p + 3, s); 282 s = strsep(&q, ","); 283 if (s) 284 rdfield(p + 4, s); 285 s = strsep(&q, ","); 286 if (s) 287 rdfield(p + 5, s); 288 if (!s || q) 289 errx(1, "Bad format"); 290 break; 291 default: 292 errx(1, "Invalid read format."); 293 } 294 p->flags |= SET; 295 } 296 297 void 298 getinfo(fd) 299 int fd; 300 { 301 int pos, i; 302 303 if (ioctl(fd, AUDIO_GETDEV, &adev) < 0) 304 err(1, "AUDIO_GETDEV"); 305 for (pos = 0, i = 0; ; i++) { 306 audio_encoding_t enc; 307 enc.index = i; 308 if (ioctl(fd, AUDIO_GETENC, &enc) < 0) 309 break; 310 if (pos >= sizeof(encbuf)-1) 311 break; 312 if (pos) 313 encbuf[pos++] = ','; 314 if (pos >= sizeof(encbuf)-1) 315 break; 316 pos += snprintf(encbuf+pos, sizeof(encbuf)-pos, "%s:%d%s", 317 enc.name, enc.precision, 318 enc.flags & AUDIO_ENCODINGFLAG_EMULATED ? "*" : ""); 319 } 320 if (ioctl(fd, AUDIO_GETFD, &fullduplex) < 0) 321 err(1, "AUDIO_GETFD"); 322 if (ioctl(fd, AUDIO_GETPROPS, &properties) < 0) 323 err(1, "AUDIO_GETPROPS"); 324 if (ioctl(fd, AUDIO_RERROR, &rerror) < 0) 325 err(1, "AUDIO_RERROR"); 326 if (ioctl(fd, AUDIO_GETINFO, &info) < 0) 327 err(1, "AUDIO_GETINFO"); 328 } 329 330 void 331 usage() 332 { 333 334 fprintf(stderr, "Usage: %s [-f file] [-n] name ...\n", prog); 335 fprintf(stderr, "Usage: %s [-f file] [-n] -w name=value ...\n", prog); 336 fprintf(stderr, "Usage: %s [-f file] [-n] -a\n", prog); 337 exit(1); 338 } 339 340 int 341 main(argc, argv) 342 int argc; 343 char **argv; 344 { 345 int fd, i, ch; 346 int aflag = 0, wflag = 0; 347 struct stat dstat, ostat; 348 const char *file; 349 char *sep = "="; 350 extern char *__progname; 351 352 file = getenv("AUDIOCTLDEVICE"); 353 if (file == 0) 354 file = _PATH_AUDIOCTL; 355 356 prog = __progname; 357 358 while ((ch = getopt(argc, argv, "af:nw")) != -1) { 359 switch(ch) { 360 case 'a': 361 aflag++; 362 break; 363 case 'w': 364 wflag++; 365 break; 366 case 'n': 367 sep = 0; 368 break; 369 case 'f': 370 file = optarg; 371 break; 372 case '?': 373 default: 374 usage(); 375 } 376 } 377 argc -= optind; 378 argv += optind; 379 380 fd = open(file, O_WRONLY); 381 if (fd < 0) 382 fd = open(file, O_RDONLY); 383 #ifdef _PATH_OAUDIOCTL 384 /* Allow the non-unit device to be used. */ 385 if (fd < 0 && file == _PATH_AUDIOCTL) { 386 file = _PATH_OAUDIOCTL; 387 fd = open(file, O_WRONLY); 388 if (fd < 0) 389 fd = open(file, O_RDONLY); 390 } 391 #endif 392 if (fd < 0) 393 err(1, "%s", file); 394 395 /* Check if stdout is the same device as the audio device. */ 396 if (fstat(fd, &dstat) < 0) 397 err(1, "fstat audioctl"); 398 if (fstat(STDOUT_FILENO, &ostat) < 0) 399 err(1, "fstat stdout"); 400 if (S_ISCHR(dstat.st_mode) && S_ISCHR(ostat.st_mode) && 401 major(dstat.st_dev) == major(ostat.st_dev) && 402 minor(dstat.st_dev) == minor(ostat.st_dev)) 403 /* We can't write to stdout so use stderr */ 404 out = stderr; 405 406 if (!wflag) 407 getinfo(fd); 408 409 if (argc == 0 && aflag && !wflag) { 410 for (i = 0; fields[i].name; i++) { 411 if (!(fields[i].flags & ALIAS)) { 412 prfield(&fields[i], sep); 413 fprintf(out, "\n"); 414 } 415 } 416 } else if (argc > 0 && !aflag) { 417 if (wflag) { 418 audioctl_write(fd, argc, argv); 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 struct field *p; 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 450 void 451 audioctl_write(fd, argc, argv) 452 int fd; 453 int argc; 454 char *argv[]; 455 { 456 struct field *p; 457 458 AUDIO_INITINFO(&info); 459 while (argc--) { 460 char *q; 461 462 q = strchr(*argv, '='); 463 if (q) { 464 *q++ = 0; 465 p = findfield(*argv); 466 if (p == 0) 467 warnx("field `%s' does not exist", *argv); 468 else { 469 if (p->flags & READONLY) 470 warnx("`%s' is read only", *argv); 471 else { 472 rdfield(p, q); 473 if (p->valp == &fullduplex) 474 if (ioctl(fd, AUDIO_SETFD, &fullduplex) < 0) 475 err(1, "set failed"); 476 } 477 } 478 } else 479 warnx("No `=' in %s", *argv); 480 argv++; 481 } 482 if (ioctl(fd, AUDIO_SETINFO, &info) < 0) 483 err(1, "set failed"); 484 } 485