Index | Thread | Search

From:
Alexandre Ratchov <alex@caoua.org>
Subject:
Re: sndio: show the real device name in server.device control
To:
tech@openbsd.org
Date:
Tue, 14 May 2024 07:40:29 +0200

Download raw body.

Thread
Here's a second (and shorter) version of the diff to add the device
names to the sndioctl server.device control. Example:

$ sndioctl -i
output.level=*
output.mute=*
server.device=0(azalia0),1(envy0),2(envy1),3(uaudio0),4(uaudio1),5,6,7

Now, sndiod exposes the driver name of the devices that are connected.
The plan is to change the low-level drivers to report the chipset, the
vendor/product name, the codec models, or whatever appropriate.

In summary:

- Add a "char display[]" member to the sioctl_desc structure. To do
  so, we reuse the padding, which allows binaries linked to the old
  libsndio to use the new libsndio. So this is a shlib_minor bump,
  allowing to test this diff without rebuilding all audio ports.

- The sndiod(8) network protocol gets a new AMSG_CTLSUB op-code to
  subscribe to the new version of the control descriptions (with the
  display string). The old op-code remains, which allows systems or
  VMs with the old libsndio version (or static binaries) to connect to
  the new sndiod.

Compared to the previous diff version:

- The SIGHUP handling and the kernel part are gone (already
  committed).

- Showing only present devices was a bad idea (it broke hot-plugging in
  certain cases), so we retain the current behavior: show all devices,
  i.e. all sndiod -fF options

OK?

Index: include/sndio.h
===================================================================
RCS file: /cvs/src/include/sndio.h,v
diff -u -p -u -p -r1.14 sndio.h
--- include/sndio.h	29 Apr 2022 08:30:48 -0000	1.14
+++ include/sndio.h	14 May 2024 05:07:42 -0000
@@ -27,8 +27,17 @@
 
 /*
  * limits
+ *
+ * For now SIOCTL_DISPLAYMAX is 12 byte only. It nicely fits in the
+ * padding of the sioctl_desc structure: this allows any binary linked
+ * to the library version with no sioctl_desc->display to work with
+ * this library version. Currently, any string reported by the lower
+ * layers fits in the 12-byte buffer. Once larger strings start
+ * being used (or the ABI changes for any other reason) increase
+ * SIOCTL_DISPLAYMAX and properly pad the sioctl_desc structure.
  */
 #define SIOCTL_NAMEMAX		12	/* max name length */
+#define SIOCTL_DISPLAYMAX	12	/* max display string length */
 
 /*
  * private ``handle'' structure
@@ -115,7 +124,7 @@ struct sioctl_desc {
 	struct sioctl_node node0;	/* affected node */
 	struct sioctl_node node1;	/* dito for SIOCTL_{VEC,LIST,SEL} */
 	unsigned int maxval;		/* max value */
-	int __pad[3];
+	char display[SIOCTL_DISPLAYMAX];	/* free-format hint */
 };
 
 /*
Index: lib/libsndio/amsg.h
===================================================================
RCS file: /cvs/src/lib/libsndio/amsg.h,v
diff -u -p -u -p -r1.15 amsg.h
--- lib/libsndio/amsg.h	29 Apr 2022 08:30:48 -0000	1.15
+++ lib/libsndio/amsg.h	14 May 2024 05:07:42 -0000
@@ -46,6 +46,13 @@
  * limits
  */
 #define AMSG_CTL_NAMEMAX	16	/* max name length */
+#define AMSG_CTL_DISPLAYMAX	32	/* max display string length */
+
+/*
+ * Size of the struct amsg_ctl_desc expected by clients
+ * using the AMSG_CTLSUB_OLD request
+ */
+#define AMSG_OLD_DESC_SIZE	92
 
 /*
  * WARNING: since the protocol may be simultaneously used by static
@@ -69,9 +76,10 @@ struct amsg {
 #define AMSG_HELLO	10	/* say hello, check versions and so ... */
 #define AMSG_BYE	11	/* ask server to drop connection */
 #define AMSG_AUTH	12	/* send authentication cookie */
