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