1 /**
2 * \file control/eld.c
3 * \brief ELD decoder
4 * \author Jaroslav Kysela <perex@perex>
5 * \date 2022
6 */
7 /*
8 * Control Interface - Decode ELD
9 *
10 * Copyright (c) 2022 Jaroslav Kysela <perex@perex.cz>
11 *
12 *
13 * This library is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU Lesser General Public License as
15 * published by the Free Software Foundation; either version 2.1 of
16 * the License, or (at your option) any later version.
17 *
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License for more details.
22 *
23 * You should have received a copy of the GNU Lesser General Public
24 * License along with this library; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
26 *
27 */
28
29 #include "control_local.h"
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <unistd.h>
33 #include <string.h>
34 #include <ctype.h>
35
__fill_eld_ctl_id(snd_ctl_elem_id_t * id,int dev,int subdev)36 static void __fill_eld_ctl_id(snd_ctl_elem_id_t *id, int dev, int subdev)
37 {
38 snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_PCM);
39 snd_ctl_elem_id_set_name(id, "ELD");
40 snd_ctl_elem_id_set_device(id, dev);
41 snd_ctl_elem_id_set_index(id, subdev);
42 }
43
__snd_pcm_info_eld_fixup(snd_pcm_info_t * info)44 int __snd_pcm_info_eld_fixup(snd_pcm_info_t * info)
45 {
46 snd_ctl_t *ctl;
47 snd_ctl_elem_info_t cinfo = {0};
48 snd_ctl_elem_value_t value = {0};
49 unsigned char *eld;
50 unsigned int l, spc;
51 char *s, c;
52 int ret, valid;
53
54 ret = snd_ctl_hw_open(&ctl, NULL, info->card, 0);
55 if (ret < 0) {
56 SYSMSG("Cannot open the associated CTL");
57 return ret;
58 }
59
60 __fill_eld_ctl_id(&cinfo.id, info->device, info->subdevice);
61 value.id = cinfo.id;
62 ret = snd_ctl_elem_info(ctl, &cinfo);
63 if (ret >= 0 && cinfo.type == SND_CTL_ELEM_TYPE_BYTES)
64 ret = snd_ctl_elem_read(ctl, &value);
65 snd_ctl_close(ctl);
66 if (ret == -ENOENT || cinfo.type != SND_CTL_ELEM_TYPE_BYTES || cinfo.count == 0)
67 return 0;
68 if (ret < 0) {
69 SYSMSG("Cannot read ELD");
70 return ret;
71 }
72 /* decode connected HDMI device name */
73 eld = value.value.bytes.data;
74 if (cinfo.count < 20 || cinfo.count > 256)
75 return -EIO;
76 l = eld[4] & 0x1f;
77 if (l == 0)
78 /* no monitor name detected */
79 goto __present;
80 if (l > 16 || 20 + l > cinfo.count) {
81 SNDERR("ELD decode failed, using old HDMI output names");
82 return 0;
83 }
84 s = alloca(l + 1);
85 /* sanitize */
86 valid = 0;
87 spc = 0;
88 while (l > 0) {
89 l--;
90 c = eld[20 + l];
91 if (c <= ' ' || c >= 0x7f) {
92 s[l] = ' ';
93 } else {
94 valid += !!isalnum(c);
95 s[l] = c;
96 if (spc == 0)
97 spc = l + 1;
98 }
99 }
100 if (valid > 3) {
101 s[spc] = '\0';
102 snd_strlcpy((char *)info->name, s, sizeof(info->name));
103 } else {
104 __present:
105 strncat((char *)info->name, " *", sizeof(info->name) - 1);
106 ((char *)info->name)[sizeof(info->name)-1] = '\0';
107 }
108 return 0;
109 }
110