babc4e8145
This patch fixes a bug that made the mixer command enter an infinite loop when instructed to set the value of a device to an empty string (e.g., `mixer vol ""`). Additionally, some tests for mixer(8) are being added. PR: 240039 Reviewed by: hselasky, mav Approved by: src (hselasky, mav) MFC after: 1 week Differential Revision: https://reviews.freebsd.org/D21409
342 lines
7.0 KiB
C
342 lines
7.0 KiB
C
/*
|
|
* This is an example of a mixer program for Linux
|
|
*
|
|
* updated 1/1/93 to add stereo, level query, broken
|
|
* devmask kludge - cmetz@thor.tjhsst.edu
|
|
*
|
|
* (C) Craig Metz and Hannu Savolainen 1993.
|
|
*
|
|
* You may do anything you wish with this program.
|
|
*
|
|
* ditto for my modifications (John-Mark Gurney, 1997)
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
__FBSDID("$FreeBSD$");
|
|
|
|
#include <err.h>
|
|
#include <fcntl.h>
|
|
#include <libgen.h>
|
|
#include <limits.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <sys/soundcard.h>
|
|
|
|
static const char *names[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_NAMES;
|
|
|
|
static void usage(int devmask, int recmask) __dead2;
|
|
static int res_name(const char *name, int mask);
|
|
static void print_recsrc(int recsrc, int recmask, int sflag);
|
|
|
|
static void __dead2
|
|
usage(int devmask, int recmask)
|
|
{
|
|
int i, n;
|
|
|
|
printf("usage: mixer [-f device] [-s | -S] [dev [+|-][voll[:[+|-]volr]] ...\n"
|
|
" mixer [-f device] [-s | -S] recsrc ...\n"
|
|
" mixer [-f device] [-s | -S] {^|+|-|=}rec rdev ...\n");
|
|
if (devmask != 0) {
|
|
printf(" devices: ");
|
|
for (i = 0, n = 0; i < SOUND_MIXER_NRDEVICES; i++)
|
|
if ((1 << i) & devmask) {
|
|
if (n)
|
|
printf(", ");
|
|
printf("%s", names[i]);
|
|
n++;
|
|
}
|
|
}
|
|
if (recmask != 0) {
|
|
printf("\n rec devices: ");
|
|
for (i = 0, n = 0; i < SOUND_MIXER_NRDEVICES; i++)
|
|
if ((1 << i) & recmask) {
|
|
if (n)
|
|
printf(", ");
|
|
printf("%s", names[i]);
|
|
n++;
|
|
}
|
|
}
|
|
printf("\n");
|
|
exit(1);
|
|
}
|
|
|
|
static int
|
|
res_name(const char *name, int mask)
|
|
{
|
|
int foo;
|
|
|
|
for (foo = 0; foo < SOUND_MIXER_NRDEVICES; foo++)
|
|
if ((1 << foo) & mask && strcmp(names[foo], name) == 0)
|
|
break;
|
|
|
|
return (foo == SOUND_MIXER_NRDEVICES ? -1 : foo);
|
|
}
|
|
|
|
static void
|
|
print_recsrc(int recsrc, int recmask, int sflag)
|
|
{
|
|
int i, n;
|
|
|
|
if (recmask == 0)
|
|
return;
|
|
|
|
if (!sflag)
|
|
printf("Recording source: ");
|
|
|
|
for (i = 0, n = 0; i < SOUND_MIXER_NRDEVICES; i++)
|
|
if ((1 << i) & recsrc) {
|
|
if (sflag)
|
|
printf("%srec ", n ? " +" : "=");
|
|
else if (n)
|
|
printf(", ");
|
|
printf("%s", names[i]);
|
|
n++;
|
|
}
|
|
if (!sflag)
|
|
printf("\n");
|
|
}
|
|
|
|
int
|
|
main(int argc, char *argv[])
|
|
{
|
|
char mixer[PATH_MAX] = "/dev/mixer";
|
|
char lstr[8], rstr[8];
|
|
char *name, *eptr;
|
|
int devmask = 0, recmask = 0, recsrc = 0, orecsrc;
|
|
int dusage = 0, drecsrc = 0, sflag = 0, Sflag = 0;
|
|
int l, r, lrel, rrel;
|
|
int ch, foo, bar, baz, dev, m, n, t;
|
|
|
|
if ((name = strdup(basename(argv[0]))) == NULL)
|
|
err(1, "strdup()");
|
|
if (strncmp(name, "mixer", 5) == 0 && name[5] != '\0') {
|
|
n = strtol(name + 5, &eptr, 10) - 1;
|
|
if (n > 0 && *eptr == '\0')
|
|
snprintf(mixer, PATH_MAX - 1, "/dev/mixer%d", n);
|
|
}
|
|
free(name);
|
|
name = mixer;
|
|
|
|
n = 1;
|
|
for (;;) {
|
|
if (n >= argc || *argv[n] != '-')
|
|
break;
|
|
if (strlen(argv[n]) != 2) {
|
|
if (strcmp(argv[n] + 1, "rec") != 0)
|
|
dusage = 1;
|
|
break;
|
|
}
|
|
ch = *(argv[n] + 1);
|
|
if (ch == 'f' && n < argc - 1) {
|
|
name = argv[n + 1];
|
|
n += 2;
|
|
} else if (ch == 's') {
|
|
sflag = 1;
|
|
n++;
|
|
} else if (ch == 'S') {
|
|
Sflag = 1;
|
|
n++;
|
|
} else {
|
|
dusage = 1;
|
|
break;
|
|
}
|
|
}
|
|
if (sflag && Sflag)
|
|
dusage = 1;
|
|
|
|
argc -= n - 1;
|
|
argv += n - 1;
|
|
|
|
if ((baz = open(name, O_RDWR)) < 0)
|
|
err(1, "%s", name);
|
|
if (ioctl(baz, SOUND_MIXER_READ_DEVMASK, &devmask) == -1)
|
|
err(1, "SOUND_MIXER_READ_DEVMASK");
|
|
if (ioctl(baz, SOUND_MIXER_READ_RECMASK, &recmask) == -1)
|
|
err(1, "SOUND_MIXER_READ_RECMASK");
|
|
if (ioctl(baz, SOUND_MIXER_READ_RECSRC, &recsrc) == -1)
|
|
err(1, "SOUND_MIXER_READ_RECSRC");
|
|
orecsrc = recsrc;
|
|
|
|
if (argc == 1 && dusage == 0) {
|
|
for (foo = 0, n = 0; foo < SOUND_MIXER_NRDEVICES; foo++) {
|
|
if (!((1 << foo) & devmask))
|
|
continue;
|
|
if (ioctl(baz, MIXER_READ(foo),&bar) == -1) {
|
|
warn("MIXER_READ");
|
|
continue;
|
|
}
|
|
if (Sflag || sflag) {
|
|
printf("%s%s%c%d:%d", n ? " " : "",
|
|
names[foo], Sflag ? ':' : ' ',
|
|
bar & 0x7f, (bar >> 8) & 0x7f);
|
|
n++;
|
|
} else
|
|
printf("Mixer %-8s is currently set to "
|
|
"%3d:%d\n", names[foo], bar & 0x7f,
|
|
(bar >> 8) & 0x7f);
|
|
}
|
|
if (n && recmask)
|
|
printf(" ");
|
|
print_recsrc(recsrc, recmask, Sflag || sflag);
|
|
return (0);
|
|
}
|
|
|
|
argc--;
|
|
argv++;
|
|
|
|
n = 0;
|
|
while (argc > 0 && dusage == 0) {
|
|
if (strcmp("recsrc", *argv) == 0) {
|
|
drecsrc = 1;
|
|
argc--;
|
|
argv++;
|
|
continue;
|
|
} else if (strcmp("rec", *argv + 1) == 0) {
|
|
if (**argv != '+' && **argv != '-' &&
|
|
**argv != '=' && **argv != '^') {
|
|
warnx("unknown modifier: %c", **argv);
|
|
dusage = 1;
|
|
break;
|
|
}
|
|
if (argc <= 1) {
|
|
warnx("no recording device specified");
|
|
dusage = 1;
|
|
break;
|
|
}
|
|
if ((dev = res_name(argv[1], recmask)) == -1) {
|
|
warnx("unknown recording device: %s", argv[1]);
|
|
dusage = 1;
|
|
break;
|
|
}
|
|
switch (**argv) {
|
|
case '+':
|
|
recsrc |= (1 << dev);
|
|
break;
|
|
case '-':
|
|
recsrc &= ~(1 << dev);
|
|
break;
|
|
case '=':
|
|
recsrc = (1 << dev);
|
|
break;
|
|
case '^':
|
|
recsrc ^= (1 << dev);
|
|
break;
|
|
}
|
|
drecsrc = 1;
|
|
argc -= 2;
|
|
argv += 2;
|
|
continue;
|
|
}
|
|
|
|
if ((t = sscanf(*argv, "%d:%d", &l, &r)) > 0)
|
|
dev = 0;
|
|
else if ((dev = res_name(*argv, devmask)) == -1) {
|
|
warnx("unknown device: %s", *argv);
|
|
dusage = 1;
|
|
break;
|
|
}
|
|
|
|
lrel = rrel = 0;
|
|
if (argc > 1) {
|
|
m = sscanf(argv[1], "%7[^:]:%7s", lstr, rstr);
|
|
if (m == EOF) {
|
|
warnx("invalid value: %s", argv[1]);
|
|
dusage = 1;
|
|
break;
|
|
}
|
|
if (m > 0) {
|
|
if (*lstr == '+' || *lstr == '-')
|
|
lrel = rrel = 1;
|
|
l = strtol(lstr, NULL, 10);
|
|
}
|
|
if (m > 1) {
|
|
if (*rstr == '+' || *rstr == '-')
|
|
rrel = 1;
|
|
r = strtol(rstr, NULL, 10);
|
|
}
|
|
}
|
|
|
|
switch (argc > 1 ? m : t) {
|
|
case 0:
|
|
if (ioctl(baz, MIXER_READ(dev), &bar) == -1) {
|
|
warn("MIXER_READ");
|
|
argc--;
|
|
argv++;
|
|
continue;
|
|
}
|
|
if (Sflag || sflag) {
|
|
printf("%s%s%c%d:%d", n ? " " : "",
|
|
names[dev], Sflag ? ':' : ' ',
|
|
bar & 0x7f, (bar >> 8) & 0x7f);
|
|
n++;
|
|
} else
|
|
printf("Mixer %-8s is currently set to "
|
|
"%3d:%d\n", names[dev], bar & 0x7f,
|
|
(bar >> 8) & 0x7f);
|
|
|
|
argc--;
|
|
argv++;
|
|
break;
|
|
case 1:
|
|
r = l;
|
|
/* FALLTHROUGH */
|
|
case 2:
|
|
if (ioctl(baz, MIXER_READ(dev), &bar) == -1) {
|
|
warn("MIXER_READ");
|
|
argc--;
|
|
argv++;
|
|
continue;
|
|
}
|
|
|
|
if (lrel)
|
|
l = (bar & 0x7f) + l;
|
|
if (rrel)
|
|
r = ((bar >> 8) & 0x7f) + r;
|
|
|
|
if (l < 0)
|
|
l = 0;
|
|
else if (l > 100)
|
|
l = 100;
|
|
if (r < 0)
|
|
r = 0;
|
|
else if (r > 100)
|
|
r = 100;
|
|
|
|
if (!Sflag)
|
|
printf("Setting the mixer %s from %d:%d to "
|
|
"%d:%d.\n", names[dev], bar & 0x7f,
|
|
(bar >> 8) & 0x7f, l, r);
|
|
|
|
l |= r << 8;
|
|
if (ioctl(baz, MIXER_WRITE(dev), &l) == -1)
|
|
warn("WRITE_MIXER");
|
|
|
|
argc -= 2;
|
|
argv += 2;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (dusage) {
|
|
close(baz);
|
|
usage(devmask, recmask);
|
|
/* NOTREACHED */
|
|
}
|
|
|
|
if (orecsrc != recsrc) {
|
|
if (ioctl(baz, SOUND_MIXER_WRITE_RECSRC, &recsrc) == -1)
|
|
err(1, "SOUND_MIXER_WRITE_RECSRC");
|
|
if (ioctl(baz, SOUND_MIXER_READ_RECSRC, &recsrc) == -1)
|
|
err(1, "SOUND_MIXER_READ_RECSRC");
|
|
}
|
|
|
|
if (drecsrc)
|
|
print_recsrc(recsrc, recmask, Sflag || sflag);
|
|
|
|
close(baz);
|
|
|
|
return (0);
|
|
}
|