📄 i810_audio.c
字号:
* (assuming one is already allocated, if it isn't, then allocate it). */ if ((ret = alloc_dmabuf(state))) return ret; /* FIXME: figure out all this OSS fragment stuff */ /* I did, it now does what it should according to the OSS API. DL */ /* We may not have realloced our dmabuf, but the fragment size to * fragment number ratio may have changed, so go ahead and reprogram * things */ dmabuf->dmasize = PAGE_SIZE << dmabuf->buforder; dmabuf->numfrag = SG_LEN; dmabuf->fragsize = dmabuf->dmasize/dmabuf->numfrag; dmabuf->fragsamples = dmabuf->fragsize >> 1; dmabuf->userfragsize = dmabuf->ossfragsize; dmabuf->userfrags = dmabuf->dmasize/dmabuf->ossfragsize; memset(dmabuf->rawbuf, 0, dmabuf->dmasize); if(dmabuf->ossmaxfrags == 4) { fragint = 8; dmabuf->fragshift = 2; } else if (dmabuf->ossmaxfrags == 8) { fragint = 4; dmabuf->fragshift = 3; } else if (dmabuf->ossmaxfrags == 16) { fragint = 2; dmabuf->fragshift = 4; } else { fragint = 1; dmabuf->fragshift = 5; } /* * Now set up the ring */ if(dmabuf->read_channel) c = dmabuf->read_channel; else c = dmabuf->write_channel; while(c != NULL) { sg=&c->sg[0]; /* * Load up 32 sg entries and take an interrupt at half * way (we might want more interrupts later..) */ for(i=0;i<dmabuf->numfrag;i++) { sg->busaddr=virt_to_bus(dmabuf->rawbuf+dmabuf->fragsize*i); // the card will always be doing 16bit stereo sg->control=dmabuf->fragsamples; if(state->card->pci_id == PCI_DEVICE_ID_SI_7012) sg->control <<= 1; sg->control|=CON_BUFPAD; // set us up to get IOC interrupts as often as needed to // satisfy numfrag requirements, no more if( ((i+1) % fragint) == 0) { sg->control|=CON_IOC; } sg++; } spin_lock_irqsave(&state->card->lock, flags); outb(2, state->card->iobase+c->port+OFF_CR); /* reset DMA machine */ outl(virt_to_bus(&c->sg[0]), state->card->iobase+c->port+OFF_BDBAR); outb(0, state->card->iobase+c->port+OFF_CIV); outb(0, state->card->iobase+c->port+OFF_LVI); spin_unlock_irqrestore(&state->card->lock, flags); if(c != dmabuf->write_channel) c = dmabuf->write_channel; else c = NULL; } /* set the ready flag for the dma buffer */ dmabuf->ready = 1;#ifdef DEBUG printk("i810_audio: prog_dmabuf, sample rate = %d, format = %d,\n\tnumfrag = %d, " "fragsize = %d dmasize = %d\n", dmabuf->rate, dmabuf->fmt, dmabuf->numfrag, dmabuf->fragsize, dmabuf->dmasize);#endif return 0;}static void __i810_update_lvi(struct i810_state *state, int rec){ struct dmabuf *dmabuf = &state->dmabuf; int x, port; port = state->card->iobase; if(rec) port += dmabuf->read_channel->port; else port += dmabuf->write_channel->port; /* if we are currently stopped, then our CIV is actually set to our * *last* sg segment and we are ready to wrap to the next. However, * if we set our LVI to the last sg segment, then it won't wrap to * the next sg segment, it won't even get a start. So, instead, when * we are stopped, we set both the LVI value and also we increment * the CIV value to the next sg segment to be played so that when * we call start_{dac,adc}, things will operate properly */ if (!dmabuf->enable && dmabuf->ready) { if(rec && dmabuf->count < dmabuf->dmasize && (dmabuf->trigger & PCM_ENABLE_INPUT)) { outb((inb(port+OFF_CIV)+1)&31, port+OFF_LVI); __start_adc(state); while( !(inb(port + OFF_CR) & ((1<<4) | (1<<2))) ) ; } else if (!rec && dmabuf->count && (dmabuf->trigger & PCM_ENABLE_OUTPUT)) { outb((inb(port+OFF_CIV)+1)&31, port+OFF_LVI); __start_dac(state); while( !(inb(port + OFF_CR) & ((1<<4) | (1<<2))) ) ; } } /* swptr - 1 is the tail of our transfer */ x = (dmabuf->dmasize + dmabuf->swptr - 1) % dmabuf->dmasize; x /= dmabuf->fragsize; outb(x, port+OFF_LVI);}static void i810_update_lvi(struct i810_state *state, int rec){ struct dmabuf *dmabuf = &state->dmabuf; unsigned long flags; if(!dmabuf->ready) return; spin_lock_irqsave(&state->card->lock, flags); __i810_update_lvi(state, rec); spin_unlock_irqrestore(&state->card->lock, flags);}/* update buffer manangement pointers, especially, dmabuf->count and dmabuf->hwptr */static void i810_update_ptr(struct i810_state *state){ struct dmabuf *dmabuf = &state->dmabuf; unsigned hwptr; int diff; /* error handling and process wake up for DAC */ if (dmabuf->enable == ADC_RUNNING) { /* update hardware pointer */ hwptr = i810_get_dma_addr(state, 1); diff = (dmabuf->dmasize + hwptr - dmabuf->hwptr) % dmabuf->dmasize;#if defined(DEBUG_INTERRUPTS) || defined(DEBUG_MMAP) printk("ADC HWP %d,%d,%d\n", hwptr, dmabuf->hwptr, diff);#endif dmabuf->hwptr = hwptr; dmabuf->total_bytes += diff; dmabuf->count += diff; if (dmabuf->count > dmabuf->dmasize) { /* buffer underrun or buffer overrun */ /* this is normal for the end of a read */ /* only give an error if we went past the */ /* last valid sg entry */ if((inb(state->card->iobase + PI_CIV) & 31) != (inb(state->card->iobase + PI_LVI) & 31)) { printk(KERN_WARNING "i810_audio: DMA overrun on read\n"); dmabuf->error++; } } if (dmabuf->count > dmabuf->userfragsize) wake_up(&dmabuf->wait); } /* error handling and process wake up for DAC */ if (dmabuf->enable == DAC_RUNNING) { /* update hardware pointer */ hwptr = i810_get_dma_addr(state, 0); diff = (dmabuf->dmasize + hwptr - dmabuf->hwptr) % dmabuf->dmasize;#if defined(DEBUG_INTERRUPTS) || defined(DEBUG_MMAP) printk("DAC HWP %d,%d,%d\n", hwptr, dmabuf->hwptr, diff);#endif dmabuf->hwptr = hwptr; dmabuf->total_bytes += diff; dmabuf->count -= diff; if (dmabuf->count < 0) { /* buffer underrun or buffer overrun */ /* this is normal for the end of a write */ /* only give an error if we went past the */ /* last valid sg entry */ if((inb(state->card->iobase + PO_CIV) & 31) != (inb(state->card->iobase + PO_LVI) & 31)) { printk(KERN_WARNING "i810_audio: DMA overrun on write\n"); printk("i810_audio: CIV %d, LVI %d, hwptr %x, " "count %d\n", inb(state->card->iobase + PO_CIV) & 31, inb(state->card->iobase + PO_LVI) & 31, dmabuf->hwptr, dmabuf->count); dmabuf->error++; } } if (dmabuf->count < (dmabuf->dmasize-dmabuf->userfragsize)) wake_up(&dmabuf->wait); }}static inline int i810_get_free_write_space(struct i810_state *state){ struct dmabuf *dmabuf = &state->dmabuf; int free; i810_update_ptr(state); // catch underruns during playback if (dmabuf->count < 0) { dmabuf->count = 0; dmabuf->swptr = dmabuf->hwptr; } free = dmabuf->dmasize - dmabuf->count; free -= (dmabuf->hwptr % dmabuf->fragsize); if(free < 0) return(0); return(free);}static inline int i810_get_available_read_data(struct i810_state *state){ struct dmabuf *dmabuf = &state->dmabuf; int avail; i810_update_ptr(state); // catch overruns during record if (dmabuf->count > dmabuf->dmasize) { dmabuf->count = dmabuf->dmasize; dmabuf->swptr = dmabuf->hwptr; } avail = dmabuf->count; avail -= (dmabuf->hwptr % dmabuf->fragsize); if(avail < 0) return(0); return(avail);}static int drain_dac(struct i810_state *state, int signals_allowed){ DECLARE_WAITQUEUE(wait, current); struct dmabuf *dmabuf = &state->dmabuf; unsigned long flags; unsigned long tmo; int count; if (!dmabuf->ready) return 0; if(dmabuf->mapped) { stop_dac(state); return 0; } add_wait_queue(&dmabuf->wait, &wait); for (;;) { spin_lock_irqsave(&state->card->lock, flags); i810_update_ptr(state); count = dmabuf->count; spin_unlock_irqrestore(&state->card->lock, flags); if (count <= 0) break; /* * This will make sure that our LVI is correct, that our * pointer is updated, and that the DAC is running. We * have to force the setting of dmabuf->trigger to avoid * any possible deadlocks. */ if(!dmabuf->enable) { dmabuf->trigger = PCM_ENABLE_OUTPUT; i810_update_lvi(state,0); } if (signal_pending(current) && signals_allowed) { break; } /* It seems that we have to set the current state to * TASK_INTERRUPTIBLE every time to make the process * really go to sleep. This also has to be *after* the * update_ptr() call because update_ptr is likely to * do a wake_up() which will unset this before we ever * try to sleep, resuling in a tight loop in this code * instead of actually sleeping and waiting for an * interrupt to wake us up! */ set_current_state(TASK_INTERRUPTIBLE); /* * set the timeout to significantly longer than it *should* * take for the DAC to drain the DMA buffer */ tmo = (count * HZ) / (dmabuf->rate); if (!schedule_timeout(tmo >= 2 ? tmo : 2)){ printk(KERN_ERR "i810_audio: drain_dac, dma timeout?\n"); count = 0; break; } } set_current_state(TASK_RUNNING); remove_wait_queue(&dmabuf->wait, &wait); if(count > 0 && signal_pending(current) && signals_allowed) return -ERESTARTSYS; stop_dac(state); return 0;}static void i810_channel_interrupt(struct i810_card *card){ int i, count; #ifdef DEBUG_INTERRUPTS printk("CHANNEL ");#endif for(i=0;i<NR_HW_CH;i++) { struct i810_state *state = card->states[i]; struct i810_channel *c; struct dmabuf *dmabuf; unsigned long port = card->iobase; u16 status; if(!state) continue; if(!state->dmabuf.ready) continue; dmabuf = &state->dmabuf; if(dmabuf->enable & DAC_RUNNING) { c=dmabuf->write_channel; } else if(dmabuf->enable & ADC_RUNNING) { c=dmabuf->read_channel; } else /* This can occur going from R/W to close */ continue; port+=c->port; if(card->pci_id == PCI_DEVICE_ID_SI_7012) status = inw(port + OFF_PICB); else status = inw(port + OFF_SR);#ifdef DEBUG_INTERRUPTS printk("NUM %d PORT %X IRQ ( ST%d ", c->num, c->port, status);#endif if(status & DMA_INT_COMPLETE) { /* only wake_up() waiters if this interrupt signals * us being beyond a userfragsize of data open or * available, and i810_update_ptr() does that for * us */ i810_update_ptr(state);#ifdef DEBUG_INTERRUPTS printk("COMP %d ", dmabuf->hwptr / dmabuf->fragsize);#endif } if(status & (DMA_INT_LVI | DMA_INT_DCH)) { /* wake_up() unconditionally on LVI and DCH */ i810_update_ptr(state); wake_up(&dmabuf->wait);#ifdef DEBUG_INTERRUPTS if(status & DMA_INT_LVI) printk("LVI "); if(status & DMA_INT_DCH) printk("DCH -");#endif if(dmabuf->enable & DAC_RUNNING) count = dmabuf->count; else count = dmabuf->dmasize - dmabuf->count; if(count > 0) { outb(inb(port+OFF_CR) | 1, port+OFF_CR);#ifdef DEBUG_INTERRUPTS printk(" CONTINUE ");#endif } else { if (dmabuf->enable & DAC_RUNNING) __stop_dac(state); if (dmabuf->enable & ADC_RUNNING) __stop_adc(state); dmabuf->enable = 0; wake_up(&dmabuf->wait);#ifdef DEBUG_INTERRUPTS printk(" STOP ");#endif } } if(card->pci_id == PCI_DEVICE_ID_SI_7012) outw(status & DMA_INT_MASK, port + OFF_PICB); else outw(status & DMA_INT_MASK, port + OFF_SR); }#ifdef DEBUG_INTERRUPTS printk(")\n");#endif}static void i810_interrupt(int irq, void *dev_id, struct pt_regs *regs){ struct i810_card *card = (struct i810_card *)dev_id; u32 status; spin_lock(&card->lock); status = inl(card->iobase + GLOB_STA); if(!(status & INT_MASK)) { spin_unlock(&card->lock); return; /* not for us */ } if(status & (INT_PO|INT_PI|INT_MC)) i810_channel_interrupt(card); /* clear 'em */ outl(status & INT_MASK, card->iobase + GLOB_STA); spin_unlock(&card->lock);}/* in this loop, dmabuf.count signifies the amount of data that is waiting to be copied to the user's buffer. It is filled by the dma machine and drained by this loop. */static ssize_t i810_read(struct file *file, char *buffer, size_t count, loff_t *ppos){ struct i810_state *state = (struct i810_state *)file->private_data; struct i810_card *card=state ? state->card : 0; struct dmabuf *dmabuf = &state->dmabuf; ssize_t ret; unsigned long flags; unsigned int swptr; int cnt; DECLARE_WAITQUEUE(waita, current);#ifdef DEBUG2 printk("i810_audio: i810_read called, count = %d\n", count);#endif if (ppos != &file->f_pos) return -ESPIPE;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -