From b41a47587b9cbe27cf99a534e99691547ffd9d1b Mon Sep 17 00:00:00 2001 From: "Geoffrey D. Bennett" Date: Thu, 20 Feb 2025 23:58:32 +1030 Subject: [PATCH] Add support for TLVs from the FCP driver Decode level meter labels and the FCP socket location from TLVs. --- src/alsa.c | 100 +++++++++++++++++++++++++++++++++++++++++------------ src/alsa.h | 4 +++ 2 files changed, 82 insertions(+), 22 deletions(-) diff --git a/src/alsa.c b/src/alsa.c index 8038374..2d0684a 100644 --- a/src/alsa.c +++ b/src/alsa.c @@ -9,7 +9,12 @@ #include "stringhelper.h" #include "window-iface.h" -#define MAX_TLV_RANGE_SIZE 256 +#define MAX_TLV_RANGE_SIZE 1024 + +// TLV type for channel labels +#ifndef SNDRV_CTL_TLVT_FCP_CHANNEL_LABELS +#define SNDRV_CTL_TLVT_FCP_CHANNEL_LABELS 0x110 +#endif // names for the port categories const char *port_category_names[PC_COUNT] = { @@ -346,6 +351,8 @@ char *alsa_get_item_name(struct alsa_elem *elem, int i) { // static void alsa_get_elem_tlv(struct alsa_elem *elem) { + struct alsa_card *card = elem->card; + if (elem->type != SND_CTL_ELEM_TYPE_INTEGER) return; @@ -353,7 +360,7 @@ static void alsa_get_elem_tlv(struct alsa_elem *elem) { snd_ctl_elem_info_alloca(&elem_info); snd_ctl_elem_info_set_numid(elem_info, elem->numid); - snd_ctl_elem_info(elem->card->handle, elem_info); + snd_ctl_elem_info(card->handle, elem_info); if (!snd_ctl_elem_info_is_tlv_readable(elem_info)) return; @@ -368,33 +375,82 @@ static void alsa_get_elem_tlv(struct alsa_elem *elem) { snd_ctl_elem_id_set_numid(elem_id, elem->numid); ret = snd_ctl_elem_tlv_read( - elem->card->handle, elem_id, tlv, sizeof(tlv) + card->handle, elem_id, tlv, sizeof(tlv) ); if (ret < 0) { fprintf(stderr, "TLV read error: %s\n", snd_strerror(ret)); return; } - ret = snd_tlv_parse_dB_info(tlv, sizeof(tlv), &dbrec); - if (ret <= 0) { - fprintf(stderr, "TLV parse error: %s\n", snd_strerror(ret)); - return; + // meter map + if (tlv[SNDRV_CTL_TLVO_TYPE] == SNDRV_CTL_TLVT_FCP_CHANNEL_LABELS) { + int label_data_size = tlv[SNDRV_CTL_TLVO_LEN]; + char *label_data = (char *)&tlv[SNDRV_CTL_TLVO_LEN + 1]; + + // check that there are at least elem->count labels in the data + int label_count = 0; + for (int i = 0; i < label_data_size; i++) { + if (!label_data[i]) + label_count++; + } + + if (label_count < elem->count) { + fprintf( + stderr, + "TLV label count %d < %d\n", + label_count, + elem->count + ); + return; + } + + if (elem->count < 0 || elem->count > 255) { + fprintf(stderr, "TLV label count %d out of range\n", elem->count); + exit(1); + } + + elem->meter_labels = calloc(elem->count, sizeof(char *)); + + char *cur_label = label_data; + for (int i = 0; i < elem->count; i++) { + elem->meter_labels[i] = strdup(cur_label); + if (!elem->meter_labels[i]) { + fprintf(stderr, "strdup failed\n"); + exit(1); + } + + cur_label += strlen(cur_label) + 1; + } + + /* firmware version TLV contains socket location */ + } else if (tlv[SNDRV_CTL_TLVO_TYPE] == 0x53434B54) { + card->fcp_socket = strdup((char *)&tlv[SNDRV_CTL_TLVO_LEN + 1]); + + /* dB range */ + } else { + ret = snd_tlv_parse_dB_info(tlv, sizeof(tlv), &dbrec); + if (ret <= 0) { + fprintf(stderr, "TLV parse error: %s\n", snd_strerror(ret)); + return; + } + + int min_val = snd_ctl_elem_info_get_min(elem_info); + int max_val = snd_ctl_elem_info_get_max(elem_info); + + ret = snd_tlv_get_dB_range( + dbrec, min_val, max_val, &min_cdB, &max_cdB + ); + if (ret != 0) { + fprintf(stderr, "TLV range error: %s\n", snd_strerror(ret)); + return; + } + + elem->min_val = min_val; + elem->max_val = max_val; + elem->dB_type = dbrec[SNDRV_CTL_TLVO_TYPE]; + elem->min_cdB = min_cdB; + elem->max_cdB = max_cdB; } - - int min_val = snd_ctl_elem_info_get_min(elem_info); - int max_val = snd_ctl_elem_info_get_max(elem_info); - - ret = snd_tlv_get_dB_range(dbrec, min_val, max_val, &min_cdB, &max_cdB); - if (ret != 0) { - fprintf(stderr, "TLV range error: %s\n", snd_strerror(ret)); - return; - } - - elem->min_val = min_val; - elem->max_val = max_val; - elem->dB_type = dbrec[SNDRV_CTL_TLVO_TYPE]; - elem->min_cdB = min_cdB; - elem->max_cdB = max_cdB; } static void alsa_get_elem(struct alsa_card *card, int numid) { diff --git a/src/alsa.h b/src/alsa.h index a350965..e7548ef 100644 --- a/src/alsa.h +++ b/src/alsa.h @@ -133,6 +133,9 @@ struct alsa_elem { int min_cdB; int max_cdB; + // level meter labels + char **meter_labels; + // for routing sinks int is_routing_snk; int port_category; @@ -159,6 +162,7 @@ struct alsa_card { uint32_t pid; char *serial; char *name; + char *fcp_socket; int best_firmware_version; snd_ctl_t *handle; struct pollfd pfd;