📄 omap-audio.c
字号:
if ((buffer - buffer0)) ret = buffer - buffer0; DPRINTK("audio_write: return=%d\n", ret); return ret;}/********************************************************************************* * * audio_read(): Exposed as read() function * *********************************************************************************/static intaudio_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;#ifdef OMAP_AUDIO_DMA_RETRY int ret1 = 0, try_count = 0;#endif unsigned long flags; DPRINTK("audio_read: count=%d\n", count); if (*ppos != file->f_pos) { printk("AudioRead - FPOS not ppos ppos=0x%x fpos =0x%x\n", (u32) * ppos, (u32) file->f_pos); return -ESPIPE; } if (s->mapped) { printk("AudioRead - s already mapped\n"); return -ENXIO; } if (!s->active) { if (!s->buffers && audio_setup_buf(s)) { printk("AudioRead - No Memory\n"); 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 */ local_irq_save(flags); b->offset = 0; if (++s->usr_head >= s->nbfrags) s->usr_head = 0; s->pending_frags++; local_irq_restore(flags);#ifdef OMAP_AUDIO_DMA_RETRY try_count = 0; ret1 = 0; do { ret1 = audio_process_dma(s); if (ret1 == -2) { /* Queue full? */ udelay(QUEUE_WAIT_TIME); /* give a chance to empty queue */ } try_count++; } while ((try_count < MAX_QUEUE_FULL_RETRIES) && (ret1 == -2));#else audio_process_dma(s);#endif#ifdef OMAP_AUDIO_DMA_RETRY /* recovery strategy */ if (ret1) { printk(KERN_ERR "audio_read: Queue is Full or dma processing error (%d)!\n", ret1); break; }#endif } if ((buffer - buffer0)) ret = buffer - buffer0; DPRINTK("audio_read: return=%d\n", ret); return ret;}/********************************************************************************* * * audio_mmap(): Exposed as mmap Function * *********************************************************************************/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; FN_IN; 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, vma_addr, buf->dma_addr, buf->master, vma->vm_page_prot); if (ret) return ret; vma_addr += buf->master; } s->mapped = 1; FN_OUT(0); return 0;}/********************************************************************************* * * audio_poll(): Exposed as poll function * *********************************************************************************/static unsigned intaudio_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" : ""); FN_OUT(mask); return mask;}/********************************************************************************* * * audio_llseek(): Exposed as lseek() function. * *********************************************************************************/static loff_t audio_llseek(struct file *file, loff_t offset, int origin){ FN_IN; FN_OUT(0); return -ESPIPE;}/********************************************************************************* * * audio_ioctl(): Handles generic ioctls. If there is a request for something this * fn cannot handle, its then given to client specific ioctl routine, that will take * up platform specific requests * *********************************************************************************/static intaudio_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; FN_OUT(1); return put_user(val, (int *)arg); case SNDCTL_DSP_SETFRAGMENT: if (get_user(val, (long *)arg)) { FN_OUT(2); return -EFAULT; } if (file->f_mode & FMODE_READ) { int ret = audio_set_fragments(is, val); if (ret < 0) { FN_OUT(3); return ret; } ret = put_user(ret, (int *)arg); if (ret) { FN_OUT(4); return ret; } } if (file->f_mode & FMODE_WRITE) { int ret = audio_set_fragments(os, val); if (ret < 0) { FN_OUT(5); return ret; } ret = put_user(ret, (int *)arg); if (ret) { FN_OUT(6); return ret; } } FN_OUT(7); return 0; case SNDCTL_DSP_SYNC: FN_OUT(8); return audio_sync(file); case SNDCTL_DSP_SETDUPLEX: FN_OUT(9); return 0; case SNDCTL_DSP_POST: FN_OUT(10); 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; FN_OUT(11); return put_user(val, (int *)arg); case SNDCTL_DSP_SETTRIGGER: if (get_user(val, (int *)arg)) { FN_OUT(12); 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)) { FN_OUT(13); return -ENOMEM; } audio_prime_rx(state); } local_irq_save(flags); is->stopped = 0; local_irq_restore(flags); audio_process_dma(is); } 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)) { FN_OUT(14); 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; local_irq_restore(flags); audio_process_dma(os); } else { audio_stop_dma(os); } } FN_OUT(15); 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))) { FN_OUT(16); 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; } FN_OUT(17); 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))) { FN_OUT(18); return -EINVAL; } if (!s->buffers && audio_setup_buf(s)) { FN_OUT(19); return -ENOMEM; } inf.bytes = atomic_read(&s->sem.count) * s->fragsize; inf.fragments = inf.bytes / s->fragsize; inf.fragsize = s->fragsize; inf.fragstotal = s->nbfrags; FN_OUT(20); return copy_to_user((void *)arg, &inf, sizeof(inf)); } case SNDCTL_DSP_NONBLOCK: file->f_flags |= O_NONBLOCK; FN_OUT(21); 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); } FN_OUT(22); return 0; default: /* * Let the client of this module handle the * non generic ioctls */ FN_OUT(23); return state->client_ioctl(inode, file, cmd, arg); } FN_OUT(0); return 0;}/********************************************************************************* * * audio_open(): Exposed as open() function * *********************************************************************************/static int audio_open(struct inode *inode, struct file *file){ audio_state_t *state = (&audio_state); audio_stream_t *os = state->output_stream; audio_stream_t *is = state->input_stream; int err, need_tx_dma; static unsigned char tsc2101_init_flag = 0; FN_IN; down(&state->sem); /* access control */ err = -ENODEV; if ((file->f_mode & FMODE_WRITE) && !os) goto out; if ((file->f_mode & FMODE_READ) && !is) goto out; err = -EBUSY; if ((file->f_mode & FMODE_WRITE) && state->wr_ref) goto out; if ((file->f_mode & FMODE_READ) && state->rd_ref) goto out; err = -EINVAL; if ((file->f_mode & FMODE_READ) && state->need_tx_for_rx && !os) goto out; /* request DMA channels */ need_tx_dma = ((file->f_mode & FMODE_WRITE) || ((file->f_mode & FMODE_READ) && state->need_tx_for_rx)); if (state->wr_ref || (state->rd_ref && state->need_tx_for_rx)) need_tx_dma = 0; if (need_tx_dma) { DMA_REQUEST(err, os, audio_dma_callback); DPRINTK(" err: %d\n", err); if (err < 0) goto out; } if (file->f_mode & FMODE_READ) { DMA_REQUEST(err, is, audio_dma_callback); if (err < 0) { if (need_tx_dma) DMA_FREE(os); goto out; } } /* now complete initialisation */ if (!AUDIO_ACTIVE(state)) { if (state->hw_init && !tsc2101_init_flag) { state->hw_init(state->data); tsc2101_init_flag = 0; } } if ((file->f_mode & FMODE_WRITE)) { state->wr_ref = 1; audio_reset(os); os->fragsize = AUDIO_FRAGSIZE_DEFAULT; os->nbfrags = AUDIO_NBFRAGS_DEFAULT; os->mapped = 0; init_waitqueue_head(&os->wq); } if (file->f_mode & FMODE_READ) { state->rd_ref = 1; audio_reset(is); is->fragsize = AUDIO_FRAGSIZE_DEFAULT; is->nbfrags = AUDIO_NBFRAGS_DEFAULT; is->mapped = 0; init_waitqueue_head(&is->wq); } file->private_data = state; err = 0; out: up(&state->sem); FN_OUT(err); return err;}/********************************************************************************* * * audio_release(): Exposed as release function() * *********************************************************************************/static int audio_release(struct inode *inode, struct file *file){ audio_state_t *state = file->private_data; audio_stream_t *os = state->output_stream; audio_stream_t *is = state->input_stream; FN_IN; down(&state->sem); if (file->f_mode & FMODE_READ) { audio_discard_buf(is); DMA_FREE(is); is->dma_spinref = 0; if (state->need_tx_for_rx) { os->spin_idle = 0; if (!state->wr_ref) { DMA_FREE(os); os->dma_spinref = 0; } } state->rd_ref = 0; } if (file->f_mode & FMODE_WRITE) { audio_sync(file); audio_discard_buf(os); if (!state->need_tx_for_rx || !state->rd_ref) { DMA_FREE(os); os->dma_spinref = 0; } state->wr_ref = 0; } if (!AUDIO_ACTIVE(state)) { if (state->hw_shutdown) state->hw_shutdown(state->data); } up(&state->sem); FN_OUT(0); return 0;}EXPORT_SYMBOL(audio_register_codec);EXPORT_SYMBOL(audio_unregister_codec);EXPORT_SYMBOL(audio_get_fops);MODULE_AUTHOR("Texas Instruments");MODULE_DESCRIPTION("Common audio handling for OMAP processors");MODULE_LICENSE("GPL");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -