From ddd9c29a0dd1db1860d951931768ce3a333b5993 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 23 May 2005 08:51:59 +0000 Subject: [PATCH] Fix dshare plugin codes Modify dshare plugin codes to follow the recent change of dmix: proper XRUN and poll handling, async data transfer. --- src/pcm/pcm_dshare.c | 188 +++++++++++++++++++++++++++++-------------- 1 file changed, 128 insertions(+), 60 deletions(-) diff --git a/src/pcm/pcm_dshare.c b/src/pcm/pcm_dshare.c index c1181f95..097e2ba6 100644 --- a/src/pcm/pcm_dshare.c +++ b/src/pcm/pcm_dshare.c @@ -49,6 +49,9 @@ const char *_snd_module_pcm_dshare = ""; #endif +/* start is pending - this state happens when rate plugin does a delayed commit */ +#define STATE_RUN_PENDING 1024 + static void do_silence(snd_pcm_t *pcm) { snd_pcm_direct_t *dshare = pcm->private_data; @@ -94,30 +97,51 @@ static void share_areas(snd_pcm_direct_t *dshare, /* * synchronize shm ring buffer with hardware */ -static void snd_pcm_dshare_sync_area(snd_pcm_t *pcm, snd_pcm_uframes_t size) +static void snd_pcm_dshare_sync_area(snd_pcm_t *pcm) { snd_pcm_direct_t *dshare = pcm->private_data; - snd_pcm_uframes_t appl_ptr, slave_appl_ptr, transfer; + snd_pcm_uframes_t appl_ptr, slave_appl_ptr, slave_bsize; + snd_pcm_uframes_t size, slave_hw_ptr; const snd_pcm_channel_area_t *src_areas, *dst_areas; - /* get the start of update area */ - appl_ptr = dshare->appl_ptr - size; - if (appl_ptr > pcm->boundary) - appl_ptr += pcm->boundary; - appl_ptr %= pcm->buffer_size; + /* calculate the size to transfer */ + size = dshare->appl_ptr - dshare->last_appl_ptr; + if (! size) + return; + slave_bsize = dshare->shmptr->s.buffer_size; + slave_hw_ptr = dshare->slave_hw_ptr; + /* don't write on the last active period - this area may be cleared + * by the driver during write operation... + */ + slave_hw_ptr -= slave_hw_ptr % dshare->shmptr->s.period_size; + slave_hw_ptr += slave_bsize; + if (dshare->slave_hw_ptr > dshare->slave_appl_ptr) + slave_hw_ptr -= dshare->shmptr->s.boundary; + if (dshare->slave_appl_ptr + size >= slave_hw_ptr) + size = slave_hw_ptr - dshare->slave_appl_ptr; + if (! size) + return; /* add sample areas here */ src_areas = snd_pcm_mmap_areas(pcm); dst_areas = snd_pcm_mmap_areas(dshare->spcm); - slave_appl_ptr = dshare->slave_appl_ptr % dshare->shmptr->s.buffer_size; + appl_ptr = dshare->last_appl_ptr % pcm->buffer_size; + dshare->last_appl_ptr += size; + dshare->last_appl_ptr %= pcm->boundary; + slave_appl_ptr = dshare->slave_appl_ptr % slave_bsize; dshare->slave_appl_ptr += size; dshare->slave_appl_ptr %= dshare->shmptr->s.boundary; - while (size > 0) { - transfer = appl_ptr + size > pcm->buffer_size ? pcm->buffer_size - appl_ptr : size; - transfer = slave_appl_ptr + transfer > dshare->shmptr->s.buffer_size ? dshare->shmptr->s.buffer_size - slave_appl_ptr : transfer; - size -= transfer; + for (;;) { + snd_pcm_uframes_t transfer = size; + if (appl_ptr + transfer > pcm->buffer_size) + transfer = pcm->buffer_size - appl_ptr; + if (slave_appl_ptr + transfer > slave_bsize) + transfer = slave_bsize - slave_appl_ptr; share_areas(dshare, src_areas, dst_areas, appl_ptr, slave_appl_ptr, transfer); + size -= transfer; + if (! size) + break; slave_appl_ptr += transfer; - slave_appl_ptr %= dshare->shmptr->s.buffer_size; + slave_appl_ptr %= slave_bsize; appl_ptr += transfer; appl_ptr %= pcm->buffer_size; } @@ -146,6 +170,10 @@ static int snd_pcm_dshare_sync_ptr(snd_pcm_t *pcm) diff = slave_hw_ptr - old_slave_hw_ptr; if (diff == 0) /* fast path */ return 0; + if (dshare->state != SND_PCM_STATE_RUNNING && + dshare->state != SND_PCM_STATE_DRAINING) + /* not really started yet - don't update hw_ptr */ + return 0; if (diff < 0) { slave_hw_ptr += dshare->shmptr->s.boundary; diff = slave_hw_ptr - old_slave_hw_ptr; @@ -155,17 +183,23 @@ static int snd_pcm_dshare_sync_ptr(snd_pcm_t *pcm) // printf("sync ptr diff = %li\n", diff); if (pcm->stop_threshold >= pcm->boundary) /* don't care */ return 0; - if ((avail = snd_pcm_mmap_playback_avail(pcm)) >= pcm->stop_threshold) { + avail = snd_pcm_mmap_playback_avail(pcm); + if (avail > dshare->avail_max) + dshare->avail_max = avail; + if (avail >= pcm->stop_threshold) { struct timeval tv; + snd_timer_stop(dshare->timer); gettimeofday(&tv, 0); dshare->trigger_tstamp.tv_sec = tv.tv_sec; dshare->trigger_tstamp.tv_nsec = tv.tv_usec * 1000L; - dshare->state = SND_PCM_STATE_XRUN; - dshare->avail_max = avail; - return -EPIPE; + if (dshare->state == SND_PCM_STATE_RUNNING) { + dshare->state = SND_PCM_STATE_XRUN; + return -EPIPE; + } + dshare->state = SND_PCM_STATE_SETUP; + /* clear queue to remove pending poll events */ + snd_pcm_direct_clear_timer_queue(dshare); } - if (avail > dshare->avail_max) - dshare->avail_max = avail; return 0; } @@ -176,7 +210,6 @@ static int snd_pcm_dshare_sync_ptr(snd_pcm_t *pcm) static int snd_pcm_dshare_status(snd_pcm_t *pcm, snd_pcm_status_t * status) { snd_pcm_direct_t *dshare = pcm->private_data; - snd_pcm_state_t state; switch (dshare->state) { case SNDRV_PCM_STATE_DRAINING: @@ -187,8 +220,7 @@ static int snd_pcm_dshare_status(snd_pcm_t *pcm, snd_pcm_status_t * status) break; } memset(status, 0, sizeof(*status)); - state = snd_pcm_state(dshare->spcm); - status->state = state == SND_PCM_STATE_RUNNING ? dshare->state : state; + status->state = snd_pcm_state(dshare->spcm); status->trigger_tstamp = dshare->trigger_tstamp; status->tstamp = snd_pcm_hw_fast_tstamp(dshare->spcm); status->avail = snd_pcm_mmap_playback_avail(pcm); @@ -204,11 +236,12 @@ static snd_pcm_state_t snd_pcm_dshare_state(snd_pcm_t *pcm) case SND_PCM_STATE_SUSPENDED: return SND_PCM_STATE_SUSPENDED; case SND_PCM_STATE_DISCONNECTED: - dshare->state = SNDRV_PCM_STATE_DISCONNECTED; - return -ENOTTY; + return SND_PCM_STATE_DISCONNECTED; default: break; } + if (dshare->state == STATE_RUN_PENDING) + return SNDRV_PCM_STATE_RUNNING; return dshare->state; } @@ -223,8 +256,10 @@ static int snd_pcm_dshare_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delayp) err = snd_pcm_dshare_sync_ptr(pcm); if (err < 0) return err; + /* fallthru */ case SNDRV_PCM_STATE_PREPARED: case SNDRV_PCM_STATE_SUSPENDED: + case STATE_RUN_PENDING: *delayp = snd_pcm_mmap_playback_hw_avail(pcm); return 0; case SNDRV_PCM_STATE_XRUN: @@ -263,7 +298,7 @@ static int snd_pcm_dshare_prepare(snd_pcm_t *pcm) snd_pcm_direct_check_interleave(dshare, pcm); // assert(pcm->boundary == dshare->shmptr->s.boundary); /* for sure */ dshare->state = SND_PCM_STATE_PREPARED; - dshare->appl_ptr = 0; + dshare->appl_ptr = dshare->last_appl_ptr = 0; dshare->hw_ptr = 0; return snd_pcm_direct_set_timer_params(dshare); } @@ -272,11 +307,24 @@ static int snd_pcm_dshare_reset(snd_pcm_t *pcm) { snd_pcm_direct_t *dshare = pcm->private_data; dshare->hw_ptr %= pcm->period_size; - dshare->appl_ptr = dshare->hw_ptr; + dshare->appl_ptr = dshare->last_appl_ptr = dshare->hw_ptr; dshare->slave_appl_ptr = dshare->slave_hw_ptr = *dshare->spcm->hw.ptr; return 0; } +static int snd_pcm_dshare_start_timer(snd_pcm_direct_t *dshare) +{ + int err; + + snd_pcm_hwsync(dshare->spcm); + dshare->slave_appl_ptr = dshare->slave_hw_ptr = *dshare->spcm->hw.ptr; + err = snd_timer_start(dshare->timer); + if (err < 0) + return err; + dshare->state = SND_PCM_STATE_RUNNING; + return 0; +} + static int snd_pcm_dshare_start(snd_pcm_t *pcm) { snd_pcm_direct_t *dshare = pcm->private_data; @@ -286,18 +334,16 @@ static int snd_pcm_dshare_start(snd_pcm_t *pcm) if (dshare->state != SND_PCM_STATE_PREPARED) return -EBADFD; - snd_pcm_hwsync(dshare->spcm); - dshare->slave_appl_ptr = dshare->slave_hw_ptr = *dshare->spcm->hw.ptr; - err = snd_timer_start(dshare->timer); - if (err < 0) - return err; - dshare->state = SND_PCM_STATE_RUNNING; avail = snd_pcm_mmap_playback_hw_avail(pcm); - if (avail < 0) + if (avail == 0) + dshare->state = STATE_RUN_PENDING; + else if (avail < 0) return 0; - if (avail > (snd_pcm_sframes_t)pcm->buffer_size) - avail = pcm->buffer_size; - snd_pcm_dshare_sync_area(pcm, avail); + else { + if ((err = snd_pcm_dshare_start_timer(dshare)) < 0) + return err; + snd_pcm_dshare_sync_area(pcm); + } gettimeofday(&tv, 0); dshare->trigger_tstamp.tv_sec = tv.tv_sec; dshare->trigger_tstamp.tv_nsec = tv.tv_usec * 1000L; @@ -309,7 +355,7 @@ static int snd_pcm_dshare_drop(snd_pcm_t *pcm) snd_pcm_direct_t *dshare = pcm->private_data; if (dshare->state == SND_PCM_STATE_OPEN) return -EBADFD; - snd_timer_stop(dshare->timer); + snd_pcm_direct_timer_stop(dshare); do_silence(pcm); dshare->state = SND_PCM_STATE_SETUP; return 0; @@ -323,22 +369,34 @@ static int snd_pcm_dshare_drain(snd_pcm_t *pcm) if (dshare->state == SND_PCM_STATE_OPEN) return -EBADFD; + if (pcm->mode & SND_PCM_NONBLOCK) + return -EAGAIN; + if (dshare->state == SND_PCM_STATE_PREPARED) { + if (snd_pcm_mmap_playback_hw_avail(pcm) > 0) + snd_pcm_dshare_start(pcm); + else { + snd_pcm_dshare_drop(pcm); + return 0; + } + } stop_threshold = pcm->stop_threshold; if (pcm->stop_threshold > pcm->buffer_size) pcm->stop_threshold = pcm->buffer_size; - if (dshare->state == SND_PCM_STATE_PREPARED && - snd_pcm_mmap_playback_hw_avail(pcm) > 0) - snd_pcm_dshare_start(pcm); - while (dshare->state == SND_PCM_STATE_RUNNING) { + dshare->state = SND_PCM_STATE_DRAINING; + do { err = snd_pcm_dshare_sync_ptr(pcm); - if (err < 0) + if (err < 0) { + snd_pcm_dshare_drop(pcm); break; - if (pcm->mode & SND_PCM_NONBLOCK) - return -EAGAIN; - snd_pcm_wait(pcm, -1); - } + } + if (dshare->state == SND_PCM_STATE_DRAINING) { + snd_pcm_dshare_sync_area(pcm); + snd_pcm_wait_nocheck(pcm, -1); + snd_pcm_direct_clear_timer_queue(dshare); /* force poll to wait */ + } + } while (dshare->state == SND_PCM_STATE_DRAINING); pcm->stop_threshold = stop_threshold; - return snd_pcm_dshare_drop(pcm); + return 0; } static int snd_pcm_dshare_pause(snd_pcm_t *pcm ATTRIBUTE_UNUSED, int enable ATTRIBUTE_UNUSED) @@ -346,18 +404,22 @@ static int snd_pcm_dshare_pause(snd_pcm_t *pcm ATTRIBUTE_UNUSED, int enable ATTR return -EIO; } -static snd_pcm_sframes_t snd_pcm_dshare_rewind(snd_pcm_t *pcm, snd_pcm_uframes_t frames) +static snd_pcm_sframes_t snd_pcm_dshare_rewind(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_uframes_t frames ATTRIBUTE_UNUSED) { +#if 0 /* FIXME: substract samples from the mix ring buffer, too? */ snd_pcm_mmap_appl_backward(pcm, frames); return frames; +#else + return -EIO; +#endif } static snd_pcm_sframes_t snd_pcm_dshare_forward(snd_pcm_t *pcm, snd_pcm_uframes_t frames) { snd_pcm_sframes_t avail; - avail = snd_pcm_mmap_avail(pcm); + avail = snd_pcm_mmap_playback_avail(pcm); if (avail < 0) return 0; if (frames > (snd_pcm_uframes_t)avail) @@ -425,28 +487,34 @@ static snd_pcm_sframes_t snd_pcm_dshare_mmap_commit(snd_pcm_t *pcm, default: break; } + if (! size) + return 0; snd_pcm_mmap_appl_forward(pcm, size); - if (dshare->state == SND_PCM_STATE_RUNNING) { - err = snd_pcm_dshare_sync_ptr(pcm); - if (err < 0) + if (dshare->state == STATE_RUN_PENDING) { + if ((err = snd_pcm_dshare_start_timer(dshare)) < 0) return err; + } else if (dshare->state == SND_PCM_STATE_RUNNING || + dshare->state == SND_PCM_STATE_DRAINING) + snd_pcm_dshare_sync_ptr(pcm); + if (dshare->state == SND_PCM_STATE_RUNNING || + dshare->state == SND_PCM_STATE_DRAINING) { /* ok, we commit the changes after the validation of area */ /* it's intended, although the result might be crappy */ - snd_pcm_dshare_sync_area(pcm, size); + snd_pcm_dshare_sync_area(pcm); + /* clear timer queue to avoid a bogus return from poll */ + if (snd_pcm_mmap_playback_avail(pcm) < pcm->avail_min) + snd_pcm_direct_clear_timer_queue(dshare); } return size; } -static snd_pcm_sframes_t snd_pcm_dshare_avail_update(snd_pcm_t *pcm ATTRIBUTE_UNUSED) +static snd_pcm_sframes_t snd_pcm_dshare_avail_update(snd_pcm_t *pcm) { snd_pcm_direct_t *dshare = pcm->private_data; - int err; - if (dshare->state == SND_PCM_STATE_RUNNING) { - err = snd_pcm_dshare_sync_ptr(pcm); - if (err < 0) - return err; - } + if (dshare->state == SND_PCM_STATE_RUNNING || + dshare->state == SND_PCM_STATE_DRAINING) + snd_pcm_dshare_sync_ptr(pcm); return snd_pcm_mmap_playback_avail(pcm); } -- 2.47.1