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