diff --git a/usr.sbin/mixer/mixer.8 b/usr.sbin/mixer/mixer.8 --- a/usr.sbin/mixer/mixer.8 +++ b/usr.sbin/mixer/mixer.8 @@ -19,7 +19,7 @@ .\" OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN .\" THE SOFTWARE. .\" -.Dd February 8, 2024 +.Dd August 14, 2024 .Dt MIXER 8 .Os .Sh NAME @@ -28,7 +28,7 @@ .Sh SYNOPSIS .Nm .Op Fl f Ar device -.Op Fl d Ar pcmN | N +.Op Fl d Ar pcmN | N Op Fl V Ar voss_device:mode .Op Fl os .Op Ar dev Ns Op Cm \&. Ns Ar control Ns Op Cm \&= Ns Ar value .Ar ... @@ -43,7 +43,7 @@ utility is used to set and display soundcard mixer device controls. .Pp The options are as follows: -.Bl -tag -width "-d pcmN | N" +.Bl -tag -width "-V voss_device:mode" .It Fl a Print the values for all mixer devices available in the system .Pq see Sx FILES . @@ -54,6 +54,31 @@ See .Sx EXAMPLES on how to list all available audio devices in the system. +.Pp +There is also the possibility of hot-swapping to the new default device if +.Xr virtual_oss 8 +exists in the system and is running on the default PCM device, in which case +the +.Fl V +option needs to be specified as well. +.Pp +Hot-swapping generally cannot happen with plain +.Xr sound 4 , +so the user has to restart the track in order to get sound coming out of the +new default device. +This is because applications usually open a device at the start of the track +and do not check for default device changes, in order to open the new device +mid-track. +.Xr virtual_oss 8 , +on the other hand, can do hot-swapping, because it creates a virtual device for +applications to open, and then does all the necessary routing and conversions +to the appropriate device(s). +.Pp +Note that hot-swapping will work only for applications that are using +.Xr virtual_oss 8 +devices, and not plain +.Xr sound 4 +ones. .It Fl f Ar device Open .Ar device @@ -66,6 +91,33 @@ 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. +.It Fl V Ar voss_device:mode +Specify a +.Xr virtual_oss 8 +control device, as well as a mode (see below), in order to hot-swap devices. +This option is meant to only be used in combination with the +.Fl d +option. +.Pp +The available modes are as follows: +.Bl -column play +.It Sy Mode Ta Sy Action +.It all Ta Playback and recording +.It play Ta Playback +.It rec Ta Recording +.El +.Pp +The +.Pa mode +part is needed, so that +.Nm +will not accidentally hot-swap both the recording and playback device in +.Xr virtual_oss 8 , +if only one direction is to be hot-swapped. +.Pp +See +.Sx EXAMPLES +on how to use this option. .El .Pp The list of mixer devices that may be modified are: @@ -273,10 +325,26 @@ \&... $ mixer -f /dev/mixer0 `cat info` .Ed +.Pp +Suppose +.Xr virtual_oss 8 +is running with +.Pa /dev/vdsp.ctl +as its control device, and +.Pa pcm0 +as the playback device. +Change the default device to +.Pa pcm1 , +and hot-swap to it for both recording and playback in +.Xr virtual_oss 8 : +.Bd -literal -offset indent +$ mixer -d pcm1 -V /dev/vdsp.ctl:all +.Ed .Sh SEE ALSO .Xr mixer 3 , .Xr sound 4 , -.Xr sysctl 8 +.Xr sysctl 8 , +.Xr virtual_oss 8 .Sh HISTORY The .Nm diff --git a/usr.sbin/mixer/mixer.c b/usr.sbin/mixer/mixer.c --- a/usr.sbin/mixer/mixer.c +++ b/usr.sbin/mixer/mixer.c @@ -20,6 +20,8 @@ * THE SOFTWARE. */ +#include + #include #include #include @@ -40,7 +42,7 @@ static void printminfo(struct mixer *, int); static void printdev(struct mixer *, int); static void printrecsrc(struct mixer *, int); /* XXX: change name */ -static int set_dunit(struct mixer *, int); +static int set_dunit(struct mixer *, int, char *); /* Control handlers */ static int mod_volume(struct mix_dev *, void *); static int mod_mute(struct mix_dev *, void *); @@ -54,13 +56,13 @@ { struct mixer *m; mix_ctl_t *cp; - char *name = NULL, buf[NAME_MAX]; + char *name = NULL, buf[NAME_MAX], *vctl = NULL; char *p, *q, *devstr, *ctlstr, *valstr = NULL; int dunit, i, n, pall = 1, shorthand; int aflag = 0, dflag = 0, oflag = 0, sflag = 0; int ch; - while ((ch = getopt(argc, argv, "ad:f:hos")) != -1) { + while ((ch = getopt(argc, argv, "ad:f:hosV:")) != -1) { switch (ch) { case 'a': aflag = 1; @@ -83,6 +85,9 @@ case 's': sflag = 1; break; + case 'V': + vctl = optarg; + break; case 'h': /* FALLTHROUGH */ case '?': default: @@ -119,7 +124,7 @@ initctls(m); if (dflag) { - if (set_dunit(m, dunit) < 0) + if (set_dunit(m, dunit, vctl) < 0) goto parse; else { /* @@ -209,7 +214,8 @@ static void __dead2 usage(void) { - fprintf(stderr, "usage: %1$s [-f device] [-d pcmN | N] [-os] [dev[.control[=value]]] ...\n" + fprintf(stderr, "usage: %1$s [-f device] [-d pcmN | N " + "[-V voss_device:mode]] [-os] [dev[.control[=value]]] ...\n" " %1$s [-os] -a\n" " %1$s -h\n", getprogname()); exit(1); @@ -322,14 +328,53 @@ } static int -set_dunit(struct mixer *m, int dunit) +set_dunit(struct mixer *m, int dunit, char *vctl) { - int n; + const char *opt; + char *dev, *mode; + char buf[32]; + int n, rc; if ((n = mixer_get_dunit()) < 0) { warn("cannot get default unit"); return (-1); } + /* Hot-swap in case virtual_oss exists and is running. */ + if (vctl != NULL) { + dev = strsep(&vctl, ":"); + mode = vctl; + if (dev == NULL || mode == NULL) { + warnx("voss_device:mode tuple incomplete"); + return (-1); + } + if (strcmp(mode, "all") == 0) + opt = "-f"; + else if (strcmp(mode, "play") == 0) + opt = "-P"; + else if (strcmp(mode, "rec") == 0) + opt = "-R"; + else { + warnx("please use one of the following modes: " + "all, play, rec"); + return (-1); + } + snprintf(buf, sizeof(buf), "/dev/dsp%d", dunit); + switch (fork()) { + case -1: + warn("fork"); + break; + case 0: + rc = execl("/usr/local/sbin/virtual_oss_cmd", + "virtual_oss_cmd", dev, opt, buf, NULL); + if (rc < 0) + warn("virtual_oss_cmd"); + _exit(0); + default: + if (wait(NULL) < 0) + warn("wait"); + break; + } + } if (mixer_set_dunit(m, dunit) < 0) { warn("cannot set default unit to %d", dunit); return (-1);