1 /* $NetBSD: mixerctl.c,v 1.9 1997/10/19 07:46:04 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 = "/dev/mixer"; 218 char *sep = "="; 219 mixer_devinfo_t dinfo; 220 mixer_ctrl_t val; 221 int ndev; 222 223 prog = *argv; 224 225 while ((ch = getopt(argc, argv, "af:nvw")) != -1) { 226 switch(ch) { 227 case 'a': 228 aflag++; 229 break; 230 case 'w': 231 wflag++; 232 break; 233 case 'v': 234 vflag++; 235 break; 236 case 'n': 237 sep = 0; 238 break; 239 case 'f': 240 file = optarg; 241 break; 242 case '?': 243 default: 244 usage: 245 fprintf(out, "%s [-f file] [-v] [-n] name ...\n", prog); 246 fprintf(out, "%s [-f file] [-v] [-n] -w name=value ...\n", prog); 247 fprintf(out, "%s [-f file] [-v] [-n] -a\n", prog); 248 exit(0); 249 } 250 } 251 argc -= optind; 252 argv += optind; 253 254 fd = open(file, O_RDWR); 255 if (fd < 0) 256 err(1, "%s", file); 257 258 for(ndev = 0; ; ndev++) { 259 dinfo.index = ndev; 260 if (ioctl(fd, AUDIO_MIXER_DEVINFO, &dinfo) < 0) 261 break; 262 } 263 rfields = calloc(ndev, sizeof *rfields); 264 fields = calloc(ndev, sizeof *fields); 265 infos = calloc(ndev, sizeof *infos); 266 values = calloc(ndev, sizeof *values); 267 268 for(i = 0; i < ndev; i++) { 269 infos[i].index = i; 270 ioctl(fd, AUDIO_MIXER_DEVINFO, &infos[i]); 271 } 272 273 for(i = 0; i < ndev; i++) { 274 rfields[i].name = infos[i].label.name; 275 rfields[i].valp = &values[i]; 276 rfields[i].infp = &infos[i]; 277 } 278 279 for(i = 0; i < ndev; i++) { 280 values[i].dev = i; 281 values[i].type = infos[i].type; 282 if (infos[i].type != AUDIO_MIXER_CLASS) { 283 values[i].un.value.num_channels = 2; 284 if (ioctl(fd, AUDIO_MIXER_READ, &values[i]) < 0) { 285 values[i].un.value.num_channels = 1; 286 if (ioctl(fd, AUDIO_MIXER_READ, &values[i]) < 0) 287 err(1, "AUDIO_MIXER_READ"); 288 } 289 } 290 } 291 292 for(j = i = 0; i < ndev; i++) { 293 if (infos[i].type != AUDIO_MIXER_CLASS && 294 infos[i].type != -1) { 295 fields[j++] = rfields[i]; 296 for(pos = infos[i].next; pos != AUDIO_MIXER_LAST; 297 pos = infos[pos].next) { 298 fields[j] = rfields[pos]; 299 fields[j].name = catstr(rfields[i].name, 300 infos[pos].label.name); 301 infos[pos].type = -1; 302 j++; 303 } 304 } 305 } 306 307 for(i = 0; i < j; i++) { 308 int cls = fields[i].infp->mixer_class; 309 if (cls >= 0 && cls < ndev) 310 fields[i].name = catstr(infos[cls].label.name, 311 fields[i].name); 312 } 313 314 if (argc == 0 && aflag && !wflag) { 315 for(i = 0; fields[i].name; i++) { 316 prfield(&fields[i], sep, vflag); 317 fprintf(out, "\n"); 318 } 319 } else if (argc > 0 && !aflag) { 320 struct field *p; 321 if (wflag) { 322 while(argc--) { 323 char *q; 324 325 q = strchr(*argv, '='); 326 if (q) { 327 *q++ = 0; 328 p = findfield(*argv); 329 if (p == 0) 330 warnx("field %s does not exist", *argv); 331 else { 332 val = *p->valp; 333 if (rdfield(p, q)) { 334 if (ioctl(fd, AUDIO_MIXER_WRITE, p->valp) < 0) 335 warn("AUDIO_MIXER_WRITE"); 336 else if (sep) { 337 *p->valp = val; 338 prfield(p, ": ", 0); 339 ioctl(fd, AUDIO_MIXER_READ, p->valp); 340 printf(" -> "); 341 prfield(p, 0, 0); 342 printf("\n"); 343 } 344 } 345 } 346 } else { 347 warnx("No `=' in %s", *argv); 348 } 349 argv++; 350 } 351 } else { 352 while(argc--) { 353 p = findfield(*argv); 354 if (p == 0) 355 warnx("field %s does not exist", *argv); 356 else 357 prfield(p, sep, vflag), fprintf(out, "\n"); 358 argv++; 359 } 360 } 361 } else 362 goto usage; 363 exit(0); 364 } 365