static int find_output_space(int dev, char **buf, int *size) { struct audio_operations *adev = audio_devs[dev]; struct dma_buffparms *dmap = adev->dmap_out; unsigned long flags; unsigned long active_offs; long len, offs; int maxfrags; int occupied_bytes = (dmap->user_counter % dmap->fragment_size); *buf = dmap->raw_buf; if (!(maxfrags = DMAbuf_space_in_queue(dev)) && !occupied_bytes) return 0; save_flags(flags); cli(); #ifdef BE_CONSERVATIVE active_offs = dmap->byte_counter + dmap->qhead * dmap->fragment_size; #else active_offs = DMAbuf_get_buffer_pointer(dev, dmap, DMODE_OUTPUT); /* Check for pointer wrapping situation */ if (active_offs < 0 || active_offs >= dmap->bytes_in_use) active_offs = 0; active_offs += dmap->byte_counter; #endif offs = (dmap->user_counter % dmap->bytes_in_use) & ~SAMPLE_ROUNDUP; if (offs < 0 || offs >= dmap->bytes_in_use) { restore_flags(flags); printk(KERN_ERR "Sound: Got unexpected offs %ld. Giving up.\n", offs); printk("Counter = %ld, bytes=%d\n", dmap->user_counter, dmap->bytes_in_use); return 0; } *buf = dmap->raw_buf + offs; len = active_offs + dmap->bytes_in_use - dmap->user_counter; /* Number of unused bytes in buffer */ if ((offs + len) > dmap->bytes_in_use) len = dmap->bytes_in_use - offs; if (len < 0) { restore_flags(flags); return 0; } if (len > ((maxfrags * dmap->fragment_size) - occupied_bytes)) len = (maxfrags * dmap->fragment_size) - occupied_bytes; *size = len & ~SAMPLE_ROUNDUP; restore_flags(flags); return (*size > 0); }
int audio_ioctl(int dev, struct file *file, unsigned int cmd, void __user *arg) { int val, count; unsigned long flags; struct dma_buffparms *dmap; int __user *p = arg; dev = dev >> 4; if (_IOC_TYPE(cmd) == 'C') { if (audio_devs[dev]->coproc) /* Coprocessor ioctl */ return audio_devs[dev]->coproc->ioctl(audio_devs[dev]->coproc->devc, cmd, arg, 0); /* else printk(KERN_DEBUG"/dev/dsp%d: No coprocessor for this device\n", dev); */ return -ENXIO; } else switch (cmd) { case SNDCTL_DSP_SYNC: if (!(audio_devs[dev]->open_mode & OPEN_WRITE)) return 0; if (audio_devs[dev]->dmap_out->fragment_size == 0) return 0; sync_output(dev); DMAbuf_sync(dev); DMAbuf_reset(dev); return 0; case SNDCTL_DSP_POST: if (!(audio_devs[dev]->open_mode & OPEN_WRITE)) return 0; if (audio_devs[dev]->dmap_out->fragment_size == 0) return 0; audio_devs[dev]->dmap_out->flags |= DMA_POST | DMA_DIRTY; sync_output(dev); dma_ioctl(dev, SNDCTL_DSP_POST, NULL); return 0; case SNDCTL_DSP_RESET: audio_devs[dev]->audio_mode = AM_NONE; DMAbuf_reset(dev); return 0; case SNDCTL_DSP_GETFMTS: val = audio_devs[dev]->format_mask | AFMT_MU_LAW; break; case SNDCTL_DSP_SETFMT: if (get_user(val, p)) return -EFAULT; val = set_format(dev, val); break; case SNDCTL_DSP_GETISPACE: if (!(audio_devs[dev]->open_mode & OPEN_READ)) return 0; if ((audio_devs[dev]->audio_mode & AM_WRITE) && !(audio_devs[dev]->flags & DMA_DUPLEX)) return -EBUSY; return dma_ioctl(dev, cmd, arg); case SNDCTL_DSP_GETOSPACE: if (!(audio_devs[dev]->open_mode & OPEN_WRITE)) return -EPERM; if ((audio_devs[dev]->audio_mode & AM_READ) && !(audio_devs[dev]->flags & DMA_DUPLEX)) return -EBUSY; return dma_ioctl(dev, cmd, arg); case SNDCTL_DSP_NONBLOCK: spin_lock(&file->f_lock); file->f_flags |= O_NONBLOCK; spin_unlock(&file->f_lock); return 0; case SNDCTL_DSP_GETCAPS: val = 1 | DSP_CAP_MMAP; /* Revision level of this ioctl() */ if (audio_devs[dev]->flags & DMA_DUPLEX && audio_devs[dev]->open_mode == OPEN_READWRITE) val |= DSP_CAP_DUPLEX; if (audio_devs[dev]->coproc) val |= DSP_CAP_COPROC; if (audio_devs[dev]->d->local_qlen) /* Device has hidden buffers */ val |= DSP_CAP_BATCH; if (audio_devs[dev]->d->trigger) /* Supports SETTRIGGER */ val |= DSP_CAP_TRIGGER; break; case SOUND_PCM_WRITE_RATE: if (get_user(val, p)) return -EFAULT; val = audio_devs[dev]->d->set_speed(dev, val); break; case SOUND_PCM_READ_RATE: val = audio_devs[dev]->d->set_speed(dev, 0); break; case SNDCTL_DSP_STEREO: if (get_user(val, p)) return -EFAULT; if (val > 1 || val < 0) return -EINVAL; val = audio_devs[dev]->d->set_channels(dev, val + 1) - 1; break; case SOUND_PCM_WRITE_CHANNELS: if (get_user(val, p)) return -EFAULT; val = audio_devs[dev]->d->set_channels(dev, val); break; case SOUND_PCM_READ_CHANNELS: val = audio_devs[dev]->d->set_channels(dev, 0); break; case SOUND_PCM_READ_BITS: val = audio_devs[dev]->d->set_bits(dev, 0); break; case SNDCTL_DSP_SETDUPLEX: if (audio_devs[dev]->open_mode != OPEN_READWRITE) return -EPERM; return (audio_devs[dev]->flags & DMA_DUPLEX) ? 0 : -EIO; case SNDCTL_DSP_PROFILE: if (get_user(val, p)) return -EFAULT; if (audio_devs[dev]->open_mode & OPEN_WRITE) audio_devs[dev]->dmap_out->applic_profile = val; if (audio_devs[dev]->open_mode & OPEN_READ) audio_devs[dev]->dmap_in->applic_profile = val; return 0; case SNDCTL_DSP_GETODELAY: dmap = audio_devs[dev]->dmap_out; if (!(audio_devs[dev]->open_mode & OPEN_WRITE)) return -EINVAL; if (!(dmap->flags & DMA_ALLOC_DONE)) { val=0; break; } spin_lock_irqsave(&dmap->lock,flags); /* Compute number of bytes that have been played */ count = DMAbuf_get_buffer_pointer (dev, dmap, DMODE_OUTPUT); if (count < dmap->fragment_size && dmap->qhead != 0) count += dmap->bytes_in_use; /* Pointer wrap not handled yet */ count += dmap->byte_counter; /* Subtract current count from the number of bytes written by app */ count = dmap->user_counter - count; if (count < 0) count = 0; spin_unlock_irqrestore(&dmap->lock,flags); val = count; break; default: return dma_ioctl(dev, cmd, arg); } return put_user(val, p); }