📄 pcm_native.c
字号:
* @state: PCM state after stopping the stream * * Try to stop all running streams in the substream group. * The state of each stream is changed to the given value after that unconditionally. */int snd_pcm_stop(struct snd_pcm_substream *substream, int state){ return snd_pcm_action(&snd_pcm_action_stop, substream, state);}EXPORT_SYMBOL(snd_pcm_stop);/** * snd_pcm_drain_done * @substream: the PCM substream * * Stop the DMA only when the given stream is playback. * The state is changed to SETUP. * Unlike snd_pcm_stop(), this affects only the given stream. */int snd_pcm_drain_done(struct snd_pcm_substream *substream){ return snd_pcm_action_single(&snd_pcm_action_stop, substream, SNDRV_PCM_STATE_SETUP);}/* * pause callbacks */static int snd_pcm_pre_pause(struct snd_pcm_substream *substream, int push){ struct snd_pcm_runtime *runtime = substream->runtime; if (!(runtime->info & SNDRV_PCM_INFO_PAUSE)) return -ENOSYS; if (push) { if (runtime->status->state != SNDRV_PCM_STATE_RUNNING) return -EBADFD; } else if (runtime->status->state != SNDRV_PCM_STATE_PAUSED) return -EBADFD; runtime->trigger_master = substream; return 0;}static int snd_pcm_do_pause(struct snd_pcm_substream *substream, int push){ if (substream->runtime->trigger_master != substream) return 0; return substream->ops->trigger(substream, push ? SNDRV_PCM_TRIGGER_PAUSE_PUSH : SNDRV_PCM_TRIGGER_PAUSE_RELEASE);}static void snd_pcm_undo_pause(struct snd_pcm_substream *substream, int push){ if (substream->runtime->trigger_master == substream) substream->ops->trigger(substream, push ? SNDRV_PCM_TRIGGER_PAUSE_RELEASE : SNDRV_PCM_TRIGGER_PAUSE_PUSH);}static void snd_pcm_post_pause(struct snd_pcm_substream *substream, int push){ struct snd_pcm_runtime *runtime = substream->runtime; snd_pcm_trigger_tstamp(substream); if (push) { runtime->status->state = SNDRV_PCM_STATE_PAUSED; if (substream->timer) snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MPAUSE, &runtime->trigger_tstamp); snd_pcm_tick_set(substream, 0); wake_up(&runtime->sleep); } else { runtime->status->state = SNDRV_PCM_STATE_RUNNING; if (runtime->sleep_min) snd_pcm_tick_prepare(substream); if (substream->timer) snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MCONTINUE, &runtime->trigger_tstamp); }}static struct action_ops snd_pcm_action_pause = { .pre_action = snd_pcm_pre_pause, .do_action = snd_pcm_do_pause, .undo_action = snd_pcm_undo_pause, .post_action = snd_pcm_post_pause};/* * Push/release the pause for all linked streams. */static int snd_pcm_pause(struct snd_pcm_substream *substream, int push){ return snd_pcm_action(&snd_pcm_action_pause, substream, push);}#ifdef CONFIG_PM/* suspend */static int snd_pcm_pre_suspend(struct snd_pcm_substream *substream, int state){ struct snd_pcm_runtime *runtime = substream->runtime; if (runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) return -EBUSY; runtime->trigger_master = substream; return 0;}static int snd_pcm_do_suspend(struct snd_pcm_substream *substream, int state){ struct snd_pcm_runtime *runtime = substream->runtime; if (runtime->trigger_master != substream) return 0; if (! snd_pcm_running(substream)) return 0; substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_SUSPEND); return 0; /* suspend unconditionally */}static void snd_pcm_post_suspend(struct snd_pcm_substream *substream, int state){ struct snd_pcm_runtime *runtime = substream->runtime; snd_pcm_trigger_tstamp(substream); if (substream->timer) snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MSUSPEND, &runtime->trigger_tstamp); runtime->status->suspended_state = runtime->status->state; 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 * @substream: the PCM substream * * Trigger SUSPEND to all linked streams. * After this call, all streams are changed to SUSPENDED state. */int snd_pcm_suspend(struct snd_pcm_substream *substream){ int err; unsigned long flags; if (! substream) return 0; snd_pcm_stream_lock_irqsave(substream, flags); err = snd_pcm_action(&snd_pcm_action_suspend, substream, 0); snd_pcm_stream_unlock_irqrestore(substream, flags); return err;}EXPORT_SYMBOL(snd_pcm_suspend);/** * snd_pcm_suspend_all * @pcm: the PCM instance * * Trigger SUSPEND to all substreams in the given pcm. * After this call, all streams are changed to SUSPENDED state. */int snd_pcm_suspend_all(struct snd_pcm *pcm){ struct snd_pcm_substream *substream; int stream, err = 0; if (! pcm) return 0; 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; err = snd_pcm_suspend(substream); if (err < 0 && err != -EBUSY) return err; } } return 0;}EXPORT_SYMBOL(snd_pcm_suspend_all);/* resume */static int snd_pcm_pre_resume(struct snd_pcm_substream *substream, int state){ struct snd_pcm_runtime *runtime = substream->runtime; if (!(runtime->info & SNDRV_PCM_INFO_RESUME)) return -ENOSYS; runtime->trigger_master = substream; return 0;}static int snd_pcm_do_resume(struct snd_pcm_substream *substream, int state){ struct snd_pcm_runtime *runtime = substream->runtime; if (runtime->trigger_master != substream) return 0; /* DMA not running previously? */ if (runtime->status->suspended_state != SNDRV_PCM_STATE_RUNNING && (runtime->status->suspended_state != SNDRV_PCM_STATE_DRAINING || substream->stream != SNDRV_PCM_STREAM_PLAYBACK)) return 0; return substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_RESUME);}static void snd_pcm_undo_resume(struct snd_pcm_substream *substream, int state){ if (substream->runtime->trigger_master == substream && snd_pcm_running(substream)) substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_SUSPEND);}static void snd_pcm_post_resume(struct snd_pcm_substream *substream, int state){ struct snd_pcm_runtime *runtime = substream->runtime; snd_pcm_trigger_tstamp(substream); if (substream->timer) snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MRESUME, &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, .undo_action = snd_pcm_undo_resume, .post_action = snd_pcm_post_resume};static int snd_pcm_resume(struct snd_pcm_substream *substream){ struct snd_card *card = substream->pcm->card; int res; snd_power_lock(card); if ((res = snd_power_wait(card, SNDRV_CTL_POWER_D0)) >= 0) res = snd_pcm_action_lock_irq(&snd_pcm_action_resume, substream, 0); snd_power_unlock(card); return res;}#elsestatic int snd_pcm_resume(struct snd_pcm_substream *substream){ return -ENOSYS;}#endif /* CONFIG_PM *//* * xrun ioctl * * Change the RUNNING stream(s) to XRUN state. */static int snd_pcm_xrun(struct snd_pcm_substream *substream){ struct snd_card *card = substream->pcm->card; struct snd_pcm_runtime *runtime = substream->runtime; int result; snd_power_lock(card); if (runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) { result = snd_power_wait(card, SNDRV_CTL_POWER_D0); if (result < 0) goto _unlock; } snd_pcm_stream_lock_irq(substream); 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; default: result = -EBADFD; } snd_pcm_stream_unlock_irq(substream); _unlock: snd_power_unlock(card); return result;}/* * reset ioctl */static int snd_pcm_pre_reset(struct snd_pcm_substream *substream, int state){ struct snd_pcm_runtime *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(struct snd_pcm_substream *substream, int state){ struct snd_pcm_runtime *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(struct snd_pcm_substream *substream, int state){ struct snd_pcm_runtime *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(struct snd_pcm_substream *substream){ return snd_pcm_action_nonatomic(&snd_pcm_action_reset, substream, 0);}/* * prepare ioctl *//* we use the second argument for updating f_flags */static int snd_pcm_pre_prepare(struct snd_pcm_substream *substream, int f_flags){ struct snd_pcm_runtime *runtime = substream->runtime; if (runtime->status->state == SNDRV_PCM_STATE_OPEN || runtime->status->state == SNDRV_PCM_STATE_DISCONNECTED) return -EBADFD; if (snd_pcm_running(substream)) return -EBUSY; substream->f_flags = f_flags; return 0;}static int snd_pcm_do_prepare(struct snd_pcm_substream *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(struct snd_pcm_substream *substream, int state){ struct snd_pcm_runtime *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 * @substream: the PCM substream instance * @file: file to refer f_flags * * Prepare the PCM substream to be triggerable. */static int snd_pcm_prepare(struct snd_pcm_substream *substream, struct file *file){ int res; struct snd_card *card = substream->pcm->card; int f_flags; if (file) f_flags = file->f_flags; else f_flags = substream->f_flags; snd_power_lock(card); if ((res = snd_power_wait(card, SNDRV_CTL_POWER_D0)) >= 0) res = snd_pcm_action_nonatomic(&snd_pcm_action_prepare, substream, f_flags); snd_power_unlock(card); return res;}/* * drain ioctl */static int snd_pcm_pre_drain_init(struct snd_pcm_substream *substream, int state){ if (substream->f_flags & O_NONBLOCK) return -EAGAIN; substream->runtime->trigger_master = substream; return 0;}static int snd_pcm_do_drain_init(struct snd_pcm_substream *substream, int state){ struct snd_pcm_runtime *runtime = substream->runtime; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { switch (runtime->status->state) { case SNDRV_PCM_STATE_PREPARED: /* start playback stream if possible */ if (! snd_pcm_playback_empty(substream)) { snd_pcm_do_start(substream, SNDRV_PCM_STATE_DRAINING); snd_pcm_post_start(substream, SNDRV_PCM_STATE_DRAINING); } break; case SNDRV_PCM_STATE_RUNNING: runtime->status->state = SNDRV_PCM_STATE_DRAINING; break; default: break; } } else { /* stop running stream */ if (runtime->status->state == SNDRV_PCM_STATE_RUNNING) { int state = snd_pcm_capture_avail(runtime) > 0 ? SNDRV_PCM_STATE_DRAINING : SNDRV_PCM_STATE_SETUP; snd_pcm_do_stop(substream, state); snd_pcm_post_stop(substream, state); } } return 0;}static void snd_pcm_post_drain_init(struct snd_pcm_substream *substream, int state){}static struct action_ops snd_pcm_action_drain_init = { .pre_action = snd_pcm_pre_drain_init, .do_action = snd_pcm_do_drain_init, .post_action = snd_pcm_post_drain_init};struct drain_rec { struct snd_pcm_substream *substream; wait_queue_t wait;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -