ALSA: pcm_lib - return back hw_ptr_interrupt
authorJaroslav Kysela <perex@perex.cz>
Tue, 26 Jan 2010 16:08:24 +0000 (17:08 +0100)
committerJaroslav Kysela <perex@perex.cz>
Tue, 26 Jan 2010 16:50:50 +0000 (17:50 +0100)
Clemens Ladisch noted for hw_ptr_removal in "cleanup & merge hw_ptr
update functions" commit:

"It is possible for the status/delay ioctls to be called when the sound
card's pointer register alreay shows a position at the beginning of the
new period, but immediately before the interrupt is actually executed.
(This happens regularly on a SMP machine with mplayer.)  When that
happens, the code thinks that the position must be at least one period
ahead of the current position and drops an entire buffer of data."

Return back the hw_ptr_interrupt variable. The last interrupt pointer
is always computed from the latest hw_ptr instead of tracking it
separately (in this case all hw_ptr checks and modifications might
influence also hw_ptr_interrupt and it is difficult to keep it
consistent).

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

index 3bc9bca771ece4e24714d0481f63360fd52d009f..13bc83ca35fb1230c53550b82c388ca0aa5f7720 100644 (file)
@@ -271,6 +271,7 @@ struct snd_pcm_runtime {
        int overrange;
        snd_pcm_uframes_t avail_max;
        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 */
        snd_pcm_sframes_t delay;        /* extra delay; typically FIFO size */
 
index 255ad910077ae906cb4427a120f24ca92d0e9a32..82d4e3329b3daa11c956e631bab2fb65a59f5d58 100644 (file)
@@ -635,8 +635,7 @@ static long snd_pcm_alsa_frames(struct snd_pcm_substream *substream, long bytes)
 static inline
 snd_pcm_uframes_t get_hw_ptr_period(struct snd_pcm_runtime *runtime)
 {
-       snd_pcm_uframes_t ptr = runtime->status->hw_ptr;
-       return ptr - (ptr % runtime->period_size);
+       return runtime->hw_ptr_interrupt;
 }
 
 /* define extended formats in the recent OSS versions (if any) */
index e2a817eac2a9a3a7d4087599a8e6c147120a8a3d..aa54195ef3b005595b151e277dd71cb7d580f36c 100644 (file)
@@ -325,8 +325,7 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
        if (in_interrupt) {
                /* we know that one period was processed */
                /* delta = "expected next hw_ptr" for in_interrupt != 0 */
-               delta = old_hw_ptr - (old_hw_ptr % runtime->period_size)
-                       + runtime->period_size;
+               delta = runtime->hw_ptr_interrupt + runtime->period_size;
                if (delta > new_hw_ptr) {
                        hw_base += runtime->buffer_size;
                        if (hw_base >= runtime->boundary)
@@ -437,6 +436,10 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
            runtime->silence_size > 0)
                snd_pcm_playback_silence(substream, new_hw_ptr);
 
+       if (in_interrupt) {
+               runtime->hw_ptr_interrupt = new_hw_ptr -
+                               (new_hw_ptr % runtime->period_size);
+       }
        runtime->hw_ptr_base = hw_base;
        runtime->status->hw_ptr = new_hw_ptr;
        runtime->hw_ptr_jiffies = jiffies;
index 56ec35e8510b1fdf296c260245820ca1c89a1189..7a002db512b4faf5120feb79cfc58addae49da74 100644 (file)
@@ -1252,6 +1252,8 @@ static int snd_pcm_do_reset(struct snd_pcm_substream *substream, int state)
        if (err < 0)
                return err;
        runtime->hw_ptr_base = 0;
+       runtime->hw_ptr_interrupt = runtime->status->hw_ptr -
+               runtime->status->hw_ptr % runtime->period_size;
        runtime->silence_start = runtime->status->hw_ptr;
        runtime->silence_filled = 0;
        return 0;