]> git.alsa-project.org Git - alsa-lib.git/commitdiff
control: decode HDMI device name from ELD
authorJaroslav Kysela <perex@perex.cz>
Thu, 5 May 2022 12:28:41 +0000 (14:28 +0200)
committerJaroslav Kysela <perex@perex.cz>
Thu, 5 May 2022 12:31:45 +0000 (14:31 +0200)
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 <perex@perex.cz>
src/control/Makefile.am
src/control/control_hw.c
src/control/control_local.h
src/control/eld.c [new file with mode: 0644]
src/pcm/pcm_hw.c

index eb66fa50376da33446b5509dda311941b690878d..d1ff1fd8af732f5f398205c6df16f84ecc985014 100644 (file)
@@ -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
index 680f0fecdc0ee90de65d59a05d05ed85f3c79c49..0ed9f6b229236fe073c2ef286508cccd147a5c81 100644 (file)
@@ -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;
 }
 
index b3f6ee79274bf0fba353d223ddc420700568cc3e..973fa04c145d87e4c605a7929729adba998e5944 100644 (file)
@@ -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 (file)
index 0000000..c4169c2
--- /dev/null
@@ -0,0 +1,95 @@
+/**
+ * \file control/eld.c
+ * \brief ELD decoder
+ * \author Jaroslav Kysela <perex@perex>
+ * \date 2022
+ */
+/*
+ *  Control Interface - Decode ELD
+ *
+ *  Copyright (c) 2022 Jaroslav Kysela <perex@perex.cz>
+ *
+ *
+ *   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 <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#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;
+}
index b3f9d1579d2978b51b4902872e95b09e8891d9c2..bd93fa37680183d310e5bcad9308bcdcec8d4ff8 100644 (file)
@@ -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;
 }