pcm_native.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 2,359 行 · 第 1/5 页

C
2,359
字号
/* *  Digital Audio (PCM) abstract layer *  Copyright (c) by Jaroslav Kysela <perex@suse.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/smp_lock.h>#include <linux/file.h>#include <linux/slab.h>#include <linux/time.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 sndrv_pcm_hw_params_old {	unsigned int flags;	unsigned int masks[SNDRV_PCM_HW_PARAM_SUBFORMAT -			   SNDRV_PCM_HW_PARAM_ACCESS + 1];	struct sndrv_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;	sndrv_pcm_uframes_t fifo_size;	unsigned char reserved[64];};#define SNDRV_PCM_IOCTL_HW_REFINE_OLD _IOWR('A', 0x10, struct sndrv_pcm_hw_params_old)#define SNDRV_PCM_IOCTL_HW_PARAMS_OLD _IOWR('A', 0x11, struct sndrv_pcm_hw_params_old)static int snd_pcm_hw_refine_old_user(snd_pcm_substream_t * substream, struct sndrv_pcm_hw_params_old __user * _oparams);static int snd_pcm_hw_params_old_user(snd_pcm_substream_t * substream, struct sndrv_pcm_hw_params_old __user * _oparams);/* * */rwlock_t snd_pcm_link_rwlock = RW_LOCK_UNLOCKED;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(snd_pcm_substream_t * substream, snd_pcm_info_t *info){	snd_pcm_runtime_t * runtime;	snd_pcm_t *pcm = substream->pcm;	snd_pcm_str_t *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(snd_pcm_substream_t * substream, snd_pcm_info_t __user * _info){	snd_pcm_info_t info;	int err = snd_pcm_info(substream, &info);	if (copy_to_user(_info, &info, sizeof(info)))		return -EFAULT;	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(snd_pcm_substream_t *substream, 		      snd_pcm_hw_params_t *params){	unsigned int k;	snd_pcm_hardware_t *hw;	snd_interval_t *i = NULL;	snd_mask_t *m = NULL;	snd_pcm_hw_constraints_t *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++) {			snd_pcm_hw_rule_t *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;}static int snd_pcm_hw_refine_user(snd_pcm_substream_t * substream, snd_pcm_hw_params_t __user * _params){	snd_pcm_hw_params_t *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 snd_pcm_hw_params(snd_pcm_substream_t *substream,			     snd_pcm_hw_params_t *params){	snd_pcm_runtime_t *runtime;	int err;	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(&runtime->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;	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(snd_pcm_substream_t * substream, snd_pcm_hw_params_t __user * _params){	snd_pcm_hw_params_t *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_params(substream, params);	if (copy_to_user(_params, params, sizeof(*params))) {		if (!err)			err = -EFAULT;	}out:	kfree(params);	return err;}static int snd_pcm_hw_free(snd_pcm_substream_t * substream){	snd_pcm_runtime_t *runtime;	int result;	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_SETUP:	case SNDRV_PCM_STATE_PREPARED:		break;	default:		snd_pcm_stream_unlock_irq(substream);		return -EBADFD;	}	snd_pcm_stream_unlock_irq(substream);	if (atomic_read(&runtime->mmap_count))		return -EBADFD;	if (substream->ops->hw_free == NULL) {		runtime->status->state = SNDRV_PCM_STATE_OPEN;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?