📄 ca0106_main.c
字号:
snd_ca0106_ptr_write(emu, EXTENDED_INT_MASK, 0, snd_ca0106_ptr_read(emu, EXTENDED_INT_MASK, 0) & ~(0x110000<<channel)); epcm->running = 0; break; default: result = -EINVAL; break; } return result;}/* pointer_playback callback */static snd_pcm_uframes_tsnd_ca0106_pcm_pointer_playback(snd_pcm_substream_t *substream){ ca0106_t *emu = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; ca0106_pcm_t *epcm = runtime->private_data; snd_pcm_uframes_t ptr, ptr1, ptr2,ptr3,ptr4 = 0; int channel = epcm->channel_id; if (!epcm->running) return 0; ptr3 = snd_ca0106_ptr_read(emu, PLAYBACK_LIST_PTR, channel); ptr1 = snd_ca0106_ptr_read(emu, PLAYBACK_POINTER, channel); ptr4 = snd_ca0106_ptr_read(emu, PLAYBACK_LIST_PTR, channel); if (ptr3 != ptr4) ptr1 = snd_ca0106_ptr_read(emu, PLAYBACK_POINTER, channel); ptr2 = bytes_to_frames(runtime, ptr1); ptr2+= (ptr4 >> 3) * runtime->period_size; ptr=ptr2; if (ptr >= runtime->buffer_size) ptr -= runtime->buffer_size; //printk("ptr1 = 0x%lx, ptr2=0x%lx, ptr=0x%lx, buffer_size = 0x%x, period_size = 0x%x, bits=%d, rate=%d\n", ptr1, ptr2, ptr, (int)runtime->buffer_size, (int)runtime->period_size, (int)runtime->frame_bits, (int)runtime->rate); return ptr;}/* pointer_capture callback */static snd_pcm_uframes_tsnd_ca0106_pcm_pointer_capture(snd_pcm_substream_t *substream){ ca0106_t *emu = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; ca0106_pcm_t *epcm = runtime->private_data; snd_pcm_uframes_t ptr, ptr1, ptr2 = 0; int channel = channel=epcm->channel_id; if (!epcm->running) return 0; ptr1 = snd_ca0106_ptr_read(emu, CAPTURE_POINTER, channel); ptr2 = bytes_to_frames(runtime, ptr1); ptr=ptr2; if (ptr >= runtime->buffer_size) ptr -= runtime->buffer_size; //printk("ptr1 = 0x%lx, ptr2=0x%lx, ptr=0x%lx, buffer_size = 0x%x, period_size = 0x%x, bits=%d, rate=%d\n", ptr1, ptr2, ptr, (int)runtime->buffer_size, (int)runtime->period_size, (int)runtime->frame_bits, (int)runtime->rate); return ptr;}/* operators */static snd_pcm_ops_t snd_ca0106_playback_front_ops = { .open = snd_ca0106_pcm_open_playback_front, .close = snd_ca0106_pcm_close_playback, .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_ca0106_pcm_hw_params_playback, .hw_free = snd_ca0106_pcm_hw_free_playback, .prepare = snd_ca0106_pcm_prepare_playback, .trigger = snd_ca0106_pcm_trigger_playback, .pointer = snd_ca0106_pcm_pointer_playback,};static snd_pcm_ops_t snd_ca0106_capture_0_ops = { .open = snd_ca0106_pcm_open_0_capture, .close = snd_ca0106_pcm_close_capture, .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_ca0106_pcm_hw_params_capture, .hw_free = snd_ca0106_pcm_hw_free_capture, .prepare = snd_ca0106_pcm_prepare_capture, .trigger = snd_ca0106_pcm_trigger_capture, .pointer = snd_ca0106_pcm_pointer_capture,};static snd_pcm_ops_t snd_ca0106_capture_1_ops = { .open = snd_ca0106_pcm_open_1_capture, .close = snd_ca0106_pcm_close_capture, .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_ca0106_pcm_hw_params_capture, .hw_free = snd_ca0106_pcm_hw_free_capture, .prepare = snd_ca0106_pcm_prepare_capture, .trigger = snd_ca0106_pcm_trigger_capture, .pointer = snd_ca0106_pcm_pointer_capture,};static snd_pcm_ops_t snd_ca0106_capture_2_ops = { .open = snd_ca0106_pcm_open_2_capture, .close = snd_ca0106_pcm_close_capture, .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_ca0106_pcm_hw_params_capture, .hw_free = snd_ca0106_pcm_hw_free_capture, .prepare = snd_ca0106_pcm_prepare_capture, .trigger = snd_ca0106_pcm_trigger_capture, .pointer = snd_ca0106_pcm_pointer_capture,};static snd_pcm_ops_t snd_ca0106_capture_3_ops = { .open = snd_ca0106_pcm_open_3_capture, .close = snd_ca0106_pcm_close_capture, .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_ca0106_pcm_hw_params_capture, .hw_free = snd_ca0106_pcm_hw_free_capture, .prepare = snd_ca0106_pcm_prepare_capture, .trigger = snd_ca0106_pcm_trigger_capture, .pointer = snd_ca0106_pcm_pointer_capture,};static snd_pcm_ops_t snd_ca0106_playback_center_lfe_ops = { .open = snd_ca0106_pcm_open_playback_center_lfe, .close = snd_ca0106_pcm_close_playback, .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_ca0106_pcm_hw_params_playback, .hw_free = snd_ca0106_pcm_hw_free_playback, .prepare = snd_ca0106_pcm_prepare_playback, .trigger = snd_ca0106_pcm_trigger_playback, .pointer = snd_ca0106_pcm_pointer_playback, };static snd_pcm_ops_t snd_ca0106_playback_unknown_ops = { .open = snd_ca0106_pcm_open_playback_unknown, .close = snd_ca0106_pcm_close_playback, .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_ca0106_pcm_hw_params_playback, .hw_free = snd_ca0106_pcm_hw_free_playback, .prepare = snd_ca0106_pcm_prepare_playback, .trigger = snd_ca0106_pcm_trigger_playback, .pointer = snd_ca0106_pcm_pointer_playback, };static snd_pcm_ops_t snd_ca0106_playback_rear_ops = { .open = snd_ca0106_pcm_open_playback_rear, .close = snd_ca0106_pcm_close_playback, .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_ca0106_pcm_hw_params_playback, .hw_free = snd_ca0106_pcm_hw_free_playback, .prepare = snd_ca0106_pcm_prepare_playback, .trigger = snd_ca0106_pcm_trigger_playback, .pointer = snd_ca0106_pcm_pointer_playback, };static unsigned short snd_ca0106_ac97_read(ac97_t *ac97, unsigned short reg){ ca0106_t *emu = ac97->private_data; unsigned long flags; unsigned short val; spin_lock_irqsave(&emu->emu_lock, flags); outb(reg, emu->port + AC97ADDRESS); val = inw(emu->port + AC97DATA); spin_unlock_irqrestore(&emu->emu_lock, flags); return val;}static void snd_ca0106_ac97_write(ac97_t *ac97, unsigned short reg, unsigned short val){ ca0106_t *emu = ac97->private_data; unsigned long flags; spin_lock_irqsave(&emu->emu_lock, flags); outb(reg, emu->port + AC97ADDRESS); outw(val, emu->port + AC97DATA); spin_unlock_irqrestore(&emu->emu_lock, flags);}static int snd_ca0106_ac97(ca0106_t *chip){ ac97_bus_t *pbus; ac97_template_t ac97; int err; static ac97_bus_ops_t ops = { .write = snd_ca0106_ac97_write, .read = snd_ca0106_ac97_read, }; if ((err = snd_ac97_bus(chip->card, 0, &ops, NULL, &pbus)) < 0) return err; pbus->no_vra = 1; /* we don't need VRA */ memset(&ac97, 0, sizeof(ac97)); ac97.private_data = chip; ac97.scaps = AC97_SCAP_NO_SPDIF; return snd_ac97_mixer(pbus, &ac97, &chip->ac97);}static int snd_ca0106_free(ca0106_t *chip){ if (chip->res_port != NULL) { /* avoid access to already used hardware */ // disable interrupts snd_ca0106_ptr_write(chip, BASIC_INTERRUPT, 0, 0); outl(0, chip->port + INTE); snd_ca0106_ptr_write(chip, EXTENDED_INT_MASK, 0, 0); udelay(1000); // disable audio //outl(HCFG_LOCKSOUNDCACHE, chip->port + HCFG); outl(0, chip->port + HCFG); /* FIXME: We need to stop and DMA transfers here. * But as I am not sure how yet, we cannot from the dma pages. * So we can fix: snd-malloc: Memory leak? pages not freed = 8 */ } // release the data#if 1 if (chip->buffer.area) snd_dma_free_pages(&chip->buffer);#endif // release the i/o port release_and_free_resource(chip->res_port); // release the irq if (chip->irq >= 0) free_irq(chip->irq, (void *)chip); pci_disable_device(chip->pci); kfree(chip); return 0;}static int snd_ca0106_dev_free(snd_device_t *device){ ca0106_t *chip = device->device_data; return snd_ca0106_free(chip);}static irqreturn_t snd_ca0106_interrupt(int irq, void *dev_id, struct pt_regs *regs){ unsigned int status; ca0106_t *chip = dev_id; int i; int mask; unsigned int stat76; ca0106_channel_t *pchannel; spin_lock(&chip->emu_lock); status = inl(chip->port + IPR); // call updater, unlock before it spin_unlock(&chip->emu_lock); if (! status) return IRQ_NONE; stat76 = snd_ca0106_ptr_read(chip, EXTENDED_INT, 0); //snd_printk("interrupt status = 0x%08x, stat76=0x%08x\n", status, stat76); //snd_printk("ptr=0x%08x\n",snd_ca0106_ptr_read(chip, PLAYBACK_POINTER, 0)); mask = 0x11; /* 0x1 for one half, 0x10 for the other half period. */ for(i = 0; i < 4; i++) { pchannel = &(chip->playback_channels[i]); if(stat76 & mask) {/* FIXME: Select the correct substream for period elapsed */ if(pchannel->use) { snd_pcm_period_elapsed(pchannel->epcm->substream); //printk(KERN_INFO "interrupt [%d] used\n", i); } } //printk(KERN_INFO "channel=%p\n",pchannel); //printk(KERN_INFO "interrupt stat76[%d] = %08x, use=%d, channel=%d\n", i, stat76, pchannel->use, pchannel->number); mask <<= 1; } mask = 0x110000; /* 0x1 for one half, 0x10 for the other half period. */ for(i = 0; i < 4; i++) { pchannel = &(chip->capture_channels[i]); if(stat76 & mask) {/* FIXME: Select the correct substream for period elapsed */ if(pchannel->use) { snd_pcm_period_elapsed(pchannel->epcm->substream); //printk(KERN_INFO "interrupt [%d] used\n", i); } } //printk(KERN_INFO "channel=%p\n",pchannel); //printk(KERN_INFO "interrupt stat76[%d] = %08x, use=%d, channel=%d\n", i, stat76, pchannel->use, pchannel->number); mask <<= 1; } snd_ca0106_ptr_write(chip, EXTENDED_INT, 0, stat76); spin_lock(&chip->emu_lock); if (chip->midi.dev_id && (status & (chip->midi.ipr_tx|chip->midi.ipr_rx))) { if (chip->midi.interrupt) chip->midi.interrupt(&chip->midi, status); else chip->midi.interrupt_disable(&chip->midi, chip->midi.tx_enable | chip->midi.rx_enable); } // acknowledge the interrupt if necessary outl(status, chip->port+IPR); spin_unlock(&chip->emu_lock); return IRQ_HANDLED;}static void snd_ca0106_pcm_free(snd_pcm_t *pcm){ ca0106_t *emu = pcm->private_data; emu->pcm = NULL; snd_pcm_lib_preallocate_free_for_all(pcm);}static int __devinit snd_ca0106_pcm(ca0106_t *emu, int device, snd_pcm_t **rpcm){ snd_pcm_t *pcm; snd_pcm_substream_t *substream; int err; if (rpcm) *rpcm = NULL; if ((err = snd_pcm_new(emu->card, "ca0106", device, 1, 1, &pcm)) < 0) return err; pcm->private_data = emu; pcm->private_free = snd_ca0106_pcm_free; switch (device) { case 0: snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ca0106_playback_front_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ca0106_capture_0_ops); break; case 1: snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ca0106_playback_rear_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ca0106_capture_1_ops); break; case 2: snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ca0106_playback_center_lfe_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ca0106_capture_2_ops); break; case 3: snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ca0106_playback_unknown_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ca0106_capture_3_ops); break; } pcm->info_flags = 0; pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX; strcpy(pcm->name, "CA0106"); emu->pcm = pcm; for(substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; substream; substream = substream->next) { if ((err = snd_pcm_lib_preallocate_pages(substream, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(emu->pci), 64*1024, 64*1024)) < 0) /* FIXME: 32*1024 for sound buffer, between 32and64 for Periods table. */ return err; } for (substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; substream; substream = substream->next) { if ((err = snd_pcm_lib_preallocate_pages(substream, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(emu->pci), 64*1024, 64*1024)) < 0) return err; } if (rpcm) *rpcm = pcm; return 0;}static int __devinit snd_ca0106_create(snd_card_t *card, struct pci_dev *pci, ca0106_t **rchip){
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -