Download raw body.
sndiod: control of midithru ports with sndioctl.
This diff allows sndioctl to control midithru/N ports.
For now there's a single "server.port" control that specifies which
hardware MIDI ports programs see. It's the equivalent of what's the
"server.device" control is for audio. This can be used to expose
multiple hardware ports as a single "midithru/N" port, ex., allowing a
program to see all the hardware without using hairy midicat(1)-based
plumbing that needs to be restarted whenever a new device is
plugged. Ex.:
sndioctl -f midithru/0 server.port=0,2,3
With a simple hotplugd(8) one-liner, MIDI devices show in programs as
soon as they are connected. Ex.:
case $DEVNAME in
midi[0-9])
sndioctl -f midithru/0 server.port=+${DEVNAME#midi}
;;
esac
The diff also simplifies sndiod internals, as it makes MIDI and audio
handling very similar: less logic, less code, less bugs.
OK?
Index: lib/libsndio/sioctl.c
===================================================================
RCS file: /cvs/src/lib/libsndio/sioctl.c,v
diff -u -p -r1.2 sioctl.c
--- lib/libsndio/sioctl.c 1 Nov 2021 14:43:24 -0000 1.2
+++ lib/libsndio/sioctl.c 11 Jun 2026 06:58:58 -0000
@@ -45,7 +45,7 @@ sioctl_open(const char *str, unsigned in
return hdl;
return _sioctl_sun_open("rsnd/0", mode, nbio);
}
- if (_sndio_parsetype(str, "snd"))
+ if (_sndio_parsetype(str, "snd") || _sndio_parsetype(str, "midithru"))
return _sioctl_aucat_open(str, mode, nbio);
if (_sndio_parsetype(str, "rsnd"))
return _sioctl_sun_open(str, mode, nbio);
Index: usr.bin/sndiod/dev.c
===================================================================
RCS file: /cvs/src/usr.bin/sndiod/dev.c,v
diff -u -p -r1.136 dev.c
--- usr.bin/sndiod/dev.c 11 Jun 2026 06:56:44 -0000 1.136
+++ usr.bin/sndiod/dev.c 11 Jun 2026 06:58:59 -0000
@@ -1726,7 +1726,7 @@ slot_read(struct slot *s)
* allocate at control slot
*/
struct ctlslot *
-ctlslot_new(struct opt *o, struct ctlops *ops, void *arg)
+ctlslot_new(struct opt *o, unsigned int tag, struct ctlops *ops, void *arg)
{
struct ctlslot *s;
struct ctl *c;
@@ -1742,9 +1742,12 @@ ctlslot_new(struct opt *o, struct ctlops
i++;
}
s->opt = o;
+ s->tag = tag;
s->self = 1 << i;
if (s->opt != NULL && !opt_ref(s->opt))
return NULL;
+ if (s->tag != 0)
+ midithru_ref(tag);
s->ops = ops;
s->arg = arg;
for (c = ctl_list; c != NULL; c = c->next) {
@@ -1775,6 +1778,8 @@ ctlslot_del(struct ctlslot *s)
s->ops = NULL;
if (s->opt != NULL)
opt_unref(s->opt);
+ if (s->tag)
+ midithru_unref(s->tag);
}
int
@@ -1797,6 +1802,8 @@ ctlslot_visible(struct ctlslot *s, struc
return (s->opt != NULL && s->opt == c->u.any.arg0);
case CTL_APP_LEVEL:
return (s->opt != NULL && s->opt == c->u.app_level.opt);
+ case CTL_MIDI_PORT:
+ return (s->tag == c->u.midi.tag);
default:
return 0;
}
@@ -1877,6 +1884,9 @@ ctl_scope_fmt(char *buf, size_t size, st
case CTL_OPT_MODE:
return snprintf(buf, size, "opt_mode:%s/%s",
c->u.opt_mode.opt->name, opt_modes[c->u.opt_mode.idx].name);
+ case CTL_MIDI_PORT:
+ return snprintf(buf, size, "midi_port:%u/%u",
+ c->u.midi.tag, c->u.midi.port->num);
default:
return snprintf(buf, size, "unknown");
}
@@ -1950,6 +1960,12 @@ ctl_setval(struct ctl *c, int val)
c->val_mask = ~0U;
c->curval = val;
return 1;
+ case CTL_MIDI_PORT:
+ if (midithru_setport(c->u.midi.tag, c->u.midi.port, val)) {
+ c->val_mask = ~0U;
+ c->curval = val;
+ }
+ return 1;
default:
logx(2, "ctl%u: not writable", c->addr);
return 1;
@@ -2009,6 +2025,9 @@ ctl_new(int scope, void *arg0, void *arg
case CTL_OPT_MODE:
c->u.opt_mode.idx = *(int *)arg1;
break;
+ case CTL_MIDI_PORT:
+ c->u.midi.tag = *(unsigned int *)arg1;
+ break;
default:
c->u.any.arg1 = NULL;
}
@@ -2078,6 +2097,10 @@ ctl_match(struct ctl *c, int scope, void
break;
case CTL_OPT_MODE:
if (arg1 != NULL && c->u.opt_mode.idx != *(int *)arg1)
+ return 0;
+ break;
+ case CTL_MIDI_PORT:
+ if (arg1 != NULL && c->u.midi.tag != *(unsigned int *)arg1)
return 0;
break;
}
Index: usr.bin/sndiod/dev.h
===================================================================
RCS file: /cvs/src/usr.bin/sndiod/dev.h,v
diff -u -p -r1.55 dev.h
--- usr.bin/sndiod/dev.h 26 May 2026 14:50:52 -0000 1.55
+++ usr.bin/sndiod/dev.h 11 Jun 2026 06:58:59 -0000
@@ -125,6 +125,7 @@ struct ctl {
#define CTL_OPT_DEV 2
#define CTL_APP_LEVEL 3
#define CTL_OPT_MODE 4
+#define CTL_MIDI_PORT 5
unsigned int scope;
union {
struct {
@@ -150,6 +151,10 @@ struct ctl {
struct opt *opt;
int idx;
} opt_mode;
+ struct {
+ struct port *port;
+ unsigned int tag;
+ } midi;
} u;
unsigned int addr; /* slot side control address */
@@ -176,6 +181,7 @@ struct ctlslot {
struct ctlops *ops;
void *arg;
struct opt *opt;
+ unsigned int tag; /* midithru index */
unsigned int self; /* equal to (1 << index) */
unsigned int mode;
};
@@ -345,7 +351,7 @@ struct ctl *ctl_find(int, void *, void *
void ctl_update(struct ctl *);
int ctl_onval(int, void *, void *, int);
-struct ctlslot *ctlslot_new(struct opt *, struct ctlops *, void *);
+struct ctlslot *ctlslot_new(struct opt *, unsigned int, struct ctlops *, void *);
void ctlslot_del(struct ctlslot *);
int ctlslot_visible(struct ctlslot *, struct ctl *);
struct ctl *ctlslot_lookup(struct ctlslot *, int);
Index: usr.bin/sndiod/midi.c
===================================================================
RCS file: /cvs/src/usr.bin/sndiod/midi.c,v
diff -u -p -r1.36 midi.c
--- usr.bin/sndiod/midi.c 20 May 2026 13:27:41 -0000 1.36
+++ usr.bin/sndiod/midi.c 11 Jun 2026 06:58:59 -0000
@@ -46,7 +46,9 @@ struct port *port_list = NULL;
unsigned int midi_portnum = 0;
struct midithru {
- unsigned int txmask, rxmask;
+ unsigned int portmask;
+ unsigned int progmask;
+ unsigned int prefportmask;
int refcnt;
#define MIDITHRU_NMAX 32
} midithru[MIDITHRU_NMAX];
@@ -133,8 +135,8 @@ midi_del(struct midi *ep)
}
}
for (i = 0; i < MIDITHRU_NMAX; i++) {
- midithru[i].txmask &= ~ep->self;
- midithru[i].rxmask &= ~ep->self;
+ midithru[i].progmask &= ~ep->self;
+ midithru[i].portmask &= ~ep->self;
}
ep->ops = NULL;
if (ep->mode & MODE_MIDIIN) {
@@ -181,57 +183,6 @@ midi_unlink(struct midi *ep, struct midi
}
/*
- * return the list of endpoints the given one receives from
- */
-unsigned int
-midi_rxmask(struct midi *ep)
-{
- int i, rxmask;
-
- for (rxmask = 0, i = 0; i < MIDI_NEP; i++) {
- if ((midi_ep[i].txmask & ep->self) == 0)
- continue;
- rxmask |= midi_ep[i].self;
- }
-
- return rxmask;
-}
-
-/*
- * add the midi endpoint in the ``tag'' midi thru box
- */
-void
-midi_tag(struct midi *ep, unsigned int tag)
-{
- struct midi *peer;
- struct midithru *t = midithru + tag;
- int i;
-
- if (ep->mode & MODE_MIDIOUT) {
- ep->txmask |= t->txmask;
- midi_tickets(ep);
- }
- if (ep->mode & MODE_MIDIIN) {
-#ifdef DEBUG
- if (ep->obuf.used > 0) {
- logx(0, "midi%u: tagged with non-empty buffer", ep->num);
- panic();
- }
-#endif
- for (i = 0; i < MIDI_NEP; i++) {
- if (!(t->rxmask & (1 << i)))
- continue;
- peer = midi_ep + i;
- peer->txmask |= ep->self;
- }
- }
- if (ep->mode & MODE_MIDIOUT)
- t->rxmask |= ep->self;
- if (ep->mode & MODE_MIDIIN)
- t->txmask |= ep->self;
-}
-
-/*
* broadcast the given message to other endpoints
*/
void
@@ -436,45 +387,6 @@ 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_imsg(void *arg, unsigned char *msg, int size)
{
@@ -524,7 +436,6 @@ port_new(char *path, unsigned int mode,
c->refcnt = 0;
c->midi = midi_new(&port_midiops, c, mode);
c->num = midi_portnum++;
- c->alt_next = c;
c->next = port_list;
port_list = c;
return c;
@@ -576,59 +487,6 @@ port_unref(struct port *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) {
- logx(2, "midi%u: no fall-back port found", op->midi->num);
- return op;
- }
-
- if (port_ref(np))
- break;
- }
-
- logx(2, "midi%u: switching to midi%u", op->midi->num, np->midi->num);
-
- midi_migrate(op->midi, np->midi);
- return np;
-}
-
-struct port *
port_bynum(int num)
{
struct port *p;
@@ -684,8 +542,8 @@ port_drain(struct port *c)
int
port_init(struct port *c)
{
- if (c->hold)
- return port_open(c);
+ if (c->hold && !port_ref(c))
+ return 0;
return 1;
}
@@ -694,4 +552,159 @@ port_done(struct port *c)
{
if (c->state == PORT_INIT)
port_drain(c);
+}
+
+/*
+ * unlink the port from midithru's (but keep it on the prefportmask in case
+ * the port is back) and update server.port accordingly
+ */
+void
+port_abort(struct port *p)
+{
+ struct ctl *c;
+ int i;
+
+ for (i = 0; i < MIDITHRU_NMAX; i++) {
+ midithru_rm(i, p->midi);
+
+ c = ctl_find(CTL_MIDI_PORT, p, &i);
+ if (c != NULL && c->curval != 0) {
+ c->val_mask = ~0U;
+ c->curval = 0;
+ }
+ }
+ midi_abort(p->midi);
+}
+
+void
+midithru_ref(unsigned int tag)
+{
+ struct midithru *t = midithru + tag;
+ struct port *c;
+ char name[64];
+
+#ifdef DEBUG
+ logx(3, "%u: midithru requested", tag);
+#endif
+ if (t->refcnt++ > 0)
+ return;
+ for (c = port_list; c != NULL; c = c->next) {
+ c->refcnt++;
+ if (c->state == DEV_CFG)
+ port_open(c);
+ if (c->state == DEV_INIT && (t->prefportmask & c->midi->self))
+ midithru_addport(tag, c);
+ snprintf(name, sizeof(name), "%u", c->num);
+ ctl_new(CTL_MIDI_PORT, c, &tag,
+ CTL_VEC, "", "", "server", -1, "port",
+ name, -1, 1, !!(t->portmask & c->midi->self));
+ }
+}
+
+void
+midithru_unref(unsigned int tag)
+{
+ struct midithru *t = midithru + tag;
+ struct port *c;
+
+#ifdef DEBUG
+ logx(3, "%u: midithru released", tag);
+#endif
+ if (--t->refcnt > 0)
+ return;
+ /* delete server.port control */
+ for (c = port_list; c != NULL; c = c->next) {
+ if (ctl_del(CTL_MIDI_PORT, c, &tag)) {
+ midithru_rm(tag, c->midi);
+ port_unref(c);
+ }
+ }
+}
+
+void
+midithru_addport(unsigned int tag, struct port *c)
+{
+ struct midithru *t = midithru + tag;
+ int i;
+
+ if (c->state == DEV_INIT) {
+ for (i = 0; i < MIDI_NEP; i++) {
+ if (t->progmask & (1 << i))
+ midi_link(midi_ep + i, c->midi);
+ }
+ }
+ t->portmask |= c->midi->self;
+}
+
+void
+midithru_addprog(unsigned int tag, struct midi *ep)
+{
+ struct midithru *t = midithru + tag;
+ int i;
+
+ for (i = 0; i < MIDI_NEP; i++) {
+ if ((t->portmask | t->progmask) & (1 << i))
+ midi_link(ep, midi_ep + i);
+ }
+ t->progmask |= ep->self;
+}
+
+void
+midithru_rm(unsigned int tag, struct midi *ep)
+{
+ struct midithru *t = midithru + tag;
+ int i;
+
+ t->progmask &= ~ep->self;
+ t->portmask &= ~ep->self;
+
+ for (i = 0; i < MIDI_NEP; i++) {
+ if ((t->portmask | t->progmask) & (1 << i))
+ midi_unlink(ep, midi_ep + i);
+ }
+}
+
+int
+midithru_setport(unsigned int tag, struct port *c, int val)
+{
+ struct midithru *t = midithru + tag;
+
+ if (val) {
+ if (c->state == PORT_CFG && !port_open(c))
+ return 0;
+ midithru_addport(tag, c);
+ t->prefportmask |= c->midi->self;
+ } else {
+ midithru_rm(tag, c->midi);
+ t->prefportmask &= ~c->midi->self;
+ }
+ return 1;
+}
+
+/*
+ * Reopen failed ports.
+ */
+void
+midithru_scanports(void)
+{
+ struct port *p;
+ struct ctl *c;
+ int i;
+
+ for (p = port_list; p != NULL; p = p->next) {
+
+ if (p->refcnt == 0 || p->state != PORT_CFG || !port_open(p))
+ continue;
+
+ for (i = 0; i < MIDITHRU_NMAX; i++) {
+ if (!(midithru[i].prefportmask & p->midi->self))
+ continue;
+ midithru_addport(i, p);
+ c = ctl_find(CTL_MIDI_PORT, p, &i);
+ if (c != NULL && c->curval != 0) {
+ c->val_mask = ~0U;
+ c->curval = 1;
+ }
+ }
+ }
}
Index: usr.bin/sndiod/midi.h
===================================================================
RCS file: /cvs/src/usr.bin/sndiod/midi.h,v
diff -u -p -r1.20 midi.h
--- usr.bin/sndiod/midi.h 20 May 2026 13:27:41 -0000 1.20
+++ usr.bin/sndiod/midi.h 11 Jun 2026 06:58:59 -0000
@@ -91,7 +91,6 @@ struct port {
unsigned int state;
unsigned int num; /* port serial number */
char *path;
- struct port *alt_next;
int hold; /* hold the port open ? */
int refcnt;
struct midi *midi;
@@ -112,7 +111,6 @@ void midi_in(struct midi *, unsigned cha
void midi_out(struct midi *, unsigned char *, int);
void midi_send(struct midi *, unsigned char *, int);
void midi_fill(struct midi *);
-void midi_tag(struct midi *, unsigned int);
unsigned int midi_rxmask(struct midi *);
void midi_link(struct midi *, struct midi *);
void midi_unlink(struct midi *, struct midi *);
@@ -127,8 +125,17 @@ void port_unref(struct port *);
int port_init(struct port *);
void port_done(struct port *);
void port_drain(struct port *);
+int port_open(struct port *);
int port_close(struct port *);
struct port *port_alt_ref(int);
-struct port *port_migrate(struct port *);
+void port_abort(struct port *p);
+
+void midithru_ref(unsigned int);
+void midithru_unref(unsigned int);
+void midithru_addport(unsigned int, struct port *);
+void midithru_addprog(unsigned int, struct midi *);
+void midithru_rm(unsigned int, struct midi *);
+int midithru_setport(unsigned int, struct port *, int);
+void midithru_scanports(void);
#endif /* !defined(MIDI_H) */
Index: usr.bin/sndiod/miofile.c
===================================================================
RCS file: /cvs/src/usr.bin/sndiod/miofile.c,v
diff -u -p -r1.9 miofile.c
--- usr.bin/sndiod/miofile.c 1 Nov 2021 14:43:25 -0000 1.9
+++ usr.bin/sndiod/miofile.c 11 Jun 2026 06:58:59 -0000
@@ -129,8 +129,8 @@ port_mio_hup(void *arg)
{
struct port *p = arg;
- port_migrate(p);
- midi_abort(p->midi);
+ port_abort(p);
+
if (p->state != PORT_CFG)
port_close(p);
}
Index: usr.bin/sndiod/opt.c
===================================================================
RCS file: /cvs/src/usr.bin/sndiod/opt.c,v
diff -u -p -r1.17 opt.c
--- usr.bin/sndiod/opt.c 20 May 2026 13:02:04 -0000 1.17
+++ usr.bin/sndiod/opt.c 11 Jun 2026 06:58:59 -0000
@@ -382,7 +382,7 @@ opt_new(struct dev *d, char *name,
* allocated
*/
o->midi = midi_new(&opt_midiops, o, MODE_MIDIIN | MODE_MIDIOUT);
- midi_tag(o->midi, o->num);
+ midithru_addprog(o->num, o->midi);
o->pmin = pmin;
o->pmax = pmax;
Index: usr.bin/sndiod/sndiod.8
===================================================================
RCS file: /cvs/src/usr.bin/sndiod/sndiod.8,v
diff -u -p -r1.21 sndiod.8
--- usr.bin/sndiod/sndiod.8 26 May 2026 14:50:52 -0000 1.21
+++ usr.bin/sndiod/sndiod.8 11 Jun 2026 06:58:59 -0000
@@ -34,7 +34,6 @@
.Op Fl j Ar flag
.Op Fl L Ar addr
.Op Fl m Ar mode
-.Op Fl Q Ar port
.Op Fl q Ar port
.Op Fl r Ar rate
.Op Fl s Ar name
@@ -263,15 +262,6 @@ Multiple modes can be specified, separat
The default is
.Ar play , Ns Ar rec
(i.e. full-duplex).
-.It Fl Q Ar port
-Specify an alternate MIDI port to use.
-If it doesn't work, the one given with the last
-.Fl Q
-or
-.Fl q
-options will be used.
-For instance, this allows a USB MIDI controller to be replaced without
-the need to restart programs using it.
.It Fl q Ar port
Expose the given MIDI port.
This allows multiple programs to share the port.
Index: usr.bin/sndiod/sndiod.c
===================================================================
RCS file: /cvs/src/usr.bin/sndiod/sndiod.c,v
diff -u -p -r1.55 sndiod.c
--- usr.bin/sndiod/sndiod.c 26 May 2026 14:50:52 -0000 1.55
+++ usr.bin/sndiod/sndiod.c 11 Jun 2026 06:58:59 -0000
@@ -287,47 +287,6 @@ reopen_devs(void)
}
}
-/*
- * For each port, open the alt with the highest priority and switch to it
- */
-static void
-reopen_ports(void)
-{
- struct port *p, *a, *apri;
- int inuse;
-
- for (p = port_list; p != NULL; p = a->next) {
-
- /* skip unused ports */
- inuse = 0;
- a = p;
- while (1) {
- if (midi_rxmask(a->midi) || a->midi->txmask)
- inuse = 1;
- if (a->alt_next == p)
- break;
- a = a->alt_next;
- }
- if (!inuse)
- continue;
-
- /* open the alt with the highest prio */
- apri = port_alt_ref(p->num);
-
- /* switch to it */
- a = p;
- while (1) {
- if (a != apri) {
- midi_migrate(a->midi, apri->midi);
- port_unref(a);
- }
- if (a->alt_next == p)
- break;
- a = a->alt_next;
- }
- }
-}
-
void
setsig(void)
{
@@ -505,7 +464,7 @@ main(int argc, char **argv)
struct opt *o;
struct dev *d;
struct opt_alt *a, **pa, *alt_list;
- struct port *p, *port_first, *port_next;
+ struct port *p;
struct listen *l;
struct passwd *pw;
struct tcpaddr {
@@ -537,7 +496,6 @@ main(int argc, char **argv)
par.msb = 0;
mode = MODE_PLAY | MODE_REC;
alt_list = NULL;
- port_first = port_next = NULL;
tcpaddr_list = NULL;
d = NULL;
p = NULL;
@@ -600,18 +558,8 @@ main(int argc, char **argv)
return 1;
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);
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();
@@ -766,7 +714,7 @@ main(int argc, char **argv)
if (reopen_flag) {
reopen_flag = 0;
reopen_devs();
- reopen_ports();
+ midithru_scanports();
}
if (!fdpass_peer)
break;
Index: usr.bin/sndiod/sock.c
===================================================================
RCS file: /cvs/src/usr.bin/sndiod/sock.c,v
diff -u -p -r1.60 sock.c
--- usr.bin/sndiod/sock.c 20 May 2026 13:26:02 -0000 1.60
+++ usr.bin/sndiod/sock.c 11 Jun 2026 06:58:59 -0000
@@ -180,6 +180,10 @@ sock_close(struct sock *f)
opt_unref(f->opt);
f->opt = NULL;
}
+ if (f->tag) {
+ midithru_unref(f->tag);
+ f->tag = 0;
+ }
}
if (f->ctlslot) {
ctlslot_del(f->ctlslot);
@@ -311,6 +315,7 @@ sock_new(int fd)
f = xmalloc(sizeof(struct sock));
f->pstate = SOCK_AUTH;
+ f->tag = 0;
f->slot = NULL;
f->port = NULL;
f->midi = NULL;
@@ -713,6 +718,7 @@ sock_hello(struct sock *f)
struct opt *opt;
unsigned int mode;
unsigned int id;
+ unsigned int tag;
mode = ntohs(p->mode);
id = ntohl(p->id);
@@ -754,7 +760,7 @@ sock_hello(struct sock *f)
if (!opt_ref(opt))
return 0;
f->opt = opt;
- midi_tag(f->midi, opt->num);
+ midithru_addprog(opt->num, f->midi);
} else if (p->devnum < 16) {
opt = legacy_opt(p->devnum, p->opt);
if (opt == NULL)
@@ -762,12 +768,14 @@ sock_hello(struct sock *f)
if (!opt_ref(opt))
return 0;
f->opt = opt;
- midi_tag(f->midi, opt->num);
+ midithru_addprog(opt->num, f->midi);
} else if (p->devnum < 32) {
- midi_tag(f->midi, p->devnum);
+ f->tag = p->devnum;
+ midithru_ref(f->tag);
+ midithru_addprog(f->tag, f->midi);
} else if (p->devnum < 48) {
- c = port_alt_ref(p->devnum - 32);
- if (c == NULL)
+ c = port_bynum(p->devnum - 32);
+ if (c == NULL || !port_ref(c))
return 0;
f->port = c;
midi_link(f->midi, c->midi);
@@ -777,15 +785,24 @@ sock_hello(struct sock *f)
}
if (mode & MODE_CTLMASK) {
if (p->devnum == AMSG_NODEV) {
+ tag = 0;
opt = opt_byname(p->opt);
if (opt == NULL)
return 0;
- } else {
+ } else if (p->devnum < 16) {
+ tag = 0;
opt = legacy_opt(p->devnum, p->opt);
if (opt == NULL)
return 0;
+ } else if (p->devnum < 32) {
+ tag = p->devnum;
+ opt = NULL;
+ logx(2, "sock %d: controlling midithru", f->fd);
+ } else {
+ logx(2, "sock %d: unhandled device", f->fd);
+ return 0;
}
- f->ctlslot = ctlslot_new(opt, &sock_ctlops, f);
+ f->ctlslot = ctlslot_new(opt, tag, &sock_ctlops, f);
if (f->ctlslot == NULL) {
logx(2, "sock %d: couldn't get ctlslot", f->fd);
return 0;
Index: usr.bin/sndiod/sock.h
===================================================================
RCS file: /cvs/src/usr.bin/sndiod/sock.h,v
diff -u -p -r1.11 sock.h
--- usr.bin/sndiod/sock.h 20 May 2026 13:26:02 -0000 1.11
+++ usr.bin/sndiod/sock.h 11 Jun 2026 06:58:59 -0000
@@ -57,6 +57,7 @@ struct sock {
unsigned int walign; /* align written data to this */
unsigned int ralign; /* read data is aligned to this */
int lastvol; /* last volume */
+ unsigned int tag; /* controlled or connected midithru */
struct slot *slot; /* audio device slot number */
struct midi *midi; /* midi endpoint */
struct port *port; /* midi port */
sndiod: control of midithru ports with sndioctl.