[RK3288][Android6.0] ALSA的DMA buffer读写位置更新

来源:互联网 发布:如何自学软件编程 编辑:程序博客网 时间:2024/06/05 08:58

Platform: Rockchip
OS: Android 6.0
Kernel: 3.10.92


刚开始我以为走的是下面的通路一,后来调试发现根本没更新,而是在后面提到的通路二中更新了,不过通路一也提一下吧!

通路一:

以录音为例:

snd_pcm_lib_read1 -> pcm_lib.c
    snd_pcm_update_hw_ptr ->
        snd_pcm_update_hw_ptr0 -> pcm_lib.c
            substream->ops->pointer ->    //这个函数指针很关键! 初始化时在soc_new_pcm()中注册
            soc_pcm_pointer -> soc-pcm.c
                    platform->driver->ops->pointer ->    //snd_dmaengine_pcm_register()中注册dmaengine_no_residue_pcm_platform
                    snd_dmaengine_pcm_pointer_no_residue
              
static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,  unsigned int in_interrupt){......pos = substream->ops->pointer(substream);......}
 snd_dmaengine_pcm_pointer_no_residue(): 
snd_pcm_uframes_t snd_dmaengine_pcm_pointer_no_residue(struct snd_pcm_substream *substream){    //pl330.c里注册    struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);    //可是rk在开头做了#undef CONFIG_ARCH_ROCKCHIP,所以这里不会跑到了   //也就是说通路一是不会更新pos了#ifdef CONFIG_ARCH_ROCKCHIP    dma_addr_t src, dst;    //pl330_probe()中注册,对应pl330_dma_getposition    prtd->dma_chan->device->dma_getposition(prtd->dma_chan, &src, &dst);    //dma_addr为申请dma buffer物理地址,相减之后得到当前相对    //物理地址的偏移,也就是当前播放和录音的位置了。    if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)        prtd->pos = dst - substream->runtime->dma_addr;    else        prtd->pos = src - substream->runtime->dma_addr;#endif    //转成frame返回    return bytes_to_frames(substream->runtime, prtd->pos);}


通路二

那么pos,是在哪里更新的呢?其实它是在回调函数中dmaengine_pcm_dma_complete()中完成,dma中断函数最终会调用更新它。

pl330_irq_handler -> pl330.c
    pl330_update ->
        _callback ->
            r->xfer_cb ->
                dma_pl330_rqcb ->
                    tasklet_schedule ->
                        pl330_tasklet ->
                            handle_cyclic_desc_list ->
                                callback ->
                                    dmaengine_pcm_dma_complete ->
                                        snd_pcm_period_elapsed ->
                                            snd_pcm_update_hw_ptr0

static void dmaengine_pcm_dma_complete(void *arg){    struct snd_pcm_substream *substream = arg;    struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);......    //以peroid_size为单位增加偏移    prtd->pos += snd_pcm_lib_period_bytes(substream);    //当到达buffer_size后,会被重置    if (prtd->pos >= snd_pcm_lib_buffer_bytes(substream))        prtd->pos = 0;    snd_pcm_period_elapsed(substream);}

snd_pcm_lib_period_bytes():

static inline size_t snd_pcm_lib_period_bytes(struct snd_pcm_substream *substream){    struct snd_pcm_runtime *runtime = substream->runtime;    //每次其实就是加上一个peroid_size    return frames_to_bytes(runtime, runtime->period_size);}
snd_pcm_lib_buffer_bytes():
static inline size_t snd_pcm_lib_buffer_bytes(struct snd_pcm_substream *substream){    struct snd_pcm_runtime *runtime = substream->runtime;    //获取buffer_size大小的frame    return frames_to_bytes(runtime, runtime->buffer_size);}

阅读全文
1 0
原创粉丝点击