Download raw body.
sndiod: Add the server.mode control making the server mode dynamic
This diff allows the server mode to be changed at run-time with the
new server.mode control exposed by sndioctl(1), ex:
$ sndioctl server.mode=play,rec,mon
Useful to record your screen with voiceover without restarting sndiod
twice. The -m option defines its initial value.
If the server is switched to play-only mode, then existing clients
will start recording silence. Similarly if it's switched to rec-only
mode, clients are muted. Connections are never dropped.
At sndiod startup, trying to determine the hardware mode based on the
-m options doesn't make sense anymore. So -m can no longer be used to
force the hardware mode, which is not very useful these days imho.
OK?
Index: dev.c
===================================================================
RCS file: /cvs/src/usr.bin/sndiod/dev.c,v
diff -u -p -r1.132 dev.c
--- dev.c 15 Mar 2026 14:24:43 -0000 1.132
+++ dev.c 15 Mar 2026 14:38:07 -0000
@@ -42,7 +42,6 @@ void dev_sub_bcopy(struct dev *, struct
void dev_onmove(struct dev *, int);
void dev_master(struct dev *, unsigned int);
void dev_cycle(struct dev *);
-void dev_adjpar(struct dev *, int, int, int);
int dev_allocbufs(struct dev *);
void dev_freebufs(struct dev *);
int dev_ref(struct dev *);
@@ -747,8 +746,7 @@ dev_master(struct dev *d, unsigned int m
* Create a sndio device
*/
struct dev *
-dev_new(char *path, struct aparams *par,
- unsigned int mode, unsigned int bufsz, unsigned int round,
+dev_new(char *path, struct aparams *par, unsigned int bufsz, unsigned int round,
unsigned int rate, unsigned int hold, unsigned int autovol)
{
struct dev *d, **pd;
@@ -762,7 +760,6 @@ dev_new(char *path, struct aparams *par,
d->num = dev_sndnum++;
d->reqpar = *par;
- d->reqmode = mode;
d->reqpchan = d->reqrchan = 0;
d->reqbufsz = bufsz;
d->reqround = round;
@@ -786,18 +783,12 @@ dev_new(char *path, struct aparams *par,
* adjust device parameters and mode
*/
void
-dev_adjpar(struct dev *d, int mode,
- int pmax, int rmax)
+dev_adjpar(struct dev *d, int pmax, int rmax)
{
- d->reqmode |= mode & MODE_AUDIOMASK;
- if (mode & MODE_PLAY) {
- if (d->reqpchan < pmax + 1)
- d->reqpchan = pmax + 1;
- }
- if (mode & MODE_REC) {
- if (d->reqrchan < rmax + 1)
- d->reqrchan = rmax + 1;
- }
+ if (d->reqpchan < pmax + 1)
+ d->reqpchan = pmax + 1;
+ if (d->reqrchan < rmax + 1)
+ d->reqrchan = rmax + 1;
}
/*
@@ -867,7 +858,7 @@ dev_allocbufs(struct dev *d)
int
dev_open(struct dev *d)
{
- d->mode = d->reqmode;
+ d->mode = MODE_AUDIOMASK;
d->round = d->reqround;
d->bufsz = d->reqbufsz;
d->rate = d->reqrate;
@@ -996,12 +987,6 @@ dev_unref(struct dev *d)
int
dev_init(struct dev *d)
{
- if ((d->reqmode & MODE_AUDIOMASK) == 0) {
-#ifdef DEBUG
- logx(1, "%s: has no streams", d->path);
-#endif
- return 0;
- }
if (d->hold && !dev_ref(d))
return 0;
return 1;
@@ -1473,12 +1458,6 @@ slot_attach(struct slot *s)
struct dev *d = s->opt->dev;
long long pos;
- if (((s->mode & MODE_PLAY) && !(s->opt->mode & MODE_PLAY)) ||
- ((s->mode & MODE_RECMASK) && !(s->opt->mode & MODE_RECMASK))) {
- logx(1, "slot%zu at %s: mode not allowed", s - slot_array, s->opt->name);
- return;
- }
-
/*
* setup conversions layer
*/
@@ -1814,6 +1793,7 @@ ctlslot_visible(struct ctlslot *s, struc
case CTL_DEV_MASTER:
return (s->opt->dev == c->u.any.arg0);
case CTL_OPT_DEV:
+ case CTL_OPT_MODE:
return (s->opt == c->u.any.arg0);
case CTL_APP_LEVEL:
return (s->opt == c->u.app_level.opt);
@@ -1894,6 +1874,9 @@ ctl_scope_fmt(char *buf, size_t size, st
case CTL_OPT_DEV:
return snprintf(buf, size, "opt_dev:%s/%s",
c->u.opt_dev.opt->name, c->u.opt_dev.dev->name);
+ 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);
default:
return snprintf(buf, size, "unknown");
}
@@ -1962,6 +1945,11 @@ ctl_setval(struct ctl *c, int val)
opt_setalt(c->u.opt_dev.opt, c->u.opt_dev.dev);
}
return 1;
+ case CTL_OPT_MODE:
+ opt_setmode(c->u.opt_mode.opt, c->u.opt_mode.idx, val);
+ c->val_mask = ~0U;
+ c->curval = val;
+ return 1;
default:
logx(2, "ctl%u: not writable", c->addr);
return 1;
@@ -2018,6 +2006,9 @@ ctl_new(int scope, void *arg0, void *arg
case CTL_APP_LEVEL:
c->u.any.arg1 = arg1;
break;
+ case CTL_OPT_MODE:
+ c->u.opt_mode.idx = *(int *)arg1;
+ break;
default:
c->u.any.arg1 = NULL;
}
@@ -2083,6 +2074,10 @@ ctl_match(struct ctl *c, int scope, void
case CTL_OPT_DEV:
case CTL_APP_LEVEL:
if (arg1 != NULL && c->u.any.arg1 != arg1)
+ return 0;
+ break;
+ case CTL_OPT_MODE:
+ if (arg1 != NULL && c->u.opt_mode.idx != *(int *)arg1)
return 0;
break;
}
Index: dev.h
===================================================================
RCS file: /cvs/src/usr.bin/sndiod/dev.h,v
diff -u -p -r1.53 dev.h
--- dev.h 15 Mar 2026 14:24:43 -0000 1.53
+++ dev.h 15 Mar 2026 14:38:07 -0000
@@ -124,6 +124,7 @@ struct ctl {
#define CTL_DEV_MASTER 1
#define CTL_OPT_DEV 2
#define CTL_APP_LEVEL 3
+#define CTL_OPT_MODE 4
unsigned int scope;
union {
struct {
@@ -145,6 +146,10 @@ struct ctl {
struct opt *opt;
struct dev *dev;
} opt_dev;
+ struct {
+ struct opt *opt;
+ int idx;
+ } opt_mode;
} u;
unsigned int addr; /* slot side control address */
@@ -240,7 +245,6 @@ struct dev {
/*
* desired parameters
*/
- unsigned int reqmode; /* mode */
struct aparams reqpar; /* parameters */
int reqpchan, reqrchan; /* play & rec chans */
unsigned int reqbufsz; /* buffer size */
@@ -280,11 +284,11 @@ int dev_open(struct dev *);
void dev_close(struct dev *);
void dev_abort(struct dev *);
void dev_migrate(struct dev *);
-struct dev *dev_new(char *, struct aparams *, unsigned int, unsigned int,
+struct dev *dev_new(char *, struct aparams *, unsigned int,
unsigned int, unsigned int, unsigned int, unsigned int);
struct dev *dev_bynum(int);
void dev_del(struct dev *);
-void dev_adjpar(struct dev *, int, int, int);
+void dev_adjpar(struct dev *, int, int);
int dev_init(struct dev *);
void dev_done(struct dev *);
int dev_ref(struct dev *);
Index: opt.c
===================================================================
RCS file: /cvs/src/usr.bin/sndiod/opt.c,v
diff -u -p -r1.16 opt.c
--- opt.c 26 Nov 2025 08:40:16 -0000 1.16
+++ opt.c 15 Mar 2026 14:38:08 -0000
@@ -37,6 +37,12 @@ struct midiops opt_midiops = {
opt_midi_exit
};
+struct opt_mode opt_modes[] = {
+ {MODE_PLAY, "play"},
+ {MODE_REC, "rec"},
+ {MODE_MON, "mon"},
+};
+
struct app *
opt_mkapp(struct opt *o, char *who)
{
@@ -378,14 +384,10 @@ opt_new(struct dev *d, char *name,
o->midi = midi_new(&opt_midiops, o, MODE_MIDIIN | MODE_MIDIOUT);
midi_tag(o->midi, o->num);
- if (mode & MODE_PLAY) {
- o->pmin = pmin;
- o->pmax = pmax;
- }
- if (mode & MODE_RECMASK) {
- o->rmin = rmin;
- o->rmax = rmax;
- }
+ o->pmin = pmin;
+ o->pmax = pmax;
+ o->rmin = rmin;
+ o->rmax = rmax;
o->maxweight = maxweight;
o->mtc = mmc ? &mtc_array[0] : NULL;
o->dup = dup;
@@ -486,19 +488,52 @@ opt_del(struct opt *o)
void
opt_init(struct opt *o)
{
+ int i;
+
+ for (i = 0; i < sizeof(opt_modes) / sizeof(opt_modes[0]); i++) {
+ ctl_new(CTL_OPT_MODE, o, &i, CTL_VEC, "",
+ o->name, "server", -1, "mode", opt_modes[i].name, -1,
+ 1, (o->mode & opt_modes[i].bit) ? 1 : 0);
+ }
}
void
opt_done(struct opt *o)
{
struct dev *d;
+ int i;
if (o->refcnt != 0) {
// XXX: all clients are already kicked, so this never happens
logx(0, "%s: still has refs", o->name);
}
+
+ for (i = 0; i < sizeof(opt_modes) / sizeof(opt_modes[0]); i++)
+ ctl_del(CTL_OPT_MODE, o, &i);
+
for (d = dev_list; d != NULL; d = d->next)
ctl_del(CTL_OPT_DEV, o, d);
+}
+
+/*
+ * Flip a bit of opt's mode
+ */
+void
+opt_setmode(struct opt *o, int idx, int val)
+{
+ int mode;
+
+ /*
+ * The o->mode field is directly used by the device,
+ * so just flipping the bit is OK.
+ */
+ mode = opt_modes[idx].bit;
+ if (val)
+ o->mode |= mode;
+ else
+ o->mode &= ~mode;
+
+ logx(2, "%s: %s -> %d", __func__, opt_modes[idx].name, val);
}
/*
Index: opt.h
===================================================================
RCS file: /cvs/src/usr.bin/sndiod/opt.h,v
diff -u -p -r1.11 opt.h
--- opt.h 26 Nov 2025 08:40:16 -0000 1.11
+++ opt.h 15 Mar 2026 14:38:08 -0000
@@ -55,8 +55,15 @@ struct opt {
int refcnt;
};
+struct opt_mode {
+ int bit;
+ char *name;
+};
+
extern struct opt *opt_list;
+extern struct opt_mode opt_modes[];
+
struct app *opt_mkapp(struct opt *o, char *who);
void opt_appvol(struct opt *o, struct app *a, int vol);
void opt_midi_vol(struct opt *, struct app *);
@@ -70,6 +77,7 @@ struct opt *opt_byname(char *);
struct opt *opt_bynum(int);
void opt_init(struct opt *);
void opt_done(struct opt *);
+void opt_setmode(struct opt *, int, int);
int opt_setdev(struct opt *, struct dev *);
void opt_migrate(struct opt *, struct dev *);
struct dev *opt_ref(struct opt *);
Index: siofile.c
===================================================================
RCS file: /cvs/src/usr.bin/sndiod/siofile.c,v
diff -u -p -r1.30 siofile.c
--- siofile.c 22 Jan 2026 09:24:26 -0000 1.30
+++ siofile.c 15 Mar 2026 14:38:08 -0000
@@ -104,7 +104,7 @@ int
dev_sio_open(struct dev *d)
{
struct sio_par par;
- unsigned int rate, mode = d->reqmode & (SIO_PLAY | SIO_REC);
+ unsigned int rate, mode = SIO_PLAY | SIO_REC;
d->sio.hdl = fdpass_sio_open(d->num, mode);
if (d->sio.hdl == NULL) {
Index: sndiod.c
===================================================================
RCS file: /cvs/src/usr.bin/sndiod/sndiod.c,v
diff -u -p -r1.53 sndiod.c
--- sndiod.c 15 Mar 2026 10:05:09 -0000 1.53
+++ sndiod.c 15 Mar 2026 14:38:08 -0000
@@ -103,8 +103,7 @@ unsigned int opt_mode(void);
void getbasepath(char *);
void setsig(void);
void unsetsig(void);
-struct dev *mkdev(char *, struct aparams *,
- int, int, int, int, int, int);
+struct dev *mkdev(char *, struct aparams *, int, int, int, int, int);
struct port *mkport(char *, int);
struct opt *mkopt(char *, struct dev *, struct opt_alt *,
int, int, int, int, int, int, int, int);
@@ -366,7 +365,7 @@ unsetsig(void)
struct dev *
mkdev(char *path, struct aparams *par,
- int mode, int bufsz, int round, int rate, int hold, int autovol)
+ int bufsz, int round, int rate, int hold, int autovol)
{
struct dev *d;
@@ -381,7 +380,7 @@ mkdev(char *path, struct aparams *par,
bufsz = round * 2;
} else if (!round)
round = bufsz / 2;
- d = dev_new(path, par, mode, bufsz, round, rate, hold, autovol);
+ d = dev_new(path, par, bufsz, round, rate, hold, autovol);
if (d == NULL)
exit(1);
return d;
@@ -414,7 +413,7 @@ mkopt(char *path, struct dev *d, struct
MIDI_TO_ADATA(vol), mmc, dup, mode);
if (o == NULL)
return NULL;
- dev_adjpar(d, o->mode, o->pmax, o->rmax);
+ dev_adjpar(d, o->pmax, o->rmax);
for (a = alt_list; a != NULL; a = a->next)
opt_setalt(o, a->dev);
return o;
@@ -602,7 +601,7 @@ main(int argc, char **argv)
case 's':
if (d == NULL) {
for (i = 0; default_devs[i] != NULL; i++) {
- mkdev(default_devs[i], &par, 0,
+ mkdev(default_devs[i], &par,
bufsz, round, rate, 0, autovol);
}
d = dev_list;
@@ -642,7 +641,7 @@ main(int argc, char **argv)
errx(1, "%s: block size is %s", optarg, str);
break;
case 'f':
- d = mkdev(optarg, &par, 0, bufsz, round,
+ d = mkdev(optarg, &par, bufsz, round,
rate, hold, autovol);
while ((a = alt_list) != NULL) {
alt_list = a->next;
@@ -653,7 +652,7 @@ main(int argc, char **argv)
if (d == NULL)
errx(1, "-F %s: no devices defined", optarg);
a = xmalloc(sizeof(struct opt_alt));
- a->dev = mkdev(optarg, &par, 0, bufsz, round,
+ a->dev = mkdev(optarg, &par, bufsz, round,
rate, hold, autovol);
for (pa = &alt_list; *pa != NULL; pa = &(*pa)->next)
;
@@ -677,7 +676,7 @@ main(int argc, char **argv)
}
if (dev_list == NULL) {
for (i = 0; default_devs[i] != NULL; i++) {
- mkdev(default_devs[i], &par, 0,
+ mkdev(default_devs[i], &par,
bufsz, round, rate, 0, autovol);
}
}
@@ -701,7 +700,7 @@ main(int argc, char **argv)
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);
+ dev_adjpar(d, o->pmax, o->rmax);
}
while ((a = alt_list) != NULL) {
sndiod: Add the server.mode control making the server mode dynamic