📄 pcm_native.c
字号:
/* * Digital Audio (PCM) abstract layer * Copyright (c) by Jaroslav Kysela <perex@perex.cz> * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */#include <sound/driver.h>#include <linux/mm.h>#include <linux/file.h>#include <linux/slab.h>#include <linux/time.h>#include <linux/latency.h>#include <linux/uio.h>#include <sound/core.h>#include <sound/control.h>#include <sound/info.h>#include <sound/pcm.h>#include <sound/pcm_params.h>#include <sound/timer.h>#include <sound/minors.h>#include <asm/io.h>/* * Compatibility */struct snd_pcm_hw_params_old { unsigned int flags; unsigned int masks[SNDRV_PCM_HW_PARAM_SUBFORMAT - SNDRV_PCM_HW_PARAM_ACCESS + 1]; struct snd_interval intervals[SNDRV_PCM_HW_PARAM_TICK_TIME - SNDRV_PCM_HW_PARAM_SAMPLE_BITS + 1]; unsigned int rmask; unsigned int cmask; unsigned int info; unsigned int msbits; unsigned int rate_num; unsigned int rate_den; snd_pcm_uframes_t fifo_size; unsigned char reserved[64];};#ifdef CONFIG_SND_SUPPORT_OLD_API#define SNDRV_PCM_IOCTL_HW_REFINE_OLD _IOWR('A', 0x10, struct snd_pcm_hw_params_old)#define SNDRV_PCM_IOCTL_HW_PARAMS_OLD _IOWR('A', 0x11, struct snd_pcm_hw_params_old)static int snd_pcm_hw_refine_old_user(struct snd_pcm_substream *substream, struct snd_pcm_hw_params_old __user * _oparams);static int snd_pcm_hw_params_old_user(struct snd_pcm_substream *substream, struct snd_pcm_hw_params_old __user * _oparams);#endifstatic int snd_pcm_open(struct file *file, struct snd_pcm *pcm, int stream);/* * */DEFINE_RWLOCK(snd_pcm_link_rwlock);EXPORT_SYMBOL(snd_pcm_link_rwlock);static DECLARE_RWSEM(snd_pcm_link_rwsem);static inline mm_segment_t snd_enter_user(void){ mm_segment_t fs = get_fs(); set_fs(get_ds()); return fs;}static inline void snd_leave_user(mm_segment_t fs){ set_fs(fs);}int snd_pcm_info(struct snd_pcm_substream *substream, struct snd_pcm_info *info){ struct snd_pcm_runtime *runtime; struct snd_pcm *pcm = substream->pcm; struct snd_pcm_str *pstr = substream->pstr; snd_assert(substream != NULL, return -ENXIO); memset(info, 0, sizeof(*info)); info->card = pcm->card->number; info->device = pcm->device; info->stream = substream->stream; info->subdevice = substream->number; strlcpy(info->id, pcm->id, sizeof(info->id)); strlcpy(info->name, pcm->name, sizeof(info->name)); info->dev_class = pcm->dev_class; info->dev_subclass = pcm->dev_subclass; info->subdevices_count = pstr->substream_count; info->subdevices_avail = pstr->substream_count - pstr->substream_opened; strlcpy(info->subname, substream->name, sizeof(info->subname)); runtime = substream->runtime; /* AB: FIXME!!! This is definitely nonsense */ if (runtime) { info->sync = runtime->sync; substream->ops->ioctl(substream, SNDRV_PCM_IOCTL1_INFO, info); } return 0;}int snd_pcm_info_user(struct snd_pcm_substream *substream, struct snd_pcm_info __user * _info){ struct snd_pcm_info *info; int err; info = kmalloc(sizeof(*info), GFP_KERNEL); if (! info) return -ENOMEM; err = snd_pcm_info(substream, info); if (err >= 0) { if (copy_to_user(_info, info, sizeof(*info))) err = -EFAULT; } kfree(info); return err;}#undef RULES_DEBUG#ifdef RULES_DEBUG#define HW_PARAM(v) [SNDRV_PCM_HW_PARAM_##v] = #vchar *snd_pcm_hw_param_names[] = { HW_PARAM(ACCESS), HW_PARAM(FORMAT), HW_PARAM(SUBFORMAT), HW_PARAM(SAMPLE_BITS), HW_PARAM(FRAME_BITS), HW_PARAM(CHANNELS), HW_PARAM(RATE), HW_PARAM(PERIOD_TIME), HW_PARAM(PERIOD_SIZE), HW_PARAM(PERIOD_BYTES), HW_PARAM(PERIODS), HW_PARAM(BUFFER_TIME), HW_PARAM(BUFFER_SIZE), HW_PARAM(BUFFER_BYTES), HW_PARAM(TICK_TIME),};#endifint snd_pcm_hw_refine(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params){ unsigned int k; struct snd_pcm_hardware *hw; struct snd_interval *i = NULL; struct snd_mask *m = NULL; struct snd_pcm_hw_constraints *constrs = &substream->runtime->hw_constraints; unsigned int rstamps[constrs->rules_num]; unsigned int vstamps[SNDRV_PCM_HW_PARAM_LAST_INTERVAL + 1]; unsigned int stamp = 2; int changed, again; params->info = 0; params->fifo_size = 0; if (params->rmask & (1 << SNDRV_PCM_HW_PARAM_SAMPLE_BITS)) params->msbits = 0; if (params->rmask & (1 << SNDRV_PCM_HW_PARAM_RATE)) { params->rate_num = 0; params->rate_den = 0; } for (k = SNDRV_PCM_HW_PARAM_FIRST_MASK; k <= SNDRV_PCM_HW_PARAM_LAST_MASK; k++) { m = hw_param_mask(params, k); if (snd_mask_empty(m)) return -EINVAL; if (!(params->rmask & (1 << k))) continue;#ifdef RULES_DEBUG printk("%s = ", snd_pcm_hw_param_names[k]); printk("%04x%04x%04x%04x -> ", m->bits[3], m->bits[2], m->bits[1], m->bits[0]);#endif changed = snd_mask_refine(m, constrs_mask(constrs, k));#ifdef RULES_DEBUG printk("%04x%04x%04x%04x\n", m->bits[3], m->bits[2], m->bits[1], m->bits[0]);#endif if (changed) params->cmask |= 1 << k; if (changed < 0) return changed; } for (k = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL; k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++) { i = hw_param_interval(params, k); if (snd_interval_empty(i)) return -EINVAL; if (!(params->rmask & (1 << k))) continue;#ifdef RULES_DEBUG printk("%s = ", snd_pcm_hw_param_names[k]); if (i->empty) printk("empty"); else printk("%c%u %u%c", i->openmin ? '(' : '[', i->min, i->max, i->openmax ? ')' : ']'); printk(" -> ");#endif changed = snd_interval_refine(i, constrs_interval(constrs, k));#ifdef RULES_DEBUG if (i->empty) printk("empty\n"); else printk("%c%u %u%c\n", i->openmin ? '(' : '[', i->min, i->max, i->openmax ? ')' : ']');#endif if (changed) params->cmask |= 1 << k; if (changed < 0) return changed; } for (k = 0; k < constrs->rules_num; k++) rstamps[k] = 0; for (k = 0; k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++) vstamps[k] = (params->rmask & (1 << k)) ? 1 : 0; do { again = 0; for (k = 0; k < constrs->rules_num; k++) { struct snd_pcm_hw_rule *r = &constrs->rules[k]; unsigned int d; int doit = 0; if (r->cond && !(r->cond & params->flags)) continue; for (d = 0; r->deps[d] >= 0; d++) { if (vstamps[r->deps[d]] > rstamps[k]) { doit = 1; break; } } if (!doit) continue;#ifdef RULES_DEBUG printk("Rule %d [%p]: ", k, r->func); if (r->var >= 0) { printk("%s = ", snd_pcm_hw_param_names[r->var]); if (hw_is_mask(r->var)) { m = hw_param_mask(params, r->var); printk("%x", *m->bits); } else { i = hw_param_interval(params, r->var); if (i->empty) printk("empty"); else printk("%c%u %u%c", i->openmin ? '(' : '[', i->min, i->max, i->openmax ? ')' : ']'); } }#endif changed = r->func(params, r);#ifdef RULES_DEBUG if (r->var >= 0) { printk(" -> "); if (hw_is_mask(r->var)) printk("%x", *m->bits); else { if (i->empty) printk("empty"); else printk("%c%u %u%c", i->openmin ? '(' : '[', i->min, i->max, i->openmax ? ')' : ']'); } } printk("\n");#endif rstamps[k] = stamp; if (changed && r->var >= 0) { params->cmask |= (1 << r->var); vstamps[r->var] = stamp; again = 1; } if (changed < 0) return changed; stamp++; } } while (again); if (!params->msbits) { i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS); if (snd_interval_single(i)) params->msbits = snd_interval_value(i); } if (!params->rate_den) { i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); if (snd_interval_single(i)) { params->rate_num = snd_interval_value(i); params->rate_den = 1; } } hw = &substream->runtime->hw; if (!params->info) params->info = hw->info; if (!params->fifo_size) params->fifo_size = hw->fifo_size; params->rmask = 0; return 0;}EXPORT_SYMBOL(snd_pcm_hw_refine);static int snd_pcm_hw_refine_user(struct snd_pcm_substream *substream, struct snd_pcm_hw_params __user * _params){ struct snd_pcm_hw_params *params; int err; params = kmalloc(sizeof(*params), GFP_KERNEL); if (!params) { err = -ENOMEM; goto out; } if (copy_from_user(params, _params, sizeof(*params))) { err = -EFAULT; goto out; } err = snd_pcm_hw_refine(substream, params); if (copy_to_user(_params, params, sizeof(*params))) { if (!err) err = -EFAULT; }out: kfree(params); return err;}static int period_to_usecs(struct snd_pcm_runtime *runtime){ int usecs; if (! runtime->rate) return -1; /* invalid */ /* take 75% of period time as the deadline */ usecs = (750000 / runtime->rate) * runtime->period_size; usecs += ((750000 % runtime->rate) * runtime->period_size) / runtime->rate; return usecs;}static int snd_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params){ struct snd_pcm_runtime *runtime; int err, usecs; unsigned int bits; snd_pcm_uframes_t frames; snd_assert(substream != NULL, return -ENXIO); runtime = substream->runtime; snd_assert(runtime != NULL, return -ENXIO); snd_pcm_stream_lock_irq(substream); switch (runtime->status->state) { case SNDRV_PCM_STATE_OPEN: case SNDRV_PCM_STATE_SETUP: case SNDRV_PCM_STATE_PREPARED: break; default: snd_pcm_stream_unlock_irq(substream); return -EBADFD; } snd_pcm_stream_unlock_irq(substream);#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE) if (!substream->oss.oss)#endif if (atomic_read(&substream->mmap_count)) return -EBADFD; params->rmask = ~0U; err = snd_pcm_hw_refine(substream, params); if (err < 0) goto _error; err = snd_pcm_hw_params_choose(substream, params); if (err < 0) goto _error; if (substream->ops->hw_params != NULL) { err = substream->ops->hw_params(substream, params); if (err < 0) goto _error; } runtime->access = params_access(params); runtime->format = params_format(params); runtime->subformat = params_subformat(params); runtime->channels = params_channels(params); runtime->rate = params_rate(params); runtime->period_size = params_period_size(params); runtime->periods = params_periods(params); runtime->buffer_size = params_buffer_size(params); runtime->tick_time = params_tick_time(params); runtime->info = params->info; runtime->rate_num = params->rate_num; runtime->rate_den = params->rate_den; bits = snd_pcm_format_physical_width(runtime->format); runtime->sample_bits = bits; bits *= runtime->channels; runtime->frame_bits = bits; frames = 1; while (bits % 8 != 0) { bits *= 2; frames *= 2; } runtime->byte_align = bits / 8; runtime->min_align = frames; /* Default sw params */ runtime->tstamp_mode = SNDRV_PCM_TSTAMP_NONE; runtime->period_step = 1; runtime->sleep_min = 0; runtime->control->avail_min = runtime->period_size; runtime->xfer_align = runtime->period_size; runtime->start_threshold = 1; runtime->stop_threshold = runtime->buffer_size; runtime->silence_threshold = 0; runtime->silence_size = 0; runtime->boundary = runtime->buffer_size; while (runtime->boundary * 2 <= LONG_MAX - runtime->buffer_size) runtime->boundary *= 2; snd_pcm_timer_resolution_change(substream); runtime->status->state = SNDRV_PCM_STATE_SETUP; remove_acceptable_latency(substream->latency_id); if ((usecs = period_to_usecs(runtime)) >= 0) set_acceptable_latency(substream->latency_id, usecs); return 0; _error: /* hardware might be unuseable from this time, so we force application to retry to set the correct hardware parameter settings */ runtime->status->state = SNDRV_PCM_STATE_OPEN; if (substream->ops->hw_free != NULL) substream->ops->hw_free(substream); return err;}static int snd_pcm_hw_params_user(struct snd_pcm_substream *substream, struct snd_pcm_hw_params __user * _params){ struct snd_pcm_hw_params *params; int err; params = kmalloc(sizeof(*params), GFP_KERNEL); if (!params) { err = -ENOMEM; goto out;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -