aboutsummaryrefslogtreecommitdiffstats
path: root/sound/core
diff options
context:
space:
mode:
Diffstat (limited to 'sound/core')
-rw-r--r--sound/core/Makefile3
-rw-r--r--sound/core/control.c71
-rw-r--r--sound/core/init.c33
-rw-r--r--sound/core/pcm.c48
-rw-r--r--sound/core/pcm_lib.c148
-rw-r--r--sound/core/pcm_native.c214
-rw-r--r--sound/core/pcm_trace.h110
-rw-r--r--sound/core/seq/oss/seq_oss_init.c9
-rw-r--r--sound/core/seq/seq.c5
-rw-r--r--sound/core/seq/seq_device.c103
-rw-r--r--sound/core/sgbuf.c3
-rw-r--r--sound/core/sound.c9
12 files changed, 462 insertions, 294 deletions
diff --git a/sound/core/Makefile b/sound/core/Makefile
index 394a38909f6b..4daf2f58261c 100644
--- a/sound/core/Makefile
+++ b/sound/core/Makefile
@@ -14,6 +14,9 @@ snd-pcm-y := pcm.o pcm_native.o pcm_lib.o pcm_timer.o pcm_misc.o \
pcm_memory.o memalloc.o
snd-pcm-$(CONFIG_SND_DMA_SGBUF) += sgbuf.o
+# for trace-points
+CFLAGS_pcm_lib.o := -I$(src)
+
snd-pcm-dmaengine-objs := pcm_dmaengine.o
snd-rawmidi-objs := rawmidi.o
diff --git a/sound/core/control.c b/sound/core/control.c
index b9611344ff9e..bb96a467e88d 100644
--- a/sound/core/control.c
+++ b/sound/core/control.c
@@ -141,6 +141,16 @@ static int snd_ctl_release(struct inode *inode, struct file *file)
return 0;
}
+/**
+ * snd_ctl_notify - Send notification to user-space for a control change
+ * @card: the card to send notification
+ * @mask: the event mask, SNDRV_CTL_EVENT_*
+ * @id: the ctl element id to send notification
+ *
+ * This function adds an event record with the given id and mask, appends
+ * to the list and wakes up the user-space for notification. This can be
+ * called in the atomic context.
+ */
void snd_ctl_notify(struct snd_card *card, unsigned int mask,
struct snd_ctl_elem_id *id)
{
@@ -179,7 +189,6 @@ void snd_ctl_notify(struct snd_card *card, unsigned int mask,
}
read_unlock(&card->ctl_files_rwlock);
}
-
EXPORT_SYMBOL(snd_ctl_notify);
/**
@@ -261,7 +270,6 @@ struct snd_kcontrol *snd_ctl_new1(const struct snd_kcontrol_new *ncontrol,
kctl.private_data = private_data;
return snd_ctl_new(&kctl, access);
}
-
EXPORT_SYMBOL(snd_ctl_new1);
/**
@@ -280,7 +288,6 @@ void snd_ctl_free_one(struct snd_kcontrol *kcontrol)
kfree(kcontrol);
}
}
-
EXPORT_SYMBOL(snd_ctl_free_one);
static bool snd_ctl_remove_numid_conflict(struct snd_card *card,
@@ -376,7 +383,6 @@ int snd_ctl_add(struct snd_card *card, struct snd_kcontrol *kcontrol)
snd_ctl_free_one(kcontrol);
return err;
}
-
EXPORT_SYMBOL(snd_ctl_add);
/**
@@ -471,7 +477,6 @@ int snd_ctl_remove(struct snd_card *card, struct snd_kcontrol *kcontrol)
snd_ctl_free_one(kcontrol);
return 0;
}
-
EXPORT_SYMBOL(snd_ctl_remove);
/**
@@ -499,7 +504,6 @@ int snd_ctl_remove_id(struct snd_card *card, struct snd_ctl_elem_id *id)
up_write(&card->controls_rwsem);
return ret;
}
-
EXPORT_SYMBOL(snd_ctl_remove_id);
/**
@@ -568,7 +572,7 @@ int snd_ctl_activate_id(struct snd_card *card, struct snd_ctl_elem_id *id,
ret = -ENOENT;
goto unlock;
}
- index_offset = snd_ctl_get_ioff(kctl, &kctl->id);
+ index_offset = snd_ctl_get_ioff(kctl, id);
vd = &kctl->vd[index_offset];
ret = 0;
if (active) {
@@ -617,7 +621,6 @@ int snd_ctl_rename_id(struct snd_card *card, struct snd_ctl_elem_id *src_id,
up_write(&card->controls_rwsem);
return 0;
}
-
EXPORT_SYMBOL(snd_ctl_rename_id);
/**
@@ -645,7 +648,6 @@ struct snd_kcontrol *snd_ctl_find_numid(struct snd_card *card, unsigned int numi
}
return NULL;
}
-
EXPORT_SYMBOL(snd_ctl_find_numid);
/**
@@ -687,7 +689,6 @@ struct snd_kcontrol *snd_ctl_find_id(struct snd_card *card,
}
return NULL;
}
-
EXPORT_SYMBOL(snd_ctl_find_id);
static int snd_ctl_card_info(struct snd_card *card, struct snd_ctl_file * ctl,
@@ -1526,19 +1527,28 @@ static int _snd_ctl_register_ioctl(snd_kctl_ioctl_func_t fcn, struct list_head *
return 0;
}
+/**
+ * snd_ctl_register_ioctl - register the device-specific control-ioctls
+ * @fcn: ioctl callback function
+ *
+ * called from each device manager like pcm.c, hwdep.c, etc.
+ */
int snd_ctl_register_ioctl(snd_kctl_ioctl_func_t fcn)
{
return _snd_ctl_register_ioctl(fcn, &snd_control_ioctls);
}
-
EXPORT_SYMBOL(snd_ctl_register_ioctl);
#ifdef CONFIG_COMPAT
+/**
+ * snd_ctl_register_ioctl_compat - register the device-specific 32bit compat
+ * control-ioctls
+ * @fcn: ioctl callback function
+ */
int snd_ctl_register_ioctl_compat(snd_kctl_ioctl_func_t fcn)
{
return _snd_ctl_register_ioctl(fcn, &snd_control_compat_ioctls);
}
-
EXPORT_SYMBOL(snd_ctl_register_ioctl_compat);
#endif
@@ -1566,19 +1576,26 @@ static int _snd_ctl_unregister_ioctl(snd_kctl_ioctl_func_t fcn,
return -EINVAL;
}
+/**
+ * snd_ctl_unregister_ioctl - de-register the device-specific control-ioctls
+ * @fcn: ioctl callback function to unregister
+ */
int snd_ctl_unregister_ioctl(snd_kctl_ioctl_func_t fcn)
{
return _snd_ctl_unregister_ioctl(fcn, &snd_control_ioctls);
}
-
EXPORT_SYMBOL(snd_ctl_unregister_ioctl);
#ifdef CONFIG_COMPAT
+/**
+ * snd_ctl_unregister_ioctl - de-register the device-specific compat 32bit
+ * control-ioctls
+ * @fcn: ioctl callback function to unregister
+ */
int snd_ctl_unregister_ioctl_compat(snd_kctl_ioctl_func_t fcn)
{
return _snd_ctl_unregister_ioctl(fcn, &snd_control_compat_ioctls);
}
-
EXPORT_SYMBOL(snd_ctl_unregister_ioctl_compat);
#endif
@@ -1702,6 +1719,16 @@ int snd_ctl_create(struct snd_card *card)
/*
* Frequently used control callbacks/helpers
*/
+
+/**
+ * snd_ctl_boolean_mono_info - Helper function for a standard boolean info
+ * callback with a mono channel
+ * @kcontrol: the kcontrol instance
+ * @uinfo: info to store
+ *
+ * This is a function that can be used as info callback for a standard
+ * boolean control with a single mono channel.
+ */
int snd_ctl_boolean_mono_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
@@ -1711,9 +1738,17 @@ int snd_ctl_boolean_mono_info(struct snd_kcontrol *kcontrol,
uinfo->value.integer.max = 1;
return 0;
}
-
EXPORT_SYMBOL(snd_ctl_boolean_mono_info);
+/**
+ * snd_ctl_boolean_stereo_info - Helper function for a standard boolean info
+ * callback with stereo two channels
+ * @kcontrol: the kcontrol instance
+ * @uinfo: info to store
+ *
+ * This is a function that can be used as info callback for a standard
+ * boolean control with stereo two channels.
+ */
int snd_ctl_boolean_stereo_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
@@ -1723,7 +1758,6 @@ int snd_ctl_boolean_stereo_info(struct snd_kcontrol *kcontrol,
uinfo->value.integer.max = 1;
return 0;
}
-
EXPORT_SYMBOL(snd_ctl_boolean_stereo_info);
/**
@@ -1745,8 +1779,13 @@ int snd_ctl_enum_info(struct snd_ctl_elem_info *info, unsigned int channels,
info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
info->count = channels;
info->value.enumerated.items = items;
+ if (!items)
+ return 0;
if (info->value.enumerated.item >= items)
info->value.enumerated.item = items - 1;
+ WARN(strlen(names[info->value.enumerated.item]) >= sizeof(info->value.enumerated.name),
+ "ALSA: too long item name '%s'\n",
+ names[info->value.enumerated.item]);
strlcpy(info->value.enumerated.name,
names[info->value.enumerated.item],
sizeof(info->value.enumerated.name));
diff --git a/sound/core/init.c b/sound/core/init.c
index 7bdfd19e24a8..074875d68c15 100644
--- a/sound/core/init.c
+++ b/sound/core/init.c
@@ -438,17 +438,6 @@ int snd_card_disconnect(struct snd_card *card)
EXPORT_SYMBOL(snd_card_disconnect);
-/**
- * snd_card_free - frees given soundcard structure
- * @card: soundcard structure
- *
- * This function releases the soundcard structure and the all assigned
- * devices automatically. That is, you don't have to release the devices
- * by yourself.
- *
- * Return: Zero. Frees all associated devices and frees the control
- * interface associated to given soundcard.
- */
static int snd_card_do_free(struct snd_card *card)
{
#if IS_ENABLED(CONFIG_SND_MIXER_OSS)
@@ -469,6 +458,15 @@ static int snd_card_do_free(struct snd_card *card)
return 0;
}
+/**
+ * snd_card_free_when_closed - Disconnect the card, free it later eventually
+ * @card: soundcard structure
+ *
+ * Unlike snd_card_free(), this function doesn't try to release the 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.
+ */
int snd_card_free_when_closed(struct snd_card *card)
{
int ret = snd_card_disconnect(card);
@@ -479,6 +477,19 @@ int snd_card_free_when_closed(struct snd_card *card)
}
EXPORT_SYMBOL(snd_card_free_when_closed);
+/**
+ * snd_card_free - frees given soundcard structure
+ * @card: soundcard structure
+ *
+ * This function releases the soundcard structure and the all assigned
+ * devices automatically. That is, you don't have to release the devices
+ * by yourself.
+ *
+ * This function waits until the all resources are properly released.
+ *
+ * Return: Zero. Frees all associated devices and frees the control
+ * interface associated to given soundcard.
+ */
int snd_card_free(struct snd_card *card)
{
struct completion released;
diff --git a/sound/core/pcm.c b/sound/core/pcm.c
index c6ff94ab1ad6..cfc56c806964 100644
--- a/sound/core/pcm.c
+++ b/sound/core/pcm.c
@@ -220,6 +220,10 @@ static char *snd_pcm_format_names[] = {
FORMAT(DSD_U32_BE),
};
+/**
+ * snd_pcm_format_name - Return a name string for the given PCM format
+ * @format: PCM format
+ */
const char *snd_pcm_format_name(snd_pcm_format_t format)
{
if ((__force unsigned int)format >= ARRAY_SIZE(snd_pcm_format_names))
@@ -481,6 +485,19 @@ static void snd_pcm_substream_proc_status_read(struct snd_info_entry *entry,
}
#ifdef CONFIG_SND_PCM_XRUN_DEBUG
+static void snd_pcm_xrun_injection_write(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_pcm_substream *substream = entry->private_data;
+ struct snd_pcm_runtime *runtime;
+
+ snd_pcm_stream_lock_irq(substream);
+ runtime = substream->runtime;
+ if (runtime && runtime->status->state == SNDRV_PCM_STATE_RUNNING)
+ snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
+ snd_pcm_stream_unlock_irq(substream);
+}
+
static void snd_pcm_xrun_debug_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
@@ -612,6 +629,22 @@ static int snd_pcm_substream_proc_init(struct snd_pcm_substream *substream)
}
substream->proc_status_entry = entry;
+#ifdef CONFIG_SND_PCM_XRUN_DEBUG
+ entry = snd_info_create_card_entry(card, "xrun_injection",
+ substream->proc_root);
+ if (entry) {
+ entry->private_data = substream;
+ entry->c.text.read = NULL;
+ entry->c.text.write = snd_pcm_xrun_injection_write;
+ entry->mode = S_IFREG | S_IWUSR;
+ if (snd_info_register(entry) < 0) {
+ snd_info_free_entry(entry);
+ entry = NULL;
+ }
+ }
+ substream->proc_xrun_injection_entry = entry;
+#endif /* CONFIG_SND_PCM_XRUN_DEBUG */
+
return 0;
}
@@ -625,6 +658,10 @@ static int snd_pcm_substream_proc_done(struct snd_pcm_substream *substream)
substream->proc_sw_params_entry = NULL;
snd_info_free_entry(substream->proc_status_entry);
substream->proc_status_entry = NULL;
+#ifdef CONFIG_SND_PCM_XRUN_DEBUG
+ snd_info_free_entry(substream->proc_xrun_injection_entry);
+ substream->proc_xrun_injection_entry = NULL;
+#endif
snd_info_free_entry(substream->proc_root);
substream->proc_root = NULL;
return 0;
@@ -709,7 +746,6 @@ int snd_pcm_new_stream(struct snd_pcm *pcm, int stream, int substream_count)
}
return 0;
}
-
EXPORT_SYMBOL(snd_pcm_new_stream);
static int _snd_pcm_new(struct snd_card *card, const char *id, int device,
@@ -1157,6 +1193,15 @@ static int snd_pcm_dev_disconnect(struct snd_device *device)
return 0;
}
+/**
+ * snd_pcm_notify - Add/remove the notify list
+ * @notify: PCM notify list
+ * @nfree: 0 = register, 1 = unregister
+ *
+ * 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.
+ */
int snd_pcm_notify(struct snd_pcm_notify *notify, int nfree)
{
struct snd_pcm *pcm;
@@ -1179,7 +1224,6 @@ int snd_pcm_notify(struct snd_pcm_notify *notify, int nfree)
mutex_unlock(&register_mutex);
return 0;
}
-
EXPORT_SYMBOL(snd_pcm_notify);
#ifdef CONFIG_PROC_FS
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c
index dfc28542a007..ec9e7866177f 100644
--- a/sound/core/pcm_lib.c
+++ b/sound/core/pcm_lib.c
@@ -32,6 +32,15 @@
#include <sound/pcm_params.h>
#include <sound/timer.h>
+#ifdef CONFIG_SND_PCM_XRUN_DEBUG
+#define CREATE_TRACE_POINTS
+#include "pcm_trace.h"
+#else
+#define trace_hwptr(substream, pos, in_interrupt)
+#define trace_xrun(substream)
+#define trace_hw_ptr_error(substream, reason)
+#endif
+
/*
* fill ring buffer with silence
* runtime->silence_start: starting pointer to silence area
@@ -146,10 +155,6 @@ EXPORT_SYMBOL(snd_pcm_debug_name);
#define XRUN_DEBUG_BASIC (1<<0)
#define XRUN_DEBUG_STACK (1<<1) /* dump also stack */
#define XRUN_DEBUG_JIFFIESCHECK (1<<2) /* do jiffies check */
-#define XRUN_DEBUG_PERIODUPDATE (1<<3) /* full period update info */
-#define XRUN_DEBUG_HWPTRUPDATE (1<<4) /* full hwptr update info */
-#define XRUN_DEBUG_LOG (1<<5) /* show last 10 positions on err */
-#define XRUN_DEBUG_LOGONCE (1<<6) /* do above only once */
#ifdef CONFIG_SND_PCM_XRUN_DEBUG
@@ -168,6 +173,7 @@ static void xrun(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
+ trace_xrun(substream);
if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE)
snd_pcm_gettime(runtime, (struct timespec *)&runtime->status->tstamp);
snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
@@ -180,97 +186,19 @@ static void xrun(struct snd_pcm_substream *substream)
}
#ifdef CONFIG_SND_PCM_XRUN_DEBUG
-#define hw_ptr_error(substream, fmt, args...) \
+#define hw_ptr_error(substream, in_interrupt, reason, fmt, args...) \
do { \
+ trace_hw_ptr_error(substream, reason); \
if (xrun_debug(substream, XRUN_DEBUG_BASIC)) { \
- xrun_log_show(substream); \
- pr_err_ratelimited("ALSA: PCM: " fmt, ##args); \
+ pr_err_ratelimited("ALSA: PCM: [%c] " reason ": " fmt, \
+ (in_interrupt) ? 'Q' : 'P', ##args); \
dump_stack_on_xrun(substream); \
} \
} while (0)
-#define XRUN_LOG_CNT 10
-
-struct hwptr_log_entry {
- unsigned int in_interrupt;
- unsigned long jiffies;
- snd_pcm_uframes_t pos;
- snd_pcm_uframes_t period_size;
- snd_pcm_uframes_t buffer_size;
- snd_pcm_uframes_t old_hw_ptr;
- snd_pcm_uframes_t hw_ptr_base;
-};
-
-struct snd_pcm_hwptr_log {
- unsigned int idx;
- unsigned int hit: 1;
- struct hwptr_log_entry entries[XRUN_LOG_CNT];
-};
-
-static void xrun_log(struct snd_pcm_substream *substream,
- snd_pcm_uframes_t pos, int in_interrupt)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_pcm_hwptr_log *log = runtime->hwptr_log;
- struct hwptr_log_entry *entry;
-
- if (log == NULL) {
- log = kzalloc(sizeof(*log), GFP_ATOMIC);
- if (log == NULL)
- return;
- runtime->hwptr_log = log;
- } else {
- if (xrun_debug(substream, XRUN_DEBUG_LOGONCE) && log->hit)
- return;
- }
- entry = &log->entries[log->idx];
- entry->in_interrupt = in_interrupt;
- entry->jiffies = jiffies;
- entry->pos = pos;
- entry->period_size = runtime->period_size;
- entry->buffer_size = runtime->buffer_size;
- entry->old_hw_ptr = runtime->status->hw_ptr;
- entry->hw_ptr_base = runtime->hw_ptr_base;
- log->idx = (log->idx + 1) % XRUN_LOG_CNT;
-}
-
-static void xrun_log_show(struct snd_pcm_substream *substream)
-{
- struct snd_pcm_hwptr_log *log = substream->runtime->hwptr_log;
- struct hwptr_log_entry *entry;
- char name[16];
- unsigned int idx;
- int cnt;
-
- if (log == NULL)
- return;
- if (xrun_debug(substream, XRUN_DEBUG_LOGONCE) && log->hit)
- return;
- snd_pcm_debug_name(substream, name, sizeof(name));
- for (cnt = 0, idx = log->idx; cnt < XRUN_LOG_CNT; cnt++) {
- entry = &log->entries[idx];
- if (entry->period_size == 0)
- break;
- pr_info("hwptr log: %s: %sj=%lu, pos=%ld/%ld/%ld, "
- "hwptr=%ld/%ld\n",
- name, entry->in_interrupt ? "[Q] " : "",
- entry->jiffies,
- (unsigned long)entry->pos,
- (unsigned long)entry->period_size,
- (unsigned long)entry->buffer_size,
- (unsigned long)entry->old_hw_ptr,
- (unsigned long)entry->hw_ptr_base);
- idx++;
- idx %= XRUN_LOG_CNT;
- }
- log->hit = 1;
-}
-
#else /* ! CONFIG_SND_PCM_XRUN_DEBUG */
#define hw_ptr_error(substream, fmt, args...) do { } while (0)
-#define xrun_log(substream, pos, in_interrupt) do { } while (0)
-#define xrun_log_show(substream) do { } while (0)
#endif
@@ -343,17 +271,15 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
if (printk_ratelimit()) {
char name[16];
snd_pcm_debug_name(substream, name, sizeof(name));
- xrun_log_show(substream);
pcm_err(substream->pcm,
- "XRUN: %s, pos = %ld, buffer size = %ld, period size = %ld\n",
+ "BUG: %s, pos = %ld, buffer size = %ld, period size = %ld\n",
name, pos, runtime->buffer_size,
runtime->period_size);
}
pos = 0;
}
pos -= pos % runtime->min_align;
- if (xrun_debug(substream, XRUN_DEBUG_LOG))
- xrun_log(substream, pos, in_interrupt);
+ trace_hwptr(substream, pos, in_interrupt);
hw_base = runtime->hw_ptr_base;
new_hw_ptr = hw_base + pos;
if (in_interrupt) {
@@ -388,22 +314,6 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
delta = new_hw_ptr - old_hw_ptr;
if (delta < 0)
delta += runtime->boundary;
- if (xrun_debug(substream, in_interrupt ?
- XRUN_DEBUG_PERIODUPDATE : XRUN_DEBUG_HWPTRUPDATE)) {
- char name[16];
- snd_pcm_debug_name(substream, name, sizeof(name));
- pcm_dbg(substream->pcm,
- "%s_update: %s: pos=%u/%u/%u, hwptr=%ld/%ld/%ld/%ld\n",
- in_interrupt ? "period" : "hwptr",
- name,
- (unsigned int)pos,
- (unsigned int)runtime->period_size,
- (unsigned int)runtime->buffer_size,
- (unsigned long)delta,
- (unsigned long)old_hw_ptr,
- (unsigned long)new_hw_ptr,
- (unsigned long)runtime->hw_ptr_base);
- }
if (runtime->no_period_wakeup) {
snd_pcm_sframes_t xrun_threshold;
@@ -431,13 +341,10 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
/* something must be really wrong */
if (delta >= runtime->buffer_size + runtime->period_size) {
- hw_ptr_error(substream,
- "Unexpected hw_pointer value %s"
- "(stream=%i, pos=%ld, new_hw_ptr=%ld, "
- "old_hw_ptr=%ld)\n",
- in_interrupt ? "[Q] " : "[P]",
- substream->stream, (long)pos,
- (long)new_hw_ptr, (long)old_hw_ptr);
+ hw_ptr_error(substream, in_interrupt, "Unexpected hw_ptr",
+ "(stream=%i, pos=%ld, new_hw_ptr=%ld, old_hw_ptr=%ld)\n",
+ substream->stream, (long)pos,
+ (long)new_hw_ptr, (long)old_hw_ptr);
return 0;
}
@@ -474,11 +381,8 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
delta--;
}
/* align hw_base to buffer_size */
- hw_ptr_error(substream,
- "hw_ptr skipping! %s"
- "(pos=%ld, delta=%ld, period=%ld, "
- "jdelta=%lu/%lu/%lu, hw_ptr=%ld/%ld)\n",
- in_interrupt ? "[Q] " : "",
+ hw_ptr_error(substream, in_interrupt, "hw_ptr skipping",
+ "(pos=%ld, delta=%ld, period=%ld, jdelta=%lu/%lu/%lu, hw_ptr=%ld/%ld)\n",
(long)pos, (long)hdelta,
(long)runtime->period_size, jdelta,
((hdelta * HZ) / runtime->rate), hw_base,
@@ -490,11 +394,9 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
}
no_jiffies_check:
if (delta > runtime->period_size + runtime->period_size / 2) {
- hw_ptr_error(substream,
- "Lost interrupts? %s"
- "(stream=%i, delta=%ld, new_hw_ptr=%ld, "
- "old_hw_ptr=%ld)\n",
- in_interrupt ? "[Q] " : "",
+ hw_ptr_error(substream, in_interrupt,
+ "Lost interrupts?",
+ "(stream=%i, delta=%ld, new_hw_ptr=%ld, old_hw_ptr=%ld)\n",
substream->stream, (long)delta,
(long)new_hw_ptr,
(long)old_hw_ptr);
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index 166d59cdc86b..095d9572ad2b 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -35,9 +35,6 @@
#include <sound/timer.h>
#include <sound/minors.h>
#include <asm/io.h>
-#if defined(CONFIG_MIPS) && defined(CONFIG_DMA_NONCOHERENT)
-#include <dma-coherence.h>
-#endif
/*
* Compatibility
@@ -77,6 +74,14 @@ static int snd_pcm_open(struct file *file, struct snd_pcm *pcm, int stream);
static DEFINE_RWLOCK(snd_pcm_link_rwlock);
static DECLARE_RWSEM(snd_pcm_link_rwsem);
+/**
+ * snd_pcm_stream_lock - Lock the PCM stream
+ * @substream: PCM substream
+ *
+ * This locks the PCM stream's spinlock or mutex depending on the nonatomic
+ * flag of the given substream. This also takes the global link rw lock
+ * (or rw sem), too, for avoiding the race with linked streams.
+ */
void snd_pcm_stream_lock(struct snd_pcm_substream *substream)
{
if (substream->pcm->nonatomic) {
@@ -89,6 +94,12 @@ void snd_pcm_stream_lock(struct snd_pcm_substream *substream)
}
EXPORT_SYMBOL_GPL(snd_pcm_stream_lock);
+/**
+ * snd_pcm_stream_lock - Unlock the PCM stream
+ * @substream: PCM substream
+ *
+ * This unlocks the PCM stream that has been locked via snd_pcm_stream_lock().
+ */
void snd_pcm_stream_unlock(struct snd_pcm_substream *substream)
{
if (substream->pcm->nonatomic) {
@@ -101,6 +112,14 @@ void snd_pcm_stream_unlock(struct snd_pcm_substream *substream)
}
EXPORT_SYMBOL_GPL(snd_pcm_stream_unlock);
+/**
+ * snd_pcm_stream_lock_irq - Lock the PCM stream
+ * @substream: PCM substream
+ *
+ * This locks the PCM stream like snd_pcm_stream_lock() and disables the local
+ * IRQ (only when nonatomic is false). In nonatomic case, this is identical
+ * as snd_pcm_stream_lock().
+ */
void snd_pcm_stream_lock_irq(struct snd_pcm_substream *substream)
{
if (!substream->pcm->nonatomic)
@@ -109,6 +128,12 @@ void snd_pcm_stream_lock_irq(struct snd_pcm_substream *substream)
}
EXPORT_SYMBOL_GPL(snd_pcm_stream_lock_irq);
+/**
+ * snd_pcm_stream_unlock_irq - Unlock the PCM stream
+ * @substream: PCM substream
+ *
+ * This is a counter-part of snd_pcm_stream_lock_irq().
+ */
void snd_pcm_stream_unlock_irq(struct snd_pcm_substream *substream)
{
snd_pcm_stream_unlock(substream);
@@ -127,6 +152,13 @@ unsigned long _snd_pcm_stream_lock_irqsave(struct snd_pcm_substream *substream)
}
EXPORT_SYMBOL_GPL(_snd_pcm_stream_lock_irqsave);
+/**
+ * snd_pcm_stream_unlock_irqrestore - Unlock the PCM stream
+ * @substream: PCM substream
+ * @flags: irq flags
+ *
+ * This is a counter-part of snd_pcm_stream_lock_irqsave().
+ */
void snd_pcm_stream_unlock_irqrestore(struct snd_pcm_substream *substream,
unsigned long flags)
{
@@ -195,6 +227,21 @@ int snd_pcm_info_user(struct snd_pcm_substream *substream,
return err;
}
+static bool hw_support_mmap(struct snd_pcm_substream *substream)
+{
+ if (!(substream->runtime->hw.info & SNDRV_PCM_INFO_MMAP))
+ return false;
+ /* check architectures that return -EINVAL from dma_mmap_coherent() */
+ /* FIXME: this should be some global flag */
+#if defined(CONFIG_C6X) || defined(CONFIG_FRV) || defined(CONFIG_MN10300) ||\
+ defined(CONFIG_PARISC) || defined(CONFIG_XTENSA)
+ if (!substream->ops->mmap &&
+ substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV)
+ return false;
+#endif
+ return true;
+}
+
#undef RULES_DEBUG
#ifdef RULES_DEBUG
@@ -372,8 +419,12 @@ int snd_pcm_hw_refine(struct snd_pcm_substream *substream,
}
hw = &substream->runtime->hw;
- if (!params->info)
+ if (!params->info) {
params->info = hw->info & ~SNDRV_PCM_INFO_FIFO_IN_FRAMES;
+ if (!hw_support_mmap(substream))
+ params->info &= ~(SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID);
+ }
if (!params->fifo_size) {
m = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
@@ -849,14 +900,19 @@ static int snd_pcm_action_single(struct action_ops *ops,
return res;
}
-/* call in mutex-protected context */
-static int snd_pcm_action_mutex(struct action_ops *ops,
- struct snd_pcm_substream *substream,
- int state)
+/*
+ * Note: call with stream lock
+ */
+static int snd_pcm_action(struct action_ops *ops,
+ struct snd_pcm_substream *substream,
+ int state)
{
int res;
- if (snd_pcm_stream_linked(substream)) {
+ if (!snd_pcm_stream_linked(substream))
+ return snd_pcm_action_single(ops, substream, state);
+
+ if (substream->pcm->nonatomic) {
if (!mutex_trylock(&substream->group->mutex)) {
mutex_unlock(&substream->self_group.mutex);
mutex_lock(&substream->group->mutex);
@@ -865,24 +921,6 @@ static int snd_pcm_action_mutex(struct action_ops *ops,
res = snd_pcm_action_group(ops, substream, state, 1);
mutex_unlock(&substream->group->mutex);
} else {
- res = snd_pcm_action_single(ops, substream, state);
- }
- return res;
-}
-
-/*
- * Note: call with stream lock
- */
-static int snd_pcm_action(struct action_ops *ops,
- struct snd_pcm_substream *substream,
- int state)
-{
- int res;
-
- if (substream->pcm->nonatomic)
- return snd_pcm_action_mutex(ops, substream, state);
-
- if (snd_pcm_stream_linked(substream)) {
if (!spin_trylock(&substream->group->lock)) {
spin_unlock(&substream->self_group.lock);
spin_lock(&substream->group->lock);
@@ -890,34 +928,10 @@ static int snd_pcm_action(struct action_ops *ops,
}
res = snd_pcm_action_group(ops, substream, state, 1);
spin_unlock(&substream->group->lock);
- } else {
- res = snd_pcm_action_single(ops, substream, state);
}
return res;
}
-static int snd_pcm_action_lock_mutex(struct action_ops *ops,
- struct snd_pcm_substream *substream,
- int state)
-{
- int res;
-
- down_read(&snd_pcm_link_rwsem);
- if (snd_pcm_stream_linked(substream)) {
- mutex_lock(&substream->group->mutex);
- mutex_lock(&substream->self_group.mutex);
- res = snd_pcm_action_group(ops, substream, state, 1);
- mutex_unlock(&substream->self_group.mutex);
- mutex_unlock(&substream->group->mutex);
- } else {
- mutex_lock(&substream->self_group.mutex);
- res = snd_pcm_action_single(ops, substream, state);
- mutex_unlock(&substream->self_group.mutex);
- }
- up_read(&snd_pcm_link_rwsem);
- return res;
-}
-
/*
* Note: don't use any locks before
*/
@@ -927,22 +941,9 @@ static int snd_pcm_action_lock_irq(struct action_ops *ops,
{
int res;
- if (substream->pcm->nonatomic)
- return snd_pcm_action_lock_mutex(ops, substream, state);
-
- read_lock_irq(&snd_pcm_link_rwlock);
- if (snd_pcm_stream_linked(substream)) {
- spin_lock(&substream->group->lock);
- spin_lock(&substream->self_group.lock);
- res = snd_pcm_action_group(ops, substream, state, 1);
- spin_unlock(&substream->self_group.lock);
- spin_unlock(&substream->group->lock);
- } else {
- spin_lock(&substream->self_group.lock);
- res = snd_pcm_action_single(ops, substream, state);
- spin_unlock(&substream->self_group.lock);
- }
- read_unlock_irq(&snd_pcm_link_rwlock);
+ snd_pcm_stream_lock_irq(substream);
+ res = snd_pcm_action(ops, substream, state);
+ snd_pcm_stream_unlock_irq(substream);
return res;
}
@@ -1051,10 +1052,10 @@ static void snd_pcm_post_stop(struct snd_pcm_substream *substream, int state)
struct snd_pcm_runtime *runtime = substream->runtime;
if (runtime->status->state != state) {
snd_pcm_trigger_tstamp(substream);
+ runtime->status->state = state;
if (substream->timer)
snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MSTOP,
&runtime->trigger_tstamp);
- runtime->status->state = state;
}
wake_up(&runtime->sleep);
wake_up(&runtime->tsleep);
@@ -1097,6 +1098,28 @@ int snd_pcm_drain_done(struct snd_pcm_substream *substream)
SNDRV_PCM_STATE_SETUP);
}
+/**
+ * snd_pcm_stop_xrun - stop the running streams as XRUN
+ * @substream: the PCM substream instance
+ *
+ * This stops the given running substream (and all linked substreams) as XRUN.
+ * Unlike snd_pcm_stop(), this function takes the substream lock by itself.
+ *
+ * Return: Zero if successful, or a negative error code.
+ */
+int snd_pcm_stop_xrun(struct snd_pcm_substream *substream)
+{
+ unsigned long flags;
+ int ret = 0;
+
+ snd_pcm_stream_lock_irqsave(substream, flags);
+ if (snd_pcm_running(substream))
+ ret = snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
+ snd_pcm_stream_unlock_irqrestore(substream, flags);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(snd_pcm_stop_xrun);
+
/*
* pause callbacks
*/
@@ -1203,11 +1226,11 @@ static void snd_pcm_post_suspend(struct snd_pcm_substream *substream, int state)
{
struct snd_pcm_runtime *runtime = substream->runtime;
snd_pcm_trigger_tstamp(substream);
+ runtime->status->suspended_state = runtime->status->state;
+ runtime->status->state = SNDRV_PCM_STATE_SUSPENDED;
if (substream->timer)
snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MSUSPEND,
&runtime->trigger_tstamp);
- runtime->status->suspended_state = runtime->status->state;
- runtime->status->state = SNDRV_PCM_STATE_SUSPENDED;
wake_up(&runtime->sleep);
wake_up(&runtime->tsleep);
}
@@ -1310,10 +1333,10 @@ static void snd_pcm_post_resume(struct snd_pcm_substream *substream, int state)
{
struct snd_pcm_runtime *runtime = substream->runtime;
snd_pcm_trigger_tstamp(substream);
+ runtime->status->state = runtime->status->suspended_state;
if (substream->timer)
snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MRESUME,
&runtime->trigger_tstamp);
- runtime->status->state = runtime->status->suspended_state;
}
static struct action_ops snd_pcm_action_resume = {
@@ -2070,7 +2093,7 @@ int snd_pcm_hw_constraints_complete(struct snd_pcm_substream *substream)
mask |= 1 << SNDRV_PCM_ACCESS_RW_INTERLEAVED;
if (hw->info & SNDRV_PCM_INFO_NONINTERLEAVED)
mask |= 1 << SNDRV_PCM_ACCESS_RW_NONINTERLEAVED;
- if (hw->info & SNDRV_PCM_INFO_MMAP) {
+ if (hw_support_mmap(substream)) {
if (hw->info & SNDRV_PCM_INFO_INTERLEAVED)
mask |= 1 << SNDRV_PCM_ACCESS_MMAP_INTERLEAVED;
if (hw->info & SNDRV_PCM_INFO_NONINTERLEAVED)
@@ -3249,20 +3272,6 @@ static inline struct page *
snd_pcm_default_page_ops(struct snd_pcm_substream *substream, unsigned long ofs)
{
void *vaddr = substream->runtime->dma_area + ofs;
-#if defined(CONFIG_MIPS) && defined(CONFIG_DMA_NONCOHERENT)
- if (substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV)
- return virt_to_page(CAC_ADDR(vaddr));
-#endif
-#if defined(CONFIG_PPC32) && defined(CONFIG_NOT_COHERENT_CACHE)
- if (substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV) {
- dma_addr_t addr = substream->runtime->dma_addr + ofs;
- addr -= get_dma_offset(substream->dma_buffer.dev.dev);
- /* assume dma_handle set via pfn_to_phys() in
- * mm/dma-noncoherent.c
- */
- return pfn_to_page(addr >> PAGE_SHIFT);
- }
-#endif
return virt_to_page(vaddr);
}
@@ -3307,16 +3316,18 @@ static const struct vm_operations_struct snd_pcm_vm_ops_data_fault = {
.fault = snd_pcm_mmap_data_fault,
};
-#ifndef ARCH_HAS_DMA_MMAP_COHERENT
-/* This should be defined / handled globally! */
-#if defined(CONFIG_ARM) || defined(CONFIG_ARM64)
-#define ARCH_HAS_DMA_MMAP_COHERENT
-#endif
-#endif
-
/*
* mmap the DMA buffer on RAM
*/
+
+/**
+ * snd_pcm_lib_default_mmap - Default PCM data mmap function
+ * @substream: PCM substream
+ * @area: VMA
+ *
+ * This is the default mmap handler for PCM data. When mmap pcm_ops is NULL,
+ * this function is invoked implicitly.
+ */
int snd_pcm_lib_default_mmap(struct snd_pcm_substream *substream,
struct vm_area_struct *area)
{
@@ -3329,7 +3340,7 @@ int snd_pcm_lib_default_mmap(struct snd_pcm_substream *substream,
area->vm_end - area->vm_start, area->vm_page_prot);
}
#endif /* CONFIG_GENERIC_ALLOCATOR */
-#ifdef ARCH_HAS_DMA_MMAP_COHERENT
+#ifndef CONFIG_X86 /* for avoiding warnings arch/x86/mm/pat.c */
if (!substream->ops->page &&
substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV)
return dma_mmap_coherent(substream->dma_buffer.dev.dev,
@@ -3337,11 +3348,7 @@ int snd_pcm_lib_default_mmap(struct snd_pcm_substream *substream,
substream->runtime->dma_area,
substream->runtime->dma_addr,
area->vm_end - area->vm_start);
-#elif defined(CONFIG_MIPS) && defined(CONFIG_DMA_NONCOHERENT)
- if (substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV &&
- !plat_device_is_coherent(substream->dma_buffer.dev.dev))
- area->vm_page_prot = pgprot_noncached(area->vm_page_prot);
-#endif /* ARCH_HAS_DMA_MMAP_COHERENT */
+#endif /* CONFIG_X86 */
/* mmap with fault handler */
area->vm_ops = &snd_pcm_vm_ops_data_fault;
return 0;
@@ -3352,6 +3359,15 @@ EXPORT_SYMBOL_GPL(snd_pcm_lib_default_mmap);
* mmap the DMA buffer on I/O memory area
*/
#if SNDRV_PCM_INFO_MMAP_IOMEM
+/**
+ * snd_pcm_lib_mmap_iomem - Default PCM data mmap function for I/O mem
+ * @substream: PCM substream
+ * @area: VMA
+ *
+ * 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.
+ */
int snd_pcm_lib_mmap_iomem(struct snd_pcm_substream *substream,
struct vm_area_struct *area)
{
diff --git a/sound/core/pcm_trace.h b/sound/core/pcm_trace.h
new file mode 100644
index 000000000000..b63b654da5ff
--- /dev/null
+++ b/sound/core/pcm_trace.h
@@ -0,0 +1,110 @@
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM snd_pcm
+#define TRACE_INCLUDE_FILE pcm_trace
+
+#if !defined(_PCM_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _PCM_TRACE_H
+
+#include <linux/tracepoint.h>
+
+TRACE_EVENT(hwptr,
+ TP_PROTO(struct snd_pcm_substream *substream, snd_pcm_uframes_t pos, bool irq),
+ TP_ARGS(substream, pos, irq),
+ TP_STRUCT__entry(
+ __field( bool, in_interrupt )
+ __field( unsigned int, card )
+ __field( unsigned int, device )
+ __field( unsigned int, number )
+ __field( unsigned int, stream )
+ __field( snd_pcm_uframes_t, pos )
+ __field( snd_pcm_uframes_t, period_size )
+ __field( snd_pcm_uframes_t, buffer_size )
+ __field( snd_pcm_uframes_t, old_hw_ptr )
+ __field( snd_pcm_uframes_t, hw_ptr_base )
+ ),
+ TP_fast_assign(
+ __entry->in_interrupt = (irq);
+ __entry->card = (substream)->pcm->card->number;
+ __entry->device = (substream)->pcm->device;
+ __entry->number = (substream)->number;
+ __entry->stream = (substream)->stream;
+ __entry->pos = (pos);
+ __entry->period_size = (substream)->runtime->period_size;
+ __entry->buffer_size = (substream)->runtime->buffer_size;
+ __entry->old_hw_ptr = (substream)->runtime->status->hw_ptr;
+ __entry->hw_ptr_base = (substream)->runtime->hw_ptr_base;
+ ),
+ TP_printk("pcmC%dD%d%c/sub%d: %s: pos=%lu, old=%lu, base=%lu, period=%lu, buf=%lu",
+ __entry->card, __entry->device,
+ __entry->stream == SNDRV_PCM_STREAM_PLAYBACK ? 'p' : 'c',
+ __entry->number,
+ __entry->in_interrupt ? "IRQ" : "POS",
+ (unsigned long)__entry->pos,
+ (unsigned long)__entry->old_hw_ptr,
+ (unsigned long)__entry->hw_ptr_base,
+ (unsigned long)__entry->period_size,
+ (unsigned long)__entry->buffer_size)
+);
+
+TRACE_EVENT(xrun,
+ TP_PROTO(struct snd_pcm_substream *substream),
+ TP_ARGS(substream),
+ TP_STRUCT__entry(
+ __field( unsigned int, card )
+ __field( unsigned int, device )
+ __field( unsigned int, number )
+ __field( unsigned int, stream )
+ __field( snd_pcm_uframes_t, period_size )
+ __field( snd_pcm_uframes_t, buffer_size )
+ __field( snd_pcm_uframes_t, old_hw_ptr )
+ __field( snd_pcm_uframes_t, hw_ptr_base )
+ ),
+ TP_fast_assign(
+ __entry->card = (substream)->pcm->card->number;
+ __entry->device = (substream)->pcm->device;
+ __entry->number = (substream)->number;
+ __entry->stream = (substream)->stream;
+ __entry->period_size = (substream)->runtime->period_size;
+ __entry->buffer_size = (substream)->runtime->buffer_size;
+ __entry->old_hw_ptr = (substream)->runtime->status->hw_ptr;
+ __entry->hw_ptr_base = (substream)->runtime->hw_ptr_base;
+ ),
+ TP_printk("pcmC%dD%d%c/sub%d: XRUN: old=%lu, base=%lu, period=%lu, buf=%lu",
+ __entry->card, __entry->device,
+ __entry->stream == SNDRV_PCM_STREAM_PLAYBACK ? 'p' : 'c',
+ __entry->number,
+ (unsigned long)__entry->old_hw_ptr,
+ (unsigned long)__entry->hw_ptr_base,
+ (unsigned long)__entry->period_size,
+ (unsigned long)__entry->buffer_size)
+);
+
+TRACE_EVENT(hw_ptr_error,
+ TP_PROTO(struct snd_pcm_substream *substream, const char *why),
+ TP_ARGS(substream, why),
+ TP_STRUCT__entry(
+ __field( unsigned int, card )
+ __field( unsigned int, device )
+ __field( unsigned int, number )
+ __field( unsigned int, stream )
+ __field( const char *, reason )
+ ),
+ TP_fast_assign(
+ __entry->card = (substream)->pcm->card->number;
+ __entry->device = (substream)->pcm->device;
+ __entry->number = (substream)->number;
+ __entry->stream = (substream)->stream;
+ __entry->reason = (why);
+ ),
+ TP_printk("pcmC%dD%d%c/sub%d: ERROR: %s",
+ __entry->card, __entry->device,
+ __entry->stream == SNDRV_PCM_STREAM_PLAYBACK ? 'p' : 'c',
+ __entry->number, __entry->reason)
+);
+
+#endif /* _PCM_TRACE_H */
+
+/* This part must be outside protection */
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#include <trace/define_trace.h>
diff --git a/sound/core/seq/oss/seq_oss_init.c b/sound/core/seq/oss/seq_oss_init.c
index b9184d20c39f..b0e32e161dd1 100644
--- a/sound/core/seq/oss/seq_oss_init.c
+++ b/sound/core/seq/oss/seq_oss_init.c
@@ -403,14 +403,11 @@ free_devinfo(void *private)
{
struct seq_oss_devinfo *dp = (struct seq_oss_devinfo *)private;
- if (dp->timer)
- snd_seq_oss_timer_delete(dp->timer);
+ snd_seq_oss_timer_delete(dp->timer);
- if (dp->writeq)
- snd_seq_oss_writeq_delete(dp->writeq);
+ snd_seq_oss_writeq_delete(dp->writeq);
- if (dp->readq)
- snd_seq_oss_readq_delete(dp->readq);
+ snd_seq_oss_readq_delete(dp->readq);
kfree(dp);
}
diff --git a/sound/core/seq/seq.c b/sound/core/seq/seq.c
index 712110561082..7e0aabb808a6 100644
--- a/sound/core/seq/seq.c
+++ b/sound/core/seq/seq.c
@@ -86,7 +86,6 @@ static int __init alsa_seq_init(void)
{
int err;
- snd_seq_autoload_lock();
if ((err = client_init_data()) < 0)
goto error;
@@ -110,8 +109,8 @@ static int __init alsa_seq_init(void)
if ((err = snd_seq_system_client_init()) < 0)
goto error;
+ snd_seq_autoload_init();
error:
- snd_seq_autoload_unlock();
return err;
}
@@ -131,6 +130,8 @@ static void __exit alsa_seq_exit(void)
/* release event memory */
snd_sequencer_memory_done();
+
+ snd_seq_autoload_exit();
}
module_init(alsa_seq_init)
diff --git a/sound/core/seq/seq_device.c b/sound/core/seq/seq_device.c
index 91a786a783e1..0631bdadd12b 100644
--- a/sound/core/seq/seq_device.c
+++ b/sound/core/seq/seq_device.c
@@ -56,6 +56,7 @@ MODULE_LICENSE("GPL");
#define DRIVER_LOADED (1<<0)
#define DRIVER_REQUESTED (1<<1)
#define DRIVER_LOCKED (1<<2)
+#define DRIVER_REQUESTING (1<<3)
struct ops_list {
char id[ID_LEN]; /* driver id */
@@ -127,42 +128,82 @@ static void snd_seq_device_info(struct snd_info_entry *entry,
#ifdef CONFIG_MODULES
/* avoid auto-loading during module_init() */
-static int snd_seq_in_init;
+static atomic_t snd_seq_in_init = ATOMIC_INIT(1); /* blocked as default */
void snd_seq_autoload_lock(void)
{
- snd_seq_in_init++;
+ atomic_inc(&snd_seq_in_init);
}
void snd_seq_autoload_unlock(void)
{
- snd_seq_in_init--;
+ atomic_dec(&snd_seq_in_init);
}
-#endif
-void snd_seq_device_load_drivers(void)
+static void autoload_drivers(void)
{
-#ifdef CONFIG_MODULES
- struct ops_list *ops;
+ /* avoid reentrance */
+ if (atomic_inc_return(&snd_seq_in_init) == 1) {
+ struct ops_list *ops;
+
+ mutex_lock(&ops_mutex);
+ list_for_each_entry(ops, &opslist, list) {
+ if ((ops->driver & DRIVER_REQUESTING) &&
+ !(ops->driver & DRIVER_REQUESTED)) {
+ ops->used++;
+ mutex_unlock(&ops_mutex);
+ ops->driver |= DRIVER_REQUESTED;
+ request_module("snd-%s", ops->id);
+ mutex_lock(&ops_mutex);
+ ops->used--;
+ }
+ }
+ mutex_unlock(&ops_mutex);
+ }
+ atomic_dec(&snd_seq_in_init);
+}
- /* Calling request_module during module_init()
- * may cause blocking.
- */
- if (snd_seq_in_init)
- return;
+static void call_autoload(struct work_struct *work)
+{
+ autoload_drivers();
+}
- mutex_lock(&ops_mutex);
- list_for_each_entry(ops, &opslist, list) {
- if (! (ops->driver & DRIVER_LOADED) &&
- ! (ops->driver & DRIVER_REQUESTED)) {
- ops->used++;
- mutex_unlock(&ops_mutex);
- ops->driver |= DRIVER_REQUESTED;
- request_module("snd-%s", ops->id);
- mutex_lock(&ops_mutex);
- ops->used--;
- }
+static DECLARE_WORK(autoload_work, call_autoload);
+
+static void try_autoload(struct ops_list *ops)
+{
+ if (!ops->driver) {
+ ops->driver |= DRIVER_REQUESTING;
+ schedule_work(&autoload_work);
}
+}
+
+static void queue_autoload_drivers(void)
+{
+ struct ops_list *ops;
+
+ mutex_lock(&ops_mutex);
+ list_for_each_entry(ops, &opslist, list)
+ try_autoload(ops);
mutex_unlock(&ops_mutex);
+}
+
+void snd_seq_autoload_init(void)
+{
+ atomic_dec(&snd_seq_in_init);
+#ifdef CONFIG_SND_SEQUENCER_MODULE
+ /* initial autoload only when snd-seq is a module */
+ queue_autoload_drivers();
+#endif
+}
+#else
+#define try_autoload(ops) /* NOP */
+#endif
+
+void snd_seq_device_load_drivers(void)
+{
+#ifdef CONFIG_MODULES
+ queue_autoload_drivers();
+ flush_work(&autoload_work);
#endif
}
@@ -214,13 +255,14 @@ int snd_seq_device_new(struct snd_card *card, int device, char *id, int argsize,
ops->num_devices++;
mutex_unlock(&ops->reg_mutex);
- unlock_driver(ops);
-
if ((err = snd_device_new(card, SNDRV_DEV_SEQUENCER, dev, &dops)) < 0) {
snd_seq_device_free(dev);
return err;
}
+ try_autoload(ops);
+ unlock_driver(ops);
+
if (result)
*result = dev;
@@ -318,16 +360,12 @@ int snd_seq_device_register_driver(char *id, struct snd_seq_dev_ops *entry,
entry->init_device == NULL || entry->free_device == NULL)
return -EINVAL;
- snd_seq_autoload_lock();
ops = find_driver(id, 1);
- if (ops == NULL) {
- snd_seq_autoload_unlock();
+ if (ops == NULL)
return -ENOMEM;
- }
if (ops->driver & DRIVER_LOADED) {
pr_warn("ALSA: seq: driver_register: driver '%s' already exists\n", id);
unlock_driver(ops);
- snd_seq_autoload_unlock();
return -EBUSY;
}
@@ -344,7 +382,6 @@ int snd_seq_device_register_driver(char *id, struct snd_seq_dev_ops *entry,
mutex_unlock(&ops->reg_mutex);
unlock_driver(ops);
- snd_seq_autoload_unlock();
return 0;
}
@@ -554,6 +591,9 @@ static int __init alsa_seq_device_init(void)
static void __exit alsa_seq_device_exit(void)
{
+#ifdef CONFIG_MODULES
+ cancel_work_sync(&autoload_work);
+#endif
remove_drivers();
#ifdef CONFIG_PROC_FS
snd_info_free_entry(info_entry);
@@ -570,6 +610,7 @@ EXPORT_SYMBOL(snd_seq_device_new);
EXPORT_SYMBOL(snd_seq_device_register_driver);
EXPORT_SYMBOL(snd_seq_device_unregister_driver);
#ifdef CONFIG_MODULES
+EXPORT_SYMBOL(snd_seq_autoload_init);
EXPORT_SYMBOL(snd_seq_autoload_lock);
EXPORT_SYMBOL(snd_seq_autoload_unlock);
#endif
diff --git a/sound/core/sgbuf.c b/sound/core/sgbuf.c
index 0a418503ec41..84fffabdd129 100644
--- a/sound/core/sgbuf.c
+++ b/sound/core/sgbuf.c
@@ -39,8 +39,7 @@ int snd_free_sgbuf_pages(struct snd_dma_buffer *dmab)
if (! sgbuf)
return -EINVAL;
- if (dmab->area)
- vunmap(dmab->area);
+ vunmap(dmab->area);
dmab->area = NULL;
tmpb.dev.type = SNDRV_DMA_TYPE_DEV;
diff --git a/sound/core/sound.c b/sound/core/sound.c
index 38ad1a0dd3f7..f1333060bf1c 100644
--- a/sound/core/sound.c
+++ b/sound/core/sound.c
@@ -355,8 +355,13 @@ int snd_unregister_device(int type, struct snd_card *card, int dev)
EXPORT_SYMBOL(snd_unregister_device);
-/* get the assigned device to the given type and device number;
- * the caller needs to release it via put_device() after using it
+/**
+ * snd_get_device - get the assigned device to the given type and device number
+ * @type: the device type, SNDRV_DEVICE_TYPE_XXX
+ * @card:the card instance
+ * @dev: the device index
+ *
+ * The caller needs to release it via put_device() after using it.
*/
struct device *snd_get_device(int type, struct snd_card *card, int dev)
{