GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/mixerctl/mixerctl.c Lines: 0 182 0.0 %
Date: 2016-12-06 Branches: 0 164 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: mixerctl.c,v 1.30 2015/02/08 23:40:34 deraadt Exp $	*/
2
/*	$NetBSD: mixerctl.c,v 1.11 1998/04/27 16:55:23 augustss Exp $	*/
3
4
/*
5
 * Copyright (c) 1997 The NetBSD Foundation, Inc.
6
 * All rights reserved.
7
 *
8
 * Author: Lennart Augustsson, with some code and ideas from Chuck Cranor.
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
32
/*
33
 * mixerctl(1) - a program to control audio mixing.
34
 */
35
36
#include <sys/types.h>
37
#include <sys/ioctl.h>
38
#include <sys/audioio.h>
39
40
#include <err.h>
41
#include <errno.h>
42
#include <fcntl.h>
43
#include <limits.h>
44
#include <stdio.h>
45
#include <stdlib.h>
46
#include <string.h>
47
#include <unistd.h>
48
49
struct field *findfield(char *);
50
void adjlevel(char **, u_char *, int);
51
void catstr(char *, char *, char *);
52
void prfield(struct field *, char *, int, mixer_ctrl_t *);
53
void rdfield(int, struct field *, char *, int, char *);
54
__dead void usage(void);
55
56
#define FIELD_NAME_MAX	64
57
58
struct field {
59
	char name[FIELD_NAME_MAX];
60
	mixer_ctrl_t *valp;
61
	mixer_devinfo_t *infp;
62
} *fields, *rfields;
63
64
mixer_ctrl_t *values;
65
mixer_devinfo_t *infos;
66
67
void
68
catstr(char *p, char *q, char *out)
69
{
70
	char tmp[FIELD_NAME_MAX];
71
72
	snprintf(tmp, FIELD_NAME_MAX, "%s.%s", p, q);
73
	strlcpy(out, tmp, FIELD_NAME_MAX);
74
}
75
76
struct field *
77
findfield(char *name)
78
{
79
	int i;
80
	for (i = 0; fields[i].name[0] != '\0'; i++)
81
		if (strcmp(fields[i].name, name) == 0)
82
			return &fields[i];
83
	return (0);
84
}
85
86
#define e_member_name	un.e.member[i].label.name
87
#define s_member_name	un.s.member[i].label.name
88
89
void
90
prfield(struct field *p, char *sep, int prvalset, mixer_ctrl_t *m)
91
{
92
	int i, n;
93
94
	if (sep)
95
		printf("%s%s", p->name, sep);
96
	switch (m->type) {
97
	case AUDIO_MIXER_ENUM:
98
		for (i = 0; i < p->infp->un.e.num_mem; i++)
99
			if (p->infp->un.e.member[i].ord == m->un.ord)
100
				printf("%s",
101
					p->infp->e_member_name);
102
		if (prvalset) {
103
			printf("  [ ");
104
			for (i = 0; i < p->infp->un.e.num_mem; i++)
105
				printf("%s ", p->infp->e_member_name);
106
			printf("]");
107
		}
108
		break;
109
	case AUDIO_MIXER_SET:
110
		for (n = i = 0; i < p->infp->un.s.num_mem; i++)
111
			if (m->un.mask & p->infp->un.s.member[i].mask)
112
				printf("%s%s", n++ ? "," : "",
113
						p->infp->s_member_name);
114
		if (prvalset) {
115
			printf("  { ");
116
			for (i = 0; i < p->infp->un.s.num_mem; i++)
117
				printf("%s ", p->infp->s_member_name);
118
			printf("}");
119
		}
120
		break;
121
	case AUDIO_MIXER_VALUE:
122
		if (m->un.value.num_channels == 1)
123
			printf("%d", m->un.value.level[0]);
124
		else
125
			printf("%d,%d", m->un.value.level[0],
126
			    m->un.value.level[1]);
127
		if (prvalset)
128
			printf(" %s", p->infp->un.v.units.name);
129
		break;
130
	default:
131
		errx(1, "Invalid format.");
132
	}
133
}
134
135
void
136
adjlevel(char **p, u_char *olevel, int more)
137
{
138
	char *ep, *cp = *p;
139
	long inc;
140
	u_char level;
141
142
	if (*cp != '+' && *cp != '-')
143
		*olevel = 0;		/* absolute setting */
144
145
	errno = 0;
146
	inc = strtol(cp, &ep, 10);
147
	if (*cp == '\0' || (*ep != '\0' && *ep != ',') ||
148
	    (errno == ERANGE && (inc == LONG_MAX || inc == LONG_MIN)))
149
		errx(1, "Bad number %s", cp);
150
	if (*ep == ',' && !more)
151
		errx(1, "Too many values");
152
	*p = ep;
153
154
	if (inc < AUDIO_MIN_GAIN - *olevel)
155
		level = AUDIO_MIN_GAIN;
156
	else if (inc > AUDIO_MAX_GAIN - *olevel)
157
		level = AUDIO_MAX_GAIN;
158
	else
159
		level = *olevel + inc;
160
	*olevel = level;
161
}
162
163
void
164
rdfield(int fd, struct field *p, char *q, int quiet, char *sep)
165
{
166
	mixer_ctrl_t *m, oldval;
167
	int i, mask;
168
	char *s;
169
170
	oldval = *p->valp;
171
	m = p->valp;
172
173
	switch (m->type) {
174
	case AUDIO_MIXER_ENUM:
175
		if (strcmp(q, "toggle") == 0) {
176
			for (i = 0; i < p->infp->un.e.num_mem; i++) {
177
				if (m->un.ord == p->infp->un.e.member[i].ord)
178
					break;
179
			}
180
			if (i < p->infp->un.e.num_mem)
181
				i++;
182
			else
183
				i = 0;
184
			m->un.ord = p->infp->un.e.member[i].ord;
185
			break;
186
		}
187
		for (i = 0; i < p->infp->un.e.num_mem; i++)
188
			if (strcmp(p->infp->e_member_name, q) == 0)
189
				break;
190
		if (i < p->infp->un.e.num_mem)
191
			m->un.ord = p->infp->un.e.member[i].ord;
192
		else
193
			errx(1, "Bad enum value %s", q);
194
		break;
195
	case AUDIO_MIXER_SET:
196
		mask = 0;
197
		for (; q && *q; q = s) {
198
			if ((s = strchr(q, ',')) != NULL)
199
				*s++ = 0;
200
			for (i = 0; i < p->infp->un.s.num_mem; i++)
201
				if (strcmp(p->infp->s_member_name, q) == 0)
202
					break;
203
			if (i < p->infp->un.s.num_mem)
204
				mask |= p->infp->un.s.member[i].mask;
205
			else
206
				errx(1, "Bad set value %s", q);
207
		}
208
		m->un.mask = mask;
209
		break;
210
	case AUDIO_MIXER_VALUE:
211
		if (m->un.value.num_channels == 1) {
212
			adjlevel(&q, &m->un.value.level[0], 0);
213
		} else {
214
			adjlevel(&q, &m->un.value.level[0], 1);
215
			if (*q++ == ',')
216
				adjlevel(&q, &m->un.value.level[1], 0);
217
			else
218
				m->un.value.level[1] = m->un.value.level[0];
219
		}
220
		break;
221
	default:
222
		errx(1, "Invalid format.");
223
	}
224
225
	if (ioctl(fd, AUDIO_MIXER_WRITE, p->valp) < 0) {
226
		warn("AUDIO_MIXER_WRITE");
227
	} else if (!quiet) {
228
		if (ioctl(fd, AUDIO_MIXER_READ, p->valp) < 0) {
229
			warn("AUDIO_MIXER_READ");
230
		} else {
231
			if (sep) {
232
				prfield(p, ": ", 0, &oldval);
233
				printf(" -> ");
234
			}
235
			prfield(p, NULL, 0, p->valp);
236
			printf("\n");
237
		}
238
	}
239
}
240
241
int
242
main(int argc, char **argv)
243
{
244
	int fd, i, j, ch, pos;
245
	int aflag = 0, qflag = 0, vflag = 0, tflag = 0;
246
	char *file;
247
	char *sep = "=";
248
	mixer_devinfo_t dinfo;
249
	int ndev;
250
251
	if ((file = getenv("MIXERDEVICE")) == 0 || *file == '\0')
252
		file = "/dev/mixer";
253
254
	while ((ch = getopt(argc, argv, "af:nqtvw")) != -1) {
255
		switch (ch) {
256
		case 'a':
257
			aflag = 1;
258
			break;
259
		case 'w':
260
			/* compat */
261
			break;
262
		case 'v':
263
			vflag = 1;
264
			break;
265
		case 'n':
266
			sep = 0;
267
			break;
268
		case 'f':
269
			file = optarg;
270
			break;
271
		case 'q':
272
			qflag = 1;
273
			break;
274
		case 't':
275
			tflag = 1;
276
			break;
277
		default:
278
			usage();
279
		}
280
	}
281
	argc -= optind;
282
	argv += optind;
283
284
	if (argc == 0 && tflag == 0)
285
		aflag = 1;
286
287
	if ((fd = open(file, O_RDWR)) == -1)
288
		if ((fd = open(file, O_RDONLY)) == -1)
289
			err(1, "%s", file);
290
291
	for (ndev = 0; ; ndev++) {
292
		dinfo.index = ndev;
293
		if (ioctl(fd, AUDIO_MIXER_DEVINFO, &dinfo) < 0)
294
			break;
295
	}
296
297
	if (!ndev)
298
		errx(1, "no mixer devices configured");
299
300
	if ((rfields = calloc(ndev, sizeof *rfields)) == NULL ||
301
	    (fields = calloc(ndev, sizeof *fields)) == NULL ||
302
	    (infos = calloc(ndev, sizeof *infos)) == NULL ||
303
	    (values = calloc(ndev, sizeof *values)) == NULL)
304
		err(1, "calloc()");
305
306
	for (i = 0; i < ndev; i++) {
307
		infos[i].index = i;
308
		if (ioctl(fd, AUDIO_MIXER_DEVINFO, &infos[i]) < 0) {
309
			ndev--;
310
			i--;
311
			continue;
312
		}
313
	}
314
315
	for (i = 0; i < ndev; i++) {
316
		strlcpy(rfields[i].name, infos[i].label.name, FIELD_NAME_MAX);
317
		rfields[i].valp = &values[i];
318
		rfields[i].infp = &infos[i];
319
	}
320
321
	for (i = 0; i < ndev; i++) {
322
		values[i].dev = i;
323
		values[i].type = infos[i].type;
324
		if (infos[i].type != AUDIO_MIXER_CLASS) {
325
			values[i].un.value.num_channels = 2;
326
			if (ioctl(fd, AUDIO_MIXER_READ, &values[i]) < 0) {
327
				values[i].un.value.num_channels = 1;
328
				if (ioctl(fd, AUDIO_MIXER_READ, &values[i]) < 0)
329
					err(1, "AUDIO_MIXER_READ");
330
			}
331
		}
332
	}
333
334
	for (j = i = 0; i < ndev; i++) {
335
		if (infos[i].type != AUDIO_MIXER_CLASS &&
336
		    infos[i].prev == AUDIO_MIXER_LAST) {
337
			fields[j++] = rfields[i];
338
			for (pos = infos[i].next; pos != AUDIO_MIXER_LAST;
339
			    pos = infos[pos].next) {
340
				fields[j] = rfields[pos];
341
				catstr(rfields[i].name, infos[pos].label.name,
342
				    fields[j].name);
343
				j++;
344
			}
345
		}
346
	}
347
348
	for (i = 0; i < j; i++) {
349
		int cls = fields[i].infp->mixer_class;
350
		if (cls >= 0 && cls < ndev)
351
			catstr(infos[cls].label.name, fields[i].name,
352
			    fields[i].name);
353
	}
354
355
	if (!argc && aflag) {
356
		for (i = 0; fields[i].name[0] != '\0'; i++) {
357
			prfield(&fields[i], sep, vflag, fields[i].valp);
358
			printf("\n");
359
		}
360
	} else if (argc > 0 && !aflag) {
361
		struct field *p;
362
363
		while (argc--) {
364
			char *q;
365
366
			ch = 0;
367
			if ((q = strchr(*argv, '=')) != NULL) {
368
				*q++ = '\0';
369
				ch = 1;
370
			}
371
372
			if ((p = findfield(*argv)) == NULL) {
373
				warnx("field %s does not exist", *argv);
374
			} else if (ch || tflag) {
375
				if (tflag && q == NULL)
376
					q = "toggle";
377
				rdfield(fd, p, q, qflag, sep);
378
			} else {
379
				prfield(p, sep, vflag, p->valp);
380
				printf("\n");
381
			}
382
383
			argv++;
384
		}
385
	} else
386
		usage();
387
	exit(0);
388
}
389
390
__dead void
391
usage(void)
392
{
393
	extern char *__progname;	/* from crt0.o */
394
395
	fprintf(stderr,
396
	    "usage: %s [-anv] [-f file]\n"
397
	    "       %s [-nv] [-f file] name ...\n"
398
	    "       %s [-qt] [-f file] name ...\n"
399
	    "       %s [-q] [-f file] name=value ...\n",
400
	    __progname, __progname, __progname, __progname);
401
402
	exit(1);
403
}