/**
 * \brief Get TLV value for an HCTL element
 * \param elem HCTL element
 * \param tlv TLV array for value
 * \param tlv_size size of TLV array in bytes
 * \return 0 otherwise a negative error code on failure
 */
int snd_hctl_elem_tlv_read(snd_hctl_elem_t *elem, unsigned int *tlv, unsigned int tlv_size)
{
	assert(elem);
	assert(tlv);
	assert(tlv_size >= 12);
	return snd_ctl_elem_tlv_read(elem->hctl->ctl, &elem->id, tlv, tlv_size);
}
static int check_tlv(struct elem_set_trial *trial)
{
	unsigned int orig[8], curr[8];
	int err;

	/*
	 * See a layout of 'struct snd_ctl_tlv'. I don't know the reason to
	 * construct this buffer with the same layout. It should be abstracted
	 * inner userspace library...
	 */
	orig[0] = snd_ctl_elem_id_get_numid(trial->id);
	orig[1] = 6 * sizeof(orig[0]);
	orig[2] = 'a';
	orig[3] = 'b';
	orig[4] = 'c';
	orig[5] = 'd';
	orig[6] = 'e';
	orig[7] = 'f';

	/*
	 * In in-kernel implementation, write and command operations are the
	 * same  for an element set added by userspace applications. Here, I
	 * use write.
	 */
	err = snd_ctl_elem_tlv_write(trial->handle, trial->id,
				     (const unsigned int *)orig);
	if (err < 0)
		return err;

	err = snd_ctl_elem_tlv_read(trial->handle, trial->id, curr,
				    sizeof(curr));
	if (err < 0)
		return err;

	if (memcmp(curr, orig, sizeof(orig)) != 0)
		return -EIO;

	return 0;
}
/*
 * add the TLV string and dB ranges to comment fields
 */
static int add_tlv_comments(snd_ctl_t *handle, snd_ctl_elem_id_t *id,
			    snd_ctl_elem_info_t *info, snd_config_t *comment)
{
	unsigned int tlv[MAX_USER_TLV_SIZE];
	unsigned int *db;
	long dbmin, dbmax;
	int err;

	if (snd_ctl_elem_tlv_read(handle, id, tlv, sizeof(tlv)) < 0)
		return 0; /* ignore error */

	if (snd_ctl_elem_info_is_tlv_writable(info)) {
		char *s = tlv_to_str(tlv);
		if (s) {
			err = snd_config_string_add(comment, "tlv", s);
			if (err < 0) {
				error("snd_config_string_add: %s", snd_strerror(err));
				return err;
			}
			free(s);
		}
	}

	err = snd_tlv_parse_dB_info(tlv, sizeof(tlv), &db);
	if (err <= 0)
		return 0;

	snd_tlv_get_dB_range(db, snd_ctl_elem_info_get_min(info),
			     snd_ctl_elem_info_get_max(info),
			     &dbmin, &dbmax);
	if (err < 0)
		return err;
	snd_config_integer_add(comment, "dbmin", dbmin);
	snd_config_integer_add(comment, "dbmax", dbmax);
	return 0;
}