-#define AMSG_CTLSUB	13	/* ondesc/onctl subscription */
+#define AMSG_CTLSUB_OLD	13	/* amsg_ctl_desc with no "display" attribute */
 #define AMSG_CTLSET	14	/* set control value */
 #define AMSG_CTLSYNC	15	/* end of controls descriptions */
+#define AMSG_CTLSUB	16	/* ondesc/onctl subscription */
 	uint32_t cmd;
 	uint32_t __pad;
 	union {
@@ -151,7 +159,8 @@ struct amsg_ctl_desc {
 	uint16_t addr;			/* control address */
 	uint16_t maxval;
 	uint16_t curval;
-	uint32_t __pad2[3];
+	uint32_t __pad2[4];
+	char display[AMSG_CTL_DISPLAYMAX];	/* free-format hint */
 };
 
 /*
Index: lib/libsndio/shlib_version
===================================================================
RCS file: /cvs/src/lib/libsndio/shlib_version,v
diff -u -p -u -p -r1.13 shlib_version
--- lib/libsndio/shlib_version	29 Apr 2022 08:30:48 -0000	1.13
+++ lib/libsndio/shlib_version	14 May 2024 05:07:42 -0000
@@ -1,2 +1,2 @@
 major=7
-minor=2
+minor=3
Index: lib/libsndio/sioctl_aucat.c
===================================================================
RCS file: /cvs/src/lib/libsndio/sioctl_aucat.c,v
diff -u -p -u -p -r1.1 sioctl_aucat.c
--- lib/libsndio/sioctl_aucat.c	26 Feb 2020 13:53:58 -0000	1.1
+++ lib/libsndio/sioctl_aucat.c	14 May 2024 05:07:42 -0000
@@ -87,6 +87,7 @@ sioctl_aucat_rdata(struct sioctl_aucat_h
 			strlcpy(desc.node1.name, c->node1.name, SIOCTL_NAMEMAX);
 			desc.node1.unit = (int16_t)ntohs(c->node1.unit);
 			strlcpy(desc.func, c->func, SIOCTL_NAMEMAX);
+			strlcpy(desc.display, c->display, SIOCTL_DISPLAYMAX);
 			desc.type = c->type;
 			desc.addr = ntohs(c->addr);
 			desc.maxval = ntohs(c->maxval);
Index: lib/libsndio/sioctl_open.3
===================================================================
RCS file: /cvs/src/lib/libsndio/sioctl_open.3,v
diff -u -p -u -p -r1.13 sioctl_open.3
--- lib/libsndio/sioctl_open.3	3 May 2022 13:03:30 -0000	1.13
+++ lib/libsndio/sioctl_open.3	14 May 2024 05:07:42 -0000
@@ -168,6 +168,7 @@ struct sioctl_desc {
 	struct sioctl_node node0;	/* affected node */
 	struct sioctl_node node1;	/* dito for SIOCTL_{VEC,LIST,SEL} */
 	unsigned int maxval;		/* max value */
+	char display[SIOCTL_DISPLAYMAX];	/* free-format hint */
 };
 .Ed
 .Pp
@@ -238,6 +239,11 @@ The
 .Fa maxval
 attribute indicates the maximum value of this control.
 For boolean control types it is set to 1.
+.Pp
+The
+.Fa display
+attribute contains an optional free-format string providing additional
+hints about the control, like the hardware model, or the units.
 .Ss Changing and reading control values
 Controls are changed with the
 .Fn sioctl_setval
Index: lib/libsndio/sioctl_sun.c
===================================================================
RCS file: /cvs/src/lib/libsndio/sioctl_sun.c,v
diff -u -p -u -p -r1.2 sioctl_sun.c
--- lib/libsndio/sioctl_sun.c	30 Apr 2020 12:30:47 -0000	1.2
+++ lib/libsndio/sioctl_sun.c	14 May 2024 05:07:42 -0000
@@ -53,6 +53,8 @@ struct volume
 
 struct sioctl_sun_hdl {
 	struct sioctl_hdl sioctl;
+	char display[SIOCTL_DISPLAYMAX];
+	int display_addr;
 	struct volume output, input;
 	int fd, events;
 };
