📄 pcm_lib.c
字号:
* @r: struct snd_ratdens constriants */int snd_pcm_hw_constraint_ratdens(struct snd_pcm_runtime *runtime, unsigned int cond, snd_pcm_hw_param_t var, struct snd_pcm_hw_constraint_ratdens *r){ return snd_pcm_hw_rule_add(runtime, cond, var, snd_pcm_hw_rule_ratdens, r, var, -1);}EXPORT_SYMBOL(snd_pcm_hw_constraint_ratdens);static int snd_pcm_hw_rule_msbits(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule){ unsigned int l = (unsigned long) rule->private; int width = l & 0xffff; unsigned int msbits = l >> 16; struct snd_interval *i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS); if (snd_interval_single(i) && snd_interval_value(i) == width) params->msbits = msbits; return 0;}/** * snd_pcm_hw_constraint_msbits * @runtime: PCM runtime instance * @cond: condition bits * @width: sample bits width * @msbits: msbits width */int snd_pcm_hw_constraint_msbits(struct snd_pcm_runtime *runtime, unsigned int cond, unsigned int width, unsigned int msbits){ unsigned long l = (msbits << 16) | width; return snd_pcm_hw_rule_add(runtime, cond, -1, snd_pcm_hw_rule_msbits, (void*) l, SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -1);}EXPORT_SYMBOL(snd_pcm_hw_constraint_msbits);static int snd_pcm_hw_rule_step(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule){ unsigned long step = (unsigned long) rule->private; return snd_interval_step(hw_param_interval(params, rule->var), 0, step);}/** * snd_pcm_hw_constraint_step * @runtime: PCM runtime instance * @cond: condition bits * @var: hw_params variable to apply the step constraint * @step: step size */int snd_pcm_hw_constraint_step(struct snd_pcm_runtime *runtime, unsigned int cond, snd_pcm_hw_param_t var, unsigned long step){ return snd_pcm_hw_rule_add(runtime, cond, var, snd_pcm_hw_rule_step, (void *) step, var, -1);}EXPORT_SYMBOL(snd_pcm_hw_constraint_step);static int snd_pcm_hw_rule_pow2(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule){ static int pow2_sizes[] = { 1<<0, 1<<1, 1<<2, 1<<3, 1<<4, 1<<5, 1<<6, 1<<7, 1<<8, 1<<9, 1<<10, 1<<11, 1<<12, 1<<13, 1<<14, 1<<15, 1<<16, 1<<17, 1<<18, 1<<19, 1<<20, 1<<21, 1<<22, 1<<23, 1<<24, 1<<25, 1<<26, 1<<27, 1<<28, 1<<29, 1<<30 }; return snd_interval_list(hw_param_interval(params, rule->var), ARRAY_SIZE(pow2_sizes), pow2_sizes, 0);} /** * snd_pcm_hw_constraint_pow2 * @runtime: PCM runtime instance * @cond: condition bits * @var: hw_params variable to apply the power-of-2 constraint */int snd_pcm_hw_constraint_pow2(struct snd_pcm_runtime *runtime, unsigned int cond, snd_pcm_hw_param_t var){ return snd_pcm_hw_rule_add(runtime, cond, var, snd_pcm_hw_rule_pow2, NULL, var, -1);}EXPORT_SYMBOL(snd_pcm_hw_constraint_pow2);static void _snd_pcm_hw_param_any(struct snd_pcm_hw_params *params, snd_pcm_hw_param_t var){ if (hw_is_mask(var)) { snd_mask_any(hw_param_mask(params, var)); params->cmask |= 1 << var; params->rmask |= 1 << var; return; } if (hw_is_interval(var)) { snd_interval_any(hw_param_interval(params, var)); params->cmask |= 1 << var; params->rmask |= 1 << var; return; } snd_BUG();}void _snd_pcm_hw_params_any(struct snd_pcm_hw_params *params){ unsigned int k; memset(params, 0, sizeof(*params)); for (k = SNDRV_PCM_HW_PARAM_FIRST_MASK; k <= SNDRV_PCM_HW_PARAM_LAST_MASK; k++) _snd_pcm_hw_param_any(params, k); for (k = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL; k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++) _snd_pcm_hw_param_any(params, k); params->info = ~0U;}EXPORT_SYMBOL(_snd_pcm_hw_params_any);/** * snd_pcm_hw_param_value * @params: the hw_params instance * @var: parameter to retrieve * @dir: pointer to the direction (-1,0,1) or NULL * * Return the value for field PAR if it's fixed in configuration space * defined by PARAMS. Return -EINVAL otherwise */int snd_pcm_hw_param_value(const struct snd_pcm_hw_params *params, snd_pcm_hw_param_t var, int *dir){ if (hw_is_mask(var)) { const struct snd_mask *mask = hw_param_mask_c(params, var); if (!snd_mask_single(mask)) return -EINVAL; if (dir) *dir = 0; return snd_mask_value(mask); } if (hw_is_interval(var)) { const struct snd_interval *i = hw_param_interval_c(params, var); if (!snd_interval_single(i)) return -EINVAL; if (dir) *dir = i->openmin; return snd_interval_value(i); } return -EINVAL;}EXPORT_SYMBOL(snd_pcm_hw_param_value);void _snd_pcm_hw_param_setempty(struct snd_pcm_hw_params *params, snd_pcm_hw_param_t var){ if (hw_is_mask(var)) { snd_mask_none(hw_param_mask(params, var)); params->cmask |= 1 << var; params->rmask |= 1 << var; } else if (hw_is_interval(var)) { snd_interval_none(hw_param_interval(params, var)); params->cmask |= 1 << var; params->rmask |= 1 << var; } else { snd_BUG(); }}EXPORT_SYMBOL(_snd_pcm_hw_param_setempty);static int _snd_pcm_hw_param_first(struct snd_pcm_hw_params *params, snd_pcm_hw_param_t var){ int changed; if (hw_is_mask(var)) changed = snd_mask_refine_first(hw_param_mask(params, var)); else if (hw_is_interval(var)) changed = snd_interval_refine_first(hw_param_interval(params, var)); else return -EINVAL; if (changed) { params->cmask |= 1 << var; params->rmask |= 1 << var; } return changed;}/** * snd_pcm_hw_param_first * @pcm: PCM instance * @params: the hw_params instance * @var: parameter to retrieve * @dir: pointer to the direction (-1,0,1) or NULL * * Inside configuration space defined by PARAMS remove from PAR all * values > minimum. Reduce configuration space accordingly. * Return the minimum. */int snd_pcm_hw_param_first(struct snd_pcm_substream *pcm, struct snd_pcm_hw_params *params, snd_pcm_hw_param_t var, int *dir){ int changed = _snd_pcm_hw_param_first(params, var); if (changed < 0) return changed; if (params->rmask) { int err = snd_pcm_hw_refine(pcm, params); snd_assert(err >= 0, return err); } return snd_pcm_hw_param_value(params, var, dir);}EXPORT_SYMBOL(snd_pcm_hw_param_first);static int _snd_pcm_hw_param_last(struct snd_pcm_hw_params *params, snd_pcm_hw_param_t var){ int changed; if (hw_is_mask(var)) changed = snd_mask_refine_last(hw_param_mask(params, var)); else if (hw_is_interval(var)) changed = snd_interval_refine_last(hw_param_interval(params, var)); else return -EINVAL; if (changed) { params->cmask |= 1 << var; params->rmask |= 1 << var; } return changed;}/** * snd_pcm_hw_param_last * @pcm: PCM instance * @params: the hw_params instance * @var: parameter to retrieve * @dir: pointer to the direction (-1,0,1) or NULL * * Inside configuration space defined by PARAMS remove from PAR all * values < maximum. Reduce configuration space accordingly. * Return the maximum. */int snd_pcm_hw_param_last(struct snd_pcm_substream *pcm, struct snd_pcm_hw_params *params, snd_pcm_hw_param_t var, int *dir){ int changed = _snd_pcm_hw_param_last(params, var); if (changed < 0) return changed; if (params->rmask) { int err = snd_pcm_hw_refine(pcm, params); snd_assert(err >= 0, return err); } return snd_pcm_hw_param_value(params, var, dir);}EXPORT_SYMBOL(snd_pcm_hw_param_last);/** * snd_pcm_hw_param_choose * @pcm: PCM instance * @params: the hw_params instance * * Choose one configuration from configuration space defined by PARAMS * The configuration chosen is that obtained fixing in this order: * first access, first format, first subformat, min channels, * min rate, min period time, max buffer size, min tick time */int snd_pcm_hw_params_choose(struct snd_pcm_substream *pcm, struct snd_pcm_hw_params *params){ static int vars[] = { SNDRV_PCM_HW_PARAM_ACCESS, SNDRV_PCM_HW_PARAM_FORMAT, SNDRV_PCM_HW_PARAM_SUBFORMAT, SNDRV_PCM_HW_PARAM_CHANNELS, SNDRV_PCM_HW_PARAM_RATE, SNDRV_PCM_HW_PARAM_PERIOD_TIME, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, SNDRV_PCM_HW_PARAM_TICK_TIME, -1 }; int err, *v; for (v = vars; *v != -1; v++) { if (*v != SNDRV_PCM_HW_PARAM_BUFFER_SIZE) err = snd_pcm_hw_param_first(pcm, params, *v, NULL); else err = snd_pcm_hw_param_last(pcm, params, *v, NULL); snd_assert(err >= 0, return err); } return 0;}static int snd_pcm_lib_ioctl_reset(struct snd_pcm_substream *substream, void *arg){ struct snd_pcm_runtime *runtime = substream->runtime; unsigned long flags; snd_pcm_stream_lock_irqsave(substream, flags); if (snd_pcm_running(substream) && snd_pcm_update_hw_ptr(substream) >= 0) runtime->status->hw_ptr %= runtime->buffer_size; else runtime->status->hw_ptr = 0; snd_pcm_stream_unlock_irqrestore(substream, flags); return 0;}static int snd_pcm_lib_ioctl_channel_info(struct snd_pcm_substream *substream, void *arg){ struct snd_pcm_channel_info *info = arg; struct snd_pcm_runtime *runtime = substream->runtime; int width; if (!(runtime->info & SNDRV_PCM_INFO_MMAP)) { info->offset = -1; return 0; } width = snd_pcm_format_physical_width(runtime->format); if (width < 0) return width; info->offset = 0; switch (runtime->access) { case SNDRV_PCM_ACCESS_MMAP_INTERLEAVED: case SNDRV_PCM_ACCESS_RW_INTERLEAVED: info->first = info->channel * width; info->step = runtime->channels * width; break; case SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED: case SNDRV_PCM_ACCESS_RW_NONINTERLEAVED: { size_t size = runtime->dma_bytes / runtime->channels; info->first = info->channel * size * 8; info->step = width; break; } default: snd_BUG(); break; } return 0;}/** * snd_pcm_lib_ioctl - a generic PCM ioctl callback * @substream: the pcm substream instance * @cmd: ioctl command * @arg: ioctl argument * * Processes the generic ioctl commands for PCM. * Can be passed as the ioctl callback for PCM ops. * * Returns zero if successful, or a negative error code on failure. */int snd_pcm_lib_ioctl(struct snd_pcm_substream *substream, unsigned int cmd, void *arg){ switch (cmd) { case SNDRV_PCM_IOCTL1_INFO: return 0; case SNDRV_PCM_IOCTL1_RESET: return snd_pcm_lib_ioctl_reset(substream, arg); case SNDRV_PCM_IOCTL1_CHANNEL_INFO: return snd_pcm_lib_ioctl_channel_info(substream, arg); } return -ENXIO;}EXPORT_SYMBOL(snd_pcm_lib_ioctl);/* * Conditions */static void snd_pcm_system_tick_set(struct snd_pcm_substream *substream, unsigned long ticks){ struct snd_pcm_runtime *runtime = substream->runtime; if (ticks == 0) del_timer(&runtime->tick_timer); else { ticks += (1000000 / HZ) - 1; ticks /= (1000000 / HZ); mod_timer(&runtime->tick_timer, jiffies + ticks); }}/* Temporary alias */void snd_pcm_tick_set(struct snd_pcm_substream *substream, unsigned long ticks){ snd_pcm_system_tick_set(substream, ticks);}void snd_pcm_tick_prepare(struct snd_pcm_substream *substream){ struct snd_pcm_runtime *runtime = substream->runtime; snd_pcm_uframes_t frames = ULONG_MAX; snd_pcm_uframes_t avail, dist; unsigned int ticks; u_int64_t n; u_int32_t r; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { if (runtime->silence_size >= runtime->boundary) { frames = 1; } else if (runtime->silence_size > 0 && runtime->silence_filled < runtime->buffer_size) { snd_pcm_sframes_t noise_dist; noise_dist = snd_pcm_playback_hw_avail(runtime) + runtime->silence_filled; if (noise_dist > (snd_pcm_sframes_t)runtime->silence_threshold) frames = noise_dist - runtime->silence_threshold; } avail = snd_pcm_playback_avail(runtime); } else { avail = snd_pcm_capture_avail(runtime); } if (avail < runtime->control->avail_min) { snd_pcm_sframes_t n = runtime->control->avail_min - avail; if (n > 0 && frames > (snd_pcm_uframes_t)n) frames = n; } if (avail < runtime->buffer_size) { snd_pcm_sframes_t n = runtime->buffer_size - avail; if (n > 0 && frames > (snd_pcm_uframes_t)n) frames = n; } if (frames == ULONG_MAX) { snd_pcm_tick_set(substream, 0); return; } dist = runtime->status->hw_ptr - runtime->hw_ptr_base; /* Distance to next interrupt */ dist = runtime->period_size - dist % runtime->period_size; if (dist <= frames) { snd_pcm_tick_set(substream, 0); return; } /* the base time is us */ n = frames; n *= 1000000; div64_32(&n, runtime->tick_time * runtime->rate, &r); ticks = n + (r > 0 ? 1 : 0); if (ticks < runtime->sleep_min) ticks = runtime->sleep_min; snd_pcm_tick_set(substream, (unsigned long) ticks);}void snd_pcm_tick_elapsed(struct snd_pcm_substream *substream){ struct snd_pcm_runtime *runtime; unsigned long flags; snd_assert(substream != NULL, return); runtime = substream->runtime; snd_assert(runtime != NULL, return); snd_pcm_stream_lock_irqsave(substream, flags); if (!snd_pcm_running(substream) || snd_pcm_update_hw_ptr(substream) < 0) goto _end; if (runtime->sleep_min) snd_pcm_tick_prepare(substream); _end: snd_pcm_stream_unlock_irqrestore(substream, flags);}/** * snd_pcm_period_elapsed - update the pcm status for the next period * @substream: the pcm substream instance * * This function is called from the interrupt handler when the * PCM has processed the period size. It will update the current * pointer, set up the tick, wake up sleepers, etc. * * Even if more than one periods have elapsed since the last call, you * have to call this only once. */void snd_pcm_period_elapsed(struct snd_pcm_substream *substream){ struct snd_pcm_runtime *runtime; unsigned long flags; snd_assert(substream != NULL, return); runtime = substream->runtime; snd_assert(runtime != NULL, return); if (runtime->transfer_ack_begin) runtime->transfer_ack_begin(substream); snd_pcm_stream_lock_irqsave(substream, flags); if (!snd_pcm_running(substream) || snd_pcm_update_hw_ptr_interrupt(substream) < 0) goto _end; if (substream->timer_running) snd_timer_interrupt(substream->timer, 1); if (runtime->sleep_min) snd_pcm_tick_prepare(substream); _end: snd_pcm_stream_unlock_irqrestore(substream, flags); if (runtime->transfer_ack_end) runtime->transfer_ack_end(substream); kill_fasync(&runtime->fasync, SIGIO, POLL_IN);}EXPORT_SYMBOL(snd_pcm_period_elapsed);static int snd_pcm_lib_write_transfer(struct snd_pcm_substream *substream, unsigned int hwoff, unsigned long data, unsigned int off, snd_pcm_uframes_t frames){ struct snd_pcm_runtime *runtime = substream->runtime; int err; char __user *buf = (char __user *) data + frames_to_bytes(runtime, off); if (substream->ops->copy) { if ((err = substream->ops->copy(substream, -1, hwoff, buf, frames)) < 0)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -