Stop binding audio devices exposed by sndiod to physical devices
authorratchov <ratchov@openbsd.org>
Mon, 1 Nov 2021 14:43:24 +0000 (14:43 +0000)
committerratchov <ratchov@openbsd.org>
Mon, 1 Nov 2021 14:43:24 +0000 (14:43 +0000)
This a shift towards a new model: clients connect to logical devices
(created with -s option) then the server routes data to/from the
underlying physical device (registered with -f option). The binding
may be changed at run-time with the server.device control exposed by
sndioctl(1).

As audio devices exposed by sndiod(8) are not bound to fixed physical
devices anymore, the physical audio device number component of
sndio(7) descriptors was removed.

fixes, help from and ok denis, edd

21 files changed:
lib/libsndio/amsg.h
lib/libsndio/aucat.c
lib/libsndio/sio.c
lib/libsndio/sioctl.c
lib/libsndio/sndio.7
usr.bin/sndiod/dev.c
usr.bin/sndiod/dev.h
usr.bin/sndiod/fdpass.c
usr.bin/sndiod/fdpass.h
usr.bin/sndiod/midi.c
usr.bin/sndiod/midi.h
usr.bin/sndiod/miofile.c
usr.bin/sndiod/miofile.h
usr.bin/sndiod/opt.c
usr.bin/sndiod/opt.h
usr.bin/sndiod/siofile.c
usr.bin/sndiod/siofile.h
usr.bin/sndiod/sndiod.c
usr.bin/sndiod/sock.c
usr.bin/sndiod/utils.c
usr.bin/sndiod/utils.h

index abea908..6dfb695 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: amsg.h,v 1.13 2020/02/26 13:53:58 ratchov Exp $       */
+/*     $OpenBSD: amsg.h,v 1.14 2021/11/01 14:43:24 ratchov Exp $       */
 /*
  * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org>
  *
@@ -106,6 +106,7 @@ struct amsg {
                        uint16_t mode;          /* bitmap of MODE_XXX */
 #define AMSG_VERSION   7
                        uint8_t version;        /* protocol version */
+#define AMSG_NODEV     255
                        uint8_t devnum;         /* device number */
                        uint32_t id;            /* client id */
 #define AMSG_OPTMAX    12
index 4b86ed3..6d7a154 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: aucat.c,v 1.77 2019/07/12 06:30:55 ratchov Exp $      */
+/*     $OpenBSD: aucat.c,v 1.78 2021/11/01 14:43:24 ratchov Exp $      */
 /*
  * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org>
  *
@@ -442,15 +442,18 @@ _aucat_open(struct aucat *hdl, const char *str, unsigned int mode)
                DPRINTF("%s: '/' expected\n", str);
                return 0;
        }
-       p = _sndio_parsenum(++p, &devnum, 15);
-       if (p == NULL)
-               return 0;
-       if (*p == '.') {
-               p = parsestr(++p, opt, AMSG_OPTMAX);
+       p++;
+       if (type == 0) {
+               devnum = AMSG_NODEV;
+               p = parsestr(p, opt, AMSG_OPTMAX);
                if (p == NULL)
                        return 0;
-       } else
-               strlcpy(opt, "default", AMSG_OPTMAX);
+       } else {
+               p = _sndio_parsenum(p, &devnum, 15);
+               if (p == NULL)
+                       return 0;
+               memset(opt, 0, sizeof(opt));
+       }
        if (*p != '\0') {
                DPRINTF("%s: junk at end of dev name\n", p);
                return 0;
index b649d4b..430f884 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: sio.c,v 1.25 2020/11/19 08:14:19 ratchov Exp $        */
+/*     $OpenBSD: sio.c,v 1.26 2021/11/01 14:43:24 ratchov Exp $        */
 /*
  * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org>
  *
@@ -62,7 +62,7 @@ sio_open(const char *str, unsigned int mode, int nbio)
                        str = devany;
        }
        if (strcmp(str, devany) == 0) {
-               hdl = _sio_aucat_open("snd/0", mode, nbio);
+               hdl = _sio_aucat_open("snd/default", mode, nbio);
                if (hdl != NULL)
                        return hdl;
                return _sio_sun_open("rsnd/0", mode, nbio);
index 73bc5b5..2b2dc95 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: sioctl.c,v 1.1 2020/02/26 13:53:58 ratchov Exp $      */
+/*     $OpenBSD: sioctl.c,v 1.2 2021/11/01 14:43:24 ratchov Exp $      */
 /*
  * Copyright (c) 2014-2020 Alexandre Ratchov <alex@caoua.org>
  *
@@ -40,7 +40,7 @@ sioctl_open(const char *str, unsigned int mode, int nbio)
                        str = devany;
        }
        if (strcmp(str, devany) == 0) {
-               hdl = _sioctl_aucat_open("snd/0", mode, nbio);
+               hdl = _sioctl_aucat_open("snd/default", mode, nbio);
                if (hdl != NULL)
                        return hdl;
                return _sioctl_sun_open("rsnd/0", mode, nbio);
index 96b5661..1af611f 100644 (file)
@@ -1,4 +1,4 @@
-.\" $OpenBSD: sndio.7,v 1.26 2020/11/20 12:13:11 schwarze Exp $
+.\" $OpenBSD: sndio.7,v 1.27 2021/11/01 14:43:24 ratchov Exp $
 .\"
 .\" Copyright (c) 2007 Alexandre Ratchov <alex@caoua.org>
 .\"
@@ -14,7 +14,7 @@
 .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 .\"
-.Dd $Mdocdate: November 20 2020 $
+.Dd $Mdocdate: November 1 2021 $
 .Dt SNDIO 7
 .Os
 .Sh NAME
@@ -67,7 +67,7 @@ From the user's perspective, every audio device or MIDI port exposed by
 .Xr sndiod 8
 has a descriptor of the form:
 .Bd -literal -offset center
-type[@hostname][,servnum]/devnum[.option]
+type[@hostname][,servnum]/[devnum|option]
 .Ed
 .Pp
 This information is used by programs to determine
@@ -147,7 +147,7 @@ and/or
 .Ev AUDIORECDEVICE
 environment variables.
 If they are not set, the program first tries to connect to
-.Li snd/0 .
+.Li snd/default .
 If that fails, it then tries to use
 .Li rsnd/0 .
 .Pp
@@ -215,13 +215,13 @@ Raw audio devices.
 Raw MIDI ports.
 .El
 .Sh EXAMPLES
-.Bl -tag -width "snd/0.rear" -compact
+.Bl -tag -width "midithru/0" -compact
 .It Li snd/0
 Audio device referred to by the first
 .Fl f
 option of
 .Xr sndiod 8 .
-.It Li snd/0.rear
+.It Li snd/rear
 Sub-device registered with
 .Dq -s rear .
 .It Li midithru/0
index b68b604..b63af22 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: dev.c,v 1.102 2021/05/03 04:29:50 ratchov Exp $       */
+/*     $OpenBSD: dev.c,v 1.103 2021/11/01 14:43:24 ratchov Exp $       */
 /*
  * Copyright (c) 2008-2012 Alexandre Ratchov <alex@caoua.org>
  *
@@ -45,16 +45,13 @@ struct dev *dev_new(char *, struct aparams *, unsigned int, unsigned int,
     unsigned int, unsigned int, unsigned int, unsigned int);
 void dev_adjpar(struct dev *, int, int, int);
 int dev_allocbufs(struct dev *);
-int dev_open(struct dev *);
 void dev_freebufs(struct dev *);
-void dev_close(struct dev *);
 int dev_ref(struct dev *);
 void dev_unref(struct dev *);
 int dev_init(struct dev *);
 void dev_done(struct dev *);
 struct dev *dev_bynum(int);
 void dev_del(struct dev *);
-void dev_setalt(struct dev *, unsigned int);
 unsigned int dev_roundof(struct dev *, unsigned int);
 void dev_wakeup(struct dev *);
 
@@ -930,10 +927,8 @@ dev_new(char *path, struct aparams *par,
                return NULL;
        }
        d = xmalloc(sizeof(struct dev));
-       d->alt_list = NULL;
-       dev_addname(d,path);
+       d->path = path;
        d->num = dev_sndnum++;
-       d->alt_num = -1;
 
        d->reqpar = *par;
        d->reqmode = mode;
@@ -948,6 +943,7 @@ dev_new(char *path, struct aparams *par,
        d->slot_list = NULL;
        d->master = MIDI_MAXCTL;
        d->master_enabled = 0;
+       d->alt_next = d;
        snprintf(d->name, CTL_NAMEMAX, "%u", d->num);
        for (pd = &dev_list; *pd != NULL; pd = &(*pd)->next)
                ;
@@ -956,51 +952,6 @@ dev_new(char *path, struct aparams *par,
        return d;
 }
 
-/*
- * add a alternate name
- */
-int
-dev_addname(struct dev *d, char *name)
-{
-       struct dev_alt *a;
-
-       if (d->alt_list != NULL && d->alt_list->idx == DEV_NMAX - 1) {
-               log_puts(name);
-               log_puts(": too many alternate names\n");
-               return 0;
-       }
-       a = xmalloc(sizeof(struct dev_alt));
-       a->name = name;
-       a->idx = (d->alt_list == NULL) ? 0 : d->alt_list->idx + 1;
-       a->next = d->alt_list;
-       d->alt_list = a;
-       return 1;
-}
-
-/*
- * set prefered alt device name
- */
-void
-dev_setalt(struct dev *d, unsigned int idx)
-{
-       struct dev_alt **pa, *a;
-
-       /* find alt with given index */
-       for (pa = &d->alt_list; (a = *pa)->idx != idx; pa = &a->next)
-               ;
-
-       /* detach from list */
-       *pa = a->next;
-
-       /* attach at head */
-       a->next = d->alt_list;
-       d->alt_list = a;
-
-       /* reopen device with the new alt */
-       if (idx != d->alt_num)
-               dev_reopen(d);
-}
-
 /*
  * adjust device parameters and mode
  */
@@ -1099,9 +1050,6 @@ dev_allocbufs(struct dev *d)
 int
 dev_open(struct dev *d)
 {
-       char name[CTL_NAMEMAX];
-       struct dev_alt *a;
-
        d->mode = d->reqmode;
        d->round = d->reqround;
        d->bufsz = d->reqbufsz;
@@ -1123,16 +1071,6 @@ dev_open(struct dev *d)
        if (!dev_allocbufs(d))
                return 0;
 
-       /* if there are multiple alt devs, add server.device knob */
-       if (d->alt_list->next != NULL) {
-               for (a = d->alt_list; a != NULL; a = a->next) {
-                       snprintf(name, sizeof(name), "%d", a->idx);
-                       ctl_new(CTL_DEV_ALT, d, &a->idx,
-                           CTL_SEL, d->name, "server", -1, "device",
-                           name, -1, 1, a->idx == d->alt_num);
-               }
-       }
-
        d->pstate = DEV_INIT;
        return 1;
 }
