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

📄 echoaudio.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 4 页
字号:
/* *  ALSA driver for Echoaudio soundcards. *  Copyright (C) 2003-2004 Giuliano Pochini <pochini@shiny.it> * *  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; version 2 of the License. * *  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. */MODULE_AUTHOR("Giuliano Pochini <pochini@shiny.it>");MODULE_LICENSE("GPL v2");MODULE_DESCRIPTION("Echoaudio " ECHOCARD_NAME " soundcards driver");MODULE_SUPPORTED_DEVICE("{{Echoaudio," ECHOCARD_NAME "}}");MODULE_DEVICE_TABLE(pci, snd_echo_ids);static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;module_param_array(index, int, NULL, 0444);MODULE_PARM_DESC(index, "Index value for " ECHOCARD_NAME " soundcard.");module_param_array(id, charp, NULL, 0444);MODULE_PARM_DESC(id, "ID string for " ECHOCARD_NAME " soundcard.");module_param_array(enable, bool, NULL, 0444);MODULE_PARM_DESC(enable, "Enable " ECHOCARD_NAME " soundcard.");static unsigned int channels_list[10] = {1, 2, 4, 6, 8, 10, 12, 14, 16, 999999};static const DECLARE_TLV_DB_SCALE(db_scale_output_gain, -12800, 100, 1);static int get_firmware(const struct firmware **fw_entry,			const struct firmware *frm, struct echoaudio *chip){	int err;	char name[30];	DE_ACT(("firmware requested: %s\n", frm->data));	snprintf(name, sizeof(name), "ea/%s", frm->data);	if ((err = request_firmware(fw_entry, name, pci_device(chip))) < 0)		snd_printk(KERN_ERR "get_firmware(): Firmware not available (%d)\n", err);	return err;}static void free_firmware(const struct firmware *fw_entry){	release_firmware(fw_entry);	DE_ACT(("firmware released\n"));}/******************************************************************************	PCM interface******************************************************************************/static void audiopipe_free(struct snd_pcm_runtime *runtime){	struct audiopipe *pipe = runtime->private_data;	if (pipe->sgpage.area)		snd_dma_free_pages(&pipe->sgpage);	kfree(pipe);}static int hw_rule_capture_format_by_channels(struct snd_pcm_hw_params *params,					      struct snd_pcm_hw_rule *rule){	struct snd_interval *c = hw_param_interval(params,						   SNDRV_PCM_HW_PARAM_CHANNELS);	struct snd_mask *f = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);	struct snd_mask fmt;	snd_mask_any(&fmt);#ifndef ECHOCARD_HAS_STEREO_BIG_ENDIAN32	/* >=2 channels cannot be S32_BE */	if (c->min == 2) {		fmt.bits[0] &= ~SNDRV_PCM_FMTBIT_S32_BE;		return snd_mask_refine(f, &fmt);	}#endif	/* > 2 channels cannot be U8 and S32_BE */	if (c->min > 2) {		fmt.bits[0] &= ~(SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_BE);		return snd_mask_refine(f, &fmt);	}	/* Mono is ok with any format */	return 0;}static int hw_rule_capture_channels_by_format(struct snd_pcm_hw_params *params,					      struct snd_pcm_hw_rule *rule){	struct snd_interval *c = hw_param_interval(params,						   SNDRV_PCM_HW_PARAM_CHANNELS);	struct snd_mask *f = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);	struct snd_interval ch;	snd_interval_any(&ch);	/* S32_BE is mono (and stereo) only */	if (f->bits[0] == SNDRV_PCM_FMTBIT_S32_BE) {		ch.min = 1;#ifdef ECHOCARD_HAS_STEREO_BIG_ENDIAN32		ch.max = 2;#else		ch.max = 1;#endif		ch.integer = 1;		return snd_interval_refine(c, &ch);	}	/* U8 can be only mono or stereo */	if (f->bits[0] == SNDRV_PCM_FMTBIT_U8) {		ch.min = 1;		ch.max = 2;		ch.integer = 1;		return snd_interval_refine(c, &ch);	}	/* S16_LE, S24_3LE and S32_LE support any number of channels. */	return 0;}static int hw_rule_playback_format_by_channels(struct snd_pcm_hw_params *params,					       struct snd_pcm_hw_rule *rule){	struct snd_interval *c = hw_param_interval(params,						   SNDRV_PCM_HW_PARAM_CHANNELS);	struct snd_mask *f = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);	struct snd_mask fmt;	u64 fmask;	snd_mask_any(&fmt);	fmask = fmt.bits[0] + ((u64)fmt.bits[1] << 32);	/* >2 channels must be S16_LE, S24_3LE or S32_LE */	if (c->min > 2) {		fmask &= SNDRV_PCM_FMTBIT_S16_LE |			 SNDRV_PCM_FMTBIT_S24_3LE |			 SNDRV_PCM_FMTBIT_S32_LE;	/* 1 channel must be S32_BE or S32_LE */	} else if (c->max == 1)		fmask &= SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE;#ifndef ECHOCARD_HAS_STEREO_BIG_ENDIAN32	/* 2 channels cannot be S32_BE */	else if (c->min == 2 && c->max == 2)		fmask &= ~SNDRV_PCM_FMTBIT_S32_BE;#endif	else		return 0;	fmt.bits[0] &= (u32)fmask;	fmt.bits[1] &= (u32)(fmask >> 32);	return snd_mask_refine(f, &fmt);}static int hw_rule_playback_channels_by_format(struct snd_pcm_hw_params *params,					       struct snd_pcm_hw_rule *rule){	struct snd_interval *c = hw_param_interval(params,						   SNDRV_PCM_HW_PARAM_CHANNELS);	struct snd_mask *f = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);	struct snd_interval ch;	u64 fmask;	snd_interval_any(&ch);	ch.integer = 1;	fmask = f->bits[0] + ((u64)f->bits[1] << 32);	/* S32_BE is mono (and stereo) only */	if (fmask == SNDRV_PCM_FMTBIT_S32_BE) {		ch.min = 1;#ifdef ECHOCARD_HAS_STEREO_BIG_ENDIAN32		ch.max = 2;#else		ch.max = 1;#endif	/* U8 is stereo only */	} else if (fmask == SNDRV_PCM_FMTBIT_U8)		ch.min = ch.max = 2;	/* S16_LE and S24_3LE must be at least stereo */	else if (!(fmask & ~(SNDRV_PCM_FMTBIT_S16_LE |			       SNDRV_PCM_FMTBIT_S24_3LE)))		ch.min = 2;	else		return 0;	return snd_interval_refine(c, &ch);}/* Since the sample rate is a global setting, do allow the user to change thesample rate only if there is only one pcm device open. */static int hw_rule_sample_rate(struct snd_pcm_hw_params *params,			       struct snd_pcm_hw_rule *rule){	struct snd_interval *rate = hw_param_interval(params,						      SNDRV_PCM_HW_PARAM_RATE);	struct echoaudio *chip = rule->private;	struct snd_interval fixed;	if (!chip->can_set_rate) {		snd_interval_any(&fixed);		fixed.min = fixed.max = chip->sample_rate;		return snd_interval_refine(rate, &fixed);	}	return 0;}static int pcm_open(struct snd_pcm_substream *substream,		    signed char max_channels){	struct echoaudio *chip;	struct snd_pcm_runtime *runtime;	struct audiopipe *pipe;	int err, i;	if (max_channels <= 0)		return -EAGAIN;	chip = snd_pcm_substream_chip(substream);	runtime = substream->runtime;	pipe = kzalloc(sizeof(struct audiopipe), GFP_KERNEL);	if (!pipe)		return -ENOMEM;	pipe->index = -1;		/* Not configured yet */	/* Set up hw capabilities and contraints */	memcpy(&pipe->hw, &pcm_hardware_skel, sizeof(struct snd_pcm_hardware));	DE_HWP(("max_channels=%d\n", max_channels));	pipe->constr.list = channels_list;	pipe->constr.mask = 0;	for (i = 0; channels_list[i] <= max_channels; i++);	pipe->constr.count = i;	if (pipe->hw.channels_max > max_channels)		pipe->hw.channels_max = max_channels;	if (chip->digital_mode == DIGITAL_MODE_ADAT) {		pipe->hw.rate_max = 48000;		pipe->hw.rates &= SNDRV_PCM_RATE_8000_48000;	}	runtime->hw = pipe->hw;	runtime->private_data = pipe;	runtime->private_free = audiopipe_free;	snd_pcm_set_sync(substream);	/* Only mono and any even number of channels are allowed */	if ((err = snd_pcm_hw_constraint_list(runtime, 0,					      SNDRV_PCM_HW_PARAM_CHANNELS,					      &pipe->constr)) < 0)		return err;	/* All periods should have the same size */	if ((err = snd_pcm_hw_constraint_integer(runtime,						 SNDRV_PCM_HW_PARAM_PERIODS)) < 0)		return err;	/* The hw accesses memory in chunks 32 frames long and they should be	32-bytes-aligned. It's not a requirement, but it seems that IRQs are	generated with a resolution of 32 frames. Thus we need the following */	if ((err = snd_pcm_hw_constraint_step(runtime, 0,					      SNDRV_PCM_HW_PARAM_PERIOD_SIZE,					      32)) < 0)		return err;	if ((err = snd_pcm_hw_constraint_step(runtime, 0,					      SNDRV_PCM_HW_PARAM_BUFFER_SIZE,					      32)) < 0)		return err;	if ((err = snd_pcm_hw_rule_add(substream->runtime, 0,				       SNDRV_PCM_HW_PARAM_RATE,					hw_rule_sample_rate, chip,				       SNDRV_PCM_HW_PARAM_RATE, -1)) < 0)		return err;	/* Finally allocate a page for the scatter-gather list */	if ((err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,				       snd_dma_pci_data(chip->pci),				       PAGE_SIZE, &pipe->sgpage)) < 0) {		DE_HWP(("s-g list allocation failed\n"));		return err;	}	return 0;}static int pcm_analog_in_open(struct snd_pcm_substream *substream){	struct echoaudio *chip = snd_pcm_substream_chip(substream);	int err;	DE_ACT(("pcm_analog_in_open\n"));	if ((err = pcm_open(substream, num_analog_busses_in(chip) -			    substream->number)) < 0)		return err;	if ((err = snd_pcm_hw_rule_add(substream->runtime, 0,				       SNDRV_PCM_HW_PARAM_CHANNELS,				       hw_rule_capture_channels_by_format, NULL,				       SNDRV_PCM_HW_PARAM_FORMAT, -1)) < 0)		return err;	if ((err = snd_pcm_hw_rule_add(substream->runtime, 0,				       SNDRV_PCM_HW_PARAM_FORMAT,				       hw_rule_capture_format_by_channels, NULL,				       SNDRV_PCM_HW_PARAM_CHANNELS, -1)) < 0)		return err;	atomic_inc(&chip->opencount);	if (atomic_read(&chip->opencount) > 1 && chip->rate_set)		chip->can_set_rate=0;	DE_HWP(("pcm_analog_in_open  cs=%d  oc=%d  r=%d\n",		chip->can_set_rate, atomic_read(&chip->opencount),		chip->sample_rate));	return 0;}static int pcm_analog_out_open(struct snd_pcm_substream *substream){	struct echoaudio *chip = snd_pcm_substream_chip(substream);	int max_channels, err;#ifdef ECHOCARD_HAS_VMIXER	max_channels = num_pipes_out(chip);#else	max_channels = num_analog_busses_out(chip);#endif	DE_ACT(("pcm_analog_out_open\n"));	if ((err = pcm_open(substream, max_channels - substream->number)) < 0)		return err;	if ((err = snd_pcm_hw_rule_add(substream->runtime, 0,				       SNDRV_PCM_HW_PARAM_CHANNELS,				       hw_rule_playback_channels_by_format,				       NULL,				       SNDRV_PCM_HW_PARAM_FORMAT, -1)) < 0)		return err;	if ((err = snd_pcm_hw_rule_add(substream->runtime, 0,				       SNDRV_PCM_HW_PARAM_FORMAT,				       hw_rule_playback_format_by_channels,				       NULL,				       SNDRV_PCM_HW_PARAM_CHANNELS, -1)) < 0)		return err;	atomic_inc(&chip->opencount);	if (atomic_read(&chip->opencount) > 1 && chip->rate_set)		chip->can_set_rate=0;	DE_HWP(("pcm_analog_out_open  cs=%d  oc=%d  r=%d\n",		chip->can_set_rate, atomic_read(&chip->opencount),		chip->sample_rate));	return 0;}#ifdef ECHOCARD_HAS_DIGITAL_IOstatic int pcm_digital_in_open(struct snd_pcm_substream *substream){	struct echoaudio *chip = snd_pcm_substream_chip(substream);	int err, max_channels;	DE_ACT(("pcm_digital_in_open\n"));	max_channels = num_digital_busses_in(chip) - substream->number;	down(&chip->mode_mutex);	if (chip->digital_mode == DIGITAL_MODE_ADAT)		err = pcm_open(substream, max_channels);	else	/* If the card has ADAT, subtract the 6 channels		 * that S/PDIF doesn't have		 */		err = pcm_open(substream, max_channels - ECHOCARD_HAS_ADAT);	if (err < 0)		goto din_exit;	if ((err = snd_pcm_hw_rule_add(substream->runtime, 0,				       SNDRV_PCM_HW_PARAM_CHANNELS,				       hw_rule_capture_channels_by_format, NULL,				       SNDRV_PCM_HW_PARAM_FORMAT, -1)) < 0)		goto din_exit;	if ((err = snd_pcm_hw_rule_add(substream->runtime, 0,				       SNDRV_PCM_HW_PARAM_FORMAT,				       hw_rule_capture_format_by_channels, NULL,				       SNDRV_PCM_HW_PARAM_CHANNELS, -1)) < 0)		goto din_exit;	atomic_inc(&chip->opencount);	if (atomic_read(&chip->opencount) > 1 && chip->rate_set)		chip->can_set_rate=0;din_exit:	up(&chip->mode_mutex);	return err;}#ifndef ECHOCARD_HAS_VMIXER	/* See the note in snd_echo_new_pcm() */static int pcm_digital_out_open(struct snd_pcm_substream *substream){	struct echoaudio *chip = snd_pcm_substream_chip(substream);	int err, max_channels;	DE_ACT(("pcm_digital_out_open\n"));	max_channels = num_digital_busses_out(chip) - substream->number;	down(&chip->mode_mutex);	if (chip->digital_mode == DIGITAL_MODE_ADAT)		err = pcm_open(substream, max_channels);	else	/* If the card has ADAT, subtract the 6 channels		 * that S/PDIF doesn't have		 */		err = pcm_open(substream, max_channels - ECHOCARD_HAS_ADAT);	if (err < 0)		goto dout_exit;	if ((err = snd_pcm_hw_rule_add(substream->runtime, 0,				       SNDRV_PCM_HW_PARAM_CHANNELS,				       hw_rule_playback_channels_by_format,				       NULL, SNDRV_PCM_HW_PARAM_FORMAT,				       -1)) < 0)		goto dout_exit;	if ((err = snd_pcm_hw_rule_add(substream->runtime, 0,				       SNDRV_PCM_HW_PARAM_FORMAT,				       hw_rule_playback_format_by_channels,				       NULL, SNDRV_PCM_HW_PARAM_CHANNELS,				       -1)) < 0)		goto dout_exit;	atomic_inc(&chip->opencount);	if (atomic_read(&chip->opencount) > 1 && chip->rate_set)		chip->can_set_rate=0;dout_exit:	up(&chip->mode_mutex);	return err;}#endif /* !ECHOCARD_HAS_VMIXER */#endif /* ECHOCARD_HAS_DIGITAL_IO */static int pcm_close(struct snd_pcm_substream *substream){	struct echoaudio *chip = snd_pcm_substream_chip(substream);	int oc;	/* Nothing to do here. Audio is already off and pipe will be	 * freed by its callback	 */	DE_ACT(("pcm_close\n"));	atomic_dec(&chip->opencount);	oc = atomic_read(&chip->opencount);	DE_ACT(("pcm_close  oc=%d  cs=%d  rs=%d\n", oc,		chip->can_set_rate, chip->rate_set));	if (oc < 2)		chip->can_set_rate = 1;	if (oc == 0)		chip->rate_set = 0;	DE_ACT(("pcm_close2 oc=%d  cs=%d  rs=%d\n", oc,		chip->can_set_rate,chip->rate_set));	return 0;}/* Channel allocation and scatter-gather list setup */static int init_engine(struct snd_pcm_substream *substream,		       struct snd_pcm_hw_params *hw_params,		       int pipe_index, int interleave){	struct echoaudio *chip;	int err, per, rest, page, edge, offs;	struct snd_sg_buf *sgbuf;	struct audiopipe *pipe;	chip = snd_pcm_substream_chip(substream);	pipe = (struct audiopipe *) substream->runtime->private_data;	/* Sets up che hardware. If it's already initialized, reset and	 * redo with the new parameters	 */	spin_lock_irq(&chip->lock);	if (pipe->index >= 0) {		DE_HWP(("hwp_ie free(%d)\n", pipe->index));		err = free_pipes(chip, pipe);		snd_assert(!err);		chip->substream[pipe->index] = NULL;	}	err = allocate_pipes(chip, pipe, pipe_index, interleave);	if (err < 0) {		spin_unlock_irq(&chip->lock);		DE_ACT((KERN_NOTICE "allocate_pipes(%d) err=%d\n",			pipe_index, err));		return err;	}	spin_unlock_irq(&chip->lock);	DE_ACT((KERN_NOTICE "allocate_pipes()=%d\n", pipe_index));	DE_HWP(("pcm_hw_params (bufsize=%dB periods=%d persize=%dB)\n",		params_buffer_bytes(hw_params), params_periods(hw_params),		params_period_bytes(hw_params)));	err = snd_pcm_lib_malloc_pages(substream,				       params_buffer_bytes(hw_params));	if (err < 0) {		snd_printk(KERN_ERR "malloc_pages err=%d\n", err);		spin_lock_irq(&chip->lock);		free_pipes(chip, pipe);		spin_unlock_irq(&chip->lock);		pipe->index = -1;		return err;	}	sgbuf = snd_pcm_substream_sgbuf(substream);	DE_HWP(("pcm_hw_params table size=%d pages=%d\n",		sgbuf->size, sgbuf->pages));	sglist_init(chip, pipe);	edge = PAGE_SIZE;	for (offs = page = per = 0; offs < params_buffer_bytes(hw_params);	     per++) {		rest = params_period_bytes(hw_params);		if (offs + rest > params_buffer_bytes(hw_params))			rest = params_buffer_bytes(hw_params) - offs;		while (rest) {			if (rest <= edge - offs) {				sglist_add_mapping(chip, pipe,

⌨️ 快捷键说明

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