Index | Thread | Search

From:
Rafael Sadowski <rafael@sizeofvoid.org>
Subject:
Re: sndiod: Add the server.mode control making the server mode dynamic
To:
Alexandre Ratchov <alex@caoua.org>
Cc:
tech@openbsd.org
Date:
Tue, 19 May 2026 10:39:26 +0200

Download raw body.

Thread
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 <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.
> 
> 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 -----
>