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: Clemens Ladisch <clemens@ladisch.de> Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
parent
f92766bc89
commit
8d448162bd
|
@ -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;
|
||||||
|
|
|
@ -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,7 +1157,10 @@ 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;
|
||||||
kctl.info = snd_ctl_elem_user_info;
|
if (info->type == SNDRV_CTL_ELEM_TYPE_ENUMERATED)
|
||||||
|
kctl.info = snd_ctl_elem_user_enum_info;
|
||||||
|
else
|
||||||
|
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;
|
||||||
if (access & SNDRV_CTL_ELEM_ACCESS_WRITE)
|
if (access & SNDRV_CTL_ELEM_ACCESS_WRITE)
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in New Issue