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