Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8d448162 authored by Clemens Ladisch's avatar Clemens Ladisch Committed by Takashi Iwai
Browse files

ALSA: control: add support for ENUMERATED user space controls



Handling of user control elements was implemented for all types except
ENUMERATED.  This type will be needed for the device-specific mixers of
upcoming FireWire drivers.

Signed-off-by: default avatarClemens Ladisch <clemens@ladisch.de>
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent f92766bc
Loading
Loading
Loading
Loading
+3 −1
Original line number Original line Diff line number Diff line
@@ -706,7 +706,7 @@ struct snd_timer_tread {
 *                                                                          *
 *                                                                          *
 ****************************************************************************/
 ****************************************************************************/


#define SNDRV_CTL_VERSION		SNDRV_PROTOCOL_VERSION(2, 0, 6)
#define SNDRV_CTL_VERSION		SNDRV_PROTOCOL_VERSION(2, 0, 7)


struct snd_ctl_card_info {
struct snd_ctl_card_info {
	int card;			/* card number */
	int card;			/* card number */
@@ -803,6 +803,8 @@ struct snd_ctl_elem_info {
			unsigned int items;	/* R: number of items */
			unsigned int items;	/* R: number of items */
			unsigned int item;	/* W: item number */
			unsigned int item;	/* W: item number */
			char name[64];		/* R: value name */
			char name[64];		/* R: value name */
			__u64 names_ptr;	/* W: names list (ELEM_ADD only) */
			unsigned int names_length;
		} enumerated;
		} enumerated;
		unsigned char reserved[128];
		unsigned char reserved[128];
	} value;
	} value;
+76 −4
Original line number Original line Diff line number Diff line
@@ -989,7 +989,6 @@ struct user_element {
	void *tlv_data;			/* TLV data */
	void *tlv_data;			/* TLV data */
	unsigned long tlv_data_size;	/* TLV data size */
	unsigned long tlv_data_size;	/* TLV data size */
	void *priv_data;		/* private data (like strings for enumerated type) */
	void *priv_data;		/* private data (like strings for enumerated type) */
	unsigned long priv_data_size;	/* size of private data in bytes */
};
};


