xref: /netbsd-src/usr.bin/audio/ctl/ctl.c (revision fdecd6a253f999ae92b139670d9e15cc9df4497c)
1 /*	$NetBSD: ctl.c,v 1.3 1997/05/21 22:25:13 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 REGENTS OR CONTRIBUTORS BE
29  * 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 FILE *out = stdout;
49 
50 char *prog;
51 
52 audio_device_t adev;
53 
54 audio_info_t info;
55 
56 char encbuf[1000];
57 
58 int fullduplex, rerror;
59 
60 struct field {
61 	char *name;
62 	void *valp;
63 	int format;
64 #define STRING 1
65 #define INT 2
66 #define UINT 3
67 #define P_R 4
68 #define ULONG 5
69 #define UCHAR 6
70 #define ENC 7
71 	char flags;
72 #define READONLY 1
73 #define ALIAS 2
74 #define SET 4
75 } fields[] = {
76     { "name", 			&adev.name, 		STRING, READONLY },
77     { "version",		&adev.version,		STRING, READONLY },
78     { "config",			&adev.config,		STRING, READONLY },
79     { "encodings",		encbuf,			STRING, READONLY },
80     { "full_duplex",		&fullduplex,		INT,    0 },
81     { "blocksize",		&info.blocksize,	UINT,	0 },
82     { "hiwat",			&info.hiwat,		UINT,	0 },
83     { "lowat",			&info.lowat,		UINT,	0 },
84     { "backlog",		&info.backlog,		UINT,	0 },
85     { "mode",			&info.mode,		P_R,	READONLY },
86     { "play.rate",		&info.play.sample_rate,	UINT,	0 },
87     { "play.sample_rate",	&info.play.sample_rate,	UINT,	ALIAS },
88     { "play.channels",		&info.play.channels,	UINT,	0 },
89     { "play.precision",		&info.play.precision,	UINT,	0 },
90     { "play.encoding",		&info.play.encoding,	ENC,	0 },
91     { "play.gain",		&info.play.gain,	UINT,	0 },
92     { "play.port",		&info.play.port,	UINT,	0 },
93     { "play.seek",		&info.play.seek,	ULONG,	READONLY },
94     { "play.samples",		&info.play.samples,	UINT,	READONLY },
95     { "play.eof",		&info.play.eof,		UINT,	READONLY },
96     { "play.pause",		&info.play.pause,	UCHAR,	0 },
97     { "play.error",		&info.play.error,	UCHAR,	READONLY },
98     { "play.waiting",		&info.play.waiting,	UCHAR,	READONLY },
99     { "play.open",		&info.play.open,	UCHAR,	READONLY },
100     { "play.active",		&info.play.active,	UCHAR,	READONLY },
101     { "record.rate",		&info.record.sample_rate,UINT,	0 },
102     { "record.sample_rate",	&info.record.sample_rate,UINT,	ALIAS },
103     { "record.channels",	&info.record.channels,	UINT,	0 },
104     { "record.precision",	&info.record.precision,	UINT,	0 },
105     { "record.encoding",	&info.record.encoding,	ENC,	0 },
106     { "record.gain",		&info.record.gain,	UINT,	0 },
107     { "record.port",		&info.record.port,	UINT,	0 },
108     { "record.seek",		&info.record.seek,	ULONG,	READONLY },
109     { "record.samples",		&info.record.samples,	UINT,	READONLY },
110     { "record.eof",		&info.record.eof,	UINT,	READONLY },
111     { "record.pause",		&info.record.pause,	UCHAR,	0 },
112     { "record.error",		&info.record.error,	UCHAR,	READONLY },
113     { "record.waiting",		&info.record.waiting,	UCHAR,	READONLY },
114     { "record.open",		&info.record.open,	UCHAR,	READONLY },
115     { "record.active",		&info.record.active,	UCHAR,	READONLY },
116     { "record.errors",		&rerror,		INT,	READONLY },
117     { 0 }
118 };
119 
120 struct {
121 	char *ename;
122 	int eno;
123 } encs[] = {
124     { "ulaw", AUDIO_ENCODING_ULAW },
125     { "alaw", AUDIO_ENCODING_ALAW },
126     { "linear", AUDIO_ENCODING_LINEAR },
127     { "ulinear", AUDIO_ENCODING_ULINEAR },
128     { "adpcm", AUDIO_ENCODING_ADPCM },
129     { "ADPCM", AUDIO_ENCODING_ADPCM },
130     { "mulaw", AUDIO_ENCODING_ULAW },
131     { "linear_le", AUDIO_ENCODING_LINEAR_LE },
132     { "ulinear_le", AUDIO_ENCODING_ULINEAR_LE },
133     { "linear_be", AUDIO_ENCODING_LINEAR_BE },
134     { "ulinear_be", AUDIO_ENCODING_ULINEAR_BE },
135     { 0 }
136 };
137 
138 struct field *
139 findfield(char *name)
140 {
141     int i;
142     for(i = 0; fields[i].name; i++)
143 	if (strcmp(fields[i].name, name) == 0)
144 	    return &fields[i];
145     return 0;
146 }
147 
148 void
149 prfield(struct field *p, char *sep)
150 {
151     u_int v;
152     char *cm;
153     int i;
154 
155     if (sep)
156 	fprintf(out, "%s%s", p->name, sep);
157     switch(p->format) {
158     case STRING:
159 	fprintf(out, "%s", (char*)p->valp);
160 	break;
161     case INT:
162 	fprintf(out, "%d", *(int*)p->valp);
163 	break;
164     case UINT:
165 	fprintf(out, "%u", *(u_int*)p->valp);
166 	break;
167     case UCHAR:
168 	fprintf(out, "%u", *(u_char*)p->valp);
169 	break;
170     case ULONG:
171 	fprintf(out, "%lu", *(u_long*)p->valp);
172 	break;
173     case P_R:
174 	v = *(u_int*)p->valp;
175 	cm = "";
176 	if (v & AUMODE_PLAY) {
177 	    if (v & AUMODE_PLAY_ALL)
178 		fprintf(out, "play");
179 	    else
180 		fprintf(out, "playsync");
181 	    cm = ",";
182 	}
183 	if (v & AUMODE_RECORD)
184 	    fprintf(out, "%srecord", cm);
185 	break;
186     case ENC:
187 	v = *(u_int*)p->valp;
188 	for(i = 0; encs[i].ename; i++)
189 	    if (encs[i].eno == v)
190 		break;
191 	if (encs[i].ename)
192 	    fprintf(out, "%s", encs[i].ename);
193 	else
194 	    fprintf(out, "%u", v);
195 	break;
196     default:
197 	errx(1, "Invalid format.");
198     }
199 }
200 
201 void
202 rdfield(struct field *p, char *q)
203 {
204     int i;
205 
206     switch(p->format) {
207     case UINT:
208 	if (sscanf(q, "%u", (unsigned int *)p->valp) != 1)
209 	    warnx("Bad number %s", q);
210 	break;
211     case ENC:
212 	for(i = 0; encs[i].ename; i++)
213 	    if (strcmp(encs[i].ename, q) == 0)
214 		break;
215 	if (encs[i].ename)
216 	    *(u_int*)p->valp = encs[i].eno;
217 	else
218 	    warnx("Unknown encoding: %s", q);
219 	break;
220     default:
221 	errx(1, "Invalid format.");
222     }
223     p->flags |= SET;
224 }
225 
226 void
227 getinfo(int fd)
228 {
229     int pos, i;
230 
231     if (ioctl(fd, AUDIO_GETDEV, &adev) < 0)
232 	err(1, NULL);
233     for(pos = 0, i = 0; ; i++) {
234 	audio_encoding_t enc;
235 	enc.index = i;
236 	if (ioctl(fd, AUDIO_GETENC, &enc) < 0)
237 	    break;
238 	if (pos)
239 	    encbuf[pos++] = ',';
240 	sprintf(encbuf+pos, "%s:%d%s", enc.name,
241 		enc.precision,
242 		enc.flags & AUDIO_ENCODINGFLAG_EMULATED ? "*" : "");
243 	pos += strlen(encbuf+pos);
244     }
245     if (ioctl(fd, AUDIO_GETFD, &fullduplex) < 0)
246 	err(1, NULL);
247     if (ioctl(fd, AUDIO_RERROR, &rerror) < 0)
248 	err(1, NULL);
249     if (ioctl(fd, AUDIO_GETINFO, &info) < 0)
250 	err(1, NULL);
251 }
252 
253 void
254 usage(void)
255 {
256     fprintf(out, "%s [-f file] [-n] name ...\n", prog);
257     fprintf(out, "%s [-f file] [-n] -w name=value ...\n", prog);
258     fprintf(out, "%s [-f file] [-n] -a\n", prog);
259     exit(1);
260 }
261 
262 void
263 main(int argc, char **argv)
264 {
265     int fd, i, ch;
266     int aflag = 0, wflag = 0;
267     struct stat dstat, ostat;
268     char *file = "/dev/sound";
269     char *sep = "=";
270 
271     prog = *argv;
272 
273     while ((ch = getopt(argc, argv, "af:nw")) != -1) {
274 	switch(ch) {
275 	case 'a':
276 	    aflag++;
277 	    break;
278 	case 'w':
279 	    wflag++;
280 	    break;
281 	case 'n':
282 	    sep = 0;
283 	    break;
284 	case 'f':
285 	    file = optarg;
286 	    break;
287 	case '?':
288 	default:
289 	    usage();
290 	}
291     }
292     argc -= optind;
293     argv += optind;
294 
295     fd = open(file, O_RDWR);
296     if (fd < 0)
297 	fd = open(file, O_WRONLY);
298     if (fd < 0)
299 	fd = open(file, O_RDONLY);
300     if (fd < 0)
301 	err(1, "%s", file);
302 
303     /* Check is stdout is the same device as the audio device. */
304     if (fstat(fd, &dstat) < 0)
305 	err(1, NULL);
306     if (fstat(STDOUT_FILENO, &ostat) < 0)
307 		err(1, NULL);
308     if (S_ISCHR(dstat.st_mode) && S_ISCHR(ostat.st_mode) &&
309 	major(dstat.st_dev) == major(ostat.st_dev) &&
310 	minor(dstat.st_dev) == minor(ostat.st_dev))
311 	/* We can't write to stdout so use stderr */
312 	out = stderr;
313 
314     if (!wflag)
315 	getinfo(fd);
316 
317     if (argc == 0 && aflag && !wflag) {
318 	for(i = 0; fields[i].name; i++) {
319 	    if (!(fields[i].flags & ALIAS)) {
320 		prfield(&fields[i], sep);
321 		fprintf(out, "\n");
322 	    }
323 	}
324     } else if (argc > 0 && !aflag) {
325 	struct field *p;
326 	if (wflag) {
327 	    AUDIO_INITINFO(&info);
328 	    while(argc--) {
329 		char *q;
330 
331 		q = strchr(*argv, '=');
332 		if (q) {
333 		    *q++ = 0;
334 		    p = findfield(*argv);
335 		    if (p == 0)
336 			warnx("field %s does not exist", *argv);
337 		    else {
338 			if (p->flags & READONLY)
339 			    warnx("%s is read only", *argv);
340 			else {
341 			    rdfield(p, q);
342 			    if (p->valp == &fullduplex)
343 				if (ioctl(fd, AUDIO_SETFD, &fullduplex) < 0)
344 				    err(1, "set failed");
345 			}
346 		    }
347 		} else
348 		    warnx("No `=' in %s", *argv);
349 		argv++;
350 	    }
351 	    if (ioctl(fd, AUDIO_SETINFO, &info) < 0)
352 		err(1, "set failed");
353 	    if (sep) {
354 		getinfo(fd);
355 		for(i = 0; fields[i].name; i++) {
356 		    if (fields[i].flags & SET) {
357 			fprintf(out, "%s: -> ", fields[i].name);
358 			prfield(&fields[i], 0);
359 			fprintf(out, "\n");
360 		    }
361 		}
362 	    }
363 	} else {
364 	    while(argc--) {
365 		p = findfield(*argv);
366 		if (p == 0) {
367 		    if (strchr(*argv, '='))
368 			warnx("field %s does not exist (use -w to set a variable)", *argv);
369 		    else
370 			warnx("field %s does not exist", *argv);
371 		} else {
372 		    prfield(p, sep);
373 		    fprintf(out, "\n");
374 		}
375 		argv++;
376 	    }
377 	}
378     } else
379 	usage();
380     exit(0);
381 }
382