Home | History | Annotate | Line # | Download | only in mixerctl
mixerctl.c revision 1.21
      1 /*	$NetBSD: mixerctl.c,v 1.21 2003/10/23 22:17:58 cube 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) and 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  * 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 #include <sys/cdefs.h>
     39 
     40 #ifndef lint
     41 __RCSID("$NetBSD: mixerctl.c,v 1.21 2003/10/23 22:17:58 cube Exp $");
     42 #endif
     43 
     44 #include <stdio.h>
     45 #include <stdlib.h>
     46 #include <fcntl.h>
     47 #include <err.h>
     48 #include <unistd.h>
     49 #include <string.h>
     50 #include <sys/types.h>
     51 #include <sys/ioctl.h>
     52 #include <sys/audioio.h>
     53 
     54 #include <paths.h>
     55 
     56 FILE *out = stdout;
     57 int vflag = 0;
     58 
     59 char *prog;
     60 
     61 struct field {
     62 	char *name;
     63 	mixer_ctrl_t *valp;
     64 	mixer_devinfo_t *infp;
     65 	char changed;
     66 } *fields, *rfields;
     67 
     68 mixer_ctrl_t *values;
     69 mixer_devinfo_t *infos;
     70 
     71 static char *
     72 catstr(char *p, char *q)
     73 {
     74 	char *r;
     75 
     76 	asprintf(&r, "%s.%s", p, q);
     77 	if (!r)
     78 		err(1, "malloc");
     79 	return r;
     80 }
     81 
     82 static struct field *
     83 findfield(char *name)
     84 {
     85 	int i;
     86 	for(i = 0; fields[i].name; i++)
     87 		if (strcmp(fields[i].name, name) == 0)
     88 			return &fields[i];
     89 	return 0;
     90 }
     91 
     92 static void
     93 prfield(struct field *p, char *sep, int prvalset)
     94 {
     95 	mixer_ctrl_t *m;
     96 	int i, n;
     97 
     98 	if (sep)
     99 		fprintf(out, "%s%s", p->name, sep);
    100 	m = p->valp;
    101 	switch(m->type) {
    102 	case AUDIO_MIXER_ENUM:
    103 		for(i = 0; i < p->infp->un.e.num_mem; i++)
    104 			if (p->infp->un.e.member[i].ord == m->un.ord)
    105 				fprintf(out, "%s",
    106 					p->infp->un.e.member[i].label.name);
    107 		if (prvalset) {
    108 			fprintf(out, "  [ ");
    109 			for(i = 0; i < p->infp->un.e.num_mem; i++)
    110 				fprintf(out, "%s ",
    111 				    p->infp->un.e.member[i].label.name);
    112 			fprintf(out, "]");
    113 		}
    114 		break;
    115 	case AUDIO_MIXER_SET:
    116 		for(n = i = 0; i < p->infp->un.s.num_mem; i++)
    117 			if (m->un.mask & p->infp->un.s.member[i].mask)
    118 				fprintf(out, "%s%s", n++ ? "," : "",
    119 					p->infp->un.s.member[i].label.name);
    120 		if (prvalset) {
    121 			fprintf(out, "  { ");
    122 			for(i = 0; i < p->infp->un.s.num_mem; i++)
    123 				fprintf(out, "%s ",
    124 				    p->infp->un.s.member[i].label.name);
    125 			fprintf(out, "}");
    126 		}
    127 		break;
    128 	case AUDIO_MIXER_VALUE:
    129 		if (m->un.value.num_channels == 1)
    130 			fprintf(out, "%d", m->un.value.level[0]);
    131 		else
    132 			fprintf(out, "%d,%d", m->un.value.level[0],
    133 			       m->un.value.level[1]);
    134 		if (prvalset) {
    135 			fprintf(out, " %s", p->infp->un.v.units.name);
    136 			if (p->infp->un.v.delta)
    137 				fprintf(out, " delta=%d", p->infp->un.v.delta);
    138 		}
    139 		break;
    140 	default:
    141 		printf("\n");
    142 		errx(1, "Invalid format.");
    143 	}
    144 }
    145 
    146 static int
    147 rdfield(struct field *p, char *q)
    148 {
    149 	mixer_ctrl_t *m;
    150 	int v, v0, v1, mask;
    151 	int i;
    152 	char *s;
    153 
    154 	m = p->valp;
    155 	switch(m->type) {
    156 	case AUDIO_MIXER_ENUM:
    157 		for(i = 0; i < p->infp->un.e.num_mem; i++)
    158 			if (strcmp(p->infp->un.e.member[i].label.name, q) == 0)
    159 				break;
    160 		if (i < p->infp->un.e.num_mem)
    161 			m->un.ord = p->infp->un.e.member[i].ord;
    162 		else {
    163 			warnx("Bad enum value %s", q);
    164 			return 0;
    165 		}
    166 		break;
    167 	case AUDIO_MIXER_SET:
    168 		mask = 0;
    169 		for(v = 0; q && *q; q = s) {
    170 			s = strchr(q, ',');
    171 			if (s)
    172 				*s++ = 0;
    173 			for(i = 0; i < p->infp->un.s.num_mem; i++)
    174 				if (strcmp(p->infp->un.s.member[i].label.name,
    175 					   q) == 0)
    176 					break;
    177 			if (i < p->infp->un.s.num_mem) {
    178 				mask |= p->infp->un.s.member[i].mask;
    179 			} else {
    180 				warnx("Bad set value %s", q);
    181 				return 0;
    182 			}
    183 		}
    184 		m->un.mask = mask;
    185 		break;
    186 	case AUDIO_MIXER_VALUE:
    187 		if (m->un.value.num_channels == 1) {
    188 			if (sscanf(q, "%d", &v) == 1) {
    189 				m->un.value.level[0] = v;
    190 			} else {
    191 				warnx("Bad number %s", q);
    192 				return 0;
    193 			}
    194 		} else {
    195 			if (sscanf(q, "%d,%d", &v0, &v1) == 2) {
    196 				m->un.value.level[0] = v0;
    197 				m->un.value.level[1] = v1;
    198 			} else if (sscanf(q, "%d", &v) == 1) {
    199 				m->un.value.level[0] = m->un.value.level[1] = v;
    200 			} else {
    201 				warnx("Bad numbers %s", q);
    202 				return 0;
    203 			}
    204 		}
    205 		break;
    206 	default:
    207 		errx(1, "Invalid format.");
    208 	}
    209 	p->changed = 1;
    210 	return 1;
    211 }
    212 
    213 static int
    214 incfield(struct field *p, int inc)
    215 {
    216 	mixer_ctrl_t *m;
    217 	int i, v;
    218 
    219 	m = p->valp;
    220 	switch(m->type) {
    221 	case AUDIO_MIXER_ENUM:
    222 		m->un.ord += inc;
    223 		if (m->un.ord < 0)
    224 			m->un.ord = p->infp->un.e.num_mem-1;
    225 		if (m->un.ord >= p->infp->un.e.num_mem)
    226 			m->un.ord = 0;
    227 		break;
    228 	case AUDIO_MIXER_SET:
    229 		m->un.mask += inc;
    230 		if (m->un.mask < 0)
    231 			m->un.mask = (1 << p->infp->un.s.num_mem) - 1;
    232 		if (m->un.mask >= (1 << p->infp->un.s.num_mem))
    233 			m->un.mask = 0;
    234 		warnx("Can't ++/-- %s", p->name);
    235 		return 0;
    236 	case AUDIO_MIXER_VALUE:
    237 		if (p->infp->un.v.delta)
    238 			inc *= p->infp->un.v.delta;
    239 		for (i = 0; i < m->un.value.num_channels; i++) {
    240 			v = m->un.value.level[i];
    241 			v += inc;
    242 			if (v < AUDIO_MIN_GAIN)
    243 				v = AUDIO_MIN_GAIN;
    244 			if (v > AUDIO_MAX_GAIN)
    245 				v = AUDIO_MAX_GAIN;
    246 			m->un.value.level[i] = v;
    247 		}
    248 		break;
    249 	default:
    250 		errx(1, "Invalid format.");
    251 	}
    252 	p->changed = 1;
    253 	return 1;
    254 }
    255 
    256 static void
    257 wrarg(int fd, char *arg, char *sep)
    258 {
    259 	char *q;
    260 	struct field *p;
    261 	mixer_ctrl_t val;
    262 	int incdec, r;
    263 
    264 	q = strchr(arg, '=');
    265 	if (q == NULL) {
    266 		int l = strlen(arg);
    267 		incdec = 0;
    268 		if (l > 2 && arg[l-2] == '+' && arg[l-1] == '+')
    269 			incdec = 1;
    270 		else if (l > 2 && arg[l-2] == '-' && arg[l-1] == '-')
    271 			incdec = -1;
    272 		else {
    273 			warnx("No `=' in %s", arg);
    274 			return;
    275 		}
    276 		arg[l-2] = 0;
    277 	} else if (q > arg && (*(q-1) == '+' || *(q-1) == '-')) {
    278 		if (sscanf(q+1, "%d", &incdec) != 1) {
    279 			warnx("Bad number %s", q+1);
    280 			return;
    281 		}
    282 		if (*(q-1) == '-')
    283 			incdec *= -1;
    284 		*(q-1) = 0;
    285 		q = NULL;
    286 	} else
    287 		*q++ = 0;
    288 
    289 	p = findfield(arg);
    290 	if (p == NULL) {
    291 		warnx("field %s does not exist", arg);
    292 		return;
    293 	}
    294 
    295 	val = *p->valp;
    296 	if (q != NULL)
    297 		r = rdfield(p, q);
    298 	else
    299 		r = incfield(p, incdec);
    300 	if (r) {
    301 		if (ioctl(fd, AUDIO_MIXER_WRITE, p->valp) < 0)
    302 			warn("AUDIO_MIXER_WRITE");
    303 		else if (sep) {
    304 			*p->valp = val;
    305 			prfield(p, ": ", 0);
    306 			ioctl(fd, AUDIO_MIXER_READ, p->valp);
    307 			printf(" -> ");
    308 			prfield(p, 0, 0);
    309 			printf("\n");
    310 		}
    311 	}
    312 }
    313 
    314 static void
    315 prarg(int fd, char *arg, char *sep)
    316 {
    317 	struct field *p;
    318 
    319 	p = findfield(arg);
    320 	if (p == NULL)
    321 		warnx("field %s does not exist", arg);
    322 	else
    323 		prfield(p, sep, vflag), fprintf(out, "\n");
    324 }
    325 
    326 int
    327 main(int argc, char **argv)
    328 {
    329 	int fd, i, j, ch, pos;
    330 	int aflag = 0, wflag = 0;
    331 	char *file;
    332 	char *sep = "=";
    333 	mixer_devinfo_t dinfo;
    334 	int ndev;
    335 
    336 	file = getenv("MIXERDEVICE");
    337 	if (file == NULL)
    338 		file = _PATH_MIXER;
    339 
    340 	prog = *argv;
    341 
    342 	while ((ch = getopt(argc, argv, "ad:f:nvw")) != -1) {
    343 		switch(ch) {
    344 		case 'a':
    345 			aflag++;
    346 			break;
    347 		case 'w':
    348 			wflag++;
    349 			break;
    350 		case 'v':
    351 			vflag++;
    352 			break;
    353 		case 'n':
    354 			sep = 0;
    355 			break;
    356 		case 'f': /* compatibility */
    357 		case 'd':
    358 			file = optarg;
    359 			break;
    360 		case '?':
    361 		default:
    362 		usage:
    363 		fprintf(out, "%s [-d file] [-v] [-n] name ...\n", prog);
    364 		fprintf(out, "%s [-d file] [-v] [-n] -w name=value ...\n",prog);
    365 		fprintf(out, "%s [-d file] [-v] [-n] -a\n", prog);
    366 		exit(0);
    367 		}
    368 	}
    369 	argc -= optind;
    370 	argv += optind;
    371 
    372 	fd = open(file, O_RDWR);
    373         /* Try with mixer0. */
    374         if (fd < 0 && file == _PATH_MIXER) {
    375         	file = _PATH_MIXER0;
    376                 fd = open(file, O_RDWR);
    377         }
    378 
    379 	if (fd < 0)
    380 		err(1, "%s", file);
    381 
    382 	for(ndev = 0; ; ndev++) {
    383 		dinfo.index = ndev;
    384 		if (ioctl(fd, AUDIO_MIXER_DEVINFO, &dinfo) < 0)
    385 			break;
    386 	}
    387 	rfields = calloc(ndev, sizeof *rfields);
    388 	fields = calloc(ndev, sizeof *fields);
    389 	infos = calloc(ndev, sizeof *infos);
    390 	values = calloc(ndev, sizeof *values);
    391 
    392 	for(i = 0; i < ndev; i++) {
    393 		infos[i].index = i;
    394 		ioctl(fd, AUDIO_MIXER_DEVINFO, &infos[i]);
    395 	}
    396 
    397 	for(i = 0; i < ndev; i++) {
    398 		rfields[i].name = infos[i].label.name;
    399 		rfields[i].valp = &values[i];
    400 		rfields[i].infp = &infos[i];
    401 	}
    402 
    403 	for(i = 0; i < ndev; i++) {
    404 		values[i].dev = i;
    405 		values[i].type = infos[i].type;
    406 		if (infos[i].type != AUDIO_MIXER_CLASS) {
    407 			values[i].un.value.num_channels = 2;
    408 			if (ioctl(fd, AUDIO_MIXER_READ, &values[i]) < 0) {
    409 				values[i].un.value.num_channels = 1;
    410 				if (ioctl(fd, AUDIO_MIXER_READ, &values[i]) < 0)
    411 					err(1, "AUDIO_MIXER_READ");
    412 			}
    413 		}
    414 	}
    415 
    416 	for(j = i = 0; i < ndev; i++) {
    417 		if (infos[i].type != AUDIO_MIXER_CLASS &&
    418 		    infos[i].type != -1) {
    419 			fields[j++] = rfields[i];
    420 			for(pos = infos[i].next; pos != AUDIO_MIXER_LAST;
    421 			    pos = infos[pos].next) {
    422 				fields[j] = rfields[pos];
    423 				fields[j].name = catstr(rfields[i].name,
    424 							infos[pos].label.name);
    425 				infos[pos].type = -1;
    426 				j++;
    427 			}
    428 		}
    429 	}
    430 
    431 	for(i = 0; i < j; i++) {
    432 		int cls = fields[i].infp->mixer_class;
    433 		if (cls >= 0 && cls < ndev)
    434 			fields[i].name = catstr(infos[cls].label.name,
    435 						fields[i].name);
    436 	}
    437 
    438 	if (argc == 0 && aflag && !wflag) {
    439 		for(i = 0; fields[i].name; i++) {
    440 			prfield(&fields[i], sep, vflag);
    441 			fprintf(out, "\n");
    442 		}
    443 	} else if (argc > 0 && !aflag) {
    444 		while(argc--) {
    445 			if (wflag)
    446 				wrarg(fd, *argv, sep);
    447 			else
    448 				prarg(fd, *argv, sep);
    449 			argv++;
    450 		}
    451 	} else
    452 		goto usage;
    453 	exit(0);
    454 }
    455