首页 > 其他分享 >DMA_CYCLIC使用

DMA_CYCLIC使用

时间:2024-04-01 18:57:06浏览次数:16  
标签:DMA vchan struct dma CYCLIC callback substream 使用 desc

DMA传输类型:

 enum dma_transaction_type {
         DMA_MEMCPY,                // 内存拷贝
         DMA_XOR,                   // 异或操作
         DMA_PQ,                    // 乘方操作
         DMA_XOR_VAL,               // 异或值操作
         DMA_PQ_VAL,                // 乘方值操作
         DMA_INTERRUPT,             // DMA中断?
         DMA_SG,                    // 散射-聚集(Scatter-Gather)传输
         DMA_PRIVATE,               // 私有的DMA传输
         DMA_ASYNC_TX,              // 异步传输
         DMA_SLAVE,                 // DMA控制器设备功能,必有
         DMA_CYCLIC,                // 循环DMA传输
         DMA_INTERLEAVE,            // 交错DMA传输
 /* last transaction type for creation of the capabilities mask */
         DMA_TX_TYPE_END,
 };

 

此节以audio使用DMA_CYCLIC举例,说明下如何使用:
 static int dmaengine_pcm_prepare_and_submit(struct snd_pcm_substream *substream)
 {
         struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);
         struct dma_chan *chan = prtd->dma_chan;
         struct dma_async_tx_descriptor *desc;
         enum dma_transfer_direction direction;
         unsigned long flags = DMA_CTRL_ACK;

         direction = snd_pcm_substream_to_dma_direction(substream);

         if (!substream->runtime->no_period_wakeup)
                 flags |= DMA_PREP_INTERRUPT;

         prtd->pos = 0;
         desc = dmaengine_prep_dma_cyclic(chan,
                 substream->runtime->dma_addr,
                 snd_pcm_lib_buffer_bytes(substream),
                 snd_pcm_lib_period_bytes(substream), direction, flags);    // 进行dmac cyclic传输

         if (!desc)
                 return -ENOMEM;

         desc->callback = dmaengine_pcm_dma_complete;    // 传输完成时的回调函数
         desc->callback_param = substream;                // 传输完成时回调函数的参数
         prtd->cookie = dmaengine_submit(desc);            // 提交一次传输

         return 0;
 }

 

调用到dmaengine的核心层:
 static inline struct dma_async_tx_descriptor *dmaengine_prep_dma_cyclic(
                 struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len,
                 size_t period_len, enum dma_transfer_direction dir,
                 unsigned long flags)
 {
         if (!chan || !chan->device || !chan->device->device_prep_dma_cyclic)
                 return NULL;

         return chan->device->device_prep_dma_cyclic(chan, buf_addr, buf_len,
                                                 period_len, dir, flags);        // dmac具体实现
 }
 
 struct dma_async_tx_descriptor {
         dma_cookie_t cookie;                // 一个整型数,用于追踪本次传输
         enum dma_ctrl_flags flags; /* not a 'long' to pack with cookie */
         dma_addr_t phys;
         struct dma_chan *chan;
         dma_cookie_t (*tx_submit)(struct dma_async_tx_descriptor *tx);
         int (*desc_free)(struct dma_async_tx_descriptor *tx);
         dma_async_tx_callback callback;                // 传输完成时的回调函数
         dma_async_tx_callback_result callback_result;
         void *callback_param;                            // 传输完成时回调函数的参数
         struct dmaengine_unmap_data *unmap;
         enum dma_desc_metadata_mode desc_metadata_mode;
         struct dma_descriptor_metadata_ops *metadata_ops;
 #ifdef CONFIG_ASYNC_TX_ENABLE_CHANNEL_SWITCH
         struct dma_async_tx_descriptor *next;
         struct dma_async_tx_descriptor *parent;
         spinlock_t lock;
 #endif
 };   

 

调用到dma控制器的dma_device.device_prep_dma_cyclic具体实现,device_prep_dma_cyclic会对dmac进行配置,返回dma_async_tx_descriptor描述符 在dmac实现中,通常会对每一个vchan进行vchan_init操作,例如:
... ...
         for (i = 0; i < sdc->cfg->nr_max_vchans; i++) {
                 struct sun6i_vchan *vchan = &sdc->vchans[i];

                 INIT_LIST_HEAD(&vchan->node);
                 vchan->vc.desc_free = sun6i_dma_free_desc;
                 vchan_init(&vchan->vc, &sdc->slave);
         }
... ...

 

