xref: /openbsd-src/usr.bin/audioctl/audioctl.c (revision b2ea75c1b17e1a9a339660e7ed45cd24946b230e)
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