ctl.c revision 1.23.10.2 1 /* $NetBSD: ctl.c,v 1.23.10.2 2002/02/09 17:31:59 he 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 (at) 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 __P((char *name));
56 void prfield __P((struct field *p, char *sep));
57 void rdfield __P((struct field *p, char *q));
58 void getinfo __P((int fd));
59 void audioctl_write __P((int, int, char *[]));
60 void usage __P((void));
61 int main __P((int argc, char **argv));
62
63 char *prog;
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;
174 const char *encstr;
175 int i;
176
177 if (sep)
178 printf("%s%s", p->name, sep);
179 switch(p->format) {
180 case STRING:
181 printf("%s", (char*)p->valp);
182 break;
183 case INT:
184 printf("%d", *(int*)p->valp);
185 break;
186 case UINT:
187 printf("%u", *(u_int*)p->valp);
188 break;
189 case XINT:
190 printf("0x%x", *(u_int*)p->valp);
191 break;
192 case UCHAR:
193 printf("%u", *(u_char*)p->valp);
194 break;
195 case ULONG:
196 printf("%lu", *(u_long*)p->valp);
197 break;
198 case P_R:
199 v = *(u_int*)p->valp;
200 cm = "";
201 if (v & AUMODE_PLAY) {
202 if (v & AUMODE_PLAY_ALL)
203 printf("play");
204 else
205 printf("playsync");
206 cm = ",";
207 }
208 if (v & AUMODE_RECORD)
209 printf("%srecord", cm);
210 break;
211 case ENC:
212 v = *(u_int*)p->valp;
213 encstr = audio_enc_from_val(v);
214 if (encstr)
215 printf("%s", encstr);
216 else
217 printf("%u", v);
218 break;
219 case PROPS:
220 v = *(u_int*)p->valp;
221 for (cm = "", i = 0; props[i].name; i++) {
222 if (v & props[i].prop) {
223 printf("%s%s", cm, props[i].name);
224 cm = ",";
225 }
226 }
227 break;
228 case FORMAT:
229 prfield(p + 1, 0);
230 printf(",");
231 prfield(p + 3, 0);
232 printf(",");
233 prfield(p + 4, 0);
234 printf(",");
235 prfield(p + 5, 0);
236 break;
237 default:
238 errx(1, "Invalid print format.");
239 }
240 }
241
242 void
243 rdfield(p, q)
244 struct field *p;
245 char *q;
246 {
247 int enc;
248 u_int u;
249 char *s;
250
251 switch(p->format) {
252 case UINT:
253 if (sscanf(q, "%u", (unsigned int *)p->valp) != 1)
254 errx(1, "Bad number: %s", q);
255 break;
256 case UCHAR:
257 if (sscanf(q, "%u", &u) != 1)
258 errx(1, "Bad number: %s", q);
259 else
260 *(u_char *)p->valp = u;
261 break;
262 case XINT:
263 if (sscanf(q, "0x%x", (unsigned int *)p->valp) != 1 &&
264 sscanf(q, "%x", (unsigned int *)p->valp) != 1)
265 errx(1, "Bad number: %s", q);
266 break;
267 case ENC:
268 enc = audio_enc_to_val(q);
269 if (enc >= 0)
270 *(u_int*)p->valp = enc;
271 else
272 errx(1, "Unknown encoding: %s", q);
273 break;
274 case FORMAT:
275 s = strsep(&q, ",");
276 if (s)
277 rdfield(p + 1, s);
278 s = strsep(&q, ",");
279 if (s)
280 rdfield(p + 3, s);
281 s = strsep(&q, ",");
282 if (s)
283 rdfield(p + 4, s);
284 s = strsep(&q, ",");
285 if (s)
286 rdfield(p + 5, s);
287 if (!s || q)
288 errx(1, "Bad format");
289 break;
290 default:
291 errx(1, "Invalid read format.");
292 }
293 p->flags |= SET;
294 }
295
296 void
297 getinfo(fd)
298 int fd;
299 {
300 int pos, i;
301
302 if (ioctl(fd, AUDIO_GETDEV, &adev) < 0)
303 err(1, "AUDIO_GETDEV");
304 for (pos = 0, i = 0; ; i++) {
305 audio_encoding_t enc;
306 enc.index = i;
307 if (ioctl(fd, AUDIO_GETENC, &enc) < 0)
308 break;
309 if (pos >= sizeof(encbuf)-1)
310 break;
311 if (pos)
312 encbuf[pos++] = ',';
313 if (pos >= sizeof(encbuf)-1)
314 break;
315 pos += snprintf(encbuf+pos, sizeof(encbuf)-pos, "%s:%d%s",
316 enc.name, enc.precision,
317 enc.flags & AUDIO_ENCODINGFLAG_EMULATED ? "*" : "");
318 }
319 if (ioctl(fd, AUDIO_GETFD, &fullduplex) < 0)
320 err(1, "AUDIO_GETFD");
321 if (ioctl(fd, AUDIO_GETPROPS, &properties) < 0)
322 err(1, "AUDIO_GETPROPS");
323 if (ioctl(fd, AUDIO_RERROR, &rerror) < 0)
324 err(1, "AUDIO_RERROR");
325 if (ioctl(fd, AUDIO_GETINFO, &info) < 0)
326 err(1, "AUDIO_GETINFO");
327 }
328
329 void
330 usage()
331 {
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 const char *file;
347 char *sep = "=";
348 extern char *__progname;
349
350 file = getenv("AUDIOCTLDEVICE");
351 if (file == 0)
352 file = _PATH_AUDIOCTL;
353
354 prog = __progname;
355
356 while ((ch = getopt(argc, argv, "af:nw")) != -1) {
357 switch(ch) {
358 case 'a':
359 aflag++;
360 break;
361 case 'w':
362 wflag++;
363 break;
364 case 'n':
365 sep = 0;
366 break;
367 case 'f':
368 file = optarg;
369 break;
370 case '?':
371 default:
372 usage();
373 }
374 }
375 argc -= optind;
376 argv += optind;
377
378 fd = open(file, O_WRONLY);
379 if (fd < 0)
380 fd = open(file, O_RDONLY);
381 #ifdef _PATH_OAUDIOCTL
382 /* Allow the non-unit device to be used. */
383 if (fd < 0 && file == _PATH_AUDIOCTL) {
384 file = _PATH_OAUDIOCTL;
385 fd = open(file, O_WRONLY);
386 if (fd < 0)
387 fd = open(file, O_RDONLY);
388 }
389 #endif
390 if (fd < 0)
391 err(1, "%s", file);
392
393 if (!wflag)
394 getinfo(fd);
395
396 if (argc == 0 && aflag && !wflag) {
397 for (i = 0; fields[i].name; i++) {
398 if (!(fields[i].flags & ALIAS)) {
399 prfield(&fields[i], sep);
400 printf("\n");
401 }
402 }
403 } else if (argc > 0 && !aflag) {
404 if (wflag) {
405 audioctl_write(fd, argc, argv);
406 if (sep) {
407 getinfo(fd);
408 for (i = 0; fields[i].name; i++) {
409 if (fields[i].flags & SET) {
410 printf("%s: -> ", fields[i].name);
411 prfield(&fields[i], 0);
412 printf("\n");
413 }
414 }
415 }
416 } else {
417 struct field *p;
418 while (argc--) {
419 p = findfield(*argv);
420 if (p == 0) {
421 if (strchr(*argv, '='))
422 warnx("field %s does not exist (use -w to set a variable)", *argv);
423 else
424 warnx("field %s does not exist", *argv);
425 } else {
426 prfield(p, sep);
427 printf("\n");
428 }
429 argv++;
430 }
431 }
432 } else
433 usage();
434 exit(0);
435 }
436
437 void
438 audioctl_write(fd, argc, argv)
439 int fd;
440 int argc;
441 char *argv[];
442 {
443 struct field *p;
444
445 AUDIO_INITINFO(&info);
446 while (argc--) {
447 char *q;
448
449 q = strchr(*argv, '=');
450 if (q) {
451 *q++ = 0;
452 p = findfield(*argv);
453 if (p == 0)
454 warnx("field `%s' does not exist", *argv);
455 else {
456 if (p->flags & READONLY)
457 warnx("`%s' is read only", *argv);
458 else {
459 rdfield(p, q);
460 if (p->valp == &fullduplex)
461 if (ioctl(fd, AUDIO_SETFD, &fullduplex) < 0)
462 err(1, "set failed");
463 }
464 }
465 } else
466 warnx("No `=' in %s", *argv);
467 argv++;
468 }
469 if (ioctl(fd, AUDIO_SETINFO, &info) < 0)
470 err(1, "set failed");
471 }
472