ctl.c revision 1.33 1 /* $NetBSD: ctl.c,v 1.33 2003/01/06 13:26:27 wiz 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 (const char *name);
56 void prfield (struct field *p, const char *sep);
57 void rdfield (struct field *p, char *q);
58 void getinfo (int fd);
59 void audioctl_write (int, int, char *[]);
60 void usage (void);
61 int main (int argc, char **argv);
62
63 audio_device_t adev;
64
65 audio_info_t info;
66
67 char encbuf[1000];
68
69 int properties, fullduplex, rerror;
70
71 struct field {
72 const char *name;
73 void *valp;
74 int format;
75 #define STRING 1
76 #define INT 2
77 #define UINT 3
78 #define P_R 4
79 #define ULONG 5 /* XXX obsolete now */
80 #define UCHAR 6
81 #define ENC 7
82 #define PROPS 8
83 #define XINT 9
84 #define FORMAT 10
85 char flags;
86 #define READONLY 1
87 #define ALIAS 2
88 #define SET 4
89 } fields[] = {
90 { "name", &adev.name, STRING, READONLY },
91 { "version", &adev.version, STRING, READONLY },
92 { "config", &adev.config, STRING, READONLY },
93 { "encodings", encbuf, STRING, READONLY },
94 { "properties", &properties, PROPS, READONLY },
95 { "full_duplex", &fullduplex, UINT, 0 },
96 { "fullduplex", &fullduplex, UINT, 0 },
97 { "blocksize", &info.blocksize, UINT, 0 },
98 { "hiwat", &info.hiwat, UINT, 0 },
99 { "lowat", &info.lowat, UINT, 0 },
100 { "monitor_gain", &info.monitor_gain, UINT, 0 },
101 { "mode", &info.mode, P_R, READONLY },
102 { "play", &info.play, FORMAT, ALIAS },
103 { "play.rate", &info.play.sample_rate, UINT, 0 },
104 { "play.sample_rate", &info.play.sample_rate, UINT, ALIAS },
105 { "play.channels", &info.play.channels, UINT, 0 },
106 { "play.precision", &info.play.precision, UINT, 0 },
107 { "play.encoding", &info.play.encoding, ENC, 0 },
108 { "play.gain", &info.play.gain, UINT, 0 },
109 { "play.balance", &info.play.balance, UCHAR, 0 },
110 { "play.port", &info.play.port, XINT, 0 },
111 { "play.avail_ports", &info.play.avail_ports, XINT, 0 },
112 { "play.seek", &info.play.seek, UINT, READONLY },
113 { "play.samples", &info.play.samples, UINT, READONLY },
114 { "play.eof", &info.play.eof, UINT, READONLY },
115 { "play.pause", &info.play.pause, UCHAR, 0 },
116 { "play.error", &info.play.error, UCHAR, READONLY },
117 { "play.waiting", &info.play.waiting, UCHAR, READONLY },
118 { "play.open", &info.play.open, UCHAR, READONLY },
119 { "play.active", &info.play.active, UCHAR, READONLY },
120 { "play.buffer_size", &info.play.buffer_size, UINT, 0 },
121 { "record", &info.record, FORMAT, ALIAS },
122 { "record.rate", &info.record.sample_rate,UINT, 0 },
123 { "record.sample_rate", &info.record.sample_rate,UINT, ALIAS },
124 { "record.channels", &info.record.channels, UINT, 0 },
125 { "record.precision", &info.record.precision, UINT, 0 },
126 { "record.encoding", &info.record.encoding, ENC, 0 },
127 { "record.gain", &info.record.gain, UINT, 0 },
128 { "record.balance", &info.record.balance, UCHAR, 0 },
129 { "record.port", &info.record.port, XINT, 0 },
130 { "record.avail_ports", &info.record.avail_ports,XINT, 0 },
131 { "record.seek", &info.record.seek, UINT, READONLY },
132 { "record.samples", &info.record.samples, UINT, READONLY },
133 { "record.eof", &info.record.eof, UINT, READONLY },
134 { "record.pause", &info.record.pause, UCHAR, 0 },
135 { "record.error", &info.record.error, UCHAR, READONLY },
136 { "record.waiting", &info.record.waiting, UCHAR, READONLY },
137 { "record.open", &info.record.open, UCHAR, READONLY },
138 { "record.active", &info.record.active, UCHAR, READONLY },
139 { "record.buffer_size", &info.record.buffer_size,UINT, 0 },
140 { "record.errors", &rerror, INT, READONLY },
141 { 0 }
142 };
143
144 static struct {
145 const char *name;
146 u_int prop;
147 } props[] = {
148 { "full_duplex", AUDIO_PROP_FULLDUPLEX },
149 { "mmap", AUDIO_PROP_MMAP },
150 { "independent", AUDIO_PROP_INDEPENDENT },
151 { 0 }
152 };
153
154 struct field *
155 findfield(name)
156 const char *name;
157 {
158 int i;
159 for (i = 0; fields[i].name; i++)
160 if (strcmp(fields[i].name, name) == 0)
161 return &fields[i];
162 return 0;
163 }
164
165 void
166 prfield(p, sep)
167 struct field *p;
168 const char *sep;
169 {
170 u_int v;
171 const char *cm, *encstr;
172 int i;
173
174 if (sep)
175 printf("%s%s", p->name, sep);
176 switch(p->format) {
177 case STRING:
178 printf("%s", (char*)p->valp);
179 break;
180 case INT:
181 printf("%d", *(int*)p->valp);
182 break;
183 case UINT:
184 printf("%u", *(u_int*)p->valp);
185 break;
186 case XINT:
187 printf("0x%x", *(u_int*)p->valp);
188 break;
189 case UCHAR:
190 printf("%u", *(u_char*)p->valp);
191 break;
192 case ULONG:
193 printf("%lu", *(u_long*)p->valp);
194 break;
195 case P_R:
196 v = *(u_int*)p->valp;
197 cm = "";
198 if (v & AUMODE_PLAY) {
199 if (v & AUMODE_PLAY_ALL)
200 printf("play");
201 else
202 printf("playsync");
203 cm = ",";
204 }
205 if (v & AUMODE_RECORD)
206 printf("%srecord", cm);
207 break;
208 case ENC:
209 v = *(u_int*)p->valp;
210 encstr = audio_enc_from_val(v);
211 if (encstr)
212 printf("%s", encstr);
213 else
214 printf("%u", v);
215 break;
216 case PROPS:
217 v = *(u_int*)p->valp;
218 for (cm = "", i = 0; props[i].name; i++) {
219 if (v & props[i].prop) {
220 printf("%s%s", cm, props[i].name);
221 cm = ",";
222 }
223 }
224 break;
225 case FORMAT:
226 prfield(p + 1, 0);
227 printf(",");
228 prfield(p + 3, 0);
229 printf(",");
230 prfield(p + 4, 0);
231 printf(",");
232 prfield(p + 5, 0);
233 break;
234 default:
235 errx(1, "Invalid print format.");
236 }
237 }
238
239 void
240 rdfield(p, q)
241 struct field *p;
242 char *q;
243 {
244 int enc;
245 u_int u;
246 char *s;
247
248 switch(p->format) {
249 case UINT:
250 if (sscanf(q, "%u", (unsigned int *)p->valp) != 1)
251 errx(1, "Bad number: %s", q);
252 break;
253 case UCHAR:
254 if (sscanf(q, "%u", &u) != 1)
255 errx(1, "Bad number: %s", q);
256 else
257 *(u_char *)p->valp = u;
258 break;
259 case XINT:
260 if (sscanf(q, "0x%x", (unsigned int *)p->valp) != 1 &&
261 sscanf(q, "%x", (unsigned int *)p->valp) != 1)
262 errx(1, "Bad number: %s", q);
263 break;
264 case ENC:
265 enc = audio_enc_to_val(q);
266 if (enc >= 0)
267 *(u_int*)p->valp = enc;
268 else
269 errx(1, "Unknown encoding: %s", q);
270 break;
271 case FORMAT:
272 s = strsep(&q, ",");
273 if (s)
274 rdfield(p + 1, s);
275 s = strsep(&q, ",");
276 if (s)
277 rdfield(p + 3, s);
278 s = strsep(&q, ",");
279 if (s)
280 rdfield(p + 4, s);
281 s = strsep(&q, ",");
282 if (s)
283 rdfield(p + 5, s);
284 if (!s || q)
285 errx(1, "Bad format");
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 >= sizeof(encbuf)-1)
307 break;
308 if (pos)
309 encbuf[pos++] = ',';
310 if (pos >= sizeof(encbuf)-1)
311 break;
312 pos += snprintf(encbuf+pos, sizeof(encbuf)-pos, "%s:%d%s",
313 enc.name, enc.precision,
314 enc.flags & AUDIO_ENCODINGFLAG_EMULATED ? "*" : "");
315 }
316 if (ioctl(fd, AUDIO_GETFD, &fullduplex) < 0)
317 err(1, "AUDIO_GETFD");
318 if (ioctl(fd, AUDIO_GETPROPS, &properties) < 0)
319 err(1, "AUDIO_GETPROPS");
320 if (ioctl(fd, AUDIO_RERROR, &rerror) < 0)
321 err(1, "AUDIO_RERROR");
322 if (ioctl(fd, AUDIO_GETINFO, &info) < 0)
323 err(1, "AUDIO_GETINFO");
324 }
325
326 void
327 usage()
328 {
329 const char *prog = getprogname();
330
331 fprintf(stderr, "Usage: %s [-d file] [-n] name ...\n", prog);
332 fprintf(stderr, "Usage: %s [-d file] [-n] -w name=value ...\n", prog);
333 fprintf(stderr, "Usage: %s [-d file] [-n] -a\n", prog);
334 exit(1);
335 }
336
337 int
338 main(argc, argv)
339 int argc;
340 char **argv;
341 {
342 int fd, i, ch;
343 int aflag = 0, wflag = 0;
344 const char *deffile = _PATH_AUDIOCTL;
345 const char *file;
346 const char *sep = "=";
347
348 file = getenv("AUDIOCTLDEVICE");
349 if (file == NULL)
350 file = deffile;
351
352 while ((ch = getopt(argc, argv, "ad:f:nw")) != -1) {
353 switch(ch) {
354 case 'a':
355 aflag++;
356 break;
357 case 'w':
358 wflag++;
359 break;
360 case 'n':
361 sep = 0;
362 break;
363 case 'f': /* compatibility */
364 case 'd':
365 file = optarg;
366 break;
367 case '?':
368 default:
369 usage();
370 }
371 }
372 argc -= optind;
373 argv += optind;
374
375 fd = open(file, O_WRONLY);
376 if (fd < 0)
377 fd = open(file, O_RDONLY);
378 if (fd < 0 && file == deffile) {
379 file = _PATH_AUDIOCTL0;
380 fd = open(file, O_WRONLY);
381 if (fd < 0)
382 fd = open(file, O_RDONLY);
383 }
384
385 if (fd < 0)
386 err(1, "%s", file);
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 printf("\n");
396 }
397 }
398 } else if (argc > 0 && !aflag) {
399 if (wflag) {
400 audioctl_write(fd, argc, argv);
401 if (sep) {
402 getinfo(fd);
403 for (i = 0; fields[i].name; i++) {
404 if (fields[i].flags & SET) {
405 printf("%s: -> ", fields[i].name);
406 prfield(&fields[i], 0);
407 printf("\n");
408 }
409 }
410 }
411 } else {
412 struct field *p;
413 while (argc--) {
414 p = findfield(*argv);
415 if (p == 0) {
416 if (strchr(*argv, '='))
417 warnx("field %s does not exist (use -w to set a variable)", *argv);
418 else
419 warnx("field %s does not exist", *argv);
420 } else {
421 prfield(p, sep);
422 printf("\n");
423 }
424 argv++;
425 }
426 }
427 } else
428 usage();
429 exit(0);
430 }
431
432 void
433 audioctl_write(fd, argc, argv)
434 int fd;
435 int argc;
436 char *argv[];
437 {
438 struct field *p;
439
440 AUDIO_INITINFO(&info);
441 while (argc--) {
442 char *q;
443
444 q = strchr(*argv, '=');
445 if (q) {
446 *q++ = 0;
447 p = findfield(*argv);
448 if (p == 0)
449 warnx("field `%s' does not exist", *argv);
450 else {
451 if (p->flags & READONLY)
452 warnx("`%s' is read only", *argv);
453 else {
454 rdfield(p, q);
455 if (p->valp == &fullduplex)
456 if (ioctl(fd, AUDIO_SETFD, &fullduplex) < 0)
457 err(1, "set failed");
458 }
459 }
460 } else
461 warnx("No `=' in %s", *argv);
462 argv++;
463 }
464 if (ioctl(fd, AUDIO_SETINFO, &info) < 0)
465 err(1, "set failed");
466 }
467