/** * \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 "control_local.h" #include #include #include #include #include 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, spc; 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"); 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"); 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) /* no monitor name detected */ goto __present; if (l > 16 || 20 + l > cinfo.count) { SNDERR("ELD decode failed, using old HDMI output names"); return 0; } s = alloca(l + 1); /* sanitize */ valid = 0; spc = 0; while (l > 0) { l--; c = eld[20 + l]; if (c <= ' ' || c >= 0x7f) { s[l] = ' '; } else { valid += !!isalnum(c); s[l] = c; if (spc == 0) spc = l + 1; } } if (valid > 3) { s[spc] = '\0'; snd_strlcpy((char *)info->name, s, sizeof(info->name)); } else { __present: strncat((char *)info->name, " *", sizeof(info->name) - 1); ((char *)info->name)[sizeof(info->name)-1] = '\0'; } return 0; }