📄 echoaudio.c
字号:
/* * 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 + -