drain和drop

1 snd_pcm_drain

snd_pcm_dain并没有直接call snd_pcm_stop停掉HW

static int snd_pcm_drain(struct snd_pcm_substream *substream,
             struct file *file)
{
    struct snd_card *card;
    struct snd_pcm_runtime *runtime;
    struct snd_pcm_substream *s;
    struct snd_pcm_group *group;
    wait_queue_entry_t wait;
    int result = 0;
    int nonblock = 0;

    card = substream->pcm->card;
    runtime = substream->runtime;

    if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
        return -EBADFD;

    if (file) {
        if (file->f_flags & O_NONBLOCK)
            nonblock = 1;
    } else if (substream->f_flags & O_NONBLOCK)
        nonblock = 1;

    snd_pcm_stream_lock_irq(substream);
    /* resume pause */
    if (runtime->status->state == SNDRV_PCM_STATE_PAUSED)
        snd_pcm_pause(substream, false);

    /* pre-start/stop - all running streams are changed to DRAINING state */
    result = snd_pcm_action(&snd_pcm_action_drain_init, substream,
                ACTION_ARG_IGNORE);
    if (result < 0)
        goto unlock;
    /* in non-blocking, we don't wait in ioctl but let caller poll */
    if (nonblock) {
        result = -EAGAIN;
        goto unlock;
    }

    for (;;) {
        long tout;
        struct snd_pcm_runtime *to_check;
        if (signal_pending(current)) {
            result = -ERESTARTSYS;
            break;
        }
        /* find a substream to drain */
        to_check = NULL;
        group = snd_pcm_stream_group_ref(substream);
        snd_pcm_group_for_each_entry(s, substream) {
            if (s->stream != SNDRV_PCM_STREAM_PLAYBACK)
                continue;
            runtime = s->runtime;
            if (runtime->status->state == SNDRV_PCM_STATE_DRAINING) {
                to_check = runtime;
                break;
            }
        }
        snd_pcm_group_unref(group, substream);
        if (!to_check)
            break; /* all drained */
        init_waitqueue_entry(&wait, current);
        set_current_state(TASK_INTERRUPTIBLE);
        add_wait_queue(&to_check->sleep, &wait);//加入到队列中,并没有立马停止播放
        snd_pcm_stream_unlock_irq(substream);
        if (runtime->no_period_wakeup)//如果没设置此flag,等待时间为long型最大值
            tout = MAX_SCHEDULE_TIMEOUT;
        else {
            tout = 10;
            if (runtime->rate) {
                long t = runtime->period_size * 2 / runtime->rate;
                tout = max(t, tout);
            }
            tout = msecs_to_jiffies(tout * 1000);//否则为10ms和半个period_size最大值
        }
        tout = schedule_timeout(tout);//等待超时

        snd_pcm_stream_lock_irq(substream);
        group = snd_pcm_stream_group_ref(substream);
        snd_pcm_group_for_each_entry(s, substream) {
            if (s->runtime == to_check) {
                remove_wait_queue(&to_check->sleep, &wait);
                break;
            }
        }
        snd_pcm_group_unref(group, substream);

        if (card->shutdown) {
            result = -ENODEV;
            break;
        }
        if (tout == 0) {
            if (substream->runtime->status->state == SNDRV_PCM_STATE_SUSPENDED)
                result = -ESTRPIPE;
            else {
                dev_dbg(substream->pcm->card->dev,
                    "playback drain error (DMA or IRQ trouble?)\n");
                snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP);
                result = -EIO;
            }
            break;
        }
    }

 unlock:
    snd_pcm_stream_unlock_irq(substream);

    return result;
}

那什么时候call snd_pcm_stop呢?

嗯,是等dma中数据消耗完,那怎么等DMA数据消耗完呢?当然是看hw_ptr

snd_pcm_update_hw_ptr0->snd_pcm_update_state

int snd_pcm_update_state(struct snd_pcm_substream *substream,
             struct snd_pcm_runtime *runtime)
{
    snd_pcm_uframes_t avail;

    avail = snd_pcm_avail(substream);
    if (avail > runtime->avail_max)
        runtime->avail_max = avail;
    if (runtime->status->state == SNDRV_PCM_STATE_DRAINING) {
        if (avail >= runtime->buffer_size) {//avail是指dma的space size,如果大于或者等于buf size,表示DMA中已经没数据了
            snd_pcm_drain_done(substream);//会call snd_pcm_stop
            return -EPIPE;
        }
    } else {
        if (avail >= runtime->stop_threshold) {
            __snd_pcm_xrun(substream);
            return -EPIPE;
        }
    }
    if (runtime->twake) {
        if (avail >= runtime->twake)
            wake_up(&runtime->tsleep);
    } else if (avail >= runtime->control->avail_min)
        wake_up(&runtime->sleep);
    return 0;
}

看snd_pcm_drain_done

int snd_pcm_drain_done(struct snd_pcm_substream *substream)
{
    return snd_pcm_action_single(&snd_pcm_action_stop, substream,
                     SNDRV_PCM_STATE_SETUP);
}

static int snd_pcm_do_stop(struct snd_pcm_substream *substream,
               snd_pcm_state_t state)
{
    if (substream->runtime->trigger_master == substream &&
        snd_pcm_running(substream)) {
        substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_STOP);//disable HW,停止播放或者录音
        substream->runtime->stop_operating = true;
    }
    return 0; /* unconditionally stop all substreams */
}

2 snd_pcm_drop

static int snd_pcm_drop(struct snd_pcm_substream *substream)
{
    struct snd_pcm_runtime *runtime;
    int result = 0;
    
    if (PCM_RUNTIME_CHECK(substream))
        return -ENXIO;
    runtime = substream->runtime;

    if (runtime->status->state == SNDRV_PCM_STATE_OPEN ||
        runtime->status->state == SNDRV_PCM_STATE_DISCONNECTED)
        return -EBADFD;

    snd_pcm_stream_lock_irq(substream);
    /* resume pause */
    if (runtime->status->state == SNDRV_PCM_STATE_PAUSED)
        snd_pcm_pause(substream, false);

    snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP);//disable HW,停止播放或者录音
    /* runtime->control->appl_ptr = runtime->status->hw_ptr; */
    snd_pcm_stream_unlock_irq(substream);

    return result;
}

 

posted @ 2025-05-28 22:06  Action_er  阅读(15)  评论(0)    收藏  举报