📄 pcm86.c
字号:
}static voidfifo_recv_mono_16be(pcm_data *buf, int count, int uflag){ int i, cnt; short d, d0, d1, el, er, zlev; zlev = uflag ? -128 : 0; cnt = count / 2; if (pcm_s.speed == pcm_s.chipspeed) { /* No reason to convert the pcm speed. */ for (i = 0; i < cnt; i++) { el = inb(pcm_s.iobase + 12) << 8; el |= inb(pcm_s.iobase + 12); er = inb(pcm_s.iobase + 12) << 8; er |= inb(pcm_s.iobase + 12); d = (el + er) >> 1; *buf++ = ((d >> 8) & 0xff) + zlev; *buf++ = d & 0xff; } if (count % 2) { el = inb(pcm_s.iobase + 12) << 8; el |= inb(pcm_s.iobase + 12); er = inb(pcm_s.iobase + 12) << 8; er |= inb(pcm_s.iobase + 12); d = (el + er) >> 1; *buf++ = ((d >> 8) & 0xff) + zlev; tmpbuf.buff[0] = d & 0xff; tmpbuf.size = 1; } } else { /* Speed conversion with linear interpolation method. */ d0 = pcm_s.last_l; el = inb(pcm_s.iobase + 12) << 8; el |= inb(pcm_s.iobase + 12); er = inb(pcm_s.iobase + 12) << 8; er |= inb(pcm_s.iobase + 12); d1 = (el + er) >> 1; for (i = 0; i < cnt; i++) { while (pcm_s.acc >= pcm_s.speed) { pcm_s.acc -= pcm_s.speed; d0 = d1; el = inb(pcm_s.iobase + 12) << 8; el |= inb(pcm_s.iobase + 12); er = inb(pcm_s.iobase + 12) << 8; er |= inb(pcm_s.iobase + 12); d1 = (el + er) >> 1; } d = ((d0 * (pcm_s.speed - pcm_s.acc)) + (d1 * pcm_s.acc)) / pcm_s.speed; *buf++ = ((d >> 8) & 0xff) + zlev; *buf++ = d & 0xff; pcm_s.acc += pcm_s.chipspeed; } if (count % 2) { while (pcm_s.acc >= pcm_s.speed) { pcm_s.acc -= pcm_s.speed; d0 = d1; el = inb(pcm_s.iobase + 12) << 8; el |= inb(pcm_s.iobase + 12); er = inb(pcm_s.iobase + 12) << 8; er |= inb(pcm_s.iobase + 12); d1 = (el + er) >> 1; } d = ((d0 * (pcm_s.speed - pcm_s.acc)) + (d1 * pcm_s.acc)) / pcm_s.speed; *buf++ = ((d >> 8) & 0xff) + zlev; tmpbuf.buff[0] = d & 0xff; tmpbuf.size = 1; } pcm_s.last_l = d0; }}static voidpcm_stop(void){ fifo_stop(); /* stop FIFO */ fifo_reset(); /* reset FIFO buffer */ /* Reset driver's status. */ pcm_s.intr_busy = NO; pcm_s.intr_last = NO; pcm_s.intr_trailer = NO; pcm_s.acc = 0; pcm_s.last_l = 0; pcm_s.last_r = 0; DEB(printk("pcm_stop\n"));}static voidpcm_init(void){ /* Initialize registers on the board. */ pcm_stop(); if (pcm_s.board_type == PC980173_FAMILY) dsp73_init(); /* Set default volume. */ set_volume(DEFAULT_VOLUME); /* Initialize driver's status. */ pcm_s.opened = NO; pcm_initialized = YES;}/* * Codes for global use */intprobe_pcm86(struct address_info *hw_config){ return pcm86_detect(hw_config);}longattach_pcm86(long mem_start, struct address_info *hw_config){ if (pcm_s.board_type == NO_SUPPORTED_BOARD) return mem_start; /* Initialize the board. */ pcm_init(); printk("pcm0: <%s>", pcm86_operations.name); if (num_audiodevs < MAX_AUDIO_DEV) { my_dev = num_audiodevs++; audio_devs[my_dev] = &pcm86_operations; audio_devs[my_dev]->buffcount = DSP_BUFFCOUNT; audio_devs[my_dev]->buffsize = DSP_BUFFSIZE;#ifdef PCM86_DEBUG printk("\nbuffsize = %d", DSP_BUFFSIZE);#endif } else printk("pcm0: Too many PCM devices available"); return mem_start;}static intpcm86_detect(struct address_info *hw_config){ int opna_iobase = 0x188, irq = 12, i; unsigned char tmp; if (hw_config->io_base == -1) { printf("pcm0: iobase not specified. Assume default port(0x%x)\n", PCM86_IOBASE); hw_config->io_base = PCM86_IOBASE; } pcm_s.iobase = hw_config->io_base; /* auto configuration */ tmp = inb(pcm_s.iobase) & 0xfc; switch ((tmp & 0xf0) >> 4) { case 2: opna_iobase = 0x188; pcm_s.board_type = PC980173_FAMILY; break; case 3: opna_iobase = 0x288; pcm_s.board_type = PC980173_FAMILY; break; case 4: opna_iobase = 0x188; pcm_s.board_type = PC980186_FAMILY; break; case 5: opna_iobase = 0x288; pcm_s.board_type = PC980186_FAMILY; break; default: pcm_s.board_type = NO_SUPPORTED_BOARD; return NO; } /* Enable OPNA(YM2608) facilities. */ outb(pcm_s.iobase, tmp | 0x01); /* Wait for OPNA to be ready. */ i = 100000; /* Some large value */ while((inb(opna_iobase) & 0x80) && (i-- > 0)); /* Make IOA/IOB port ready (IOA:input, IOB:output) */ outb(opna_iobase, 0x07); outb(0x5f, 0); /* Because OPNA ports are comparatively slow(?), */ outb(0x5f, 0); /* we'd better wait a moment. */ outb(0x5f, 0); outb(0x5f, 0); tmp = inb(opna_iobase + 2) & 0x3f; outb(opna_iobase + 2, tmp | 0x80); /* Wait for OPNA to be ready. */ i = 100000; /* Some large value */ while((inb(opna_iobase) & 0x80) && (i-- > 0)); /* Get irq number from IOA port. */ outb(opna_iobase, 0x0e); outb(0x5f, 0); outb(0x5f, 0); outb(0x5f, 0); outb(0x5f, 0); tmp = inb(opna_iobase + 2) & 0xc0; switch (tmp >> 6) { case 0: /* INT0 (IRQ3)*/ irq = 3; break; case 1: /* INT6 (IRQ13)*/ irq = 13; break; case 2: /* INT4 (IRQ10)*/ irq = 10; break; case 3: /* INT5 (IRQ12)*/ irq = 12; break; default: /* error */ return NO; } /* Wait for OPNA to be ready. */ i = 100000; /* Some large value */ while((inb(opna_iobase) & 0x80) && (i-- > 0)); /* Reset OPNA timer register. */ outb(opna_iobase, 0x27); outb(0x5f, 0); outb(0x5f, 0); outb(0x5f, 0); outb(0x5f, 0); outb(opna_iobase + 2, 0x30); /* Ok. Detection finished. */ sprintf(pcm86_operations.name, board_name[pcm_s.board_type]); pcm_initialized = NO; pcm_s.irq = irq; if ((hw_config->irq > 0) && (hw_config->irq != irq)) printf("pcm0: change irq %d -> %d\n", hw_config->irq, irq); hw_config->irq = irq; return YES;}static intpcm86_open(int dev, int mode){ int err; if (!pcm_initialized) return RET_ERROR(ENXIO); if (pcm_s.intr_busy || pcm_s.opened) return RET_ERROR(EBUSY); if ((err = snd_set_irq_handler(pcm_s.irq, pcmintr, "PC-9801-73/86")) < 0) return err; pcm_stop(); tmpbuf.size = 0; pcm_s.intr_mode = IMODE_NONE; pcm_s.opened = YES; return 0;}static voidpcm86_close(int dev){ snd_release_irq(pcm_s.irq); pcm_s.opened = NO;}static voidpcm86_output_block(int dev, unsigned long buf, int count, int intrflag, int dma_restart){ unsigned long flags, cnt; int maxchunksize;#ifdef PCM86_DEBUG printk("pcm86_output_block():"); if (audio_devs[dev]->dmap->flags & DMA_BUSY) printk(" DMA_BUSY"); if (audio_devs[dev]->dmap->flags & DMA_RESTART) printk(" DMA_RESTART"); if (audio_devs[dev]->dmap->flags & DMA_ACTIVE) printk(" DMA_ACTIVE"); if (audio_devs[dev]->dmap->flags & DMA_STARTED) printk(" DMA_STARTED"); if (audio_devs[dev]->dmap->flags & DMA_ALLOC_DONE) printk(" DMA_ALLOC_DONE"); printk("\n");#endif#if 0 DISABLE_INTR(flags);#endif#ifdef PCM86_DEBUG printk("pcm86_output_block(): count = %d, intrsize= %d\n", count, pcm_s.intr_size);#endif pcm_s.pdma_buf = (pcm_data *)buf; pcm_s.pdma_count = count; pcm_s.pdma_chunkcount = 1; maxchunksize = (((PCM86_FIFOSIZE - pcm_s.intr_size * 2) / (pcm_s.bytes * 2)) * pcm_s.speed / pcm_s.chipspeed) * (pcm_s.bytes << pcm_s.stereo); if (count > maxchunksize) pcm_s.pdma_chunkcount = 2 * count / maxchunksize; /* * Let chunksize = (float)count / (float)pcm_s.pdma_chunkcount. * Data of size chunksize is sent to the FIFO buffer on the 86-board * on every occuring of interrupt. * By assuming that pcm_s.intr_size < PCM86_FIFOSIZE / 2, we can conclude * that the FIFO buffer never overflows from the following lemma. * * Lemma: * maxchunksize / 2 <= chunksize <= maxchunksize. * (Though pcm_s.pdma_chunkcount is obtained through the flooring * function, this inequality holds.) * Proof) Omitted. */ fifo_output_block(); pcm_s.intr_last = NO; pcm_s.intr_mode = IMODE_OUTPUT; if (!pcm_s.intr_busy) fifo_start(IMODE_OUTPUT); pcm_s.intr_busy = YES;#if 0 RESTORE_INTR(flags);#endif}static voidpcm86_start_input(int dev, unsigned long buf, int count, int intrflag, int dma_restart){ unsigned long flags, cnt; int maxchunksize;#ifdef PCM86_DEBUG printk("pcm86_start_input():"); if (audio_devs[dev]->dmap->flags & DMA_BUSY) printk(" DMA_BUSY"); if (audio_devs[dev]->dmap->flags & DMA_RESTART) printk(" DMA_RESTART"); if (audio_devs[dev]->dmap->flags & DMA_ACTIVE) printk(" DMA_ACTIVE"); if (audio_devs[dev]->dmap->flags & DMA_STARTED) printk(" DMA_STARTED"); if (audio_devs[dev]->dmap->flags & DMA_ALLOC_DONE) printk(" DMA_ALLOC_DONE"); printk("\n");#endif#if 0 DISABLE_INTR(flags);#endif pcm_s.intr_size = PCM86_INTRSIZE_IN;#ifdef PCM86_DEBUG printk("pcm86_start_input(): count = %d, intrsize= %d\n", count, pcm_s.intr_size);#endif pcm_s.pdma_buf = (pcm_data *)buf; pcm_s.pdma_count = count; pcm_s.pdma_chunkcount = 1; maxchunksize = ((pcm_s.intr_size / (pcm_s.bytes * 2)) * pcm_s.speed / pcm_s.chipspeed) * (pcm_s.bytes << pcm_s.stereo); if (count > maxchunksize) pcm_s.pdma_chunkcount = 2 * count / maxchunksize; pcm_s.intr_mode = IMODE_INPUT; if (!pcm_s.intr_busy) fifo_start(IMODE_INPUT); pcm_s.intr_busy = YES;#if 0 RESTORE_INTR(flags);#endif}static intpcm86_ioctl(int dev, unsigned int cmd, unsigned int arg, int local){ switch (cmd) { case SOUND_PCM_WRITE_RATE: if (local) return set_speed(arg); return IOCTL_OUT(arg, set_speed(IOCTL_IN(arg))); case SOUND_PCM_READ_RATE: if (local) return pcm_s.speed; return IOCTL_OUT(arg, pcm_s.speed); case SNDCTL_DSP_STEREO: if (local) return set_stereo(arg); return IOCTL_OUT(arg, set_stereo(IOCTL_IN(arg))); case SOUND_PCM_WRITE_CHANNELS: if (local) return set_stereo(arg - 1) + 1; return IOCTL_OUT(arg, set_stereo(IOCTL_IN(arg) - 1) + 1); case SOUND_PCM_READ_CHANNELS: if (local) return pcm_s.stereo + 1; return IOCTL_OUT(arg, pcm_s.stereo + 1); case SNDCTL_DSP_SETFMT: if (local) return set_format(arg); return IOCTL_OUT(arg, set_format(IOCTL_IN(arg))); case SOUND_PCM_READ_BITS: if (local) return pcm_s.bytes * 8; return IOCTL_OUT(arg, pcm_s.bytes * 8); } /* Invalid ioctl request */ return RET_ERROR(EINVAL);}static intpcm86_prepare_for_input(int dev, int bufsize, int nbufs){ pcm_s.intr_size = PCM86_INTRSIZE_IN; pcm_s.intr_mode = IMODE_NONE; pcm_s.acc = 0; pcm_s.last_l = 0; pcm_s.last_r = 0; DEB(printk("pcm86_prepare_for_input\n")); return 0;}static intpcm86_prepare_for_output(int dev, int bufsize, int nbufs){ pcm_s.intr_size = PCM86_INTRSIZE_OUT; pcm_s.intr_mode = IMODE_NONE; pcm_s.acc = 0; pcm_s.last_l = 0; pcm_s.last_r = 0; DEB(printk("pcm86_prepare_for_output\n")); return 0;}static voidpcm86_reset(int dev){ pcm_stop();}static voidpcm86_halt_xfer(int dev){ pcm_stop(); DEB(printk("pcm86_halt_xfer\n"));}voidpcmintr(int unit){ unsigned char tmp; if ((inb(pcm_s.iobase + 8) & 0x10) == 0) return; /* not FIFO intr. */ switch(pcm_s.intr_mode) { case IMODE_OUTPUT: if (pcm_s.intr_trailer) { DEB(printk("pcmintr(): fifo_reset\n")); fifo_reset(); pcm_s.intr_trailer = NO; pcm_s.intr_busy = NO; } if (pcm_s.pdma_count > 0) fifo_output_block(); else DMAbuf_outputintr(my_dev, 1); /* Reset intr. flag. */ tmp = inb(pcm_s.iobase + 8); outb(pcm_s.iobase + 8, tmp & ~0x10); outb(pcm_s.iobase + 8, tmp | 0x10); break; case IMODE_INPUT: fifo_input_block(); if (pcm_s.pdma_count == 0) DMAbuf_inputintr(my_dev); /* Reset intr. flag. */ tmp = inb(pcm_s.iobase + 8); outb(pcm_s.iobase + 8, tmp & ~0x10); outb(pcm_s.iobase + 8, tmp | 0x10); break; default: pcm_stop(); printk("pcm0: unexpected interrupt\n"); }}#endif /* EXCLUDE_PCM86, EXCLUDE_AUDIO */#endif /* CONFIGURE_SOUNDCARD */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -