pcm_native.c
来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 2,359 行 · 第 1/5 页
C
2,359 行
static int snd_pcm_do_suspend(snd_pcm_substream_t *substream, int state){ snd_pcm_runtime_t *runtime = substream->runtime; if (runtime->trigger_master != substream) return 0; if (runtime->status->suspended_state != SNDRV_PCM_STATE_RUNNING && runtime->status->suspended_state != SNDRV_PCM_STATE_DRAINING) return 0; return substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_SUSPEND);}static void snd_pcm_post_suspend(snd_pcm_substream_t *substream, int state){ snd_pcm_runtime_t *runtime = substream->runtime; snd_pcm_trigger_tstamp(substream); if (substream->timer) snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MPAUSE, &runtime->trigger_tstamp); runtime->status->state = SNDRV_PCM_STATE_SUSPENDED; snd_pcm_tick_set(substream, 0); wake_up(&runtime->sleep);}static struct action_ops snd_pcm_action_suspend = { .pre_action = snd_pcm_pre_suspend, .do_action = snd_pcm_do_suspend, .post_action = snd_pcm_post_suspend};/** * snd_pcm_suspend */int snd_pcm_suspend(snd_pcm_substream_t *substream){ return snd_pcm_action(&snd_pcm_action_suspend, substream, 0);}/** * snd_pcm_suspend_all */int snd_pcm_suspend_all(snd_pcm_t *pcm){ snd_pcm_substream_t *substream; int stream, err; for (stream = 0; stream < 2; stream++) { for (substream = pcm->streams[stream].substream; substream; substream = substream->next) { /* FIXME: the open/close code should lock this as well */ if (substream->runtime == NULL) continue; snd_pcm_stream_lock(substream); if (substream->runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) { snd_pcm_stream_unlock(substream); continue; } if ((err = snd_pcm_suspend(substream)) < 0) { snd_pcm_stream_unlock(substream); return err; } snd_pcm_stream_unlock(substream); } } return 0;}/* resume */static int snd_pcm_pre_resume(snd_pcm_substream_t *substream, int state){ snd_pcm_runtime_t *runtime = substream->runtime; if (!(runtime->info & SNDRV_PCM_INFO_RESUME)) return -ENOSYS; runtime->trigger_master = substream; return 0;}static int snd_pcm_do_resume(snd_pcm_substream_t *substream, int state){ snd_pcm_runtime_t *runtime = substream->runtime; if (runtime->trigger_master != substream) return 0; if (runtime->status->suspended_state != SNDRV_PCM_STATE_RUNNING && runtime->status->suspended_state != SNDRV_PCM_STATE_DRAINING) return 0; return substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_RESUME);}static void snd_pcm_post_resume(snd_pcm_substream_t *substream, int state){ snd_pcm_runtime_t *runtime = substream->runtime; snd_pcm_trigger_tstamp(substream); if (substream->timer) snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MCONTINUE, &runtime->trigger_tstamp); runtime->status->state = runtime->status->suspended_state; if (runtime->sleep_min) snd_pcm_tick_prepare(substream);}static struct action_ops snd_pcm_action_resume = { .pre_action = snd_pcm_pre_resume, .do_action = snd_pcm_do_resume, .post_action = snd_pcm_post_resume};static int snd_pcm_resume(snd_pcm_substream_t *substream){ snd_card_t *card = substream->pcm->card; int res; snd_power_lock(card); if ((res = snd_power_wait(card, SNDRV_CTL_POWER_D0, substream->ffile)) >= 0) res = snd_pcm_action_lock_irq(&snd_pcm_action_resume, substream, 0); snd_power_unlock(card); return res;}#elsestatic int snd_pcm_resume(snd_pcm_substream_t *substream){ return -ENOSYS;}#endif /* CONFIG_PM */static int snd_pcm_xrun(snd_pcm_substream_t *substream){ snd_card_t *card = substream->pcm->card; snd_pcm_runtime_t *runtime = substream->runtime; int result; snd_power_lock(card); snd_pcm_stream_lock_irq(substream); _xrun_recovery: switch (runtime->status->state) { case SNDRV_PCM_STATE_XRUN: result = 0; /* already there */ break; case SNDRV_PCM_STATE_RUNNING: result = snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); break; case SNDRV_PCM_STATE_SUSPENDED: snd_pcm_stream_unlock_irq(substream); result = snd_power_wait(card, SNDRV_CTL_POWER_D0, substream->ffile); snd_pcm_stream_lock_irq(substream); if (result >= 0) goto _xrun_recovery; break; default: result = -EBADFD; } snd_pcm_stream_unlock_irq(substream); snd_power_unlock(card); return result;}static int snd_pcm_pre_reset(snd_pcm_substream_t * substream, int state){ snd_pcm_runtime_t *runtime = substream->runtime; switch (runtime->status->state) { case SNDRV_PCM_STATE_RUNNING: case SNDRV_PCM_STATE_PREPARED: case SNDRV_PCM_STATE_PAUSED: case SNDRV_PCM_STATE_SUSPENDED: return 0; default: return -EBADFD; }}static int snd_pcm_do_reset(snd_pcm_substream_t * substream, int state){ snd_pcm_runtime_t *runtime = substream->runtime; int err = substream->ops->ioctl(substream, SNDRV_PCM_IOCTL1_RESET, NULL); if (err < 0) return err; // snd_assert(runtime->status->hw_ptr < runtime->buffer_size, ); 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;}static void snd_pcm_post_reset(snd_pcm_substream_t * substream, int state){ snd_pcm_runtime_t *runtime = substream->runtime; runtime->control->appl_ptr = runtime->status->hw_ptr; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && runtime->silence_size > 0) snd_pcm_playback_silence(substream, ULONG_MAX);}static struct action_ops snd_pcm_action_reset = { .pre_action = snd_pcm_pre_reset, .do_action = snd_pcm_do_reset, .post_action = snd_pcm_post_reset};static int snd_pcm_reset(snd_pcm_substream_t *substream){ return snd_pcm_action_nonatomic(&snd_pcm_action_reset, substream, 0);}static int snd_pcm_pre_prepare(snd_pcm_substream_t * substream, int state){ snd_pcm_runtime_t *runtime = substream->runtime; switch (runtime->status->state) { case SNDRV_PCM_STATE_OPEN: return -EBADFD; case SNDRV_PCM_STATE_RUNNING: return -EBUSY; default: return 0; }}static int snd_pcm_do_prepare(snd_pcm_substream_t * substream, int state){ int err; err = substream->ops->prepare(substream); if (err < 0) return err; return snd_pcm_do_reset(substream, 0);}static void snd_pcm_post_prepare(snd_pcm_substream_t * substream, int state){ snd_pcm_runtime_t *runtime = substream->runtime; runtime->control->appl_ptr = runtime->status->hw_ptr; runtime->status->state = SNDRV_PCM_STATE_PREPARED;}static struct action_ops snd_pcm_action_prepare = { .pre_action = snd_pcm_pre_prepare, .do_action = snd_pcm_do_prepare, .post_action = snd_pcm_post_prepare};/** * snd_pcm_prepare */int snd_pcm_prepare(snd_pcm_substream_t *substream){ int res; snd_card_t *card = substream->pcm->card; snd_power_lock(card); if ((res = snd_power_wait(card, SNDRV_CTL_POWER_D0, substream->ffile)) >= 0) res = snd_pcm_action_nonatomic(&snd_pcm_action_prepare, substream, 0); snd_power_unlock(card); return res;}static void snd_pcm_change_state(snd_pcm_substream_t *substream, int state){ struct list_head *pos; snd_pcm_substream_t *s; if (snd_pcm_stream_linked(substream)) { if (!spin_trylock(&substream->group->lock)) { spin_unlock(&substream->self_group.lock); spin_lock(&substream->group->lock); spin_lock(&substream->self_group.lock); } snd_pcm_group_for_each(pos, substream) { s = snd_pcm_group_substream_entry(pos); if (s != substream) spin_lock(&s->self_group.lock); s->runtime->status->state = state; if (s != substream) spin_unlock(&s->self_group.lock); } spin_unlock(&substream->group->lock); } else { substream->runtime->status->state = state; }}static int snd_pcm_playback_drop(snd_pcm_substream_t *substream);static int snd_pcm_playback_drain(snd_pcm_substream_t * substream){ snd_card_t *card; snd_pcm_runtime_t *runtime; int err, result = 0; wait_queue_t wait; enum { READY, EXPIRED, SUSPENDED, SIGNALED } state = READY; snd_pcm_uframes_t stop_threshold; snd_assert(substream != NULL, return -ENXIO); snd_assert(substream->stream == SNDRV_PCM_STREAM_PLAYBACK, return -EINVAL); runtime = substream->runtime; card = substream->pcm->card; snd_power_lock(card); snd_pcm_stream_lock_irq(substream); /* stop_threshold fixup to avoid endless loop when */ /* stop_threshold > buffer_size */ stop_threshold = runtime->stop_threshold; if (runtime->stop_threshold > runtime->buffer_size) runtime->stop_threshold = runtime->buffer_size; switch (runtime->status->state) { case SNDRV_PCM_STATE_PAUSED: snd_pcm_pause(substream, 0); /* Fall through */ case SNDRV_PCM_STATE_RUNNING: case SNDRV_PCM_STATE_DRAINING: break; case SNDRV_PCM_STATE_SUSPENDED: snd_pcm_stream_unlock_irq(substream); result = snd_power_wait(card, SNDRV_CTL_POWER_D0, substream->ffile); snd_pcm_stream_lock_irq(substream); if (result >= 0) snd_pcm_change_state(substream, SNDRV_PCM_STATE_SETUP); goto _end; case SNDRV_PCM_STATE_OPEN: result = -EBADFD; goto _end; case SNDRV_PCM_STATE_PREPARED: if (!snd_pcm_playback_empty(substream)) { err = snd_pcm_start(substream); if (err < 0) { result = err; goto _end; } break; } /* Fall through */ case SNDRV_PCM_STATE_XRUN: snd_pcm_change_state(substream, SNDRV_PCM_STATE_SETUP); /* Fall through */ case SNDRV_PCM_STATE_SETUP: goto _end; default: break; } if (runtime->status->state == SNDRV_PCM_STATE_RUNNING) { if (snd_pcm_playback_empty(substream)) { snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP); goto _end; } snd_pcm_change_state(substream, SNDRV_PCM_STATE_DRAINING); } if (substream->ffile->f_flags & O_NONBLOCK) { result = -EAGAIN; goto _end; } init_waitqueue_entry(&wait, current); add_wait_queue(&runtime->sleep, &wait); while (1) { long tout; if (signal_pending(current)) { state = SIGNALED; break; } set_current_state(TASK_INTERRUPTIBLE); snd_pcm_stream_unlock_irq(substream); snd_power_unlock(card); tout = schedule_timeout(10 * HZ); snd_power_lock(card); snd_pcm_stream_lock_irq(substream); if (tout == 0) { state = runtime->status->state == SNDRV_PCM_STATE_SUSPENDED ? SUSPENDED : EXPIRED; break; } if (runtime->status->state != SNDRV_PCM_STATE_DRAINING) { state = READY; break; } } remove_wait_queue(&runtime->sleep, &wait); switch (state) { case SIGNALED: result = -ERESTARTSYS; goto _end; case SUSPENDED: result = -ESTRPIPE; goto _end; case EXPIRED: snd_printd("playback drain error (DMA or IRQ trouble?)\n"); result = -EIO; goto _end; default: break; } _end: runtime->stop_threshold = stop_threshold; snd_pcm_stream_unlock_irq(substream); snd_power_unlock(card); if (state == EXPIRED) snd_pcm_playback_drop(substream); return result;}static int snd_pcm_playback_drop(snd_pcm_substream_t *substream){ snd_pcm_runtime_t *runtime = substream->runtime; snd_card_t *card = substream->pcm->card; int res = 0; snd_power_lock(card); snd_pcm_stream_lock_irq(substream); switch (runtime->status->state) { case SNDRV_PCM_STATE_OPEN: res = -EBADFD; break; case SNDRV_PCM_STATE_SETUP: break; case SNDRV_PCM_STATE_PAUSED: snd_pcm_pause(substream, 0); /* Fall through */ case SNDRV_PCM_STATE_RUNNING: case SNDRV_PCM_STATE_DRAINING: if (snd_pcm_update_hw_ptr(substream) >= 0) { snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP); break; } /* Fall through */ case SNDRV_PCM_STATE_PREPARED: case SNDRV_PCM_STATE_XRUN: snd_pcm_change_state(substream, SNDRV_PCM_STATE_SETUP); break; case SNDRV_PCM_STATE_SUSPENDED: snd_pcm_stream_unlock_irq(substream); res = snd_power_wait(card, SNDRV_CTL_POWER_D0, substream->ffile); snd_pcm_stream_lock_irq(substream); if (res >= 0) snd_pcm_change_state(substream, SNDRV_PCM_STATE_SETUP); break; default: break; } runtime->control->appl_ptr = runtime->status->hw_ptr; snd_pcm_stream_unlock_irq(substream); snd_power_unlock(card); return res;}static int snd_pcm_capture_drain(snd_pcm_substream_t * substream){ snd_pcm_runtime_t *runtime = substream->runtime; snd_card_t *card = substream->pcm->card; int res = 0; snd_power_lock(card); snd_pcm_stream_lock_irq(substream); switch (runtime->status->state) { case SNDRV_PCM_STATE_OPEN: res = -EBADFD; break; case SNDRV_PCM_STATE_PREPARED: snd_pcm_change_state(substream, SNDRV_PCM_STATE_SETUP); break; case SNDRV_PCM_STATE_SETUP: case SNDRV_PCM_STATE_DRAINING: break; case SNDRV_PCM_STATE_PAUSED: snd_pcm_pause(substream, 0); /* Fall through */ case SNDRV_PCM_STATE_RUNNING: if (snd_pcm_update_hw_ptr(substream) >= 0) { snd_pcm_stop(substream, snd_pcm_capture_avail(runtime) > 0 ? SNDRV_PCM_STATE_DRAINING : SNDRV_PCM_STATE_SETUP);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?