xref: /netbsd-src/usr.bin/audio/ctl/ctl.c (revision c41a4eebefede43f6950f838a387dc18c6a431bf)
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