static int snd_ctl_elem_user_info(struct snd_kcontrol *kcontrol,
static int snd_ctl_elem_user_info(struct snd_kcontrol *kcontrol,
@@ -1001,6 +1000,28 @@ static int snd_ctl_elem_user_info(struct snd_kcontrol *kcontrol,
	return 0;
	return 0;
}
}


static int snd_ctl_elem_user_enum_info(struct snd_kcontrol *kcontrol,
				       struct snd_ctl_elem_info *uinfo)
{
	struct user_element *ue = kcontrol->private_data;
	const char *names;
	unsigned int item;

	item = uinfo->value.enumerated.item;

	*uinfo = ue->info;

	item = min(item, uinfo->value.enumerated.items - 1);
	uinfo->value.enumerated.item = item;

	names = ue->priv_data;
	for (; item > 0; --item)
		names += strlen(names) + 1;
	strcpy(uinfo->value.enumerated.name, names);

	return 0;
}

static int snd_ctl_elem_user_get(struct snd_kcontrol *kcontrol,
static int snd_ctl_elem_user_get(struct snd_kcontrol *kcontrol,
				 struct snd_ctl_elem_value *ucontrol)
				 struct snd_ctl_elem_value *ucontrol)
{
{
@@ -1055,11 +1076,46 @@ static int snd_ctl_elem_user_tlv(struct snd_kcontrol *kcontrol,
	return change;
	return change;
}
}


static int snd_ctl_elem_init_enum_names(struct user_element *ue)
{
	char *names, *p;
	size_t buf_len, name_len;
	unsigned int i;

	if (ue->info.value.enumerated.names_length > 64 * 1024)
		return -EINVAL;

	names = memdup_user(
		(const void __user *)ue->info.value.enumerated.names_ptr,
		ue->info.value.enumerated.names_length);
	if (IS_ERR(names))
		return PTR_ERR(names);

	/* check that there are enough valid names */
	buf_len = ue->info.value.enumerated.names_length;
	p = names;
	for (i = 0; i < ue->info.value.enumerated.items; ++i) {
		name_len = strnlen(p, buf_len);
		if (name_len == 0 || name_len >= 64 || name_len == buf_len) {
			kfree(names);
			return -EINVAL;
		}
		p += name_len + 1;
		buf_len -= name_len + 1;
	}

	ue->priv_data = names;
	ue->info.value.enumerated.names_ptr = 0;

	return 0;
}

static void snd_ctl_elem_user_free(struct snd_kcontrol *kcontrol)
static void snd_ctl_elem_user_free(struct snd_kcontrol *kcontrol)
{
{
	struct user_element *ue = kcontrol->private_data;
	struct user_element *ue = kcontrol->private_data;
	if (ue->tlv_data)

	kfree(ue->tlv_data);
	kfree(ue->tlv_data);
	kfree(ue->priv_data);
	kfree(ue);
	kfree(ue);
}
}


@@ -1101,6 +1157,9 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file,
	memcpy(&kctl.id, &info->id, sizeof(info->id));
	memcpy(&kctl.id, &info->id, sizeof(info->id));
	kctl.count = info->owner ? info->owner : 1;
	kctl.count = info->owner ? info->owner : 1;
	access |= SNDRV_CTL_ELEM_ACCESS_USER;
	access |= SNDRV_CTL_ELEM_ACCESS_USER;
	if (info->type == SNDRV_CTL_ELEM_TYPE_ENUMERATED)
		kctl.info = snd_ctl_elem_user_enum_info;
	else
		kctl.info = snd_ctl_elem_user_info;
		kctl.info = snd_ctl_elem_user_info;
	if (access & SNDRV_CTL_ELEM_ACCESS_READ)
	if (access & SNDRV_CTL_ELEM_ACCESS_READ)
		kctl.get = snd_ctl_elem_user_get;
		kctl.get = snd_ctl_elem_user_get;
@@ -1122,6 +1181,11 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file,
		if (info->count > 64)
		if (info->count > 64)
			return -EINVAL;
			return -EINVAL;
		break;
		break;
	case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
		private_size = sizeof(unsigned int);
		if (info->count > 128 || info->value.enumerated.items == 0)
			return -EINVAL;
		break;
	case SNDRV_CTL_ELEM_TYPE_BYTES:
	case SNDRV_CTL_ELEM_TYPE_BYTES:
		private_size = sizeof(unsigned char);
		private_size = sizeof(unsigned char);
		if (info->count > 512)
		if (info->count > 512)
@@ -1143,9 +1207,17 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file,
	ue->info.access = 0;
	ue->info.access = 0;
	ue->elem_data = (char *)ue + sizeof(*ue);
	ue->elem_data = (char *)ue + sizeof(*ue);
	ue->elem_data_size = private_size;
	ue->elem_data_size = private_size;
	if (ue->info.type == SNDRV_CTL_ELEM_TYPE_ENUMERATED) {
		err = snd_ctl_elem_init_enum_names(ue);
		if (err < 0) {
			kfree(ue);
			return err;
		}
	}
	kctl.private_free = snd_ctl_elem_user_free;
	kctl.private_free = snd_ctl_elem_user_free;
	_kctl = snd_ctl_new(&kctl, access);
	_kctl = snd_ctl_new(&kctl, access);
	if (_kctl == NULL) {
	if (_kctl == NULL) {
		kfree(ue->priv_data);
		kfree(ue);
		kfree(ue);
		return -ENOMEM;
		return -ENOMEM;
	}
	}
+4 −0
Original line number Original line Diff line number Diff line
@@ -83,6 +83,8 @@ struct snd_ctl_elem_info32 {
			u32 items;
			u32 items;
			u32 item;
			u32 item;
			char name[64];
			char name[64];
			u64 names_ptr;
			u32 names_length;
		} enumerated;
		} enumerated;
		unsigned char reserved[128];
		unsigned char reserved[128];
	} value;
	} value;
@@ -372,6 +374,8 @@ static int snd_ctl_elem_add_compat(struct snd_ctl_file *file,
				   &data32->value.enumerated,
				   &data32->value.enumerated,
				   sizeof(data->value.enumerated)))
				   sizeof(data->value.enumerated)))
			goto error;
			goto error;
		data->value.enumerated.names_ptr =
			(uintptr_t)compat_ptr(data->value.enumerated.names_ptr);
		break;
		break;
	default:
	default:
		break;
		break;