Commit 870fd554 authored by Jaroslav Kysela's avatar Jaroslav Kysela

[ALSA] Fix handling of user-defined controls

Control Midlevel,ALSA Core
Fixed handling of user-defined controls.
The max number of user-defined controls is limited, too (as default 32).
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent cc788a7b
...@@ -148,6 +148,7 @@ struct _snd_card { ...@@ -148,6 +148,7 @@ struct _snd_card {
struct rw_semaphore controls_rwsem; /* controls list lock */ struct rw_semaphore controls_rwsem; /* controls list lock */
rwlock_t ctl_files_rwlock; /* ctl_files list lock */ rwlock_t ctl_files_rwlock; /* ctl_files list lock */
int controls_count; /* count of all controls */ int controls_count; /* count of all controls */
int user_ctl_count; /* count of all user controls */
struct list_head controls; /* all controls for this card */ struct list_head controls; /* all controls for this card */
struct list_head ctl_files; /* active control files */ struct list_head ctl_files; /* active control files */
......
...@@ -31,6 +31,9 @@ ...@@ -31,6 +31,9 @@
#include <sound/info.h> #include <sound/info.h>
#include <sound/control.h> #include <sound/control.h>
/* max number of user-defined controls */
#define MAX_USER_CONTROLS 32
typedef struct _snd_kctl_ioctl { typedef struct _snd_kctl_ioctl {
struct list_head list; /* list of all ioctls */ struct list_head list; /* list of all ioctls */
snd_kctl_ioctl_func_t fioctl; snd_kctl_ioctl_func_t fioctl;
...@@ -801,13 +804,7 @@ static int snd_ctl_elem_unlock(snd_ctl_file_t *file, snd_ctl_elem_id_t __user *_ ...@@ -801,13 +804,7 @@ static int snd_ctl_elem_unlock(snd_ctl_file_t *file, snd_ctl_elem_id_t __user *_
} }
struct user_element { struct user_element {
enum sndrv_ctl_elem_type type; /* element type */ snd_ctl_elem_info_t info;
unsigned int elem_count; /* count of elements */
union {
struct {
unsigned int items;
} enumerated;
} u;
void *elem_data; /* element data */ void *elem_data; /* element data */
unsigned long elem_data_size; /* size of element data in bytes */ unsigned long elem_data_size; /* size of element data in bytes */
void *priv_data; /* private data (like strings for enumerated type) */ void *priv_data; /* private data (like strings for enumerated type) */
...@@ -820,11 +817,10 @@ static int snd_ctl_elem_user_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t ...@@ -820,11 +817,10 @@ static int snd_ctl_elem_user_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t
{ {
struct user_element *ue = kcontrol->private_data; struct user_element *ue = kcontrol->private_data;
uinfo->type = ue->type; *uinfo = ue->info;
uinfo->count = ue->elem_count; if (ue->info.type == SNDRV_CTL_ELEM_TYPE_ENUMERATED) {
if (ue->type == SNDRV_CTL_ELEM_TYPE_ENUMERATED) { uinfo->value.enumerated.items = ue->info.value.enumerated.items;
uinfo->value.enumerated.items = ue->u.enumerated.items; if (uinfo->value.enumerated.item >= ue->info.value.enumerated.items)
if (uinfo->value.enumerated.item >= ue->u.enumerated.items)
uinfo->value.enumerated.item = 0; uinfo->value.enumerated.item = 0;
strlcpy(uinfo->value.enumerated.name, strlcpy(uinfo->value.enumerated.name,
(char *)ue->priv_data + uinfo->value.enumerated.item * 64, (char *)ue->priv_data + uinfo->value.enumerated.item * 64,
...@@ -846,9 +842,10 @@ static int snd_ctl_elem_user_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t ...@@ -846,9 +842,10 @@ static int snd_ctl_elem_user_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t
int change; int change;
struct user_element *ue = kcontrol->private_data; struct user_element *ue = kcontrol->private_data;
change = memcmp(&ucontrol->value, ue->elem_data, ue->elem_data_size); change = memcmp(&ucontrol->value, ue->elem_data, ue->elem_data_size) != 0;
memcpy(ue->elem_data, &ucontrol->value, ue->elem_data_size); if (change)
return !!change; memcpy(ue->elem_data, &ucontrol->value, ue->elem_data_size);
return change;
} }
static void snd_ctl_elem_user_free(snd_kcontrol_t * kcontrol) static void snd_ctl_elem_user_free(snd_kcontrol_t * kcontrol)
...@@ -866,6 +863,8 @@ static int snd_ctl_elem_add(snd_ctl_file_t *file, snd_ctl_elem_info_t __user *_i ...@@ -866,6 +863,8 @@ static int snd_ctl_elem_add(snd_ctl_file_t *file, snd_ctl_elem_info_t __user *_i
struct user_element *ue; struct user_element *ue;
int idx, err; int idx, err;
if (card->user_ctl_count >= MAX_USER_CONTROLS)
return -ENOMEM;
if (copy_from_user(&info, _info, sizeof(info))) if (copy_from_user(&info, _info, sizeof(info)))
return -EFAULT; return -EFAULT;
if (info.count > 1024) if (info.count > 1024)
...@@ -878,18 +877,20 @@ static int snd_ctl_elem_add(snd_ctl_file_t *file, snd_ctl_elem_info_t __user *_i ...@@ -878,18 +877,20 @@ static int snd_ctl_elem_add(snd_ctl_file_t *file, snd_ctl_elem_info_t __user *_i
info.id.numid = 0; info.id.numid = 0;
memset(&kctl, 0, sizeof(kctl)); memset(&kctl, 0, sizeof(kctl));
down_write(&card->controls_rwsem); down_write(&card->controls_rwsem);
if (!!((_kctl = snd_ctl_find_id(card, &info.id)) != NULL) ^ replace) { _kctl = snd_ctl_find_id(card, &info.id);
up_write(&card->controls_rwsem); err = 0;
return !replace ? -EBUSY : -ENOENT; if (_kctl) {
} if (replace)
if (replace) { err = snd_ctl_remove(card, _kctl);
err = snd_ctl_remove(card, _kctl); else
if (err < 0) { err = -EBUSY;
up_write(&card->controls_rwsem); } else {
return err; if (replace)
} err = -ENOENT;
} }
up_write(&card->controls_rwsem); up_write(&card->controls_rwsem);
if (err < 0)
return err;
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;
...@@ -919,7 +920,7 @@ static int snd_ctl_elem_add(snd_ctl_file_t *file, snd_ctl_elem_info_t __user *_i ...@@ -919,7 +920,7 @@ static int snd_ctl_elem_add(snd_ctl_file_t *file, snd_ctl_elem_info_t __user *_i
private_size = sizeof(unsigned int); private_size = sizeof(unsigned int);
if (info.count > 128) if (info.count > 128)
return -EINVAL; return -EINVAL;
if (info.value.enumerated.items > 1024) if (info.value.enumerated.items > 128)
return -EINVAL; return -EINVAL;
extra_size = info.value.enumerated.items * 64; extra_size = info.value.enumerated.items * 64;
break; break;
...@@ -946,8 +947,7 @@ static int snd_ctl_elem_add(snd_ctl_file_t *file, snd_ctl_elem_info_t __user *_i ...@@ -946,8 +947,7 @@ static int snd_ctl_elem_add(snd_ctl_file_t *file, snd_ctl_elem_info_t __user *_i
ue = kcalloc(1, sizeof(struct user_element) + dimen_size + private_size + extra_size, GFP_KERNEL); ue = kcalloc(1, sizeof(struct user_element) + dimen_size + private_size + extra_size, GFP_KERNEL);
if (ue == NULL) if (ue == NULL)
return -ENOMEM; return -ENOMEM;
ue->type = info.type; ue->info = info;
ue->elem_count = info.count;
if (!(info.access & SNDRV_CTL_ELEM_ACCESS_DINDIRECT)) { if (!(info.access & SNDRV_CTL_ELEM_ACCESS_DINDIRECT)) {
for (idx = 0; idx < 4 && info.dimen.d[idx]; idx++) for (idx = 0; idx < 4 && info.dimen.d[idx]; idx++)
ue->dimen[idx] = info.dimen.d[idx]; ue->dimen[idx] = info.dimen.d[idx];
...@@ -958,10 +958,9 @@ static int snd_ctl_elem_add(snd_ctl_file_t *file, snd_ctl_elem_info_t __user *_i ...@@ -958,10 +958,9 @@ static int snd_ctl_elem_add(snd_ctl_file_t *file, snd_ctl_elem_info_t __user *_i
if (extra_size) { if (extra_size) {
ue->priv_data = (char *)ue + sizeof(ue) + dimen_size + private_size; ue->priv_data = (char *)ue + sizeof(ue) + dimen_size + private_size;
ue->priv_data_size = extra_size; ue->priv_data_size = extra_size;
if (ue->type == SNDRV_CTL_ELEM_TYPE_ENUMERATED) { if (ue->info.type == SNDRV_CTL_ELEM_TYPE_ENUMERATED) {
if (copy_from_user(ue->priv_data, *(char **)info.value.enumerated.name, extra_size)) if (copy_from_user(ue->priv_data, *(char __user **)info.value.enumerated.name, extra_size))
return -EFAULT; return -EFAULT;
ue->u.enumerated.items = info.value.enumerated.items;
} }
} }
kctl.private_free = snd_ctl_elem_user_free; kctl.private_free = snd_ctl_elem_user_free;
...@@ -978,16 +977,29 @@ static int snd_ctl_elem_add(snd_ctl_file_t *file, snd_ctl_elem_info_t __user *_i ...@@ -978,16 +977,29 @@ static int snd_ctl_elem_add(snd_ctl_file_t *file, snd_ctl_elem_info_t __user *_i
snd_ctl_free_one(_kctl); snd_ctl_free_one(_kctl);
return err; return err;
} }
down_write(&card->controls_rwsem);
card->user_ctl_count++;
up_write(&card->controls_rwsem);
return 0; return 0;
} }
static int snd_ctl_elem_remove(snd_ctl_file_t *file, snd_ctl_elem_id_t __user *_id) static int snd_ctl_elem_remove(snd_ctl_file_t *file, snd_ctl_elem_id_t __user *_id)
{ {
snd_ctl_elem_id_t id; snd_ctl_elem_id_t id;
int err;
if (copy_from_user(&id, _id, sizeof(id))) if (copy_from_user(&id, _id, sizeof(id)))
return -EFAULT; return -EFAULT;
return snd_ctl_remove_unlocked_id(file, &id); err = snd_ctl_remove_unlocked_id(file, &id);
if (! err) {
snd_card_t *card = file->card;
down_write(&card->controls_rwsem);
card->user_ctl_count--;
up_write(&card->controls_rwsem);
}
return err;
} }
static int snd_ctl_subscribe_events(snd_ctl_file_t *file, int __user *ptr) static int snd_ctl_subscribe_events(snd_ctl_file_t *file, int __user *ptr)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment