int DMAbuf_activate_recording(int dev, struct dma_buffparms *dmap) { struct audio_operations *adev = audio_devs[dev]; int err; if (!(adev->open_mode & OPEN_READ)) return 0; if (!(adev->enable_bits & PCM_ENABLE_INPUT)) return 0; if (dmap->dma_mode == DMODE_OUTPUT) { /* Direction change */ DMAbuf_sync(dev); DMAbuf_reset(dev); dmap->dma_mode = DMODE_NONE; } if (!dmap->dma_mode) { reorganize_buffers(dev, dmap, 1); if ((err = adev->d->prepare_for_input(dev, dmap->fragment_size, dmap->nbufs)) < 0) return err; dmap->dma_mode = DMODE_INPUT; } if (!(dmap->flags & DMA_ACTIVE)) { if (dmap->needs_reorg) reorganize_buffers(dev, dmap, 0); local_start_dma(adev, dmap->raw_buf_phys, dmap->bytes_in_use, DMA_MODE_READ); adev->d->start_input(dev, dmap->raw_buf_phys + dmap->qtail * dmap->fragment_size, dmap->fragment_size, 0); dmap->flags |= DMA_ACTIVE; if (adev->d->trigger) adev->d->trigger(dev, adev->enable_bits * adev->go); } return 0; }
/* acquires lock */ int DMAbuf_getwrbuffer(int dev, char **buf, int *size, int dontblock) { struct audio_operations *adev = audio_devs[dev]; unsigned long flags; int err = -EIO; struct dma_buffparms *dmap = adev->dmap_out; if (dmap->mapping_flags & DMA_MAP_MAPPED) { /* printk(KERN_DEBUG "Sound: Can't write to mmapped device (3)\n");*/ return -EINVAL; } spin_lock_irqsave(&dmap->lock,flags); if (dmap->needs_reorg) reorganize_buffers(dev, dmap, 0); if (dmap->dma_mode == DMODE_INPUT) { /* Direction change */ spin_unlock_irqrestore(&dmap->lock,flags); DMAbuf_reset(dev); spin_lock_irqsave(&dmap->lock,flags); } dmap->dma_mode = DMODE_OUTPUT; while (find_output_space(dev, buf, size) <= 0) { spin_unlock_irqrestore(&dmap->lock,flags); if ((err = output_sleep(dev, dontblock)) < 0) { return err; } spin_lock_irqsave(&dmap->lock,flags); } spin_unlock_irqrestore(&dmap->lock,flags); return 0; }
/* MUST be called with holding the dmap->lock */ void DMAbuf_launch_output(int dev, struct dma_buffparms *dmap) { struct audio_operations *adev = audio_devs[dev]; if (!((adev->enable_bits * adev->go) & PCM_ENABLE_OUTPUT)) return; /* Don't start DMA yet */ dmap->dma_mode = DMODE_OUTPUT; if (!(dmap->flags & DMA_ACTIVE) || !(adev->flags & DMA_AUTOMODE) || (dmap->flags & DMA_NODMA)) { if (!(dmap->flags & DMA_STARTED)) { reorganize_buffers(dev, dmap, 0); if (adev->d->prepare_for_output(dev, dmap->fragment_size, dmap->nbufs)) return; if (!(dmap->flags & DMA_NODMA)) local_start_dma(adev, dmap->raw_buf_phys, dmap->bytes_in_use,DMA_MODE_WRITE); dmap->flags |= DMA_STARTED; } if (dmap->counts[dmap->qhead] == 0) dmap->counts[dmap->qhead] = dmap->fragment_size; dmap->dma_mode = DMODE_OUTPUT; adev->d->output_block(dev, dmap->raw_buf_phys + dmap->qhead * dmap->fragment_size, dmap->counts[dmap->qhead], 1); if (adev->d->trigger) adev->d->trigger(dev,adev->enable_bits * adev->go); } dmap->flags |= DMA_ACTIVE; }
/* acquires lock */ int DMAbuf_getrdbuffer(int dev, char **buf, int *len, int dontblock) { struct audio_operations *adev = audio_devs[dev]; unsigned long flags; int err = 0, n = 0; struct dma_buffparms *dmap = adev->dmap_in; int go; if (!(adev->open_mode & OPEN_READ)) return -EIO; spin_lock_irqsave(&dmap->lock,flags); if (dmap->needs_reorg) reorganize_buffers(dev, dmap, 0); if (adev->dmap_in->mapping_flags & DMA_MAP_MAPPED) { /* printk(KERN_WARNING "Sound: Can't read from mmapped device (1)\n");*/ spin_unlock_irqrestore(&dmap->lock,flags); return -EINVAL; } else while (dmap->qlen <= 0 && n++ < 10) { long timeout = MAX_SCHEDULE_TIMEOUT; if (!(adev->enable_bits & PCM_ENABLE_INPUT) || !adev->go) { spin_unlock_irqrestore(&dmap->lock,flags); return -EAGAIN; } if ((err = DMAbuf_activate_recording(dev, dmap)) < 0) { spin_unlock_irqrestore(&dmap->lock,flags); return err; } /* Wait for the next block */ if (dontblock) { spin_unlock_irqrestore(&dmap->lock,flags); return -EAGAIN; } if ((go = adev->go)) timeout = dmabuf_timeout(dmap); spin_unlock_irqrestore(&dmap->lock,flags); timeout = interruptible_sleep_on_timeout(&adev->in_sleeper, timeout); if (!timeout) { /* FIXME: include device name */ err = -EIO; printk(KERN_WARNING "Sound: DMA (input) timed out - IRQ/DRQ config error?\n"); dma_reset_input(dev); } else err = -EINTR; spin_lock_irqsave(&dmap->lock,flags); } spin_unlock_irqrestore(&dmap->lock,flags); if (dmap->qlen <= 0) return err ? err : -EINTR; *buf = &dmap->raw_buf[dmap->qhead * dmap->fragment_size + dmap->counts[dmap->qhead]]; *len = dmap->fragment_size - dmap->counts[dmap->qhead]; return dmap->qhead; }
static void dma_reset_input(int dev) { struct audio_operations *adev = audio_devs[dev]; unsigned long flags; struct dma_buffparms *dmap = adev->dmap_in; spin_lock_irqsave(&dmap->lock,flags); if (!(adev->flags & DMA_DUPLEX) || !adev->d->halt_input) adev->d->halt_io(dev); else adev->d->halt_input(dev); adev->dmap_in->flags &= ~DMA_STARTED; dmap->qlen = dmap->qhead = dmap->qtail = dmap->user_counter = 0; dmap->byte_counter = 0; reorganize_buffers(dev, adev->dmap_in, 1); spin_unlock_irqrestore(&dmap->lock,flags); }
static void dma_reset_output(int dev) { struct audio_operations *adev = audio_devs[dev]; unsigned long flags,f ; struct dma_buffparms *dmap = adev->dmap_out; if (!(dmap->flags & DMA_STARTED)) /* DMA is not active */ return; /* * First wait until the current fragment has been played completely */ spin_lock_irqsave(&dmap->lock,flags); adev->dmap_out->flags |= DMA_SYNCING; adev->dmap_out->underrun_count = 0; if (!signal_pending(current) && adev->dmap_out->qlen && adev->dmap_out->underrun_count == 0){ spin_unlock_irqrestore(&dmap->lock,flags); interruptible_sleep_on_timeout(&adev->out_sleeper, dmabuf_timeout(dmap)); spin_lock_irqsave(&dmap->lock,flags); } adev->dmap_out->flags &= ~(DMA_SYNCING | DMA_ACTIVE); /* * Finally shut the device off */ if (!(adev->flags & DMA_DUPLEX) || !adev->d->halt_output) adev->d->halt_io(dev); else adev->d->halt_output(dev); adev->dmap_out->flags &= ~DMA_STARTED; f=claim_dma_lock(); clear_dma_ff(dmap->dma); disable_dma(dmap->dma); release_dma_lock(f); dmap->byte_counter = 0; reorganize_buffers(dev, adev->dmap_out, 0); dmap->qlen = dmap->qhead = dmap->qtail = dmap->user_counter = 0; spin_unlock_irqrestore(&dmap->lock,flags); }
/* called with dmap->lock held in irq context */ static void do_inputintr(int dev) { struct audio_operations *adev = audio_devs[dev]; struct dma_buffparms *dmap = adev->dmap_in; if (dmap->raw_buf == NULL) { printk(KERN_ERR "Sound: Fatal error. Audio interrupt after freeing buffers.\n"); return; } if (dmap->mapping_flags & DMA_MAP_MAPPED) { dmap->qtail = (dmap->qtail + 1) % dmap->nbufs; if (dmap->qtail == 0) { /* Wrapped */ dmap->byte_counter += dmap->bytes_in_use; if (dmap->byte_counter >= dmap->max_byte_counter) { /* Overflow */ long decr = dmap->byte_counter; dmap->byte_counter = (dmap->byte_counter % dmap->bytes_in_use) + dmap->bytes_in_use; decr -= dmap->byte_counter; dmap->user_counter -= decr; } } dmap->qlen++; if (!(adev->flags & DMA_AUTOMODE)) { if (dmap->needs_reorg) reorganize_buffers(dev, dmap, 0); local_start_dma(adev, dmap->raw_buf_phys, dmap->bytes_in_use,DMA_MODE_READ); adev->d->start_input(dev, dmap->raw_buf_phys + dmap->qtail * dmap->fragment_size, dmap->fragment_size, 1); if (adev->d->trigger) adev->d->trigger(dev, adev->enable_bits * adev->go); } dmap->flags |= DMA_ACTIVE; } else if (dmap->qlen >= (dmap->nbufs - 1)) { printk(KERN_WARNING "Sound: Recording overrun\n"); dmap->underrun_count++; /* Just throw away the oldest fragment but keep the engine running */ dmap->qhead = (dmap->qhead + 1) % dmap->nbufs; dmap->qtail = (dmap->qtail + 1) % dmap->nbufs; } else if (dmap->qlen >= 0 && dmap->qlen < dmap->nbufs) { dmap->qlen++; dmap->qtail = (dmap->qtail + 1) % dmap->nbufs; if (dmap->qtail == 0) { /* Wrapped */ dmap->byte_counter += dmap->bytes_in_use; if (dmap->byte_counter >= dmap->max_byte_counter) { /* Overflow */ long decr = dmap->byte_counter; dmap->byte_counter = (dmap->byte_counter % dmap->bytes_in_use) + dmap->bytes_in_use; decr -= dmap->byte_counter; dmap->user_counter -= decr; } } } if (!(adev->flags & DMA_AUTOMODE) || (dmap->flags & DMA_NODMA)) { local_start_dma(adev, dmap->raw_buf_phys, dmap->bytes_in_use, DMA_MODE_READ); adev->d->start_input(dev, dmap->raw_buf_phys + dmap->qtail * dmap->fragment_size, dmap->fragment_size, 1); if (adev->d->trigger) adev->d->trigger(dev,adev->enable_bits * adev->go); } dmap->flags |= DMA_ACTIVE; if (dmap->qlen > 0) { wake_up(&adev->in_sleeper); wake_up(&adev->poll_sleeper); } }