#include "../include/asoundlib.h"
#include <sound/tlv.h>
+#include <stdbool.h>
struct elem_set_trial {
snd_ctl_t *handle;
snd_ctl_elem_value_t *elem_data);
int (*allocate_elem_set_tlv)(struct elem_set_trial *trial,
unsigned int **tlv);
+
+ bool tlv_readable;
};
struct chmap_entry {
continue;
/*
* I expect each event is generated separately to the same
- * element.
+ * element or several events are generated at once.
*/
- if (!(snd_ctl_event_elem_get_mask(event) & mask))
+ if ((snd_ctl_event_elem_get_mask(event) & mask) != mask)
continue;
--expected_count;
}
if (!snd_ctl_elem_info_is_locked(info))
return -EIO;
+ /*
+ * In initial state, any application can register TLV data for
+ * user-defined element set except for IEC 958 type, thus
+ * elements in any user-defined set should allow any write
+ * operation.
+ */
+ if (trial->type != SND_CTL_ELEM_TYPE_IEC958 &&
+ !snd_ctl_elem_info_is_tlv_writable(info))
+ return -EIO;
+
/* Check type-specific properties. */
if (trial->check_elem_props != NULL) {
err = trial->check_elem_props(trial, info);
err = snd_ctl_elem_unlock(trial->handle, id);
if (err < 0)
return err;
+
+ /*
+ * Till kernel v4.14, ALSA control core allows elements in any
+ * user-defined set to have TLV_READ flag even if they have no
+ * TLV data in their initial state. In this case, any read
+ * operation for TLV data should return -ENXIO.
+ */
+ if (snd_ctl_elem_info_is_tlv_readable(info)) {
+ unsigned int data[32];
+ err = snd_ctl_elem_tlv_read(trial->handle, trial->id,
+ data, sizeof(data));
+ if (err >= 0)
+ return -EIO;
+ if (err != -ENXIO)
+ return err;
+
+ trial->tlv_readable = true;
+ }
+
}
return 0;
static int check_tlv(struct elem_set_trial *trial)
{
unsigned int *tlv;
+ int mask;
+ unsigned int count;
unsigned int len;
unsigned int *curr;
int err;
if (err < 0)
goto end;
+ /*
+ * Since kernel v4.14, any write operation to an element in user-defined
+ * set can change state of the other elements in the same set. In this
+ * case, any TLV data is firstly available after the operation.
+ */
+ if (!trial->tlv_readable) {
+ mask = SND_CTL_EVENT_MASK_INFO | SND_CTL_EVENT_MASK_TLV;
+ count = trial->element_count;
+ } else {
+ mask = SND_CTL_EVENT_MASK_TLV;
+ count = 1;
+ }
+ err = check_event(trial, mask, count);
+ if (err < 0)
+ goto end;
+ if (!trial->tlv_readable) {
+ snd_ctl_elem_info_t *info;
+ snd_ctl_elem_info_alloca(&info);
+
+ snd_ctl_elem_info_set_id(info, trial->id);
+ err = snd_ctl_elem_info(trial->handle, info);
+ if (err < 0)
+ return err;
+ if (!snd_ctl_elem_info_is_tlv_readable(info))
+ return -EIO;
+
+ /* Now TLV data is available for this element set. */
+ trial->tlv_readable = true;
+ }
+
err = snd_ctl_elem_tlv_read(trial->handle, trial->id, curr, len);
if (err < 0)
goto end;
trial.change_elem_members = change_bool_elem_members;
trial.allocate_elem_set_tlv =
allocate_bool_elem_set_tlv;
+ trial.tlv_readable = false;
break;
case SND_CTL_ELEM_TYPE_INTEGER:
trial.element_count = 900;
trial.change_elem_members = change_int_elem_members;
trial.allocate_elem_set_tlv =
allocate_int_elem_set_tlv;
+ trial.tlv_readable = false;
break;
case SND_CTL_ELEM_TYPE_ENUMERATED:
trial.element_count = 900;
trial.check_elem_props = check_enum_elem_props;
trial.change_elem_members = change_enum_elem_members;
trial.allocate_elem_set_tlv = NULL;
+ trial.tlv_readable = false;
break;
case SND_CTL_ELEM_TYPE_BYTES:
trial.element_count = 900;
trial.change_elem_members = change_bytes_elem_members;
trial.allocate_elem_set_tlv =
allocate_bytes_elem_set_tlv;
+ trial.tlv_readable = false;
break;
case SND_CTL_ELEM_TYPE_IEC958:
trial.element_count = 1;
trial.check_elem_props = NULL;
trial.change_elem_members = change_iec958_elem_members;
trial.allocate_elem_set_tlv = NULL;
+ trial.tlv_readable = false;
break;
case SND_CTL_ELEM_TYPE_INTEGER64:
default:
trial.change_elem_members = change_int64_elem_members;
trial.allocate_elem_set_tlv =
allocate_int64_elem_set_tlv;
+ trial.tlv_readable = false;
break;
}
/* Check properties of each element in this element set. */
err = check_elem_set_props(&trial);
if (err < 0) {
- printf("Fail to check propetries of each element with "
+ printf("Fail to check properties of each element with "
"%s type.\n",
snd_ctl_elem_type_name(trial.type));
break;
snd_ctl_elem_type_name(trial.type));
break;
}
- err = check_event(&trial, SND_CTL_EVENT_MASK_TLV, 1);
- if (err < 0) {
- printf("Fail to check an event to change TLV"
- "data of an an element set with %s "
- "type.\n",
- snd_ctl_elem_type_name(trial.type));
- break;
- }
}
/* Test an operation to remove elements in this element set. */