@@ -1158,18 +1096,18 @@ dev_abort(struct dev *d)
        }
        d->slot_list = NULL;
 
-       for (c = ctlslot_array, i = 0; i < DEV_NCTLSLOT; i++, c++) {
-               if (c->ops == NULL)
-                       continue;
-               if (c->opt->dev != d)
-                       continue;
-               c->ops->exit(c->arg);
-               c->ops = NULL;
-       }
-
        for (o = opt_list; o != NULL; o = o->next) {
                if (o->dev != d)
                        continue;
+               for (c = ctlslot_array, i = 0; i < DEV_NCTLSLOT; i++, c++) {
+                       if (c->ops == NULL)
+                               continue;
+                       if (c->opt == o) {
+                               c->ops->exit(s->arg);
+                               c->ops = NULL;
+                       }
+               }
+
                midi_abort(o->midi);
        }
 
@@ -1208,9 +1146,6 @@ dev_freebufs(struct dev *d)
 void
 dev_close(struct dev *d)
 {
-       struct dev_alt *a;
-       unsigned int idx;
-
        d->pstate = DEV_CFG;
        dev_sio_close(d);
        dev_freebufs(d);
@@ -1219,82 +1154,6 @@ dev_close(struct dev *d)
                d->master_enabled = 0;
                ctl_del(CTL_DEV_MASTER, d, NULL);
        }
-       for (idx = 0, a = d->alt_list; a != NULL; idx++, a = a->next)
-               ctl_del(CTL_DEV_ALT, d, &idx);
-}
-
-/*
- * Close the device, but attempt to migrate everything to a new sndio
- * device.
- */
-int
-dev_reopen(struct dev *d)
-{
-       struct mtc *mtc;
-       struct slot *s;
-       long long pos;
-       unsigned int pstate;
-       int delta;
-
-       /* not opened */
-       if (d->pstate == DEV_CFG)
-               return 1;
-
-       /* save state */
-       delta = d->delta;
-       pstate = d->pstate;
-
-       if (!dev_sio_reopen(d))
-               return 0;
-
-       /* reopen returns a stopped device */
-       d->pstate = DEV_INIT;
-
-       /* reallocate new buffers, with new parameters */
-       dev_freebufs(d);
-       dev_allocbufs(d);
-
-       /*
-        * adjust time positions, make anything go back delta ticks, so
-        * that the new device can start at zero
-        */
-       for (s = d->slot_list; s != NULL; s = s->next) {
-               pos = (long long)s->delta * d->round + s->delta_rem;
-               pos -= (long long)delta * s->round;
-               s->delta_rem = pos % (int)d->round;
-               s->delta = pos / (int)d->round;
-               if (log_level >= 3) {
-                       slot_log(s);
-                       log_puts(": adjusted: delta -> ");
-                       log_puti(s->delta);
-                       log_puts(", delta_rem -> ");
-                       log_puti(s->delta_rem);
-                       log_puts("\n");
-               }
-
-               /* reinitilize the format conversion chain */
-               slot_initconv(s);
-       }
-       mtc = &mtc_array[0];
-       if (mtc->dev == d && mtc->tstate == MTC_RUN) {
-               mtc->delta -= delta * MTC_SEC;
-               if (log_level >= 2) {
-                       dev_log(mtc->dev);
-                       log_puts(": adjusted mtc: delta ->");
-                       log_puti(mtc->delta);
-                       log_puts("\n");
-               }
-       }
-
-       /* remove old controls and add new ones */
-       dev_sioctl_close(d);
-       dev_sioctl_open(d);
-
-       /* start the device if needed */
-       if (pstate == DEV_RUN)
-               dev_wakeup(d);
-
-       return 1;
 }
 
 int
@@ -1381,7 +1240,6 @@ void
 dev_del(struct dev *d)
 {
        struct dev **p;
-       struct dev_alt *a;
 
 #ifdef DEBUG
        if (log_level >= 3) {
@@ -1401,10 +1259,6 @@ dev_del(struct dev *d)
 #endif
        }
        *p = d->next;
-       while ((a = d->alt_list) != NULL) {
-               d->alt_list = a->next;
-               xfree(a);
-       }
        xfree(d);
 }
 
@@ -1443,6 +1297,103 @@ dev_wakeup(struct dev *d)
        }
 }
 
+/*
+ * Return true if both of the given devices can run the same
+ * clients
+ */
+int
+dev_iscompat(struct dev *o, struct dev *n)
+{
+       if (((long long)o->round * n->rate != (long long)n->round * o->rate) ||
+           ((long long)o->bufsz * n->rate != (long long)n->bufsz * o->rate)) {
+               if (log_level >= 1) {
+                       log_puts(n->name);
+                       log_puts(": not compatible with ");
+                       log_puts(o->name);
+                       log_puts("\n");
+               }
+               return 0;
+       }
+       return 1;
+}
+
+/*
+ * Close the device, but attempt to migrate everything to a new sndio
+ * device.
+ */
+struct dev *
+dev_migrate(struct dev *odev)
+{
+       struct dev *ndev;
+       struct opt *o;
+       struct slot *s;
+       int i;
+
+       /* not opened */
+       if (odev->pstate == DEV_CFG)
+               return odev;
+
+       ndev = odev;
+       while (1) {
+               /* try next one, circulating through the list */
+               ndev = ndev->alt_next;
+               if (ndev == odev) {
+                       if (log_level >= 1) {
+                               dev_log(odev);
+                               log_puts(": no fall-back device found\n");
+                       }
+                       return NULL;
+               }
+
+
+               if (!dev_ref(ndev))
+                       continue;
+
+               /* check if new parameters are compatible with old ones */
+               if (!dev_iscompat(odev, ndev)) {
+                       dev_unref(ndev);
+                       continue;
+               }
+
+               /* found it!*/
+               break;
+       }
+
+       if (log_level >= 1) {
+               dev_log(odev);
+               log_puts(": switching to ");
+               dev_log(ndev);
+               log_puts("\n");
+       }
+
+       if (mtc_array[0].dev == odev)
+               mtc_setdev(&mtc_array[0], ndev);
+
+       /* move opts to new device (also moves clients using the opts) */
+       for (o = opt_list; o != NULL; o = o->next) {
+               if (o->dev != odev)
+                       continue;
+               if (strcmp(o->name, o->dev->name) == 0)
+                       continue;
+               opt_setdev(o, ndev);
+       }
+
+       /* terminate remaining clients */
+       for (i = 0, s = slot_array; i < DEV_NSLOT; i++, s++) {
+               if (s->opt == NULL || s->opt->dev != odev)
+                       continue;
+               if (s->ops != NULL) {
+                       s->ops->exit(s);
+                       s->ops = NULL;
+               }
+       }
+
+       /* slots and/or MMC hold refs, drop ours */
+       dev_unref(ndev);
+
+       return ndev;
+}
+
 /*
  * check that all clients controlled by MMC are ready to start, if so,
  * attach them all at the same position
@@ -1551,6 +1502,43 @@ mtc_loc(struct mtc *mtc, unsigned int origin)
                mtc_start(mtc);
 }
 
+/*
+ * set MMC device
+ */
+void
+mtc_setdev(struct mtc *mtc, struct dev *d)
+{
+       struct opt *o;
+
+       if (mtc->dev == d)
+               return;
+
+       if (log_level >= 2) {
+               dev_log(d);
+               log_puts(": set to be MIDI clock source\n");
+       }
+
+       /* adjust clock and ref counter, if needed */
+       if (mtc->tstate == MTC_RUN) {
+               mtc->delta -= mtc->dev->delta;
+               dev_unref(mtc->dev);
+       }
+
+       mtc->dev = d;
+
+       if (mtc->tstate == MTC_RUN) {
+               mtc->delta += mtc->dev->delta;
+               dev_ref(mtc->dev);
+               dev_wakeup(mtc->dev);
+       }
+
+       /* move in once anything using MMC */
+       for (o = opt_list; o != NULL; o = o->next) {
+               if (o->mtc == mtc)
+                       opt_setdev(o, mtc->dev);
+       }
+}
+
 /*
  * allocate buffers & conversion chain
  */
@@ -1787,7 +1775,8 @@ slot_new(struct opt *opt, unsigned int id, char *who,
            NULL, -1, 127, s->vol);
 
 found:
-       if (!dev_ref(opt->dev))
+       /* open device, this may change opt's device */
+       if (!opt_ref(s->opt))
                return NULL;
        s->opt = opt;
        s->ops = ops;
@@ -1809,8 +1798,6 @@ found:
        if (log_level >= 3) {
                slot_log(s);
                log_puts(": using ");
-               dev_log(s->opt->dev);
-               log_puts(".");
                log_puts(s->opt->name);
                log_puts(", mode = ");
                log_putx(mode);
@@ -1839,7 +1826,7 @@ slot_del(struct slot *s)
                slot_stop(s, 0);
                break;
        }
-       dev_unref(s->opt->dev);
+       opt_unref(s->opt);
 }
 
 /*
@@ -1860,6 +1847,62 @@ slot_setvol(struct slot *s, unsigned int vol)
        s->mix.vol = MIDI_TO_ADATA(s->vol);
 }
 
+/*
+ * set device for this slot
+ */
+void
+slot_setopt(struct slot *s, struct opt *o)
+{
+       struct opt *t;
+       struct dev *odev, *ndev;
+       struct ctl *c;
+
+       if (s->opt == NULL || s->opt == o)
+               return;
+
+       if (log_level >= 2) {
+               slot_log(s);
+               log_puts(": moving to opt ");
+               log_puts(o->name);
+               log_puts("\n");
+       }
+
+       odev = s->opt->dev;
+       if (s->ops != NULL) {
+               ndev = opt_ref(o);
+               if (ndev == NULL)
+                       return;
+
+               if (!dev_iscompat(odev, ndev)) {
+                       opt_unref(o);
+                       return;
+               }
+       }
+
+       if (s->pstate == SLOT_RUN || s->pstate == SLOT_STOP)
+               slot_detach(s);
+
+       t = s->opt;
+       s->opt = o;
+
+       c = ctl_find(CTL_SLOT_LEVEL, s, NULL);
+       ctl_update(c);
+
+       if (o->dev != t->dev) {
+               dev_midi_slotdesc(odev, s);
+               dev_midi_slotdesc(ndev, s);
+               dev_midi_vol(ndev, s);
+       }
+
+       if (s->pstate == SLOT_RUN || s->pstate == SLOT_STOP)
+               slot_attach(s);
+
+       if (s->ops != NULL) {
+               opt_unref(t);
+               return;
+       }
+}
+
 /*
  * attach the slot to the device (ie start playing & recording
  */
@@ -2202,7 +2245,7 @@ ctlslot_new(struct opt *o, struct ctlops *ops, void *arg)
        }
        s->opt = o;
        s->self = 1 << i;
-       if (!dev_ref(o->dev))
+       if (!opt_ref(o))
                return NULL;
        s->ops = ops;
        s->arg = arg;
@@ -2232,7 +2275,7 @@ ctlslot_del(struct ctlslot *s)
                        pc = &c->next;
        }
        s->ops = NULL;
-       dev_unref(s->opt->dev);
+       opt_unref(s->opt);
 }
 
 int
@@ -2243,8 +2286,9 @@ ctlslot_visible(struct ctlslot *s, struct ctl *c)
        switch (c->scope) {
        case CTL_HW:
        case CTL_DEV_MASTER:
-       case CTL_DEV_ALT:
                return (s->opt->dev == c->u.any.arg0);
+       case CTL_OPT_DEV:
+               return (s->opt == c->u.any.arg0);
        case CTL_SLOT_LEVEL:
                return (s->opt->dev == c->u.slot_level.slot->opt->dev);
        default:
@@ -2270,6 +2314,28 @@ ctlslot_lookup(struct ctlslot *s, int addr)
        return c;
 }
 
+void
+ctlslot_update(struct ctlslot *s)
+{
+       struct ctl *c;
+       unsigned int refs_mask;
+
+       for (c = ctl_list; c != NULL; c = c->next) {
+               if (c->type == CTL_NONE)
+                       continue;
+               refs_mask = ctlslot_visible(s, c) ? s->self : 0;
+
+               /* nothing to do if no visibility change */
+               if (((c->refs_mask & s->self) ^ refs_mask) == 0)
+                       continue;
+               /* if control becomes visble */
+               if (refs_mask)
+                       c->refs_mask |= s->self;
+               /* if control is hidden */
+               c->desc_mask |= s->self;
+       }
+}
+
 void
 ctl_node_log(struct ctl_node *c)
 {
@@ -2318,16 +2384,17 @@ ctl_log(struct ctl *c)
                log_puts("dev_master:");
                log_puts(c->u.dev_master.dev->name);
                break;
-       case CTL_DEV_ALT:
-               log_puts("dev_alt:");
-               log_puts(c->u.dev_alt.dev->name);
-               log_putu(c->u.dev_alt.idx);
-               break;
        case CTL_SLOT_LEVEL:
                log_puts("slot_level:");
                log_puts(c->u.slot_level.slot->name);
                log_putu(c->u.slot_level.slot->unit);
                break;
+       case CTL_OPT_DEV:
+               log_puts("opt_dev:");
+               log_puts(c->u.opt_dev.opt->name);
+               log_puts("/");
+               log_puts(c->u.opt_dev.dev->name);
+               break;
        default:
                log_puts("unknown");
        }
@@ -2368,9 +2435,6 @@ ctl_setval(struct ctl *c, int val)
                c->val_mask = ~0U;
                c->curval = val;
                return 1;
-       case CTL_DEV_ALT:
-               dev_setalt (c->u.dev_alt.dev, c->u.dev_alt.idx);
-               return 1;
        case CTL_SLOT_LEVEL:
                slot_setvol(c->u.slot_level.slot, val);
                // XXX change dev_midi_vol() into slot_midi_vol()
@@ -2378,6 +2442,10 @@ ctl_setval(struct ctl *c, int val)
                c->val_mask = ~0U;
                c->curval = val;
                return 1;
+       case CTL_OPT_DEV:
+               c->u.opt_dev.opt->alt_first = c->u.opt_dev.dev;
+               opt_setdev(c->u.opt_dev.opt, c->u.opt_dev.dev);
+               return 1;
        default:
                if (log_level >= 2) {
                        ctl_log(c);
@@ -2429,8 +2497,8 @@ ctl_new(int scope, void *arg0, void *arg1,
        case CTL_HW:
                c->u.hw.addr = *(unsigned int *)arg1;
                break;
-       case CTL_DEV_ALT:
-               c->u.dev_alt.idx = *(unsigned int *)arg1;
+       case CTL_OPT_DEV:
+               c->u.any.arg1 = arg1;
                break;
        default:
                c->u.any.arg1 = NULL;
@@ -2494,8 +2562,8 @@ ctl_match(struct ctl *c, int scope, void *arg0, void *arg1)
                if (arg1 != NULL && c->u.hw.addr != *(unsigned int *)arg1)
                        return 0;
                break;
-       case CTL_DEV_ALT:
-               if (arg1 != NULL && c->u.dev_alt.idx != *(unsigned int *)arg1)
+       case CTL_OPT_DEV:
+               if (arg1 != NULL && c->u.any.arg1 != arg1)
                        return 0;
                break;
        }
@@ -2600,4 +2668,3 @@ dev_ctlsync(struct dev *d)
                        s->ops->sync(s->arg);
        }
 }
-
index 1e778e5..45ae71f 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: dev.h,v 1.40 2021/03/03 10:19:06 ratchov Exp $        */
+/*     $OpenBSD: dev.h,v 1.41 2021/11/01 14:43:25 ratchov Exp $        */
 /*
  * Copyright (c) 2008-2012 Alexandre Ratchov <alex@caoua.org>
  *
@@ -126,7 +126,7 @@ struct ctl {
 
 #define CTL_HW         0
 #define CTL_DEV_MASTER 1
-#define CTL_DEV_ALT    2
+#define CTL_OPT_DEV    2
 #define CTL_SLOT_LEVEL 3
        unsigned int scope;
        union {
@@ -141,13 +141,17 @@ struct ctl {
                struct {
                        struct dev *dev;
                } dev_master;
-               struct {
-                       struct dev *dev;
-                       unsigned int idx;
-               } dev_alt;
                struct {
                        struct slot *slot;
                } slot_level;
+               struct {
+                       struct slot *slot;
+                       struct opt *opt;
+               } slot_opt;
+               struct {
+                       struct opt *opt;
+                       struct dev *dev;
+               } opt_dev;
        } u;
 
        unsigned int addr;              /* slot side control address */
@@ -216,6 +220,11 @@ struct dev {
         */
        char name[CTL_NAMEMAX];
 
+       /*
+        * next to try if this fails
+        */
+       struct dev *alt_next;
+
        /*
         * audio device (while opened)
         */
@@ -256,12 +265,7 @@ struct dev {
 #define DEV_INIT       1                       /* stopped */
 #define DEV_RUN                2                       /* playin & recording */
        unsigned int pstate;                    /* one of above */
-       struct dev_alt {
-               struct dev_alt *next;
-               char *name;
-               unsigned int idx;
-       } *alt_list;
-       int alt_num;
+       char *path;
 
        /*
         * actual parameters and runtime state (i.e. once opened)
@@ -283,12 +287,13 @@ extern struct mtc mtc_array[1];
 void slot_array_init(void);
 
 void dev_log(struct dev *);
+int dev_open(struct dev *);
+void dev_close(struct dev *);
 void dev_abort(struct dev *);
-int dev_reopen(struct dev *);
+struct dev *dev_migrate(struct dev *);
 struct dev *dev_new(char *, struct aparams *, unsigned int, unsigned int,
     unsigned int, unsigned int, unsigned int, unsigned int);
 struct dev *dev_bynum(int);
-int dev_addname(struct dev *, char *);
 void dev_del(struct dev *);
 void dev_adjpar(struct dev *, int, int, int);
 int  dev_init(struct dev *);
@@ -297,6 +302,7 @@ int dev_ref(struct dev *);
 void dev_unref(struct dev *);
 int  dev_getpos(struct dev *);
 unsigned int dev_roundof(struct dev *, unsigned int);
+int dev_iscompat(struct dev *, struct dev *);
 
 /*
  * interface to hardware device
@@ -330,6 +336,7 @@ struct slot *slot_new(struct opt *, unsigned int, char *,
     struct slotops *, void *, int);
 void slot_del(struct slot *);
 void slot_setvol(struct slot *, unsigned int);
+void slot_setopt(struct slot *, struct opt *);
 void slot_start(struct slot *);
 void slot_stop(struct slot *, int);
 void slot_read(struct slot *);
@@ -356,6 +363,8 @@ struct ctlslot *ctlslot_new(struct opt *, struct ctlops *, void *);
 void ctlslot_del(struct ctlslot *);
 int ctlslot_visible(struct ctlslot *, struct ctl *);
 struct ctl *ctlslot_lookup(struct ctlslot *, int);
+void ctlslot_update(struct ctlslot *);
+
 void dev_label(struct dev *, int);
 void dev_ctlsync(struct dev *);
 
index 9f97f35..d5f7617 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: fdpass.c,v 1.10 2020/06/18 05:11:13 ratchov Exp $     */
+/*     $OpenBSD: fdpass.c,v 1.11 2021/11/01 14:43:25 ratchov Exp $     */
 /*
  * Copyright (c) 2015 Alexandre Ratchov <alex@caoua.org>
  *
@@ -36,7 +36,6 @@ struct fdpass_msg {
 #define FDPASS_RETURN          3       /* return after above commands */
        unsigned int cmd;               /* one of above */
        unsigned int num;               /* audio device or midi port number */
-       unsigned int idx;               /* index in the path list */
        unsigned int mode;              /* SIO_PLAY, SIO_REC, MIO_IN, ... */
 };
 
@@ -77,7 +76,7 @@ fdpass_log(struct fdpass *f)
 }
 
 static int
-fdpass_send(struct fdpass *f, int cmd, int num, int idx, int mode, int fd)
+fdpass_send(struct fdpass *f, int cmd, int num, int mode, int fd)
 {
        struct fdpass_msg data;
        struct msghdr msg;
@@ -91,7 +90,6 @@ fdpass_send(struct fdpass *f, int cmd, int num, int idx, int mode, int fd)
 
        data.cmd = cmd;
        data.num = num;
-       data.idx = idx;
        data.mode = mode;
        iov.iov_base = &data;
        iov.iov_len = sizeof(struct fdpass_msg);
@@ -131,8 +129,6 @@ fdpass_send(struct fdpass *f, int cmd, int num, int idx, int mode, int fd)
                log_puti(cmd);
                log_puts(", num = ");
                log_puti(num);
-               log_puts(", idx = ");
-               log_puti(idx);
                log_puts(", mode = ");
                log_puti(mode);
                log_puts(", fd = ");
@@ -146,7 +142,7 @@ fdpass_send(struct fdpass *f, int cmd, int num, int idx, int mode, int fd)
 }
 
 static int
-fdpass_recv(struct fdpass *f, int *cmd, int *num, int *idx, int *mode, int *fd)
+fdpass_recv(struct fdpass *f, int *cmd, int *num, int *mode, int *fd)
 {
        struct fdpass_msg data;
        struct msghdr msg;
@@ -217,7 +213,6 @@ fdpass_recv(struct fdpass *f, int *cmd, int *num, int *idx, int *mode, int *fd)
        }
        *cmd = data.cmd;
        *num = data.num;
-       *idx = data.idx;
        *mode = data.mode;
 #ifdef DEBUG
        if (log_level >= 3) {
@@ -226,8 +221,6 @@ fdpass_recv(struct fdpass *f, int *cmd, int *num, int *idx, int *mode, int *fd)
                log_puti(*cmd);
                log_puts(", num = ");
                log_puti(*num);
-               log_puts(", idx = ");
-               log_puti(*idx);
                log_puts(", mode = ");
                log_puti(*mode);
                log_puts(", fd = ");
@@ -243,7 +236,7 @@ fdpass_waitret(struct fdpass *f, int *retfd)
 {
        int cmd, unused;
 
-       if (!fdpass_recv(fdpass_peer, &cmd, &unused, &unused, &unused, retfd))
+       if (!fdpass_recv(fdpass_peer, &cmd, &unused, &unused, retfd))
                return 0;
        if (cmd != FDPASS_RETURN) {
                if (log_level >= 1) {
@@ -257,13 +250,13 @@ fdpass_waitret(struct fdpass *f, int *retfd)
 }
 
 struct sio_hdl *
-fdpass_sio_open(int num, int idx, unsigned int mode)
+fdpass_sio_open(int num, unsigned int mode)
 {
        int fd;
 
        if (fdpass_peer == NULL)
                return NULL;
-       if (!fdpass_send(fdpass_peer, FDPASS_OPEN_SND, num, idx, mode, -1))
+       if (!fdpass_send(fdpass_peer, FDPASS_OPEN_SND, num, mode, -1))
                return NULL;
        if (!fdpass_waitret(fdpass_peer, &fd))
                return NULL;
@@ -273,13 +266,13 @@ fdpass_sio_open(int num, int idx, unsigned int mode)
 }
 
 struct mio_hdl *
-fdpass_mio_open(int num, int idx, unsigned int mode)
+fdpass_mio_open(int num, unsigned int mode)
 {
        int fd;
 
        if (fdpass_peer == NULL)
                return NULL;
-       if (!fdpass_send(fdpass_peer, FDPASS_OPEN_MIDI, num, idx, mode, -1))
+       if (!fdpass_send(fdpass_peer, FDPASS_OPEN_MIDI, num, mode, -1))
                return NULL;
        if (!fdpass_waitret(fdpass_peer, &fd))
                return NULL;
@@ -289,13 +282,13 @@ fdpass_mio_open(int num, int idx, unsigned int mode)
 }
 
 struct sioctl_hdl *
-fdpass_sioctl_open(int num, int idx, unsigned int mode)
+fdpass_sioctl_open(int num, unsigned int mode)
 {
        int fd;
 
        if (fdpass_peer == NULL)
                return NULL;
-       if (!fdpass_send(fdpass_peer, FDPASS_OPEN_CTL, num, idx, mode, -1))
+       if (!fdpass_send(fdpass_peer, FDPASS_OPEN_CTL, num, mode, -1))
                return NULL;
        if (!fdpass_waitret(fdpass_peer, &fd))
                return NULL;
@@ -320,14 +313,12 @@ fdpass_in_worker(void *arg)
 void
 fdpass_in_helper(void *arg)
 {
-       int cmd, num, idx, mode, fd;
+       int cmd, num, mode, fd;
        struct fdpass *f = arg;
        struct dev *d;
-       struct dev_alt *da;
        struct port *p;
-       char *path;
 
-       if (!fdpass_recv(f, &cmd, &num, &idx, &mode, &fd))
+       if (!fdpass_recv(f, &cmd, &num, &mode, &fd))
                return;
        switch (cmd) {
        case FDPASS_OPEN_SND:
@@ -340,15 +331,7 @@ fdpass_in_helper(void *arg)
                        fdpass_close(f);
                        return;
                }
-               for (da = d->alt_list; ; da = da->next) {
-                       if (da == NULL) {
-                               fdpass_close(f);
-                               return;
-                       }
-                       if (da->idx == idx)
-                               break;
-               }
-               fd = sio_sun_getfd(da->name, mode, 1);
+               fd = sio_sun_getfd(d->path, mode, 1);
                break;
        case FDPASS_OPEN_MIDI:
                p = port_bynum(num);
@@ -360,12 +343,7 @@ fdpass_in_helper(void *arg)
                        fdpass_close(f);
                        return;
                }
-               path = namelist_byindex(&p->path_list, idx);
-               if (path == NULL) {
-                       fdpass_close(f);
-                       return;
-               }
-               fd = mio_rmidi_getfd(path, mode, 1);
+               fd = mio_rmidi_getfd(p->path, mode, 1);
                break;
        case FDPASS_OPEN_CTL:
                d = dev_bynum(num);
@@ -377,21 +355,13 @@ fdpass_in_helper(void *arg)
                        fdpass_close(f);
                        return;
                }
-               for (da = d->alt_list; ; da = da->next) {
-                       if (da == NULL) {
-                               fdpass_close(f);
-                               return;
-                       }
-                       if (da->idx == idx)
-                               break;
-               }
-               fd = sioctl_sun_getfd(da->name, mode, 1);
+               fd = sioctl_sun_getfd(d->path, mode, 1);
                break;
        default:
                fdpass_close(f);
                return;
        }
-       fdpass_send(f, FDPASS_RETURN, 0, 0, 0, fd);
+       fdpass_send(f, FDPASS_RETURN, 0, 0, fd);
 }
 
 void
index e11926e..fec1a23 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: fdpass.h,v 1.3 2020/02/26 13:53:58 ratchov Exp $      */
+/*     $OpenBSD: fdpass.h,v 1.4 2021/11/01 14:43:25 ratchov Exp $      */
 /*
  * Copyright (c) 2015 Alexandre Ratchov <alex@caoua.org>
  *
@@ -25,8 +25,8 @@ void fdpass_close(struct fdpass *f);
 extern struct fileops worker_fileops, helper_fileops;
 extern struct fdpass *fdpass_peer;
 
-struct sio_hdl *fdpass_sio_open(int, int, unsigned int);
-struct mio_hdl *fdpass_mio_open(int, int, unsigned int);
-struct sioctl_hdl *fdpass_sioctl_open(int, int, unsigned int);
+struct sio_hdl *fdpass_sio_open(int, unsigned int);
+struct mio_hdl *fdpass_mio_open(int, unsigned int);
+struct sioctl_hdl *fdpass_sioctl_open(int, unsigned int);
 
 #endif /* !defined(FDPASS_H) */
index fea9441..371a830 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: midi.c,v 1.28 2021/03/08 09:42:50 ratchov Exp $       */
+/*     $OpenBSD: midi.c,v 1.29 2021/11/01 14:43:25 ratchov Exp $       */
 /*
  * Copyright (c) 2008-2012 Alexandre Ratchov <alex@caoua.org>
  *
@@ -433,6 +433,45 @@ midi_abort(struct midi *p)
        }
 }
 
+/*
+ * connect to "nep" all endpoints currently connected to "oep"
+ */
+void
+midi_migrate(struct midi *oep, struct midi *nep)
+{
+       struct midithru *t;
+       struct midi *ep;
+       int i;
+
+       for (i = 0; i < MIDITHRU_NMAX; i++) {
+               t = midithru + i;
+               if (t->txmask & oep->self) {
+                       t->txmask &= ~oep->self;
+                       t->txmask |= nep->self;
+               }
+               if (t->rxmask & oep->self) {
+                       t->rxmask &= ~oep->self;
+                       t->rxmask |= nep->self;
+               }
+       }
+
+       for (i = 0; i < MIDI_NEP; i++) {
+               ep = midi_ep + i;
+               if (ep->txmask & oep->self) {
+                       ep->txmask &= ~oep->self;
+                       ep->txmask |= nep->self;
+               }
+       }
+
+       for (i = 0; i < MIDI_NEP; i++) {
+               ep = midi_ep + i;
+               if (oep->txmask & ep->self) {
+                       oep->txmask &= ~ep->self;
+                       nep->txmask |= ep->self;
+               }
+       }
+}
+
 void
 port_log(struct port *p)
 {
@@ -482,19 +521,17 @@ port_exit(void *arg)
 struct port *
 port_new(char *path, unsigned int mode, int hold)
 {
-       struct port *c, **pc;
+       struct port *c;
 
        c = xmalloc(sizeof(struct port));
-       c->path_list = NULL;
-       namelist_add(&c->path_list, path);
+       c->path = path;
        c->state = PORT_CFG;
        c->hold = hold;
        c->midi = midi_new(&port_midiops, c, mode);
        c->num = midi_portnum++;
-       for (pc = &port_list; *pc != NULL; pc = &(*pc)->next)
-               ;
-       c->next = *pc;
-       *pc = c;
+       c->alt_next = c;
+       c->next = port_list;
+       port_list = c;
        return c;
 }
 
@@ -518,7 +555,6 @@ port_del(struct port *c)
 #endif
        }
        *p = c->next;
-       namelist_clear(&c->path_list);
        xfree(c);
 }
 
@@ -554,6 +590,67 @@ port_unref(struct port *c)
                port_drain(c);
 }
 
+struct port *
+port_alt_ref(int num)
+{
+       struct port *a, *p;
+
+       a = port_bynum(num);
+       if (a == NULL)
+               return NULL;
+
+       /* circulate to first alt port */
+       while (a->alt_next->num > a->num)
+               a = a->alt_next;
+
+       p = a;
+       while (1) {
+               if (port_ref(p))
+                       break;
+               p = p->alt_next;
+               if (p == a)
+                       return NULL;
+       }
+
+       return p;
+}
+
+struct port *
+port_migrate(struct port *op)
+{
+       struct port *np;
+
+       /* not opened */
+       if (op->state == PORT_CFG)
+               return op;
+
+       np = op;
+       while (1) {
+               /* try next one, circulating through the list */
+               np = np->alt_next;
+               if (np == op) {
+                       if (log_level >= 2) {
+                               port_log(op);
+                               log_puts(": no fall-back port found\n");
+                       }
+                       return op;
+               }
+
+               if (port_ref(np))
+                       break;
+       }
+
+       if (log_level >= 2) {
+               port_log(op);
+               log_puts(": switching to ");
+               port_log(np);
+               log_puts("\n");
+       }
+
+       midi_migrate(op->midi, np->midi);
+       return np;
+}
+
 struct port *
 port_bynum(int num)
 {
@@ -590,6 +687,8 @@ port_close(struct port *c)
                panic();
        }
 #endif
+       port_log(c);
+       log_puts(": closed\n");
        c->state = PORT_CFG;
        port_mio_close(c);
        return 1;
@@ -627,15 +726,3 @@ port_done(struct port *c)
        if (c->state == PORT_INIT)
                port_drain(c);
 }
-
-int
-port_reopen(struct port *p)
-{
-       if (p->state == PORT_CFG)
-               return 1;
-
-       if (!port_mio_reopen(p))
-               return 0;
-
-       return 1;
-}
index c8b7d1e..cd398de 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: midi.h,v 1.14 2021/01/28 11:17:58 ratchov Exp $       */
+/*     $OpenBSD: midi.h,v 1.15 2021/11/01 14:43:25 ratchov Exp $       */
 /*
  * Copyright (c) 2008-2012 Alexandre Ratchov <alex@caoua.org>
  *
@@ -89,7 +89,8 @@ struct port {
 #define PORT_DRAIN     2
        unsigned int state;
        unsigned int num;               /* port serial number */
-       struct name *path_list;
+       char *path;
+       struct port *alt_next;
        int hold;                       /* hold the port open ? */
        struct midi *midi;
 };
@@ -113,6 +114,7 @@ void midi_tag(struct midi *, unsigned int);
 unsigned int midi_tags(struct midi *);
 void midi_link(struct midi *, struct midi *);
 void midi_abort(struct midi *);
+void midi_migrate(struct midi *, struct midi *);
 
 void port_log(struct port *);
 struct port *port_new(char *, unsigned int, int);
@@ -124,6 +126,7 @@ int  port_init(struct port *);
 void port_done(struct port *);
 void port_drain(struct port *);
 int  port_close(struct port *);
-int  port_reopen(struct port *);
+struct port *port_alt_ref(int);
+struct port *port_migrate(struct port *);
 
 #endif /* !defined(MIDI_H) */
index dbfacc4..f981874 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: miofile.c,v 1.8 2021/01/28 11:17:58 ratchov Exp $     */
+/*     $OpenBSD: miofile.c,v 1.9 2021/11/01 14:43:25 ratchov Exp $     */
 /*
  * Copyright (c) 2008-2012 Alexandre Ratchov <alex@caoua.org>
  *
@@ -45,74 +45,16 @@ struct fileops port_mio_ops = {
        port_mio_hup
 };
 
-/*
- * open the port using one of the provided paths
- */
-static struct mio_hdl *
-port_mio_openlist(struct port *c, unsigned int mode)
-{
-       struct mio_hdl *hdl;
-       struct name *n;
-       int idx;
-
-       idx = 0;
-       n = c->path_list;
-       while (1) {
-               if (n == NULL)
-                       break;
-               hdl = fdpass_mio_open(c->num, idx, mode);
-               if (hdl != NULL) {
-                       if (log_level >= 2) {
-                               port_log(c);
-                               log_puts(": using ");
-                               log_puts(n->str);
-                               log_puts("\n");
-                       }
-                       return hdl;
-               }
-               n = n->next;
-               idx++;
-       }
-       return NULL;
-}
-
 int
 port_mio_open(struct port *p)
 {
-       p->mio.hdl = port_mio_openlist(p, p->midi->mode);
+       p->mio.hdl = fdpass_mio_open(p->num, p->midi->mode);
        if (p->mio.hdl == NULL)
                return 0;
        p->mio.file = file_new(&port_mio_ops, p, "port", mio_nfds(p->mio.hdl));
        return 1;
 }
 
-/*
- * Open an alternate port. Upon success, close the old port
- * and continue using the new one.
- */
-int
-port_mio_reopen(struct port *p)
-{
-       struct mio_hdl *hdl;
-
-       hdl = port_mio_openlist(p, p->midi->mode);
-       if (hdl == NULL) {
-               if (log_level >= 1) {
-                       port_log(p);
-                       log_puts(": couldn't open an alternate port\n");
-               }
-               return 0;
-       }
-
-       /* close unused device */
-       file_del(p->mio.file);
-       mio_close(p->mio.hdl);
-
-       p->mio.hdl = hdl;
-       p->mio.file = file_new(&port_mio_ops, p, "port", mio_nfds(hdl));
-       return 1;
-}
-
 void
 port_mio_close(struct port *p)
 {
@@ -187,9 +129,8 @@ port_mio_hup(void *arg)
 {
        struct port *p = arg;
 
-       if (!port_reopen(p)) {
-               midi_abort(p->midi);
-               if (p->state != PORT_CFG)
-                       port_close(p);
-       }
+       port_migrate(p);
+       midi_abort(p->midi);
+       if (p->state != PORT_CFG)
+               port_close(p);
 }
index 703caa0..fd307dc 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: miofile.h,v 1.2 2019/09/21 04:42:46 ratchov Exp $     */
+/*     $OpenBSD: miofile.h,v 1.3 2021/11/01 14:43:25 ratchov Exp $     */
 /*
  * Copyright (c) 2008-2012 Alexandre Ratchov <alex@caoua.org>
  *
@@ -25,7 +25,6 @@ struct port_mio {
 };
 
 int  port_mio_open(struct port *);
-int  port_mio_reopen(struct port *);
 void port_mio_close(struct port *);
 
 #endif /* !defined(MIOFILE_H) */
index 99621d7..0dfd8ba 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: opt.c,v 1.8 2021/03/03 10:19:06 ratchov Exp $ */
+/*     $OpenBSD: opt.c,v 1.9 2021/11/01 14:43:25 ratchov Exp $ */
 /*
  * Copyright (c) 2008-2011 Alexandre Ratchov <alex@caoua.org>
  *
@@ -59,8 +59,7 @@ opt_midi_omsg(void *arg, unsigned char *msg, int len)
                chan = msg[0] & MIDI_CHANMASK;
                if (chan >= DEV_NSLOT)
                        return;
-               if (slot_array[chan].opt == NULL ||
-                   slot_array[chan].opt->dev != o->dev)
+               if (slot_array[chan].opt != o)
                        return;
                slot_setvol(slot_array + chan, msg[2]);
                ctl_onval(CTL_SLOT_LEVEL, slot_array + chan, NULL, msg[2]);
@@ -91,6 +90,7 @@ opt_midi_omsg(void *arg, unsigned char *msg, int len)
                                return;
                        if (o->mtc == NULL)
                                return;
+                       mtc_setdev(o->mtc, o->dev);
                        if (log_level >= 2) {
                                log_puts(o->name);
                                log_puts(": mmc stop\n");
@@ -102,6 +102,7 @@ opt_midi_omsg(void *arg, unsigned char *msg, int len)
                                return;
                        if (o->mtc == NULL)
                                return;
+                       mtc_setdev(o->mtc, o->dev);
                        if (log_level >= 2) {
                                log_puts(o->name);
                                log_puts(": mmc start\n");
@@ -115,6 +116,7 @@ opt_midi_omsg(void *arg, unsigned char *msg, int len)
                                return;
                        if (o->mtc == NULL)
                                return;
+                       mtc_setdev(o->mtc, o->dev);
                        switch (x->u.loc.hr >> 5) {
                        case MTC_FPS_24:
                                fps = 24;
@@ -173,22 +175,28 @@ opt_new(struct dev *d, char *name,
     int pmin, int pmax, int rmin, int rmax,
     int maxweight, int mmc, int dup, unsigned int mode)
 {
+       struct dev *a;
        struct opt *o, **po;
        unsigned int len, num;
        char c;
 
-       for (len = 0; name[len] != '\0'; len++) {
-               if (len == OPT_NAMEMAX) {
-                       log_puts(name);
-                       log_puts(": too long\n");
-                       return NULL;
-               }
-               c = name[len];
-               if ((c < 'a' || c > 'z') &&
-                   (c < 'A' || c > 'Z')) {
-                       log_puts(name);
-                       log_puts(": only alphabetic chars allowed\n");
-                       return NULL;
+       if (name == NULL) {
+               name = d->name;
+               len = strlen(name);
+       } else {
+               for (len = 0; name[len] != '\0'; len++) {
+                       if (len == OPT_NAMEMAX) {
+                               log_puts(name);
+                               log_puts(": too long\n");
+                               return NULL;
+                       }
+                       c = name[len];
+                       if ((c < 'a' || c > 'z') &&
+                           (c < 'A' || c > 'Z')) {
+                               log_puts(name);
+                               log_puts(": only alphabetic chars allowed\n");
+                               return NULL;
+                       }
                }
        }
        num = 0;
@@ -199,9 +207,8 @@ opt_new(struct dev *d, char *name,
                log_puts(": too many opts\n");
                return NULL;
        }
-       if (opt_byname(d, name)) {
-               dev_log(d);
-               log_puts(".");
+
+       if (opt_byname(name)) {
                log_puts(name);
                log_puts(": already defined\n");
                return NULL;
@@ -220,9 +227,18 @@ opt_new(struct dev *d, char *name,
                }
        }
 
+       if (strcmp(d->name, name) == 0)
+               a = d;
+       else {
+               /* circulate to the first "alternate" device (greatest num) */
+               for (a = d; a->alt_next->num > a->num; a = a->alt_next)
+                       ;
+       }
+
        o = xmalloc(sizeof(struct opt));
        o->num = num;
