From: Rafael Sadowski Subject: Re: sndiod: Add the server.mode control making the server mode dynamic To: Alexandre Ratchov Cc: tech@openbsd.org Date: Tue, 19 May 2026 10:39:26 +0200 On Tue May 12, 2026 at 12:01:46PM +0200, Alexandre Ratchov wrote: > ping I've tested it and it works perfectly. A note on the diff. Otherwise OK rsadowski > > ----- Forwarded message from Alexandre Ratchov ----- > > Date: Sun, 15 Mar 2026 21:17:43 +0100 > From: Alexandre Ratchov > To: tech@openbsd.org > Subject: 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[] = { const? > + {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[]; const? > + > 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) { > > > > ----- End forwarded message ----- >