On Tue, 15 Jul 2003, Carlo Wood wrote:

> Is someone looking into my report please?
> 
> I did include a compilable test case, so it shouldn't
> be too hard to reproduce this :/

Well, in case of urgent help we have something called as SuSE 
professional services. You can eventually buy our time, otherwise
you will have to wait for our free timeslot (if any).

I looked to the problem and it seems that the overrun state is handled 
differently in the OSS API than ALSA implemented. It seems that the stream 
is not stopped but rather the oldest period (fragment) is discarded.

The new code (plus some optimization) is in the ALSA CVS tree. The patch 
is attached to this e-mail.

                                                Jaroslav

-----
Jaroslav Kysela <[EMAIL PROTECTED]>
Linux Kernel Sound Maintainer
ALSA Project, SuSE Labs

Index: pcm_oss.c
===================================================================
RCS file: /cvsroot/alsa/alsa-kernel/core/oss/pcm_oss.c,v
retrieving revision 1.39
retrieving revision 1.41
diff -u -r1.39 -r1.41
--- pcm_oss.c   12 Jul 2003 19:21:16 -0000      1.39
+++ pcm_oss.c   15 Jul 2003 18:13:34 -0000      1.41
@@ -445,7 +445,7 @@
        } else {
                sw_params->start_threshold = runtime->boundary;
        }
-       if (atomic_read(&runtime->mmap_count))
+       if (atomic_read(&runtime->mmap_count) || substream->stream == 
SNDRV_PCM_STREAM_CAPTURE)
                sw_params->stop_threshold = runtime->boundary;
        else
                sw_params->stop_threshold = runtime->buffer_size;
@@ -570,6 +570,31 @@
        return 0;
 }
 
+static int snd_pcm_oss_capture_position_fixup(snd_pcm_substream_t *substream, 
snd_pcm_sframes_t *delay)
+{
+       snd_pcm_runtime_t *runtime;
+       snd_pcm_uframes_t frames;
+       int err = 0;
+
+       while (1) {
+               err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DELAY, delay);
+               if (err < 0)
+                       break;
+               runtime = substream->runtime;
+               if (*delay <= runtime->buffer_size)
+                       break;
+               /* in case of overrun, skip whole periods like OSS/Linux driver does */
+               /* until avail(delay) <= buffer_size */
+               frames = (*delay - runtime->buffer_size) + runtime->period_size - 1;
+               frames /= runtime->period_size;
+               frames *= runtime->period_size;
+               err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_FORWARD, 
&frames);
+               if (err < 0)
+                       break;
+       }
+       return err;
+}
+
 snd_pcm_sframes_t snd_pcm_oss_write3(snd_pcm_substream_t *substream, const char *ptr, 
snd_pcm_uframes_t frames, int in_kernel)
 {
        snd_pcm_runtime_t *runtime = substream->runtime;
@@ -608,6 +633,7 @@
 snd_pcm_sframes_t snd_pcm_oss_read3(snd_pcm_substream_t *substream, char *ptr, 
snd_pcm_uframes_t frames, int in_kernel)
 {
        snd_pcm_runtime_t *runtime = substream->runtime;
+       snd_pcm_sframes_t delay;
        int ret;
        while (1) {
                if (runtime->status->state == SNDRV_PCM_STATE_XRUN ||
@@ -626,6 +652,9 @@
                        if (ret < 0)
                                break;
                }
+               ret = snd_pcm_oss_capture_position_fixup(substream, &delay);
+               if (ret < 0)
+                       break;
                if (in_kernel) {
                        mm_segment_t fs;
                        fs = snd_enter_user();
@@ -1335,7 +1364,7 @@
 {      
        snd_pcm_substream_t *substream;
        snd_pcm_runtime_t *runtime;
-       snd_pcm_status_t status;
+       snd_pcm_sframes_t delay;
        struct count_info info;
        int err;
 
@@ -1353,14 +1382,17 @@
                        return -EFAULT;
                return 0;
        }
-       memset(&status, 0, sizeof(status));
-       err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_STATUS, &status);
+       if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DELAY, &delay);
+       } else {
+               err = snd_pcm_oss_capture_position_fixup(substream, &delay);
+       }
        if (err < 0)
                return err;
        if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
-               info.bytes = runtime->oss.bytes - snd_pcm_oss_bytes(substream, 
runtime->buffer_size - status.avail);
+               info.bytes = runtime->oss.bytes - snd_pcm_oss_bytes(substream, delay);
        } else {
-               info.bytes = runtime->oss.bytes + snd_pcm_oss_bytes(substream, 
status.avail);
+               info.bytes = runtime->oss.bytes + snd_pcm_oss_bytes(substream, delay);
        }
        info.ptr = snd_pcm_oss_bytes(substream, runtime->status->hw_ptr % 
runtime->buffer_size);
        if (atomic_read(&runtime->mmap_count)) {
@@ -1373,10 +1405,7 @@
                if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
                        snd_pcm_oss_simulate_fill(substream);
        } else {
-               if (stream == SNDRV_PCM_STREAM_PLAYBACK)
-                       info.blocks = (runtime->buffer_size - status.avail) / 
runtime->period_size;
-               else
-                       info.blocks = status.avail / runtime->period_size;
+               info.blocks = delay / runtime->period_size;
        }
        if (copy_to_user(_info, &info, sizeof(info)))
                return -EFAULT;
@@ -1387,7 +1416,7 @@
 {
        snd_pcm_substream_t *substream;
        snd_pcm_runtime_t *runtime;
-       snd_pcm_status_t status;
+       snd_pcm_sframes_t avail;
        struct audio_buf_info info;
        int err;
 
@@ -1404,7 +1433,6 @@
 
        info.fragsize = runtime->oss.period_bytes;
        info.fragstotal = runtime->periods;
-       memset(&status, 0, sizeof(status));
        if (runtime->oss.prepare) {
                if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
                        info.bytes = runtime->oss.period_bytes * runtime->periods;
@@ -1414,20 +1442,18 @@
                        info.fragments = 0;
                }
        } else {
-               err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_STATUS, &status);
+               if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+                       err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DELAY, 
&avail);
+                       avail = runtime->buffer_size - avail;
+               } else {
+                       err = snd_pcm_oss_capture_position_fixup(substream, &avail);
+               }
                if (err < 0)
                        return err;
-               info.bytes = snd_pcm_oss_bytes(substream, status.avail);
-               info.fragments = status.avail / runtime->period_size;
+               info.bytes = snd_pcm_oss_bytes(substream, avail);
+               info.fragments = avail / runtime->period_size;
        }
 
-#if 0
-       /* very experimental stuff to get Quake2 working */
-       runtime->oss.period = (info.periods - 1) << 16;
-       for (tmp = info.fragsize; tmp > 1; tmp >>= 1)
-               runtime->oss.period++;
-       runtime->oss.subdivision = 1;   /* disable SUBDIVIDE */
-#endif
 #ifdef OSS_DEBUG
        printk("pcm_oss: space: bytes = %i, fragments = %i, fragstotal = %i, fragsize 
= %i\n", info.bytes, info.fragments, info.fragstotal, info.fragsize);
 #endif

Reply via email to