summaryrefslogtreecommitdiff
path: root/sound/core
diff options
context:
space:
mode:
Diffstat (limited to 'sound/core')
-rw-r--r--sound/core/control.c126
-rw-r--r--sound/core/control_compat.c2
-rw-r--r--sound/core/control_led.c2
-rw-r--r--sound/core/jack.c2
-rw-r--r--sound/core/oss/mixer_oss.c10
-rw-r--r--sound/core/pcm.c2
-rw-r--r--sound/core/vmaster.c28
7 files changed, 135 insertions, 37 deletions
diff --git a/sound/core/control.c b/sound/core/control.c
index 8386b53acdcd..e13e9d6b3b89 100644
--- a/sound/core/control.c
+++ b/sound/core/control.c
@@ -39,6 +39,9 @@ static LIST_HEAD(snd_control_compat_ioctls);
#endif
static struct snd_ctl_layer_ops *snd_ctl_layer;
+static int snd_ctl_remove_locked(struct snd_card *card,
+ struct snd_kcontrol *kcontrol);
+
static int snd_ctl_open(struct inode *inode, struct file *file)
{
unsigned long flags;
@@ -466,11 +469,13 @@ static int __snd_ctl_add_replace(struct snd_card *card,
struct snd_kcontrol *old;
int err;
+ lockdep_assert_held_write(&card->controls_rwsem);
+
id = kcontrol->id;
if (id.index > UINT_MAX - kcontrol->count)
return -EINVAL;
- old = snd_ctl_find_id(card, &id);
+ old = snd_ctl_find_id_locked(card, &id);
if (!old) {
if (mode == CTL_REPLACE)
return -EINVAL;
@@ -483,7 +488,7 @@ static int __snd_ctl_add_replace(struct snd_card *card,
return -EBUSY;
}
- err = snd_ctl_remove(card, old);
+ err = snd_ctl_remove_locked(card, old);
if (err < 0)
return err;
}
@@ -575,6 +580,8 @@ static int __snd_ctl_remove(struct snd_card *card,
{
unsigned int idx;
+ lockdep_assert_held_write(&card->controls_rwsem);
+
if (snd_BUG_ON(!card || !kcontrol))
return -EINVAL;
list_del(&kcontrol->list);
@@ -589,20 +596,32 @@ static int __snd_ctl_remove(struct snd_card *card,
return 0;
}
+static inline int snd_ctl_remove_locked(struct snd_card *card,
+ struct snd_kcontrol *kcontrol)
+{
+ return __snd_ctl_remove(card, kcontrol, true);
+}
+
/**
* snd_ctl_remove - remove the control from the card and release it
* @card: the card instance
* @kcontrol: the control instance to remove
*
* Removes the control from the card and then releases the instance.
- * You don't need to call snd_ctl_free_one(). You must be in
- * the write lock - down_write(&card->controls_rwsem).
+ * You don't need to call snd_ctl_free_one().
*
* Return: 0 if successful, or a negative error code on failure.
+ *
+ * Note that this function takes card->controls_rwsem lock internally.
*/
int snd_ctl_remove(struct snd_card *card, struct snd_kcontrol *kcontrol)
{
- return __snd_ctl_remove(card, kcontrol, true);
+ int ret;
+
+ down_write(&card->controls_rwsem);
+ ret = snd_ctl_remove_locked(card, kcontrol);
+ up_write(&card->controls_rwsem);
+ return ret;
}
EXPORT_SYMBOL(snd_ctl_remove);
@@ -622,12 +641,12 @@ int snd_ctl_remove_id(struct snd_card *card, struct snd_ctl_elem_id *id)
int ret;
down_write(&card->controls_rwsem);
- kctl = snd_ctl_find_id(card, id);
+ kctl = snd_ctl_find_id_locked(card, id);
if (kctl == NULL) {
up_write(&card->controls_rwsem);
return -ENOENT;
}
- ret = snd_ctl_remove(card, kctl);
+ ret = snd_ctl_remove_locked(card, kctl);
up_write(&card->controls_rwsem);
return ret;
}
@@ -651,7 +670,7 @@ static int snd_ctl_remove_user_ctl(struct snd_ctl_file * file,
int idx, ret;
down_write(&card->controls_rwsem);
- kctl = snd_ctl_find_id(card, id);
+ kctl = snd_ctl_find_id_locked(card, id);
if (kctl == NULL) {
ret = -ENOENT;
goto error;
@@ -665,7 +684,7 @@ static int snd_ctl_remove_user_ctl(struct snd_ctl_file * file,
ret = -EBUSY;
goto error;
}
- ret = snd_ctl_remove(card, kctl);
+ ret = snd_ctl_remove_locked(card, kctl);
error:
up_write(&card->controls_rwsem);
return ret;
@@ -692,7 +711,7 @@ int snd_ctl_activate_id(struct snd_card *card, struct snd_ctl_elem_id *id,
int ret;
down_write(&card->controls_rwsem);
- kctl = snd_ctl_find_id(card, id);
+ kctl = snd_ctl_find_id_locked(card, id);
if (kctl == NULL) {
ret = -ENOENT;
goto unlock;
@@ -746,7 +765,7 @@ int snd_ctl_rename_id(struct snd_card *card, struct snd_ctl_elem_id *src_id,
int saved_numid;
down_write(&card->controls_rwsem);
- kctl = snd_ctl_find_id(card, src_id);
+ kctl = snd_ctl_find_id_locked(card, src_id);
if (kctl == NULL) {
up_write(&card->controls_rwsem);
return -ENOENT;
@@ -769,11 +788,12 @@ EXPORT_SYMBOL(snd_ctl_rename_id);
*
* Renames the specified control on the card to the new name.
*
- * Make sure to take the control write lock - down_write(&card->controls_rwsem).
+ * Note that this function takes card->controls_rwsem lock internally.
*/
void snd_ctl_rename(struct snd_card *card, struct snd_kcontrol *kctl,
const char *name)
{
+ down_write(&card->controls_rwsem);
remove_hash_entries(card, kctl);
if (strscpy(kctl->id.name, name, sizeof(kctl->id.name)) < 0)
@@ -781,6 +801,7 @@ void snd_ctl_rename(struct snd_card *card, struct snd_kcontrol *kctl,
name, kctl->id.name);
add_hash_entries(card, kctl);
+ up_write(&card->controls_rwsem);
}
EXPORT_SYMBOL(snd_ctl_rename);
@@ -799,7 +820,7 @@ snd_ctl_find_numid_slow(struct snd_card *card, unsigned int numid)
#endif /* !CONFIG_SND_CTL_FAST_LOOKUP */
/**
- * snd_ctl_find_numid - find the control instance with the given number-id
+ * snd_ctl_find_numid_locked - find the control instance with the given number-id
* @card: the card instance
* @numid: the number-id to search
*
@@ -809,22 +830,46 @@ snd_ctl_find_numid_slow(struct snd_card *card, unsigned int numid)
* (if the race condition can happen).
*
* Return: The pointer of the instance if found, or %NULL if not.
- *
*/
-struct snd_kcontrol *snd_ctl_find_numid(struct snd_card *card, unsigned int numid)
+struct snd_kcontrol *
+snd_ctl_find_numid_locked(struct snd_card *card, unsigned int numid)
{
if (snd_BUG_ON(!card || !numid))
return NULL;
+ lockdep_assert_held(&card->controls_rwsem);
#ifdef CONFIG_SND_CTL_FAST_LOOKUP
return xa_load(&card->ctl_numids, numid);
#else
return snd_ctl_find_numid_slow(card, numid);
#endif
}
+EXPORT_SYMBOL(snd_ctl_find_numid_locked);
+
+/**
+ * snd_ctl_find_numid - find the control instance with the given number-id
+ * @card: the card instance
+ * @numid: the number-id to search
+ *
+ * Finds the control instance with the given number-id from the card.
+ *
+ * Return: The pointer of the instance if found, or %NULL if not.
+ *
+ * Note that this function takes card->controls_rwsem lock internally.
+ */
+struct snd_kcontrol *snd_ctl_find_numid(struct snd_card *card,
+ unsigned int numid)
+{
+ struct snd_kcontrol *kctl;
+
+ down_read(&card->controls_rwsem);
+ kctl = snd_ctl_find_numid_locked(card, numid);
+ up_read(&card->controls_rwsem);
+ return kctl;
+}
EXPORT_SYMBOL(snd_ctl_find_numid);
/**
- * snd_ctl_find_id - find the control instance with the given id
+ * snd_ctl_find_id_locked - find the control instance with the given id
* @card: the card instance
* @id: the id to search
*
@@ -834,17 +879,17 @@ EXPORT_SYMBOL(snd_ctl_find_numid);
* (if the race condition can happen).
*
* Return: The pointer of the instance if found, or %NULL if not.
- *
*/
-struct snd_kcontrol *snd_ctl_find_id(struct snd_card *card,
- struct snd_ctl_elem_id *id)
+struct snd_kcontrol *snd_ctl_find_id_locked(struct snd_card *card,
+ const struct snd_ctl_elem_id *id)
{
struct snd_kcontrol *kctl;
if (snd_BUG_ON(!card || !id))
return NULL;
+ lockdep_assert_held(&card->controls_rwsem);
if (id->numid != 0)
- return snd_ctl_find_numid(card, id->numid);
+ return snd_ctl_find_numid_locked(card, id->numid);
#ifdef CONFIG_SND_CTL_FAST_LOOKUP
kctl = xa_load(&card->ctl_hash, get_ctl_id_hash(id));
if (kctl && elem_id_matches(kctl, id))
@@ -859,6 +904,29 @@ struct snd_kcontrol *snd_ctl_find_id(struct snd_card *card,
return NULL;
}
+EXPORT_SYMBOL(snd_ctl_find_id_locked);
+
+/**
+ * snd_ctl_find_id - find the control instance with the given id
+ * @card: the card instance
+ * @id: the id to search
+ *
+ * Finds the control instance with the given id from the card.
+ *
+ * Return: The pointer of the instance if found, or %NULL if not.
+ *
+ * Note that this function takes card->controls_rwsem lock internally.
+ */
+struct snd_kcontrol *snd_ctl_find_id(struct snd_card *card,
+ const struct snd_ctl_elem_id *id)
+{
+ struct snd_kcontrol *kctl;
+
+ down_read(&card->controls_rwsem);
+ kctl = snd_ctl_find_id_locked(card, id);
+ up_read(&card->controls_rwsem);
+ return kctl;
+}
EXPORT_SYMBOL(snd_ctl_find_id);
static int snd_ctl_card_info(struct snd_card *card, struct snd_ctl_file * ctl,
@@ -1173,7 +1241,7 @@ static int snd_ctl_elem_info(struct snd_ctl_file *ctl,
int result;
down_read(&card->controls_rwsem);
- kctl = snd_ctl_find_id(card, &info->id);
+ kctl = snd_ctl_find_id_locked(card, &info->id);
if (kctl == NULL)
result = -ENOENT;
else
@@ -1212,7 +1280,7 @@ static int snd_ctl_elem_read(struct snd_card *card,
int ret;
down_read(&card->controls_rwsem);
- kctl = snd_ctl_find_id(card, &control->id);
+ kctl = snd_ctl_find_id_locked(card, &control->id);
if (kctl == NULL) {
ret = -ENOENT;
goto unlock;
@@ -1289,7 +1357,7 @@ static int snd_ctl_elem_write(struct snd_card *card, struct snd_ctl_file *file,
int result;
down_write(&card->controls_rwsem);
- kctl = snd_ctl_find_id(card, &control->id);
+ kctl = snd_ctl_find_id_locked(card, &control->id);
if (kctl == NULL) {
up_write(&card->controls_rwsem);
return -ENOENT;
@@ -1370,7 +1438,7 @@ static int snd_ctl_elem_lock(struct snd_ctl_file *file,
if (copy_from_user(&id, _id, sizeof(id)))
return -EFAULT;
down_write(&card->controls_rwsem);
- kctl = snd_ctl_find_id(card, &id);
+ kctl = snd_ctl_find_id_locked(card, &id);
if (kctl == NULL) {
result = -ENOENT;
} else {
@@ -1398,7 +1466,7 @@ static int snd_ctl_elem_unlock(struct snd_ctl_file *file,
if (copy_from_user(&id, _id, sizeof(id)))
return -EFAULT;
down_write(&card->controls_rwsem);
- kctl = snd_ctl_find_id(card, &id);
+ kctl = snd_ctl_find_id_locked(card, &id);
if (kctl == NULL) {
result = -ENOENT;
} else {
@@ -1507,6 +1575,8 @@ static int replace_user_tlv(struct snd_kcontrol *kctl, unsigned int __user *buf,
int i;
int change;
+ lockdep_assert_held_write(&ue->card->controls_rwsem);
+
if (size > 1024 * 128) /* sane value */
return -EINVAL;
@@ -1583,6 +1653,8 @@ static int snd_ctl_elem_init_enum_names(struct user_element *ue)
unsigned int i;
const uintptr_t user_ptrval = ue->info.value.enumerated.names_ptr;
+ lockdep_assert_held_write(&ue->card->controls_rwsem);
+
buf_len = ue->info.value.enumerated.names_length;
if (buf_len > 64 * 1024)
return -EINVAL;
@@ -1887,6 +1959,8 @@ static int snd_ctl_tlv_ioctl(struct snd_ctl_file *file,
struct snd_ctl_elem_id id;
struct snd_kcontrol_volatile *vd;
+ lockdep_assert_held(&file->card->controls_rwsem);
+
if (copy_from_user(&header, buf, sizeof(header)))
return -EFAULT;
@@ -1900,7 +1974,7 @@ static int snd_ctl_tlv_ioctl(struct snd_ctl_file *file,
container_size = header.length;
container = buf->tlv;
- kctl = snd_ctl_find_numid(file->card, header.numid);
+ kctl = snd_ctl_find_numid_locked(file->card, header.numid);
if (kctl == NULL)
return -ENOENT;
diff --git a/sound/core/control_compat.c b/sound/core/control_compat.c
index 9cae5d74335c..0e8b1bfb040e 100644
--- a/sound/core/control_compat.c
+++ b/sound/core/control_compat.c
@@ -173,7 +173,7 @@ static int get_ctl_type(struct snd_card *card, struct snd_ctl_elem_id *id,
int err;
down_read(&card->controls_rwsem);
- kctl = snd_ctl_find_id(card, id);
+ kctl = snd_ctl_find_id_locked(card, id);
if (! kctl) {
up_read(&card->controls_rwsem);
return -ENOENT;
diff --git a/sound/core/control_led.c b/sound/core/control_led.c
index ee77547bf8dc..67fc2a1dcf7a 100644
--- a/sound/core/control_led.c
+++ b/sound/core/control_led.c
@@ -251,7 +251,7 @@ static int snd_ctl_led_set_id(int card_number, struct snd_ctl_elem_id *id,
card = snd_card_ref(card_number);
if (card) {
down_write(&card->controls_rwsem);
- kctl = snd_ctl_find_id(card, id);
+ kctl = snd_ctl_find_id_locked(card, id);
if (kctl) {
ioff = snd_ctl_get_ioff(kctl, id);
vd = &kctl->vd[ioff];
diff --git a/sound/core/jack.c b/sound/core/jack.c
index 03d155ed362b..e0f034e7275c 100644
--- a/sound/core/jack.c
+++ b/sound/core/jack.c
@@ -66,12 +66,10 @@ static int snd_jack_dev_free(struct snd_device *device)
struct snd_card *card = device->card;
struct snd_jack_kctl *jack_kctl, *tmp_jack_kctl;
- down_write(&card->controls_rwsem);
list_for_each_entry_safe(jack_kctl, tmp_jack_kctl, &jack->kctl_list, list) {
list_del_init(&jack_kctl->list);
snd_ctl_remove(card, jack_kctl->kctl);
}
- up_write(&card->controls_rwsem);
if (jack->private_free)
jack->private_free(jack);
diff --git a/sound/core/oss/mixer_oss.c b/sound/core/oss/mixer_oss.c
index 9620115cfdc0..dae2da380835 100644
--- a/sound/core/oss/mixer_oss.c
+++ b/sound/core/oss/mixer_oss.c
@@ -524,7 +524,7 @@ static struct snd_kcontrol *snd_mixer_oss_test_id(struct snd_mixer_oss *mixer, c
id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
strscpy(id.name, name, sizeof(id.name));
id.index = index;
- return snd_ctl_find_id(card, &id);
+ return snd_ctl_find_id_locked(card, &id);
}
static void snd_mixer_oss_get_volume1_vol(struct snd_mixer_oss_file *fmixer,
@@ -540,7 +540,7 @@ static void snd_mixer_oss_get_volume1_vol(struct snd_mixer_oss_file *fmixer,
if (numid == ID_UNKNOWN)
return;
down_read(&card->controls_rwsem);
- kctl = snd_ctl_find_numid(card, numid);
+ kctl = snd_ctl_find_numid_locked(card, numid);
if (!kctl) {
up_read(&card->controls_rwsem);
return;
@@ -579,7 +579,7 @@ static void snd_mixer_oss_get_volume1_sw(struct snd_mixer_oss_file *fmixer,
if (numid == ID_UNKNOWN)
return;
down_read(&card->controls_rwsem);
- kctl = snd_ctl_find_numid(card, numid);
+ kctl = snd_ctl_find_numid_locked(card, numid);
if (!kctl) {
up_read(&card->controls_rwsem);
return;
@@ -645,7 +645,7 @@ static void snd_mixer_oss_put_volume1_vol(struct snd_mixer_oss_file *fmixer,
if (numid == ID_UNKNOWN)
return;
down_read(&card->controls_rwsem);
- kctl = snd_ctl_find_numid(card, numid);
+ kctl = snd_ctl_find_numid_locked(card, numid);
if (!kctl) {
up_read(&card->controls_rwsem);
return;
@@ -688,7 +688,7 @@ static void snd_mixer_oss_put_volume1_sw(struct snd_mixer_oss_file *fmixer,
if (numid == ID_UNKNOWN)
return;
down_read(&card->controls_rwsem);
- kctl = snd_ctl_find_numid(card, numid);
+ kctl = snd_ctl_find_numid_locked(card, numid);
if (!kctl) {
up_read(&card->controls_rwsem);
return;
diff --git a/sound/core/pcm.c b/sound/core/pcm.c
index 9d95e3731123..1c0bb3a07bff 100644
--- a/sound/core/pcm.c
+++ b/sound/core/pcm.c
@@ -814,9 +814,7 @@ static void free_chmap(struct snd_pcm_str *pstr)
if (pstr->chmap_kctl) {
struct snd_card *card = pstr->pcm->card;
- down_write(&card->controls_rwsem);
snd_ctl_remove(card, pstr->chmap_kctl);
- up_write(&card->controls_rwsem);
pstr->chmap_kctl = NULL;
}
}
diff --git a/sound/core/vmaster.c b/sound/core/vmaster.c
index d0f11f37889b..378d2c7c3d4a 100644
--- a/sound/core/vmaster.c
+++ b/sound/core/vmaster.c
@@ -280,6 +280,34 @@ int _snd_ctl_add_follower(struct snd_kcontrol *master,
}
EXPORT_SYMBOL(_snd_ctl_add_follower);
+/**
+ * snd_ctl_add_followers - add multiple followers to vmaster
+ * @card: card instance
+ * @master: the target vmaster kcontrol object
+ * @list: NULL-terminated list of name strings of followers to be added
+ *
+ * Adds the multiple follower kcontrols with the given names.
+ * Returns 0 for success or a negative error code.
+ */
+int snd_ctl_add_followers(struct snd_card *card, struct snd_kcontrol *master,
+ const char * const *list)
+{
+ struct snd_kcontrol *follower;
+ int err;
+
+ for (; *list; list++) {
+ follower = snd_ctl_find_id_mixer(card, *list);
+ if (follower) {
+ err = snd_ctl_add_follower(master, follower);
+ if (err < 0)
+ return err;
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_ctl_add_followers);
+
/*
* ctl callbacks for master controls
*/