From 859448f01033b40feebf9a0aab467ba57a655b5b Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Thu, 5 May 2022 14:28:41 +0200 Subject: [PATCH] control: decode HDMI device name from ELD The HDMI drivers set an uniform PCM names. Use ELD (EDID) to obtain the HDMI device name and send this string to applications for a better user experience. Example (aplay -l): card 1: PCH [HDA Intel PCH], device 8: HDMI 2 [HDMI 2] Subdevices: 1/1 vs improved: card 1: PCH [HDA Intel PCH], device 8: HDMI 2 [Philips 272P4] Subdevices: 1/1 Fixes: https://github.com/alsa-project/alsa-lib/issues/209 Signed-off-by: Jaroslav Kysela --- src/control/Makefile.am | 2 +- src/control/control_hw.c | 3 ++ src/control/control_local.h | 9 ++++ src/control/eld.c | 95 +++++++++++++++++++++++++++++++++++++ src/pcm/pcm_hw.c | 3 ++ 5 files changed, 111 insertions(+), 1 deletion(-) create mode 100644 src/control/eld.c diff --git a/src/control/Makefile.am b/src/control/Makefile.am index eb66fa50..d1ff1fd8 100644 --- a/src/control/Makefile.am +++ b/src/control/Makefile.am @@ -1,6 +1,6 @@ EXTRA_LTLIBRARIES = libcontrol.la -libcontrol_la_SOURCES = cards.c tlv.c namehint.c hcontrol.c \ +libcontrol_la_SOURCES = cards.c tlv.c eld.c namehint.c hcontrol.c \ control.c control_hw.c control_empty.c \ setup.c ctlparse.c \ control_plugin.c control_symbols.c diff --git a/src/control/control_hw.c b/src/control/control_hw.c index 680f0fec..0ed9f6b2 100644 --- a/src/control/control_hw.c +++ b/src/control/control_hw.c @@ -287,6 +287,9 @@ static int snd_ctl_hw_pcm_info(snd_ctl_t *handle, snd_pcm_info_t * info) snd_ctl_hw_t *hw = handle->private_data; if (ioctl(hw->fd, SNDRV_CTL_IOCTL_PCM_INFO, info) < 0) return -errno; + /* may be configurable (optional) */ + if (__snd_pcm_info_eld_fixup_check(info)) + return __snd_pcm_info_eld_fixup(info); return 0; } diff --git a/src/control/control_local.h b/src/control/control_local.h index b3f6ee79..973fa04c 100644 --- a/src/control/control_local.h +++ b/src/control/control_local.h @@ -124,3 +124,12 @@ int __snd_ctl_add_elem_set(snd_ctl_t *ctl, snd_ctl_elem_info_t *info, int __snd_ctl_ascii_elem_id_parse(snd_ctl_elem_id_t *dst, const char *str, const char **ret_ptr); + +static inline int +__snd_pcm_info_eld_fixup_check(snd_pcm_info_t *info) +{ + return info->stream == SND_PCM_STREAM_PLAYBACK && + strncmp((char *)info->name, "HDMI ", 5) == 0; +} + +int __snd_pcm_info_eld_fixup(snd_pcm_info_t *info); diff --git a/src/control/eld.c b/src/control/eld.c new file mode 100644 index 00000000..c4169c2f --- /dev/null +++ b/src/control/eld.c @@ -0,0 +1,95 @@ +/** + * \file control/eld.c + * \brief ELD decoder + * \author Jaroslav Kysela + * \date 2022 + */ +/* + * Control Interface - Decode ELD + * + * Copyright (c) 2022 Jaroslav Kysela + * + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include +#include +#include +#include +#include "control_local.h" + +static void __fill_eld_ctl_id(snd_ctl_elem_id_t *id, int dev, int subdev) +{ + snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_PCM); + snd_ctl_elem_id_set_name(id, "ELD"); + snd_ctl_elem_id_set_device(id, dev); + snd_ctl_elem_id_set_index(id, subdev); +} + +int __snd_pcm_info_eld_fixup(snd_pcm_info_t * info) +{ + snd_ctl_t *ctl; + snd_ctl_elem_info_t cinfo = {0}; + snd_ctl_elem_value_t value = {0}; + unsigned char *eld; + unsigned int l; + char *s, c; + int ret, valid; + + ret = snd_ctl_hw_open(&ctl, NULL, info->card, 0); + if (ret < 0) { + SYSMSG("Cannot open the associated CTL\n"); + return ret; + } + + __fill_eld_ctl_id(&cinfo.id, info->device, info->subdevice); + value.id = cinfo.id; + ret = snd_ctl_elem_info(ctl, &cinfo); + if (ret >= 0 && cinfo.type == SND_CTL_ELEM_TYPE_BYTES) + ret = snd_ctl_elem_read(ctl, &value); + snd_ctl_close(ctl); + if (ret == -ENOENT || cinfo.type != SND_CTL_ELEM_TYPE_BYTES || cinfo.count == 0) + return 0; + if (ret < 0) { + SYSMSG("Cannot read ELD\n"); + return ret; + } + /* decode connected HDMI device name */ + eld = value.value.bytes.data; + if (cinfo.count < 20 || cinfo.count > 256) + return -EIO; + l = eld[4] & 0x1f; + if (l == 0 || l > 16 || 20 + l > cinfo.count) + return -EIO; + s = alloca(l + 1); + s[l] = '\0'; + /* sanitize */ + valid = 0; + while (l > 0) { + l--; + c = eld[20 + l]; + if (c < ' ' || c >= 0x7f) { + s[l] = ' '; + } else { + valid += !!isalnum(c); + s[l] = c; + } + } + if (valid > 3) + snd_strlcpy((char *)info->name, s, sizeof(info->name)); + return 0; +} diff --git a/src/pcm/pcm_hw.c b/src/pcm/pcm_hw.c index b3f9d157..bd93fa37 100644 --- a/src/pcm/pcm_hw.c +++ b/src/pcm/pcm_hw.c @@ -320,6 +320,9 @@ static int snd_pcm_hw_info(snd_pcm_t *pcm, snd_pcm_info_t * info) SYSMSG("SNDRV_PCM_IOCTL_INFO failed (%i)", err); return err; } + /* may be configurable (optional) */ + if (__snd_pcm_info_eld_fixup_check(info)) + return __snd_pcm_info_eld_fixup(info); return 0; } -- 2.47.3