Home | History | Annotate | Line # | Download | only in mixerctl
mixerctl.c revision 1.10
      1 /*	$NetBSD: mixerctl.c,v 1.10 1998/04/27 16:52:03 augustss Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 1997 The NetBSD Foundation, Inc.
      5  * All rights reserved.
      6  *
      7  * Author: Lennart Augustsson, with some code and ideas from Chuck Cranor.
      8  *
      9  * Redistribution and use in source and binary forms, with or without
     10  * modification, are permitted provided that the following conditions
     11  * are met:
     12  * 1. Redistributions of source code must retain the above copyright
     13  *    notice, this list of conditions and the following disclaimer.
     14  * 2. Redistributions in binary form must reproduce the above copyright
     15  *    notice, this list of conditions and the following disclaimer in the
     16  *    documentation and/or other materials provided with the distribution.
     17  * 3. All advertising materials mentioning features or use of this software
     18  *    must display the following acknowledgement:
     19  *        This product includes software developed by the NetBSD
     20  *        Foundation, Inc. and its contributors.
     21  * 4. Neither the name of The NetBSD Foundation nor the names of its
     22  *    contributors may be used to endorse or promote products derived
     23  *    from this software without specific prior written permission.
     24  *
     25  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     26  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     27  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     28  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     29  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     30  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     31  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     32  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     33  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     34  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     35  * POSSIBILITY OF SUCH DAMAGE.
     36  */
     37 #include <stdio.h>
     38 #include <stdlib.h>
     39 #include <fcntl.h>
     40 #include <err.h>
     41 #include <unistd.h>
     42 #include <string.h>
     43 #include <sys/types.h>
     44 #include <sys/ioctl.h>
     45 #include <sys/audioio.h>
     46 
     47 char *catstr __P((char *p, char *q));
     48 struct field *findfield __P((char *name));
     49 void prfield __P((struct field *p, char *sep, int prvalset));
     50 int rdfield __P((struct field *p, char *q));
     51 int main(int argc, char **argv);
     52 
     53 FILE *out = stdout;
     54 
     55 char *prog;
     56 
     57 struct field {
     58 	char *name;
     59 	mixer_ctrl_t *valp;
     60 	mixer_devinfo_t *infp;
     61 	char changed;
     62 } *fields, *rfields;
     63 
     64 mixer_ctrl_t *values;
     65 mixer_devinfo_t *infos;
     66 
     67 char *
     68 catstr(p, q)
     69 	char *p;
     70         char *q;
     71 {
     72 	char *r = malloc(strlen(p) + strlen(q) + 2);
     73 	strcpy(r, p);
     74 	strcat(r, ".");
     75 	strcat(r, q);
     76 	return r;
     77 }
     78 
     79 struct field *
     80 findfield(name)
     81 	char *name;
     82 {
     83 	int i;
     84 	for(i = 0; fields[i].name; i++)
     85 		if (strcmp(fields[i].name, name) == 0)
     86 			return &fields[i];
     87 	return 0;
     88 }
     89 
     90 void
     91 prfield(p, sep, prvalset)
     92 	struct field *p;
     93         char *sep;
     94         int prvalset;
     95 {
     96 	mixer_ctrl_t *m;
     97 	int i, n;
     98 
     99 	if (sep)
    100 		fprintf(out, "%s%s", p->name, sep);
    101 	m = p->valp;
    102 	switch(m->type) {
    103 	case AUDIO_MIXER_ENUM:
    104 		for(i = 0; i < p->infp->un.e.num_mem; i++)
    105 			if (p->infp->un.e.member[i].ord == m->un.ord)
    106 				fprintf(out, "%s",
    107 					p->infp->un.e.member[i].label.name);
    108 		if (prvalset) {
    109 			fprintf(out, "  [ ");
    110 			for(i = 0; i < p->infp->un.e.num_mem; i++)
    111 				fprintf(out, "%s ", 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 ", 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 		break;
    136 	default:
    137 		printf("\n");
    138 		errx(1, "Invalid format.");
    139 	}
    140 }
    141 
    142 int
    143 rdfield(p, q)
    144 	struct field *p;
    145         char *q;
    146 {
    147 	mixer_ctrl_t *m;
    148 	int v, v0, v1, mask;
    149 	int i;
    150 	char *s;
    151 
    152 	m = p->valp;
    153 	switch(m->type) {
    154 	case AUDIO_MIXER_ENUM:
    155 		for(i = 0; i < p->infp->un.e.num_mem; i++)
    156 			if (strcmp(p->infp->un.e.member[i].label.name, q) == 0)
    157 				break;
    158 		if (i < p->infp->un.e.num_mem)
    159 			m->un.ord = p->infp->un.e.member[i].ord;
    160 		else {
    161 			warnx("Bad enum value %s", q);
    162 			return 0;
    163 		}
    164 		break;
    165 	case AUDIO_MIXER_SET:
    166 		mask = 0;
    167 		for(v = 0; q && *q; q = s) {
    168 			s = strchr(q, ',');
    169 			if (s)
    170 				*s++ = 0;
    171 			for(i = 0; i < p->infp->un.s.num_mem; i++)
    172 				if (strcmp(p->infp->un.s.member[i].label.name, q) == 0)
    173 					break;
    174 			if (i < p->infp->un.s.num_mem) {
    175 				mask |= p->infp->un.s.member[i].mask;
    176 			} else {
    177 				warnx("Bad set value %s", q);
    178 				return 0;
    179 			}
    180 		}
    181 		m->un.mask = mask;
    182 		break;
    183 	case AUDIO_MIXER_VALUE:
    184 		if (m->un.value.num_channels == 1) {
    185 			if (sscanf(q, "%d", &v) == 1) {
    186 				m->un.value.level[0] = v;
    187 			} else {
    188 				warnx("Bad number %s", q);
    189 				return 0;
    190 			}
    191 		} else {
    192 			if (sscanf(q, "%d,%d", &v0, &v1) == 2) {
    193 				m->un.value.level[0] = v0;
    194 				m->un.value.level[1] = v1;
    195 			} else if (sscanf(q, "%d", &v) == 1) {
    196 				m->un.value.level[0] = m->un.value.level[1] = v;
    197 			} else {
    198 				warnx("Bad numbers %s", q);
    199 				return 0;
    200 			}
    201 		}
    202 		break;
    203 	default:
    204 		errx(1, "Invalid format.");
    205 	}
    206 	p->changed = 1;
    207 	return 1;
    208 }
    209 
    210 int
    211 main(argc, argv)
    212 	int argc;
    213         char **argv;
    214 {
    215 	int fd, i, j, ch, pos;
    216 	int aflag = 0, wflag = 0, vflag = 0;
    217 	char *file;
    218 	char *sep = "=";
    219 	mixer_devinfo_t dinfo;
    220 	mixer_ctrl_t val;
    221 	int ndev;
    222 
    223 	file = getenv("MIXERUNIT");
    224 	if (file == 0)
    225 		file = "/dev/mixer";
    226 
    227 	prog = *argv;
    228 
    229 	while ((ch = getopt(argc, argv, "af:nvw")) != -1) {
    230 		switch(ch) {
    231 		case 'a':
    232 			aflag++;
    233 			break;
    234 		case 'w':
    235 			wflag++;
    236 			break;
    237 		case 'v':
    238 			vflag++;
    239 			break;
    240 		case 'n':
    241 			sep = 0;
    242 			break;
    243 		case 'f':
    244 			file = optarg;
    245 			break;
    246 		case '?':
    247 		default:
    248 		usage:
    249 		fprintf(out, "%s [-f file] [-v] [-n] name ...\n", prog);
    250 		fprintf(out, "%s [-f file] [-v] [-n] -w name=value ...\n", prog);
    251 		fprintf(out, "%s [-f file] [-v] [-n] -a\n", prog);
    252 		exit(0);
    253 		}
    254 	}
    255 	argc -= optind;
    256 	argv += optind;
    257 
    258 	fd = open(file, O_RDWR);
    259 	if (fd < 0)
    260 		err(1, "%s", file);
    261 
    262 	for(ndev = 0; ; ndev++) {
    263 		dinfo.index = ndev;
    264 		if (ioctl(fd, AUDIO_MIXER_DEVINFO, &dinfo) < 0)
    265 			break;
    266 	}
    267 	rfields = calloc(ndev, sizeof *rfields);
    268 	fields = calloc(ndev, sizeof *fields);
    269 	infos = calloc(ndev, sizeof *infos);
    270 	values = calloc(ndev, sizeof *values);
    271 
    272 	for(i = 0; i < ndev; i++) {
    273 		infos[i].index = i;
    274 		ioctl(fd, AUDIO_MIXER_DEVINFO, &infos[i]);
    275 	}
    276 
    277 	for(i = 0; i < ndev; i++) {
    278 		rfields[i].name = infos[i].label.name;
    279 		rfields[i].valp = &values[i];
    280 		rfields[i].infp = &infos[i];
    281 	}
    282 
    283 	for(i = 0; i < ndev; i++) {
    284 		values[i].dev = i;
    285 		values[i].type = infos[i].type;
    286 		if (infos[i].type != AUDIO_MIXER_CLASS) {
    287 			values[i].un.value.num_channels = 2;
    288 			if (ioctl(fd, AUDIO_MIXER_READ, &values[i]) < 0) {
    289 				values[i].un.value.num_channels = 1;
    290 				if (ioctl(fd, AUDIO_MIXER_READ, &values[i]) < 0)
    291 					err(1, "AUDIO_MIXER_READ");
    292 			}
    293 		}
    294 	}
    295 
    296 	for(j = i = 0; i < ndev; i++) {
    297 		if (infos[i].type != AUDIO_MIXER_CLASS &&
    298 		    infos[i].type != -1) {
    299 			fields[j++] = rfields[i];
    300 			for(pos = infos[i].next; pos != AUDIO_MIXER_LAST;
    301 			    pos = infos[pos].next) {
    302 				fields[j] = rfields[pos];
    303 				fields[j].name = catstr(rfields[i].name,
    304 							infos[pos].label.name);
    305 				infos[pos].type = -1;
    306 				j++;
    307 			}
    308 		}
    309 	}
    310 
    311 	for(i = 0; i < j; i++) {
    312 		int cls = fields[i].infp->mixer_class;
    313 		if (cls >= 0 && cls < ndev)
    314 			fields[i].name = catstr(infos[cls].label.name,
    315 						fields[i].name);
    316 	}
    317 
    318 	if (argc == 0 && aflag && !wflag) {
    319 		for(i = 0; fields[i].name; i++) {
    320 			prfield(&fields[i], sep, vflag);
    321 			fprintf(out, "\n");
    322 		}
    323 	} else if (argc > 0 && !aflag) {
    324 		struct field *p;
    325 		if (wflag) {
    326 			while(argc--) {
    327 				char *q;
    328 
    329 				q = strchr(*argv, '=');
    330 				if (q) {
    331 					*q++ = 0;
    332 					p = findfield(*argv);
    333 					if (p == 0)
    334 						warnx("field %s does not exist", *argv);
    335 					else {
    336 						val = *p->valp;
    337 						if (rdfield(p, q)) {
    338 							if (ioctl(fd, AUDIO_MIXER_WRITE, p->valp) < 0)
    339 								warn("AUDIO_MIXER_WRITE");
    340 							else if (sep) {
    341 								*p->valp = val;
    342 								prfield(p, ": ", 0);
    343 								ioctl(fd, AUDIO_MIXER_READ, p->valp);
    344 								printf(" -> ");
    345 								prfield(p, 0, 0);
    346 								printf("\n");
    347 							}
    348 						}
    349 					}
    350 				} else {
    351 					warnx("No `=' in %s", *argv);
    352 				}
    353 				argv++;
    354 			}
    355 		} else {
    356 			while(argc--) {
    357 				p = findfield(*argv);
    358 				if (p == 0)
    359 					warnx("field %s does not exist", *argv);
    360 				else
    361 					prfield(p, sep, vflag), fprintf(out, "\n");
    362 				argv++;
    363 			}
    364 		}
    365 	} else
    366 		goto usage;
    367 	exit(0);
    368 }
    369