Add support for TLVs from the FCP driver
Decode level meter labels and the FCP socket location from TLVs.
This commit is contained in:
64
src/alsa.c
64
src/alsa.c
@@ -9,7 +9,12 @@
|
|||||||
#include "stringhelper.h"
|
#include "stringhelper.h"
|
||||||
#include "window-iface.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
|
// names for the port categories
|
||||||
const char *port_category_names[PC_COUNT] = {
|
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) {
|
static void alsa_get_elem_tlv(struct alsa_elem *elem) {
|
||||||
|
struct alsa_card *card = elem->card;
|
||||||
|
|
||||||
if (elem->type != SND_CTL_ELEM_TYPE_INTEGER)
|
if (elem->type != SND_CTL_ELEM_TYPE_INTEGER)
|
||||||
return;
|
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_alloca(&elem_info);
|
||||||
snd_ctl_elem_info_set_numid(elem_info, elem->numid);
|
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))
|
if (!snd_ctl_elem_info_is_tlv_readable(elem_info))
|
||||||
return;
|
return;
|
||||||
@@ -368,13 +375,59 @@ static void alsa_get_elem_tlv(struct alsa_elem *elem) {
|
|||||||
snd_ctl_elem_id_set_numid(elem_id, elem->numid);
|
snd_ctl_elem_id_set_numid(elem_id, elem->numid);
|
||||||
|
|
||||||
ret = snd_ctl_elem_tlv_read(
|
ret = snd_ctl_elem_tlv_read(
|
||||||
elem->card->handle, elem_id, tlv, sizeof(tlv)
|
card->handle, elem_id, tlv, sizeof(tlv)
|
||||||
);
|
);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
fprintf(stderr, "TLV read error: %s\n", snd_strerror(ret));
|
fprintf(stderr, "TLV read error: %s\n", snd_strerror(ret));
|
||||||
return;
|
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);
|
ret = snd_tlv_parse_dB_info(tlv, sizeof(tlv), &dbrec);
|
||||||
if (ret <= 0) {
|
if (ret <= 0) {
|
||||||
fprintf(stderr, "TLV parse error: %s\n", snd_strerror(ret));
|
fprintf(stderr, "TLV parse error: %s\n", snd_strerror(ret));
|
||||||
@@ -384,7 +437,9 @@ static void alsa_get_elem_tlv(struct alsa_elem *elem) {
|
|||||||
int min_val = snd_ctl_elem_info_get_min(elem_info);
|
int min_val = snd_ctl_elem_info_get_min(elem_info);
|
||||||
int max_val = snd_ctl_elem_info_get_max(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);
|
ret = snd_tlv_get_dB_range(
|
||||||
|
dbrec, min_val, max_val, &min_cdB, &max_cdB
|
||||||
|
);
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
fprintf(stderr, "TLV range error: %s\n", snd_strerror(ret));
|
fprintf(stderr, "TLV range error: %s\n", snd_strerror(ret));
|
||||||
return;
|
return;
|
||||||
@@ -396,6 +451,7 @@ static void alsa_get_elem_tlv(struct alsa_elem *elem) {
|
|||||||
elem->min_cdB = min_cdB;
|
elem->min_cdB = min_cdB;
|
||||||
elem->max_cdB = max_cdB;
|
elem->max_cdB = max_cdB;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void alsa_get_elem(struct alsa_card *card, int numid) {
|
static void alsa_get_elem(struct alsa_card *card, int numid) {
|
||||||
// allocate a temporary struct alsa_elem (will be copied later if
|
// allocate a temporary struct alsa_elem (will be copied later if
|
||||||
|
|||||||
@@ -133,6 +133,9 @@ struct alsa_elem {
|
|||||||
int min_cdB;
|
int min_cdB;
|
||||||
int max_cdB;
|
int max_cdB;
|
||||||
|
|
||||||
|
// level meter labels
|
||||||
|
char **meter_labels;
|
||||||
|
|
||||||
// for routing sinks
|
// for routing sinks
|
||||||
int is_routing_snk;
|
int is_routing_snk;
|
||||||
int port_category;
|
int port_category;
|
||||||
@@ -159,6 +162,7 @@ struct alsa_card {
|
|||||||
uint32_t pid;
|
uint32_t pid;
|
||||||
char *serial;
|
char *serial;
|
||||||
char *name;
|
char *name;
|
||||||
|
char *fcp_socket;
|
||||||
int best_firmware_version;
|
int best_firmware_version;
|
||||||
snd_ctl_t *handle;
|
snd_ctl_t *handle;
|
||||||
struct pollfd pfd;
|
struct pollfd pfd;
|
||||||
|
|||||||
Reference in New Issue
Block a user