📄 echoaudio.c
字号:
static int snd_echo_automute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){ struct echoaudio *chip = snd_kcontrol_chip(kcontrol); ucontrol->value.integer.value[0] = chip->digital_in_automute; return 0;}static int snd_echo_automute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){ struct echoaudio *chip = snd_kcontrol_chip(kcontrol); int automute, changed = 0; automute = !!ucontrol->value.integer.value[0]; if (chip->digital_in_automute != automute) { spin_lock_irq(&chip->lock); changed = set_input_auto_mute(chip, automute); spin_unlock_irq(&chip->lock); if (changed == 0) changed = 1; /* no errors */ } return changed;}static struct snd_kcontrol_new snd_echo_automute_switch __devinitdata = { .name = "Digital Capture Switch (automute)", .iface = SNDRV_CTL_ELEM_IFACE_CARD, .info = snd_echo_automute_info, .get = snd_echo_automute_get, .put = snd_echo_automute_put,};#endif /* ECHOCARD_HAS_DIGITAL_IN_AUTOMUTE *//******************* VU-meters switch *******************/#define snd_echo_vumeters_switch_info snd_ctl_boolean_mono_infostatic int snd_echo_vumeters_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){ struct echoaudio *chip; chip = snd_kcontrol_chip(kcontrol); spin_lock_irq(&chip->lock); set_meters_on(chip, ucontrol->value.integer.value[0]); spin_unlock_irq(&chip->lock); return 1;}static struct snd_kcontrol_new snd_echo_vumeters_switch __devinitdata = { .name = "VU-meters Switch", .iface = SNDRV_CTL_ELEM_IFACE_CARD, .access = SNDRV_CTL_ELEM_ACCESS_WRITE, .info = snd_echo_vumeters_switch_info, .put = snd_echo_vumeters_switch_put,};/***** Read VU-meters (input, output, analog and digital together) *****/static int snd_echo_vumeters_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 = 96; uinfo->value.integer.min = ECHOGAIN_MINOUT; uinfo->value.integer.max = 0;#ifdef ECHOCARD_HAS_VMIXER uinfo->dimen.d[0] = 3; /* Out, In, Virt */#else uinfo->dimen.d[0] = 2; /* Out, In */#endif uinfo->dimen.d[1] = 16; /* 16 channels */ uinfo->dimen.d[2] = 2; /* 0=level, 1=peak */ return 0;}static int snd_echo_vumeters_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){ struct echoaudio *chip; chip = snd_kcontrol_chip(kcontrol); get_audio_meters(chip, ucontrol->value.integer.value); return 0;}static struct snd_kcontrol_new snd_echo_vumeters __devinitdata = { .name = "VU-meters", .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, .info = snd_echo_vumeters_info, .get = snd_echo_vumeters_get, .tlv = {.p = db_scale_output_gain},};/*** Channels info - it exports informations about the number of channels ***/static int snd_echo_channels_info_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 = 6; uinfo->value.integer.min = 0; uinfo->value.integer.max = 1 << ECHO_CLOCK_NUMBER; return 0;}static int snd_echo_channels_info_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){ struct echoaudio *chip; int detected, clocks, bit, src; chip = snd_kcontrol_chip(kcontrol); ucontrol->value.integer.value[0] = num_busses_in(chip); ucontrol->value.integer.value[1] = num_analog_busses_in(chip); ucontrol->value.integer.value[2] = num_busses_out(chip); ucontrol->value.integer.value[3] = num_analog_busses_out(chip); ucontrol->value.integer.value[4] = num_pipes_out(chip); /* Compute the bitmask of the currently valid input clocks */ detected = detect_input_clocks(chip); clocks = 0; src = chip->num_clock_sources - 1; for (bit = ECHO_CLOCK_NUMBER - 1; bit >= 0; bit--) if (detected & (1 << bit)) for (; src >= 0; src--) if (bit == chip->clock_source_list[src]) { clocks |= 1 << src; break; } ucontrol->value.integer.value[5] = clocks; return 0;}static struct snd_kcontrol_new snd_echo_channels_info __devinitdata = { .name = "Channels info", .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, .info = snd_echo_channels_info_info, .get = snd_echo_channels_info_get,};/****************************************************************************** IRQ Handler******************************************************************************/static irqreturn_t snd_echo_interrupt(int irq, void *dev_id){ struct echoaudio *chip = dev_id; struct snd_pcm_substream *substream; int period, ss, st; spin_lock(&chip->lock); st = service_irq(chip); if (st < 0) { spin_unlock(&chip->lock); return IRQ_NONE; } /* The hardware doesn't tell us which substream caused the irq, thus we have to check all running substreams. */ for (ss = 0; ss < DSP_MAXPIPES; ss++) { if ((substream = chip->substream[ss])) { period = pcm_pointer(substream) / substream->runtime->period_size; if (period != chip->last_period[ss]) { chip->last_period[ss] = period; spin_unlock(&chip->lock); snd_pcm_period_elapsed(substream); spin_lock(&chip->lock); } } } spin_unlock(&chip->lock);#ifdef ECHOCARD_HAS_MIDI if (st > 0 && chip->midi_in) { snd_rawmidi_receive(chip->midi_in, chip->midi_buffer, st); DE_MID(("rawmidi_iread=%d\n", st)); }#endif return IRQ_HANDLED;}/****************************************************************************** Module construction / destruction******************************************************************************/static int snd_echo_free(struct echoaudio *chip){ DE_INIT(("Stop DSP...\n")); if (chip->comm_page) { rest_in_peace(chip); snd_dma_free_pages(&chip->commpage_dma_buf); } DE_INIT(("Stopped.\n")); if (chip->irq >= 0) free_irq(chip->irq, chip); if (chip->dsp_registers) iounmap(chip->dsp_registers); if (chip->iores) release_and_free_resource(chip->iores); DE_INIT(("MMIO freed.\n")); pci_disable_device(chip->pci); /* release chip data */ kfree(chip); DE_INIT(("Chip freed.\n")); return 0;}static int snd_echo_dev_free(struct snd_device *device){ struct echoaudio *chip = device->device_data; DE_INIT(("snd_echo_dev_free()...\n")); return snd_echo_free(chip);}/* <--snd_echo_probe() */static __devinit int snd_echo_create(struct snd_card *card, struct pci_dev *pci, struct echoaudio **rchip){ struct echoaudio *chip; int err; size_t sz; static struct snd_device_ops ops = { .dev_free = snd_echo_dev_free, }; *rchip = NULL; pci_write_config_byte(pci, PCI_LATENCY_TIMER, 0xC0); if ((err = pci_enable_device(pci)) < 0) return err; pci_set_master(pci); /* allocate a chip-specific data */ chip = kzalloc(sizeof(*chip), GFP_KERNEL); if (!chip) { pci_disable_device(pci); return -ENOMEM; } DE_INIT(("chip=%p\n", chip)); spin_lock_init(&chip->lock); chip->card = card; chip->pci = pci; chip->irq = -1; /* PCI resource allocation */ chip->dsp_registers_phys = pci_resource_start(pci, 0); sz = pci_resource_len(pci, 0); if (sz > PAGE_SIZE) sz = PAGE_SIZE; /* We map only the required part */ if ((chip->iores = request_mem_region(chip->dsp_registers_phys, sz, ECHOCARD_NAME)) == NULL) { snd_echo_free(chip); snd_printk(KERN_ERR "cannot get memory region\n"); return -EBUSY; } chip->dsp_registers = (volatile u32 __iomem *) ioremap_nocache(chip->dsp_registers_phys, sz); if (request_irq(pci->irq, snd_echo_interrupt, IRQF_SHARED, ECHOCARD_NAME, chip)) { snd_echo_free(chip); snd_printk(KERN_ERR "cannot grab irq\n"); return -EBUSY; } chip->irq = pci->irq; DE_INIT(("pci=%p irq=%d subdev=%04x Init hardware...\n", chip->pci, chip->irq, chip->pci->subsystem_device)); /* Create the DSP comm page - this is the area of memory used for most of the communication with the DSP, which accesses it via bus mastering */ if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci), sizeof(struct comm_page), &chip->commpage_dma_buf) < 0) { snd_echo_free(chip); snd_printk(KERN_ERR "cannot allocate the comm page\n"); return -ENOMEM; } chip->comm_page_phys = chip->commpage_dma_buf.addr; chip->comm_page = (struct comm_page *)chip->commpage_dma_buf.area; err = init_hw(chip, chip->pci->device, chip->pci->subsystem_device); if (err) { DE_INIT(("init_hw err=%d\n", err)); snd_echo_free(chip); return err; } DE_INIT(("Card init OK\n")); if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { snd_echo_free(chip); return err; } atomic_set(&chip->opencount, 0); init_MUTEX(&chip->mode_mutex); chip->can_set_rate = 1; *rchip = chip; /* Init done ! */ return 0;}/* constructor */static int __devinit snd_echo_probe(struct pci_dev *pci, const struct pci_device_id *pci_id){ static int dev; struct snd_card *card; struct echoaudio *chip; char *dsp; int i, err; if (dev >= SNDRV_CARDS) return -ENODEV; if (!enable[dev]) { dev++; return -ENOENT; } DE_INIT(("Echoaudio driver starting...\n")); i = 0; card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0); if (card == NULL) return -ENOMEM; snd_card_set_dev(card, &pci->dev); if ((err = snd_echo_create(card, pci, &chip)) < 0) { snd_card_free(card); return err; } strcpy(card->driver, "Echo_" ECHOCARD_NAME); strcpy(card->shortname, chip->card_name); dsp = "56301"; if (pci_id->device == 0x3410) dsp = "56361"; sprintf(card->longname, "%s rev.%d (DSP%s) at 0x%lx irq %i", card->shortname, pci_id->subdevice & 0x000f, dsp, chip->dsp_registers_phys, chip->irq); if ((err = snd_echo_new_pcm(chip)) < 0) { snd_printk(KERN_ERR "new pcm error %d\n", err); snd_card_free(card); return err; }#ifdef ECHOCARD_HAS_MIDI if (chip->has_midi) { /* Some Mia's do not have midi */ if ((err = snd_echo_midi_create(card, chip)) < 0) { snd_printk(KERN_ERR "new midi error %d\n", err); snd_card_free(card); return err; } }#endif#ifdef ECHOCARD_HAS_VMIXER snd_echo_vmixer.count = num_pipes_out(chip) * num_busses_out(chip); if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_line_output_gain, chip))) < 0) goto ctl_error; if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_vmixer, chip))) < 0) goto ctl_error;#else if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_pcm_output_gain, chip))) < 0) goto ctl_error;#endif#ifdef ECHOCARD_HAS_INPUT_GAIN if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_line_input_gain, chip))) < 0) goto ctl_error;#endif#ifdef ECHOCARD_HAS_INPUT_NOMINAL_LEVEL if (!chip->hasnt_input_nominal_level) if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_intput_nominal_level, chip))) < 0) goto ctl_error;#endif#ifdef ECHOCARD_HAS_OUTPUT_NOMINAL_LEVEL if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_output_nominal_level, chip))) < 0) goto ctl_error;#endif if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_vumeters_switch, chip))) < 0) goto ctl_error; if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_vumeters, chip))) < 0) goto ctl_error;#ifdef ECHOCARD_HAS_MONITOR snd_echo_monitor_mixer.count = num_busses_in(chip) * num_busses_out(chip); if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_monitor_mixer, chip))) < 0) goto ctl_error;#endif#ifdef ECHOCARD_HAS_DIGITAL_IN_AUTOMUTE if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_automute_switch, chip))) < 0) goto ctl_error;#endif if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_channels_info, chip))) < 0) goto ctl_error;#ifdef ECHOCARD_HAS_DIGITAL_MODE_SWITCH /* Creates a list of available digital modes */ chip->num_digital_modes = 0; for (i = 0; i < 6; i++) if (chip->digital_modes & (1 << i)) chip->digital_mode_list[chip->num_digital_modes++] = i; if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_digital_mode_switch, chip))) < 0) goto ctl_error;#endif /* ECHOCARD_HAS_DIGITAL_MODE_SWITCH */#ifdef ECHOCARD_HAS_EXTERNAL_CLOCK /* Creates a list of available clock sources */ chip->num_clock_sources = 0; for (i = 0; i < 10; i++) if (chip->input_clock_types & (1 << i)) chip->clock_source_list[chip->num_clock_sources++] = i; if (chip->num_clock_sources > 1) { chip->clock_src_ctl = snd_ctl_new1(&snd_echo_clock_source_switch, chip); if ((err = snd_ctl_add(chip->card, chip->clock_src_ctl)) < 0) goto ctl_error; }#endif /* ECHOCARD_HAS_EXTERNAL_CLOCK */#ifdef ECHOCARD_HAS_DIGITAL_IO if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_spdif_mode_switch, chip))) < 0) goto ctl_error;#endif#ifdef ECHOCARD_HAS_PHANTOM_POWER if (chip->has_phantom_power) if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_phantom_power_switch, chip))) < 0) goto ctl_error;#endif if ((err = snd_card_register(card)) < 0) { snd_card_free(card); goto ctl_error; } snd_printk(KERN_INFO "Card registered: %s\n", card->longname); pci_set_drvdata(pci, chip); dev++; return 0;ctl_error: snd_printk(KERN_ERR "new control error %d\n", err); snd_card_free(card); return err;}static void __devexit snd_echo_remove(struct pci_dev *pci){ struct echoaudio *chip; chip = pci_get_drvdata(pci); if (chip) snd_card_free(chip->card); pci_set_drvdata(pci, NULL);}/****************************************************************************** Everything starts and ends here******************************************************************************//* pci_driver definition */static struct pci_driver driver = { .name = "Echoaudio " ECHOCARD_NAME, .id_table = snd_echo_ids, .probe = snd_echo_probe, .remove = __devexit_p(snd_echo_remove),};/* initialization of the module */static int __init alsa_card_echo_init(void){ return pci_register_driver(&driver);}/* clean up the module */static void __exit alsa_card_echo_exit(void){ pci_unregister_driver(&driver);}module_init(alsa_card_echo_init)module_exit(alsa_card_echo_exit)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -