📄 pcm_lib.c
字号:
return err; } else { char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, hwoff); snd_assert(runtime->dma_area, return -EFAULT); if (copy_from_user(hwbuf, buf, frames_to_bytes(runtime, frames))) return -EFAULT; } return 0;} typedef int (*transfer_f)(struct snd_pcm_substream *substream, unsigned int hwoff, unsigned long data, unsigned int off, snd_pcm_uframes_t size);static snd_pcm_sframes_t snd_pcm_lib_write1(struct snd_pcm_substream *substream, unsigned long data, snd_pcm_uframes_t size, int nonblock, transfer_f transfer){ struct snd_pcm_runtime *runtime = substream->runtime; snd_pcm_uframes_t xfer = 0; snd_pcm_uframes_t offset = 0; int err = 0; if (size == 0) return 0; if (size > runtime->xfer_align) size -= size % runtime->xfer_align; snd_pcm_stream_lock_irq(substream); switch (runtime->status->state) { case SNDRV_PCM_STATE_PREPARED: case SNDRV_PCM_STATE_RUNNING: case SNDRV_PCM_STATE_PAUSED: break; case SNDRV_PCM_STATE_XRUN: err = -EPIPE; goto _end_unlock; case SNDRV_PCM_STATE_SUSPENDED: err = -ESTRPIPE; goto _end_unlock; default: err = -EBADFD; goto _end_unlock; } while (size > 0) { snd_pcm_uframes_t frames, appl_ptr, appl_ofs; snd_pcm_uframes_t avail; snd_pcm_uframes_t cont; if (runtime->sleep_min == 0 && runtime->status->state == SNDRV_PCM_STATE_RUNNING) snd_pcm_update_hw_ptr(substream); avail = snd_pcm_playback_avail(runtime); if (((avail < runtime->control->avail_min && size > avail) || (size >= runtime->xfer_align && avail < runtime->xfer_align))) { wait_queue_t wait; enum { READY, SIGNALED, ERROR, SUSPENDED, EXPIRED, DROPPED } state; long tout; if (nonblock) { err = -EAGAIN; goto _end_unlock; } init_waitqueue_entry(&wait, current); add_wait_queue(&runtime->sleep, &wait); while (1) { if (signal_pending(current)) { state = SIGNALED; break; } set_current_state(TASK_INTERRUPTIBLE); snd_pcm_stream_unlock_irq(substream); tout = schedule_timeout(10 * HZ); snd_pcm_stream_lock_irq(substream); if (tout == 0) { if (runtime->status->state != SNDRV_PCM_STATE_PREPARED && runtime->status->state != SNDRV_PCM_STATE_PAUSED) { state = runtime->status->state == SNDRV_PCM_STATE_SUSPENDED ? SUSPENDED : EXPIRED; break; } } switch (runtime->status->state) { case SNDRV_PCM_STATE_XRUN: case SNDRV_PCM_STATE_DRAINING: state = ERROR; goto _end_loop; case SNDRV_PCM_STATE_SUSPENDED: state = SUSPENDED; goto _end_loop; case SNDRV_PCM_STATE_SETUP: state = DROPPED; goto _end_loop; default: break; } avail = snd_pcm_playback_avail(runtime); if (avail >= runtime->control->avail_min) { state = READY; break; } } _end_loop: remove_wait_queue(&runtime->sleep, &wait); switch (state) { case ERROR: err = -EPIPE; goto _end_unlock; case SUSPENDED: err = -ESTRPIPE; goto _end_unlock; case SIGNALED: err = -ERESTARTSYS; goto _end_unlock; case EXPIRED: snd_printd("playback write error (DMA or IRQ trouble?)\n"); err = -EIO; goto _end_unlock; case DROPPED: err = -EBADFD; goto _end_unlock; default: break; } } if (avail > runtime->xfer_align) avail -= avail % runtime->xfer_align; frames = size > avail ? avail : size; cont = runtime->buffer_size - runtime->control->appl_ptr % runtime->buffer_size; if (frames > cont) frames = cont; snd_assert(frames != 0, snd_pcm_stream_unlock_irq(substream); return -EINVAL); appl_ptr = runtime->control->appl_ptr; appl_ofs = appl_ptr % runtime->buffer_size; snd_pcm_stream_unlock_irq(substream); if ((err = transfer(substream, appl_ofs, data, offset, frames)) < 0) goto _end; snd_pcm_stream_lock_irq(substream); switch (runtime->status->state) { case SNDRV_PCM_STATE_XRUN: err = -EPIPE; goto _end_unlock; case SNDRV_PCM_STATE_SUSPENDED: err = -ESTRPIPE; goto _end_unlock; default: break; } appl_ptr += frames; if (appl_ptr >= runtime->boundary) appl_ptr -= runtime->boundary; runtime->control->appl_ptr = appl_ptr; if (substream->ops->ack) substream->ops->ack(substream); offset += frames; size -= frames; xfer += frames; if (runtime->status->state == SNDRV_PCM_STATE_PREPARED && snd_pcm_playback_hw_avail(runtime) >= (snd_pcm_sframes_t)runtime->start_threshold) { err = snd_pcm_start(substream); if (err < 0) goto _end_unlock; } if (runtime->sleep_min && runtime->status->state == SNDRV_PCM_STATE_RUNNING) snd_pcm_tick_prepare(substream); } _end_unlock: snd_pcm_stream_unlock_irq(substream); _end: return xfer > 0 ? (snd_pcm_sframes_t)xfer : err;}snd_pcm_sframes_t snd_pcm_lib_write(struct snd_pcm_substream *substream, const void __user *buf, snd_pcm_uframes_t size){ struct snd_pcm_runtime *runtime; int nonblock; snd_assert(substream != NULL, return -ENXIO); runtime = substream->runtime; snd_assert(runtime != NULL, return -ENXIO); snd_assert(substream->ops->copy != NULL || runtime->dma_area != NULL, return -EINVAL); if (runtime->status->state == SNDRV_PCM_STATE_OPEN) return -EBADFD; nonblock = !!(substream->f_flags & O_NONBLOCK); if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED && runtime->channels > 1) return -EINVAL; return snd_pcm_lib_write1(substream, (unsigned long)buf, size, nonblock, snd_pcm_lib_write_transfer);}EXPORT_SYMBOL(snd_pcm_lib_write);static int snd_pcm_lib_writev_transfer(struct snd_pcm_substream *substream, unsigned int hwoff, unsigned long data, unsigned int off, snd_pcm_uframes_t frames){ struct snd_pcm_runtime *runtime = substream->runtime; int err; void __user **bufs = (void __user **)data; int channels = runtime->channels; int c; if (substream->ops->copy) { snd_assert(substream->ops->silence != NULL, return -EINVAL); for (c = 0; c < channels; ++c, ++bufs) { if (*bufs == NULL) { if ((err = substream->ops->silence(substream, c, hwoff, frames)) < 0) return err; } else { char __user *buf = *bufs + samples_to_bytes(runtime, off); if ((err = substream->ops->copy(substream, c, hwoff, buf, frames)) < 0) return err; } } } else { /* default transfer behaviour */ size_t dma_csize = runtime->dma_bytes / channels; snd_assert(runtime->dma_area, return -EFAULT); for (c = 0; c < channels; ++c, ++bufs) { char *hwbuf = runtime->dma_area + (c * dma_csize) + samples_to_bytes(runtime, hwoff); if (*bufs == NULL) { snd_pcm_format_set_silence(runtime->format, hwbuf, frames); } else { char __user *buf = *bufs + samples_to_bytes(runtime, off); if (copy_from_user(hwbuf, buf, samples_to_bytes(runtime, frames))) return -EFAULT; } } } return 0;} snd_pcm_sframes_t snd_pcm_lib_writev(struct snd_pcm_substream *substream, void __user **bufs, snd_pcm_uframes_t frames){ struct snd_pcm_runtime *runtime; int nonblock; snd_assert(substream != NULL, return -ENXIO); runtime = substream->runtime; snd_assert(runtime != NULL, return -ENXIO); snd_assert(substream->ops->copy != NULL || runtime->dma_area != NULL, return -EINVAL); if (runtime->status->state == SNDRV_PCM_STATE_OPEN) return -EBADFD; nonblock = !!(substream->f_flags & O_NONBLOCK); if (runtime->access != SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) return -EINVAL; return snd_pcm_lib_write1(substream, (unsigned long)bufs, frames, nonblock, snd_pcm_lib_writev_transfer);}EXPORT_SYMBOL(snd_pcm_lib_writev);static int snd_pcm_lib_read_transfer(struct snd_pcm_substream *substream, unsigned int hwoff, unsigned long data, unsigned int off, snd_pcm_uframes_t frames){ struct snd_pcm_runtime *runtime = substream->runtime; int err; char __user *buf = (char __user *) data + frames_to_bytes(runtime, off); if (substream->ops->copy) { if ((err = substream->ops->copy(substream, -1, hwoff, buf, frames)) < 0) return err; } else { char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, hwoff); snd_assert(runtime->dma_area, return -EFAULT); if (copy_to_user(buf, hwbuf, frames_to_bytes(runtime, frames))) return -EFAULT; } return 0;}static snd_pcm_sframes_t snd_pcm_lib_read1(struct snd_pcm_substream *substream, unsigned long data, snd_pcm_uframes_t size, int nonblock, transfer_f transfer){ struct snd_pcm_runtime *runtime = substream->runtime; snd_pcm_uframes_t xfer = 0; snd_pcm_uframes_t offset = 0; int err = 0; if (size == 0) return 0; if (size > runtime->xfer_align) size -= size % runtime->xfer_align; snd_pcm_stream_lock_irq(substream); switch (runtime->status->state) { case SNDRV_PCM_STATE_PREPARED: if (size >= runtime->start_threshold) { err = snd_pcm_start(substream); if (err < 0) goto _end_unlock; } break; case SNDRV_PCM_STATE_DRAINING: case SNDRV_PCM_STATE_RUNNING: case SNDRV_PCM_STATE_PAUSED: break; case SNDRV_PCM_STATE_XRUN: err = -EPIPE; goto _end_unlock; case SNDRV_PCM_STATE_SUSPENDED: err = -ESTRPIPE; goto _end_unlock; default: err = -EBADFD; goto _end_unlock; } while (size > 0) { snd_pcm_uframes_t frames, appl_ptr, appl_ofs; snd_pcm_uframes_t avail; snd_pcm_uframes_t cont; if (runtime->sleep_min == 0 && runtime->status->state == SNDRV_PCM_STATE_RUNNING) snd_pcm_update_hw_ptr(substream); __draining: avail = snd_pcm_capture_avail(runtime); if (runtime->status->state == SNDRV_PCM_STATE_DRAINING) { if (avail < runtime->xfer_align) { err = -EPIPE; goto _end_unlock; } } else if ((avail < runtime->control->avail_min && size > avail) || (size >= runtime->xfer_align && avail < runtime->xfer_align)) { wait_queue_t wait; enum { READY, SIGNALED, ERROR, SUSPENDED, EXPIRED, DROPPED } state; long tout; if (nonblock) { err = -EAGAIN; goto _end_unlock; } init_waitqueue_entry(&wait, current); add_wait_queue(&runtime->sleep, &wait); while (1) { if (signal_pending(current)) { state = SIGNALED; break; } set_current_state(TASK_INTERRUPTIBLE); snd_pcm_stream_unlock_irq(substream); tout = schedule_timeout(10 * HZ); snd_pcm_stream_lock_irq(substream); if (tout == 0) { if (runtime->status->state != SNDRV_PCM_STATE_PREPARED && runtime->status->state != SNDRV_PCM_STATE_PAUSED) { state = runtime->status->state == SNDRV_PCM_STATE_SUSPENDED ? SUSPENDED : EXPIRED; break; } } switch (runtime->status->state) { case SNDRV_PCM_STATE_XRUN: state = ERROR; goto _end_loop; case SNDRV_PCM_STATE_SUSPENDED: state = SUSPENDED; goto _end_loop; case SNDRV_PCM_STATE_DRAINING: goto __draining; case SNDRV_PCM_STATE_SETUP: state = DROPPED; goto _end_loop; default: break; } avail = snd_pcm_capture_avail(runtime); if (avail >= runtime->control->avail_min) { state = READY; break; } } _end_loop: remove_wait_queue(&runtime->sleep, &wait); switch (state) { case ERROR: err = -EPIPE; goto _end_unlock; case SUSPENDED: err = -ESTRPIPE; goto _end_unlock; case SIGNALED: err = -ERESTARTSYS; goto _end_unlock; case EXPIRED: snd_printd("capture read error (DMA or IRQ trouble?)\n"); err = -EIO; goto _end_unlock; case DROPPED: err = -EBADFD; goto _end_unlock; default: break; } } if (avail > runtime->xfer_align) avail -= avail % runtime->xfer_align; frames = size > avail ? avail : size; cont = runtime->buffer_size - runtime->control->appl_ptr % runtime->buffer_size; if (frames > cont) frames = cont; snd_assert(frames != 0, snd_pcm_stream_unlock_irq(substream); return -EINVAL); appl_ptr = runtime->control->appl_ptr; appl_ofs = appl_ptr % runtime->buffer_size; snd_pcm_stream_unlock_irq(substream); if ((err = transfer(substream, appl_ofs, data, offset, frames)) < 0) goto _end; snd_pcm_stream_lock_irq(substream); switch (runtime->status->state) { case SNDRV_PCM_STATE_XRUN: err = -EPIPE; goto _end_unlock; case SNDRV_PCM_STATE_SUSPENDED: err = -ESTRPIPE; goto _end_unlock; default: break; } appl_ptr += frames; if (appl_ptr >= runtime->boundary) appl_ptr -= runtime->boundary; runtime->control->appl_ptr = appl_ptr; if (substream->ops->ack) substream->ops->ack(substream); offset += frames; size -= frames; xfer += frames; if (runtime->sleep_min && runtime->status->state == SNDRV_PCM_STATE_RUNNING) snd_pcm_tick_prepare(substream); } _end_unlock: snd_pcm_stream_unlock_irq(substream); _end: return xfer > 0 ? (snd_pcm_sframes_t)xfer : err;}snd_pcm_sframes_t snd_pcm_lib_read(struct snd_pcm_substream *substream, void __user *buf, snd_pcm_uframes_t size){ struct snd_pcm_runtime *runtime; int nonblock; snd_assert(substream != NULL, return -ENXIO); runtime = substream->runtime; snd_assert(runtime != NULL, return -ENXIO); snd_assert(substream->ops->copy != NULL || runtime->dma_area != NULL, return -EINVAL); if (runtime->status->state == SNDRV_PCM_STATE_OPEN) return -EBADFD; nonblock = !!(substream->f_flags & O_NONBLOCK); if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED) return -EINVAL; return snd_pcm_lib_read1(substream, (unsigned long)buf, size, nonblock, snd_pcm_lib_read_transfer);}EXPORT_SYMBOL(snd_pcm_lib_read);static int snd_pcm_lib_readv_transfer(struct snd_pcm_substream *substream, unsigned int hwoff, unsigned long data, unsigned int off, snd_pcm_uframes_t frames){ struct snd_pcm_runtime *runtime = substream->runtime; int err; void __user **bufs = (void __user **)data; int channels = runtime->channels; int c; if (substream->ops->copy) { for (c = 0; c < channels; ++c, ++bufs) { char __user *buf; if (*bufs == NULL) continue; buf = *bufs + samples_to_bytes(runtime, off); if ((err = substream->ops->copy(substream, c, hwoff, buf, frames)) < 0) return err; } } else { snd_pcm_uframes_t dma_csize = runtime->dma_bytes / channels; snd_assert(runtime->dma_area, return -EFAULT); for (c = 0; c < channels; ++c, ++bufs) { char *hwbuf; char __user *buf; if (*bufs == NULL) continue; hwbuf = runtime->dma_area + (c * dma_csize) + samples_to_bytes(runtime, hwoff); buf = *bufs + samples_to_bytes(runtime, off); if (copy_to_user(buf, hwbuf, samples_to_bytes(runtime, frames))) return -EFAULT; } } return 0;} snd_pcm_sframes_t snd_pcm_lib_readv(struct snd_pcm_substream *substream, void __user **bufs, snd_pcm_uframes_t frames){ struct snd_pcm_runtime *runtime; int nonblock; snd_assert(substream != NULL, return -ENXIO); runtime = substream->runtime; snd_assert(runtime != NULL, return -ENXIO); snd_assert(substream->ops->copy != NULL || runtime->dma_area != NULL, return -EINVAL); if (runtime->status->state == SNDRV_PCM_STATE_OPEN) return -EBADFD; nonblock = !!(substream->f_flags & O_NONBLOCK); if (runtime->access != SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) return -EINVAL; return snd_pcm_lib_read1(substream, (unsigned long)bufs, frames, nonblock, snd_pcm_lib_readv_transfer);}EXPORT_SYMBOL(snd_pcm_lib_readv);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -