Download raw body.
sndiod: Add the server.mode control making the server mode dynamic
sndiod: Add the server.mode control making the server mode dynamic
sndiod: Add the server.mode control making the server mode dynamic
Hello Alex,
Some questions / feedbacks inline but works fine.
OK
On Tue, May 12, 2026, at 12:01, Alexandre Ratchov wrote:
> ping
>
> ----- Forwarded message from Alexandre Ratchov <alex@caoua.org> -----
>
> Date: Sun, 15 Mar 2026 21:17:43 +0100
> From: Alexandre Ratchov <alex@caoua.org>
> 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.
Maybe worth a man update ?
>
> 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;
> - }
> -
Where does server.mode is constrained to what hardware really support ? Not wanted ?
> /*
> * 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"},
> +};
> +
const ?
> 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++) {
I don't ever remember if we have nitems ;D
> + 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) {
>
>
>
> ----- End forwarded message -----
sndiod: Add the server.mode control making the server mode dynamic
sndiod: Add the server.mode control making the server mode dynamic
sndiod: Add the server.mode control making the server mode dynamic