The branch main has been updated by christos:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=9aac27599acaffa21ff69c5be8a2d71d29cc3d6b

commit 9aac27599acaffa21ff69c5be8a2d71d29cc3d6b
Author:     Christos Margiolis <[email protected]>
AuthorDate: 2024-08-24 12:07:35 +0000
Commit:     Christos Margiolis <[email protected]>
CommitDate: 2024-08-24 12:07:35 +0000

    mixer(8): Implement hot-swapping
    
    Introduce a -V option, which can be used alongside -d (default unit
    change), in order to hot-swap devices (i.e switch to them on the fly
    without needing to restart the track), in case virtual_oss(8) exists and
    is running.
    
    Sponsored by:   The FreeBSD Foundation
    MFC after:      2 days
    Reviewed by:    dev_submerge.ch
    Differential Revision:  https://reviews.freebsd.org/D46253
---
 usr.sbin/mixer/mixer.8 | 75 +++++++++++++++++++++++++++++++++++++++++++---
 usr.sbin/mixer/mixer.c | 81 +++++++++++++++++++++++++++++++++++++++++++++-----
 2 files changed, 145 insertions(+), 11 deletions(-)

diff --git a/usr.sbin/mixer/mixer.8 b/usr.sbin/mixer/mixer.8
index 75c6a81e3a55..819d8ae73ab1 100644
--- 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 @@ The
 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,30 @@ where N is the unit number (e.g for pcm0, the unit number is 
0).
 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, 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 +90,33 @@ 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.
+.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 +324,26 @@ $ mixer -f /dev/mixer0 -o > info
 \&...
 $ 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
index 109d3ad09bc5..468130ddaa88 100644
--- a/usr.sbin/mixer/mixer.c
+++ b/usr.sbin/mixer/mixer.c
@@ -20,6 +20,9 @@
  * THE SOFTWARE.
  */
 
+#include <sys/sysctl.h>
+#include <sys/wait.h>
+
 #include <err.h>
 #include <errno.h>
 #include <mixer.h>
@@ -40,7 +43,7 @@ static void printall(struct mixer *, int);
 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 +57,13 @@ main(int argc, char *argv[])
 {
        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 +86,9 @@ main(int argc, char *argv[])
                case 's':
                        sflag = 1;
                        break;
+               case 'V':
+                       vctl = optarg;
+                       break;
                case 'h': /* FALLTHROUGH */
                case '?':
                default:
@@ -119,7 +125,7 @@ main(int argc, char *argv[])
        initctls(m);
 
        if (dflag) {
-               if (set_dunit(m, dunit) < 0)
+               if (set_dunit(m, dunit, vctl) < 0)
                        goto parse;
                else {
                        /*
@@ -209,7 +215,8 @@ next:
 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,9 +329,32 @@ printrecsrc(struct mixer *m, int oflag)
 }
 
 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];
+       size_t size;
+       int n, rc;
+
+       /*
+        * Issue warning in case of hw.snd.basename_clone being unset. Omit the
+        * check and warning if the -V flag is used, since the user is most
+        * likely to be aware of this, and the warning might be confusing.
+        */
+       if (vctl == NULL) {
+               size = sizeof(int);
+               if (sysctlbyname("hw.snd.basename_clone", &n, &size,
+                   NULL, 0) < 0) {
+                       warn("hw.snd.basename_clone failed");
+                       return (-1);
+               }
+               if (n == 0) {
+                       warnx("warning: hw.snd.basename_clone not set. "
+                           "/dev/dsp is managed externally and does not "
+                           "change with the default unit change here.");
+               }
+       }
 
        if ((n = mixer_get_dunit()) < 0) {
                warn("cannot get default unit");
@@ -336,6 +366,43 @@ set_dunit(struct mixer *m, int dunit)
        }
        printf("default_unit: %d -> %d\n", n, dunit);
 
+       /* 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;
+               }
+       }
+
        return (0);
 }
 

Reply via email to