@@ -147,6 +149,7 @@ init(struct sioctl_sun_hdl *hdl)
 		{AudioCinputs, AudioNvolume},
 		{AudioCinputs, AudioNinput}
 	};
+	struct audio_device getdev;
 	int i;
 
 	for (i = 0; i < sizeof(output_names) / sizeof(output_names[0]); i++) {
@@ -165,6 +168,13 @@ init(struct sioctl_sun_hdl *hdl)
 			break;
 		}
 	}
+
+	hdl->display_addr = 128;
+	if (ioctl(hdl->fd, AUDIO_GETDEV, &getdev) == -1)
+		strlcpy(hdl->display, "unknown", SIOCTL_DISPLAYMAX);
+	else
+		strlcpy(hdl->display, getdev.name, SIOCTL_DISPLAYMAX);
+	DPRINTF("init: server.device: display = %s\n", hdl->display);
 }
 
 static int
@@ -407,12 +417,27 @@ static int
 sioctl_sun_ondesc(struct sioctl_hdl *addr)
 {
 	struct sioctl_sun_hdl *hdl = (struct sioctl_sun_hdl *)addr;
+	struct sioctl_desc desc;
 
 	if (!scanvol(hdl, &hdl->output) ||
 	    !scanvol(hdl, &hdl->input)) {
 		hdl->sioctl.eof = 1;
 		return 0;
 	}
+
+	/* report "server.device" control */
+	memset(&desc, 0, sizeof(struct sioctl_desc));
+	desc.type = SIOCTL_SEL;
+	desc.maxval = 1;
+	strlcpy(desc.func, "device", SIOCTL_NAMEMAX);
+	strlcpy(desc.node0.name, "server", SIOCTL_NAMEMAX);
+	desc.node0.unit = -1;
+	strlcpy(desc.node1.name, "0", SIOCTL_NAMEMAX);
+	desc.node1.unit = -1;
+	strlcpy(desc.display, hdl->display, SIOCTL_DISPLAYMAX);
+	desc.addr = hdl->display_addr;
+	_sioctl_ondesc_cb(&hdl->sioctl, &desc, 1);
+
 	_sioctl_ondesc_cb(&hdl->sioctl, NULL, 0);
 	return 1;
 }
Index: usr.bin/sndiod/dev.c
===================================================================
RCS file: /cvs/src/usr.bin/sndiod/dev.c,v
diff -u -p -u -p -r1.113 dev.c
--- usr.bin/sndiod/dev.c	6 May 2024 05:37:26 -0000	1.113
+++ usr.bin/sndiod/dev.c	14 May 2024 05:07:43 -0000
@@ -1054,6 +1054,8 @@ dev_allocbufs(struct dev *d)
 int
 dev_open(struct dev *d)
 {
+	struct opt *o;
+
 	d->mode = d->reqmode;
 	d->round = d->reqround;
 	d->bufsz = d->reqbufsz;
@@ -1076,6 +1078,18 @@ dev_open(struct dev *d)
 		return 0;
 
 	d->pstate = DEV_INIT;
+
+	/* add server.device if device is opened after opt_ref() call */
+	for (o = opt_list; o != NULL; o = o->next) {
+		if (o->refcnt > 0 && !ctl_find(CTL_OPT_DEV, o, d)) {
+			ctl_new(CTL_OPT_DEV, o, d,
+			    CTL_SEL, dev_getdisplay(d),
+			    o->name, "server", -1, "device",
+			    d->name, -1, 1, o->dev == d);
+			d->refcnt++;
+		}
+	}
+
 	return 1;
 }
 
