⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 pcm_oss.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
/* *  Digital Audio (PCM) abstract layer / OSS compatible *  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 * */#if 0#define PLUGIN_DEBUG#endif#if 0#define OSS_DEBUG#endif#include <sound/driver.h>#include <linux/init.h>#include <linux/slab.h>#include <linux/time.h>#include <linux/vmalloc.h>#include <linux/moduleparam.h>#include <linux/string.h>#include <sound/core.h>#include <sound/minors.h>#include <sound/pcm.h>#include <sound/pcm_params.h>#include "pcm_plugin.h"#include <sound/info.h>#include <linux/soundcard.h>#include <sound/initval.h>#define OSS_ALSAEMULVER		_SIOR ('M', 249, int)static int dsp_map[SNDRV_CARDS];static int adsp_map[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = 1};static int nonblock_open = 1;MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>, Abramo Bagnara <abramo@alsa-project.org>");MODULE_DESCRIPTION("PCM OSS emulation for ALSA.");MODULE_LICENSE("GPL");module_param_array(dsp_map, int, NULL, 0444);MODULE_PARM_DESC(dsp_map, "PCM device number assigned to 1st OSS device.");module_param_array(adsp_map, int, NULL, 0444);MODULE_PARM_DESC(adsp_map, "PCM device number assigned to 2nd OSS device.");module_param(nonblock_open, bool, 0644);MODULE_PARM_DESC(nonblock_open, "Don't block opening busy PCM devices.");MODULE_ALIAS_SNDRV_MINOR(SNDRV_MINOR_OSS_PCM);MODULE_ALIAS_SNDRV_MINOR(SNDRV_MINOR_OSS_PCM1);extern int snd_mixer_oss_ioctl_card(struct snd_card *card, unsigned int cmd, unsigned long arg);static int snd_pcm_oss_get_rate(struct snd_pcm_oss_file *pcm_oss_file);static int snd_pcm_oss_get_channels(struct snd_pcm_oss_file *pcm_oss_file);static int snd_pcm_oss_get_format(struct snd_pcm_oss_file *pcm_oss_file);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);}/* * helper functions to process hw_params */static int snd_interval_refine_min(struct snd_interval *i, unsigned int min, int openmin){	int changed = 0;	if (i->min < min) {		i->min = min;		i->openmin = openmin;		changed = 1;	} else if (i->min == min && !i->openmin && openmin) {		i->openmin = 1;		changed = 1;	}	if (i->integer) {		if (i->openmin) {			i->min++;			i->openmin = 0;		}	}	if (snd_interval_checkempty(i)) {		snd_interval_none(i);		return -EINVAL;	}	return changed;}static int snd_interval_refine_max(struct snd_interval *i, unsigned int max, int openmax){	int changed = 0;	if (i->max > max) {		i->max = max;		i->openmax = openmax;		changed = 1;	} else if (i->max == max && !i->openmax && openmax) {		i->openmax = 1;		changed = 1;	}	if (i->integer) {		if (i->openmax) {			i->max--;			i->openmax = 0;		}	}	if (snd_interval_checkempty(i)) {		snd_interval_none(i);		return -EINVAL;	}	return changed;}static int snd_interval_refine_set(struct snd_interval *i, unsigned int val){	struct snd_interval t;	t.empty = 0;	t.min = t.max = val;	t.openmin = t.openmax = 0;	t.integer = 1;	return snd_interval_refine(i, &t);}/** * snd_pcm_hw_param_value_min * @params: the hw_params instance * @var: parameter to retrieve * @dir: pointer to the direction (-1,0,1) or NULL * * Return the minimum value for field PAR. */static unsigned intsnd_pcm_hw_param_value_min(const struct snd_pcm_hw_params *params,			   snd_pcm_hw_param_t var, int *dir){	if (hw_is_mask(var)) {		if (dir)			*dir = 0;		return snd_mask_min(hw_param_mask_c(params, var));	}	if (hw_is_interval(var)) {		const struct snd_interval *i = hw_param_interval_c(params, var);		if (dir)			*dir = i->openmin;		return snd_interval_min(i);	}	return -EINVAL;}/** * snd_pcm_hw_param_value_max * @params: the hw_params instance * @var: parameter to retrieve * @dir: pointer to the direction (-1,0,1) or NULL * * Return the maximum value for field PAR. */static unsigned intsnd_pcm_hw_param_value_max(const struct snd_pcm_hw_params *params,			   snd_pcm_hw_param_t var, int *dir){	if (hw_is_mask(var)) {		if (dir)			*dir = 0;		return snd_mask_max(hw_param_mask_c(params, var));	}	if (hw_is_interval(var)) {		const struct snd_interval *i = hw_param_interval_c(params, var);		if (dir)			*dir = - (int) i->openmax;		return snd_interval_max(i);	}	return -EINVAL;}static int _snd_pcm_hw_param_mask(struct snd_pcm_hw_params *params,				  snd_pcm_hw_param_t var,				  const struct snd_mask *val){	int changed;	changed = snd_mask_refine(hw_param_mask(params, var), val);	if (changed) {		params->cmask |= 1 << var;		params->rmask |= 1 << var;	}	return changed;}static int snd_pcm_hw_param_mask(struct snd_pcm_substream *pcm,				 struct snd_pcm_hw_params *params,				 snd_pcm_hw_param_t var,				 const struct snd_mask *val){	int changed = _snd_pcm_hw_param_mask(params, var, val);	if (changed < 0)		return changed;	if (params->rmask) {		int err = snd_pcm_hw_refine(pcm, params);		if (err < 0)			return err;	}	return 0;}static int _snd_pcm_hw_param_min(struct snd_pcm_hw_params *params,				 snd_pcm_hw_param_t var, unsigned int val,				 int dir){	int changed;	int open = 0;	if (dir) {		if (dir > 0) {			open = 1;		} else if (dir < 0) {			if (val > 0) {				open = 1;				val--;			}		}	}	if (hw_is_mask(var))		changed = snd_mask_refine_min(hw_param_mask(params, var),					      val + !!open);	else if (hw_is_interval(var))		changed = snd_interval_refine_min(hw_param_interval(params, var),						  val, open);	else		return -EINVAL;	if (changed) {		params->cmask |= 1 << var;		params->rmask |= 1 << var;	}	return changed;}/** * snd_pcm_hw_param_min * @pcm: PCM instance * @params: the hw_params instance * @var: parameter to retrieve * @val: minimal value * @dir: pointer to the direction (-1,0,1) or NULL * * Inside configuration space defined by PARAMS remove from PAR all  * values < VAL. Reduce configuration space accordingly. * Return new minimum or -EINVAL if the configuration space is empty */static int snd_pcm_hw_param_min(struct snd_pcm_substream *pcm,				struct snd_pcm_hw_params *params,				snd_pcm_hw_param_t var, unsigned int val,				int *dir){	int changed = _snd_pcm_hw_param_min(params, var, val, dir ? *dir : 0);	if (changed < 0)		return changed;	if (params->rmask) {		int err = snd_pcm_hw_refine(pcm, params);		if (err < 0)			return err;	}	return snd_pcm_hw_param_value_min(params, var, dir);}static int _snd_pcm_hw_param_max(struct snd_pcm_hw_params *params,				 snd_pcm_hw_param_t var, unsigned int val,				 int dir){	int changed;	int open = 0;	if (dir) {		if (dir < 0) {			open = 1;		} else if (dir > 0) {			open = 1;			val++;		}	}	if (hw_is_mask(var)) {		if (val == 0 && open) {			snd_mask_none(hw_param_mask(params, var));			changed = -EINVAL;		} else			changed = snd_mask_refine_max(hw_param_mask(params, var),						      val - !!open);	} else if (hw_is_interval(var))		changed = snd_interval_refine_max(hw_param_interval(params, var),						  val, open);	else		return -EINVAL;	if (changed) {		params->cmask |= 1 << var;		params->rmask |= 1 << var;	}	return changed;}/** * snd_pcm_hw_param_max * @pcm: PCM instance * @params: the hw_params instance * @var: parameter to retrieve * @val: maximal value * @dir: pointer to the direction (-1,0,1) or NULL * * Inside configuration space defined by PARAMS remove from PAR all  *  values >= VAL + 1. Reduce configuration space accordingly. *  Return new maximum or -EINVAL if the configuration space is empty */static int snd_pcm_hw_param_max(struct snd_pcm_substream *pcm,				struct snd_pcm_hw_params *params,				snd_pcm_hw_param_t var, unsigned int val,				int *dir){	int changed = _snd_pcm_hw_param_max(params, var, val, dir ? *dir : 0);	if (changed < 0)		return changed;	if (params->rmask) {		int err = snd_pcm_hw_refine(pcm, params);		if (err < 0)			return err;	}	return snd_pcm_hw_param_value_max(params, var, dir);}static int boundary_sub(int a, int adir,			int b, int bdir,			int *c, int *cdir){	adir = adir < 0 ? -1 : (adir > 0 ? 1 : 0);	bdir = bdir < 0 ? -1 : (bdir > 0 ? 1 : 0);	*c = a - b;	*cdir = adir - bdir;	if (*cdir == -2) {		(*c)--;	} else if (*cdir == 2) {		(*c)++;	}	return 0;}static int boundary_lt(unsigned int a, int adir,		       unsigned int b, int bdir){	if (adir < 0) {		a--;		adir = 1;	} else if (adir > 0)		adir = 1;	if (bdir < 0) {		b--;		bdir = 1;	} else if (bdir > 0)		bdir = 1;	return a < b || (a == b && adir < bdir);}/* Return 1 if min is nearer to best than max */static int boundary_nearer(int min, int mindir,			   int best, int bestdir,			   int max, int maxdir){	int dmin, dmindir;	int dmax, dmaxdir;	boundary_sub(best, bestdir, min, mindir, &dmin, &dmindir);	boundary_sub(max, maxdir, best, bestdir, &dmax, &dmaxdir);	return boundary_lt(dmin, dmindir, dmax, dmaxdir);}/** * snd_pcm_hw_param_near * @pcm: PCM instance * @params: the hw_params instance * @var: parameter to retrieve * @best: value to set * @dir: pointer to the direction (-1,0,1) or NULL * * Inside configuration space defined by PARAMS set PAR to the available value * nearest to VAL. Reduce configuration space accordingly. * This function cannot be called for SNDRV_PCM_HW_PARAM_ACCESS, * SNDRV_PCM_HW_PARAM_FORMAT, SNDRV_PCM_HW_PARAM_SUBFORMAT. * Return the value found.  */static int snd_pcm_hw_param_near(struct snd_pcm_substream *pcm,				 struct snd_pcm_hw_params *params,				 snd_pcm_hw_param_t var, unsigned int best,				 int *dir){	struct snd_pcm_hw_params *save = NULL;	int v;	unsigned int saved_min;	int last = 0;	int min, max;	int mindir, maxdir;	int valdir = dir ? *dir : 0;	/* FIXME */	if (best > INT_MAX)		best = INT_MAX;	min = max = best;	mindir = maxdir = valdir;	if (maxdir > 0)		maxdir = 0;	else if (maxdir == 0)		maxdir = -1;	else {		maxdir = 1;		max--;	}	save = kmalloc(sizeof(*save), GFP_KERNEL);	if (save == NULL)		return -ENOMEM;	*save = *params;	saved_min = min;	min = snd_pcm_hw_param_min(pcm, params, var, min, &mindir);	if (min >= 0) {		struct snd_pcm_hw_params *params1;		if (max < 0)			goto _end;		if ((unsigned int)min == saved_min && mindir == valdir)			goto _end;		params1 = kmalloc(sizeof(*params1), GFP_KERNEL);		if (params1 == NULL) {			kfree(save);			return -ENOMEM;		}		*params1 = *save;		max = snd_pcm_hw_param_max(pcm, params1, var, max, &maxdir);		if (max < 0) {			kfree(params1);			goto _end;		}		if (boundary_nearer(max, maxdir, best, valdir, min, mindir)) {			*params = *params1;			last = 1;		}		kfree(params1);	} else {		*params = *save;		max = snd_pcm_hw_param_max(pcm, params, var, max, &maxdir);		snd_assert(max >= 0, return -EINVAL);		last = 1;	} _end: 	kfree(save);	if (last)		v = snd_pcm_hw_param_last(pcm, params, var, dir);

⌨️ 快捷键说明

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