commit 79523985cb60e5f3301e9cbfd9745c633587c11b
parent 59b41ffceb9c625d13f70729ce4960d69cc511d2
Author: Christos Margiolis <christos@margiolis.net>
Date: Wed, 30 Jun 2021 01:08:59 +0300
mixer_prog: added handling through controls sysctl-style
Diffstat:
6 files changed, 511 insertions(+), 175 deletions(-)
diff --git a/mixer_lib/mixer.3 b/mixer_lib/mixer.3
@@ -20,7 +20,7 @@
.\" THE SOFTWARE.
.\"
-.Dd June 28, 2021
+.Dd June 30, 2021
.Dt mixer 3
.Os
.Sh NAME
diff --git a/mixer_lib/mixer.h b/mixer_lib/mixer.h
@@ -69,7 +69,7 @@ struct mix_dev {
};
struct mixer {
- TAILQ_HEAD(head, mix_dev) devs; /* XXX: use LIST? */
+ TAILQ_HEAD(head, mix_dev) devs;
struct mix_dev *dev;
oss_mixerinfo mi;
oss_card_info ci;
diff --git a/mixer_prog/mixer_prog.8 b/mixer_prog/mixer_prog.8
@@ -20,87 +20,124 @@
.\" THE SOFTWARE.
.\"
-.Dd June 28, 2021
+.Dd June 30, 2021
.Dt mixer 8
.Os
.Sh NAME
.Nm mixer
-.Nd manipulate soundcard mixer values
+.Nd manipulate soundcard mixer controls
.Sh SYNOPSIS
.Nm
-.Op Fl f device
-.Op Fl d unit
-.Op Fl o
-.Oo
-.Ar dev
-.Sm off
-.Oo
-.Op Cm + | -
-.Ar lvol
-.Op : Oo Cm + | - Oc Ar rvol
-.Oc
-.Oc
-.Sm on
-.Ar ...
-.Nm
-.Op Fl f device
-.Op Fl d unit
-.Op Fl o
-.Fl s
-.Ar ...
-.Nm
-.Op Fl f device
-.Op Fl d unit
-.Op Fl o
-.Sm off
-.Bro
-.Cm ^ | + | - | =
-.Brc
-.Cm rec
-.Sm on
-.Ar rdev ...
+.Op Fl f Ar device
+.Op Fl d Ar unit
+.Op Fl os
+.Op Ar command Ar ...
.Nm
-.Op Fl o
+.Op Fl d Ar unit
+.Op Fl os
.Fl a
.Sh DESCRIPTION
The
.Nm
-utility is used to set and display soundcard mixer device levels. The list of mixer
-devices that may be modified are:
+utility is used to set and display soundcard mixer device controls.
+.Pp
+The options are as follows:
+.Bl -tag -width "-f device"
+.It Fl a
+Print the values for all mixer devices available in the system (see FILES).
+.It Fl d Ar unit
+Change the default audio card to
+.Ar unit .
+The unit has to be an integer value. To see what unit values are available, look
+at the number each mixer device has by running
+.Nm .
+.It Fl f Ar device
+Open
+.Ar device
+as the mixer device (see FILES).
+.It Fl o
+Print mixer values in a format suitable for use inside scripts. The
+mixer's header (name, audio card name, ...) will not be printed.
+.It Fl s
+Print only the recording source(s) of the mixer device.
+.El
+.Pp
+The list of mixer devices that may be modified are:
.Bd -ragged -offset indent
vol, bass, treble, synth, pcm, speaker, line, mic, cd, mix,
pcm2, rec, igain, ogain, line1, line2, line3, dig1, dig2, dig3,
phin, phout, video, radio, and monitor.
.Ed
.Pp
+Not all mixer devices are available.
+.Pp
Without any arguments,
.Nm
-displays information and the current settings for all supported devices.
-If the
+displays all information for each one of the mixer's supported devices to
+.Ar stdout .
+If the \" TODO: explain what "default mixer" means.
.Ar dev
argument is specified,
.Nm
displays only the values for
.Ar dev .
+More than one device may be specified.
+.Pp
+Commands use the following format:
+.Pp
+.Bl -column xxxxxxxxxxxxxxxxxxxxxxxx -offset indent
+.It Sy "Name Action"
+.It "dev Display all controls"
+.It "dev.control Display only the specified control"
+.It "dev.control=value Set control value"
+.El
+.Pp
+The available controls are as follows (replace
+.Ar dev
+with one of the available devices):
+.Bl -column xxxxxxxxxxxxxxxxxxxxxxxx -offset indent
+.It Sy "Name Value"
+.It "dev.volume [[+|-]lvol[:[+|-]rvol]]"
+.It "dev.rec {+|-|^|=}"
+.El
.Pp
To modify a device's volume, the optional
.Ar lvol
and/or
.Ar rvol
values have to be specified. The values have to be normalized 32-bit floats
-(0.0 to 1.0). Omitting
-.Ar dev
-will change the value of the main channel (usually "vol").
-If the the left or right volume values are prefixed with
+(0.0 to 1.0). If the the left or right volume values are prefixed with
.Cm +
or
.Cm - ,
the value following will be used as a relative adjustment, modifying the
current settings by the amount specified.
+.Pp
+.Nm
+marks devices which can be used as a recording source with
+.Ar rec .
+To modify the recording source you can use one of:
+.Bl -tag -width =rec -offset indent
+.It Cm ^
+toggles
+.Ar dev
+of possible recording devices
+.It Cm +
+adds
+.Ar dev
+to possible recording devices
+.It Cm -
+removes
+.Ar dev
+from possible recording devices
+.It Cm =
+sets the recording device to
+.Ar dev
+.El
.Sh FILES
.Bl -tag -width /dev/mixerN -compact
.It Pa /dev/mixerN
-the mixer device, where
+The mixer device, where
.Ar N
is the number of that device, for example
.Ar /dev/mixer0 .
@@ -108,11 +145,64 @@ PCM cards and mixers have a 1:1 relationship, which means that
.Ar mixer0
is the mixer for
.Ar pcm0
-and so on.
+and so on. By default,
+.Nm
+prints both the audio card's number and the mixer associated with it
+in the form of
+.Ar pcmN:mixer .
+The
+.Ar /dev/mixer
+file, although it doesn't exist in the filesystem, points to the default
+mixer device and is the file
+.Nm
+opens when the
+.Fl f Ar device
+option has not been specified.
+.El
+.Sh EXAMPLES
+.Pp
+Change the volume for the
+.Ar vol
+device of the
+.Ar /dev/mixer0
+mixer device to 0.65:
+.Bl -tag -width Ds -offset indent
+.It $ mixer -f /dev/mixer0 vol.volume=0.65
+.El
+.Pp
+Increase the
+.Ar mic
+device's left volume by 0.10 and decrease the right
+volume by 0.05:
+.Bl -tag -width Ds -offset indent
+.It $ mixer mic.volume=+0.10:-0.05
+.El
+.Pp
+Set
+.Ar mic
+and toggle
+.Ar line
+recording sources:
+.Bl -tag -width Ds -offset indent
+.It $ mixer mic.rec=+ line.rec=^
+.El
+.Pp
+Dump
+.Ar /dev/mixer0
+information to a file and retrieve back later
+.Bl -tag -width Ds -offset indent
+.It $ mixer -f /dev/mixer0 -o > info
+.It ...
+.It $ mixer -f /dev/mixer0 `cat info`
.El
-\" TODO: write about the rest of the program.
.Sh SEE ALSO
.Xr mixer 3 ,
-.Xr sound 4
+.Xr sound 4 ,
+.Xr sysctl 8
+.Sh HISTORY
+The
+.Nm
+utility first appeared in FreeBSD 2.0.5 and was rewritten completely in
+FreeBSD 12.2. \" FIXME: replace 12.2 with proper version.
.Sh AUTHORS
.An Christos Margiolis Aq Mt christos@margiolis.net
diff --git a/mixer_prog/mixer_prog.c b/mixer_prog/mixer_prog.c
@@ -29,22 +29,28 @@
#include <mixer.h>
-static void usage(void) __dead2;
+#define CTRL_VOL 0
+#define CTRL_REC 1
+
+#define LEN(x) (sizeof(x) / sizeof(x[0]))
+
+struct ctrl {
+ char name[NAME_MAX];
+ void (*mod)(struct mixer *, const char *, const char *);
+};
+
static void printall(struct mixer *, int);
static void printmixer(struct mixer *);
static void printdev(struct mix_dev *, int);
static void printrecsrc(struct mixer *, int);
+static void modvol(struct mixer *, const char *, const char *);
+static void modrecsrc(struct mixer *, const char *, const char *);
+static void usage(void) __dead2;
-static void __dead2
-usage(void)
-{
- printf("usage: %1$s [-f device] [-d unit] [-o] [dev [+|-][lvol[:[+|-]rvol]] ...\n"
- " %1$s [-f device] [-d unit] [-o] -s ...\n"
- " %1$s [-f device] [-d unit] [-o] {^|+|-|=}rec rdev ...\n"
- " %1$s [-o] -a\n",
- getprogname());
- exit(1);
-}
+struct ctrl ctrls[] = {
+ [CTRL_VOL] = { "volume", modvol },
+ [CTRL_REC] = { "rec", modrecsrc },
+};
static void
printall(struct mixer *m, int oflag)
@@ -81,10 +87,10 @@ printdev(struct mix_dev *d, int oflag)
printf(" src");
printf("\n");
} else {
- /* FIXME: get rid of the space */
- printf("%s %.2f:%.2f ", d->name, d->lvol, d->rvol);
+ printf("%s.%s=%.2f:%.2f\n",
+ d->name, ctrls[CTRL_VOL].name, d->lvol, d->rvol);
if (d->f_src)
- printf("+rec %s ", d->name);
+ printf("%s.%s=+\n", d->name, ctrls[CTRL_REC].name);
}
}
@@ -98,31 +104,131 @@ printrecsrc(struct mixer *m, int oflag)
return;
if (!oflag) {
printmixer(m);
- printf(" recording source: ");
+ printf(" recording source(s): ");
}
TAILQ_FOREACH(dp, &m->devs, devs) {
if (M_ISRECSRC(m, dp->devno)) {
- if (n)
+ if (n++)
printf("%s ", oflag ? " " : ", ");
printf("%s", dp->name);
- n++;
}
}
printf("\n");
}
+static void
+modvol(struct mixer *m, const char *dev, const char *val)
+{
+ char lstr[8], rstr[8];
+ float l, r, lprev, rprev, lrel, rrel;
+ int n;
+
+ n = sscanf(val, "%7[^:]:%7s", lstr, rstr);
+ if (n == EOF) {
+ warnx("invalid value: %s", val);
+ return;
+ }
+ lrel = rrel = 0;
+ if (n > 0) {
+ if (*lstr == '+' || *lstr == '-')
+ lrel = rrel = 1;
+ l = strtof(lstr, NULL);
+ }
+ if (n > 1) {
+ if (*rstr == '+' || *rstr == '-')
+ rrel = 1;
+ r = strtof(rstr, NULL);
+ }
+ switch (n) {
+ case 1:
+ r = l; /* FALLTHROUGH */
+ case 2:
+ if (lrel)
+ l += m->dev->lvol;
+ if (rrel)
+ r += m->dev->rvol;
+
+ if (l < M_VOLMIN)
+ l = M_VOLMIN;
+ else if (l > M_VOLMAX)
+ l = M_VOLMAX;
+ if (r < M_VOLMIN)
+ r = M_VOLMIN;
+ else if (r > M_VOLMAX)
+ r = M_VOLMAX;
+
+ if ((m->dev = mixer_getdevbyname(m, dev)) == NULL) {
+ warn("cannot open device: %s\n", dev);
+ return;
+ }
+ lprev = m->dev->lvol;
+ rprev = m->dev->rvol;
+ if (mixer_setvol(m, l, r) < 0) {
+ warnx("cannot change volume");
+ return;
+ }
+ printf("%s.%s: %.2f:%.2f -> %.2f:%.2f\n",
+ m->dev->name, ctrls[CTRL_VOL].name, lprev, rprev, l, r);
+ }
+}
+
+static void
+modrecsrc(struct mixer *m, const char *dev, const char *val)
+{
+ int n, opt = 0;
+
+ if (*val != '+' && *val != '-' &&
+ *val != '=' && *val != '^') {
+ warnx("unknown modifier: %c", *val);
+ return;
+ }
+ switch (*val) {
+ case '+':
+ opt = M_ADDRECDEV;
+ break;
+ case '-':
+ opt = M_REMOVERECDEV;
+ break;
+ case '=':
+ opt = M_SETRECDEV;
+ break;
+ case '^':
+ opt = M_TOGGLERECDEV;
+ break;
+ }
+ if ((m->dev = mixer_getdevbyname(m, dev)) == NULL) {
+ warn("unknown recording device: %s", dev);
+ return;
+ }
+ /* Keep the previous state. */
+ n = m->dev->f_src != 0;
+ if (mixer_modrecsrc(m, opt) < 0)
+ warn("cannot modify device: %c%s", *val, dev);
+ else
+ printf("%s.%s: %d -> %d\n",
+ dev, ctrls[CTRL_REC].name, n, m->dev->f_src != 0);
+}
+
+static void __dead2
+usage(void)
+{
+ printf("usage: %1$s [-f device] [-d unit] [-os] [command ...]\n"
+ " %1$s [-d unit] [-os] -a\n",
+ getprogname());
+ exit(1);
+}
+
int
main(int argc, char *argv[])
{
struct mixer *m;
- char lstr[8], rstr[8], *name = NULL, buf[NAME_MAX];
- float l, r, lrel, rrel;
- int dusage = 0, opt = 0, dunit;
+ char *name = NULL, buf[NAME_MAX];
+ char *p, *bufp, *devstr, *ctrlstr, *valstr = NULL;
+ int dunit, i, n;
int aflag = 0, dflag = 0, oflag = 0, sflag = 0;
- int i, rc = 0;
- char ch, n, k;
+ char ch;
- while ((ch = getopt(argc, argv, "ad:f:or:s")) != -1) {
+ while ((ch = getopt(argc, argv, "ad:f:os")) != -1) {
switch (ch) {
case 'a':
aflag = 1;
@@ -139,10 +245,6 @@ main(int argc, char *argv[])
case 'o':
oflag = 1;
break;
- case 'r':
- /* Reserved for {+|-|^|=}rec rdev. */
- /* FIXME: problems with -rec */
- break;
case 's':
sflag = 1;
break;
@@ -176,11 +278,9 @@ main(int argc, char *argv[])
if ((m = mixer_open(name)) == NULL)
err(1, "mixer_open: %s", name);
- while (!dusage) {
+ for (;;) {
if (dflag) {
- /* We don't want to get in here again. */
dflag = 0;
- /* XXX: should we die if any of these two fails? */
if ((n = mixer_getdunit()) < 0) {
warn("cannot get default unit");
continue;
@@ -193,114 +293,52 @@ main(int argc, char *argv[])
} else if (sflag) {
printrecsrc(m, oflag);
goto done;
- } else if (argc > 0 && strcmp("rec", *argv + 1) == 0) {
- if (**argv != '+' && **argv != '-' &&
- **argv != '=' && **argv != '^') {
- warnx("unknown modifier: %c", **argv);
- dusage = 1;
- break;
+ } else if (argc > 0) {
+ if ((p = bufp = strdup(*argv)) == NULL)
+ err(1, "strdup(%s)", *argv);
+ /* Split the string into name, control and value. */
+ devstr = strsep(&p, ".");
+ /*
+ * The input was only the device name, so we'll just
+ * print all its information.
+ */
+ if (p == NULL) {
+ /* TODO */
}
- switch (**argv) {
- case '+':
- opt = M_ADDRECDEV;
- break;
- case '-':
- opt = M_REMOVERECDEV;
- break;
- case '=':
- opt = M_SETRECDEV;
- break;
- case '^':
- opt = M_TOGGLERECDEV;
- break;
+ ctrlstr = strsep(&p, "=");
+ /*
+ * If we don't have an assignment, print the control's
+ * values.
+ */
+ if (p == NULL) {
+ /* TODO */
}
- if ((m->dev = mixer_getdevbyname(m, argv[1])) == NULL) {
- warn("unknown recording device: %s", argv[1]);
- /* XXX */
- rc = 1;
- goto done;
+ valstr = p;
+ if (ctrlstr == NULL || valstr == NULL) {
+ /* FIXME: doesn't print everything */
+ warnx("invalid syntax: %s", bufp);
+ goto next;
}
- /* Keep the previous state. */
- n = m->dev->f_src != 0;
- if (mixer_modrecsrc(m, opt) < 0)
- warn("cannot modify device: %c%s", **argv, argv[1]);
- else
- printf("%s.recsrc: %d -> %d\n", argv[1],
- n, m->dev->f_src != 0);
- argc -= 2;
- argv += 2;
- } else if (argc > 0) {
- if ((k = sscanf(*argv, "%f:%f", &l, &r)) > 0)
- ; /* nothing */
- else if ((m->dev = mixer_getdevbyname(m, *argv)) == NULL) {
- warn("unknown device: %s", *argv);
- rc = 1;
- goto done;
- }
-
- lrel = rrel = 0;
- if (argc > 1) {
- n = sscanf(argv[1], "%7[^:]:%7s", lstr, rstr);
- if (n == EOF) {
- warnx("invalid value: %s", argv[1]);
- dusage = 1;
+ for (i = 0; i < LEN(ctrls); i++) {
+ if (strncmp(ctrlstr, ctrls[i].name,
+ strlen(ctrls[i].name)) == 0) {
+ ctrls[i].mod(m, devstr, valstr);
break;
}
- if (n > 0) {
- if (*lstr == '+' || *lstr == '-')
- lrel = rrel = 1;
- l = strtof(lstr, NULL);
- }
- if (n > 1) {
- if (*rstr == '+' || *rstr == '-')
- rrel = 1;
- r = strtof(rstr, NULL);
- }
- }
- switch (argc > 1 ? n : k) {
- case 0:
- if (!oflag)
- printmixer(m);
- printdev(m->dev, oflag);
- goto done;
- case 1:
- r = l; /* FALLTHROUGH */
- case 2:
- if (lrel)
- l += m->dev->lvol;
- if (rrel)
- r += m->dev->rvol;
-
- if (l < M_VOLMIN)
- l = M_VOLMIN;
- else if (l > M_VOLMAX)
- l = M_VOLMAX;
- if (r < M_VOLMIN)
- r = M_VOLMIN;
- else if (r > M_VOLMAX)
- r = M_VOLMAX;
-
- printf("%s.volume: %.2f:%.2f -> %.2f:%.2f\n",
- m->dev->name, m->dev->lvol, m->dev->rvol, l, r);
-
- if (mixer_setvol(m, l, r) < 0) {
- warnx("cannot change volume");
- rc = 1;
- }
- argc -= 2;
- argv += 2;
}
+ if (i == LEN(ctrls))
+ warnx("invalid control: %s", ctrlstr);
+next:
+ free(p);
+ argc--;
+ argv++;
} else
break;
}
- if (dusage) {
- (void)mixer_close(m);
- usage();
- } else
- printall(m, oflag);
+ printall(m, oflag);
done:
(void)mixer_close(m);
- return (rc);
+ return (0);
}
diff --git a/rc.d/mixer b/rc.d/mixer
@@ -0,0 +1,104 @@
+#!/bin/sh -
+#
+# Copyright (c) 2004 The FreeBSD Project
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $FreeBSD: releng/12.2/libexec/rc/rc.d/mixer 298514 2016-04-23 16:10:54Z lme $
+#
+
+# PROVIDE: mixer
+# REQUIRE: FILESYSTEMS
+# KEYWORD: nojail shutdown
+
+. /etc/rc.subr
+
+name="mixer"
+desc="Save and restore soundcard mixer values"
+rcvar="mixer_enable"
+stop_cmd="mixer_stop"
+start_cmd="mixer_start"
+reload_cmd="mixer_start"
+extra_commands="reload"
+
+#
+# List current mixer devices to stdout.
+#
+list_mixers()
+{
+ ( cd /dev ; ls mixer* 2>/dev/null )
+}
+
+#
+# Save state of an individual mixer specified as $1
+#
+mixer_save()
+{
+ local dev
+
+ dev="/dev/${1}"
+ if [ -r ${dev} ]; then
+ /usr/sbin/mixer_prog -f ${dev} -o > /var/db/${1}-state 2>/dev/null
+ fi
+}
+
+#
+# Restore the state of an individual mixer specified as $1
+#
+mixer_restore()
+{
+ local file dev
+
+ dev="/dev/${1}"
+ file="/var/db/${1}-state"
+ if [ -r ${dev} -a -r ${file} ]; then
+ /usr/sbin/mixer_prog -f ${dev} `cat ${file}` > /dev/null
+ fi
+}
+
+#
+# Restore state of all mixers
+#
+mixer_start()
+{
+ local mixer
+
+ for mixer in `list_mixers`; do
+ mixer_restore ${mixer}
+ done
+}
+
+#
+# Save the state of all mixers
+#
+mixer_stop()
+{
+ local mixer
+
+ for mixer in `list_mixers`; do
+ mixer_save ${mixer}
+ done
+}
+
+load_rc_config $name
+run_rc_command "$1"
diff --git a/rc.d/mixer.old b/rc.d/mixer.old
@@ -0,0 +1,104 @@
+#!/bin/sh -
+#
+# Copyright (c) 2004 The FreeBSD Project
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $FreeBSD$
+#
+
+# PROVIDE: mixer
+# REQUIRE: FILESYSTEMS
+# KEYWORD: nojail shutdown
+
+. /etc/rc.subr
+
+name="mixer"
+desc="Save and restore soundcard mixer values"
+rcvar="mixer_enable"
+stop_cmd="mixer_stop"
+start_cmd="mixer_start"
+reload_cmd="mixer_start"
+extra_commands="reload"
+
+#
+# List current mixer devices to stdout.
+#
+list_mixers()
+{
+ ( cd /dev ; ls mixer* 2>/dev/null )
+}
+
+#
+# Save state of an individual mixer specified as $1
+#
+mixer_save()
+{
+ local dev
+
+ dev="/dev/${1}"
+ if [ -r ${dev} ]; then
+ /usr/sbin/mixer -f ${dev} -s > /var/db/${1}-state 2>/dev/null
+ fi
+}
+
+#
+# Restore the state of an individual mixer specified as $1
+#
+mixer_restore()
+{
+ local file dev
+
+ dev="/dev/${1}"
+ file="/var/db/${1}-state"
+ if [ -r ${dev} -a -r ${file} ]; then
+ /usr/sbin/mixer -f ${dev} `cat ${file}` > /dev/null
+ fi
+}
+
+#
+# Restore state of all mixers
+#
+mixer_start()
+{
+ local mixer
+
+ for mixer in `list_mixers`; do
+ mixer_restore ${mixer}
+ done
+}
+
+#
+# Save the state of all mixers
+#
+mixer_stop()
+{
+ local mixer
+
+ for mixer in `list_mixers`; do
+ mixer_save ${mixer}
+ done
+}
+
+load_rc_config $name
+run_rc_command "$1"