-       o->dev = d;
+       o->alt_first = o->dev = a;
+       o->refcnt = 0;
 
        /*
         * XXX: below, we allocate a midi input buffer, since we don't
@@ -286,19 +302,29 @@ opt_new(struct dev *d, char *name,
 }
 
 struct opt *
-opt_byname(struct dev *d, char *name)
+opt_byname(char *name)
 {
        struct opt *o;
 
        for (o = opt_list; o != NULL; o = o->next) {
-               if (d != NULL && o->dev != d)
-                       continue;
                if (strcmp(name, o->name) == 0)
                        return o;
        }
        return NULL;
 }
 
+struct opt *
+opt_bynum(int num)
+{
+       struct opt *o;
+
+       for (o = opt_list; o != NULL; o = o->next) {
+               if (o->num == num)
+                       return o;
+       }
+       return NULL;
+}
+
 void
 opt_del(struct opt *o)
 {
@@ -316,3 +342,174 @@ opt_del(struct opt *o)
        *po = o->next;
        xfree(o);
 }
+
+void
+opt_init(struct opt *o)
+{
+       struct dev *d;
+
+       if (strcmp(o->name, o->dev->name) != 0) {
+               for (d = dev_list; d != NULL; d = d->next) {
+                       ctl_new(CTL_OPT_DEV, o, d,
+                           CTL_SEL, o->name, "server", -1, "device",
+                           d->name, -1, 1, o->dev == d);
+               }
+       }
+}
+
+void
+opt_done(struct opt *o)
+{
+       struct dev *d;
+
+       if (o->refcnt != 0) {
+               // XXX: all clients are already kicked, so this never happens
+               log_puts(o->name);
+               log_puts(": still has refs\n");
+       }
+       for (d = dev_list; d != NULL; d = d->next)
+               ctl_del(CTL_OPT_DEV, o, d);
+}
+
+/*
+ * Set opt's device, and (if necessary) move clients to
+ * to the new device
+ */
+void
+opt_setdev(struct opt *o, struct dev *ndev)
+{
+       struct dev *odev;
+       struct ctl *c;
+       struct ctlslot *p;
+       struct slot *s;
+       int i;
+
+       if (!dev_ref(ndev))
+               return;
+
+       odev = o->dev;
+       if (odev == ndev) {
+               dev_unref(ndev);
+               return;
+       }
+
+       /* check if clients can use new device */
+       for (i = 0, s = slot_array; i < DEV_NSLOT; i++, s++) {
+               if (s->opt != o)
+                       continue;
+               if (s->ops != NULL && !dev_iscompat(odev, ndev)) {
+                       dev_unref(ndev);
+                       return;
+               }
+       }
+
+       /*
+        * if we're using MMC, move all opts to the new device, mtc_setdev()
+        * will call us back
+        */
+       if (o->mtc != NULL && o->mtc->dev != ndev) {
+               mtc_setdev(o->mtc, ndev);
+               dev_unref(ndev);
+               return;
+       }
+
+       c = ctl_find(CTL_OPT_DEV, o, o->dev);
+       if (c != NULL)
+               c->curval = 0;
+
+       /* detach clients from old device */
+       for (i = 0, s = slot_array; i < DEV_NSLOT; i++, s++) {
+               if (s->opt != o)
+                       continue;
+
+               if (s->pstate == SLOT_RUN || s->pstate == SLOT_STOP)
+                       slot_detach(s);
+       }
+
+       o->dev = ndev;
+
+       if (o->refcnt > 0) {
+               dev_unref(odev);
+               dev_ref(o->dev);
+       }
+
+       c = ctl_find(CTL_OPT_DEV, o, o->dev);
+       if (c != NULL) {
+               c->curval = 1;
+               c->val_mask = ~0;
+       }
+
+       /* attach clients to new device */
+       for (i = 0, s = slot_array; i < DEV_NSLOT; i++, s++) {
+               if (s->opt != o)
+                       continue;
+
+               if (ndev != odev) {
+                       dev_midi_slotdesc(odev, s);
+                       dev_midi_slotdesc(ndev, s);
+                       dev_midi_vol(ndev, s);
+               }
+
+               c = ctl_find(CTL_SLOT_LEVEL, s, NULL);
+               ctl_update(c);
+
+               if (s->pstate == SLOT_RUN || s->pstate == SLOT_STOP) {
+                       slot_initconv(s);
+                       slot_attach(s);
+               }
+       }
+
+       /* move controlling clients to new device */
+       for (p = ctlslot_array, i = 0; i < DEV_NCTLSLOT; i++, p++) {
+               if (p->ops == NULL)
+                       continue;
+               if (p->opt == o)
+                       ctlslot_update(p);
+       }
+
+       dev_unref(ndev);
+}
+
+/*
+ * Get a reference to opt's device
+ */
+struct dev *
+opt_ref(struct opt *o)
+{
+       struct dev *d;
+
+       if (o->refcnt == 0) {
+               if (strcmp(o->name, o->dev->name) == 0) {
+                       if (!dev_ref(o->dev))
+                               return NULL;
+               } else {
+                       /* find first working one */
+                       d = o->alt_first;
+                       while (1) {
+                               if (dev_ref(d))
+                                       break;
+                               d = d->alt_next;
+                               if (d == o->alt_first)
+                                       return NULL;
+                       }
+
+                       /* if device changed, move everything to the new one */
+                       if (d != o->dev)
+                               opt_setdev(o, d);
+               }
+       }
+
+       o->refcnt++;
+       return o->dev;
+}
+
+/*
+ * Release opt's device
+ */
+void
+opt_unref(struct opt *o)
+{
+       o->refcnt--;
+       if (o->refcnt == 0)
+               dev_unref(o->dev);
+}
index bb56f44..7dd9e2f 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: opt.h,v 1.6 2021/03/03 10:19:06 ratchov Exp $ */
+/*     $OpenBSD: opt.h,v 1.7 2021/11/01 14:43:25 ratchov Exp $ */
 /*
  * Copyright (c) 2008-2012 Alexandre Ratchov <alex@caoua.org>
  *
@@ -23,7 +23,7 @@ struct dev;
 
 struct opt {
        struct opt *next;
-       struct dev *dev;
+       struct dev *dev, *alt_first;
        struct midi *midi;
        struct mtc *mtc;        /* if set, MMC-controlled MTC source */
 
@@ -35,6 +35,7 @@ struct opt {
        int rmin, rmax;         /* recording channels */
        int dup;                /* true if join/expand enabled */
        int mode;               /* bitmap of MODE_XXX */
+       int refcnt;
 };
 
 extern struct opt *opt_list;
@@ -42,6 +43,12 @@ extern struct opt *opt_list;
 struct opt *opt_new(struct dev *, char *, int, int, int, int,
     int, int, int, unsigned int);
 void opt_del(struct opt *);
-struct opt *opt_byname(struct dev *, char *);
+struct opt *opt_byname(char *);
+struct opt *opt_bynum(int);
+void opt_init(struct opt *);
+void opt_done(struct opt *);
+void opt_setdev(struct opt *, struct dev *);
+struct dev *opt_ref(struct opt *);
+void opt_unref(struct opt *);
 
 #endif /* !defined(OPT_H) */
index 3cc9394..48bf862 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: siofile.c,v 1.24 2021/03/03 10:00:27 ratchov Exp $    */
+/*     $OpenBSD: siofile.c,v 1.25 2021/11/01 14:43:25 ratchov Exp $    */
 /*
  * Copyright (c) 2008-2012 Alexandre Ratchov <alex@caoua.org>
  *
@@ -87,24 +87,25 @@ dev_sio_timeout(void *arg)
        dev_abort(d);
 }
 
-static int
-dev_sio_openalt(struct dev *d, struct dev_alt *n,
-    struct sio_hdl **rhdl, struct sioctl_hdl **rctlhdl, unsigned int *rmode)
+/*
+ * open the device.
+ */
+int
+dev_sio_open(struct dev *d)
 {
-       struct sio_hdl *hdl;
-       struct sioctl_hdl *ctlhdl;
-       unsigned int mode = d->reqmode & (MODE_PLAY | MODE_REC);
+       struct sio_par par;
+       unsigned int rate, mode = d->reqmode & (SIO_PLAY | SIO_REC);
 
-       hdl = fdpass_sio_open(d->num, n->idx, mode);
-       if (hdl == NULL) {
+       d->sio.hdl = fdpass_sio_open(d->num, mode);
+       if (d->sio.hdl == NULL) {
                if (mode != (SIO_PLAY | SIO_REC))
                        return 0;
-               hdl = fdpass_sio_open(d->num, n->idx, SIO_PLAY);
-               if (hdl != NULL)
+               d->sio.hdl = fdpass_sio_open(d->num, SIO_PLAY);
+               if (d->sio.hdl != NULL)
                        mode = SIO_PLAY;
                else {
-                       hdl = fdpass_sio_open(d->num, n->idx, SIO_REC);
-                       if (hdl != NULL)
+                       d->sio.hdl = fdpass_sio_open(d->num, SIO_REC);
+                       if (d->sio.hdl != NULL)
                                mode = SIO_REC;
                        else
                                return 0;
@@ -115,76 +116,16 @@ dev_sio_openalt(struct dev *d, struct dev_alt *n,
                        log_puts(" mode\n");
                }
        }
+       d->mode = mode;
 
-       ctlhdl = fdpass_sioctl_open(d->num, n->idx, SIOCTL_READ | SIOCTL_WRITE);
-       if (ctlhdl == NULL) {
+       d->sioctl.hdl = fdpass_sioctl_open(d->num, SIOCTL_READ | SIOCTL_WRITE);
+       if (d->sioctl.hdl == NULL) {
                if (log_level >= 1) {
                        dev_log(d);
                        log_puts(": no control device\n");
                }
        }
 
-       *rhdl = hdl;
-       *rctlhdl = ctlhdl;
-       *rmode = mode;
-       return 1;
-}
-
-/*
- * open the device using one of the provided paths
- */
-static int
-dev_sio_openlist(struct dev *d,
-    struct sio_hdl **rhdl, struct sioctl_hdl **rctlhdl, unsigned int *rmode)
-{
-       struct dev_alt *n;
-       struct ctl *c;
-       int val;
-
-       for (n = d->alt_list; n != NULL; n = n->next) {
-               if (d->alt_num == n->idx)
-                       continue;
-               if (log_level >= 2) {
-                       dev_log(d);
-                       log_puts(": trying ");
-                       log_puts(n->name);
-                       log_puts("\n");
-               }
-               if (dev_sio_openalt(d, n, rhdl, rctlhdl, rmode)) {
-                       if (log_level >= 2) {
-                               dev_log(d);
-                               log_puts(": using ");
-                               log_puts(n->name);
-                               log_puts("\n");
-                       }
-                       d->alt_num = n->idx;
-                       for (c = ctl_list; c != NULL; c = c->next) {
-                               if (!ctl_match(c, CTL_DEV_ALT, d, NULL))
-                                       continue;
-                               val = c->u.dev_alt.idx == n->idx;
-                               if (c->curval == val)
-                                       continue;
-                               c->curval = val;
-                               if (val)
-                                       c->val_mask = ~0U;
-                       }
-                       return 1;
-               }
-       }
-       return 0;
-}
-
-/*
- * open the device.
- */
-int
-dev_sio_open(struct dev *d)
-{
-       struct sio_par par;
-
-       if (!dev_sio_openlist(d, &d->sio.hdl, &d->sioctl.hdl, &d->mode))
-               return 0;
-
        sio_initpar(&par);
        par.bits = d->par.bits;
        par.bps = d->par.bps;
@@ -195,17 +136,40 @@ dev_sio_open(struct dev *d)
                par.pchan = d->pchan;
        if (d->mode & SIO_REC)
                par.rchan = d->rchan;
-       if (d->bufsz)
-               par.appbufsz = d->bufsz;
-       if (d->round)
-               par.round = d->round;
-       if (d->rate)
-               par.rate = d->rate;
+       par.appbufsz = d->bufsz;
+       par.round = d->round;
+       par.rate = d->rate;
        if (!sio_setpar(d->sio.hdl, &par))
                goto bad_close;
        if (!sio_getpar(d->sio.hdl, &par))
                goto bad_close;
 
+       /*
+        * If the requested rate is not supported by the device,
+        * use the new one, but retry using a block size that would
+        * match the requested one
+        */
+       rate = par.rate;
+       if (rate != d->rate) {
+               sio_initpar(&par);
+               par.bits = d->par.bits;
+               par.bps = d->par.bps;
+               par.sig = d->par.sig;
+               par.le = d->par.le;
+               par.msb = d->par.msb;
+               if (mode & SIO_PLAY)
+                       par.pchan = d->reqpchan;
+               if (mode & SIO_REC)
+                       par.rchan = d->reqrchan;
+               par.appbufsz = d->bufsz * rate / d->rate;
+               par.round = d->round * rate / d->rate;
+               par.rate = rate;
+               if (!sio_setpar(d->sio.hdl, &par))
+                       goto bad_close;
+               if (!sio_getpar(d->sio.hdl, &par))
+                       goto bad_close;
+       }
+
 #ifdef DEBUG
        /*
         * We support any parameter combination exposed by the kernel,
@@ -266,7 +230,6 @@ dev_sio_open(struct dev *d)
                goto bad_close;
        }
 #endif
-
        d->par.bits = par.bits;
        d->par.bps = par.bps;
        d->par.sig = par.sig;
@@ -299,88 +262,6 @@ dev_sio_open(struct dev *d)
        return 0;
 }
 
-/*
- * Open an alternate device. Upon success and if the new device is
- * compatible with the old one, close the old device and continue
- * using the new one. The new device is not started.
- */
-int
-dev_sio_reopen(struct dev *d)
-{
-       struct sio_par par;
-       struct sio_hdl *hdl;
-       struct sioctl_hdl *ctlhdl;
-       unsigned int mode;
-
-       if (!dev_sio_openlist(d, &hdl, &ctlhdl, &mode))
-               return 0;
-
-       sio_initpar(&par);
-       par.bits = d->par.bits;
-       par.bps = d->par.bps;
-       par.sig = d->par.sig;
-       par.le = d->par.le;
-       par.msb = d->par.msb;
-       if (mode & SIO_PLAY)
-               par.pchan = d->reqpchan;
-       if (mode & SIO_REC)
-               par.rchan = d->reqrchan;
-       par.appbufsz = d->bufsz;
-       par.round = d->round;
-       par.rate = d->rate;
-       if (!sio_setpar(hdl, &par))
-               goto bad_close;
-       if (!sio_getpar(hdl, &par))
-               goto bad_close;
-
-       /* check if new parameters are compatible with old ones */
-       if (par.round != d->round || par.bufsz != d->bufsz ||
-           par.rate != d->rate) {
-               if (log_level >= 1) {
-                       dev_log(d);
-                       log_puts(": alternate device not compatible\n");
-               }
-               goto bad_close;
-       }
-
-       /* close unused device */
-       timo_del(&d->sio.watchdog);
-       file_del(d->sio.file);
-       sio_close(d->sio.hdl);
-       if (d->sioctl.hdl) {
-               file_del(d->sioctl.file);
-               sioctl_close(d->sioctl.hdl);
-               d->sioctl.hdl = NULL;
-       }
-
-       /* update parameters */
-       d->mode = mode;
-       d->par.bits = par.bits;
-       d->par.bps = par.bps;
-       d->par.sig = par.sig;
-       d->par.le = par.le;
-       d->par.msb = par.msb;
-       if (d->mode & SIO_PLAY)
-               d->pchan = par.pchan;
-       if (d->mode & SIO_REC)
-               d->rchan = par.rchan;
-
-       d->sio.hdl = hdl;
-       d->sioctl.hdl = ctlhdl;
-       d->sio.file = file_new(&dev_sio_ops, d, "dev", sio_nfds(hdl));
-       if (d->sioctl.hdl) {
-               d->sioctl.file = file_new(&dev_sioctl_ops, d, "mix",
-                   sioctl_nfds(ctlhdl));
-       }
-       sio_onmove(hdl, dev_sio_onmove, d);
-       return 1;
-bad_close:
-       sio_close(hdl);
-       if (ctlhdl)
-               sioctl_close(ctlhdl);
-       return 0;
-}
-
 void
 dev_sio_close(struct dev *d)
 {
@@ -399,7 +280,6 @@ dev_sio_close(struct dev *d)
                sioctl_close(d->sioctl.hdl);
                d->sioctl.hdl = NULL;
        }
-       d->alt_num = -1;
 }
 
 void
@@ -662,6 +542,6 @@ dev_sio_hup(void *arg)
                log_puts(": disconnected\n");
        }
 #endif
-       if (!dev_reopen(d))
-               dev_abort(d);
+       dev_migrate(d);
+       dev_abort(d);
 }
index 4cebf87..c34f922 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: siofile.h,v 1.4 2019/09/21 04:42:46 ratchov Exp $     */
+/*     $OpenBSD: siofile.h,v 1.5 2021/11/01 14:43:25 ratchov Exp $     */
 /*
  * Copyright (c) 2008-2012 Alexandre Ratchov <alex@caoua.org>
  *
@@ -38,7 +38,6 @@ struct dev_sio {
 };
 
 int dev_sio_open(struct dev *);
-int dev_sio_reopen(struct dev *);
 void dev_sio_close(struct dev *);
 void dev_sio_log(struct dev *);
 void dev_sio_start(struct dev *);
index ad6a2cc..96088e3 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: sndiod.c,v 1.46 2021/07/12 15:09:20 beck Exp $        */
+/*     $OpenBSD: sndiod.c,v 1.47 2021/11/01 14:43:25 ratchov Exp $     */
 /*
  * Copyright (c) 2008-2012 Alexandre Ratchov <alex@caoua.org>
  *
@@ -318,8 +318,7 @@ mkdev(char *path, struct aparams *par,
        struct dev *d;
 
        for (d = dev_list; d != NULL; d = d->next) {
-               if (d->alt_list->next == NULL &&
-                   strcmp(d->alt_list->name, path) == 0)
+               if (strcmp(d->path, path) == 0)
                        return d;
        }
        if (!bufsz && !round) {
@@ -341,8 +340,7 @@ mkport(char *path, int hold)
        struct port *c;
 
        for (c = port_list; c != NULL; c = c->next) {
-               if (c->path_list->next == NULL &&
-                   strcmp(c->path_list->str, path) == 0)
+               if (strcmp(c->path, path) == 0)
                        return c;
        }
        c = port_new(path, MODE_MIDIMASK, hold);
@@ -385,10 +383,8 @@ static int
 start_helper(int background)
 {
        struct dev *d;
-       struct dev_alt *da;
        struct port *p;
        struct passwd *pw;
-       struct name *n;
        int s[2];
        pid_t pid;
 
@@ -424,14 +420,11 @@ start_helper(int background)
                                err(1, "cannot drop privileges");
                }
                for (d = dev_list; d != NULL; d = d->next) {
-                       for (da = d->alt_list; da != NULL; da = da->next) {
-                               dounveil(da->name, "rsnd/", "/dev/audio");
-                               dounveil(da->name, "rsnd/", "/dev/audioctl");
-                       }
+                       dounveil(d->path, "rsnd/", "/dev/audio");
+                       dounveil(d->path, "rsnd/", "/dev/audioctl");
                }
                for (p = port_list; p != NULL; p = p->next) {
-                       for (n = p->path_list; n != NULL; n = n->next)
-                               dounveil(n->str, "rmidi/", "/dev/rmidi");
+                       dounveil(p->path, "rmidi/", "/dev/rmidi");
                }
                if (pledge("stdio sendfd rpath wpath", NULL) == -1)
                        err(1, "pledge");
@@ -461,10 +454,12 @@ main(int argc, char **argv)
        char base[SOCKPATH_MAX], path[SOCKPATH_MAX];
        unsigned int mode, dup, mmc, vol;
        unsigned int hold, autovol, bufsz, round, rate;
+       unsigned int reopen_list;
        const char *str;
        struct aparams par;
-       struct dev *d;
-       struct port *p;
+       struct opt *o;
+       struct dev *d, *dev_first, *dev_next;
+       struct port *p, *port_first, *port_next;
        struct listen *l;
        struct passwd *pw;
        struct tcpaddr {
@@ -493,6 +488,8 @@ main(int argc, char **argv)
        rmax = 1;
        aparams_init(&par);
        mode = MODE_PLAY | MODE_REC;
+       dev_first = dev_next = NULL;
+       port_first = port_next = NULL;
        tcpaddr_list = NULL;
        d = NULL;
        p = NULL;
@@ -559,11 +556,17 @@ main(int argc, char **argv)
                        break;
                case 'q':
                        p = mkport(optarg, hold);
+                       /* create new circulate list */
+                       port_first = port_next = p;
                        break;
                case 'Q':
                        if (p == NULL)
                                errx(1, "-Q %s: no ports defined", optarg);
-                       namelist_add(&p->path_list, optarg);
+                       p = mkport(optarg, hold);
+                       /* add to circulate list */
+                       p->alt_next = port_next;
+                       port_first->alt_next = p;
+                       port_next = p;
                        break;
                case 'a':
                        hold = opt_onoff();
@@ -584,12 +587,18 @@ main(int argc, char **argv)
                case 'f':
                        d = mkdev(optarg, &par, 0, bufsz, round,
                            rate, hold, autovol);
+                       /* create new circulate list */
+                       dev_first = dev_next = d;
                        break;
                case 'F':
                        if (d == NULL)
                                errx(1, "-F %s: no devices defined", optarg);
-                       if (!dev_addname(d, optarg))
-                               exit(1);
+                       d = mkdev(optarg, &par, 0, bufsz, round,
+                           rate, hold, autovol);
+                       /* add to circulate list */
+                       d->alt_next = dev_next;
+                       dev_first->alt_next = d;
+                       dev_next = d;
                        break;
                default:
                        fputs(usagestr, stderr);
@@ -612,12 +621,27 @@ main(int argc, char **argv)
                            bufsz, round, rate, 0, autovol);
                }
        }
