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