而在vchan_init函数中,会定义一个tasklet任务,如下:
 void vchan_init(struct virt_dma_chan *vc, struct dma_device *dmadev)
 {
         dma_cookie_init(&vc->chan);

         spin_lock_init(&vc->lock);
         INIT_LIST_HEAD(&vc->desc_submitted);
         INIT_LIST_HEAD(&vc->desc_issued);
         INIT_LIST_HEAD(&vc->desc_completed);

         tasklet_init(&vc->task, vchan_complete, (unsigned long)vc);        // 初始化一个tasklet任务vchan_complete

         vc->chan.device = dmadev;
         list_add_tail(&vc->chan.device_node, &dmadev->channels);
 }

 

这个tasklet在一次描述符传输完成时被调度,例如:
 static void sun6i_dma_issue_pending(struct dma_chan *chan)
 {
         struct sun6i_dma_dev *sdev = to_sun6i_dma_dev(chan->device);
         struct sun6i_vchan *vchan = to_sun6i_vchan(chan);
         unsigned long flags;

         spin_lock_irqsave(&vchan->vc.lock, flags);

         if (vchan_issue_pending(&vchan->vc)) {
                 spin_lock(&sdev->lock);

                 if (!vchan->phy && list_empty(&vchan->node)) {
                         list_add_tail(&vchan->node, &sdev->pending);
                         tasklet_schedule(&sdev->task);                    // 调度vchan_init初始化的tasklet,也就是调度vchan_complete
                         dev_dbg(chan2dev(chan), "vchan %p: issued\n",
                                 &vchan->vc);
                 }
... ...
 }
 
 或调用vchan_cyclic_callback函数调度

   static inline void vchan_cyclic_callback(struct virt_dma_desc *vd)
 {
         struct virt_dma_chan *vc = to_virt_chan(vd->tx.chan);

         vc->cyclic = vd;
         tasklet_schedule(&vc->task);
 }

 

在vchan_complete函数中,
 static void vchan_complete(struct tasklet_struct *t)
 {
         struct virt_dma_chan *vc = from_tasklet(vc, t, task);
         struct virt_dma_desc *vd, *_vd;
         struct dmaengine_desc_callback cb;
         LIST_HEAD(head);

         spin_lock_irq(&vc->lock);
         list_splice_tail_init(&vc->desc_completed, &head);
         vd = vc->cyclic;
         if (vd) {
                 vc->cyclic = NULL;
                 dmaengine_desc_get_callback(&vd->tx, &cb);    // 获取pcm设置的函数
         } else {
                 memset(&cb, 0, sizeof(cb));
         }
         spin_unlock_irq(&vc->lock);

         dmaengine_desc_callback_invoke(&cb, &vd->tx_result);        // 进行回调

         list_for_each_entry_safe(vd, _vd, &head, node) {
                 dmaengine_desc_get_callback(&vd->tx, &cb);

                 list_del(&vd->node);
                 dmaengine_desc_callback_invoke(&cb, &vd->tx_result);
                 vchan_vdesc_fini(vd);
         }
 }

 

dmaengine_desc_get_callback和dmaengine_desc_callback_invoke定义如下:
 static inline void
 dmaengine_desc_get_callback(struct dma_async_tx_descriptor *tx,
                             struct dmaengine_desc_callback *cb)
 {
         cb->callback = tx->callback;
         cb->callback_result = tx->callback_result;
         cb->callback_param = tx->callback_param;
 }
... ...
 static inline void
 dmaengine_desc_callback_invoke(struct dmaengine_desc_callback *cb,
                                const struct dmaengine_result *result)
 {
         struct dmaengine_result dummy_result = {
                 .result = DMA_TRANS_NOERROR,
                 .residue = 0
         };

         if (cb->callback_result) {    // 没设置callback_result就调用callback
                 if (!result)
                         result = &dummy_result;
                 cb->callback_result(cb->callback_param, result);
         } else if (cb->callback) {
                 cb->callback(cb->callback_param);
         }
 }

 

自此回来分析的起点dmaengine_pcm_prepare_and_submit,在这个函数中我们定义了callback函数,该callback函数定义如下:
 static void dmaengine_pcm_dma_complete(void *arg)
 {
         struct snd_pcm_substream *substream = arg;
         struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);

         prtd->pos += snd_pcm_lib_period_bytes(substream);
         if (prtd->pos >= snd_pcm_lib_buffer_bytes(substream))
                 prtd->pos = 0;
         // 通知 ALSA 子系统一个音频周期已经结束,并且 ALSA 子系统会自动进行后续处理,包括更新 PCM 子流的状态以及准备处理下一个周期的音频数据。
         snd_pcm_period_elapsed(substream);    
 }
 ... ...
  void snd_pcm_period_elapsed(struct snd_pcm_substream *substream)
 {
         struct snd_pcm_runtime *runtime;
         unsigned long flags;

         if (snd_BUG_ON(!substream))
                 return;

         snd_pcm_stream_lock_irqsave(substream, flags);
         if (PCM_RUNTIME_CHECK(substream))
                 goto _unlock;
         runtime = substream->runtime;

         if (!snd_pcm_running(substream) ||
             snd_pcm_update_hw_ptr0(substream, 1) < 0)        //  ALSA 中用于更新 PCM(Pulse Code Modulation)音频子流的硬件指针(hardware pointer)的函数
                 goto _end;

 #ifdef CONFIG_SND_PCM_TIMER
         if (substream->timer_running)
                 snd_timer_interrupt(substream->timer, 1);
 #endif
  _end:
         kill_fasync(&runtime->fasync, SIGIO, POLL_IN);
  _unlock:
         snd_pcm_stream_unlock_irqrestore(substream, flags);
 }
 EXPORT_SYMBOL(snd_pcm_period_elapsed);
