summaryrefslogtreecommitdiff
path: root/sound/core
diff options
context:
space:
mode:
Diffstat (limited to 'sound/core')
-rw-r--r--sound/core/Kconfig37
-rw-r--r--sound/core/compress_offload.c9
-rw-r--r--sound/core/control.c290
-rw-r--r--sound/core/device.c2
-rw-r--r--sound/core/info.c2
-rw-r--r--sound/core/init.c14
-rw-r--r--sound/core/isadma.c3
-rw-r--r--sound/core/memalloc.c10
-rw-r--r--sound/core/misc.c94
-rw-r--r--sound/core/pcm.c5
-rw-r--r--sound/core/pcm_dmaengine.c30
-rw-r--r--sound/core/pcm_lib.c2
-rw-r--r--sound/core/pcm_memory.c4
-rw-r--r--sound/core/pcm_native.c8
-rw-r--r--sound/core/rawmidi.c274
-rw-r--r--sound/core/timer.c11
-rw-r--r--sound/core/vmaster.c3
17 files changed, 591 insertions, 207 deletions
diff --git a/sound/core/Kconfig b/sound/core/Kconfig
index dd7b40734723..12990d9a4dff 100644
--- a/sound/core/Kconfig
+++ b/sound/core/Kconfig
@@ -154,6 +154,16 @@ config SND_VERBOSE_PRINTK
You don't need this unless you're debugging ALSA.
+config SND_CTL_FAST_LOOKUP
+ bool "Fast lookup of control elements" if EXPERT
+ default y
+ select XARRAY_MULTI
+ help
+ This option enables the faster lookup of control elements.
+ It will consume more memory because of the additional Xarray.
+ If you want to choose the memory footprint over the performance
+ inevitably, turn this off.
+
config SND_DEBUG
bool "Debug"
help
@@ -178,14 +188,29 @@ config SND_PCM_XRUN_DEBUG
sound clicking when system is loaded, it may help to determine
the process or driver which causes the scheduling gaps.
-config SND_CTL_VALIDATION
- bool "Perform sanity-checks for each control element access"
+config SND_CTL_INPUT_VALIDATION
+ bool "Validate input data to control API"
+ help
+ Say Y to enable the additional validation for the input data to
+ each control element, including the value range checks.
+ An error is returned from ALSA core for invalid inputs without
+ passing to the driver. This is a kind of hardening for drivers
+ that have no proper error checks, at the cost of a slight
+ performance overhead.
+
+config SND_CTL_DEBUG
+ bool "Enable debugging feature for control API"
depends on SND_DEBUG
help
- Say Y to enable the additional validation of each control element
- access, including sanity-checks like whether the values returned
- from the driver are in the proper ranges or the check of the invalid
- access at out-of-array areas.
+ Say Y to enable the debugging feature for ALSA control API.
+ It performs the additional sanity-checks for each control element
+ read access, such as whether the values returned from the driver
+ are in the proper ranges or the check of the invalid access at
+ out-of-array areas. The error is printed when the driver gives
+ such unexpected values.
+ When you develop a driver that deals with control elements, it's
+ strongly recommended to try this one once and verify whether you see
+ any relevant errors or not.
config SND_JACK_INJECTION_DEBUG
bool "Sound jack injection interface via debugfs"
diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c
index de514ec8c83d..243acad89fd3 100644
--- a/sound/core/compress_offload.c
+++ b/sound/core/compress_offload.c
@@ -810,7 +810,7 @@ static void error_delayed_work(struct work_struct *work)
mutex_unlock(&stream->device->lock);
}
-/*
+/**
* snd_compr_stop_error: Report a fatal error on a stream
* @stream: pointer to stream
* @state: state to transition the stream to
@@ -818,6 +818,8 @@ static void error_delayed_work(struct work_struct *work)
* Stop the stream and set its state.
*
* Should be called with compressed device lock held.
+ *
+ * Return: zero if successful, or a negative error code
*/
int snd_compr_stop_error(struct snd_compr_stream *stream,
snd_pcm_state_t state)
@@ -1157,12 +1159,15 @@ static int snd_compress_dev_free(struct snd_device *device)
return 0;
}
-/*
+/**
* snd_compress_new: create new compress device
* @card: sound card pointer
* @device: device number
* @dirn: device direction, should be of type enum snd_compr_direction
+ * @id: ID string
* @compr: compress device pointer
+ *
+ * Return: zero if successful, or a negative error code
*/
int snd_compress_new(struct snd_card *card, int device,
int dirn, const char *id, struct snd_compr *compr)
diff --git a/sound/core/control.c b/sound/core/control.c
index a25c0d64d104..f3e893715369 100644
--- a/sound/core/control.c
+++ b/sound/core/control.c
@@ -127,6 +127,7 @@ static int snd_ctl_release(struct inode *inode, struct file *file)
if (control->vd[idx].owner == ctl)
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);
@@ -181,7 +182,7 @@ void snd_ctl_notify(struct snd_card *card, unsigned int mask,
_found:
wake_up(&ctl->change_sleep);
spin_unlock(&ctl->read_lock);
- kill_fasync(&ctl->fasync, SIGIO, POLL_IN);
+ snd_kill_fasync(ctl->fasync, SIGIO, POLL_IN);
}
read_unlock_irqrestore(&card->ctl_files_rwlock, flags);
}
@@ -364,6 +365,93 @@ static int snd_ctl_find_hole(struct snd_card *card, unsigned int count)
return 0;
}
+/* check whether the given id is contained in the given kctl */
+static bool elem_id_matches(const struct snd_kcontrol *kctl,
+ const struct snd_ctl_elem_id *id)
+{
+ return kctl->id.iface == id->iface &&
+ kctl->id.device == id->device &&
+ kctl->id.subdevice == id->subdevice &&
+ !strncmp(kctl->id.name, id->name, sizeof(kctl->id.name)) &&
+ kctl->id.index <= id->index &&
+ kctl->id.index + kctl->count > id->index;
+}
+
+#ifdef CONFIG_SND_CTL_FAST_LOOKUP
+/* Compute a hash key for the corresponding ctl id
+ * It's for the name lookup, hence the numid is excluded.
+ * The hash key is bound in LONG_MAX to be used for Xarray key.
+ */
+#define MULTIPLIER 37
+static unsigned long get_ctl_id_hash(const struct snd_ctl_elem_id *id)
+{
+ unsigned long h;
+ const unsigned char *p;
+
+ h = id->iface;
+ h = MULTIPLIER * h + id->device;
+ h = MULTIPLIER * h + id->subdevice;
+ for (p = id->name; *p; p++)
+ h = MULTIPLIER * h + *p;
+ h = MULTIPLIER * h + id->index;
+ h &= LONG_MAX;
+ return h;
+}
+
+/* add hash entries to numid and ctl xarray tables */
+static void add_hash_entries(struct snd_card *card,
+ struct snd_kcontrol *kcontrol)
+{
+ struct snd_ctl_elem_id id = kcontrol->id;
+ int i;
+
+ xa_store_range(&card->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),
+ kcontrol, GFP_KERNEL)) {
+ /* skip hash for this entry, noting we had collision */
+ card->ctl_hash_collision = true;
+ dev_dbg(card->dev, "ctl_hash collision %d:%s:%d\n",
+ id.iface, id.name, id.index);
+ }
+ }
+}
+
+/* remove hash entries that have been added */
+static void remove_hash_entries(struct snd_card *card,
+ struct snd_kcontrol *kcontrol)
+{
+ struct snd_ctl_elem_id id = kcontrol->id;
+ struct snd_kcontrol *matched;
+ unsigned long h;
+ int i;
+
+ for (i = 0; i < kcontrol->count; i++) {
+ xa_erase(&card->ctl_numids, id.numid);
+ h = get_ctl_id_hash(&id);
+ matched = xa_load(&card->ctl_hash, h);
+ if (matched && (matched == kcontrol ||
+ elem_id_matches(matched, &id)))
+ xa_erase(&card->ctl_hash, h);
+ id.index++;
+ id.numid++;
+ }
+}
+#else /* CONFIG_SND_CTL_FAST_LOOKUP */
+static inline void add_hash_entries(struct snd_card *card,
+ struct snd_kcontrol *kcontrol)
+{
+}
+static inline void remove_hash_entries(struct snd_card *card,
+ struct snd_kcontrol *kcontrol)
+{
+}
+#endif /* CONFIG_SND_CTL_FAST_LOOKUP */
+
enum snd_ctl_add_mode {
CTL_ADD_EXCLUSIVE, CTL_REPLACE, CTL_ADD_ON_REPLACE,
};
@@ -408,6 +496,8 @@ static int __snd_ctl_add_replace(struct snd_card *card,
kcontrol->id.numid = card->last_numid + 1;
card->last_numid += kcontrol->count;
+ add_hash_entries(card, kcontrol);
+
for (idx = 0; idx < kcontrol->count; idx++)
snd_ctl_notify_one(card, SNDRV_CTL_EVENT_MASK_ADD, kcontrol, idx);
@@ -479,6 +569,26 @@ int snd_ctl_replace(struct snd_card *card, struct snd_kcontrol *kcontrol,
}
EXPORT_SYMBOL(snd_ctl_replace);
+static int __snd_ctl_remove(struct snd_card *card,
+ struct snd_kcontrol *kcontrol,
+ bool remove_hash)
+{
+ unsigned int idx;
+
+ if (snd_BUG_ON(!card || !kcontrol))
+ return -EINVAL;
+ list_del(&kcontrol->list);
+
+ if (remove_hash)
+ remove_hash_entries(card, kcontrol);
+
+ card->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);
+ return 0;
+}
+
/**
* snd_ctl_remove - remove the control from the card and release it
* @card: the card instance
@@ -492,16 +602,7 @@ EXPORT_SYMBOL(snd_ctl_replace);
*/
int snd_ctl_remove(struct snd_card *card, struct snd_kcontrol *kcontrol)
{
- unsigned int idx;
-
- if (snd_BUG_ON(!card || !kcontrol))
- return -EINVAL;
- list_del(&kcontrol->list);
- card->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);
- return 0;
+ return __snd_ctl_remove(card, kcontrol, true);
}
EXPORT_SYMBOL(snd_ctl_remove);
@@ -642,14 +743,30 @@ int snd_ctl_rename_id(struct snd_card *card, struct snd_ctl_elem_id *src_id,
up_write(&card->controls_rwsem);
return -ENOENT;
}
+ remove_hash_entries(card, kctl);
kctl->id = *dst_id;
kctl->id.numid = card->last_numid + 1;
card->last_numid += kctl->count;
+ add_hash_entries(card, kctl);
up_write(&card->controls_rwsem);
return 0;
}
EXPORT_SYMBOL(snd_ctl_rename_id);
+#ifndef CONFIG_SND_CTL_FAST_LOOKUP
+static struct snd_kcontrol *
+snd_ctl_find_numid_slow(struct snd_card *card, unsigned int numid)
+{
+ struct snd_kcontrol *kctl;
+
+ list_for_each_entry(kctl, &card->controls, list) {
+ if (kctl->id.numid <= numid && kctl->id.numid + kctl->count > numid)
+ return kctl;
+ }
+ return NULL;
+}
+#endif /* !CONFIG_SND_CTL_FAST_LOOKUP */
+
/**
* snd_ctl_find_numid - find the control instance with the given number-id
* @card: the card instance
@@ -665,15 +782,13 @@ EXPORT_SYMBOL(snd_ctl_rename_id);
*/
struct snd_kcontrol *snd_ctl_find_numid(struct snd_card *card, unsigned int numid)
{
- struct snd_kcontrol *kctl;
-
if (snd_BUG_ON(!card || !numid))
return NULL;
- list_for_each_entry(kctl, &card->controls, list) {
- if (kctl->id.numid <= numid && kctl->id.numid + kctl->count > numid)
- return kctl;
- }
- return NULL;
+#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);
@@ -699,21 +814,18 @@ struct snd_kcontrol *snd_ctl_find_id(struct snd_card *card,
return NULL;
if (id->numid != 0)
return snd_ctl_find_numid(card, id->numid);
- list_for_each_entry(kctl, &card->controls, list) {
- if (kctl->id.iface != id->iface)
- continue;
- if (kctl->id.device != id->device)
- continue;
- if (kctl->id.subdevice != id->subdevice)
- continue;
- if (strncmp(kctl->id.name, id->name, sizeof(kctl->id.name)))
- continue;
- if (kctl->id.index > id->index)
- continue;
- if (kctl->id.index + kctl->count <= id->index)
- continue;
+#ifdef CONFIG_SND_CTL_FAST_LOOKUP
+ 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)
+ 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)
+ if (elem_id_matches(kctl, id))
+ return kctl;
+
return NULL;
}
EXPORT_SYMBOL(snd_ctl_find_id);
@@ -855,7 +967,6 @@ static const unsigned int value_sizes[] = {
[SNDRV_CTL_ELEM_TYPE_INTEGER64] = sizeof(long long),
};
-#ifdef CONFIG_SND_CTL_VALIDATION
/* fill the remaining snd_ctl_elem_value data with the given pattern */
static void fill_remaining_elem_value(struct snd_ctl_elem_value *control,
struct snd_ctl_elem_info *info,
@@ -872,7 +983,7 @@ static void fill_remaining_elem_value(struct snd_ctl_elem_value *control,
static int sanity_check_int_value(struct snd_card *card,
const struct snd_ctl_elem_value *control,
const struct snd_ctl_elem_info *info,
- int i)
+ int i, bool print_error)
{
long long lval, lmin, lmax, lstep;
u64 rem;
@@ -906,21 +1017,23 @@ static int sanity_check_int_value(struct snd_card *card,
}
if (lval < lmin || lval > lmax) {
- dev_err(card->dev,
- "control %i:%i:%i:%s:%i: value out of range %lld (%lld/%lld) at count %i\n",
- control->id.iface, control->id.device,
- control->id.subdevice, control->id.name,
- control->id.index, lval, lmin, lmax, i);
+ if (print_error)
+ dev_err(card->dev,
+ "control %i:%i:%i:%s:%i: value out of range %lld (%lld/%lld) at count %i\n",
+ control->id.iface, control->id.device,
+ control->id.subdevice, control->id.name,
+ control->id.index, lval, lmin, lmax, i);
return -EINVAL;
}
if (lstep) {
div64_u64_rem(lval, lstep, &rem);
if (rem) {
- dev_err(card->dev,
- "control %i:%i:%i:%s:%i: unaligned value %lld (step %lld) at count %i\n",
- control->id.iface, control->id.device,
- control->id.subdevice, control->id.name,
- control->id.index, lval, lstep, i);
+ if (print_error)
+ dev_err(card->dev,
+ "control %i:%i:%i:%s:%i: unaligned value %lld (step %lld) at count %i\n",
+ control->id.iface, control->id.device,
+ control->id.subdevice, control->id.name,
+ control->id.index, lval, lstep, i);
return -EINVAL;
}
}
@@ -928,15 +1041,13 @@ static int sanity_check_int_value(struct snd_card *card,
return 0;
}
-/* perform sanity checks to the given snd_ctl_elem_value object */
-static int sanity_check_elem_value(struct snd_card *card,
- const struct snd_ctl_elem_value *control,
- const struct snd_ctl_elem_info *info,
- u32 pattern)
+/* check whether the all input values are valid for the given elem value */
+static int sanity_check_input_values(struct snd_card *card,
+ const struct snd_ctl_elem_value *control,
+ const struct snd_ctl_elem_info *info,
+ bool print_error)
{
- size_t offset;
- int i, ret = 0;
- u32 *p;
+ int i, ret;
switch (info->type) {
case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
@@ -944,7 +1055,8 @@ static int sanity_check_elem_value(struct snd_card *card,
case SNDRV_CTL_ELEM_TYPE_INTEGER64:
case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
for (i = 0; i < info->count; i++) {
- ret = sanity_check_int_value(card, control, info, i);
+ ret = sanity_check_int_value(card, control, info, i,
+ print_error);
if (ret < 0)
return ret;
}
@@ -953,6 +1065,23 @@ static int sanity_check_elem_value(struct snd_card *card,
break;
}
+ return 0;
+}
+
+/* perform sanity checks to the given snd_ctl_elem_value object */
+static int sanity_check_elem_value(struct snd_card *card,
+ const struct snd_ctl_elem_value *control,
+ const struct snd_ctl_elem_info *info,
+ u32 pattern)
+{
+ size_t offset;
+ int ret;
+ u32 *p;
+
+ ret = sanity_check_input_values(card, control, info, true);
+ if (ret < 0)
+ return ret;
+
/* check whether the remaining area kept untouched */
offset = value_sizes[info->type] * info->count;
offset = DIV_ROUND_UP(offset, sizeof(u32));
@@ -967,21 +1096,6 @@ static int sanity_check_elem_value(struct snd_card *card,
return ret;
}
-#else
-static inline void fill_remaining_elem_value(struct snd_ctl_elem_value *control,
- struct snd_ctl_elem_info *info,
- u32 pattern)
-{
-}
-
-static inline int sanity_check_elem_value(struct snd_card *card,
- struct snd_ctl_elem_value *control,
- struct snd_ctl_elem_info *info,
- u32 pattern)
-{
- return 0;
-}
-#endif
static int __snd_ctl_elem_info(struct snd_card *card,
struct snd_kcontrol *kctl,
@@ -1077,7 +1191,7 @@ static int snd_ctl_elem_read(struct snd_card *card,
snd_ctl_build_ioff(&control->id, kctl, index_offset);
-#ifdef CONFIG_SND_CTL_VALIDATION
+#ifdef CONFIG_SND_CTL_DEBUG
/* info is needed only for validation */
memset(&info, 0, sizeof(info));
info.id = control->id;
@@ -1154,6 +1268,17 @@ static int snd_ctl_elem_write(struct snd_card *card, struct snd_ctl_file *file,
snd_ctl_build_ioff(&control->id, kctl, index_offset);
result = snd_power_ref_and_wait(card);
+ /* validate input values */
+ if (IS_ENABLED(CONFIG_SND_CTL_INPUT_VALIDATION) && !result) {
+ struct snd_ctl_elem_info info;
+
+ memset(&info, 0, sizeof(info));
+ info.id = control->id;
+ result = __snd_ctl_elem_info(card, kctl, &info, NULL);
+ if (!result)
+ result = sanity_check_input_values(card, control, &info,
+ false);
+ }
if (!result)
result = kctl->put(kctl, control);
snd_power_unref(card);
@@ -1930,6 +2055,8 @@ static int _snd_ctl_register_ioctl(snd_kctl_ioctl_func_t fcn, struct list_head *
* @fcn: ioctl callback function
*
* called from each device manager like pcm.c, hwdep.c, etc.
+ *
+ * Return: zero if successful, or a negative error code
*/
int snd_ctl_register_ioctl(snd_kctl_ioctl_func_t fcn)
{
@@ -1942,6 +2069,8 @@ EXPORT_SYMBOL(snd_ctl_register_ioctl);
* snd_ctl_register_ioctl_compat - register the device-specific 32bit compat
* control-ioctls
* @fcn: ioctl callback function
+ *
+ * Return: zero if successful, or a negative error code
*/
int snd_ctl_register_ioctl_compat(snd_kctl_ioctl_func_t fcn)
{
@@ -1977,6 +2106,8 @@ static int _snd_ctl_unregister_ioctl(snd_kctl_ioctl_func_t fcn,
/**
* snd_ctl_unregister_ioctl - de-register the device-specific control-ioctls
* @fcn: ioctl callback function to unregister
+ *
+ * Return: zero if successful, or a negative error code
*/
int snd_ctl_unregister_ioctl(snd_kctl_ioctl_func_t fcn)
{
@@ -1989,6 +2120,8 @@ EXPORT_SYMBOL(snd_ctl_unregister_ioctl);
* snd_ctl_unregister_ioctl_compat - de-register the device-specific compat
* 32bit control-ioctls
* @fcn: ioctl callback function to unregister
+ *
+ * Return: zero if successful, or a negative error code
*/
int snd_ctl_unregister_ioctl_compat(snd_kctl_ioctl_func_t fcn)
{
@@ -2002,7 +2135,7 @@ static int snd_ctl_fasync(int fd, struct file * file, int on)
struct snd_ctl_file *ctl;
ctl = file->private_data;
- return fasync_helper(fd, file, on, &ctl->fasync);
+ return snd_fasync_helper(fd, file, on, &ctl->fasync);
}
/* return the preferred subdevice number if already assigned;
@@ -2044,7 +2177,7 @@ EXPORT_SYMBOL_GPL(snd_ctl_get_preferred_subdevice);
* snd_ctl_request_layer - request to use the layer
* @module_name: Name of the kernel module (NULL == build-in)
*
- * Return an error code when the module cannot be loaded.
+ * Return: zero if successful, or an error code when the module cannot be loaded
*/
int snd_ctl_request_layer(const char *module_name)
{
@@ -2170,7 +2303,7 @@ static int snd_ctl_dev_disconnect(struct snd_device *device)
read_lock_irqsave(&card->ctl_files_rwlock, flags);
list_for_each_entry(ctl, &card->ctl_files, list) {
wake_up(&ctl->change_sleep);
- kill_fasync(&ctl->fasync, SIGIO, POLL_ERR);
+ snd_kill_fasync(ctl->fasync, SIGIO, POLL_ERR);
}
read_unlock_irqrestore(&card->ctl_files_rwlock, flags);
@@ -2195,8 +2328,13 @@ static int snd_ctl_dev_free(struct snd_device *device)
down_write(&card->controls_rwsem);
while (!list_empty(&card->controls)) {
control = snd_kcontrol(card->controls.next);
- snd_ctl_remove(card, control);
+ __snd_ctl_remove(card, control, false);
}
+
+#ifdef CONFIG_SND_CTL_FAST_LOOKUP
+ xa_destroy(&card->ctl_numids);
+ xa_destroy(&card->ctl_hash);
+#endif
up_write(&card->controls_rwsem);
put_device(&card->ctl_dev);
return 0;
@@ -2241,6 +2379,8 @@ int snd_ctl_create(struct snd_card *card)
*
* This is a function that can be used as info callback for a standard
* boolean control with a single mono channel.
+ *
+ * Return: Zero (always successful)
*/
int snd_ctl_boolean_mono_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
@@ -2261,6 +2401,8 @@ EXPORT_SYMBOL(snd_ctl_boolean_mono_info);
*
* This is a function that can be used as info callback for a standard
* boolean control with stereo two channels.
+ *
+ * Return: Zero (always successful)
*/
int snd_ctl_boolean_stereo_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
@@ -2284,7 +2426,7 @@ EXPORT_SYMBOL(snd_ctl_boolean_stereo_info);
* If the control's accessibility is not the default (readable and writable),
* the caller has to fill @info->access.
*
- * Return: Zero.
+ * Return: Zero (always successful)
*/
int snd_ctl_enum_info(struct snd_ctl_elem_info *info, unsigned int channels,
unsigned int items, const char *const names[])
diff --git a/sound/core/device.c b/sound/core/device.c
index bf0b04a7ee79..b57d80a17052 100644
--- a/sound/core/device.c
+++ b/sound/core/device.c
@@ -247,6 +247,8 @@ void snd_device_free_all(struct snd_card *card)
* device, either @SNDRV_DEV_BUILD, @SNDRV_DEV_REGISTERED or
* @SNDRV_DEV_DISCONNECTED is returned.
* Or for a non-existing device, -1 is returned as an error.
+ *
+ * Return: the current state, or -1 if not found
*/
int snd_device_get_state(struct snd_card *card, void *device_data)
{
diff --git a/sound/core/info.c b/sound/core/info.c
index 782fba87cc04..b8058b341178 100644
--- a/sound/core/info.c
+++ b/sound/core/info.c
@@ -868,6 +868,8 @@ EXPORT_SYMBOL(snd_info_register);
*
* This proc file entry will be registered via snd_card_register() call, and
* it will be removed automatically at the card removal, too.
+ *
+ * Return: zero if successful, or a negative error code
*/
int snd_card_rw_proc_new(struct snd_card *card, const char *name,
void *private_data,
diff --git a/sound/core/init.c b/sound/core/init.c
index 726a8353201f..3ac95c66a4b5 100644
--- a/sound/core/init.c
+++ b/sound/core/init.c
@@ -215,6 +215,8 @@ static void __snd_card_release(struct device *dev, void *data)
* via snd_card_free() call in the error; otherwise it may lead to UAF due to
* devres call orders. You can use snd_card_free_on_error() helper for
* handling it more easily.
+ *
+ * Return: zero if successful, or a negative error code
*/
int snd_devm_card_new(struct device *parent, int idx, const char *xid,
struct module *module, size_t extra_size,
@@ -249,6 +251,8 @@ EXPORT_SYMBOL_GPL(snd_devm_card_new);
* This function handles the explicit snd_card_free() call at the error from
* the probe callback. It's just a small helper for simplifying the error
* handling with the managed devices.
+ *
+ * Return: zero if successful, or a negative error code
*/
int snd_card_free_on_error(struct device *dev, int ret)
{
@@ -310,6 +314,10 @@ static int snd_card_init(struct snd_card *card, struct device *parent,
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);
@@ -366,6 +374,8 @@ static int snd_card_init(struct snd_card *card, struct device *parent,
*
* Returns a card object corresponding to the given index or NULL if not found.
* Release the object via snd_card_unref().
+ *
+ * Return: a card object or NULL
*/
struct snd_card *snd_card_ref(int idx)
{
@@ -604,6 +614,8 @@ static int snd_card_do_free(struct snd_card *card)
* resource immediately, but tries to disconnect at first. When the card
* is still in use, the function returns before freeing the resources.
* The card resources will be freed when the refcount gets to zero.
+ *
+ * Return: zero if successful, or a negative error code
*/
int snd_card_free_when_closed(struct snd_card *card)
{
@@ -829,6 +841,8 @@ static const struct attribute_group card_dev_attr_group = {
* snd_card_add_dev_attr - Append a new sysfs attribute group to card
* @card: card instance
* @group: attribute group to append
+ *
+ * Return: zero if successful, or a negative error code
*/
int snd_card_add_dev_attr(struct snd_card *card,
const struct attribute_group *group)
diff --git a/sound/core/isadma.c b/sound/core/isadma.c
index 1f45ede023b4..2602246bd5a0 100644
--- a/sound/core/isadma.c
+++ b/sound/core/isadma.c
@@ -116,8 +116,9 @@ static void __snd_release_dma(struct device *dev, void *data)
* @dma: the dma number
* @name: the name string of the requester
*
- * Returns zero on success, or a negative error code.
* The requested DMA will be automatically released at unbinding via devres.
+ *
+ * Return: zero on success, or a negative error code
*/
int snd_devm_request_dma(struct device *dev, int dma, const char *name)
{
diff --git a/sound/core/memalloc.c b/sound/core/memalloc.c
index 8cfdaee77905..d3885cb02270 100644
--- a/sound/core/memalloc.c
+++ b/sound/core/memalloc.c
@@ -147,7 +147,7 @@ static void __snd_release_pages(struct device *dev, void *res)
* hence it can't work with SNDRV_DMA_TYPE_CONTINUOUS or
* SNDRV_DMA_TYPE_VMALLOC type.
*
- * The function returns the snd_dma_buffer object at success, or NULL if failed.
+ * Return: the snd_dma_buffer object at success, or NULL if failed
*/
struct snd_dma_buffer *
snd_devm_alloc_dir_pages(struct device *dev, int type,
@@ -179,6 +179,8 @@ EXPORT_SYMBOL_GPL(snd_devm_alloc_dir_pages);
* snd_dma_buffer_mmap - perform mmap of the given DMA buffer
* @dmab: buffer allocation information
* @area: VM area information
+ *
+ * Return: zero if successful, or a negative error code
*/
int snd_dma_buffer_mmap(struct snd_dma_buffer *dmab,
struct vm_area_struct *area)
@@ -219,6 +221,8 @@ EXPORT_SYMBOL_GPL(snd_dma_buffer_sync);
* snd_sgbuf_get_addr - return the physical address at the corresponding offset
* @dmab: buffer allocation information
* @offset: offset in the ring buffer
+ *
+ * Return: the physical address
*/
dma_addr_t snd_sgbuf_get_addr(struct snd_dma_buffer *dmab, size_t offset)
{
@@ -235,6 +239,8 @@ EXPORT_SYMBOL(snd_sgbuf_get_addr);
* snd_sgbuf_get_page - return the physical page at the corresponding offset
* @dmab: buffer allocation information
* @offset: offset in the ring buffer
+ *
+ * Return: the page pointer
*/
struct page *snd_sgbuf_get_page(struct snd_dma_buffer *dmab, size_t offset)
{
@@ -253,6 +259,8 @@ EXPORT_SYMBOL(snd_sgbuf_get_page);
* @dmab: buffer allocation information
* @ofs: offset in the ring buffer
* @size: the requested size
+ *
+ * Return: the chunk size
*/
unsigned int snd_sgbuf_get_chunk_size(struct snd_dma_buffer *dmab,
unsigned int ofs, unsigned int size)
diff --git a/sound/core/misc.c b/sound/core/misc.c
index 50e4aaa6270d..d32a19976a2b 100644
--- a/sound/core/misc.c
+++ b/sound/core/misc.c
@@ -10,6 +10,7 @@
#include <linux/time.h>
#include <linux/slab.h>
#include <linux/ioport.h>
+#include <linux/fs.h>
#include <sound/core.h>
#ifdef CONFIG_SND_DEBUG
@@ -145,3 +146,96 @@ snd_pci_quirk_lookup(struct pci_dev *pci, const struct snd_pci_quirk *list)
}
EXPORT_SYMBOL(snd_pci_quirk_lookup);
#endif
+
+/*
+ * Deferred async signal helpers
+ *
+ * Below are a few helper functions to wrap the async signal handling
+ * in the deferred work. The main purpose is to avoid the messy deadlock
+ * around tasklist_lock and co at the kill_fasync() invocation.
+ * fasync_helper() and kill_fasync() are replaced with snd_fasync_helper()
+ * and snd_kill_fasync(), respectively. In addition, snd_fasync_free() has
+ * to be called at releasing the relevant file object.
+ */
+struct snd_fasync {
+ struct fasync_struct *fasync;
+ int signal;
+ int poll;
+ int on;
+ struct list_head list;
+};
+
+static DEFINE_SPINLOCK(snd_fasync_lock);
+static LIST_HEAD(snd_fasync_list);
+
+static void snd_fasync_work_fn(struct work_struct *work)
+{
+ struct snd_fasync *fasync;
+
+ spin_lock_irq(&snd_fasync_lock);
+ while (!list_empty(&snd_fasync_list)) {
+ fasync = list_first_entry(&snd_fasync_list, struct snd_fasync, list);
+ list_del_init(&fasync->list);
+ spin_unlock_irq(&snd_fasync_lock);
+ if (fasync->on)
+ kill_fasync(&fasync->fasync, fasync->signal, fasync->poll);
+ spin_lock_irq(&snd_fasync_lock);
+ }
+ spin_unlock_irq(&snd_fasync_lock);
+}
+
+static DECLARE_WORK(snd_fasync_work, snd_fasync_work_fn);
+
+int snd_fasync_helper(int fd, struct file *file, int on,
+ struct snd_fasync **fasyncp)
+{
+ struct snd_fasync *fasync = NULL;
+
+ if (on) {
+ fasync = kzalloc(sizeof(*fasync), GFP_KERNEL);
+ if (!fasync)
+ return -ENOMEM;
+ INIT_LIST_HEAD(&fasync->list);
+ }
+
+ spin_lock_irq(&snd_fasync_lock);
+ if (*fasyncp) {
+ kfree(fasync);
+ fasync = *fasyncp;
+ } else {
+ if (!fasync) {
+ spin_unlock_irq(&snd_fasync_lock);
+ return 0;
+ }
+ *fasyncp = fasync;
+ }
+ fasync->on = on;
+ spin_unlock_irq(&snd_fasync_lock);
+ return fasync_helper(fd, file, on, &fasync->fasync);
+}
+EXPORT_SYMBOL_GPL(snd_fasync_helper);
+
+void snd_kill_fasync(struct snd_fasync *fasync, int signal, int poll)
+{
+ unsigned long flags;
+
+ if (!fasync || !fasync->on)
+ return;
+ spin_lock_irqsave(&snd_fasync_lock, flags);
+ fasync->signal = signal;
+ fasync->poll = poll;
+ list_move(&fasync->list, &snd_fasync_list);
+ schedule_work(&snd_fasync_work);
+ spin_unlock_irqrestore(&snd_fasync_lock, flags);
+}
+EXPORT_SYMBOL_GPL(snd_kill_fasync);
+
+void snd_fasync_free(struct snd_fasync *fasync)
+{
+ if (!fasync)
+ return;
+ fasync->on = 0;
+ flush_work(&snd_fasync_work);
+ kfree(fasync);
+}
+EXPORT_SYMBOL_GPL(snd_fasync_free);
diff --git a/sound/core/pcm.c b/sound/core/pcm.c
index 977d54320a5c..2ac742035310 100644
--- a/sound/core/pcm.c
+++ b/sound/core/pcm.c
@@ -216,6 +216,8 @@ static const char * const snd_pcm_format_names[] = {
/**
* snd_pcm_format_name - Return a name string for the given PCM format
* @format: PCM format
+ *
+ * Return: the format name string
*/
const char *snd_pcm_format_name(snd_pcm_format_t format)
{
@@ -1005,6 +1007,7 @@ void snd_pcm_detach_substream(struct snd_pcm_substream *substream)
substream->runtime = NULL;
}
mutex_destroy(&runtime->buffer_mutex);
+ snd_fasync_free(runtime->fasync);
kfree(runtime);
put_pid(substream->pid);
substream->pid = NULL;
@@ -1138,6 +1141,8 @@ static int snd_pcm_dev_disconnect(struct snd_device *device)
* This adds the given notifier to the global list so that the callback is
* called for each registered PCM devices. This exists only for PCM OSS
* emulation, so far.
+ *
+ * Return: zero if successful, or a negative error code
*/
int snd_pcm_notify(struct snd_pcm_notify *notify, int nfree)
{
diff --git a/sound/core/pcm_dmaengine.c b/sound/core/pcm_dmaengine.c
index af6f717e1e7e..5b2ca028f5aa 100644
--- a/sound/core/pcm_dmaengine.c
+++ b/sound/core/pcm_dmaengine.c
@@ -48,6 +48,8 @@ EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_get_chan);
*
* This function can be used to initialize a dma_slave_config from a substream
* and hw_params in a dmaengine based PCM driver implementation.
+ *
+ * Return: zero if successful, or a negative error code
*/
int snd_hwparams_to_dma_slave_config(const struct snd_pcm_substream *substream,
const struct snd_pcm_hw_params *params,
@@ -175,10 +177,10 @@ static int dmaengine_pcm_prepare_and_submit(struct snd_pcm_substream *substream)
* @substream: PCM substream
* @cmd: Trigger command
*
- * Returns 0 on success, a negative error code otherwise.
- *
* This function can be used as the PCM trigger callback for dmaengine based PCM
* driver implementations.
+ *
+ * Return: 0 on success, a negative error code otherwise
*/
int snd_dmaengine_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
{
@@ -223,6 +225,8 @@ EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_trigger);
*
* This function is deprecated and should not be used by new drivers, as its
* results may be unreliable.
+ *
+ * Return: PCM position in frames
*/
snd_pcm_uframes_t snd_dmaengine_pcm_pointer_no_residue(struct snd_pcm_substream *substream)
{
@@ -237,6 +241,8 @@ EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_pointer_no_residue);
*
* This function can be used as the PCM pointer callback for dmaengine based PCM
* driver implementations.
+ *
+ * Return: PCM position in frames
*/
snd_pcm_uframes_t snd_dmaengine_pcm_pointer(struct snd_pcm_substream *substream)
{
@@ -266,9 +272,9 @@ EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_pointer);
* @filter_fn: Filter function used to request the DMA channel
* @filter_data: Data passed to the DMA filter function
*
- * Returns NULL or the requested DMA channel.
- *
* This function request a DMA channel for usage with dmaengine PCM.
+ *
+ * Return: NULL or the requested DMA channel
*/
struct dma_chan *snd_dmaengine_pcm_request_channel(dma_filter_fn filter_fn,
void *filter_data)
@@ -288,11 +294,11 @@ EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_request_channel);
* @substream: PCM substream
* @chan: DMA channel to use for data transfers
*
- * Returns 0 on success, a negative error code otherwise.
- *
* The function should usually be called from the pcm open callback. Note that
* this function will use private_data field of the substream's runtime. So it
* is not available to your pcm driver implementation.
+ *
+ * Return: 0 on success, a negative error code otherwise
*/
int snd_dmaengine_pcm_open(struct snd_pcm_substream *substream,
struct dma_chan *chan)
@@ -326,12 +332,12 @@ EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_open);
* @filter_fn: Filter function used to request the DMA channel
* @filter_data: Data passed to the DMA filter function
*
- * Returns 0 on success, a negative error code otherwise.
- *
* This function will request a DMA channel using the passed filter function and
* data. The function should usually be called from the pcm open callback. Note
* that this function will use private_data field of the substream's runtime. So
* it is not available to your pcm driver implementation.
+ *
+ * Return: 0 on success, a negative error code otherwise
*/
int snd_dmaengine_pcm_open_request_chan(struct snd_pcm_substream *substream,
dma_filter_fn filter_fn, void *filter_data)
@@ -344,6 +350,8 @@ EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_open_request_chan);
/**
* snd_dmaengine_pcm_close - Close a dmaengine based PCM substream
* @substream: PCM substream
+ *
+ * Return: 0 on success, a negative error code otherwise
*/
int snd_dmaengine_pcm_close(struct snd_pcm_substream *substream)
{
@@ -362,6 +370,8 @@ EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_close);
* @substream: PCM substream
*
* Releases the DMA channel associated with the PCM substream.
+ *
+ * Return: zero if successful, or a negative error code
*/
int snd_dmaengine_pcm_close_release_chan(struct snd_pcm_substream *substream)
{
@@ -382,10 +392,10 @@ EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_close_release_chan);
* @hw: PCM hw params
* @chan: DMA channel to use for data transfers
*
- * Returns 0 on success, a negative error code otherwise.
- *
* This function will query DMA capability, then refine the pcm hardware
* parameters.
+ *
+ * Return: 0 on success, a negative error code otherwise
*/
int snd_dmaengine_pcm_refine_runtime_hwparams(
struct snd_pcm_substream *substream,
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c
index 1fc7c50ffa62..40751e5aff09 100644
--- a/sound/core/pcm_lib.c
+++ b/sound/core/pcm_lib.c
@@ -1822,7 +1822,7 @@ void snd_pcm_period_elapsed_under_stream_lock(struct snd_pcm_substream *substrea
snd_timer_interrupt(substream->timer, 1);
#endif
_end:
- kill_fasync(&runtime->fasync, SIGIO, POLL_IN);
+ snd_kill_fasync(runtime->fasync, SIGIO, POLL_IN);
}
EXPORT_SYMBOL(snd_pcm_period_elapsed_under_stream_lock);
diff --git a/sound/core/pcm_memory.c b/sound/core/pcm_memory.c
index b8296b6eb2c1..7bde7fb64011 100644
--- a/sound/core/pcm_memory.c
+++ b/sound/core/pcm_memory.c
@@ -350,6 +350,8 @@ EXPORT_SYMBOL(snd_pcm_lib_preallocate_pages_for_all);
* SNDRV_DMA_TYPE_VMALLOC type.
*
* Upon successful buffer allocation and setup, the function returns 0.
+ *
+ * Return: zero if successful, or a negative error code
*/
int snd_pcm_set_managed_buffer(struct snd_pcm_substream *substream, int type,
struct device *data, size_t size, size_t max)
@@ -369,6 +371,8 @@ EXPORT_SYMBOL(snd_pcm_set_managed_buffer);
*
* Do pre-allocation to all substreams of the given pcm for the specified DMA
* type and size, and set the managed_buffer_alloc flag to each substream.
+ *
+ * Return: zero if successful, or a negative error code
*/
int snd_pcm_set_managed_buffer_all(struct snd_pcm *pcm, int type,
struct device *data,
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index 4adaee62ef33..ad0541e9e888 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -3412,6 +3412,8 @@ static long snd_pcm_ioctl(struct file *file, unsigned int cmd,
* The function is provided primarily for OSS layer and USB gadget drivers,
* and it allows only the limited set of ioctls (hw_params, sw_params,
* prepare, start, drain, drop, forward).
+ *
+ * Return: zero if successful, or a negative error code
*/
int snd_pcm_kernel_ioctl(struct snd_pcm_substream *substream,
unsigned int cmd, void *arg)
@@ -3810,6 +3812,8 @@ static const struct vm_operations_struct snd_pcm_vm_ops_data_fault = {
*
* This is the default mmap handler for PCM data. When mmap pcm_ops is NULL,
* this function is invoked implicitly.
+ *
+ * Return: zero if successful, or a negative error code
*/
int snd_pcm_lib_default_mmap(struct snd_pcm_substream *substream,
struct vm_area_struct *area)
@@ -3836,6 +3840,8 @@ EXPORT_SYMBOL_GPL(snd_pcm_lib_default_mmap);
* When your hardware uses the iomapped pages as the hardware buffer and
* wants to mmap it, pass this function as mmap pcm_ops. Note that this
* is supposed to work only on limited architectures.
+ *
+ * Return: zero if successful, or a negative error code
*/
int snd_pcm_lib_mmap_iomem(struct snd_pcm_substream *substream,
struct vm_area_struct *area)
@@ -3945,7 +3951,7 @@ static int snd_pcm_fasync(int fd, struct file * file, int on)
runtime = substream->runtime;
if (runtime->status->state == SNDRV_PCM_STATE_DISCONNECTED)
return -EBADFD;
- return fasync_helper(fd, file, on, &runtime->fasync);
+ return snd_fasync_helper(fd, file, on, &runtime->fasync);
}
/*
diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c
index befa9809ff00..6963d5a487b3 100644
--- a/sound/core/rawmidi.c
+++ b/sound/core/rawmidi.c
@@ -102,13 +102,12 @@ static inline bool __snd_rawmidi_ready(struct snd_rawmidi_runtime *runtime)
static bool snd_rawmidi_ready(struct snd_rawmidi_substream *substream)
{
- struct snd_rawmidi_runtime *runtime = substream->runtime;
unsigned long flags;
bool ready;
- spin_lock_irqsave(&runtime->lock, flags);
- ready = __snd_rawmidi_ready(runtime);
- spin_unlock_irqrestore(&runtime->lock, flags);
+ spin_lock_irqsave(&substream->lock, flags);
+ ready = __snd_rawmidi_ready(substream->runtime);
+ spin_unlock_irqrestore(&substream->lock, flags);
return ready;
}
@@ -130,7 +129,7 @@ static void snd_rawmidi_input_event_work(struct work_struct *work)
runtime->event(runtime->substream);
}
-/* buffer refcount management: call with runtime->lock held */
+/* buffer refcount management: call with substream->lock held */
static inline void snd_rawmidi_buffer_ref(struct snd_rawmidi_runtime *runtime)
{
runtime->buffer_ref++;
@@ -141,6 +140,23 @@ static inline void snd_rawmidi_buffer_unref(struct snd_rawmidi_runtime *runtime)
runtime->buffer_ref--;
}
+static void snd_rawmidi_buffer_ref_sync(struct snd_rawmidi_substream *substream)
+{
+ int loop = HZ;
+
+ spin_lock_irq(&substream->lock);
+ while (substream->runtime->buffer_ref) {
+ spin_unlock_irq(&substream->lock);
+ if (!--loop) {
+ rmidi_err(substream->rmidi, "Buffer ref sync timeout\n");
+ return;
+ }
+ schedule_timeout_uninterruptible(1);
+ spin_lock_irq(&substream->lock);
+ }
+ spin_unlock_irq(&substream->lock);
+}
+
static int snd_rawmidi_runtime_create(struct snd_rawmidi_substream *substream)
{
struct snd_rawmidi_runtime *runtime;
@@ -149,7 +165,6 @@ static int snd_rawmidi_runtime_create(struct snd_rawmidi_substream *substream)
if (!runtime)
return -ENOMEM;
runtime->substream = substream;
- spin_lock_init(&runtime->lock);
init_waitqueue_head(&runtime->sleep);
INIT_WORK(&runtime->event_work, snd_rawmidi_input_event_work);
runtime->event = NULL;
@@ -203,35 +218,48 @@ static void __reset_runtime_ptrs(struct snd_rawmidi_runtime *runtime,
runtime->avail = is_input ? 0 : runtime->buffer_size;
}
-static void reset_runtime_ptrs(struct snd_rawmidi_runtime *runtime,
+static void reset_runtime_ptrs(struct snd_rawmidi_substream *substream,
bool is_input)
{
unsigned long flags;
- spin_lock_irqsave(&runtime->lock, flags);
- __reset_runtime_ptrs(runtime, is_input);
- spin_unlock_irqrestore(&runtime->lock, flags);
+ spin_lock_irqsave(&substream->lock, flags);
+ if (substream->opened && substream->runtime)
+ __reset_runtime_ptrs(substream->runtime, is_input);
+ spin_unlock_irqrestore(&substream->lock, flags);
}
int snd_rawmidi_drop_output(struct snd_rawmidi_substream *substream)
{
snd_rawmidi_output_trigger(substream, 0);
- reset_runtime_ptrs(substream->runtime, false);
+ reset_runtime_ptrs(substream, false);
return 0;
}
EXPORT_SYMBOL(snd_rawmidi_drop_output);
int snd_rawmidi_drain_output(struct snd_rawmidi_substream *substream)
{
- int err;
+ int err = 0;
long timeout;
- struct snd_rawmidi_runtime *runtime = substream->runtime;
+ struct snd_rawmidi_runtime *runtime;
+
+ spin_lock_irq(&substream->lock);
+ runtime = substream->runtime;
+ if (!substream->opened || !runtime || !runtime->buffer) {
+ err = -EINVAL;
+ } else {
+ snd_rawmidi_buffer_ref(runtime);
+ runtime->drain = 1;
+ }
+ spin_unlock_irq(&substream->lock);
+ if (err < 0)
+ return err;
- err = 0;
- runtime->drain = 1;
timeout = wait_event_interruptible_timeout(runtime->sleep,
(runtime->avail >= runtime->buffer_size),
10*HZ);
+
+ spin_lock_irq(&substream->lock);
if (signal_pending(current))
err = -ERESTARTSYS;
if (runtime->avail < runtime->buffer_size && !timeout) {
@@ -241,6 +269,8 @@ int snd_rawmidi_drain_output(struct snd_rawmidi_substream *substream)
err = -EIO;
}
runtime->drain = 0;
+ spin_unlock_irq(&substream->lock);
+
if (err != -ERESTARTSYS) {
/* we need wait a while to make sure that Tx FIFOs are empty */
if (substream->ops->drain)
@@ -249,6 +279,11 @@ int snd_rawmidi_drain_output(struct snd_rawmidi_substream *substream)
msleep(50);
snd_rawmidi_drop_output(substream);
}
+
+ spin_lock_irq(&substream->lock);
+ snd_rawmidi_buffer_unref(runtime);
+ spin_unlock_irq(&substream->lock);
+
return err;
}
EXPORT_SYMBOL(snd_rawmidi_drain_output);
@@ -256,7 +291,7 @@ EXPORT_SYMBOL(snd_rawmidi_drain_output);
int snd_rawmidi_drain_input(struct snd_rawmidi_substream *substream)
{
snd_rawmidi_input_trigger(substream, 0);
- reset_runtime_ptrs(substream->runtime, true);
+ reset_runtime_ptrs(substream, true);
return 0;
}
EXPORT_SYMBOL(snd_rawmidi_drain_input);
@@ -311,12 +346,14 @@ static int open_substream(struct snd_rawmidi *rmidi,
snd_rawmidi_runtime_free(substream);
return err;
}
+ spin_lock_irq(&substream->lock);
substream->opened = 1;
substream->active_sensing = 0;
if (mode & SNDRV_RAWMIDI_LFLG_APPEND)
substream->append = 1;
substream->pid = get_pid(task_pid(current));
rmidi->streams[substream->stream].substream_opened++;
+ spin_unlock_irq(&substream->lock);
}
substream->use_count++;
return 0;
@@ -521,13 +558,16 @@ static void close_substream(struct snd_rawmidi *rmidi,
if (snd_rawmidi_drain_output(substream) == -ERESTARTSYS)
snd_rawmidi_output_trigger(substream, 0);
}
+ snd_rawmidi_buffer_ref_sync(substream);
}
+ spin_lock_irq(&substream->lock);
+ substream->opened = 0;
+ substream->append = 0;
+ spin_unlock_irq(&substream->lock);
substream->ops->close(substream);
if (substream->runtime->private_free)
substream->runtime->private_free(substream);
snd_rawmidi_runtime_free(substream);
- substream->opened = 0;
- substream->append = 0;
put_pid(substream->pid);
substream->pid = NULL;
rmidi->streams[substream->stream].substream_opened--;
@@ -676,10 +716,11 @@ static int snd_rawmidi_info_select_user(struct snd_card *card,
return 0;
}
-static int resize_runtime_buffer(struct snd_rawmidi_runtime *runtime,
+static int resize_runtime_buffer(struct snd_rawmidi_substream *substream,
struct snd_rawmidi_params *params,
bool is_input)
{
+ struct snd_rawmidi_runtime *runtime = substream->runtime;
char *newbuf, *oldbuf;
unsigned int framing = params->mode & SNDRV_RAWMIDI_MODE_FRAMING_MASK;
@@ -693,9 +734,9 @@ static int resize_runtime_buffer(struct snd_rawmidi_runtime *runtime,
newbuf = kvzalloc(params->buffer_size, GFP_KERNEL);
if (!newbuf)
return -ENOMEM;
- spin_lock_irq(&runtime->lock);
+ spin_lock_irq(&substream->lock);
if (runtime->buffer_ref) {
- spin_unlock_irq(&runtime->lock);
+ spin_unlock_irq(&substream->lock);
kvfree(newbuf);
return -EBUSY;
}
@@ -703,7 +744,7 @@ static int resize_runtime_buffer(struct snd_rawmidi_runtime *runtime,
runtime->buffer = newbuf;
runtime->buffer_size = params->buffer_size;
__reset_runtime_ptrs(runtime, is_input);
- spin_unlock_irq(&runtime->lock);
+ spin_unlock_irq(&substream->lock);
kvfree(oldbuf);
}
runtime->avail_min = params->avail_min;
@@ -713,11 +754,19 @@ static int resize_runtime_buffer(struct snd_rawmidi_runtime *runtime,
int snd_rawmidi_output_params(struct snd_rawmidi_substream *substream,
struct snd_rawmidi_params *params)
{
- if (substream->append && substream->use_count > 1)
- return -EBUSY;
+ int err;
+
snd_rawmidi_drain_output(substream);
- substream->active_sensing = !params->no_active_sensing;
- return resize_runtime_buffer(substream->runtime, params, false);
+ mutex_lock(&substream->rmidi->open_mutex);
+ if (substream->append && substream->use_count > 1)
+ err = -EBUSY;
+ else
+ err = resize_runtime_buffer(substream, params, false);
+
+ if (!err)
+ substream->active_sensing = !params->no_active_sensing;
+ mutex_unlock(&substream->rmidi->open_mutex);
+ return err;
}
EXPORT_SYMBOL(snd_rawmidi_output_params);
@@ -728,19 +777,22 @@ int snd_rawmidi_input_params(struct snd_rawmidi_substream *substream,
unsigned int clock_type = params->mode & SNDRV_RAWMIDI_MODE_CLOCK_MASK;
int err;
+ snd_rawmidi_drain_input(substream);
+ mutex_lock(&substream->rmidi->open_mutex);
if (framing == SNDRV_RAWMIDI_MODE_FRAMING_NONE && clock_type != SNDRV_RAWMIDI_MODE_CLOCK_NONE)
- return -EINVAL;
+ err = -EINVAL;
else if (clock_type > SNDRV_RAWMIDI_MODE_CLOCK_MONOTONIC_RAW)
- return -EINVAL;
- if (framing > SNDRV_RAWMIDI_MODE_FRAMING_TSTAMP)
- return -EINVAL;
- snd_rawmidi_drain_input(substream);
- err = resize_runtime_buffer(substream->runtime, params, true);
- if (err < 0)
- return err;
+ err = -EINVAL;
+ else if (framing > SNDRV_RAWMIDI_MODE_FRAMING_TSTAMP)
+ err = -EINVAL;
+ else
+ err = resize_runtime_buffer(substream, params, true);
- substream->framing = framing;
- substream->clock_type = clock_type;
+ if (!err) {
+ substream->framing = framing;
+ substream->clock_type = clock_type;
+ }
+ mutex_unlock(&substream->rmidi->open_mutex);
return 0;
}
EXPORT_SYMBOL(snd_rawmidi_input_params);
@@ -752,9 +804,9 @@ static int snd_rawmidi_output_status(struct snd_rawmidi_substream *substream,
memset(status, 0, sizeof(*status));
status->stream = SNDRV_RAWMIDI_STREAM_OUTPUT;
- spin_lock_irq(&runtime->lock);
+ spin_lock_irq(&substream->lock);
status->avail = runtime->avail;
- spin_unlock_irq(&runtime->lock);
+ spin_unlock_irq(&substream->lock);
return 0;
}
@@ -765,11 +817,11 @@ static int snd_rawmidi_input_status(struct snd_rawmidi_substream *substream,
memset(status, 0, sizeof(*status));
status->stream = SNDRV_RAWMIDI_STREAM_INPUT;
- spin_lock_irq(&runtime->lock);
+ spin_lock_irq(&substream->lock);
status->avail = runtime->avail;
status->xruns = runtime->xruns;
runtime->xruns = 0;
- spin_unlock_irq(&runtime->lock);
+ spin_unlock_irq(&substream->lock);
return 0;
}
@@ -1064,17 +1116,21 @@ int snd_rawmidi_receive(struct snd_rawmidi_substream *substream,
unsigned long flags;
struct timespec64 ts64 = get_framing_tstamp(substream);
int result = 0, count1;
- struct snd_rawmidi_runtime *runtime = substream->runtime;
+ struct snd_rawmidi_runtime *runtime;
- if (!substream->opened)
- return -EBADFD;
- if (runtime->buffer == NULL) {
+ spin_lock_irqsave(&substream->lock, flags);
+ if (!substream->opened) {
+ result = -EBADFD;
+ goto unlock;
+ }
+ runtime = substream->runtime;
+ if (!runtime || !runtime->buffer) {
rmidi_dbg(substream->rmidi,
"snd_rawmidi_receive: input is not active!!!\n");
- return -EINVAL;
+ result = -EINVAL;
+ goto unlock;
}
- spin_lock_irqsave(&runtime->lock, flags);
if (substream->framing == SNDRV_RAWMIDI_MODE_FRAMING_TSTAMP) {
result = receive_with_tstamp_framing(substream, buffer, count, &ts64);
} else if (count == 1) { /* special case, faster code */
@@ -1121,7 +1177,8 @@ int snd_rawmidi_receive(struct snd_rawmidi_substream *substream,
else if (__snd_rawmidi_ready(runtime))
wake_up(&runtime->sleep);
}
- spin_unlock_irqrestore(&runtime->lock, flags);
+ unlock:
+ spin_unlock_irqrestore(&substream->lock, flags);
return result;
}
EXPORT_SYMBOL(snd_rawmidi_receive);
@@ -1136,7 +1193,7 @@ static long snd_rawmidi_kernel_read1(struct snd_rawmidi_substream *substream,
unsigned long appl_ptr;
int err = 0;
- spin_lock_irqsave(&runtime->lock, flags);
+ spin_lock_irqsave(&substream->lock, flags);
snd_rawmidi_buffer_ref(runtime);
while (count > 0 && runtime->avail) {
count1 = runtime->buffer_size - runtime->appl_ptr;
@@ -1154,11 +1211,11 @@ static long snd_rawmidi_kernel_read1(struct snd_rawmidi_substream *substream,
if (kernelbuf)
memcpy(kernelbuf + result, runtime->buffer + appl_ptr, count1);
if (userbuf) {
- spin_unlock_irqrestore(&runtime->lock, flags);
+ spin_unlock_irqrestore(&substream->lock, flags);
if (copy_to_user(userbuf + result,
runtime->buffer + appl_ptr, count1))
err = -EFAULT;
- spin_lock_irqsave(&runtime->lock, flags);
+ spin_lock_irqsave(&substream->lock, flags);
if (err)
goto out;
}
@@ -1167,7 +1224,7 @@ static long snd_rawmidi_kernel_read1(struct snd_rawmidi_substream *substream,
}
out:
snd_rawmidi_buffer_unref(runtime);
- spin_unlock_irqrestore(&runtime->lock, flags);
+ spin_unlock_irqrestore(&substream->lock, flags);
return result > 0 ? result : err;
}
@@ -1196,31 +1253,31 @@ static ssize_t snd_rawmidi_read(struct file *file, char __user *buf, size_t coun
snd_rawmidi_input_trigger(substream, 1);
result = 0;
while (count > 0) {
- spin_lock_irq(&runtime->lock);
+ spin_lock_irq(&substream->lock);
while (!__snd_rawmidi_ready(runtime)) {
wait_queue_entry_t wait;
if ((file->f_flags & O_NONBLOCK) != 0 || result > 0) {
- spin_unlock_irq(&runtime->lock);
+ spin_unlock_irq(&substream->lock);
return result > 0 ? result : -EAGAIN;
}
init_waitqueue_entry(&wait, current);
add_wait_queue(&runtime->sleep, &wait);
set_current_state(TASK_INTERRUPTIBLE);
- spin_unlock_irq(&runtime->lock);
+ spin_unlock_irq(&substream->lock);
schedule();
remove_wait_queue(&runtime->sleep, &wait);
if (rfile->rmidi->card->shutdown)
return -ENODEV;
if (signal_pending(current))
return result > 0 ? result : -ERESTARTSYS;
- spin_lock_irq(&runtime->lock);
+ spin_lock_irq(&substream->lock);
if (!runtime->avail) {
- spin_unlock_irq(&runtime->lock);
+ spin_unlock_irq(&substream->lock);
return result > 0 ? result : -EIO;
}
}
- spin_unlock_irq(&runtime->lock);
+ spin_unlock_irq(&substream->lock);
count1 = snd_rawmidi_kernel_read1(substream,
(unsigned char __user *)buf,
NULL/*kernelbuf*/,
@@ -1242,23 +1299,25 @@ static ssize_t snd_rawmidi_read(struct file *file, char __user *buf, size_t coun
*/
int snd_rawmidi_transmit_empty(struct snd_rawmidi_substream *substream)
{
- struct snd_rawmidi_runtime *runtime = substream->runtime;
+ struct snd_rawmidi_runtime *runtime;
int result;
unsigned long flags;
- if (runtime->buffer == NULL) {
+ spin_lock_irqsave(&substream->lock, flags);
+ runtime = substream->runtime;
+ if (!substream->opened || !runtime || !runtime->buffer) {
rmidi_dbg(substream->rmidi,
"snd_rawmidi_transmit_empty: output is not active!!!\n");
- return 1;
+ result = 1;
+ } else {
+ result = runtime->avail >= runtime->buffer_size;
}
- spin_lock_irqsave(&runtime->lock, flags);
- result = runtime->avail >= runtime->buffer_size;
- spin_unlock_irqrestore(&runtime->lock, flags);
+ spin_unlock_irqrestore(&substream->lock, flags);
return result;
}
EXPORT_SYMBOL(snd_rawmidi_transmit_empty);
-/**
+/*
* __snd_rawmidi_transmit_peek - copy data from the internal buffer
* @substream: the rawmidi substream
* @buffer: the buffer pointer
@@ -1266,8 +1325,8 @@ EXPORT_SYMBOL(snd_rawmidi_transmit_empty);
*
* This is a variant of snd_rawmidi_transmit_peek() without spinlock.
*/
-int __snd_rawmidi_transmit_peek(struct snd_rawmidi_substream *substream,
- unsigned char *buffer, int count)
+static int __snd_rawmidi_transmit_peek(struct snd_rawmidi_substream *substream,
+ unsigned char *buffer, int count)
{
int result, count1;
struct snd_rawmidi_runtime *runtime = substream->runtime;
@@ -1304,7 +1363,6 @@ int __snd_rawmidi_transmit_peek(struct snd_rawmidi_substream *substream,
__skip:
return result;
}
-EXPORT_SYMBOL(__snd_rawmidi_transmit_peek);
/**
* snd_rawmidi_transmit_peek - copy data from the internal buffer
@@ -1323,25 +1381,28 @@ EXPORT_SYMBOL(__snd_rawmidi_transmit_peek);
int snd_rawmidi_transmit_peek(struct snd_rawmidi_substream *substream,
unsigned char *buffer, int count)
{
- struct snd_rawmidi_runtime *runtime = substream->runtime;
int result;
unsigned long flags;
- spin_lock_irqsave(&runtime->lock, flags);
- result = __snd_rawmidi_transmit_peek(substream, buffer, count);
- spin_unlock_irqrestore(&runtime->lock, flags);
+ spin_lock_irqsave(&substream->lock, flags);
+ if (!substream->opened || !substream->runtime)
+ result = -EBADFD;
+ else
+ result = __snd_rawmidi_transmit_peek(substream, buffer, count);
+ spin_unlock_irqrestore(&substream->lock, flags);
return result;
}
EXPORT_SYMBOL(snd_rawmidi_transmit_peek);
-/**
+/*
* __snd_rawmidi_transmit_ack - acknowledge the transmission
* @substream: the rawmidi substream
* @count: the transferred count
*
* This is a variant of __snd_rawmidi_transmit_ack() without spinlock.
*/
-int __snd_rawmidi_transmit_ack(struct snd_rawmidi_substream *substream, int count)
+static int __snd_rawmidi_transmit_ack(struct snd_rawmidi_substream *substream,
+ int count)
{
struct snd_rawmidi_runtime *runtime = substream->runtime;
@@ -1361,7 +1422,6 @@ int __snd_rawmidi_transmit_ack(struct snd_rawmidi_substream *substream, int coun
}
return count;
}
-EXPORT_SYMBOL(__snd_rawmidi_transmit_ack);
/**
* snd_rawmidi_transmit_ack - acknowledge the transmission
@@ -1376,13 +1436,15 @@ EXPORT_SYMBOL(__snd_rawmidi_transmit_ack);
*/
int snd_rawmidi_transmit_ack(struct snd_rawmidi_substream *substream, int count)
{
- struct snd_rawmidi_runtime *runtime = substream->runtime;
int result;
unsigned long flags;
- spin_lock_irqsave(&runtime->lock, flags);
- result = __snd_rawmidi_transmit_ack(substream, count);
- spin_unlock_irqrestore(&runtime->lock, flags);
+ spin_lock_irqsave(&substream->lock, flags);
+ if (!substream->opened || !substream->runtime)
+ result = -EBADFD;
+ else
+ result = __snd_rawmidi_transmit_ack(substream, count);
+ spin_unlock_irqrestore(&substream->lock, flags);
return result;
}
EXPORT_SYMBOL(snd_rawmidi_transmit_ack);
@@ -1400,11 +1462,10 @@ EXPORT_SYMBOL(snd_rawmidi_transmit_ack);
int snd_rawmidi_transmit(struct snd_rawmidi_substream *substream,
unsigned char *buffer, int count)
{
- struct snd_rawmidi_runtime *runtime = substream->runtime;
int result;
unsigned long flags;
- spin_lock_irqsave(&runtime->lock, flags);
+ spin_lock_irqsave(&substream->lock, flags);
if (!substream->opened)
result = -EBADFD;
else {
@@ -1414,7 +1475,7 @@ int snd_rawmidi_transmit(struct snd_rawmidi_substream *substream,
else
result = __snd_rawmidi_transmit_ack(substream, count);
}
- spin_unlock_irqrestore(&runtime->lock, flags);
+ spin_unlock_irqrestore(&substream->lock, flags);
return result;
}
EXPORT_SYMBOL(snd_rawmidi_transmit);
@@ -1427,16 +1488,18 @@ EXPORT_SYMBOL(snd_rawmidi_transmit);
*/
int snd_rawmidi_proceed(struct snd_rawmidi_substream *substream)
{
- struct snd_rawmidi_runtime *runtime = substream->runtime;
+ struct snd_rawmidi_runtime *runtime;
unsigned long flags;
int count = 0;
- spin_lock_irqsave(&runtime->lock, flags);
- if (runtime->avail < runtime->buffer_size) {
+ spin_lock_irqsave(&substream->lock, flags);
+ runtime = substream->runtime;
+ if (substream->opened && runtime &&
+ runtime->avail < runtime->buffer_size) {
count = runtime->buffer_size - runtime->avail;
__snd_rawmidi_transmit_ack(substream, count);
}
- spin_unlock_irqrestore(&runtime->lock, flags);
+ spin_unlock_irqrestore(&substream->lock, flags);
return count;
}
EXPORT_SYMBOL(snd_rawmidi_proceed);
@@ -1457,10 +1520,10 @@ static long snd_rawmidi_kernel_write1(struct snd_rawmidi_substream *substream,
return -EINVAL;
result = 0;
- spin_lock_irqsave(&runtime->lock, flags);
+ spin_lock_irqsave(&substream->lock, flags);
if (substream->append) {
if ((long)runtime->avail < count) {
- spin_unlock_irqrestore(&runtime->lock, flags);
+ spin_unlock_irqrestore(&substream->lock, flags);
return -EAGAIN;
}
}
@@ -1482,14 +1545,14 @@ static long snd_rawmidi_kernel_write1(struct snd_rawmidi_substream *substream,
memcpy(runtime->buffer + appl_ptr,
kernelbuf + result, count1);
else if (userbuf) {
- spin_unlock_irqrestore(&runtime->lock, flags);
+ spin_unlock_irqrestore(&substream->lock, flags);
if (copy_from_user(runtime->buffer + appl_ptr,
userbuf + result, count1)) {
- spin_lock_irqsave(&runtime->lock, flags);
+ spin_lock_irqsave(&substream->lock, flags);
result = result > 0 ? result : -EFAULT;
goto __end;
}
- spin_lock_irqsave(&runtime->lock, flags);
+ spin_lock_irqsave(&substream->lock, flags);
}
result += count1;
count -= count1;
@@ -1497,7 +1560,7 @@ static long snd_rawmidi_kernel_write1(struct snd_rawmidi_substream *substream,
__end:
count1 = runtime->avail < runtime->buffer_size;
snd_rawmidi_buffer_unref(runtime);
- spin_unlock_irqrestore(&runtime->lock, flags);
+ spin_unlock_irqrestore(&substream->lock, flags);
if (count1)
snd_rawmidi_output_trigger(substream, 1);
return result;
@@ -1527,31 +1590,31 @@ static ssize_t snd_rawmidi_write(struct file *file, const char __user *buf,
return -EIO;
result = 0;
while (count > 0) {
- spin_lock_irq(&runtime->lock);
+ spin_lock_irq(&substream->lock);
while (!snd_rawmidi_ready_append(substream, count)) {
wait_queue_entry_t wait;
if (file->f_flags & O_NONBLOCK) {
- spin_unlock_irq(&runtime->lock);
+ spin_unlock_irq(&substream->lock);
return result > 0 ? result : -EAGAIN;
}
init_waitqueue_entry(&wait, current);
add_wait_queue(&runtime->sleep, &wait);
set_current_state(TASK_INTERRUPTIBLE);
- spin_unlock_irq(&runtime->lock);
+ spin_unlock_irq(&substream->lock);
timeout = schedule_timeout(30 * HZ);
remove_wait_queue(&runtime->sleep, &wait);
if (rfile->rmidi->card->shutdown)
return -ENODEV;
if (signal_pending(current))
return result > 0 ? result : -ERESTARTSYS;
- spin_lock_irq(&runtime->lock);
+ spin_lock_irq(&substream->lock);
if (!runtime->avail && !timeout) {
- spin_unlock_irq(&runtime->lock);
+ spin_unlock_irq(&substream->lock);
return result > 0 ? result : -EIO;
}
}
- spin_unlock_irq(&runtime->lock);
+ spin_unlock_irq(&substream->lock);
count1 = snd_rawmidi_kernel_write1(substream, buf, NULL, count);
if (count1 < 0)
return result > 0 ? result : count1;
@@ -1562,7 +1625,7 @@ static ssize_t snd_rawmidi_write(struct file *file, const char __user *buf,
count -= count1;
}
if (file->f_flags & O_DSYNC) {
- spin_lock_irq(&runtime->lock);
+ spin_lock_irq(&substream->lock);
while (runtime->avail != runtime->buffer_size) {
wait_queue_entry_t wait;
unsigned int last_avail = runtime->avail;
@@ -1570,16 +1633,16 @@ static ssize_t snd_rawmidi_write(struct file *file, const char __user *buf,
init_waitqueue_entry(&wait, current);
add_wait_queue(&runtime->sleep, &wait);
set_current_state(TASK_INTERRUPTIBLE);
- spin_unlock_irq(&runtime->lock);
+ spin_unlock_irq(&substream->lock);
timeout = schedule_timeout(30 * HZ);
remove_wait_queue(&runtime->sleep, &wait);
if (signal_pending(current))
return result > 0 ? result : -ERESTARTSYS;
if (runtime->avail == last_avail && !timeout)
return result > 0 ? result : -EIO;
- spin_lock_irq(&runtime->lock);
+ spin_lock_irq(&substream->lock);
}
- spin_unlock_irq(&runtime->lock);
+ spin_unlock_irq(&substream->lock);
}
return result;
}
@@ -1650,10 +1713,10 @@ static void snd_rawmidi_proc_info_read(struct snd_info_entry *entry,
" Owner PID : %d\n",
pid_vnr(substream->pid));
runtime = substream->runtime;
- spin_lock_irq(&runtime->lock);
+ spin_lock_irq(&substream->lock);
buffer_size = runtime->buffer_size;
avail = runtime->avail;
- spin_unlock_irq(&runtime->lock);
+ spin_unlock_irq(&substream->lock);
snd_iprintf(buffer,
" Mode : %s\n"
" Buffer size : %lu\n"
@@ -1677,11 +1740,11 @@ static void snd_rawmidi_proc_info_read(struct snd_info_entry *entry,
" Owner PID : %d\n",
pid_vnr(substream->pid));
runtime = substream->runtime;
- spin_lock_irq(&runtime->lock);
+ spin_lock_irq(&substream->lock);
buffer_size = runtime->buffer_size;
avail = runtime->avail;
xruns = runtime->xruns;
- spin_unlock_irq(&runtime->lock);
+ spin_unlock_irq(&substream->lock);
snd_iprintf(buffer,
" Buffer size : %lu\n"
" Avail : %lu\n"
@@ -1733,6 +1796,7 @@ static int snd_rawmidi_alloc_substreams(struct snd_rawmidi *rmidi,
substream->number = idx;
substream->rmidi = rmidi;
substream->pstr = stream;
+ spin_lock_init(&substream->lock);
list_add_tail(&substream->list, &stream->substreams);
stream->substream_count++;
}
diff --git a/sound/core/timer.c b/sound/core/timer.c
index b3214baa8919..e08a37c23add 100644
--- a/sound/core/timer.c
+++ b/sound/core/timer.c
@@ -83,7 +83,7 @@ struct snd_timer_user {
unsigned int filter;
struct timespec64 tstamp; /* trigger tstamp */
wait_queue_head_t qchange_sleep;
- struct fasync_struct *fasync;
+ struct snd_fasync *fasync;
struct mutex ioctl_lock;
};
@@ -1345,7 +1345,7 @@ static void snd_timer_user_interrupt(struct snd_timer_instance *timeri,
}
__wake:
spin_unlock(&tu->qlock);
- kill_fasync(&tu->fasync, SIGIO, POLL_IN);
+ snd_kill_fasync(tu->fasync, SIGIO, POLL_IN);
wake_up(&tu->qchange_sleep);
}
@@ -1383,7 +1383,7 @@ static void snd_timer_user_ccallback(struct snd_timer_instance *timeri,
spin_lock_irqsave(&tu->qlock, flags);
snd_timer_user_append_to_tqueue(tu, &r1);
spin_unlock_irqrestore(&tu->qlock, flags);
- kill_fasync(&tu->fasync, SIGIO, POLL_IN);
+ snd_kill_fasync(tu->fasync, SIGIO, POLL_IN);
wake_up(&tu->qchange_sleep);
}
@@ -1453,7 +1453,7 @@ static void snd_timer_user_tinterrupt(struct snd_timer_instance *timeri,
spin_unlock(&tu->qlock);
if (append == 0)
return;
- kill_fasync(&tu->fasync, SIGIO, POLL_IN);
+ snd_kill_fasync(tu->fasync, SIGIO, POLL_IN);
wake_up(&tu->qchange_sleep);
}
@@ -1521,6 +1521,7 @@ static int snd_timer_user_release(struct inode *inode, struct file *file)
snd_timer_instance_free(tu->timeri);
}
mutex_unlock(&tu->ioctl_lock);
+ snd_fasync_free(tu->fasync);
kfree(tu->queue);
kfree(tu->tqueue);
kfree(tu);
@@ -2135,7 +2136,7 @@ static int snd_timer_user_fasync(int fd, struct file * file, int on)
struct snd_timer_user *tu;
tu = file->private_data;
- return fasync_helper(fd, file, on, &tu->fasync);
+ return snd_fasync_helper(fd, file, on, &tu->fasync);
}
static ssize_t snd_timer_user_read(struct file *file, char __user *buffer,
diff --git a/sound/core/vmaster.c b/sound/core/vmaster.c
index ab36f9898711..d0f11f37889b 100644
--- a/sound/core/vmaster.c
+++ b/sound/core/vmaster.c
@@ -494,7 +494,8 @@ EXPORT_SYMBOL_GPL(snd_ctl_sync_vmaster);
* @arg: optional function argument
*
* Apply the function @func to each follower kctl of the given vmaster kctl.
- * Returns 0 if successful, or a negative error code.
+ *
+ * Return: 0 if successful, or a negative error code
*/
int snd_ctl_apply_vmaster_followers(struct snd_kcontrol *kctl,
int (*func)(struct snd_kcontrol *vfollower,