From: Alexandre Ratchov Subject: Expose better display names for audio(4) devices To: tech@openbsd.org Date: Sun, 13 Jul 2025 18:37:28 +0200 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