@@ -1150,6 +1164,14 @@ dev_freebufs(struct dev *d)
 void
 dev_close(struct dev *d)
 {
+	struct opt *o;
+
+	/* remove server.device entries */
+	for (o = opt_list; o != NULL; o = o->next) {
+		if (ctl_del(CTL_OPT_DEV, o, d))
+			d->refcnt--;
+	}
+
 	d->pstate = DEV_CFG;
 	dev_sio_close(d);
 	dev_freebufs(d);
@@ -1776,7 +1798,7 @@ slot_new(struct opt *opt, unsigned int i
 	s->opt = opt;
 	slot_ctlname(s, ctl_name, CTL_NAMEMAX);
 	ctl_new(CTL_SLOT_LEVEL, s, NULL,
-	    CTL_NUM, "app", ctl_name, -1, "level",
+	    CTL_NUM, "", "app", ctl_name, -1, "level",
 	    NULL, -1, 127, s->vol);
 
 found:
@@ -2290,6 +2312,14 @@ ctlslot_visible(struct ctlslot *s, struc
 		return 1;
 	switch (c->scope) {
 	case CTL_HW:
+		/*
+		 * Disable hardware's server.device control as its
+		 * replaced by sndiod's one
+		 */
+		if (strcmp(c->node0.name, "server") == 0 &&
+		    strcmp(c->func, "device") == 0)
+			return 0;
+		/* FALLTHROUHG */
 	case CTL_DEV_MASTER:
 		return (s->opt->dev == c->u.any.arg0);
 	case CTL_OPT_DEV:
@@ -2405,6 +2435,11 @@ ctl_log(struct ctl *c)
 	default:
 		log_puts("unknown");
 	}
+	if (c->display[0] != 0) {
+		log_puts(" (");
+		log_puts(c->display);
+		log_puts(")");
+	}
 }
 
 int
@@ -2467,7 +2502,7 @@ ctl_setval(struct ctl *c, int val)
  */
 struct ctl *
 ctl_new(int scope, void *arg0, void *arg1,
-    int type, char *gstr,
+    int type, char *display, char *gstr,
     char *str0, int unit0, char *func,
     char *str1, int unit1, int maxval, int val)
 {
@@ -2491,6 +2526,7 @@ ctl_new(int scope, void *arg0, void *arg
 	c->type = type;
 	strlcpy(c->func, func, CTL_NAMEMAX);
 	strlcpy(c->group, gstr, CTL_NAMEMAX);
+	strlcpy(c->display, display, CTL_DISPLAYMAX);
 	strlcpy(c->node0.name, str0, CTL_NAMEMAX);
 	c->node0.unit = unit0;
 	if (c->type == CTL_VEC || c->type == CTL_LIST || c->type == CTL_SEL) {
@@ -2637,6 +2673,54 @@ ctl_del(int scope, void *arg0, void *arg
 }
 
 void
+dev_setdisplay(struct dev *d, char *display)
+{
+	struct ctl *c;
+	struct ctlslot *s;
+	int changed, i;
+
+	changed = 0;
+	for (c = ctl_list; c != NULL; c = c->next) {
+		if (c->scope != CTL_OPT_DEV ||
+		    c->u.opt_dev.dev != d ||
+		    strcmp(c->display, display) == 0)
+			continue;
+		strlcpy(c->display, display, CTL_DISPLAYMAX);
+		c->desc_mask = ~0;
+		changed = 1;
+	}
+
+	if (changed) {
+		for (s = ctlslot_array, i = 0; i < DEV_NCTLSLOT; i++, s++) {
+			if (s->ops == NULL)
+				continue;
+			if (s->opt->dev == d)
+				s->ops->sync(s->arg);
+		}
+	}
+}
+
+char *
+dev_getdisplay(struct dev *d)
+{
+	struct ctl *c;
+	char *display;
+
+	display = "";
+	for (c = ctl_list; c != NULL; c = c->next) {
+		if (c->scope == CTL_HW &&
+		    c->u.hw.dev == d &&
+		    c->type == CTL_SEL &&
+		    strcmp(c->group, d->name) == 0 &&
+		    strcmp(c->node0.name, "server") == 0 &&
+		    strcmp(c->func, "device") == 0 &&
+		    c->curval == 1)
+			display = c->display;
+	}
+	return display;
+}
+
+void
 dev_ctlsync(struct dev *d)
 {
 	struct ctl *c;
@@ -2668,7 +2752,7 @@ dev_ctlsync(struct dev *d)
 		}
 		d->master_enabled = 1;
 		ctl_new(CTL_DEV_MASTER, d, NULL,
-		    CTL_NUM, d->name, "output", -1, "level",
+		    CTL_NUM, "", d->name, "output", -1, "level",
 		    NULL, -1, 127, d->master);
 	}
 
Index: usr.bin/sndiod/dev.h
===================================================================
RCS file: /cvs/src/usr.bin/sndiod/dev.h,v
diff -u -p -u -p -r1.44 dev.h
--- usr.bin/sndiod/dev.h	22 Apr 2024 10:39:51 -0000	1.44
+++ usr.bin/sndiod/dev.h	14 May 2024 05:07:43 -0000
@@ -155,9 +155,11 @@ struct ctl {
 	} u;
 
 	unsigned int addr;		/* slot side control address */
-#define CTL_NAMEMAX	16		/* max name length */
+#define CTL_NAMEMAX	12		/* max name length */
+#define CTL_DISPLAYMAX	24		/* max name length */
 	char func[CTL_NAMEMAX];		/* parameter function name */
 	char group[CTL_NAMEMAX];	/* group aka namespace */
+	char display[CTL_DISPLAYMAX];	/* free-format hint */
 	struct ctl_node {
 		char name[CTL_NAMEMAX];	/* stream name */
 		int unit;
@@ -351,7 +353,7 @@ void slot_detach(struct slot *);
  */
 
 struct ctl *ctl_new(int, void *, void *,
-    int, char *, char *, int, char *, char *, int, int, int);
+    int, char *, char *, char *, int, char *, char *, int, int, int);
 int ctl_del(int, void *, void *);
 void ctl_log(struct ctl *);
 int ctl_setval(struct ctl *c, int val);
@@ -367,6 +369,8 @@ struct ctl *ctlslot_lookup(struct ctlslo
 void ctlslot_update(struct ctlslot *);
 
 void dev_label(struct dev *, int);
+void dev_setdisplay(struct dev *, char *);
+char *dev_getdisplay(struct dev *);
 void dev_ctlsync(struct dev *);
 
 #endif /* !defined(DEV_H) */
Index: usr.bin/sndiod/dev_sioctl.c
===================================================================
RCS file: /cvs/src/usr.bin/sndiod/dev_sioctl.c,v
diff -u -p -u -p -r1.8 dev_sioctl.c
--- usr.bin/sndiod/dev_sioctl.c	22 Apr 2024 10:43:55 -0000	1.8
+++ usr.bin/sndiod/dev_sioctl.c	14 May 2024 05:07:43 -0000
@@ -70,7 +70,7 @@ dev_sioctl_ondesc(void *arg, struct sioc
 	}
 
 	ctl_new(CTL_HW, d, &desc->addr,
-	    desc->type, group,
+	    desc->type, desc->display, group,
 	    desc->node0.name, desc->node0.unit, desc->func,
 	    desc->node1.name, desc->node1.unit, desc->maxval, val);
 }
@@ -91,7 +91,8 @@ dev_sioctl_onval(void *arg, unsigned int
 	}
 
 	for (c = ctl_list; c != NULL; c = c->next) {
-		if (c->scope != CTL_HW || c->u.hw.addr != addr)
+		if (c->scope != CTL_HW || c->u.hw.dev != d ||
+		    c->u.hw.addr != addr)
 			continue;
 
 		if (log_level >= 2) {
@@ -103,6 +104,14 @@ dev_sioctl_onval(void *arg, unsigned int
 
 		c->val_mask = ~0U;
 		c->curval = val;
+
+		/* if hardware's server.device changed, update name */
+		if (c->type == CTL_SEL &&
+		    strcmp(c->group, d->name) == 0 &&
+		    strcmp(c->node0.name, "server") == 0 &&
+		    strcmp(c->func, "device") == 0 &&
+		    c->curval == 1)
+			dev_setdisplay(d, c->display);
 	}
 }
 
Index: usr.bin/sndiod/opt.c
===================================================================
RCS file: /cvs/src/usr.bin/sndiod/opt.c,v
diff -u -p -u -p -r1.10 opt.c
--- usr.bin/sndiod/opt.c	22 Apr 2024 10:42:04 -0000	1.10
+++ usr.bin/sndiod/opt.c	14 May 2024 05:07:43 -0000
@@ -346,15 +346,6 @@ opt_del(struct opt *o)
 void
 opt_init(struct opt *o)
 {
-	struct dev *d;
-
-	if (strcmp(o->name, o->dev->name) != 0) {
-		for (d = dev_list; d != NULL; d = d->next) {
-			ctl_new(CTL_OPT_DEV, o, d,
-			    CTL_SEL, o->name, "server", -1, "device",
-			    d->name, -1, 1, o->dev == d);
-		}
-	}
 }
 
 void
@@ -499,6 +490,17 @@ opt_ref(struct opt *o)
 			/* if device changed, move everything to the new one */
 			if (d != o->dev)
 				opt_setdev(o, d);
+
+			/* create server.device control */
+			for (d = dev_list; d != NULL; d = d->next) {
+				d->refcnt++;
+				if (d->pstate == DEV_CFG)
+					dev_open(d);
+				ctl_new(CTL_OPT_DEV, o, d,
+				    CTL_SEL, dev_getdisplay(d),
+				    o->name, "server", -1, "device",
+				    d->name, -1, 1, o->dev == d);
+			}
 		}
 	}
 
@@ -512,7 +514,15 @@ opt_ref(struct opt *o)
 void
 opt_unref(struct opt *o)
 {
+	struct dev *d;
+
 	o->refcnt--;
-	if (o->refcnt == 0)
+	if (o->refcnt == 0) {
+		/* delete server.device control */
+		for (d = dev_list; d != NULL; d = d->next) {
+			if (ctl_del(CTL_OPT_DEV, o, d))
+				dev_unref(d);
+		}
 		dev_unref(o->dev);
+	}
 }
Index: usr.bin/sndiod/sock.c
===================================================================
RCS file: /cvs/src/usr.bin/sndiod/sock.c,v
diff -u -p -u -p -r1.47 sock.c
--- usr.bin/sndiod/sock.c	26 Dec 2022 19:16:03 -0000	1.47
+++ usr.bin/sndiod/sock.c	14 May 2024 05:07:43 -0000
@@ -33,7 +33,7 @@
 #include "sock.h"
 #include "utils.h"
 
-#define SOCK_CTLDESC_SIZE	16	/* number of entries in s->ctldesc */
+#define SOCK_CTLDESC_SIZE	0x800	/* size of s->ctldesc */
 
 void sock_log(struct sock *);
 void sock_close(struct sock *);
@@ -626,8 +626,7 @@ sock_wdata(struct sock *f)
 		else if (f->midi)
 			data = abuf_rgetblk(&f->midi->obuf, &count);
 		else {
-			data = (unsigned char *)f->ctldesc +
-			    (f->wsize - f->wtodo);
+			data = f->ctldesc + (f->wsize - f->wtodo);
 			count = f->wtodo;
 		}
 		if (count > f->wtodo)
@@ -961,8 +960,7 @@ sock_hello(struct sock *f)
 			}
 			return 0;
 		}
-		f->ctldesc = xmalloc(SOCK_CTLDESC_SIZE *
-		    sizeof(struct amsg_ctl_desc));
+		f->ctldesc = xmalloc(SOCK_CTLDESC_SIZE);
 		f->ctlops = 0;
 		f->ctlsyncpending = 0;
 		return 1;
@@ -989,8 +987,10 @@ sock_execmsg(struct sock *f)
 	struct amsg *m = &f->rmsg;
 	unsigned char *data;
 	int size, ctl;
+	int cmd;
 
-	switch (ntohl(m->cmd)) {
+	cmd = ntohl(m->cmd);
+	switch (cmd) {
 	case AMSG_DATA:
 #ifdef DEBUG
 		if (log_level >= 4) {
@@ -1284,6 +1284,7 @@ sock_execmsg(struct sock *f)
 		dev_midi_vol(s->opt->dev, s);
 		ctl_onval(CTL_SLOT_LEVEL, s, NULL, ctl);
 		break;
+	case AMSG_CTLSUB_OLD:
 	case AMSG_CTLSUB:
 #ifdef DEBUG
 		if (log_level >= 3) {
@@ -1316,6 +1317,9 @@ sock_execmsg(struct sock *f)
 				}
 				f->ctlops |= SOCK_CTLDESC;
 				f->ctlsyncpending = 1;
+				f->ctl_desc_size = (cmd == AMSG_CTLSUB) ?
+				    sizeof(struct amsg_ctl_desc) :
+				    AMSG_OLD_DESC_SIZE;
 			}
 		} else
 			f->ctlops &= ~SOCK_CTLDESC;
@@ -1604,7 +1608,6 @@ sock_buildmsg(struct sock *f)
 	 * searching for the {desc,val}_mask bits
 	 */
 	if (f->ctlslot && (f->ctlops & SOCK_CTLDESC)) {
-		desc = f->ctldesc;
 		mask = f->ctlslot->self;
 		size = 0;
 		pc = &ctl_list;
@@ -1614,9 +1617,9 @@ sock_buildmsg(struct sock *f)
 				pc = &c->next;
 				continue;
 			}
-			if (size == SOCK_CTLDESC_SIZE *
-				sizeof(struct amsg_ctl_desc))
+			if (size + f->ctl_desc_size > SOCK_CTLDESC_SIZE)
 				break;
+			desc = (struct amsg_ctl_desc *)(f->ctldesc + size);
 			c->desc_mask &= ~mask;
 			c->val_mask &= ~mask;
 			type = ctlslot_visible(f->ctlslot, c) ?
@@ -1633,8 +1636,14 @@ sock_buildmsg(struct sock *f)
 			desc->addr = htons(c->addr);
 			desc->maxval = htons(c->maxval);
 			desc->curval = htons(c->curval);
-			size += sizeof(struct amsg_ctl_desc);
-			desc++;
+
+			/* old clients don't have the 'display' member */
+			if (f->ctl_desc_size >= offsetof(struct amsg_ctl_desc,
+				display) + AMSG_CTL_DISPLAYMAX) {
+				strlcpy(desc->display, c->display, AMSG_CTL_DISPLAYMAX);
+			}
+
+			size += f->ctl_desc_size;
 
 			/* if this is a deleted entry unref it */
 			if (type == CTL_NONE) {
Index: usr.bin/sndiod/sock.h
===================================================================
RCS file: /cvs/src/usr.bin/sndiod/sock.h,v
diff -u -p -u -p -r1.7 sock.h
--- usr.bin/sndiod/sock.h	26 Apr 2020 14:13:22 -0000	1.7
+++ usr.bin/sndiod/sock.h	14 May 2024 05:07:43 -0000
@@ -59,7 +59,8 @@ struct sock {
 	struct midi *midi;		/* midi endpoint */
 	struct port *port;		/* midi port */
 	struct ctlslot *ctlslot;
-	struct amsg_ctl_desc *ctldesc;	/* temporary buffer */
+	unsigned char *ctldesc;		/* temporary buffer */
+	size_t ctl_desc_size;		/* size of client amsg_ctl_desc */
 #define SOCK_CTLDESC	1		/* dump desc and send changes */
 #define SOCK_CTLVAL	2		/* send value changes */
 	unsigned int ctlops;		/* bitmap of above */
Index: usr.bin/sndioctl/sndioctl.c
===================================================================
RCS file: /cvs/src/usr.bin/sndioctl/sndioctl.c,v
diff -u -p -u -p -r1.20 sndioctl.c
--- usr.bin/sndioctl/sndioctl.c	22 Apr 2024 10:49:01 -0000	1.20
+++ usr.bin/sndioctl/sndioctl.c	14 May 2024 05:07:43 -0000
@@ -47,6 +47,7 @@ int matchpar(struct info *, char *, int)
 int matchent(struct info *, char *, int);
 int ismono(struct info *);
 void print_node(struct sioctl_node *, int);
+void print_display(struct info *);
 void print_desc(struct info *, int);
 void print_num(struct info *);
 void print_ent(struct info *, char *);
@@ -310,6 +311,9 @@ ismono(struct info *g)
 						continue;
 					if (e1->curval != e2->curval)
 						return 0;
+					if (strcmp(e1->desc.display,
+						e2->desc.display) != 0)
+						return 0;
 				}
 			}
 		}
@@ -330,6 +334,28 @@ print_node(struct sioctl_node *c, int mo
 }
 
 /*
+ * print display string, with '(' and ')' and non-printable chars removed
+ * in order to match command syntax
+ */
+void
+print_display(struct info *p)
+{
+	char buf[SIOCTL_NAMEMAX], *s, *d;
+	unsigned int c;
+
+	s = p->desc.display;
+	d = buf;
+	while ((c = *s++) != 0) {
+		if (c == '(' || c == ')' || c < ' ')
+			continue;
+		*d++ = c;
+	}
+	*d = 0;
+	if (buf[0] != 0)
+		printf("(%s)", buf);
+}
+
+/*
  * print info about the parameter
  */
 void
@@ -342,6 +368,7 @@ print_desc(struct info *p, int mono)
 	case SIOCTL_NUM:
 	case SIOCTL_SW:
 		printf("*");
+		print_display(p);
 		break;
 	case SIOCTL_SEL:
 	case SIOCTL_VEC:
@@ -359,6 +386,8 @@ print_desc(struct info *p, int mono)
 			print_node(&e->desc.node1, mono);
 			if (p->desc.type != SIOCTL_SEL)
 				printf(":*");
+			if (e->desc.display[0] != 0)
+				print_display(e);
 			more = 1;
 		}
 	}