+
+       /*
+        * Add default sub-device (if none) backed by the last device
+        */
+       o = opt_byname("default");
+       if (o == NULL) {
+               o = mkopt("default", dev_list, pmin, pmax, rmin, rmax,
+                   mode, vol, 0, dup);
+               if (o == NULL)
+                       return 1;
+       }
+
+       /*
+        * For each device create an anonymous sub-device using
+        * the "default" sub-device as template
+        */
        for (d = dev_list; d != NULL; d = d->next) {
-               if (opt_byname(d, "default"))
-                       continue;
-               if (mkopt("default", d, pmin, pmax, rmin, rmax,
-                       mode, vol, mmc, dup) == NULL)
+               if (opt_new(d, NULL, o->pmin, o->pmax, o->rmin, o->rmax,
+                       o->maxweight, o->mtc != NULL, o->dup, o->mode) == NULL)
                        return 1;
+               dev_adjpar(d, o->mode, o->pmax, o->rmax);
        }
 
        setsig();
@@ -652,6 +676,8 @@ main(int argc, char **argv)
                if (!dev_init(d))
                        return 1;
        }
+       for (o = opt_list; o != NULL; o = o->next)
+               opt_init(o);
        if (background) {
                log_flush();
                log_level = 0;
@@ -680,10 +706,28 @@ main(int argc, char **argv)
                        break;
                if (reopen_flag) {
                        reopen_flag = 0;
-                       for (d = dev_list; d != NULL; d = d->next)
-                               dev_reopen(d);
-                       for (p = port_list; p != NULL; p = p->next)
-                               port_reopen(p);
+
+                       reopen_list = 0;
+                       for (d = dev_list; d != NULL; d = d->next) {
+                               if (d->pstate != DEV_CFG)
+                                       reopen_list |= (1 << d->num);
+                       }
+                       for (d = dev_list; d != NULL; d = d->next) {
+                               if (reopen_list & (1 << d->num))
+                                       dev_migrate(d);
+                       }
+
+                       reopen_list = 0;
+                       for (p = port_list; p != NULL; p = p->next) {
+                               if (p->state != PORT_CFG)
+                                       reopen_list |= (1 << p->num);
+                       }
+                       for (p = port_list; p != NULL; p = p->next) {
+                               if (reopen_list & (1 << p->num)) {
+                                       if (port_migrate(p) != p)
+                                               port_close(p);
+                               }
+                       }
                }
                if (!fdpass_peer)
                        break;
@@ -695,6 +739,8 @@ main(int argc, char **argv)
                listen_close(listen_list);
        while (sock_list != NULL)
                sock_close(sock_list);
+       for (o = opt_list; o != NULL; o = o->next)
+               opt_done(o);
        for (d = dev_list; d != NULL; d = d->next)
                dev_done(d);
        for (p = port_list; p != NULL; p = p->next)
index c29ea58..fcf97cf 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: sock.c,v 1.44 2021/03/03 10:19:06 ratchov Exp $       */
+/*     $OpenBSD: sock.c,v 1.45 2021/11/01 14:43:25 ratchov Exp $       */
 /*
  * Copyright (c) 2008-2012 Alexandre Ratchov <alex@caoua.org>
  *
@@ -101,6 +101,48 @@ struct sock *sock_list = NULL;
 unsigned int sock_sesrefs = 0;         /* connections to the session */
 uint8_t sock_sescookie[AMSG_COOKIELEN];        /* owner of the session */
 
+/*
+ * Old clients used to send dev number and opt name. This routine
+ * finds proper opt pointer for the given device.
+ */
+static struct opt *
+legacy_opt(int devnum, char *optname)
+{
+       struct dev *d;
+       struct opt *o;
+
+       d = dev_bynum(devnum);
+       if (d == NULL)
+               return NULL;
+       if (strcmp(optname, "default") == 0) {
+               for (o = opt_list; o != NULL; o = o->next) {
+                       if (strcmp(o->name, d->name) == 0)
+                               return o;
+               }
+               return NULL;
+       } else {
+               o = opt_byname(optname);
+               return (o != NULL && o->dev == d) ? o : NULL;
+       }
+}
+
+/*
+ * If control slot is associated to a particular opt, then
+ * remove the unused group part of the control name to make mixer
+ * look nicer
+ */
+static char *
+ctlgroup(struct sock *f, struct ctl *c)
+{
+       if (f->ctlslot->opt == NULL)
+               return c->group;
+       if (strcmp(c->group, f->ctlslot->opt->name) == 0)
+               return "";
+       if (strcmp(c->group, f->ctlslot->opt->dev->name) == 0)
+               return "";
+       return c->group;
+}
+
 void
 sock_log(struct sock *f)
 {
@@ -130,7 +172,7 @@ sock_log(struct sock *f)
 void
 sock_close(struct sock *f)
 {
-       struct dev *d;
+       struct opt *o;
        struct sock **pf;
        unsigned int tags, i;
 
@@ -159,8 +201,8 @@ sock_close(struct sock *f)
        if (f->midi) {
                tags = midi_tags(f->midi);
                for (i = 0; i < DEV_NMAX; i++) {
-                       if ((tags & (1 << i)) && (d = dev_bynum(i)) != NULL)
-                               dev_unref(d);
+                       if ((tags & (1 << i)) && (o = opt_bynum(i)) != NULL)
+                               opt_unref(o);
                }
                midi_del(f->midi);
                f->midi = NULL;
@@ -818,7 +860,6 @@ sock_hello(struct sock *f)
 {
        struct amsg_hello *p = &f->rmsg.u.hello;
        struct port *c;
-       struct dev *d;
        struct opt *opt;
        unsigned int mode;
        unsigned int id;
@@ -876,21 +917,25 @@ sock_hello(struct sock *f)
                if (f->midi == NULL)
                        return 0;
                /* XXX: add 'devtype' to libsndio */
-               if (p->devnum < 16) {
-                       d = dev_bynum(p->devnum);
-                       if (d == NULL)
+               if (p->devnum == AMSG_NODEV) {
+                       opt = opt_byname(p->opt);
+                       if (opt == NULL)
+                               return 0;
+                       if (!opt_ref(opt))
                                return 0;
-                       opt = opt_byname(d, p->opt);
+                       midi_tag(f->midi, opt->num);
+               } else if (p->devnum < 16) {
+                       opt = legacy_opt(p->devnum, p->opt);
                        if (opt == NULL)
                                return 0;
-                       if (!dev_ref(d))
+                       if (!opt_ref(opt))
                                return 0;
                        midi_tag(f->midi, opt->num);
                } else if (p->devnum < 32) {
                        midi_tag(f->midi, p->devnum);
                } else if (p->devnum < 48) {
-                       c = port_bynum(p->devnum - 32);
-                       if (c == NULL || !port_ref(c))
+                       c = port_alt_ref(p->devnum - 32);
+                       if (c == NULL)
                                return 0;
                        f->port = c;
                        midi_link(f->midi, c->midi);
@@ -899,19 +944,15 @@ sock_hello(struct sock *f)
                return 1;
        }
        if (mode & MODE_CTLMASK) {
-               d = dev_bynum(p->devnum);
-               if (d == NULL) {
-                       if (log_level >= 2) {
-                               sock_log(f);
-                               log_puts(": ");
-                               log_putu(p->devnum);
-                               log_puts(": no such device\n");
-                       }
-                       return 0;
+               if (p->devnum == AMSG_NODEV) {
+                       opt = opt_byname(p->opt);
+                       if (opt == NULL)
+                               return 0;
+               } else {
+                       opt = legacy_opt(p->devnum, p->opt);
+                       if (opt == NULL)
+                               return 0;
                }
-               opt = opt_byname(d, p->opt);
-               if (opt == NULL)
-                       return 0;
                f->ctlslot = ctlslot_new(opt, &sock_ctlops, f);
                if (f->ctlslot == NULL) {
                        if (log_level >= 2) {
@@ -926,10 +967,8 @@ sock_hello(struct sock *f)
                f->ctlsyncpending = 0;
                return 1;
        }
-       d = dev_bynum(p->devnum);
-       if (d == NULL)
-               return 0;
-       opt = opt_byname(d, p->opt);
+       opt = (p->devnum == AMSG_NODEV) ?
+           opt_byname(p->opt) : legacy_opt(p->devnum, p->opt);
        if (opt == NULL)
                return 0;
        f->slot = slot_new(opt, id, p->who, &sock_slotops, f, mode);
@@ -1582,10 +1621,7 @@ sock_buildmsg(struct sock *f)
                        c->val_mask &= ~mask;
                        type = ctlslot_visible(f->ctlslot, c) ?
                            c->type : CTL_NONE;
-                       strlcpy(desc->group, (f->ctlslot->opt == NULL ||
-                           strcmp(c->group, f->ctlslot->opt->dev->name) != 0) ?
-                           c->group : "",
-                           AMSG_CTL_NAMEMAX);
+                       strlcpy(desc->group, ctlgroup(f, c), AMSG_CTL_NAMEMAX);
                        strlcpy(desc->node0.name, c->node0.name,
                            AMSG_CTL_NAMEMAX);
                        desc->node0.unit = ntohs(c->node0.unit);
index 118f236..228c570 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: utils.c,v 1.7 2020/01/23 05:40:09 ratchov Exp $       */
+/*     $OpenBSD: utils.c,v 1.8 2021/11/01 14:43:25 ratchov Exp $       */
 /*
  * Copyright (c) 2003-2012 Alexandre Ratchov <alex@caoua.org>
  *
@@ -188,47 +188,3 @@ xstrdup(char *s)
        memcpy(p, s, size);
        return p;
 }
-
-/*
- * copy and append the given string to the name list
- */
-void
-namelist_add(struct name **list, char *str)
-{
-       struct name *n;
-       size_t size;
-
-       size = strlen(str) + 1;
-       n = xmalloc(sizeof(struct name) + size);
-       memcpy(n->str, str, size);
-       n->next = *list;
-       *list = n;
-}
-
-void
-namelist_clear(struct name **list)
-{
-       struct name *n;
-
-       while ((n = *list) != NULL) {
-               *list = n->next;
-               xfree(n);
-       }
-}
-
-char *
-namelist_byindex(struct name **list, unsigned int idx)
-{
-       struct name *n;
-
-       n = *list;
-       while (1) {
-               if (n == NULL)
-                       return NULL;
-               if (idx == 0)
-                       break;
-               n = n->next;
-               idx--;
-       }
-       return n->str;
-}
index ef589f6..a3ba936 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: utils.h,v 1.5 2020/01/23 05:40:09 ratchov Exp $       */
+/*     $OpenBSD: utils.h,v 1.6 2021/11/01 14:43:25 ratchov Exp $       */
 /*
  * Copyright (c) 2003-2012 Alexandre Ratchov <alex@caoua.org>
  *
@@ -36,10 +36,6 @@ void *xmalloc(size_t);
 char *xstrdup(char *);
 void xfree(void *);
 
-void namelist_add(struct name **, char *);
-void namelist_clear(struct name **);
-char *namelist_byindex(struct name **, unsigned int);
-
 /*
  * Log levels:
  *