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