pcm_native.c
来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 2,359 行 · 第 1/5 页
C
2,359 行
return 0; } result = substream->ops->hw_free(substream); runtime->status->state = SNDRV_PCM_STATE_OPEN; return result;}static int snd_pcm_sw_params(snd_pcm_substream_t * substream, snd_pcm_sw_params_t *params){ snd_pcm_runtime_t *runtime; snd_assert(substream != NULL, return -ENXIO); runtime = substream->runtime; snd_assert(runtime != NULL, return -ENXIO); snd_pcm_stream_lock_irq(substream); if (runtime->status->state == SNDRV_PCM_STATE_OPEN) { snd_pcm_stream_unlock_irq(substream); return -EBADFD; } snd_pcm_stream_unlock_irq(substream); if (params->tstamp_mode > SNDRV_PCM_TSTAMP_LAST) return -EINVAL; if (params->avail_min == 0) return -EINVAL; if (params->xfer_align == 0 || params->xfer_align % runtime->min_align != 0) return -EINVAL; if (params->silence_size >= runtime->boundary) { if (params->silence_threshold != 0) return -EINVAL; } else { if (params->silence_size > params->silence_threshold) return -EINVAL; if (params->silence_threshold > runtime->buffer_size) return -EINVAL; } snd_pcm_stream_lock_irq(substream); runtime->tstamp_mode = params->tstamp_mode; runtime->sleep_min = params->sleep_min; runtime->period_step = params->period_step; runtime->control->avail_min = params->avail_min; runtime->start_threshold = params->start_threshold; runtime->stop_threshold = params->stop_threshold; runtime->silence_threshold = params->silence_threshold; runtime->silence_size = params->silence_size; runtime->xfer_align = params->xfer_align; params->boundary = runtime->boundary; if (snd_pcm_running(substream)) { if (runtime->sleep_min) snd_pcm_tick_prepare(substream); else snd_pcm_tick_set(substream, 0); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && runtime->silence_size > 0) snd_pcm_playback_silence(substream, ULONG_MAX); wake_up(&runtime->sleep); } snd_pcm_stream_unlock_irq(substream); return 0;}static int snd_pcm_sw_params_user(snd_pcm_substream_t * substream, snd_pcm_sw_params_t __user * _params){ snd_pcm_sw_params_t params; int err; if (copy_from_user(¶ms, _params, sizeof(params))) return -EFAULT; err = snd_pcm_sw_params(substream, ¶ms); if (copy_to_user(_params, ¶ms, sizeof(params))) return -EFAULT; return err;}int snd_pcm_status(snd_pcm_substream_t *substream, snd_pcm_status_t *status){ snd_pcm_runtime_t *runtime = substream->runtime; snd_pcm_stream_lock_irq(substream); status->state = runtime->status->state; status->suspended_state = runtime->status->suspended_state; if (status->state == SNDRV_PCM_STATE_OPEN) goto _end; status->trigger_tstamp = runtime->trigger_tstamp; if (snd_pcm_running(substream)) { snd_pcm_update_hw_ptr(substream); if (runtime->tstamp_mode & SNDRV_PCM_TSTAMP_MMAP) status->tstamp = runtime->status->tstamp; else snd_timestamp_now(&status->tstamp, runtime->tstamp_timespec); } else snd_timestamp_now(&status->tstamp, runtime->tstamp_timespec); status->appl_ptr = runtime->control->appl_ptr; status->hw_ptr = runtime->status->hw_ptr; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { status->avail = snd_pcm_playback_avail(runtime); if (runtime->status->state == SNDRV_PCM_STATE_RUNNING || runtime->status->state == SNDRV_PCM_STATE_DRAINING) status->delay = runtime->buffer_size - status->avail; else status->delay = 0; } else { status->avail = snd_pcm_capture_avail(runtime); if (runtime->status->state == SNDRV_PCM_STATE_RUNNING) status->delay = status->avail; else status->delay = 0; } status->avail_max = runtime->avail_max; status->overrange = runtime->overrange; runtime->avail_max = 0; runtime->overrange = 0; _end: snd_pcm_stream_unlock_irq(substream); return 0;}static int snd_pcm_status_user(snd_pcm_substream_t * substream, snd_pcm_status_t __user * _status){ snd_pcm_status_t status; snd_pcm_runtime_t *runtime; int res; snd_assert(substream != NULL, return -ENXIO); runtime = substream->runtime; memset(&status, 0, sizeof(status)); res = snd_pcm_status(substream, &status); if (res < 0) return res; if (copy_to_user(_status, &status, sizeof(status))) return -EFAULT; return 0;}static int snd_pcm_channel_info(snd_pcm_substream_t * substream, snd_pcm_channel_info_t __user * _info){ snd_pcm_channel_info_t info; snd_pcm_runtime_t *runtime; int res; unsigned int channel; snd_assert(substream != NULL, return -ENXIO); if (copy_from_user(&info, _info, sizeof(info))) return -EFAULT; channel = info.channel; runtime = substream->runtime; snd_pcm_stream_lock_irq(substream); if (runtime->status->state == SNDRV_PCM_STATE_OPEN) { snd_pcm_stream_unlock_irq(substream); return -EBADFD; } snd_pcm_stream_unlock_irq(substream); if (channel >= runtime->channels) return -EINVAL; memset(&info, 0, sizeof(info)); info.channel = channel; res = substream->ops->ioctl(substream, SNDRV_PCM_IOCTL1_CHANNEL_INFO, &info); if (res < 0) return res; if (copy_to_user(_info, &info, sizeof(info))) return -EFAULT; return 0;}static void snd_pcm_trigger_tstamp(snd_pcm_substream_t *substream){ snd_pcm_runtime_t *runtime = substream->runtime; if (runtime->trigger_master == NULL) return; if (runtime->trigger_master == substream) { snd_timestamp_now(&runtime->trigger_tstamp, runtime->tstamp_timespec); } else { snd_pcm_trigger_tstamp(runtime->trigger_master); runtime->trigger_tstamp = runtime->trigger_master->runtime->trigger_tstamp; } runtime->trigger_master = NULL;}struct action_ops { int (*pre_action)(snd_pcm_substream_t *substream, int state); int (*do_action)(snd_pcm_substream_t *substream, int state); void (*post_action)(snd_pcm_substream_t *substream, int state);};/* * this functions is core for handling of linked stream * Note: the stream state might be changed also on failure * Note2: call with calling stream lock + link lock */static int snd_pcm_action_group(struct action_ops *ops, snd_pcm_substream_t *substream, int state, int do_lock){ struct list_head *pos; snd_pcm_substream_t *s = NULL; int err, res = 0; snd_pcm_group_for_each(pos, substream) { s = snd_pcm_group_substream_entry(pos); if (do_lock && s != substream) spin_lock(&s->self_group.lock); res = ops->pre_action(s, state); if (res < 0) break; } if (res >= 0) { snd_pcm_group_for_each(pos, substream) { s = snd_pcm_group_substream_entry(pos); err = ops->do_action(s, state); if (err < 0) { if (res == 0) res = err; } else { ops->post_action(s, state); } if (do_lock && s != substream) spin_unlock(&s->self_group.lock); } } else if (do_lock) { snd_pcm_substream_t *s1; /* unlock all streams */ snd_pcm_group_for_each(pos, substream) { s1 = snd_pcm_group_substream_entry(pos); if (s1 != substream) spin_unlock(&s1->self_group.lock); if (s1 == s) /* end */ break; } } return res;}/* * Note: call with stream lock */static int snd_pcm_action_single(struct action_ops *ops, snd_pcm_substream_t *substream, int state){ int res; res = ops->pre_action(substream, state); if (res < 0) return res; res = ops->do_action(substream, state); if (res == 0) { ops->post_action(substream, state); } return res;}/* * Note: call with stream lock */static int snd_pcm_action(struct action_ops *ops, snd_pcm_substream_t *substream, int state){ int res; 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); } res = snd_pcm_action_group(ops, substream, state, 1); spin_unlock(&substream->group->lock); } else { res = snd_pcm_action_single(ops, substream, state); } return res;}/* * Note: don't use any locks before */static int snd_pcm_action_lock_irq(struct action_ops *ops, snd_pcm_substream_t *substream, int state){ int res; read_lock_irq(&snd_pcm_link_rwlock); if (snd_pcm_stream_linked(substream)) { spin_lock(&substream->group->lock); spin_lock(&substream->self_group.lock); res = snd_pcm_action_group(ops, substream, state, 1); spin_unlock(&substream->self_group.lock); spin_unlock(&substream->group->lock); } else { spin_lock(&substream->self_group.lock); res = snd_pcm_action_single(ops, substream, state); spin_unlock(&substream->self_group.lock); } read_unlock_irq(&snd_pcm_link_rwlock); return res;}/* */static int snd_pcm_action_nonatomic(struct action_ops *ops, snd_pcm_substream_t *substream, int state){ int res; down_read(&snd_pcm_link_rwsem); if (snd_pcm_stream_linked(substream)) res = snd_pcm_action_group(ops, substream, state, 0); else res = snd_pcm_action_single(ops, substream, state); up_read(&snd_pcm_link_rwsem); return res;}static int snd_pcm_pre_start(snd_pcm_substream_t *substream, int state){ snd_pcm_runtime_t *runtime = substream->runtime; if (runtime->status->state != SNDRV_PCM_STATE_PREPARED) return -EBADFD; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && !snd_pcm_playback_data(substream)) return -EPIPE; runtime->trigger_master = substream; return 0;}static int snd_pcm_do_start(snd_pcm_substream_t *substream, int state){ if (substream->runtime->trigger_master != substream) return 0; return substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_START);}static void snd_pcm_post_start(snd_pcm_substream_t *substream, int state){ snd_pcm_runtime_t *runtime = substream->runtime; snd_pcm_trigger_tstamp(substream); runtime->status->state = SNDRV_PCM_STATE_RUNNING; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && runtime->silence_size > 0) snd_pcm_playback_silence(substream, ULONG_MAX); if (runtime->sleep_min) snd_pcm_tick_prepare(substream); if (substream->timer) snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MSTART, &runtime->trigger_tstamp);}static struct action_ops snd_pcm_action_start = { .pre_action = snd_pcm_pre_start, .do_action = snd_pcm_do_start, .post_action = snd_pcm_post_start};/** * snd_pcm_start */int snd_pcm_start(snd_pcm_substream_t *substream){ return snd_pcm_action(&snd_pcm_action_start, substream, 0);}static int snd_pcm_pre_stop(snd_pcm_substream_t *substream, int state){ snd_pcm_runtime_t *runtime = substream->runtime; if (substream->runtime->status->state != SNDRV_PCM_STATE_RUNNING && substream->runtime->status->state != SNDRV_PCM_STATE_DRAINING) return -EBADFD; runtime->trigger_master = substream; return 0;}static int snd_pcm_do_stop(snd_pcm_substream_t *substream, int state){ if (substream->runtime->trigger_master != substream) return 0; return substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_STOP);}static void snd_pcm_post_stop(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_MSTOP, &runtime->trigger_tstamp); runtime->status->state = state; snd_pcm_tick_set(substream, 0); wake_up(&runtime->sleep);}static struct action_ops snd_pcm_action_stop = { .pre_action = snd_pcm_pre_stop, .do_action = snd_pcm_do_stop, .post_action = snd_pcm_post_stop};/** * snd_pcm_stop */int snd_pcm_stop(snd_pcm_substream_t *substream, int state){ return snd_pcm_action(&snd_pcm_action_stop, substream, state);}static int snd_pcm_pre_pause(snd_pcm_substream_t *substream, int push){ snd_pcm_runtime_t *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(snd_pcm_substream_t *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_post_pause(snd_pcm_substream_t *substream, int push){ snd_pcm_runtime_t *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, .post_action = snd_pcm_post_pause};static int snd_pcm_pause(snd_pcm_substream_t *substream, int push){ return snd_pcm_action(&snd_pcm_action_pause, substream, push);}#ifdef CONFIG_PM/* suspend */static int snd_pcm_pre_suspend(snd_pcm_substream_t *substream, int state){ snd_pcm_runtime_t *runtime = substream->runtime; if (runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) return -EBUSY; runtime->status->suspended_state = runtime->status->state; runtime->trigger_master = substream; return 0;}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?