1 /* $NetBSD: mixerctl.c,v 1.15 2000/12/29 13:30:26 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 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, "af: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': 342 file = optarg; 343 break; 344 case '?': 345 default: 346 usage: 347 fprintf(out, "%s [-f file] [-v] [-n] name ...\n", prog); 348 fprintf(out, "%s [-f file] [-v] [-n] -w name=value ...\n",prog); 349 fprintf(out, "%s [-f file] [-v] [-n] -a\n", prog); 350 exit(0); 351 } 352 } 353 argc -= optind; 354 argv += optind; 355 356 fd = open(file, O_RDWR); 357 #ifdef OLD_MIXER 358 /* Allow the non-unit device to be used. */ 359 if (fd < 0 && file == MIXER) { 360 file = OLD_MIXER; 361 fd = open(file, O_RDWR); 362 } 363 #endif 364 if (fd < 0) 365 err(1, "%s", file); 366 367 for(ndev = 0; ; ndev++) { 368 dinfo.index = ndev; 369 if (ioctl(fd, AUDIO_MIXER_DEVINFO, &dinfo) < 0) 370 break; 371 } 372 rfields = calloc(ndev, sizeof *rfields); 373 fields = calloc(ndev, sizeof *fields); 374 infos = calloc(ndev, sizeof *infos); 375 values = calloc(ndev, sizeof *values); 376 377 for(i = 0; i < ndev; i++) { 378 infos[i].index = i; 379 ioctl(fd, AUDIO_MIXER_DEVINFO, &infos[i]); 380 } 381 382 for(i = 0; i < ndev; i++) { 383 rfields[i].name = infos[i].label.name; 384 rfields[i].valp = &values[i]; 385 rfields[i].infp = &infos[i]; 386 } 387 388 for(i = 0; i < ndev; i++) { 389 values[i].dev = i; 390 values[i].type = infos[i].type; 391 if (infos[i].type != AUDIO_MIXER_CLASS) { 392 values[i].un.value.num_channels = 2; 393 if (ioctl(fd, AUDIO_MIXER_READ, &values[i]) < 0) { 394 values[i].un.value.num_channels = 1; 395 if (ioctl(fd, AUDIO_MIXER_READ, &values[i]) < 0) 396 err(1, "AUDIO_MIXER_READ"); 397 } 398 } 399 } 400 401 for(j = i = 0; i < ndev; i++) { 402 if (infos[i].type != AUDIO_MIXER_CLASS && 403 infos[i].type != -1) { 404 fields[j++] = rfields[i]; 405 for(pos = infos[i].next; pos != AUDIO_MIXER_LAST; 406 pos = infos[pos].next) { 407 fields[j] = rfields[pos]; 408 fields[j].name = catstr(rfields[i].name, 409 infos[pos].label.name); 410 infos[pos].type = -1; 411 j++; 412 } 413 } 414 } 415 416 for(i = 0; i < j; i++) { 417 int cls = fields[i].infp->mixer_class; 418 if (cls >= 0 && cls < ndev) 419 fields[i].name = catstr(infos[cls].label.name, 420 fields[i].name); 421 } 422 423 if (argc == 0 && aflag && !wflag) { 424 for(i = 0; fields[i].name; i++) { 425 prfield(&fields[i], sep, vflag); 426 fprintf(out, "\n"); 427 } 428 } else if (argc > 0 && !aflag) { 429 while(argc--) { 430 if (wflag) 431 wrarg(fd, *argv, sep); 432 else 433 prarg(fd, *argv, sep); 434 argv++; 435 } 436 } else 437 goto usage; 438 exit(0); 439 } 440