ctl.c revision 1.39 1 /* $NetBSD: ctl.c,v 1.39 2011/08/28 01:17:48 joerg 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 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31 #include <sys/cdefs.h>
32
33 #ifndef lint
34 __RCSID("$NetBSD: ctl.c,v 1.39 2011/08/28 01:17:48 joerg Exp $");
35 #endif
36
37
38 #include <sys/types.h>
39 #include <sys/stat.h>
40 #include <sys/ioctl.h>
41 #include <sys/audioio.h>
42
43 #include <err.h>
44 #include <fcntl.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include <unistd.h>
49
50 #include <paths.h>
51
52 #include "libaudio.h"
53
54 static struct field *findfield(const char *name);
55 static void prfield(const struct field *p, const char *sep);
56 static void rdfield(struct field *p, char *q);
57 static void getinfo(int fd);
58 static void audioctl_write(int, int, char *[]);
59 __dead static void usage(void);
60
61 static audio_device_t adev;
62
63 static audio_info_t info;
64
65 static char encbuf[1000];
66
67 static int properties, fullduplex, rerror;
68
69 static struct field {
70 const char *name;
71 void *valp;
72 int format;
73 #define STRING 1
74 #define INT 2
75 #define UINT 3
76 #define P_R 4
77 #define ULONG 5 /* XXX obsolete now */
78 #define UCHAR 6
79 #define ENC 7
80 #define PROPS 8
81 #define XINT 9
82 #define FORMAT 10
83 char flags;
84 #define READONLY 1
85 #define ALIAS 2
86 #define SET 4
87 } fields[] = {
88 { "name", &adev.name, STRING, READONLY },
89 { "version", &adev.version, STRING, READONLY },
90 { "config", &adev.config, STRING, READONLY },
91 { "encodings", encbuf, STRING, READONLY },
92 { "properties", &properties, PROPS, READONLY },
93 { "full_duplex", &fullduplex, UINT, 0 },
94 { "fullduplex", &fullduplex, UINT, 0 },
95 { "blocksize", &info.blocksize, UINT, 0 },
96 { "hiwat", &info.hiwat, UINT, 0 },
97 { "lowat", &info.lowat, UINT, 0 },
98 { "monitor_gain", &info.monitor_gain, UINT, 0 },
99 { "mode", &info.mode, P_R, READONLY },
100 { "play", &info.play, FORMAT, ALIAS },
101 { "play.rate", &info.play.sample_rate, UINT, 0 },
102 { "play.sample_rate", &info.play.sample_rate, UINT, ALIAS },
103 { "play.channels", &info.play.channels, UINT, 0 },
104 { "play.precision", &info.play.precision, UINT, 0 },
105 { "play.encoding", &info.play.encoding, ENC, 0 },
106 { "play.gain", &info.play.gain, UINT, 0 },
107 { "play.balance", &info.play.balance, UCHAR, 0 },
108 { "play.port", &info.play.port, XINT, 0 },
109 { "play.avail_ports", &info.play.avail_ports, XINT, 0 },
110 { "play.seek", &info.play.seek, UINT, READONLY },
111 { "play.samples", &info.play.samples, UINT, READONLY },
112 { "play.eof", &info.play.eof, UINT, READONLY },
113 { "play.pause", &info.play.pause, UCHAR, 0 },
114 { "play.error", &info.play.error, UCHAR, READONLY },
115 { "play.waiting", &info.play.waiting, UCHAR, READONLY },
116 { "play.open", &info.play.open, UCHAR, READONLY },
117 { "play.active", &info.play.active, UCHAR, READONLY },
118 { "play.buffer_size", &info.play.buffer_size, UINT, 0 },
119 { "record", &info.record, FORMAT, ALIAS },
120 { "record.rate", &info.record.sample_rate,UINT, 0 },
121 { "record.sample_rate", &info.record.sample_rate,UINT, ALIAS },
122 { "record.channels", &info.record.channels, UINT, 0 },
123 { "record.precision", &info.record.precision, UINT, 0 },
124 { "record.encoding", &info.record.encoding, ENC, 0 },
125 { "record.gain", &info.record.gain, UINT, 0 },
126 { "record.balance", &info.record.balance, UCHAR, 0 },
127 { "record.port", &info.record.port, XINT, 0 },
128 { "record.avail_ports", &info.record.avail_ports,XINT, 0 },
129 { "record.seek", &info.record.seek, UINT, READONLY },
130 { "record.samples", &info.record.samples, UINT, READONLY },
131 { "record.eof", &info.record.eof, UINT, READONLY },
132 { "record.pause", &info.record.pause, UCHAR, 0 },
133 { "record.error", &info.record.error, UCHAR, READONLY },
134 { "record.waiting", &info.record.waiting, UCHAR, READONLY },
135 { "record.open", &info.record.open, UCHAR, READONLY },
136 { "record.active", &info.record.active, UCHAR, READONLY },
137 { "record.buffer_size", &info.record.buffer_size,UINT, 0 },
138 { "record.errors", &rerror, INT, READONLY },
139 { .name = NULL },
140 };
141
142 static const struct {
143 const char *name;
144 u_int prop;
145 } props[] = {
146 { "full_duplex", AUDIO_PROP_FULLDUPLEX },
147 { "mmap", AUDIO_PROP_MMAP },
148 { "independent", AUDIO_PROP_INDEPENDENT },
149 { .name = NULL },
150 };
151
152 static struct field *
153 findfield(const char *name)
154 {
155 int i;
156 for (i = 0; fields[i].name; i++)
157 if (strcmp(fields[i].name, name) == 0)
158 return &fields[i];
159 return 0;
160 }
161
162 static void
163 prfield(const struct field *p, const char *sep)
164 {
165 u_int v;
166 const char *cm, *encstr;
167 int i;
168
169 if (sep)
170 printf("%s%s", p->name, sep);
171 switch(p->format) {
172 case STRING:
173 printf("%s", (const char*)p->valp);
174 break;
175 case INT:
176 printf("%d", *(const int*)p->valp);
177 break;
178 case UINT:
179 printf("%u", *(const u_int*)p->valp);
180 break;
181 case XINT:
182 printf("0x%x", *(const u_int*)p->valp);
183 break;
184 case UCHAR:
185 printf("%u", *(const u_char*)p->valp);
186 break;
187 case ULONG:
188 printf("%lu", *(const u_long*)p->valp);
189 break;
190 case P_R:
191 v = *(const u_int*)p->valp;
192 cm = "";
193 if (v & AUMODE_PLAY) {
194 if (v & AUMODE_PLAY_ALL)
195 printf("play");
196 else
197 printf("playsync");
198 cm = ",";
199 }
200 if (v & AUMODE_RECORD)
201 printf("%srecord", cm);
202 break;
203 case ENC:
204 v = *(u_int*)p->valp;
205 encstr = audio_enc_from_val(v);
206 if (encstr)
207 printf("%s", encstr);
208 else
209 printf("%u", v);
210 break;
211 case PROPS:
212 v = *(u_int*)p->valp;
213 for (cm = "", i = 0; props[i].name; i++) {
214 if (v & props[i].prop) {
215 printf("%s%s", cm, props[i].name);
216 cm = ",";
217 }
218 }
219 break;
220 case FORMAT:
221 prfield(p + 1, 0);
222 printf(",");
223 prfield(p + 3, 0);
224 printf(",");
225 prfield(p + 4, 0);
226 printf(",");
227 prfield(p + 5, 0);
228 break;
229 default:
230 errx(1, "Invalid print format.");
231 }
232 }
233
234 static void
235 rdfield(struct field *p, char *q)
236 {
237 int enc;
238 u_int u;
239 char *s;
240
241 switch(p->format) {
242 case UINT:
243 if (sscanf(q, "%u", (unsigned int *)p->valp) != 1)
244 errx(1, "Bad number: %s", q);
245 break;
246 case UCHAR:
247 if (sscanf(q, "%u", &u) != 1)
248 errx(1, "Bad number: %s", q);
249 else
250 *(u_char *)p->valp = u;
251 break;
252 case XINT:
253 if (sscanf(q, "0x%x", (unsigned int *)p->valp) != 1 &&
254 sscanf(q, "%x", (unsigned int *)p->valp) != 1)
255 errx(1, "Bad number: %s", q);
256 break;
257 case ENC:
258 enc = audio_enc_to_val(q);
259 if (enc >= 0)
260 *(u_int*)p->valp = enc;
261 else
262 errx(1, "Unknown encoding: %s", q);
263 break;
264 case FORMAT:
265 s = strsep(&q, ",");
266 if (s)
267 rdfield(p + 1, s);
268 s = strsep(&q, ",");
269 if (s)
270 rdfield(p + 3, s);
271 s = strsep(&q, ",");
272 if (s)
273 rdfield(p + 4, s);
274 s = strsep(&q, ",");
275 if (s)
276 rdfield(p + 5, s);
277 if (!s || q)
278 errx(1, "Bad format");
279 break;
280 default:
281 errx(1, "Invalid read format.");
282 }
283 p->flags |= SET;
284 }
285
286 static void
287 getinfo(int fd)
288 {
289 int pos, i;
290
291 if (ioctl(fd, AUDIO_GETDEV, &adev) < 0)
292 err(1, "AUDIO_GETDEV");
293 for (pos = 0, i = 0; ; i++) {
294 audio_encoding_t enc;
295 enc.index = i;
296 if (ioctl(fd, AUDIO_GETENC, &enc) < 0)
297 break;
298 if (pos >= (int)sizeof(encbuf)-1)
299 break;
300 if (pos)
301 encbuf[pos++] = ',';
302 if (pos >= (int)sizeof(encbuf)-1)
303 break;
304 pos += snprintf(encbuf+pos, sizeof(encbuf)-pos, "%s:%d%s",
305 enc.name, enc.precision,
306 enc.flags & AUDIO_ENCODINGFLAG_EMULATED ? "*" : "");
307 }
308 if (ioctl(fd, AUDIO_GETFD, &fullduplex) < 0)
309 err(1, "AUDIO_GETFD");
310 if (ioctl(fd, AUDIO_GETPROPS, &properties) < 0)
311 err(1, "AUDIO_GETPROPS");
312 if (ioctl(fd, AUDIO_RERROR, &rerror) < 0)
313 err(1, "AUDIO_RERROR");
314 if (ioctl(fd, AUDIO_GETINFO, &info) < 0)
315 err(1, "AUDIO_GETINFO");
316 }
317
318 static void
319 usage(void)
320 {
321 const char *prog = getprogname();
322
323 fprintf(stderr, "Usage: %s [-d file] [-n] name ...\n", prog);
324 fprintf(stderr, "Usage: %s [-d file] [-n] -w name=value ...\n", prog);
325 fprintf(stderr, "Usage: %s [-d file] [-n] -a\n", prog);
326 exit(1);
327 }
328
329 int
330 main(int argc, char *argv[])
331 {
332 int fd, i, ch;
333 int aflag = 0, wflag = 0;
334 const char *deffile = _PATH_AUDIOCTL;
335 const char *file;
336 const char *sep = "=";
337
338 file = getenv("AUDIOCTLDEVICE");
339 if (file == NULL)
340 file = deffile;
341
342 while ((ch = getopt(argc, argv, "ad:f: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': /* compatibility */
354 case 'd':
355 file = optarg;
356 break;
357 case '?':
358 default:
359 usage();
360 }
361 }
362 argc -= optind;
363 argv += optind;
364
365 fd = open(file, O_WRONLY);
366 if (fd < 0)
367 fd = open(file, O_RDONLY);
368 if (fd < 0 && file == deffile) {
369 file = _PATH_AUDIOCTL0;
370 fd = open(file, O_WRONLY);
371 if (fd < 0)
372 fd = open(file, O_RDONLY);
373 }
374
375 if (fd < 0)
376 err(1, "%s", file);
377
378 if (!wflag)
379 getinfo(fd);
380
381 if (argc == 0 && aflag && !wflag) {
382 for (i = 0; fields[i].name; i++) {
383 if (!(fields[i].flags & ALIAS)) {
384 prfield(&fields[i], sep);
385 printf("\n");
386 }
387 }
388 } else if (argc > 0 && !aflag) {
389 if (wflag) {
390 audioctl_write(fd, argc, argv);
391 if (sep) {
392 getinfo(fd);
393 for (i = 0; fields[i].name; i++) {
394 if (fields[i].flags & SET) {
395 printf("%s: -> ", fields[i].name);
396 prfield(&fields[i], 0);
397 printf("\n");
398 }
399 }
400 }
401 } else {
402 struct field *p;
403 while (argc--) {
404 p = findfield(*argv);
405 if (p == 0) {
406 if (strchr(*argv, '='))
407 warnx("field %s does not exist (use -w to set a variable)", *argv);
408 else
409 warnx("field %s does not exist", *argv);
410 } else {
411 prfield(p, sep);
412 printf("\n");
413 }
414 argv++;
415 }
416 }
417 } else
418 usage();
419 exit(0);
420 }
421
422 static void
423 audioctl_write(int fd, int argc, char *argv[])
424 {
425 struct field *p;
426
427 AUDIO_INITINFO(&info);
428 while (argc--) {
429 char *q;
430
431 q = strchr(*argv, '=');
432 if (q) {
433 *q++ = 0;
434 p = findfield(*argv);
435 if (p == 0)
436 warnx("field `%s' does not exist", *argv);
437 else {
438 if (p->flags & READONLY)
439 warnx("`%s' is read only", *argv);
440 else {
441 rdfield(p, q);
442 if (p->valp == &fullduplex)
443 if (ioctl(fd, AUDIO_SETFD, &fullduplex) < 0)
444 err(1, "set failed");
445 }
446 }
447 } else
448 warnx("No `=' in %s", *argv);
449 argv++;
450 }
451 if (ioctl(fd, AUDIO_SETINFO, &info) < 0)
452 err(1, "set failed");
453 }
454