static unsigned int audio_poll(struct file *file, struct poll_table_struct *wait) { audio_state_t *state = file->private_data; audio_stream_t *is = state->input_stream; audio_stream_t *os = state->output_stream; unsigned int mask = 0; DPRINTK("audio_poll(): mode=%s%s\n", (file->f_mode & FMODE_READ) ? "r" : "", (file->f_mode & FMODE_WRITE) ? "w" : ""); if (file->f_mode & FMODE_READ) { /* Start audio input if not already active */ if (!is->active) { if (!is->buffers && audio_setup_buf(is)) return -ENOMEM; audio_prime_rx(state); } poll_wait(file, &is->wq, wait); } if (file->f_mode & FMODE_WRITE) { if (!os->buffers && audio_setup_buf(os)) return -ENOMEM; poll_wait(file, &os->wq, wait); } if (file->f_mode & FMODE_READ) if (( is->mapped && is->bytecount > 0) || (!is->mapped && atomic_read(&is->sem.count) > 0)) mask |= POLLIN | POLLRDNORM; if (file->f_mode & FMODE_WRITE) if (( os->mapped && os->bytecount > 0) || (!os->mapped && atomic_read(&os->sem.count) > 0)) mask |= POLLOUT | POLLWRNORM; DPRINTK("audio_poll() returned mask of %s%s\n", (mask & POLLIN) ? "r" : "", (mask & POLLOUT) ? "w" : ""); return mask; }
static unsigned int audio_poll(struct file *file, struct poll_table_struct *wait) { audio_state_t *state = file->private_data; audio_stream_t *is = state->input_stream; audio_stream_t *os = state->output_stream; unsigned int mask = 0; if (file->f_mode & FMODE_READ) { /* Start audio input if not already active */ if (!is->buffers && audio_setup_buf(is)) return -ENOMEM; if (DCSR(is->dma_ch) & DCSR_STOPSTATE) { DDADR(is->dma_ch) = is->buffers[is->dma_frag].dma_desc->ddadr; DCSR(is->dma_ch) = DCSR_RUN; } poll_wait(file, &is->frag_wq, wait); } if (file->f_mode & FMODE_WRITE) { if (!os->buffers && audio_setup_buf(os)) return -ENOMEM; poll_wait(file, &os->frag_wq, wait); } if (file->f_mode & FMODE_READ) if (( is->mapped && is->bytecount > 0) || (!is->mapped && atomic_read(&is->sem.count) > 0)) mask |= POLLIN | POLLRDNORM; if (file->f_mode & FMODE_WRITE) if (( os->mapped && os->bytecount > 0) || (!os->mapped && atomic_read(&os->sem.count) > 0)) mask |= POLLOUT | POLLWRNORM; return mask; }
static int audio_mmap(struct file *file, struct vm_area_struct *vma) { audio_state_t *state = file->private_data; audio_stream_t *s; unsigned long size, vma_addr; int i, ret; if (vma->vm_pgoff != 0) return -EINVAL; if (vma->vm_flags & VM_WRITE) { if (!state->wr_ref) return -EINVAL;; s = state->output_stream; } else if (vma->vm_flags & VM_READ) { if (!state->rd_ref) return -EINVAL; s = state->input_stream; } else return -EINVAL; if (s->mapped) return -EINVAL; size = vma->vm_end - vma->vm_start; if (size != s->fragsize * s->nbfrags) return -EINVAL; if (!s->buffers && audio_setup_buf(s)) return -ENOMEM; vma_addr = vma->vm_start; for (i = 0; i < s->nbfrags; i++) { audio_buf_t *buf = &s->buffers[i]; if (!buf->master) continue; ret = remap_page_range(vma_addr, buf->dma_desc->dsadr, buf->master, vma->vm_page_prot); if (ret) return ret; vma_addr += buf->master; } for (i = 0; i < s->nbfrags; i++) s->buffers[i].dma_desc->ddadr &= ~DDADR_STOP; s->mapped = 1; return 0; }
static int audio_set_fragments(audio_stream_t *s, int val) { if (s->active) return -EBUSY; if (s->buffers) audio_discard_buf(s); s->nbfrags = (val >> 16) & 0x7FFF; val &= 0xffff; if (val < 4) val = 4; if (val > 15) val = 15; s->fragsize = 1 << val; if (s->nbfrags < 2) s->nbfrags = 2; if (s->nbfrags * s->fragsize > 128 * 1024) s->nbfrags = 128 * 1024 / s->fragsize; if (audio_setup_buf(s)) return -ENOMEM; return val|(s->nbfrags << 16); }
/* * Validate and sets up buffer fragments, etc. */ static int audio_set_fragments(audio_stream_t *s, int val) { if (s->mapped || DCSR(s->dma_ch) & DCSR_RUN) return -EBUSY; if (s->buffers) audio_clear_buf(s); s->nbfrags = (val >> 16) & 0x7FFF; val &= 0xffff; if (val < 5) val = 5; if (val > 15) val = 15; s->fragsize = 1 << val; if (s->nbfrags < 2) s->nbfrags = 2; if (s->nbfrags * s->fragsize > 256 * 1024) s->nbfrags = 256 * 1024 / s->fragsize; if (audio_setup_buf(s)) return -ENOMEM; return val|(s->nbfrags << 16); }
static int audio_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg) { audio_state_t *state = file->private_data; audio_stream_t *os = state->output_stream; audio_stream_t *is = state->input_stream; long val; DPRINTK(__FILE__ " audio_ioctl 0x%08x\n", cmd); /* dispatch based on command */ switch (cmd) { case OSS_GETVERSION: return put_user(SOUND_VERSION, (int *)arg); case SNDCTL_DSP_GETBLKSIZE: if (file->f_mode & FMODE_WRITE) return put_user(os->fragsize, (int *)arg); else return put_user(is->fragsize, (int *)arg); case SNDCTL_DSP_GETCAPS: val = DSP_CAP_REALTIME|DSP_CAP_TRIGGER|DSP_CAP_MMAP; if (is && os) val |= DSP_CAP_DUPLEX; return put_user(val, (int *)arg); case SNDCTL_DSP_SETFRAGMENT: if (get_user(val, (long *) arg)) return -EFAULT; if (file->f_mode & FMODE_READ) { int ret = audio_set_fragments(is, val); if (ret < 0) return ret; ret = put_user(ret, (int *)arg); if (ret) return ret; } if (file->f_mode & FMODE_WRITE) { int ret = audio_set_fragments(os, val); if (ret < 0) return ret; ret = put_user(ret, (int *)arg); if (ret) return ret; } return 0; case SNDCTL_DSP_SYNC: return audio_sync(file); case SNDCTL_DSP_SETDUPLEX: return 0; case SNDCTL_DSP_POST: return 0; case SNDCTL_DSP_GETTRIGGER: val = 0; if (file->f_mode & FMODE_READ && is->active && !is->stopped) val |= PCM_ENABLE_INPUT; if (file->f_mode & FMODE_WRITE && os->active && !os->stopped) val |= PCM_ENABLE_OUTPUT; return put_user(val, (int *)arg); case SNDCTL_DSP_SETTRIGGER: if (get_user(val, (int *)arg)) return -EFAULT; if (file->f_mode & FMODE_READ) { if (val & PCM_ENABLE_INPUT) { unsigned long flags; if (!is->active) { if (!is->buffers && audio_setup_buf(is)) return -ENOMEM; audio_prime_rx(state); } local_irq_save(flags); is->stopped = 0; audio_process_dma(is); local_irq_restore(flags); } else { audio_stop_dma(is); } } if (file->f_mode & FMODE_WRITE) { if (val & PCM_ENABLE_OUTPUT) { unsigned long flags; if (!os->buffers && audio_setup_buf(os)) return -ENOMEM; local_irq_save(flags); if (os->mapped && !os->pending_frags) { os->pending_frags = os->nbfrags; sema_init(&os->sem, 0); os->active = 1; } os->stopped = 0; audio_process_dma(os); local_irq_restore(flags); } else { audio_stop_dma(os); } } return 0; case SNDCTL_DSP_GETOPTR: case SNDCTL_DSP_GETIPTR: { count_info inf = { 0, }; audio_stream_t *s = (cmd == SNDCTL_DSP_GETOPTR) ? os : is; int bytecount, offset; unsigned long flags; if ((s == is && !(file->f_mode & FMODE_READ)) || (s == os && !(file->f_mode & FMODE_WRITE))) return -EINVAL; if (s->active) { local_irq_save(flags); offset = audio_get_dma_pos(s); inf.ptr = s->dma_tail * s->fragsize + offset; bytecount = s->bytecount + offset; s->bytecount = -offset; inf.blocks = s->fragcount; s->fragcount = 0; local_irq_restore(flags); if (bytecount < 0) bytecount = 0; inf.bytes = bytecount; } return copy_to_user((void *)arg, &inf, sizeof(inf)); } case SNDCTL_DSP_GETOSPACE: case SNDCTL_DSP_GETISPACE: { audio_buf_info inf = { 0, }; audio_stream_t *s = (cmd == SNDCTL_DSP_GETOSPACE) ? os : is; if ((s == is && !(file->f_mode & FMODE_READ)) || (s == os && !(file->f_mode & FMODE_WRITE))) return -EINVAL; if (!s->buffers && audio_setup_buf(s)) return -ENOMEM; inf.bytes = atomic_read(&s->sem.count) * s->fragsize; /* inf.bytes -= s->buffers[s->usr_head].offset; */ inf.fragments = inf.bytes / s->fragsize; inf.fragsize = s->fragsize; inf.fragstotal = s->nbfrags; return copy_to_user((void *)arg, &inf, sizeof(inf)); } case SNDCTL_DSP_NONBLOCK: file->f_flags |= O_NONBLOCK; return 0; case SNDCTL_DSP_RESET: if (file->f_mode & FMODE_READ) { audio_reset(is); if (state->need_tx_for_rx) { unsigned long flags; local_irq_save(flags); os->spin_idle = 0; local_irq_restore(flags); } } if (file->f_mode & FMODE_WRITE) { audio_reset(os); } return 0; default: /* * Let the client of this module handle the * non generic ioctls */ return state->client_ioctl(inode, file, cmd, arg); } return 0; }
static int audio_read(struct file *file, char *buffer, size_t count, loff_t * ppos) { char *buffer0 = buffer; audio_state_t *state = file->private_data; audio_stream_t *s = state->input_stream; int chunksize, ret = 0; unsigned long flags; DPRINTK("audio_read: count=%d\n", count); if (ppos != &file->f_pos) return -ESPIPE; if (s->mapped) return -ENXIO; if (!s->active) { if (!s->buffers && audio_setup_buf(s)) return -ENOMEM; audio_prime_rx(state); } while (count > 0) { audio_buf_t *b = &s->buffers[s->usr_head]; /* Wait for a buffer to become full */ if (file->f_flags & O_NONBLOCK) { ret = -EAGAIN; if (down_trylock(&s->sem)) break; } else { ret = -ERESTARTSYS; if (down_interruptible(&s->sem)) break; } /* Grab data from the current buffer */ chunksize = s->fragsize - b->offset; if (chunksize > count) chunksize = count; DPRINTK("read %d from %d\n", chunksize, s->usr_head); if (copy_to_user(buffer, b->data+ b->offset, chunksize)) { up(&s->sem); return -EFAULT; } buffer += chunksize; count -= chunksize; b->offset += chunksize; if (b->offset < s->fragsize) { up(&s->sem); break; } /* Update pointers and return current fragment to DMA */ b->offset = 0; if (++s->usr_head >= s->nbfrags) s->usr_head = 0; local_irq_save(flags); s->pending_frags++; audio_process_dma(s); local_irq_restore(flags); } if ((buffer - buffer0)) ret = buffer - buffer0; DPRINTK("audio_read: return=%d\n", ret); return ret; }
/* * Driver interface functions */ static int audio_write(struct file *file, const char *buffer, size_t count, loff_t * ppos) { const char *buffer0 = buffer; audio_state_t *state = file->private_data; audio_stream_t *s = state->output_stream; int chunksize, ret = 0; unsigned long flags; DPRINTK("audio_write: count=%d\n", count); if (s->mapped) return -ENXIO; if (!s->buffers && audio_setup_buf(s)) return -ENOMEM; while (count > 0) { audio_buf_t *b = &s->buffers[s->usr_head]; /* Wait for a buffer to become free */ if (file->f_flags & O_NONBLOCK) { ret = -EAGAIN; if (down_trylock(&s->sem)) break; } else { ret = -ERESTARTSYS; if (down_interruptible(&s->sem)) break; } /* Feed the current buffer */ if(state->sound_mode == MONO) { /* mono mode play back */ if(count > s->fragsize/2) chunksize = s->fragsize/2; else chunksize = count; if (copy_from_user_mono_stereo(b->data + b->offset, buffer, chunksize)) { up(&s->sem); return -EFAULT; } b->offset += chunksize*2; } else { /* stereo mode play back */ chunksize = s->fragsize - b->offset; if (chunksize > count) chunksize = count; DPRINTK("write %d to %d\n", chunksize, s->usr_head); if (copy_from_user(b->data + b->offset, buffer, chunksize)) { up(&s->sem); return -EFAULT; } b->offset += chunksize; } count -= chunksize; buffer += chunksize; if (b->offset < s->fragsize) { up(&s->sem); break; } /* Update pointers and send current fragment to DMA */ b->offset = 0; if (++s->usr_head >= s->nbfrags) s->usr_head = 0; local_irq_save(flags); s->pending_frags++; s->active = 1; audio_process_dma(s); local_irq_restore(flags); } if ((buffer - buffer0)) ret = buffer - buffer0; DPRINTK("audio_write: return=%d\n", ret); return ret; }
static int audio_ioctl( struct inode *inode, struct file *file, uint cmd, ulong arg) { audio_state_t *state = file->private_data; audio_stream_t *os = state->output_stream; audio_stream_t *is = state->input_stream; long val; switch (cmd) { case OSS_GETVERSION: return put_user(SOUND_VERSION, (int *)arg); case SNDCTL_DSP_GETBLKSIZE: if (file->f_mode & FMODE_WRITE) return put_user(os->fragsize, (int *)arg); else return put_user(is->fragsize, (int *)arg); case SNDCTL_DSP_GETCAPS: val = DSP_CAP_REALTIME|DSP_CAP_TRIGGER|DSP_CAP_MMAP; if (is && os) val |= DSP_CAP_DUPLEX; return put_user(val, (int *)arg); case SNDCTL_DSP_SETFRAGMENT: if (get_user(val, (long *) arg)) return -EFAULT; if (file->f_mode & FMODE_READ) { int ret = audio_set_fragments(is, val); if (ret < 0) return ret; ret = put_user(ret, (int *)arg); if (ret) return ret; } if (file->f_mode & FMODE_WRITE) { int ret = audio_set_fragments(os, val); if (ret < 0) return ret; ret = put_user(ret, (int *)arg); if (ret) return ret; } return 0; case SNDCTL_DSP_SYNC: return audio_sync(file); case SNDCTL_DSP_SETDUPLEX: return 0; case SNDCTL_DSP_POST: return 0; case SNDCTL_DSP_GETTRIGGER: val = 0; if (file->f_mode & FMODE_READ && DCSR(is->dma_ch) & DCSR_RUN) val |= PCM_ENABLE_INPUT; if (file->f_mode & FMODE_WRITE && DCSR(os->dma_ch) & DCSR_RUN) val |= PCM_ENABLE_OUTPUT; return put_user(val, (int *)arg); case SNDCTL_DSP_SETTRIGGER: if (get_user(val, (int *)arg)) return -EFAULT; if (file->f_mode & FMODE_READ) { if (val & PCM_ENABLE_INPUT) { if (!is->buffers && audio_setup_buf(is)) return -ENOMEM; if (!(DCSR(is->dma_ch) & DCSR_RUN)) { audio_buf_t *b = &is->buffers[is->dma_frag]; DDADR(is->dma_ch) = b->dma_desc->ddadr; DCSR(is->dma_ch) = DCSR_RUN; } } else { DCSR(is->dma_ch) = 0; } } if (file->f_mode & FMODE_WRITE) { if (val & PCM_ENABLE_OUTPUT) { if (!os->buffers && audio_setup_buf(os)) return -ENOMEM; if (!(DCSR(os->dma_ch) & DCSR_RUN)) { audio_buf_t *b = &os->buffers[os->dma_frag]; DDADR(os->dma_ch) = b->dma_desc->ddadr; DCSR(os->dma_ch) = DCSR_RUN; } } else { DCSR(os->dma_ch) = 0; } } return 0; case SNDCTL_DSP_GETOSPACE: case SNDCTL_DSP_GETISPACE: { audio_buf_info inf = { 0, }; audio_stream_t *s = (cmd == SNDCTL_DSP_GETOSPACE) ? os : is; if ((s == is && !(file->f_mode & FMODE_READ)) || (s == os && !(file->f_mode & FMODE_WRITE))) return -EINVAL; if (!s->buffers && audio_setup_buf(s)) return -ENOMEM; inf.bytes = atomic_read(&s->sem.count) * s->fragsize; inf.bytes -= s->buffers[s->usr_frag].offset; inf.fragments = inf.bytes / s->fragsize; inf.fragsize = s->fragsize; inf.fragstotal = s->nbfrags; return copy_to_user((void *)arg, &inf, sizeof(inf)); } case SNDCTL_DSP_GETOPTR: case SNDCTL_DSP_GETIPTR: { count_info inf = { 0, }; audio_stream_t *s = (cmd == SNDCTL_DSP_GETOPTR) ? os : is; dma_addr_t ptr; int bytecount, offset, flags; if ((s == is && !(file->f_mode & FMODE_READ)) || (s == os && !(file->f_mode & FMODE_WRITE))) return -EINVAL; if (DCSR(s->dma_ch) & DCSR_RUN) { audio_buf_t *b; save_flags_cli(flags); ptr = (s->output) ? DSADR(s->dma_ch) : DTADR(s->dma_ch); b = &s->buffers[s->dma_frag]; offset = ptr - b->dma_desc->dsadr; if (offset >= s->fragsize) offset = s->fragsize - 4; } else { save_flags(flags); offset = 0; } inf.ptr = s->dma_frag * s->fragsize + offset; bytecount = s->bytecount + offset; s->bytecount = -offset; inf.blocks = s->fragcount; s->fragcount = 0; restore_flags(flags); if (bytecount < 0) bytecount = 0; inf.bytes = bytecount; return copy_to_user((void *)arg, &inf, sizeof(inf)); } case SNDCTL_DSP_NONBLOCK: file->f_flags |= O_NONBLOCK; return 0; case SNDCTL_DSP_RESET: if (file->f_mode & FMODE_WRITE) audio_clear_buf(os); if (file->f_mode & FMODE_READ) audio_clear_buf(is); return 0; default: return state->client_ioctl(inode, file, cmd, arg); } return 0; }
static int audio_read(struct file *file, char *buffer, size_t count, loff_t * ppos) { char *buffer0 = buffer; audio_state_t *state = file->private_data; audio_stream_t *s = state->input_stream; int chunksize, ret = 0; if (ppos != &file->f_pos) return -ESPIPE; if (s->mapped) return -ENXIO; if (!s->buffers && audio_setup_buf(s)) return -ENOMEM; while (count > 0) { audio_buf_t *b = &s->buffers[s->usr_frag]; /* prime DMA */ if (DCSR(s->dma_ch) & DCSR_STOPSTATE) { DDADR(s->dma_ch) = s->buffers[s->dma_frag].dma_desc->ddadr; DCSR(s->dma_ch) = DCSR_RUN; } /* Wait for a buffer to become full */ if (file->f_flags & O_NONBLOCK) { ret = -EAGAIN; if (down_trylock(&s->sem)) break; } else { ret = -ERESTARTSYS; if (down_interruptible(&s->sem)) break; } /* Grab data from current buffer */ chunksize = s->fragsize - b->offset; if (chunksize > count) chunksize = count; if (copy_to_user(buffer, b->data + b->offset, chunksize)) { up(&s->sem); return -EFAULT; } b->offset += chunksize; buffer += chunksize; count -= chunksize; if (b->offset < s->fragsize) { up(&s->sem); break; } /* * Make this buffer available for DMA again. * We unlock this fragment's checkpoint descriptor and * kick DMA if it is idle. Using checkpoint descriptors * allows for control operations without the need for * stopping the DMA channel if it is already running. */ b->offset = 0; b->dma_desc->ddadr &= ~DDADR_STOP; /* move the index to the next fragment */ if (++s->usr_frag >= s->nbfrags) s->usr_frag = 0; } if ((buffer - buffer0)) ret = buffer - buffer0; return ret; }
static int audio_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg) { audio_state_t *state = (audio_state_t *)file->private_data; audio_stream_t *os = state->output_stream; audio_stream_t *is = state->input_stream; long val; /* dispatch based on command */ switch (cmd) { case OSS_GETVERSION: return put_user(SOUND_VERSION, (int *)arg); case SNDCTL_DSP_GETBLKSIZE: if (file->f_mode & FMODE_WRITE) return put_user(os->fragsize, (int *)arg); else return put_user(is->fragsize, (int *)arg); case SNDCTL_DSP_GETCAPS: val = DSP_CAP_REALTIME|DSP_CAP_TRIGGER|DSP_CAP_MMAP; if (is && os) val |= DSP_CAP_DUPLEX; return put_user(val, (int *)arg); case SNDCTL_DSP_SETFRAGMENT: if (get_user(val, (long *) arg)) return -EFAULT; if (file->f_mode & FMODE_READ) { int ret = audio_set_fragments(is, val); if (ret < 0) return ret; ret = put_user(ret, (int *)arg); if (ret) return ret; } if (file->f_mode & FMODE_WRITE) { int ret = audio_set_fragments(os, val); if (ret < 0) return ret; ret = put_user(ret, (int *)arg); if (ret) return ret; } return 0; case SNDCTL_DSP_SYNC: return audio_sync(file); case SNDCTL_DSP_SETDUPLEX: return 0; case SNDCTL_DSP_POST: return 0; case SNDCTL_DSP_GETTRIGGER: val = 0; if (file->f_mode & FMODE_READ && is->active && !is->stopped) val |= PCM_ENABLE_INPUT; if (file->f_mode & FMODE_WRITE && os->active && !os->stopped) val |= PCM_ENABLE_OUTPUT; return put_user(val, (int *)arg); case SNDCTL_DSP_SETTRIGGER: if (get_user(val, (int *)arg)) return -EFAULT; if (file->f_mode & FMODE_READ) { if (val & PCM_ENABLE_INPUT) { if (!is->active) { if (!is->buffers && audio_setup_buf(is)) return -ENOMEM; audio_prime_dma(is); } audio_check_tx_spin(state); if (is->stopped) { is->stopped = 0; sa1100_dma_resume(is->dma_ch); } } else { sa1100_dma_stop(is->dma_ch); is->stopped = 1; } } if (file->f_mode & FMODE_WRITE) { if (val & PCM_ENABLE_OUTPUT) { if (!os->active) { if (!os->buffers && audio_setup_buf(os)) return -ENOMEM; if (os->mapped) audio_prime_dma(os); } if (os->stopped) { os->stopped = 0; sa1100_dma_resume(os->dma_ch); } } else { sa1100_dma_stop(os->dma_ch); os->stopped = 1; } } return 0; case SNDCTL_DSP_GETOPTR: case SNDCTL_DSP_GETIPTR: { count_info inf = { 0, }; audio_stream_t *s = (cmd == SNDCTL_DSP_GETOPTR) ? os : is; audio_buf_t *b; dma_addr_t ptr; int bytecount, offset, flags; if ((s == is && !(file->f_mode & FMODE_READ)) || (s == os && !(file->f_mode & FMODE_WRITE))) return -EINVAL; if (s->active) { save_flags_cli(flags); if (sa1100_dma_get_current(s->dma_ch, (void *)&b, &ptr) == 0) { offset = ptr - b->dma_addr; inf.ptr = (b - s->buffers) * s->fragsize + offset; } else offset = 0; bytecount = s->bytecount + offset; s->bytecount = -offset; inf.blocks = s->fragcount; s->fragcount = 0; restore_flags(flags); if (bytecount < 0) bytecount = 0; inf.bytes = bytecount; } return copy_to_user((void *)arg, &inf, sizeof(inf)); } case SNDCTL_DSP_GETOSPACE: { audio_buf_info inf = { 0, }; int i; if (!(file->f_mode & FMODE_WRITE)) return -EINVAL; if (!os->buffers && audio_setup_buf(os)) return -ENOMEM; for (i = 0; i < os->nbfrags; i++) { if (atomic_read(&os->buffers[i].sem.count) > 0) { if (os->buffers[i].size == 0) inf.fragments++; inf.bytes += os->fragsize - os->buffers[i].size; } } inf.fragstotal = os->nbfrags; inf.fragsize = os->fragsize; return copy_to_user((void *)arg, &inf, sizeof(inf)); } case SNDCTL_DSP_GETISPACE: { audio_buf_info inf = { 0, }; int i; if (!(file->f_mode & FMODE_READ)) return -EINVAL; if (!is->buffers && audio_setup_buf(is)) return -ENOMEM; for (i = 0; i < is->nbfrags; i++) { if (atomic_read(&is->buffers[i].sem.count) > 0) { if (is->buffers[i].size == is->fragsize) inf.fragments++; inf.bytes += is->buffers[i].size; } } inf.fragstotal = is->nbfrags; inf.fragsize = is->fragsize; return copy_to_user((void *)arg, &inf, sizeof(inf)); } case SNDCTL_DSP_NONBLOCK: file->f_flags |= O_NONBLOCK; return 0; case SNDCTL_DSP_GETODELAY: { int count = 0; int i; int flags; audio_buf_t *b; dma_addr_t ptr; if (!(file->f_mode & FMODE_WRITE)) return -EINVAL; if (!os->buffers && audio_setup_buf(os)) return -ENOMEM; save_flags_cli(flags); for (i = 0; i < os->nbfrags; i++) { /* if contains data */ if (atomic_read(&os->buffers[i].sem.count) <= 0) { count += os->fragsize; } } if (sa1100_dma_get_current(os->dma_ch, (void *)&b, &ptr) == 0) count -= ptr - b->dma_addr; restore_flags(flags); return put_user(count, (int *)arg); } case SNDCTL_DSP_RESET: if (file->f_mode & FMODE_READ) { if (state->tx_spinning) { sa1100_dma_set_spin(os->dma_ch, 0, 0); state->tx_spinning = 0; } audio_reset_buf(is); } if (file->f_mode & FMODE_WRITE) { audio_reset_buf(os); } return 0; default: /* * Let the client of this module handle the * non generic ioctls */ return state->client_ioctl(inode, file, cmd, arg); } return 0; }
static unsigned int audio_poll(struct file *file, struct poll_table_struct *wait) { audio_state_t *state = (audio_state_t *)file->private_data; audio_stream_t *is = state->input_stream; audio_stream_t *os = state->output_stream; unsigned int mask = 0; int i; DPRINTK("audio_poll(): mode=%s%s\n", (file->f_mode & FMODE_READ) ? "r" : "", (file->f_mode & FMODE_WRITE) ? "w" : ""); if (file->f_mode & FMODE_READ) { /* Start audio input if not already active */ if (!is->active) { if (!is->buffers && audio_setup_buf(is)) return -ENOMEM; audio_check_tx_spin(state); audio_prime_dma(is); } poll_wait(file, &is->wq, wait); } if (file->f_mode & FMODE_WRITE) { if (!os->buffers && audio_setup_buf(os)) return -ENOMEM; poll_wait(file, &os->wq, wait); } if (file->f_mode & FMODE_READ) { if (is->mapped) { if (is->bytecount > 0) mask |= POLLIN | POLLRDNORM; } else { for (i = 0; i < is->nbfrags; i++) { if (atomic_read(&is->buffers[i].sem.count) > 0) { mask |= POLLIN | POLLRDNORM; break; } } } } if (file->f_mode & FMODE_WRITE) { if (os->mapped) { if (os->bytecount > 0) mask |= POLLOUT | POLLWRNORM; } else { for (i = 0; i < os->nbfrags; i++) { if (atomic_read(&os->buffers[i].sem.count) > 0) { mask |= POLLOUT | POLLWRNORM; break; } } } } DPRINTK("audio_poll() returned mask of %s%s\n", (mask & POLLIN) ? "r" : "", (mask & POLLOUT) ? "w" : ""); return mask; }
static int audio_read(struct file *file, char *buffer, size_t count, loff_t * ppos) { char *buffer0 = buffer; audio_state_t *state = (audio_state_t *)file->private_data; audio_stream_t *s = state->input_stream; int chunksize, ret = 0; DPRINTK("audio_read: count=%d\n", count); if (ppos != &file->f_pos) return -ESPIPE; if (s->mapped) return -ENXIO; if (!s->active) { if (!s->buffers && audio_setup_buf(s)) return -ENOMEM; audio_check_tx_spin(state); audio_prime_dma(s); } while (count > 0) { audio_buf_t *b = s->buf; /* Wait for a buffer to become full */ if (file->f_flags & O_NONBLOCK) { ret = -EAGAIN; if (down_trylock(&b->sem)) break; } else { ret = -ERESTARTSYS; if (down_interruptible(&b->sem)) break; } /* Grab data from the current buffer */ chunksize = b->size; if (chunksize > count) chunksize = count; DPRINTK("read %d from %d\n", chunksize, s->buf_idx); if (copy_to_user(buffer, b->start + s->fragsize - b->size, chunksize)) { up(&b->sem); return -EFAULT; } b->size -= chunksize; buffer += chunksize; count -= chunksize; if (b->size > 0) { up(&b->sem); break; } /* Make current buffer available for DMA again */ sa1100_dma_queue_buffer(s->dma_ch, (void *) b, b->dma_addr, s->fragsize); NEXT_BUF(s, buf); } if ((buffer - buffer0)) ret = buffer - buffer0; DPRINTK("audio_read: return=%d\n", ret); return ret; }
static int audio_write(struct file *file, const char *buffer, size_t count, loff_t * ppos) { const char *buffer0 = buffer; audio_state_t *state = (audio_state_t *)file->private_data; audio_stream_t *s = state->output_stream; int chunksize, ret = 0; DPRINTK("audio_write: count=%d\n", count); if (ppos != &file->f_pos) return -ESPIPE; if (s->mapped) return -ENXIO; if (!s->buffers && audio_setup_buf(s)) return -ENOMEM; while (count > 0) { audio_buf_t *b = s->buf; /* Wait for a buffer to become free */ if (file->f_flags & O_NONBLOCK) { ret = -EAGAIN; if (down_trylock(&b->sem)) break; } else { ret = -ERESTARTSYS; if (down_interruptible(&b->sem)) break; } /* Feed the current buffer */ chunksize = s->fragsize - b->size; if (chunksize > count) chunksize = count; DPRINTK("write %d to %d\n", chunksize, s->buf_idx); if (copy_from_user(b->start + b->size, buffer, chunksize)) { up(&b->sem); return -EFAULT; } b->size += chunksize; buffer += chunksize; count -= chunksize; if (b->size < s->fragsize) { up(&b->sem); break; } /* Send current buffer to dma */ s->active = 1; sa1100_dma_queue_buffer(s->dma_ch, (void *) b, b->dma_addr, b->size); b->size = 0; /* indicate that the buffer has been sent */ NEXT_BUF(s, buf); } if ((buffer - buffer0)) ret = buffer - buffer0; DPRINTK("audio_write: return=%d\n", ret); return ret; }