1270631Sjfv/* 2270631Sjfv * This is an example of a mixer program for Linux 3292095Ssmh * 4270631Sjfv * updated 1/1/93 to add stereo, level query, broken 5270631Sjfv * devmask kludge - cmetz@thor.tjhsst.edu 6270631Sjfv * 7270631Sjfv * (C) Craig Metz and Hannu Savolainen 1993. 8270631Sjfv * 9270631Sjfv * You may do anything you wish with this program. 10270631Sjfv * 11270631Sjfv * ditto for my modifications (John-Mark Gurney, 1997) 12270631Sjfv */ 13270631Sjfv 14270631Sjfv#include <sys/cdefs.h> 15270631Sjfv__FBSDID("$FreeBSD: releng/10.3/usr.sbin/mixer/mixer.c 230611 2012-01-27 09:15:55Z mav $"); 16270631Sjfv 17270631Sjfv#include <err.h> 18270631Sjfv#include <fcntl.h> 19270631Sjfv#include <libgen.h> 20270631Sjfv#include <limits.h> 21270631Sjfv#include <stdio.h> 22270631Sjfv#include <string.h> 23270631Sjfv#include <stdlib.h> 24270631Sjfv#include <unistd.h> 25270631Sjfv#include <sys/soundcard.h> 26270631Sjfv 27270631Sjfvstatic const char *names[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_NAMES; 28270631Sjfv 29270631Sjfvstatic void usage(int devmask, int recmask); 30270631Sjfvstatic int res_name(const char *name, int mask); 31270631Sjfvstatic void print_recsrc(int recsrc, int recmask, int sflag); 32270631Sjfv 33270631Sjfvstatic void 34270631Sjfvusage(int devmask, int recmask) 35270631Sjfv{ 36270631Sjfv int i, n; 37270631Sjfv 38270631Sjfv printf("usage: mixer [-f device] [-s | -S] [dev [+|-][voll[:[+|-]volr]] ...\n" 39270631Sjfv " mixer [-f device] [-s | -S] recsrc ...\n" 40292095Ssmh " mixer [-f device] [-s | -S] {^|+|-|=}rec rdev ...\n"); 41270631Sjfv if (devmask != 0) { 42270631Sjfv printf(" devices: "); 43270631Sjfv for (i = 0, n = 0; i < SOUND_MIXER_NRDEVICES; i++) 44270631Sjfv if ((1 << i) & devmask) { 45270631Sjfv if (n) 46270631Sjfv printf(", "); 47270631Sjfv printf("%s", names[i]); 48270631Sjfv n++; 49270631Sjfv } 50270631Sjfv } 51270631Sjfv if (recmask != 0) { 52270631Sjfv printf("\n rec devices: "); 53270631Sjfv for (i = 0, n = 0; i < SOUND_MIXER_NRDEVICES; i++) 54270631Sjfv if ((1 << i) & recmask) { 55270631Sjfv if (n) 56270631Sjfv printf(", "); 57270631Sjfv printf("%s", names[i]); 58270631Sjfv n++; 59270631Sjfv } 60270631Sjfv } 61270631Sjfv printf("\n"); 62270631Sjfv exit(1); 63270631Sjfv} 64270631Sjfv 65292100Ssmhstatic int 66292095Ssmhres_name(const char *name, int mask) 67292100Ssmh{ 68270631Sjfv int foo; 69270631Sjfv 70292100Ssmh for (foo = 0; foo < SOUND_MIXER_NRDEVICES; foo++) 71292100Ssmh if ((1 << foo) & mask && strcmp(names[foo], name) == 0) 72292100Ssmh break; 73292100Ssmh 74292100Ssmh return (foo == SOUND_MIXER_NRDEVICES ? -1 : foo); 75292100Ssmh} 76292100Ssmh 77292100Ssmhstatic void 78292100Ssmhprint_recsrc(int recsrc, int recmask, int sflag) 79292100Ssmh{ 80292100Ssmh int i, n; 81292100Ssmh 82292100Ssmh if (recmask == 0) 83270631Sjfv return; 84270631Sjfv 85270631Sjfv if (!sflag) 86270631Sjfv printf("Recording source: "); 87270631Sjfv 88270631Sjfv for (i = 0, n = 0; i < SOUND_MIXER_NRDEVICES; i++) 89270631Sjfv if ((1 << i) & recsrc) { 90270631Sjfv if (sflag) 91270631Sjfv printf("%srec ", n ? " +" : "="); 92270631Sjfv else if (n) 93270631Sjfv printf(", "); 94270631Sjfv printf("%s", names[i]); 95270631Sjfv n++; 96270631Sjfv } 97270631Sjfv if (!sflag) 98270631Sjfv printf("\n"); 99270631Sjfv} 100270631Sjfv 101292100Ssmhint 102292100Ssmhmain(int argc, char *argv[]) 103292100Ssmh{ 104292100Ssmh char mixer[PATH_MAX] = "/dev/mixer"; 105292100Ssmh char lstr[5], rstr[5]; 106292100Ssmh char *name, *eptr; 107292100Ssmh int devmask = 0, recmask = 0, recsrc = 0, orecsrc; 108292100Ssmh int dusage = 0, drecsrc = 0, sflag = 0, Sflag = 0; 109292100Ssmh int l, r, lrel, rrel; 110292100Ssmh int ch, foo, bar, baz, dev, m, n, t; 111292100Ssmh 112292100Ssmh if ((name = strdup(basename(argv[0]))) == NULL) 113292100Ssmh err(1, "strdup()"); 114292100Ssmh if (strncmp(name, "mixer", 5) == 0 && name[5] != '\0') { 115292100Ssmh n = strtol(name + 5, &eptr, 10) - 1; 116292100Ssmh if (n > 0 && *eptr == '\0') 117292100Ssmh snprintf(mixer, PATH_MAX - 1, "/dev/mixer%d", n); 118292100Ssmh } 119292100Ssmh free(name); 120292100Ssmh name = mixer; 121292100Ssmh 122292100Ssmh n = 1; 123292100Ssmh for (;;) { 124292100Ssmh if (n >= argc || *argv[n] != '-') 125292100Ssmh break; 126292100Ssmh if (strlen(argv[n]) != 2) { 127292100Ssmh if (strcmp(argv[n] + 1, "rec") != 0) 128292100Ssmh dusage = 1; 129292100Ssmh break; 130292100Ssmh } 131292100Ssmh ch = *(argv[n] + 1); 132292100Ssmh if (ch == 'f' && n < argc - 1) { 133292100Ssmh name = argv[n + 1]; 134292100Ssmh n += 2; 135292100Ssmh } else if (ch == 's') { 136292100Ssmh sflag = 1; 137292100Ssmh n++; 138292100Ssmh } else if (ch == 'S') { 139292100Ssmh Sflag = 1; 140292100Ssmh n++; 141292100Ssmh } else { 142292100Ssmh dusage = 1; 143292100Ssmh break; 144292100Ssmh } 145292100Ssmh } 146292100Ssmh if (sflag && Sflag) 147292100Ssmh dusage = 1; 148292100Ssmh 149292100Ssmh argc -= n - 1; 150292100Ssmh argv += n - 1; 151292100Ssmh 152292100Ssmh if ((baz = open(name, O_RDWR)) < 0) 153292100Ssmh err(1, "%s", name); 154292100Ssmh if (ioctl(baz, SOUND_MIXER_READ_DEVMASK, &devmask) == -1) 155292100Ssmh err(1, "SOUND_MIXER_READ_DEVMASK"); 156292100Ssmh if (ioctl(baz, SOUND_MIXER_READ_RECMASK, &recmask) == -1) 157292100Ssmh err(1, "SOUND_MIXER_READ_RECMASK"); 158292100Ssmh if (ioctl(baz, SOUND_MIXER_READ_RECSRC, &recsrc) == -1) 159292100Ssmh err(1, "SOUND_MIXER_READ_RECSRC"); 160292100Ssmh orecsrc = recsrc; 161292100Ssmh 162292100Ssmh if (argc == 1 && dusage == 0) { 163292100Ssmh for (foo = 0, n = 0; foo < SOUND_MIXER_NRDEVICES; foo++) { 164292100Ssmh if (!((1 << foo) & devmask)) 165292100Ssmh continue; 166292100Ssmh if (ioctl(baz, MIXER_READ(foo),&bar) == -1) { 167292100Ssmh warn("MIXER_READ"); 168292100Ssmh continue; 169292100Ssmh } 170292100Ssmh if (Sflag || sflag) { 171292100Ssmh printf("%s%s%c%d:%d", n ? " " : "", 172292100Ssmh names[foo], Sflag ? ':' : ' ', 173292100Ssmh bar & 0x7f, (bar >> 8) & 0x7f); 174292100Ssmh n++; 175292100Ssmh } else 176292100Ssmh printf("Mixer %-8s is currently set to " 177292100Ssmh "%3d:%d\n", names[foo], bar & 0x7f, 178292100Ssmh (bar >> 8) & 0x7f); 179292100Ssmh } 180292100Ssmh if (n && recmask) 181292100Ssmh printf(" "); 182292100Ssmh print_recsrc(recsrc, recmask, Sflag || sflag); 183292100Ssmh return (0); 184292100Ssmh } 185292100Ssmh 186292100Ssmh argc--; 187292100Ssmh argv++; 188292100Ssmh 189292100Ssmh n = 0; 190292100Ssmh while (argc > 0 && dusage == 0) { 191292100Ssmh if (strcmp("recsrc", *argv) == 0) { 192292100Ssmh drecsrc = 1; 193292100Ssmh argc--; 194292100Ssmh argv++; 195292100Ssmh continue; 196292100Ssmh } else if (strcmp("rec", *argv + 1) == 0) { 197292100Ssmh if (**argv != '+' && **argv != '-' && 198292100Ssmh **argv != '=' && **argv != '^') { 199292100Ssmh warnx("unknown modifier: %c", **argv); 200292100Ssmh dusage = 1; 201292100Ssmh break; 202292100Ssmh } 203292100Ssmh if (argc <= 1) { 204292100Ssmh warnx("no recording device specified"); 205292100Ssmh dusage = 1; 206292100Ssmh break; 207292100Ssmh } 208292100Ssmh if ((dev = res_name(argv[1], recmask)) == -1) { 209292100Ssmh warnx("unknown recording device: %s", argv[1]); 210292100Ssmh dusage = 1; 211292100Ssmh break; 212292100Ssmh } 213292100Ssmh switch (**argv) { 214292100Ssmh case '+': 215292100Ssmh recsrc |= (1 << dev); 216292100Ssmh break; 217292100Ssmh case '-': 218292100Ssmh recsrc &= ~(1 << dev); 219292100Ssmh break; 220292100Ssmh case '=': 221292100Ssmh recsrc = (1 << dev); 222292100Ssmh break; 223292100Ssmh case '^': 224292100Ssmh recsrc ^= (1 << dev); 225292100Ssmh break; 226292100Ssmh } 227292100Ssmh drecsrc = 1; 228292100Ssmh argc -= 2; 229292100Ssmh argv += 2; 230292100Ssmh continue; 231292100Ssmh } 232292100Ssmh 233292100Ssmh if ((t = sscanf(*argv, "%d:%d", &l, &r)) > 0) 234292100Ssmh dev = 0; 235292100Ssmh else if ((dev = res_name(*argv, devmask)) == -1) { 236292100Ssmh warnx("unknown device: %s", *argv); 237292100Ssmh dusage = 1; 238292100Ssmh break; 239292100Ssmh } 240292100Ssmh 241292100Ssmh lrel = rrel = 0; 242292100Ssmh if (argc > 1) { 243292100Ssmh m = sscanf(argv[1], "%7[^:]:%7s", lstr, rstr); 244292100Ssmh if (m > 0) { 245292100Ssmh if (*lstr == '+' || *lstr == '-') 246292100Ssmh lrel = rrel = 1; 247292100Ssmh l = strtol(lstr, NULL, 10); 248292100Ssmh } 249292100Ssmh if (m > 1) { 250292100Ssmh if (*rstr == '+' || *rstr == '-') 251292100Ssmh rrel = 1; 252292100Ssmh r = strtol(rstr, NULL, 10); 253292100Ssmh } 254292100Ssmh } 255292100Ssmh 256292100Ssmh switch (argc > 1 ? m : t) { 257292100Ssmh case 0: 258292100Ssmh if (ioctl(baz, MIXER_READ(dev), &bar) == -1) { 259292100Ssmh warn("MIXER_READ"); 260292100Ssmh argc--; 261292100Ssmh argv++; 262292100Ssmh continue; 263292100Ssmh } 264292100Ssmh if (Sflag || sflag) { 265292100Ssmh printf("%s%s%c%d:%d", n ? " " : "", 266292100Ssmh names[dev], Sflag ? ':' : ' ', 267292100Ssmh bar & 0x7f, (bar >> 8) & 0x7f); 268292100Ssmh n++; 269292100Ssmh } else 270292100Ssmh printf("Mixer %-8s is currently set to " 271292100Ssmh "%3d:%d\n", names[dev], bar & 0x7f, 272292100Ssmh (bar >> 8) & 0x7f); 273292100Ssmh 274292100Ssmh argc--; 275292100Ssmh argv++; 276292100Ssmh break; 277292100Ssmh case 1: 278292100Ssmh r = l; 279292100Ssmh /* FALLTHROUGH */ 280292100Ssmh case 2: 281292100Ssmh if (ioctl(baz, MIXER_READ(dev), &bar) == -1) { 282292100Ssmh warn("MIXER_READ"); 283292100Ssmh argc--; 284292100Ssmh argv++; 285292100Ssmh continue; 286292100Ssmh } 287292100Ssmh 288292100Ssmh if (lrel) 289292100Ssmh l = (bar & 0x7f) + l; 290292100Ssmh if (rrel) 291292100Ssmh r = ((bar >> 8) & 0x7f) + r; 292292100Ssmh 293292100Ssmh if (l < 0) 294292100Ssmh l = 0; 295292100Ssmh else if (l > 100) 296292100Ssmh l = 100; 297292100Ssmh if (r < 0) 298292100Ssmh r = 0; 299292100Ssmh else if (r > 100) 300292100Ssmh r = 100; 301292100Ssmh 302292100Ssmh if (!Sflag) 303292100Ssmh printf("Setting the mixer %s from %d:%d to " 304292100Ssmh "%d:%d.\n", names[dev], bar & 0x7f, 305292100Ssmh (bar >> 8) & 0x7f, l, r); 306292100Ssmh 307270631Sjfv l |= r << 8; 308270631Sjfv if (ioctl(baz, MIXER_WRITE(dev), &l) == -1) 309270631Sjfv warn("WRITE_MIXER"); 310270631Sjfv 311270631Sjfv argc -= 2; 312270631Sjfv argv += 2; 313270631Sjfv break; 314270631Sjfv } 315270631Sjfv } 316270631Sjfv 317270631Sjfv if (dusage) { 318270631Sjfv close(baz); 319270631Sjfv usage(devmask, recmask); 320270631Sjfv /* NOTREACHED */ 321291248Ssmh } 322291248Ssmh 323270631Sjfv if (orecsrc != recsrc) { 324270631Sjfv if (ioctl(baz, SOUND_MIXER_WRITE_RECSRC, &recsrc) == -1) 325270631Sjfv err(1, "SOUND_MIXER_WRITE_RECSRC"); 326270631Sjfv if (ioctl(baz, SOUND_MIXER_READ_RECSRC, &recsrc) == -1) 327270631Sjfv err(1, "SOUND_MIXER_READ_RECSRC"); 328270631Sjfv } 329291248Ssmh 330291248Ssmh if (drecsrc) 331291248Ssmh print_recsrc(recsrc, recmask, Sflag || sflag); 332291248Ssmh 333270631Sjfv close(baz); 334291248Ssmh 335291248Ssmh return (0); 336270631Sjfv} 337291248Ssmh