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

📄 echoaudio.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 4 页
字号:
						   snd_sgbuf_get_addr(sgbuf, offs),						   rest);				sglist_add_irq(chip, pipe);				offs += rest;				rest = 0;			} else {				sglist_add_mapping(chip, pipe,						   snd_sgbuf_get_addr(sgbuf, offs),						   edge - offs);				rest -= edge - offs;				offs = edge;			}			if (offs == edge) {				edge += PAGE_SIZE;				page++;			}		}	}	/* Close the ring buffer */	sglist_wrap(chip, pipe);	/* This stuff is used by the irq handler, so it must be	 * initialized before chip->substream	 */	chip->last_period[pipe_index] = 0;	pipe->last_counter = 0;	pipe->position = 0;	smp_wmb();	chip->substream[pipe_index] = substream;	chip->rate_set = 1;	spin_lock_irq(&chip->lock);	set_sample_rate(chip, hw_params->rate_num / hw_params->rate_den);	spin_unlock_irq(&chip->lock);	DE_HWP(("pcm_hw_params ok\n"));	return 0;}static int pcm_analog_in_hw_params(struct snd_pcm_substream *substream,				   struct snd_pcm_hw_params *hw_params){	struct echoaudio *chip = snd_pcm_substream_chip(substream);	return init_engine(substream, hw_params, px_analog_in(chip) +			substream->number, params_channels(hw_params));}static int pcm_analog_out_hw_params(struct snd_pcm_substream *substream,				    struct snd_pcm_hw_params *hw_params){	return init_engine(substream, hw_params, substream->number,			   params_channels(hw_params));}#ifdef ECHOCARD_HAS_DIGITAL_IOstatic int pcm_digital_in_hw_params(struct snd_pcm_substream *substream,				    struct snd_pcm_hw_params *hw_params){	struct echoaudio *chip = snd_pcm_substream_chip(substream);	return init_engine(substream, hw_params, px_digital_in(chip) +			substream->number, params_channels(hw_params));}#ifndef ECHOCARD_HAS_VMIXER	/* See the note in snd_echo_new_pcm() */static int pcm_digital_out_hw_params(struct snd_pcm_substream *substream,				     struct snd_pcm_hw_params *hw_params){	struct echoaudio *chip = snd_pcm_substream_chip(substream);	return init_engine(substream, hw_params, px_digital_out(chip) +			substream->number, params_channels(hw_params));}#endif /* !ECHOCARD_HAS_VMIXER */#endif /* ECHOCARD_HAS_DIGITAL_IO */static int pcm_hw_free(struct snd_pcm_substream *substream){	struct echoaudio *chip;	struct audiopipe *pipe;	chip = snd_pcm_substream_chip(substream);	pipe = (struct audiopipe *) substream->runtime->private_data;	spin_lock_irq(&chip->lock);	if (pipe->index >= 0) {		DE_HWP(("pcm_hw_free(%d)\n", pipe->index));		free_pipes(chip, pipe);		chip->substream[pipe->index] = NULL;		pipe->index = -1;	}	spin_unlock_irq(&chip->lock);	DE_HWP(("pcm_hw_freed\n"));	snd_pcm_lib_free_pages(substream);	return 0;}static int pcm_prepare(struct snd_pcm_substream *substream){	struct echoaudio *chip = snd_pcm_substream_chip(substream);	struct snd_pcm_runtime *runtime = substream->runtime;	struct audioformat format;	int pipe_index = ((struct audiopipe *)runtime->private_data)->index;	DE_HWP(("Prepare rate=%d format=%d channels=%d\n",		runtime->rate, runtime->format, runtime->channels));	format.interleave = runtime->channels;	format.data_are_bigendian = 0;	format.mono_to_stereo = 0;	switch (runtime->format) {	case SNDRV_PCM_FORMAT_U8:		format.bits_per_sample = 8;		break;	case SNDRV_PCM_FORMAT_S16_LE:		format.bits_per_sample = 16;		break;	case SNDRV_PCM_FORMAT_S24_3LE:		format.bits_per_sample = 24;		break;	case SNDRV_PCM_FORMAT_S32_BE:		format.data_are_bigendian = 1;	case SNDRV_PCM_FORMAT_S32_LE:		format.bits_per_sample = 32;		break;	default:		DE_HWP(("Prepare error: unsupported format %d\n",			runtime->format));		return -EINVAL;	}	snd_assert(pipe_index < px_num(chip), return -EINVAL);	snd_assert(is_pipe_allocated(chip, pipe_index), return -EINVAL);	set_audio_format(chip, pipe_index, &format);	return 0;}static int pcm_trigger(struct snd_pcm_substream *substream, int cmd){	struct echoaudio *chip = snd_pcm_substream_chip(substream);	struct snd_pcm_runtime *runtime = substream->runtime;	struct audiopipe *pipe = runtime->private_data;	int i, err;	u32 channelmask = 0;	struct snd_pcm_substream *s;	snd_pcm_group_for_each_entry(s, substream) {		for (i = 0; i < DSP_MAXPIPES; i++) {			if (s == chip->substream[i]) {				channelmask |= 1 << i;				snd_pcm_trigger_done(s, substream);			}		}	}	spin_lock(&chip->lock);	switch (cmd) {	case SNDRV_PCM_TRIGGER_START:	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:		DE_ACT(("pcm_trigger start\n"));		for (i = 0; i < DSP_MAXPIPES; i++) {			if (channelmask & (1 << i)) {				pipe = chip->substream[i]->runtime->private_data;				switch (pipe->state) {				case PIPE_STATE_STOPPED:					chip->last_period[i] = 0;					pipe->last_counter = 0;					pipe->position = 0;					*pipe->dma_counter = 0;				case PIPE_STATE_PAUSED:					pipe->state = PIPE_STATE_STARTED;					break;				case PIPE_STATE_STARTED:					break;				}			}		}		err = start_transport(chip, channelmask,				      chip->pipe_cyclic_mask);		break;	case SNDRV_PCM_TRIGGER_STOP:		DE_ACT(("pcm_trigger stop\n"));		for (i = 0; i < DSP_MAXPIPES; i++) {			if (channelmask & (1 << i)) {				pipe = chip->substream[i]->runtime->private_data;				pipe->state = PIPE_STATE_STOPPED;			}		}		err = stop_transport(chip, channelmask);		break;	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:		DE_ACT(("pcm_trigger pause\n"));		for (i = 0; i < DSP_MAXPIPES; i++) {			if (channelmask & (1 << i)) {				pipe = chip->substream[i]->runtime->private_data;				pipe->state = PIPE_STATE_PAUSED;			}		}		err = pause_transport(chip, channelmask);		break;	default:		err = -EINVAL;	}	spin_unlock(&chip->lock);	return err;}static snd_pcm_uframes_t pcm_pointer(struct snd_pcm_substream *substream){	struct snd_pcm_runtime *runtime = substream->runtime;	struct audiopipe *pipe = runtime->private_data;	size_t cnt, bufsize, pos;	cnt = le32_to_cpu(*pipe->dma_counter);	pipe->position += cnt - pipe->last_counter;	pipe->last_counter = cnt;	bufsize = substream->runtime->buffer_size;	pos = bytes_to_frames(substream->runtime, pipe->position);	while (pos >= bufsize) {		pipe->position -= frames_to_bytes(substream->runtime, bufsize);		pos -= bufsize;	}	return pos;}/* pcm *_ops structures */static struct snd_pcm_ops analog_playback_ops = {	.open = pcm_analog_out_open,	.close = pcm_close,	.ioctl = snd_pcm_lib_ioctl,	.hw_params = pcm_analog_out_hw_params,	.hw_free = pcm_hw_free,	.prepare = pcm_prepare,	.trigger = pcm_trigger,	.pointer = pcm_pointer,	.page = snd_pcm_sgbuf_ops_page,};static struct snd_pcm_ops analog_capture_ops = {	.open = pcm_analog_in_open,	.close = pcm_close,	.ioctl = snd_pcm_lib_ioctl,	.hw_params = pcm_analog_in_hw_params,	.hw_free = pcm_hw_free,	.prepare = pcm_prepare,	.trigger = pcm_trigger,	.pointer = pcm_pointer,	.page = snd_pcm_sgbuf_ops_page,};#ifdef ECHOCARD_HAS_DIGITAL_IO#ifndef ECHOCARD_HAS_VMIXERstatic struct snd_pcm_ops digital_playback_ops = {	.open = pcm_digital_out_open,	.close = pcm_close,	.ioctl = snd_pcm_lib_ioctl,	.hw_params = pcm_digital_out_hw_params,	.hw_free = pcm_hw_free,	.prepare = pcm_prepare,	.trigger = pcm_trigger,	.pointer = pcm_pointer,	.page = snd_pcm_sgbuf_ops_page,};#endif /* !ECHOCARD_HAS_VMIXER */static struct snd_pcm_ops digital_capture_ops = {	.open = pcm_digital_in_open,	.close = pcm_close,	.ioctl = snd_pcm_lib_ioctl,	.hw_params = pcm_digital_in_hw_params,	.hw_free = pcm_hw_free,	.prepare = pcm_prepare,	.trigger = pcm_trigger,	.pointer = pcm_pointer,	.page = snd_pcm_sgbuf_ops_page,};#endif /* ECHOCARD_HAS_DIGITAL_IO *//* Preallocate memory only for the first substream because it's the most * used one */static int snd_echo_preallocate_pages(struct snd_pcm *pcm, struct device *dev){	struct snd_pcm_substream *ss;	int stream, err;	for (stream = 0; stream < 2; stream++)		for (ss = pcm->streams[stream].substream; ss; ss = ss->next) {			err = snd_pcm_lib_preallocate_pages(ss, SNDRV_DMA_TYPE_DEV_SG,							    dev,							    ss->number ? 0 : 128<<10,							    256<<10);			if (err < 0)				return err;		}	return 0;}/*<--snd_echo_probe() */static int __devinit snd_echo_new_pcm(struct echoaudio *chip){	struct snd_pcm *pcm;	int err;#ifdef ECHOCARD_HAS_VMIXER	/* This card has a Vmixer, that is there is no direct mapping from PCM	streams to physical outputs. The user can mix the streams as he wishes	via control interface and it's possible to send any stream to any	output, thus it makes no sense to keep analog and digital outputs	separated */	/* PCM#0 Virtual outputs and analog inputs */	if ((err = snd_pcm_new(chip->card, "PCM", 0, num_pipes_out(chip),				num_analog_busses_in(chip), &pcm)) < 0)		return err;	pcm->private_data = chip;	chip->analog_pcm = pcm;	strcpy(pcm->name, chip->card->shortname);	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &analog_playback_ops);	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &analog_capture_ops);	if ((err = snd_echo_preallocate_pages(pcm, snd_dma_pci_data(chip->pci))) < 0)		return err;	DE_INIT(("Analog PCM ok\n"));#ifdef ECHOCARD_HAS_DIGITAL_IO	/* PCM#1 Digital inputs, no outputs */	if ((err = snd_pcm_new(chip->card, "Digital PCM", 1, 0,			       num_digital_busses_in(chip), &pcm)) < 0)		return err;	pcm->private_data = chip;	chip->digital_pcm = pcm;	strcpy(pcm->name, chip->card->shortname);	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &digital_capture_ops);	if ((err = snd_echo_preallocate_pages(pcm, snd_dma_pci_data(chip->pci))) < 0)		return err;	DE_INIT(("Digital PCM ok\n"));#endif /* ECHOCARD_HAS_DIGITAL_IO */#else /* ECHOCARD_HAS_VMIXER */	/* The card can manage substreams formed by analog and digital channels	at the same time, but I prefer to keep analog and digital channels	separated, because that mixed thing is confusing and useless. So we	register two PCM devices: */	/* PCM#0 Analog i/o */	if ((err = snd_pcm_new(chip->card, "Analog PCM", 0,			       num_analog_busses_out(chip),			       num_analog_busses_in(chip), &pcm)) < 0)		return err;	pcm->private_data = chip;	chip->analog_pcm = pcm;	strcpy(pcm->name, chip->card->shortname);	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &analog_playback_ops);	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &analog_capture_ops);	if ((err = snd_echo_preallocate_pages(pcm, snd_dma_pci_data(chip->pci))) < 0)		return err;	DE_INIT(("Analog PCM ok\n"));#ifdef ECHOCARD_HAS_DIGITAL_IO	/* PCM#1 Digital i/o */	if ((err = snd_pcm_new(chip->card, "Digital PCM", 1,			       num_digital_busses_out(chip),			       num_digital_busses_in(chip), &pcm)) < 0)		return err;	pcm->private_data = chip;	chip->digital_pcm = pcm;	strcpy(pcm->name, chip->card->shortname);	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &digital_playback_ops);	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &digital_capture_ops);	if ((err = snd_echo_preallocate_pages(pcm, snd_dma_pci_data(chip->pci))) < 0)		return err;	DE_INIT(("Digital PCM ok\n"));#endif /* ECHOCARD_HAS_DIGITAL_IO */#endif /* ECHOCARD_HAS_VMIXER */	return 0;}/******************************************************************************	Control interface******************************************************************************//******************* PCM output volume *******************/static int snd_echo_output_gain_info(struct snd_kcontrol *kcontrol,				     struct snd_ctl_elem_info *uinfo){	struct echoaudio *chip;	chip = snd_kcontrol_chip(kcontrol);	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;	uinfo->count = num_busses_out(chip);	uinfo->value.integer.min = ECHOGAIN_MINOUT;	uinfo->value.integer.max = ECHOGAIN_MAXOUT;	return 0;}static int snd_echo_output_gain_get(struct snd_kcontrol *kcontrol,				    struct snd_ctl_elem_value *ucontrol){	struct echoaudio *chip;	int c;	chip = snd_kcontrol_chip(kcontrol);	for (c = 0; c < num_busses_out(chip); c++)		ucontrol->value.integer.value[c] = chip->output_gain[c];	return 0;}static int snd_echo_output_gain_put(struct snd_kcontrol *kcontrol,				    struct snd_ctl_elem_value *ucontrol){	struct echoaudio *chip;	int c, changed, gain;	changed = 0;	chip = snd_kcontrol_chip(kcontrol);	spin_lock_irq(&chip->lock);	for (c = 0; c < num_busses_out(chip); c++) {		gain = ucontrol->value.integer.value[c];		/* Ignore out of range values */		if (gain < ECHOGAIN_MINOUT || gain > ECHOGAIN_MAXOUT)			continue;		if (chip->output_gain[c] != gain) {			set_output_gain(chip, c, gain);			changed = 1;		}	}	if (changed)		update_output_line_level(chip);	spin_unlock_irq(&chip->lock);	return changed;}#ifdef ECHOCARD_HAS_VMIXER/* On Vmixer cards this one controls the line-out volume */static struct snd_kcontrol_new snd_echo_line_output_gain __devinitdata = {	.name = "Line Playback Volume",	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ,	.info = snd_echo_output_gain_info,	.get = snd_echo_output_gain_get,	.put = snd_echo_output_gain_put,	.tlv = {.p = db_scale_output_gain},};#elsestatic struct snd_kcontrol_new snd_echo_pcm_output_gain __devinitdata = {	.name = "PCM Playback Volume",	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ,	.info = snd_echo_output_gain_info,	.get = snd_echo_output_gain_get,	.put = snd_echo_output_gain_put,	.tlv = {.p = db_scale_output_gain},};#endif#ifdef ECHOCARD_HAS_INPUT_GAIN/******************* Analog input volume *******************/static int snd_echo_input_gain_info(struct snd_kcontrol *kcontrol,				    struct snd_ctl_elem_info *uinfo){	struct echoaudio *chip;	chip = snd_kcontrol_chip(kcontrol);	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;	uinfo->count = num_analog_busses_in(chip);	uinfo->value.integer.min = ECHOGAIN_MININP;	uinfo->value.integer.max = ECHOGAIN_MAXINP;	return 0;}static int snd_echo_input_gain_get(struct snd_kcontrol *kcontrol,				   struct snd_ctl_elem_value *ucontrol){	struct echoaudio *chip;	int c;	chip = snd_kcontrol_chip(kcontrol);	for (c = 0; c < num_analog_busses_in(chip); c++)		ucontrol->value.integer.value[c] = chip->input_gain[c];	return 0;}static int snd_echo_input_gain_put(struct snd_kcontrol *kcontrol,				   struct snd_ctl_elem_value *ucontrol){	struct echoaudio *chip;	int c, gain, changed;	changed = 0;	chip = snd_kcontrol_chip(kcontrol);	spin_lock_irq(&chip->lock);	for (c = 0; c < num_analog_busses_in(chip); c++) {		gain = ucontrol->value.integer.value[c];		/* Ignore out of range values */		if (gain < ECHOGAIN_MININP || gain > ECHOGAIN_MAXINP)			continue;		if (chip->input_gain[c] != gain) {			set_input_gain(chip, c, gain);			changed = 1;		}	}	if (changed)		update_input_line_level(chip);	spin_unlock_irq(&chip->lock);	return changed;}static const DECLARE_TLV_DB_SCALE(db_scale_input_gain, -2500, 50, 0);static struct snd_kcontrol_new snd_echo_line_input_gain __devinitdata = {	.name = "Line Capture Volume",	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ,	.info = snd_echo_input_gain_info,	.get = snd_echo_input_gain_get,	.put = snd_echo_input_gain_put,

⌨️ 快捷键说明

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