ALSA: pcm midlevel code - add time check for double interrupt acknowledge
authorJaroslav Kysela <perex@perex.cz>
Wed, 18 Aug 2010 12:16:54 +0000 (14:16 +0200)
committerJaroslav Kysela <perex@perex.cz>
Thu, 19 Aug 2010 07:15:24 +0000 (09:15 +0200)
The current code in pcm_lib.c do all checks using only the position
in the ring buffer. Unfortunately, where the interrupts gets delayed or
merged into one, we need another timing source to check when the
buffer size boundary overlaps to avoid the wrong updating of the
ring buffer pointers.

This code uses jiffies to check the right time window without any
performance impact.

Signed-off-by: Jaroslav Kysela <perex@perex.cz>
include/sound/pcm.h
sound/core/pcm_lib.c
sound/core/pcm_native.c

index dd76cdede64dce39cb03cf6fc2bff105315fece3..54c4ccf6fec25bd2ff90e947cfc49e1c029b9cfa 100644 (file)
@@ -274,6 +274,7 @@ struct snd_pcm_runtime {
        snd_pcm_uframes_t hw_ptr_base;  /* Position at buffer restart */
        snd_pcm_uframes_t hw_ptr_interrupt; /* Position at interrupt time */
        unsigned long hw_ptr_jiffies;   /* Time when hw_ptr is updated */
+       unsigned long hw_ptr_buffer_jiffies; /* buffer time in jiffies */
        snd_pcm_sframes_t delay;        /* extra delay; typically FIFO size */
 
        /* -- HW params -- */
index e9d98be190c58db786c53ea8adc9770bd808d5ec..d6ecca27bb68e4fb49766630f546291c92ee2357 100644 (file)
@@ -329,11 +329,15 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
                /* delta = "expected next hw_ptr" for in_interrupt != 0 */
                delta = runtime->hw_ptr_interrupt + runtime->period_size;
                if (delta > new_hw_ptr) {
-                       hw_base += runtime->buffer_size;
-                       if (hw_base >= runtime->boundary)
-                               hw_base = 0;
-                       new_hw_ptr = hw_base + pos;
-                       goto __delta;
+                       /* check for double acknowledged interrupts */
+                       hdelta = jiffies - runtime->hw_ptr_jiffies;
+                       if (hdelta > runtime->hw_ptr_buffer_jiffies/2) {
+                               hw_base += runtime->buffer_size;
+                               if (hw_base >= runtime->boundary)
+                                       hw_base = 0;
+                               new_hw_ptr = hw_base + pos;
+                               goto __delta;
+                       }
                }
        }
        /* new_hw_ptr might be lower than old_hw_ptr in case when */
index 303ac04ff6e427066bf95fe0932361efd7a94ea3..2d2e1b65ee9aec232a9870758729f8069a900e2b 100644 (file)
@@ -867,6 +867,8 @@ static void snd_pcm_post_start(struct snd_pcm_substream *substream, int state)
        struct snd_pcm_runtime *runtime = substream->runtime;
        snd_pcm_trigger_tstamp(substream);
        runtime->hw_ptr_jiffies = jiffies;
+       runtime->hw_ptr_buffer_jiffies = (runtime->buffer_size * HZ) / 
+                                                           runtime->rate;
        runtime->status->state = state;
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
            runtime->silence_size > 0)