ctl.c revision 1.11 1 /* $NetBSD: ctl.c,v 1.11 1997/10/11 13:40:26 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 char flags;
80 #define READONLY 1
81 #define ALIAS 2
82 #define SET 4
83 } fields[] = {
84 { "name", &adev.name, STRING, READONLY },
85 { "version", &adev.version, STRING, READONLY },
86 { "config", &adev.config, STRING, READONLY },
87 { "encodings", encbuf, STRING, READONLY },
88 { "properties", &properties, PROPS, READONLY },
89 { "full_duplex", &fullduplex, INT, 0 },
90 { "buffersize", &info.buffersize, UINT, 0 },
91 { "blocksize", &info.blocksize, UINT, 0 },
92 { "hiwat", &info.hiwat, UINT, 0 },
93 { "lowat", &info.lowat, UINT, 0 },
94 { "backlog", &info.backlog, 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.port", &info.play.port, UINT, 0 },
103 { "play.seek", &info.play.seek, ULONG, READONLY },
104 { "play.samples", &info.play.samples, UINT, READONLY },
105 { "play.eof", &info.play.eof, UINT, READONLY },
106 { "play.pause", &info.play.pause, UCHAR, 0 },
107 { "play.error", &info.play.error, UCHAR, READONLY },
108 { "play.waiting", &info.play.waiting, UCHAR, READONLY },
109 { "play.open", &info.play.open, UCHAR, READONLY },
110 { "play.active", &info.play.active, UCHAR, READONLY },
111 { "record.rate", &info.record.sample_rate,UINT, 0 },
112 { "record.sample_rate", &info.record.sample_rate,UINT, ALIAS },
113 { "record.channels", &info.record.channels, UINT, 0 },
114 { "record.precision", &info.record.precision, UINT, 0 },
115 { "record.encoding", &info.record.encoding, ENC, 0 },
116 { "record.gain", &info.record.gain, UINT, 0 },
117 { "record.port", &info.record.port, UINT, 0 },
118 { "record.seek", &info.record.seek, ULONG, READONLY },
119 { "record.samples", &info.record.samples, UINT, READONLY },
120 { "record.eof", &info.record.eof, UINT, READONLY },
121 { "record.pause", &info.record.pause, UCHAR, 0 },
122 { "record.error", &info.record.error, UCHAR, READONLY },
123 { "record.waiting", &info.record.waiting, UCHAR, READONLY },
124 { "record.open", &info.record.open, UCHAR, READONLY },
125 { "record.active", &info.record.active, UCHAR, READONLY },
126 { "record.errors", &rerror, INT, READONLY },
127 { 0 }
128 };
129
130 struct {
131 char *ename;
132 int eno;
133 } encs[] = {
134 { "ulaw", AUDIO_ENCODING_ULAW },
135 { "mulaw", AUDIO_ENCODING_ULAW },
136 { "alaw", AUDIO_ENCODING_ALAW },
137 { "slinear", AUDIO_ENCODING_SLINEAR },
138 { "linear", AUDIO_ENCODING_SLINEAR },
139 { "ulinear", AUDIO_ENCODING_ULINEAR },
140 { "adpcm", AUDIO_ENCODING_ADPCM },
141 { "ADPCM", AUDIO_ENCODING_ADPCM },
142 { "slinear_le", AUDIO_ENCODING_SLINEAR_LE },
143 { "linear_le", AUDIO_ENCODING_SLINEAR_LE },
144 { "ulinear_le", AUDIO_ENCODING_ULINEAR_LE },
145 { "slinear_be", AUDIO_ENCODING_SLINEAR_BE },
146 { "linear_be", AUDIO_ENCODING_SLINEAR_BE },
147 { "ulinear_be", AUDIO_ENCODING_ULINEAR_BE },
148 { 0 }
149 };
150
151 static struct {
152 char *name;
153 u_int prop;
154 } props[] = {
155 { "full_duplex", AUDIO_PROP_FULLDUPLEX },
156 { "mmap", AUDIO_PROP_MMAP },
157 { "independent", AUDIO_PROP_INDEPENDENT },
158 { 0 }
159 };
160
161 struct field *
162 findfield(name)
163 char *name;
164 {
165 int i;
166 for(i = 0; fields[i].name; i++)
167 if (strcmp(fields[i].name, name) == 0)
168 return &fields[i];
169 return 0;
170 }
171
172 void
173 prfield(p, sep)
174 struct field *p;
175 char *sep;
176 {
177 u_int v;
178 char *cm;
179 int i;
180
181 if (sep)
182 fprintf(out, "%s%s", p->name, sep);
183 switch(p->format) {
184 case STRING:
185 fprintf(out, "%s", (char*)p->valp);
186 break;
187 case INT:
188 fprintf(out, "%d", *(int*)p->valp);
189 break;
190 case UINT:
191 fprintf(out, "%u", *(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 for(i = 0; encs[i].ename; i++)
215 if (encs[i].eno == v)
216 break;
217 if (encs[i].ename)
218 fprintf(out, "%s", encs[i].ename);
219 else
220 fprintf(out, "%u", v);
221 break;
222 case PROPS:
223 v = *(u_int*)p->valp;
224 for (cm = "", i = 0; props[i].name; i++) {
225 if (v & props[i].prop) {
226 fprintf(out, "%s%s", cm, props[i].name);
227 cm = ",";
228 }
229 }
230 break;
231 default:
232 errx(1, "Invalid format.");
233 }
234 }
235
236 void
237 rdfield(p, q)
238 struct field *p;
239 char *q;
240 {
241 int i;
242
243 switch(p->format) {
244 case UINT:
245 if (sscanf(q, "%u", (unsigned int *)p->valp) != 1)
246 warnx("Bad number %s", q);
247 break;
248 case ENC:
249 for(i = 0; encs[i].ename; i++)
250 if (strcmp(encs[i].ename, q) == 0)
251 break;
252 if (encs[i].ename)
253 *(u_int*)p->valp = encs[i].eno;
254 else
255 warnx("Unknown encoding: %s", q);
256 break;
257 default:
258 errx(1, "Invalid format.");
259 }
260 p->flags |= SET;
261 }
262
263 void
264 getinfo(fd)
265 int fd;
266 {
267 int pos, i;
268
269 if (ioctl(fd, AUDIO_GETDEV, &adev) < 0)
270 err(1, "AUDIO_GETDEV");
271 for(pos = 0, i = 0; ; i++) {
272 audio_encoding_t enc;
273 enc.index = i;
274 if (ioctl(fd, AUDIO_GETENC, &enc) < 0)
275 break;
276 if (pos)
277 encbuf[pos++] = ',';
278 sprintf(encbuf+pos, "%s:%d%s", enc.name,
279 enc.precision,
280 enc.flags & AUDIO_ENCODINGFLAG_EMULATED ? "*" : "");
281 pos += strlen(encbuf+pos);
282 }
283 if (ioctl(fd, AUDIO_GETFD, &fullduplex) < 0)
284 err(1, "AUDIO_GETFD");
285 if (ioctl(fd, AUDIO_GETPROPS, &properties) < 0)
286 err(1, "AUDIO_GETPROPS");
287 if (ioctl(fd, AUDIO_RERROR, &rerror) < 0)
288 err(1, "AUDIO_RERROR");
289 if (ioctl(fd, AUDIO_GETINFO, &info) < 0)
290 err(1, "AUDIO_GETINFO");
291 }
292
293 void
294 usage()
295 {
296 fprintf(out, "%s [-f file] [-n] name ...\n", prog);
297 fprintf(out, "%s [-f file] [-n] -w name=value ...\n", prog);
298 fprintf(out, "%s [-f file] [-n] -a\n", prog);
299 exit(1);
300 }
301
302 int
303 main(argc, argv)
304 int argc;
305 char **argv;
306 {
307 int fd, i, ch;
308 int aflag = 0, wflag = 0;
309 struct stat dstat, ostat;
310 char *file = "/dev/audioctl";
311 char *sep = "=";
312
313 prog = *argv;
314
315 while ((ch = getopt(argc, argv, "af:nw")) != -1) {
316 switch(ch) {
317 case 'a':
318 aflag++;
319 break;
320 case 'w':
321 wflag++;
322 break;
323 case 'n':
324 sep = 0;
325 break;
326 case 'f':
327 file = optarg;
328 break;
329 case '?':
330 default:
331 usage();
332 }
333 }
334 argc -= optind;
335 argv += optind;
336
337 fd = open(file, O_WRONLY);
338 if (fd < 0)
339 fd = open(file, O_RDONLY);
340 if (fd < 0)
341 err(1, "%s", file);
342
343 /* Check if stdout is the same device as the audio device. */
344 if (fstat(fd, &dstat) < 0)
345 err(1, "fstat au");
346 if (fstat(STDOUT_FILENO, &ostat) < 0)
347 err(1, "fstat stdout");
348 if (S_ISCHR(dstat.st_mode) && S_ISCHR(ostat.st_mode) &&
349 major(dstat.st_dev) == major(ostat.st_dev) &&
350 minor(dstat.st_dev) == minor(ostat.st_dev))
351 /* We can't write to stdout so use stderr */
352 out = stderr;
353
354 if (!wflag)
355 getinfo(fd);
356
357 if (argc == 0 && aflag && !wflag) {
358 for(i = 0; fields[i].name; i++) {
359 if (!(fields[i].flags & ALIAS)) {
360 prfield(&fields[i], sep);
361 fprintf(out, "\n");
362 }
363 }
364 } else if (argc > 0 && !aflag) {
365 struct field *p;
366 if (wflag) {
367 AUDIO_INITINFO(&info);
368 while(argc--) {
369 char *q;
370
371 q = strchr(*argv, '=');
372 if (q) {
373 *q++ = 0;
374 p = findfield(*argv);
375 if (p == 0)
376 warnx("field `%s' does not exist", *argv);
377 else {
378 if (p->flags & READONLY)
379 warnx("`%s' is read only", *argv);
380 else {
381 rdfield(p, q);
382 if (p->valp == &fullduplex)
383 if (ioctl(fd, AUDIO_SETFD, &fullduplex) < 0)
384 err(1, "set failed");
385 }
386 }
387 } else
388 warnx("No `=' in %s", *argv);
389 argv++;
390 }
391 if (ioctl(fd, AUDIO_SETINFO, &info) < 0)
392 err(1, "set failed");
393 if (sep) {
394 getinfo(fd);
395 for(i = 0; fields[i].name; i++) {
396 if (fields[i].flags & SET) {
397 fprintf(out, "%s: -> ", fields[i].name);
398 prfield(&fields[i], 0);
399 fprintf(out, "\n");
400 }
401 }
402 }
403 } else {
404 while(argc--) {
405 p = findfield(*argv);
406 if (p == 0) {
407 if (strchr(*argv, '='))
408 warnx("field %s does not exist (use -w to set a variable)", *argv);
409 else
410 warnx("field %s does not exist", *argv);
411 } else {
412 prfield(p, sep);
413 fprintf(out, "\n");
414 }
415 argv++;
416 }
417 }
418 } else
419 usage();
420 exit(0);
421 }
422