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@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