Download raw body.
Expose better display names for audio(4) devices
If there are multiple uaudio(4) devices, it is difficult to know which
sndioctl(1) selector corresponds to which hardware interface without
searching in the dmesg output. Example:
$ sndioctl -i server.device
server.device=0(azalia0),1(envy0),2(cmpci0),3(uaudio0),4(uaudio1),5(uaudio2),6,7
The diff below makes the kernel expose a slightly more useful string
instead of the device->dv_xname as the device display name. Ex., the
product name for uaudio(4), the codec type for azalia(4), the card
name for envy(4) and cmpci(4). The result:
$ sndioctl -i server.device
server.device=0(HDA: Analog),1(ESI Julia),2(CMI8738),3(U-24),4(Webcam C310),5(AudioBox Go),6,7
These are short strings mainly intended for GUIs or as simple hints.
OK?
Index: share/man/man9/audio.9
===================================================================
RCS file: /cvs/src/share/man/man9/audio.9,v
diff -u -p -r1.35 audio.9
--- share/man/man9/audio.9 15 Oct 2023 15:49:47 -0000 1.35
+++ share/man/man9/audio.9 8 Jul 2025 17:00:51 -0000
@@ -79,6 +79,7 @@ struct audio_hw_if {
struct audio_params *, struct audio_params *, int);
int (*set_nblks)(void *, int, int,
struct audio_params *, int);
+ size_t (*display_name)(void *, char *, size_t);
};
struct audio_params {
@@ -453,6 +454,22 @@ function.
is the desired number of blocks in the ring buffer.
It may be lowered to at least two, to match hardware constraints.
This function returns the adjusted number of blocks.
+.It Ft size_t Fn (*display_name) "void *hdl" "char *buf" "size_t size"
+This function produces a string suitable for user interfaces and
+intended to help the user to identify the hardware.
+Like
+.Xr snprintf 3 ,
+this function returns the total length of the string it tried to create
+(excluding the final
+.Ql \e0 ) .
+Unless
+.Fa size
+is 0, it writes at most
+.Fa size Ns \-1
+characters followed by a terminating
+.Ql \e0
+at the location pointed by
+.Fa buf .
.El
.Pp
If the audio hardware is capable of input from more
Index: sys/dev/audio.c
===================================================================
RCS file: /cvs/src/sys/dev/audio.c,v
diff -u -p -r1.211 audio.c
--- sys/dev/audio.c 14 Feb 2025 13:29:00 -0000 1.211
+++ sys/dev/audio.c 8 Jul 2025 17:00:52 -0000
@@ -1735,12 +1735,22 @@ audio_write(struct audio_softc *sc, stru
}
int
-audio_getdev(struct audio_softc *sc, struct audio_device *adev)
+audio_getdev(struct audio_softc *sc, struct audio_device *p)
{
- memset(adev, 0, sizeof(struct audio_device));
- if (sc->dev.dv_parent == NULL)
- return EIO;
- strlcpy(adev->name, sc->dev.dv_parent->dv_xname, MAX_AUDIO_DEV_LEN);
+ size_t sz;
+
+ memset(p, 0, sizeof(struct audio_device));
+ sz = 0;
+
+ if (sc->ops->display_name)
+ sz = sc->ops->display_name(sc->arg, p->name, sizeof(p->name));
+
+ if (sz == 0) {
+ if (sc->dev.dv_parent == NULL)
+ return EIO;
+ strlcpy(p->name, sc->dev.dv_parent->dv_xname, sizeof(p->name));
+ }
+
return 0;
}
Index: sys/dev/audio_if.h
===================================================================
RCS file: /cvs/src/sys/dev/audio_if.h,v
diff -u -p -r1.42 audio_if.h
--- sys/dev/audio_if.h 2 Nov 2022 10:41:34 -0000 1.42
+++ sys/dev/audio_if.h 8 Jul 2025 17:00:52 -0000
@@ -125,6 +125,7 @@ struct audio_hw_if {
struct audio_params *, struct audio_params *, unsigned int);
unsigned int (*set_nblks)(void *, int,
struct audio_params *, unsigned int, unsigned int);
+ size_t (*display_name)(void *, char *, size_t);
};
struct audio_attach_args {
Index: sys/dev/pci/azalia.c
===================================================================
RCS file: /cvs/src/sys/dev/pci/azalia.c,v
diff -u -p -r1.290 azalia.c
--- sys/dev/pci/azalia.c 18 Aug 2024 14:42:56 -0000 1.290
+++ sys/dev/pci/azalia.c 8 Jul 2025 17:00:54 -0000
@@ -269,6 +269,7 @@ int azalia_trigger_output(void *, void *
void (*)(void *), void *, audio_params_t *);
int azalia_trigger_input(void *, void *, void *, int,
void (*)(void *), void *, audio_params_t *);
+size_t azalia_display_name(void *, char *, size_t);
int azalia_params2fmt(const audio_params_t *, uint16_t *);
@@ -305,6 +306,7 @@ const struct audio_hw_if azalia_hw_if =
.trigger_input = azalia_trigger_input,
.set_blksz = azalia_set_blksz,
.set_nblks = azalia_set_nblks,
+ .display_name = azalia_display_name,
};
static const char *pin_devices[16] = {
@@ -4208,6 +4210,30 @@ azalia_trigger_input(void *v, void *star
az->rstream.swpos = 0;
return azalia_stream_start(&az->rstream);
+}
+
+size_t
+azalia_display_name(void *self, char *buf, size_t size)
+{
+ azalia_t *az = (azalia_t *)self;
+ codec_t *codec = &az->codecs[az->codecno];
+ const char *name;
+
+ switch (codec->codec_type) {
+ case AZ_CODEC_TYPE_ANALOG:
+ name = "HDA: Analog";
+ break;
+ case AZ_CODEC_TYPE_DIGITAL:
+ name = "HDA: Digital";
+ break;
+ case AZ_CODEC_TYPE_HDMI:
+ name = "HDA: HDMI";
+ break;
+ default:
+ return 0;
+ }
+
+ return strlcpy(buf, name, size);
}
/* --------------------------------
Index: sys/dev/pci/cmpci.c
===================================================================
RCS file: /cvs/src/sys/dev/pci/cmpci.c,v
diff -u -p -r1.54 cmpci.c
--- sys/dev/pci/cmpci.c 24 May 2024 06:02:53 -0000 1.54
+++ sys/dev/pci/cmpci.c 8 Jul 2025 17:00:54 -0000
@@ -145,6 +145,7 @@ int cmpci_trigger_output(void *, void *,
int cmpci_trigger_input(void *, void *, void *, int,
void (*)(void *), void *,
struct audio_params *);
+size_t cmpci_display_name(void *, char *, size_t);
const struct audio_hw_if cmpci_hw_if = {
.open = cmpci_open,
@@ -161,6 +162,7 @@ const struct audio_hw_if cmpci_hw_if = {
.round_buffersize = cmpci_round_buffersize,
.trigger_output = cmpci_trigger_output,
.trigger_input = cmpci_trigger_input,
+ .display_name = cmpci_display_name,
};
/*
@@ -1860,6 +1862,31 @@ cmpci_trigger_input(void *handle, void *
cmpci_reg_set_4(sc, CMPCI_REG_FUNC_0, CMPCI_REG_CH1_ENABLE);
mtx_leave(&audio_lock);
return 0;
+}
+
+size_t
+cmpci_display_name(void *self, char *buf, size_t size)
+{
+ struct cmpci_softc *sc = (struct cmpci_softc *)self;
+ const char *name;
+
+ switch (PCI_PRODUCT(sc->sc_id)) {
+ case PCI_PRODUCT_CMI_CMI8338A:
+ name = "CMI8338A";
+ break;
+ case PCI_PRODUCT_CMI_CMI8338B:
+ name = "CMI8338B";
+ break;
+ case PCI_PRODUCT_CMI_CMI8738:
+ name = "CMI8738";
+ break;
+ case PCI_PRODUCT_CMI_CMI8738B:
+ name = "CMI8738B";
+ break;
+ default:
+ name = sc->sc_dev.dv_xname;
+ }
+ return strlcpy(buf, name, size);
}
/* end of file */
Index: sys/dev/pci/envy.c
===================================================================
RCS file: /cvs/src/sys/dev/pci/envy.c,v
diff -u -p -r1.88 envy.c
--- sys/dev/pci/envy.c 24 May 2024 06:02:53 -0000 1.88
+++ sys/dev/pci/envy.c 8 Jul 2025 17:00:55 -0000
@@ -110,6 +110,7 @@ int envy_halt_input(void *);
int envy_query_devinfo(void *, struct mixer_devinfo *);
int envy_get_port(void *, struct mixer_ctrl *);
int envy_set_port(void *, struct mixer_ctrl *);
+size_t envy_display_name(void *, char *, size_t);
#if NMIDI > 0
int envy_midi_open(void *, int, void (*)(void *, int),
void (*)(void *), void *);
@@ -191,6 +192,7 @@ const struct audio_hw_if envy_hw_if = {
.freem = envy_freem,
.trigger_output = envy_trigger_output,
.trigger_input = envy_trigger_input,
+ .display_name = envy_display_name,
};
#if NMIDI > 0
@@ -2426,6 +2428,14 @@ envy_set_port(void *self, struct mixer_c
if (idx < ndev)
return sc->card->dac->set(sc, ctl, idx);
return ENXIO;
+}
+
+size_t
+envy_display_name(void *self, char *buf, size_t size)
+{
+ struct envy_softc *sc = (struct envy_softc *)self;
+
+ return strlcpy(buf, sc->card->name, size);
}
#if NMIDI > 0
Index: sys/dev/usb/uaudio.c
===================================================================
RCS file: /cvs/src/sys/dev/usb/uaudio.c,v
diff -u -p -r1.178 uaudio.c
--- sys/dev/usb/uaudio.c 7 Jan 2025 12:49:40 -0000 1.178
+++ sys/dev/usb/uaudio.c 8 Jul 2025 17:00:59 -0000
@@ -212,6 +212,7 @@ struct uaudio_softc {
struct device dev;
struct usbd_device *udev;
int version;
+ int instnum;
/*
* UAC exposes the device as a circuit of units. Input and
@@ -442,6 +443,7 @@ int uaudio_halt_input(void *);
int uaudio_query_devinfo(void *, struct mixer_devinfo *);
int uaudio_get_port(void *, struct mixer_ctrl *);
int uaudio_set_port(void *, struct mixer_ctrl *);
+size_t uaudio_display_name(void *, char *, size_t);
int uaudio_process_unit(struct uaudio_softc *,
struct uaudio_unit *, int,
@@ -493,6 +495,7 @@ const struct audio_hw_if uaudio_hw_if =
.copy_output = uaudio_copy_output,
.underrun = uaudio_underrun,
.set_blksz = uaudio_set_blksz,
+ .display_name = uaudio_display_name,
};
/*
@@ -2800,6 +2803,7 @@ uaudio_process_conf(struct uaudio_softc
i = uaudio_iface_index(sc, ifnum);
if (i != -1 && usbd_iface_claimed(sc->udev, i)) {
DPRINTF("%s: %d: AC already claimed\n", __func__, ifnum);
+ sc->instnum++;
break;
}
if (sc->unit_list != NULL) {
@@ -4480,6 +4484,38 @@ uaudio_set_port(void *arg, struct mixer_
rc = uaudio_set_port_do(sc, ctl);
usbd_ref_decr(sc->udev);
return rc;
+}
+
+size_t
+uaudio_display_name(void *arg, char *buf, size_t size)
+{
+ struct uaudio_softc *sc = arg;
+ char *vendor = sc->udev->vendor;
+ char *product = sc->udev->product;
+ size_t i;
+
+ if (product == NULL || vendor == NULL)
+ return strlcpy(buf, DEVNAME(sc), size);
+
+ /*
+ * Certain devices prefix the product name with the vendor name,
+ * drop the prefix
+ */
+ for (i = 0; product[i] != 0; i++) {
+ if (vendor[i] == 0) {
+ while (product[i] == ' ')
+ i++;
+ product += i;
+ break;
+ }
+ if (vendor[i] != product[i])
+ break;
+ }
+
+ if (sc->instnum > 0)
+ return snprintf(buf, size, "%s#%u", product, sc->instnum + 1);
+ else
+ return strlcpy(buf, product, size);
}
int
Expose better display names for audio(4) devices