@@ -404,6 +433,7 @@ print_ent(struct info *e, char *comment)
 	case SIOCTL_NUM:
 		print_num(e);
 	}
+	print_display(e);
 	if (comment)
 		printf("\t# %s", comment);
 	printf("\n");
@@ -422,6 +452,7 @@ print_val(struct info *p, int mono)
 	case SIOCTL_NUM:
 	case SIOCTL_SW:
 		print_num(p);
+		print_display(p);
 		break;
 	case SIOCTL_SEL:
 	case SIOCTL_VEC:
@@ -439,6 +470,7 @@ print_val(struct info *p, int mono)
 					if (more)
 						printf(",");
 					print_node(&e->desc.node1, mono);
+					print_display(e);
 					more = 1;
 				}
 			} else {
@@ -447,6 +479,7 @@ print_val(struct info *p, int mono)
 				print_node(&e->desc.node1, mono);
 				printf(":");
 				print_num(e);
+				print_display(e);
 				more = 1;
 			}
 		}
@@ -631,6 +664,7 @@ dump(void)
 			print_node(&i->desc.node1, 0);
 			printf(":0..%d (%u)", i->desc.maxval, i->curval);
 		}
+		print_display(i);
 		printf("\n");
 	}
 }
@@ -751,6 +785,12 @@ cmd(char *line)
 						e->mode = mode;
 						nent++;
 					}
+				}
+			}
+			if (*pos == '(') {
+				while (*pos != 0) {
+					if (*pos++ == ')')
+						break;
 				}
 			}
 			if (nent == 0) {