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