Home | History | Annotate | Line # | Download | only in mixerctl
mixerctl.c revision 1.14
      1 /*	$NetBSD: mixerctl.c,v 1.14 1998/11/25 22:17:08 augustss 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 char *catstr __P((char *p, char *q));
     52 struct field *findfield __P((char *name));
     53 void prfield __P((struct field *p, char *sep, int prvalset));
     54 int rdfield __P((struct field *p, char *q));
     55 int main(int argc, char **argv);
     56 
     57 FILE *out = stdout;
     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 char *
     72 catstr(p, q)
     73 	char *p;
     74         char *q;
     75 {
     76 	char *r = malloc(strlen(p) + strlen(q) + 2);
     77 	strcpy(r, p);
     78 	strcat(r, ".");
     79 	strcat(r, q);
     80 	return r;
     81 }
     82 
     83 struct field *
     84 findfield(name)
     85 	char *name;
     86 {
     87 	int i;
     88 	for(i = 0; fields[i].name; i++)
     89 		if (strcmp(fields[i].name, name) == 0)
     90 			return &fields[i];
     91 	return 0;
     92 }
     93 
     94 void
     95 prfield(p, sep, prvalset)
     96 	struct field *p;
     97         char *sep;
     98         int prvalset;
     99 {
    100 	mixer_ctrl_t *m;
    101 	int i, n;
    102 
    103 	if (sep)
    104 		fprintf(out, "%s%s", p->name, sep);
    105 	m = p->valp;
    106 	switch(m->type) {
    107 	case AUDIO_MIXER_ENUM:
    108 		for(i = 0; i < p->infp->un.e.num_mem; i++)
    109 			if (p->infp->un.e.member[i].ord == m->un.ord)
    110 				fprintf(out, "%s",
    111 					p->infp->un.e.member[i].label.name);
    112 		if (prvalset) {
    113 			fprintf(out, "  [ ");
    114 			for(i = 0; i < p->infp->un.e.num_mem; i++)
    115 				fprintf(out, "%s ", p->infp->un.e.member[i].label.name);
    116 			fprintf(out, "]");
    117 		}
    118 		break;
    119 	case AUDIO_MIXER_SET:
    120 		for(n = i = 0; i < p->infp->un.s.num_mem; i++)
    121 			if (m->un.mask & p->infp->un.s.member[i].mask)
    122 				fprintf(out, "%s%s", n++ ? "," : "",
    123 					p->infp->un.s.member[i].label.name);
    124 		if (prvalset) {
    125 			fprintf(out, "  { ");
    126 			for(i = 0; i < p->infp->un.s.num_mem; i++)
    127 				fprintf(out, "%s ", p->infp->un.s.member[i].label.name);
    128 			fprintf(out, "}");
    129 		}
    130 		break;
    131 	case AUDIO_MIXER_VALUE:
    132 		if (m->un.value.num_channels == 1)
    133 			fprintf(out, "%d", m->un.value.level[0]);
    134 		else
    135 			fprintf(out, "%d,%d", m->un.value.level[0],
    136 			       m->un.value.level[1]);
    137 		if (prvalset)
    138 			fprintf(out, " %s", p->infp->un.v.units.name);
    139 		break;
    140 	default:
    141 		printf("\n");
    142 		errx(1, "Invalid format.");
    143 	}
    144 }
    145 
    146 int
    147 rdfield(p, q)
    148 	struct field *p;
    149         char *q;
    150 {
    151 	mixer_ctrl_t *m;
    152 	int v, v0, v1, mask;
    153 	int i;
    154 	char *s;
    155 
    156 	m = p->valp;
    157 	switch(m->type) {
    158 	case AUDIO_MIXER_ENUM:
    159 		for(i = 0; i < p->infp->un.e.num_mem; i++)
    160 			if (strcmp(p->infp->un.e.member[i].label.name, q) == 0)
    161 				break;
    162 		if (i < p->infp->un.e.num_mem)
    163 			m->un.ord = p->infp->un.e.member[i].ord;
    164 		else {
    165 			warnx("Bad enum value %s", q);
    166 			return 0;
    167 		}
    168 		break;
    169 	case AUDIO_MIXER_SET:
    170 		mask = 0;
    171 		for(v = 0; q && *q; q = s) {
    172 			s = strchr(q, ',');
    173 			if (s)
    174 				*s++ = 0;
    175 			for(i = 0; i < p->infp->un.s.num_mem; i++)
    176 				if (strcmp(p->infp->un.s.member[i].label.name, q) == 0)
    177 					break;
    178 			if (i < p->infp->un.s.num_mem) {
    179 				mask |= p->infp->un.s.member[i].mask;
    180 			} else {
    181 				warnx("Bad set value %s", q);
    182 				return 0;
    183 			}
    184 		}
    185 		m->un.mask = mask;
    186 		break;
    187 	case AUDIO_MIXER_VALUE:
    188 		if (m->un.value.num_channels == 1) {
    189 			if (sscanf(q, "%d", &v) == 1) {
    190 				m->un.value.level[0] = v;
    191 			} else {
    192 				warnx("Bad number %s", q);
    193 				return 0;
    194 			}
    195 		} else {
    196 			if (sscanf(q, "%d,%d", &v0, &v1) == 2) {
    197 				m->un.value.level[0] = v0;
    198 				m->un.value.level[1] = v1;
    199 			} else if (sscanf(q, "%d", &v) == 1) {
    200 				m->un.value.level[0] = m->un.value.level[1] = v;
    201 			} else {
    202 				warnx("Bad numbers %s", q);
    203 				return 0;
    204 			}
    205 		}
    206 		break;
    207 	default:
    208 		errx(1, "Invalid format.");
    209 	}
    210 	p->changed = 1;
    211 	return 1;
    212 }
    213 
    214 int
    215 main(argc, argv)
    216 	int argc;
    217         char **argv;
    218 {
    219 	int fd, i, j, ch, pos;
    220 	int aflag = 0, wflag = 0, vflag = 0;
    221 	char *file;
    222 	char *sep = "=";
    223 	mixer_devinfo_t dinfo;
    224 	mixer_ctrl_t val;
    225 	int ndev;
    226 
    227 	file = getenv("MIXERDEVICE");
    228 	if (file == 0)
    229 		file = MIXER;
    230 
    231 	prog = *argv;
    232 
    233 	while ((ch = getopt(argc, argv, "af:nvw")) != -1) {
    234 		switch(ch) {
    235 		case 'a':
    236 			aflag++;
    237 			break;
    238 		case 'w':
    239 			wflag++;
    240 			break;
    241 		case 'v':
    242 			vflag++;
    243 			break;
    244 		case 'n':
    245 			sep = 0;
    246 			break;
    247 		case 'f':
    248 			file = optarg;
    249 			break;
    250 		case '?':
    251 		default:
    252 		usage:
    253 		fprintf(out, "%s [-f file] [-v] [-n] name ...\n", prog);
    254 		fprintf(out, "%s [-f file] [-v] [-n] -w name=value ...\n", prog);
    255 		fprintf(out, "%s [-f file] [-v] [-n] -a\n", prog);
    256 		exit(0);
    257 		}
    258 	}
    259 	argc -= optind;
    260 	argv += optind;
    261 
    262 	fd = open(file, O_RDWR);
    263 #ifdef OLD_MIXER
    264         /* Allow the non-unit device to be used. */
    265         if (fd < 0 && file == MIXER) {
    266         	file = OLD_MIXER;
    267                 fd = open(file, O_RDWR);
    268         }
    269 #endif
    270 	if (fd < 0)
    271 		err(1, "%s", file);
    272 
    273 	for(ndev = 0; ; ndev++) {
    274 		dinfo.index = ndev;
    275 		if (ioctl(fd, AUDIO_MIXER_DEVINFO, &dinfo) < 0)
    276 			break;
    277 	}
    278 	rfields = calloc(ndev, sizeof *rfields);
    279 	fields = calloc(ndev, sizeof *fields);
    280 	infos = calloc(ndev, sizeof *infos);
    281 	values = calloc(ndev, sizeof *values);
    282 
    283 	for(i = 0; i < ndev; i++) {
    284 		infos[i].index = i;
    285 		ioctl(fd, AUDIO_MIXER_DEVINFO, &infos[i]);
    286 	}
    287 
    288 	for(i = 0; i < ndev; i++) {
    289 		rfields[i].name = infos[i].label.name;
    290 		rfields[i].valp = &values[i];
    291 		rfields[i].infp = &infos[i];
    292 	}
    293 
    294 	for(i = 0; i < ndev; i++) {
    295 		values[i].dev = i;
    296 		values[i].type = infos[i].type;
    297 		if (infos[i].type != AUDIO_MIXER_CLASS) {
    298 			values[i].un.value.num_channels = 2;
    299 			if (ioctl(fd, AUDIO_MIXER_READ, &values[i]) < 0) {
    300 				values[i].un.value.num_channels = 1;
    301 				if (ioctl(fd, AUDIO_MIXER_READ, &values[i]) < 0)
    302 					err(1, "AUDIO_MIXER_READ");
    303 			}
    304 		}
    305 	}
    306 
    307 	for(j = i = 0; i < ndev; i++) {
    308 		if (infos[i].type != AUDIO_MIXER_CLASS &&
    309 		    infos[i].type != -1) {
    310 			fields[j++] = rfields[i];
    311 			for(pos = infos[i].next; pos != AUDIO_MIXER_LAST;
    312 			    pos = infos[pos].next) {
    313 				fields[j] = rfields[pos];
    314 				fields[j].name = catstr(rfields[i].name,
    315 							infos[pos].label.name);
    316 				infos[pos].type = -1;
    317 				j++;
    318 			}
    319 		}
    320 	}
    321 
    322 	for(i = 0; i < j; i++) {
    323 		int cls = fields[i].infp->mixer_class;
    324 		if (cls >= 0 && cls < ndev)
    325 			fields[i].name = catstr(infos[cls].label.name,
    326 						fields[i].name);
    327 	}
    328 
    329 	if (argc == 0 && aflag && !wflag) {
    330 		for(i = 0; fields[i].name; i++) {
    331 			prfield(&fields[i], sep, vflag);
    332 			fprintf(out, "\n");
    333 		}
    334 	} else if (argc > 0 && !aflag) {
    335 		struct field *p;
    336 		if (wflag) {
    337 			while(argc--) {
    338 				char *q;
    339 
    340 				q = strchr(*argv, '=');
    341 				if (q) {
    342 					*q++ = 0;
    343 					p = findfield(*argv);
    344 					if (p == 0)
    345 						warnx("field %s does not exist", *argv);
    346 					else {
    347 						val = *p->valp;
    348 						if (rdfield(p, q)) {
    349 							if (ioctl(fd, AUDIO_MIXER_WRITE, p->valp) < 0)
    350 								warn("AUDIO_MIXER_WRITE");
    351 							else if (sep) {
    352 								*p->valp = val;
    353 								prfield(p, ": ", 0);
    354 								ioctl(fd, AUDIO_MIXER_READ, p->valp);
    355 								printf(" -> ");
    356 								prfield(p, 0, 0);
    357 								printf("\n");
    358 							}
    359 						}
    360 					}
    361 				} else {
    362 					warnx("No `=' in %s", *argv);
    363 				}
    364 				argv++;
    365 			}
    366 		} else {
    367 			while(argc--) {
    368 				p = findfield(*argv);
    369 				if (p == 0)
    370 					warnx("field %s does not exist", *argv);
    371 				else
    372 					prfield(p, sep, vflag), fprintf(out, "\n");
    373 				argv++;
    374 			}
    375 		}
    376 	} else
    377 		goto usage;
    378 	exit(0);
    379 }
    380