@@ -128,6 +128,7 @@ typedef int (*snd_kctl_ioctl_func_t) (struct snd_card * card,
struct snd_ctl_file * control,
unsigned int cmd, unsigned long arg);
+int snd_control_new(struct snd_card * card);
void snd_ctl_notify(struct snd_card * card, unsigned int mask, struct snd_ctl_elem_id * id);
void snd_ctl_notify_one(struct snd_card * card, unsigned int mask, struct snd_kcontrol * kctl, unsigned int ioff);
@@ -95,21 +95,7 @@ struct snd_card {
void (*private_free) (struct snd_card *card); /* callback for freeing of
private data */
struct list_head devices; /* devices */
-
- struct device *ctl_dev; /* control device */
- unsigned int last_numid; /* last used numeric ID */
- struct rw_semaphore controls_rwsem; /* controls lock (list and values) */
- rwlock_t ctl_files_rwlock; /* ctl_files list lock */
- int controls_count; /* count of all controls */
- size_t user_ctl_alloc_size; // current memory allocation by user controls.
- struct list_head controls; /* all controls for this card */
- struct list_head ctl_files; /* active control files */
-#ifdef CONFIG_SND_CTL_FAST_LOOKUP
- struct xarray ctl_numids; /* hash table for numids */
- struct xarray ctl_hash; /* hash table for ctl id matching */
- bool ctl_hash_collision; /* ctl_hash collision seen? */
-#endif
-
+ struct snd_control *ctl; /* control devices */
struct snd_info_entry *proc_root; /* root for soundcard specific files */
struct proc_dir_entry *proc_root_link; /* number link to real id */
@@ -147,6 +133,23 @@ struct snd_card {
#endif
};
+struct snd_control
+{
+ struct device dev; /* control device */
+ struct rw_semaphore controls_rwsem; /* controls lock (list and values) */
+ rwlock_t files_rwlock; /* ctl_files list lock */
+ int controls_count; /* count of all controls */
+ size_t user_ctl_alloc_size; // current memory allocation by user controls.
+ struct list_head controls; /* all controls for this card */
+ struct list_head files; /* active control files */
+ unsigned int last_numid; /* last used numeric ID */
+#ifdef CONFIG_SND_CTL_FAST_LOOKUP
+ struct xarray numids; /* hash table for numids */
+ struct xarray hash; /* hash table for ctl id matching */
+ bool hash_collision; /* ctl_hash collision seen? */
+#endif
+};
+
#define dev_to_snd_card(p) container_of(p, struct snd_card, card_dev)
#ifdef CONFIG_PM
@@ -41,12 +41,49 @@ static struct snd_ctl_layer_ops *snd_ctl_layer;
static int snd_ctl_remove_locked(struct snd_card *card,
struct snd_kcontrol *kcontrol);
+/**
+ * snd_control_new - Allocate and initialize snd_control
+ * @card: the card to be initialized
+ *
+ * This function creates and initializes snd_control
+ */
+int snd_control_new(struct snd_card *card)
+{
+ struct snd_control *ctl = kzalloc(sizeof(*ctl), GFP_KERNEL);
+ int err;
+
+ if (snd_BUG_ON(!card))
+ return -EINVAL;
+ if (!ctl)
+ return -ENOMEM;
+
+ init_rwsem(&ctl->controls_rwsem);
+ rwlock_init(&ctl->files_rwlock);
+ INIT_LIST_HEAD(&ctl->controls);
+ INIT_LIST_HEAD(&ctl->files);
+#ifdef CONFIG_SND_CTL_FAST_LOOKUP
+ xa_init(&ctl->numids);
+ xa_init(&ctl->hash);
+#endif
+ card->ctl = ctl;
+ /* the control interface cannot be accessed from the user space until */
+ /* snd_cards_bitmask and snd_cards are set with snd_card_register */
+ err = snd_ctl_create(card);
+ if (err < 0) {
+ dev_err(card->dev, "unable to register control minors\n");
+ kfree(ctl);
+ return err;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(snd_control_new);
static int snd_ctl_open(struct inode *inode, struct file *file)
{
unsigned long flags;
struct snd_card *card;
- struct snd_ctl_file *ctl;
+ struct snd_control *ctl;
+ struct snd_ctl_file *ctlf;
int i, err;
err = stream_open(inode, file);
@@ -58,6 +95,7 @@ static int snd_ctl_open(struct inode *inode, struct file *file)
err = -ENODEV;
goto __error1;
}
+ ctl = card->ctl;
err = snd_card_file_add(card, file);
if (err < 0) {
err = -ENODEV;
@@ -67,22 +105,22 @@ static int snd_ctl_open(struct inode *inode, struct file *file)
err = -EFAULT;
goto __error2;
}
- ctl = kzalloc(sizeof(*ctl), GFP_KERNEL);
- if (ctl == NULL) {
+ ctlf = kzalloc(sizeof(*ctlf), GFP_KERNEL);
+ if (ctlf == NULL) {
err = -ENOMEM;
goto __error;
}
- INIT_LIST_HEAD(&ctl->events);
- init_waitqueue_head(&ctl->change_sleep);
- spin_lock_init(&ctl->read_lock);
- ctl->card = card;
+ INIT_LIST_HEAD(&ctlf->events);
+ init_waitqueue_head(&ctlf->change_sleep);
+ spin_lock_init(&ctlf->read_lock);
+ ctlf->card = card;
for (i = 0; i < SND_CTL_SUBDEV_ITEMS; i++)
- ctl->preferred_subdevice[i] = -1;
- ctl->pid = get_pid(task_pid(current));
- file->private_data = ctl;
- write_lock_irqsave(&card->ctl_files_rwlock, flags);
- list_add_tail(&ctl->list, &card->ctl_files);
- write_unlock_irqrestore(&card->ctl_files_rwlock, flags);
+ ctlf->preferred_subdevice[i] = -1;
+ ctlf->pid = get_pid(task_pid(current));
+ file->private_data = ctlf;
+ write_lock_irqsave(&ctl->files_rwlock, flags);
+ list_add_tail(&ctlf->list, &ctl->files);
+ write_unlock_irqrestore(&ctl->files_rwlock, flags);
snd_card_unref(card);
return 0;
@@ -113,27 +151,26 @@ static void snd_ctl_empty_read_queue(struct snd_ctl_file * ctl)
static int snd_ctl_release(struct inode *inode, struct file *file)
{
unsigned long flags;
- struct snd_card *card;
- struct snd_ctl_file *ctl;
+ struct snd_ctl_file *ctlf = file->private_data;
+ struct snd_card *card = ctlf->card;
+ struct snd_control *ctl = card->ctl;
struct snd_kcontrol *control;
unsigned int idx;
- ctl = file->private_data;
file->private_data = NULL;
- card = ctl->card;
- write_lock_irqsave(&card->ctl_files_rwlock, flags);
- list_del(&ctl->list);
- write_unlock_irqrestore(&card->ctl_files_rwlock, flags);
- down_write(&card->controls_rwsem);
- list_for_each_entry(control, &card->controls, list)
+ write_lock_irqsave(&ctl->files_rwlock, flags);
+ list_del(&ctlf->list);
+ write_unlock_irqrestore(&ctl->files_rwlock, flags);
+ down_write(&ctl->controls_rwsem);
+ list_for_each_entry(control, &ctl->controls, list)
for (idx = 0; idx < control->count; idx++)
- if (control->vd[idx].owner == ctl)
+ if (control->vd[idx].owner == ctlf)
control->vd[idx].owner = NULL;
- up_write(&card->controls_rwsem);
- snd_fasync_free(ctl->fasync);
- snd_ctl_empty_read_queue(ctl);
- put_pid(ctl->pid);
- kfree(ctl);
+ up_write(&ctl->controls_rwsem);
+ snd_fasync_free(ctlf->fasync);
+ snd_ctl_empty_read_queue(ctlf);
+ put_pid(ctlf->pid);
+ kfree(ctlf);
module_put(card->module);
snd_card_file_remove(card, file);
return 0;
@@ -160,11 +197,11 @@ void snd_ctl_notify(struct snd_card *card, unsigned int mask,
return;
if (card->shutdown)
return;
- read_lock_irqsave(&card->ctl_files_rwlock, flags);
+ read_lock_irqsave(&card->ctl->files_rwlock, flags);
#if IS_ENABLED(CONFIG_SND_MIXER_OSS)
card->mixer_oss_change_count++;
#endif
- list_for_each_entry(ctl, &card->ctl_files, list) {
+ list_for_each_entry(ctl, &card->ctl->files, list) {
if (!ctl->subscribed)
continue;
spin_lock(&ctl->read_lock);
@@ -187,7 +224,7 @@ void snd_ctl_notify(struct snd_card *card, unsigned int mask,
spin_unlock(&ctl->read_lock);
snd_kill_fasync(ctl->fasync, SIGIO, POLL_IN);
}
- read_unlock_irqrestore(&card->ctl_files_rwlock, flags);
+ read_unlock_irqrestore(&card->ctl->files_rwlock, flags);
}
EXPORT_SYMBOL(snd_ctl_notify);
@@ -341,13 +378,13 @@ static bool snd_ctl_remove_numid_conflict(struct snd_card *card,
struct snd_kcontrol *kctl;
/* Make sure that the ids assigned to the control do not wrap around */
- if (card->last_numid >= UINT_MAX - count)
- card->last_numid = 0;
+ if (card->ctl->last_numid >= UINT_MAX - count)
+ card->ctl->last_numid = 0;
- list_for_each_entry(kctl, &card->controls, list) {
- if (kctl->id.numid < card->last_numid + 1 + count &&
- kctl->id.numid + kctl->count > card->last_numid + 1) {
- card->last_numid = kctl->id.numid + kctl->count - 1;
+ list_for_each_entry(kctl, &card->ctl->controls, list) {
+ if (kctl->id.numid < card->ctl->last_numid + 1 + count &&
+ kctl->id.numid + kctl->count > card->ctl->last_numid + 1) {
+ card->ctl->last_numid = kctl->id.numid + kctl->count - 1;
return true;
}
}
@@ -406,18 +443,19 @@ static void add_hash_entries(struct snd_card *card,
struct snd_kcontrol *kcontrol)
{
struct snd_ctl_elem_id id = kcontrol->id;
+ struct snd_control *ctl = card->ctl;
int i;
- xa_store_range(&card->ctl_numids, kcontrol->id.numid,
+ xa_store_range(&ctl->numids, kcontrol->id.numid,
kcontrol->id.numid + kcontrol->count - 1,
kcontrol, GFP_KERNEL);
for (i = 0; i < kcontrol->count; i++) {
id.index = kcontrol->id.index + i;
- if (xa_insert(&card->ctl_hash, get_ctl_id_hash(&id),
+ if (xa_insert(&ctl->hash, get_ctl_id_hash(&id),
kcontrol, GFP_KERNEL)) {
/* skip hash for this entry, noting we had collision */
- card->ctl_hash_collision = true;
+ ctl->hash_collision = true;
dev_dbg(card->dev, "ctl_hash collision %d:%s:%d\n",
id.iface, id.name, id.index);
}
@@ -429,17 +467,18 @@ static void remove_hash_entries(struct snd_card *card,
struct snd_kcontrol *kcontrol)
{
struct snd_ctl_elem_id id = kcontrol->id;
+ struct snd_control *ctl = card->ctl;
struct snd_kcontrol *matched;
unsigned long h;
int i;
for (i = 0; i < kcontrol->count; i++) {
- xa_erase(&card->ctl_numids, id.numid);
+ xa_erase(&ctl->numids, id.numid);
h = get_ctl_id_hash(&id);
- matched = xa_load(&card->ctl_hash, h);
+ matched = xa_load(&ctl->hash, h);
if (matched && (matched == kcontrol ||
elem_id_matches(matched, &id)))
- xa_erase(&card->ctl_hash, h);
+ xa_erase(&ctl->hash, h);
id.index++;
id.numid++;
}
@@ -469,7 +508,7 @@ static int __snd_ctl_add_replace(struct snd_card *card,
struct snd_kcontrol *old;
int err;
- lockdep_assert_held_write(&card->controls_rwsem);
+ lockdep_assert_held_write(&card->ctl->controls_rwsem);
id = kcontrol->id;
if (id.index > UINT_MAX - kcontrol->count)
@@ -496,10 +535,10 @@ static int __snd_ctl_add_replace(struct snd_card *card,
if (snd_ctl_find_hole(card, kcontrol->count) < 0)
return -ENOMEM;
- list_add_tail(&kcontrol->list, &card->controls);
- card->controls_count += kcontrol->count;
- kcontrol->id.numid = card->last_numid + 1;
- card->last_numid += kcontrol->count;
+ list_add_tail(&kcontrol->list, &card->ctl->controls);
+ card->ctl->controls_count += kcontrol->count;
+ kcontrol->id.numid = card->ctl->last_numid + 1;
+ card->ctl->last_numid += kcontrol->count;
add_hash_entries(card, kcontrol);
@@ -520,9 +559,9 @@ static int snd_ctl_add_replace(struct snd_card *card,
if (snd_BUG_ON(!card || !kcontrol->info))
goto error;
- down_write(&card->controls_rwsem);
+ down_write(&card->ctl->controls_rwsem);
err = __snd_ctl_add_replace(card, kcontrol, mode);
- up_write(&card->controls_rwsem);
+ up_write(&card->ctl->controls_rwsem);
if (err < 0)
goto error;
return 0;
@@ -580,7 +619,7 @@ static int __snd_ctl_remove(struct snd_card *card,
{
unsigned int idx;
- lockdep_assert_held_write(&card->controls_rwsem);
+ lockdep_assert_held_write(&card->ctl->controls_rwsem);
if (snd_BUG_ON(!card || !kcontrol))
return -EINVAL;
@@ -589,7 +628,7 @@ static int __snd_ctl_remove(struct snd_card *card,
if (remove_hash)
remove_hash_entries(card, kcontrol);
- card->controls_count -= kcontrol->count;
+ card->ctl->controls_count -= kcontrol->count;
for (idx = 0; idx < kcontrol->count; idx++)
snd_ctl_notify_one(card, SNDRV_CTL_EVENT_MASK_REMOVE, kcontrol, idx);
snd_ctl_free_one(kcontrol);
@@ -618,9 +657,9 @@ int snd_ctl_remove(struct snd_card *card, struct snd_kcontrol *kcontrol)
{
int ret;
- down_write(&card->controls_rwsem);
+ down_write(&card->ctl->controls_rwsem);
ret = snd_ctl_remove_locked(card, kcontrol);
- up_write(&card->controls_rwsem);
+ up_write(&card->ctl->controls_rwsem);
return ret;
}
EXPORT_SYMBOL(snd_ctl_remove);
@@ -640,14 +679,14 @@ int snd_ctl_remove_id(struct snd_card *card, struct snd_ctl_elem_id *id)
struct snd_kcontrol *kctl;
int ret;
- down_write(&card->controls_rwsem);
+ down_write(&card->ctl->controls_rwsem);
kctl = snd_ctl_find_id_locked(card, id);
if (kctl == NULL) {
- up_write(&card->controls_rwsem);
+ up_write(&card->ctl->controls_rwsem);
return -ENOENT;
}
ret = snd_ctl_remove_locked(card, kctl);
- up_write(&card->controls_rwsem);
+ up_write(&card->ctl->controls_rwsem);
return ret;
}
EXPORT_SYMBOL(snd_ctl_remove_id);
@@ -669,7 +708,7 @@ static int snd_ctl_remove_user_ctl(struct snd_ctl_file * file,
struct snd_kcontrol *kctl;
int idx, ret;
- down_write(&card->controls_rwsem);
+ down_write(&card->ctl->controls_rwsem);
kctl = snd_ctl_find_id_locked(card, id);
if (kctl == NULL) {
ret = -ENOENT;
@@ -686,7 +725,7 @@ static int snd_ctl_remove_user_ctl(struct snd_ctl_file * file,
}
ret = snd_ctl_remove_locked(card, kctl);
error:
- up_write(&card->controls_rwsem);
+ up_write(&card->ctl->controls_rwsem);
return ret;
}
@@ -710,7 +749,7 @@ int snd_ctl_activate_id(struct snd_card *card, struct snd_ctl_elem_id *id,
unsigned int index_offset;
int ret;
- down_write(&card->controls_rwsem);
+ down_write(&card->ctl->controls_rwsem);
kctl = snd_ctl_find_id_locked(card, id);
if (kctl == NULL) {
ret = -ENOENT;
@@ -729,13 +768,13 @@ int snd_ctl_activate_id(struct snd_card *card, struct snd_ctl_elem_id *id,
vd->access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
}
snd_ctl_build_ioff(id, kctl, index_offset);
- downgrade_write(&card->controls_rwsem);
+ downgrade_write(&card->ctl->controls_rwsem);
snd_ctl_notify_one(card, SNDRV_CTL_EVENT_MASK_INFO, kctl, index_offset);
- up_read(&card->controls_rwsem);
+ up_read(&card->ctl->controls_rwsem);
return 1;
unlock:
- up_write(&card->controls_rwsem);
+ up_write(&card->ctl->controls_rwsem);
return ret;
}
EXPORT_SYMBOL_GPL(snd_ctl_activate_id);
@@ -764,10 +803,10 @@ int snd_ctl_rename_id(struct snd_card *card, struct snd_ctl_elem_id *src_id,
struct snd_kcontrol *kctl;
int saved_numid;
- down_write(&card->controls_rwsem);
+ down_write(&card->ctl->controls_rwsem);
kctl = snd_ctl_find_id_locked(card, src_id);
if (kctl == NULL) {
- up_write(&card->controls_rwsem);
+ up_write(&card->ctl->controls_rwsem);
return -ENOENT;
}
saved_numid = kctl->id.numid;
@@ -775,7 +814,7 @@ int snd_ctl_rename_id(struct snd_card *card, struct snd_ctl_elem_id *src_id,
kctl->id = *dst_id;
kctl->id.numid = saved_numid;
add_hash_entries(card, kctl);
- up_write(&card->controls_rwsem);
+ up_write(&card->ctl->controls_rwsem);
return 0;
}
EXPORT_SYMBOL(snd_ctl_rename_id);
@@ -793,7 +832,7 @@ EXPORT_SYMBOL(snd_ctl_rename_id);
void snd_ctl_rename(struct snd_card *card, struct snd_kcontrol *kctl,
const char *name)
{
- down_write(&card->controls_rwsem);
+ down_write(&card->ctl->controls_rwsem);
remove_hash_entries(card, kctl);
if (strscpy(kctl->id.name, name, sizeof(kctl->id.name)) < 0)
@@ -801,7 +840,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);
+ up_write(&card->ctl->controls_rwsem);
}
EXPORT_SYMBOL(snd_ctl_rename);
@@ -836,9 +875,9 @@ 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);
+ lockdep_assert_held(&card->ctl->controls_rwsem);
#ifdef CONFIG_SND_CTL_FAST_LOOKUP
- return xa_load(&card->ctl_numids, numid);
+ return xa_load(&card->ctl->numids, numid);
#else
return snd_ctl_find_numid_slow(card, numid);
#endif
@@ -861,9 +900,9 @@ struct snd_kcontrol *snd_ctl_find_numid(struct snd_card *card,
{
struct snd_kcontrol *kctl;
- down_read(&card->controls_rwsem);
+ down_read(&card->ctl->controls_rwsem);
kctl = snd_ctl_find_numid_locked(card, numid);
- up_read(&card->controls_rwsem);
+ up_read(&card->ctl->controls_rwsem);
return kctl;
}
EXPORT_SYMBOL(snd_ctl_find_numid);
@@ -887,18 +926,18 @@ struct snd_kcontrol *snd_ctl_find_id_locked(struct snd_card *card,
if (snd_BUG_ON(!card || !id))
return NULL;
- lockdep_assert_held(&card->controls_rwsem);
+ lockdep_assert_held(&card->ctl->controls_rwsem);
if (id->numid != 0)
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));
+ kctl = xa_load(&card->ctl->hash, get_ctl_id_hash(id));
if (kctl && elem_id_matches(kctl, id))
return kctl;
- if (!card->ctl_hash_collision)
+ if (!card->ctl->hash_collision)
return NULL; /* we can rely on only hash table */
#endif
/* no matching in hash table - try all as the last resort */
- list_for_each_entry(kctl, &card->controls, list)
+ list_for_each_entry(kctl, &card->ctl->controls, list)
if (elem_id_matches(kctl, id))
return kctl;
@@ -922,9 +961,9 @@ struct snd_kcontrol *snd_ctl_find_id(struct snd_card *card,
{
struct snd_kcontrol *kctl;
- down_read(&card->controls_rwsem);
+ down_read(&card->ctl->controls_rwsem);
kctl = snd_ctl_find_id_locked(card, id);
- up_read(&card->controls_rwsem);
+ up_read(&card->ctl->controls_rwsem);
return kctl;
}
EXPORT_SYMBOL(snd_ctl_find_id);
@@ -965,11 +1004,11 @@ static int snd_ctl_elem_list(struct snd_card *card,
offset = list->offset;
space = list->space;
- down_read(&card->controls_rwsem);
- list->count = card->controls_count;
+ down_read(&card->ctl->controls_rwsem);
+ list->count = card->ctl->controls_count;
list->used = 0;
if (space > 0) {
- list_for_each_entry(kctl, &card->controls, list) {
+ list_for_each_entry(kctl, &card->ctl->controls, list) {
if (offset >= kctl->count) {
offset -= kctl->count;
continue;
@@ -989,7 +1028,7 @@ static int snd_ctl_elem_list(struct snd_card *card,
}
}
out:
- up_read(&card->controls_rwsem);
+ up_read(&card->ctl->controls_rwsem);
return err;
}
@@ -1240,13 +1279,13 @@ static int snd_ctl_elem_info(struct snd_ctl_file *ctl,
struct snd_kcontrol *kctl;
int result;
- down_read(&card->controls_rwsem);
+ down_read(&card->ctl->controls_rwsem);
kctl = snd_ctl_find_id_locked(card, &info->id);
if (kctl == NULL)
result = -ENOENT;
else
result = __snd_ctl_elem_info(card, kctl, info, ctl);
- up_read(&card->controls_rwsem);
+ up_read(&card->ctl->controls_rwsem);
return result;
}
@@ -1279,7 +1318,7 @@ static int snd_ctl_elem_read(struct snd_card *card,
const u32 pattern = 0xdeadbeef;
int ret;
- down_read(&card->controls_rwsem);
+ down_read(&card->ctl->controls_rwsem);
kctl = snd_ctl_find_id_locked(card, &control->id);
if (kctl == NULL) {
ret = -ENOENT;
@@ -1323,7 +1362,7 @@ static int snd_ctl_elem_read(struct snd_card *card,
goto unlock;
}
unlock:
- up_read(&card->controls_rwsem);
+ up_read(&card->ctl->controls_rwsem);
return ret;
}
@@ -1356,10 +1395,10 @@ static int snd_ctl_elem_write(struct snd_card *card, struct snd_ctl_file *file,
unsigned int index_offset;
int result;
- down_write(&card->controls_rwsem);
+ down_write(&card->ctl->controls_rwsem);
kctl = snd_ctl_find_id_locked(card, &control->id);
if (kctl == NULL) {
- up_write(&card->controls_rwsem);
+ up_write(&card->ctl->controls_rwsem);
return -ENOENT;
}
@@ -1367,7 +1406,7 @@ static int snd_ctl_elem_write(struct snd_card *card, struct snd_ctl_file *file,
vd = &kctl->vd[index_offset];
if (!(vd->access & SNDRV_CTL_ELEM_ACCESS_WRITE) || kctl->put == NULL ||
(file && vd->owner && vd->owner != file)) {
- up_write(&card->controls_rwsem);
+ up_write(&card->ctl->controls_rwsem);
return -EPERM;
}
@@ -1388,16 +1427,16 @@ static int snd_ctl_elem_write(struct snd_card *card, struct snd_ctl_file *file,
result = kctl->put(kctl, control);
snd_power_unref(card);
if (result < 0) {
- up_write(&card->controls_rwsem);
+ up_write(&card->ctl->controls_rwsem);
return result;
}
if (result > 0) {
- downgrade_write(&card->controls_rwsem);
+ downgrade_write(&card->ctl->controls_rwsem);
snd_ctl_notify_one(card, SNDRV_CTL_EVENT_MASK_VALUE, kctl, index_offset);
- up_read(&card->controls_rwsem);
+ up_read(&card->ctl->controls_rwsem);
} else {
- up_write(&card->controls_rwsem);
+ up_write(&card->ctl->controls_rwsem);
}
return 0;
@@ -1437,7 +1476,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);
+ down_write(&card->ctl->controls_rwsem);
kctl = snd_ctl_find_id_locked(card, &id);
if (kctl == NULL) {
result = -ENOENT;
@@ -1450,7 +1489,7 @@ static int snd_ctl_elem_lock(struct snd_ctl_file *file,
result = 0;
}
}
- up_write(&card->controls_rwsem);
+ up_write(&card->ctl->controls_rwsem);
return result;
}
@@ -1465,7 +1504,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);
+ down_write(&card->ctl->controls_rwsem);
kctl = snd_ctl_find_id_locked(card, &id);
if (kctl == NULL) {
result = -ENOENT;
@@ -1480,7 +1519,7 @@ static int snd_ctl_elem_unlock(struct snd_ctl_file *file,
result = 0;
}
}
- up_write(&card->controls_rwsem);
+ up_write(&card->ctl->controls_rwsem);
return result;
}
@@ -1497,7 +1536,7 @@ struct user_element {
// check whether the addition (in bytes) of user ctl element may overflow the limit.
static bool check_user_elem_overflow(struct snd_card *card, ssize_t add)
{
- return (ssize_t)card->user_ctl_alloc_size + add > max_user_ctl_alloc_size;
+ return (ssize_t)card->ctl->user_ctl_alloc_size + add > max_user_ctl_alloc_size;
}
static int snd_ctl_elem_user_info(struct snd_kcontrol *kcontrol,
@@ -1575,7 +1614,7 @@ 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);
+ lockdep_assert_held_write(&ue->card->ctl->controls_rwsem);
if (size > 1024 * 128) /* sane value */
return -EINVAL;
@@ -1602,7 +1641,7 @@ static int replace_user_tlv(struct snd_kcontrol *kctl, unsigned int __user *buf,
kctl->vd[i].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
mask = SNDRV_CTL_EVENT_MASK_INFO;
} else {
- ue->card->user_ctl_alloc_size -= ue->tlv_data_size;
+ ue->card->ctl->user_ctl_alloc_size -= ue->tlv_data_size;
ue->tlv_data_size = 0;
kvfree(ue->tlv_data);
}
@@ -1610,7 +1649,7 @@ static int replace_user_tlv(struct snd_kcontrol *kctl, unsigned int __user *buf,
ue->tlv_data = container;
ue->tlv_data_size = size;
// decremented at private_free.
- ue->card->user_ctl_alloc_size += size;
+ ue->card->ctl->user_ctl_alloc_size += size;
mask |= SNDRV_CTL_EVENT_MASK_TLV;
for (i = 0; i < kctl->count; ++i)
@@ -1653,7 +1692,7 @@ 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);
+ lockdep_assert_held_write(&ue->card->ctl->controls_rwsem);
buf_len = ue->info.value.enumerated.names_length;
if (buf_len > 64 * 1024)
@@ -1680,7 +1719,7 @@ static int snd_ctl_elem_init_enum_names(struct user_element *ue)
ue->priv_data = names;
ue->info.value.enumerated.names_ptr = 0;
// increment the allocation size; decremented again at private_free.
- ue->card->user_ctl_alloc_size += ue->info.value.enumerated.names_length;
+ ue->card->ctl->user_ctl_alloc_size += ue->info.value.enumerated.names_length;
return 0;
}
@@ -1695,10 +1734,10 @@ static void snd_ctl_elem_user_free(struct snd_kcontrol *kcontrol)
struct user_element *ue = kcontrol->private_data;
// decrement the allocation size.
- ue->card->user_ctl_alloc_size -= compute_user_elem_size(ue->elem_data_size, kcontrol->count);
- ue->card->user_ctl_alloc_size -= ue->tlv_data_size;
+ ue->card->ctl->user_ctl_alloc_size -= compute_user_elem_size(ue->elem_data_size, kcontrol->count);
+ ue->card->ctl->user_ctl_alloc_size -= ue->tlv_data_size;
if (ue->priv_data)
- ue->card->user_ctl_alloc_size -= ue->info.value.enumerated.names_length;
+ ue->card->ctl->user_ctl_alloc_size -= ue->info.value.enumerated.names_length;
kvfree(ue->tlv_data);
kvfree(ue->priv_data);
@@ -1763,7 +1802,7 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file,
private_size = value_sizes[info->type] * info->count;
alloc_size = compute_user_elem_size(private_size, count);
- down_write(&card->controls_rwsem);
+ down_write(&card->ctl->controls_rwsem);
if (check_user_elem_overflow(card, alloc_size)) {
err = -ENOMEM;
goto unlock;
@@ -1789,7 +1828,7 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file,
kctl->private_free = snd_ctl_elem_user_free;
// increment the allocated size; decremented again at private_free.
- card->user_ctl_alloc_size += alloc_size;
+ card->ctl->user_ctl_alloc_size += alloc_size;
/* Set private data for this userspace control. */
ue->card = card;
@@ -1833,7 +1872,7 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file,
* which locks the element.
*/
unlock:
- up_write(&card->controls_rwsem);
+ up_write(&card->ctl->controls_rwsem);
return err;
}
@@ -1959,7 +1998,7 @@ 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);
+ lockdep_assert_held(&file->card->ctl->controls_rwsem);
if (copy_from_user(&header, buf, sizeof(header)))
return -EFAULT;
@@ -2036,19 +2075,19 @@ static long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg
case SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS:
return snd_ctl_subscribe_events(ctl, ip);
case SNDRV_CTL_IOCTL_TLV_READ:
- down_read(&ctl->card->controls_rwsem);
+ down_read(&ctl->card->ctl->controls_rwsem);
err = snd_ctl_tlv_ioctl(ctl, argp, SNDRV_CTL_TLV_OP_READ);
- up_read(&ctl->card->controls_rwsem);
+ up_read(&ctl->card->ctl->controls_rwsem);
return err;
case SNDRV_CTL_IOCTL_TLV_WRITE:
- down_write(&ctl->card->controls_rwsem);
+ down_write(&ctl->card->ctl->controls_rwsem);
err = snd_ctl_tlv_ioctl(ctl, argp, SNDRV_CTL_TLV_OP_WRITE);
- up_write(&ctl->card->controls_rwsem);
+ up_write(&ctl->card->ctl->controls_rwsem);
return err;
case SNDRV_CTL_IOCTL_TLV_COMMAND:
- down_write(&ctl->card->controls_rwsem);
+ down_write(&ctl->card->ctl->controls_rwsem);
err = snd_ctl_tlv_ioctl(ctl, argp, SNDRV_CTL_TLV_OP_CMD);
- up_write(&ctl->card->controls_rwsem);
+ up_write(&ctl->card->ctl->controls_rwsem);
return err;
case SNDRV_CTL_IOCTL_POWER:
return -ENOPROTOOPT;
@@ -2258,15 +2297,15 @@ int snd_ctl_get_preferred_subdevice(struct snd_card *card, int type)
int subdevice = -1;
unsigned long flags;
- read_lock_irqsave(&card->ctl_files_rwlock, flags);
- list_for_each_entry(kctl, &card->ctl_files, list) {
+ read_lock_irqsave(&card->ctl->files_rwlock, flags);
+ list_for_each_entry(kctl, &card->ctl->files, list) {
if (kctl->pid == task_pid(current)) {
subdevice = kctl->preferred_subdevice[type];
if (subdevice != -1)
break;
}
}
- read_unlock_irqrestore(&card->ctl_files_rwlock, flags);
+ read_unlock_irqrestore(&card->ctl->files_rwlock, flags);
return subdevice;
}
EXPORT_SYMBOL_GPL(snd_ctl_get_preferred_subdevice);
@@ -2326,9 +2365,9 @@ void snd_ctl_register_layer(struct snd_ctl_layer_ops *lops)
for (card_number = 0; card_number < SNDRV_CARDS; card_number++) {
card = snd_card_ref(card_number);
if (card) {
- down_read(&card->controls_rwsem);
+ down_read(&card->ctl->controls_rwsem);
lops->lregister(card);
- up_read(&card->controls_rwsem);
+ up_read(&card->ctl->controls_rwsem);
snd_card_unref(card);
}
}
@@ -2389,15 +2428,15 @@ static int snd_ctl_dev_register(struct snd_device *device)
int err;
err = snd_register_device(SNDRV_DEVICE_TYPE_CONTROL, card, -1,
- &snd_ctl_f_ops, card, card->ctl_dev);
+ &snd_ctl_f_ops, card, &card->ctl->dev);
if (err < 0)
return err;
- down_read(&card->controls_rwsem);
+ down_read(&card->ctl->controls_rwsem);
down_read(&snd_ctl_layer_rwsem);
for (lops = snd_ctl_layer; lops; lops = lops->next)
lops->lregister(card);
up_read(&snd_ctl_layer_rwsem);
- up_read(&card->controls_rwsem);
+ up_read(&card->ctl->controls_rwsem);
return 0;
}
@@ -2411,21 +2450,21 @@ static int snd_ctl_dev_disconnect(struct snd_device *device)
struct snd_ctl_layer_ops *lops;
unsigned long flags;
- read_lock_irqsave(&card->ctl_files_rwlock, flags);
- list_for_each_entry(ctl, &card->ctl_files, list) {
+ read_lock_irqsave(&card->ctl->files_rwlock, flags);
+ list_for_each_entry(ctl, &card->ctl->files, list) {
wake_up(&ctl->change_sleep);
snd_kill_fasync(ctl->fasync, SIGIO, POLL_ERR);
}
- read_unlock_irqrestore(&card->ctl_files_rwlock, flags);
+ read_unlock_irqrestore(&card->ctl->files_rwlock, flags);
- down_read(&card->controls_rwsem);
+ down_read(&card->ctl->controls_rwsem);
down_read(&snd_ctl_layer_rwsem);
for (lops = snd_ctl_layer; lops; lops = lops->next)
lops->ldisconnect(card);
up_read(&snd_ctl_layer_rwsem);
- up_read(&card->controls_rwsem);
+ up_read(&card->ctl->controls_rwsem);
- return snd_unregister_device(card->ctl_dev);
+ return snd_unregister_device(&card->ctl->dev);
}
/*
@@ -2436,21 +2475,28 @@ static int snd_ctl_dev_free(struct snd_device *device)
struct snd_card *card = device->device_data;
struct snd_kcontrol *control;
- down_write(&card->controls_rwsem);
- while (!list_empty(&card->controls)) {
- control = snd_kcontrol(card->controls.next);
+ down_write(&card->ctl->controls_rwsem);
+ while (!list_empty(&card->ctl->controls)) {
+ control = snd_kcontrol(card->ctl->controls.next);
__snd_ctl_remove(card, control, false);
}
#ifdef CONFIG_SND_CTL_FAST_LOOKUP
- xa_destroy(&card->ctl_numids);
- xa_destroy(&card->ctl_hash);
+ xa_destroy(&card->ctl->numids);
+ xa_destroy(&card->ctl->hash);
#endif
- up_write(&card->controls_rwsem);
- put_device(card->ctl_dev);
+ up_write(&card->ctl->controls_rwsem);
+ put_device(&card->ctl->dev);
return 0;
}
+static void release_control_device(struct device *dev)
+{
+ struct snd_control *ctl = container_of(dev, struct snd_control, dev);
+
+ kfree(ctl);
+}
+
/*
* create control core:
* called from init.c
@@ -2462,6 +2508,7 @@ int snd_ctl_create(struct snd_card *card)
.dev_register = snd_ctl_dev_register,
.dev_disconnect = snd_ctl_dev_disconnect,
};
+ struct snd_control *ctl = card->ctl;
int err;
if (snd_BUG_ON(!card))
@@ -2469,14 +2516,13 @@ int snd_ctl_create(struct snd_card *card)
if (snd_BUG_ON(card->number < 0 || card->number >= SNDRV_CARDS))
return -ENXIO;
- err = snd_device_alloc(&card->ctl_dev, card);
- if (err < 0)
- return err;
- dev_set_name(card->ctl_dev, "controlC%d", card->number);
+ snd_device_init(&ctl->dev, card);
+ dev_set_name(&ctl->dev, "controlC%d", card->number);
+ ctl->dev.release = release_control_device;
err = snd_device_new(card, SNDRV_DEV_CONTROL, card, &ops);
if (err < 0)
- put_device(card->ctl_dev);
+ put_device(&ctl->dev);
return err;
}
@@ -172,15 +172,15 @@ static int get_ctl_type(struct snd_card *card, struct snd_ctl_elem_id *id,
struct snd_ctl_elem_info *info;
int err;
- down_read(&card->controls_rwsem);
+ down_read(&card->ctl->controls_rwsem);
kctl = snd_ctl_find_id_locked(card, id);
if (! kctl) {
- up_read(&card->controls_rwsem);
+ up_read(&card->ctl->controls_rwsem);
return -ENOENT;
}
info = kzalloc(sizeof(*info), GFP_KERNEL);
if (info == NULL) {
- up_read(&card->controls_rwsem);
+ up_read(&card->ctl->controls_rwsem);
return -ENOMEM;
}
info->id = *id;
@@ -188,7 +188,7 @@ static int get_ctl_type(struct snd_card *card, struct snd_ctl_elem_id *id,
if (!err)
err = kctl->info(kctl, info);
snd_power_unref(card);
- up_read(&card->controls_rwsem);
+ up_read(&card->ctl->controls_rwsem);
if (err >= 0) {
err = info->type;
*countp = info->count;
@@ -243,6 +243,7 @@ static int snd_ctl_led_set_id(int card_number, struct snd_ctl_elem_id *id,
unsigned int group, bool set)
{
struct snd_card *card;
+ struct snd_control *ctl;
struct snd_kcontrol *kctl;
struct snd_kcontrol_volatile *vd;
unsigned int ioff, access, new_access;
@@ -250,7 +251,8 @@ 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);
+ ctl = card->ctl;
+ down_write(&ctl->controls_rwsem);
kctl = snd_ctl_find_id_locked(card, id);
if (kctl) {
ioff = snd_ctl_get_ioff(kctl, id);
@@ -271,7 +273,7 @@ static int snd_ctl_led_set_id(int card_number, struct snd_ctl_elem_id *id,
err = -ENOENT;
}
unlock:
- up_write(&card->controls_rwsem);
+ up_write(&ctl->controls_rwsem);
snd_card_unref(card);
} else {
err = -ENXIO;
@@ -357,7 +359,7 @@ static void snd_ctl_led_register(struct snd_card *card)
snd_ctl_led_card_valid[card->number] = true;
mutex_unlock(&snd_ctl_led_mutex);
/* the register callback is already called with held card->controls_rwsem */
- list_for_each_entry(kctl, &card->controls, list)
+ list_for_each_entry(kctl, &card->ctl->controls, list)
for (ioff = 0; ioff < kctl->count; ioff++)
snd_ctl_led_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, kctl, ioff);
snd_ctl_led_refresh();
@@ -617,12 +619,14 @@ static ssize_t list_show(struct device *dev,
struct snd_ctl_led_card *led_card = container_of(dev, struct snd_ctl_led_card, dev);
struct snd_card *card;
struct snd_ctl_led_ctl *lctl;
+ struct snd_control *ctl;
size_t l = 0;
card = snd_card_ref(led_card->number);
if (!card)
return -ENXIO;
- down_read(&card->controls_rwsem);
+ ctl = card->ctl;
+ down_read(&ctl->controls_rwsem);
mutex_lock(&snd_ctl_led_mutex);
if (snd_ctl_led_card_valid[led_card->number]) {
list_for_each_entry(lctl, &led_card->led->controls, list) {
@@ -635,7 +639,7 @@ static ssize_t list_show(struct device *dev,
}
}
mutex_unlock(&snd_ctl_led_mutex);
- up_read(&card->controls_rwsem);
+ up_read(&ctl->controls_rwsem);
snd_card_unref(card);
return l;
}
@@ -688,7 +692,7 @@ static void snd_ctl_led_sysfs_add(struct snd_card *card)
goto cerr;
led->cards[card->number] = led_card;
snprintf(link_name, sizeof(link_name), "led-%s", led->name);
- WARN(sysfs_create_link(&card->ctl_dev->kobj, &led_card->dev.kobj, link_name),
+ WARN(sysfs_create_link(&card->ctl->dev.kobj, &led_card->dev.kobj, link_name),
"can't create symlink to controlC%i device\n", card->number);
WARN(sysfs_create_link(&led_card->dev.kobj, &card->card_dev.kobj, "card"),
"can't create symlink to card%i\n", card->number);
@@ -714,7 +718,7 @@ static void snd_ctl_led_sysfs_remove(struct snd_card *card)
if (!led_card)
continue;
snprintf(link_name, sizeof(link_name), "led-%s", led->name);
- sysfs_remove_link(&card->ctl_dev->kobj, link_name);
+ sysfs_remove_link(&card->ctl->dev.kobj, link_name);
sysfs_remove_link(&led_card->dev.kobj, "card");
device_unregister(&led_card->dev);
led->cards[card->number] = NULL;
@@ -331,14 +331,6 @@ static int snd_card_init(struct snd_card *card, struct device *parent,
card->module = module;
#endif
INIT_LIST_HEAD(&card->devices);
- init_rwsem(&card->controls_rwsem);
- rwlock_init(&card->ctl_files_rwlock);
- INIT_LIST_HEAD(&card->controls);
- INIT_LIST_HEAD(&card->ctl_files);
-#ifdef CONFIG_SND_CTL_FAST_LOOKUP
- xa_init(&card->ctl_numids);
- xa_init(&card->ctl_hash);
-#endif
spin_lock_init(&card->files_lock);
INIT_LIST_HEAD(&card->files_list);
mutex_init(&card->memory_mutex);
@@ -363,11 +355,9 @@ static int snd_card_init(struct snd_card *card, struct device *parent,
snprintf(card->irq_descr, sizeof(card->irq_descr), "%s:%s",
dev_driver_string(card->dev), dev_name(&card->card_dev));
- /* the control interface cannot be accessed from the user space until */
- /* snd_cards_bitmask and snd_cards are set with snd_card_register */
- err = snd_ctl_create(card);
+ err = snd_control_new(card);
if (err < 0) {
- dev_err(parent, "unable to register control minors\n");
+ dev_err(parent, "unable to create controls and register\n");
goto __error;
}
err = snd_info_card_create(card);
@@ -1784,13 +1784,14 @@ void snd_hda_ctls_clear(struct hda_codec *codec)
int snd_hda_lock_devices(struct hda_bus *bus)
{
struct snd_card *card = bus->card;
+ struct snd_control *ctl = card->ctl;
struct hda_codec *codec;
spin_lock(&card->files_lock);
if (card->shutdown)
goto err_unlock;
card->shutdown = 1;
- if (!list_empty(&card->ctl_files))
+ if (!list_empty(&ctl->files))
goto err_clear;
list_for_each_codec(codec, bus) {
@@ -1434,9 +1434,9 @@ static int sst_fill_widget_module_info(struct snd_soc_dapm_widget *w,
struct snd_card *card = component->card->snd_card;
char *idx;
- down_read(&card->controls_rwsem);
+ down_read(&card->ctl->controls_rwsem);
- list_for_each_entry(kctl, &card->controls, list) {
+ list_for_each_entry(kctl, &card->ctl->controls, list) {
idx = strchr(kctl->id.name, ' ');
if (idx == NULL)
continue;
@@ -1469,12 +1469,12 @@ static int sst_fill_widget_module_info(struct snd_soc_dapm_widget *w,
}
if (ret < 0) {
- up_read(&card->controls_rwsem);
+ up_read(&card->ctl->controls_rwsem);
return ret;
}
}
- up_read(&card->controls_rwsem);
+ up_read(&card->ctl->controls_rwsem);
return 0;
}
@@ -35,7 +35,7 @@ struct snd_kcontrol *snd_soc_card_get_kcontrol(struct snd_soc_card *soc_card,
if (unlikely(!name))
return NULL;
- list_for_each_entry(kctl, &card->controls, list)
+ list_for_each_entry(kctl, &card->ctl->controls, list)
if (!strncmp(kctl->id.name, name, sizeof(kctl->id.name)))
return kctl;
return NULL;
@@ -163,7 +163,7 @@ void snd_media_stop_pipeline(struct snd_usb_substream *subs)
static int snd_media_mixer_init(struct snd_usb_audio *chip)
{
- struct device *ctl_dev = chip->card->ctl_dev;
+ struct device *ctl_dev = &chip->card->ctl->dev;
struct media_intf_devnode *ctl_intf;
struct usb_mixer_interface *mixer;
struct media_device *mdev = chip->media_dev;