分析完毕              

标签:DMA,vchan,struct,dma,CYCLIC,callback,substream,使用,desc
From: https://www.cnblogs.com/lethe1203/p/18109160

相关文章

  • 这篇教你如何使用python自动化图形界面任务
    这篇教你如何使用python自动化图形界面任务PyAutoGUI是什么?PyAutoGUI是一个用于自动化任务和图形用户界面操作的Python库。它可以模拟鼠标移动、点击、键盘输入等操作,帮助用户实现自动化任务。优点:跨平台性:PyAutoGUI可以在Windows、macOS和Linux等多个平台......
  • 什么库是检测未使用和简化代码在python中?
    什么库是检测未使用和简化代码在python?什么是python的Vulture呢?功能:Vulture是一个用于静态分析Python代码的库,专门用于检测未使用的代码。它可以帮助你识别项目中未被引用的函数、类、变量或导入模块,并帮助简化代码结构.使用方法:首先,安装Vulture库:pip install......
  • SQL SERVER 从入门到精通 第5版 第二篇 第6章 SQL函数的使用 读书笔记
     第六章SQL函数的使用按函数种类可以分为聚合函数,数学函数,字符串函数,日期和时间函数,转换函数和元数据函数6种.>.聚合函数. 聚合函数对一组值执行计算,并返回单个值.除count外,聚合函数都会忽略空值.通常与SELECT语句的GROUPBY子句一起......
  • Pip源设置(使用清华源)
     1、临时使用1pipinstall-ihttps://pypi.tuna.tsinghua.edu.cn/simplesome-package2、永久更改pip源升级pip到最新的版本(>=10.0.0)后进行配置:1pipinstallpip-U2pipconfigsetglobal.index-urlhttps://pypi.tuna.tsinghua.edu.cn/simple如果......
  • 使用Element-UI的form表单验证文件是否上传
    项目中有个需求,表单中的文件为必传项。 其中使用了element-ui的form表单验证,话不多说,上代码。<template><div><el-formlabel-position="top":model="reportForm"ref="checkerForm":rules="rules"label-......
  • cleanmymac有必要买吗?cleanmymac免费使用
    在使用mac时,小编遇到了运行内存不足、硬盘空间不足的情况。遇到这种情况,我们可以借助经典的电脑深度清理软件——CleanMyMacX,清理不常用的软件和系统垃圾,非常好用!不过,有许多网友发现CleanMyMacX有免费和收费两个版本,那cleanmymac有必要买吗?小编今天就带大家了解下这款软件,......
  • grafana使用变量过滤时间序列
    这里我们为Dashboard创建了一个名为node的变量,并且指定其类型为Query。Query类型的变量,允许用户指定数据源以及查询表达式,并通过正则匹配(Regex)的方式对查询结果进行处理,从而动态生成变量的可选值函数作用label_values(label)返回Promthues所有监......
  • SqlServer事务语法及使用方法
    原文链接:https://blog.csdn.net/xiaouncle/article/details/52891563事务是关于原子性的。原子性的概念是指可以把一些事情当做一个不可分割的单元来看待。从数据库的角度看,它是指应全部执行或全部不执行的一条或多条语句的最小组合。可以使用一些T-SQL语句在事务中“标记”这些......
  • 【NoSQL】SpringBoot+Redis简单使用
    【NoSQL】SpringBoot+Redis简单使用Redis是一款key-value存储结构的内存级NoSQL数据库;支持多种数据存储格式、支持持久化、支持集群windows下载:https://github.com/tporadowski/redis/releases<dependency><groupId>org.springframework.boot</groupId><artifactId......
  • 云原生最佳实践系列 6:MSE 云原生网关使用 JWT 进行认证鉴权
    方案概述MSE网关可以为后端服务提供转发路由能力,在此基础上,一些敏感的后端服务需要特定认证授权的用户才能够访问。MSE云原生网关致力于提供给云上用户体系化的安全解决方案,其中JWT认证能力是在JsonWebToken这种结构化令牌的基础上实现了一套基于用户体系对用户的API(服......