DMA

谨以此文纪念过往的岁月。

DMA传输支持4种格式,内存到内存,设备到内存,内存到设备,设备到设备。
对于内存到内存比较好理解,就是不通过CPU的复制,直接使用进行数据传输。

1.dma的初始化
在cpu.c文件中会对CPU的一些最最基本的资源初始化,如时钟,中断等等,在该文件中会注册一个s3c6410_sysclass类,
struct sysdev_class s3c6410_sysclass = {
 .name = "s3c6410-core",
};
对于搞驱动的人来说这个很不陌生,在创建一个设备前一般会去注册一个类的。
static int __init s3c6410_core_init(void)
{
 return sysdev_class_register(&s3c6410_sysclass);
}
这个会在内核启动时初始化的,因为他被这样声明了core_initcall(s3c6410_core_init);在内核中像arch_initcall和core_initcall以及module_init,这些都会在
内核启动的时候初始化。会有先后顺序的,core_initcall在内核启动后就会初始化,之后是arch_initcall再之后才是module_init。
在注册该类后,dma的驱动以及dma的设备都可以挂在该类下了。
在arch/arm/mach-s3c6410/dma.c中会将dma_driver挂在s3c6410_sysclass下
static int __init s3c6410_dma_init(void)
{
 return sysdev_driver_register(&s3c6410_sysclass, &s3c6410_dma_driver);
}
arch_initcall(s3c6410_dma_init);
对于s3c6410_dma_driver中,其实最主要的是其中两个函数s3c_dma_init,和s3c_dma_init_map。
static struct sysdev_driver s3c6410_dma_driver = {
 .add = s3c6410_dma_add,
};
static int __init s3c6410_dma_add(struct sys_device *sysdev)
{
 s3c_dma_init(S3C_DMA_CHANNELS, IRQ_DMA0, 0x20); --S3C_DMA_CHANNELS = 4*8 应为pl080有四个dma控制器,每一个有8个通道。
 return s3c_dma_init_map(&s3c6410_dma_sel);
}

s3c_dma_init顾名思义是DMA的初始化,s3c_dma_init_map则是将dma通道与硬件匹配。
s3c_dma_init的源码在dma-pl080.c中,其具体的可以去参考源码,在其中主要是对dma的控制器以及每个通道的寄存器等等进行初始化。
其中有一个需要注意的是,为DMA开辟一个cache。dma_kmem是一个全局变量。
dma_kmem = kmem_cache_create("dma_desc", sizeof(struct s3c_dma_buf), 0,SLAB_HWCACHE_ALIGN, (void *)s3c_dma_cache_ctor);
s3c_dma_init_map的源码也在dma-pl080.c中,其具体的可以去参考源码.

2.就从内存到内存开始,以从内存开辟开始。
在linux的内存开辟中有kmalloc,vmalloc,dma_alloc_coherent等办法。kmalloc开辟连续的逻辑地址,vmalloc不连续,dma_alloc_coherent则开辟内存连续并且
一致的内存。
dma_alloc_coherent 原型:
void *dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp)
dma_alloc_coherent 应用如下:
dmabuf = (unsigned char *)dma_alloc_coherent(NULL,BUFF_SIZE,(dma_addr_t *)&dmaphys,GFP_KERNEL);
dmabuf返回的是内核虚拟地址,BUFF_SIZE为分配的地址空间大小,dmaphys为总线地址,在ARM框架下可以理解为物理地址。
在分配内存空间后,即是申请DMA通道。s3c2410_dma_request的源码在dma-pl080.c中,在此我们不去详细的分析每一个代码,主要是分析代码的架构。以下有很多函数都是
在dma-pl080.c中,以后不会再说明函数的出处,除非特殊情况。
2.1 s3c2410_dma_request
int s3c2410_dma_request(unsigned int channel,struct s3c2410_dma_client *client,void *dev) 第二个参数是dma的名称。区别于其他的。
在s3c2410_dma_request中有两个比较重要的函数s3c_dma_map_channel 和s3c_enable_dmac。
s3c_dma_map_channel将虚拟的通道与真正的并且没有使用的通道相匹配。
原型为struct s3c2410_dma_chan *s3c_dma_map_channel(int channel),该函数会返回真正dma通道的参数。
在上面初始化的时候,没有将s3c_dma_init_map这个函数描述,在这儿描述一下。
s3c_dma_init_map在查看源码的时候会发现,他仅仅是一个赋值函数,就是将s3c_dma_init_map传下来的参数赋值给该文件中的全局变量dma_sel。其实这个函数作用就是
一个传话筒的功能。只是将s3c6410_dma_sel这个参数的指针赋值给dma_sel.其实真正的信息仍然存储在s3c6410_dma_sel中。
简要的将下面的函数讲述一下,下面的源码做个删减,一部分的检测代码删去。
struct s3c2410_dma_chan *s3c_dma_map_channel(int channel)
{
 struct s3c_dma_map *ch_map;
 struct s3c2410_dma_chan *dmach;
 int ch;
 
 ch_map = dma_sel.map + channel;    dma_sel.map就是s3c6410_dma_sel.map,也就是s3c6410_dma_mappings[]数组的首指针。在这里是为了查找虚拟通道所对应的
                                    真正的通道。其中主要的是寻找一个没有使用的通道。
 for (ch = 0; ch < dma_channels; ch++) {
  if (!is_channel_valid(ch_map->channels[ch]))   在这个是根据s3c6410_dma_sel中的ch_map的中channel是否可用来判定的。因为有些是被固定设定在某一个控制器的,
                                                 甚至某些功能被设定在固定的通道上。这个需要去仔细阅读源码中s3c6410_dma_sel这个结构体数组。
   continue;
  if (s3c_dma_chans[ch].in_use == 0) {           不仅是该dma的通道可以支持该功能,还要该通道未被使用
   pr_debug("mapped channel %d to %d\n", channel, ch);
   break;
  }
 }
 found:
 dmach = &s3c_dma_chans[ch];       这个是取出真正对用的dma通道的信息的首指针返回。s3c_dma_chans是个全局变量,在s3c_dma_init中被初始化了。
 dma_chan_map[channel] = dmach;    同时在匹配数组中记录。
 (dma_sel.select)(dmach, ch_map);  这个函数比较简单直接就是dmach->map = ch_map.同样是记录信息的。

return dmach;
}
在linux中,往往真正的信息只会被一个数据结构所记录。而其他需要使用该信息的均会使用指针的办法来实现该数据结构的信息存储和调用。这也是linux内核比较难理解的地方。
在上述函数中真正存储信息的有s3c_dma_chans和s3c6410_dma_sel,而其他结构均使用指针来指向这两个结构体。这样的方法很常见。
OK,在上述函数中查询到真正的dma通道。进行了虚拟通道与真实通道的匹配。
在 s3c2410_dma_request中还有一个比较重要的函数,s3c_enable_dmac,这个函数会在该控制器的中断没有被声明使用的情况下用的,因为8个通道公用一个中断
如果该控制器第一次被使用的话,需要申请中断和打开控制器。

2.2 int s3c2410_dma_set_buffdone_fn(dmach_t channel, s3c2410_dma_cbfn_t rtn)
设置中断完成后的回调函数。这个很简单。不叙述,会在s3c_dma_irq中讲述。该回调函数是如何被调用的。

2.3 int s3c2410_dma_devconfig(int channel,enum s3c2410_dmasrc source,int hwcfg,unsigned long devaddr)
该函数配置DMA的源/目的硬件类型和地址。
参数 devaddr为源地址。source为dma传输的类型。在该函数的实现中根据不同的传输类型配置不同的寄存器。

2.4 int s3c2410_dma_config(dmach_t channel,int xferunit,int dcon)
xferunit 传输之间的字节大小 dcon DCONx 寄存器的基值

2.5 int s3c2410_dma_setflags(dmach_t channel, unsigned int flags)

2.6 int s3c2410_dma_enqueue(unsigned int channel, void *id,dma_addr_t data, int size)
这个函数非常重要,dma的操作几乎都在这里面完成。
在DMA中dma运行状态有三态
enum s3c_dma_state {
 S3C_DMA_IDLE,       --DMA空闲
 S3C_DMA_RUNNING,    --DMA运行
 S3C_DMA_PAUSED      --DMA暂停
};
而buffer的加载则有五态
enum s3c_dma_loadst {
 S3C_DMALOAD_NONE,                 --没有buffer加载,此时通道应无效
 S3C_DMALOAD_1LOADED,              --有一个buffer被加载,但是没有被DMA确认加载,一是有可能通道没有运行,二可能是dma认为此时加载太浪费,等一会儿
 S3C_DMALOAD_1RUNNING,             --buffer被确认运行了,但是还没有结束。
 S3C_DMALOAD_1LOADED_1RUNNING,     --当前有一个缓冲区等待被dma加载,一个在运行
};
在dma运行过程中,理解这几个状态很重要。
struct s3c2410_dma_chan
{   ...
 /* buffer list and information */
 struct s3c_dma_buf *curr;  /* current dma buffer */   
 struct s3c_dma_buf *next;  /* next buffer to load */
 struct s3c_dma_buf *end;  /* end of queue */
  ...
}
int s3c2410_dma_enqueue(unsigned int channel, void *id,dma_addr_t data, int size)
{
 struct s3c2410_dma_chan *chan = lookup_dma_channel(channel);
 struct s3c_dma_buf *buf;
 unsigned long flags;
 
 buf = kmem_cache_alloc(dma_kmem, GFP_ATOMIC); --在s3c_dma_init中dma_kmem = kmalloc_cache_create创建一个新的高速缓存对象。在这里调用kmem_cache_aloc
                                                 从中分配内存对象。关于这个以后在理解内存时候,好好的理解。
 if (buf == NULL) {
  return -ENOMEM;
 }
 buf->next = NULL;
 buf->data = buf->ptr = data;
 buf->size = size;
 buf->id = id;
 buf->magic = BUF_MAGIC;

local_irq_save(flags);

if (chan->curr == NULL) {   --如果当前的缓冲区为NULL
  chan->curr = buf;         当前DMA缓冲区设置为buf
  chan->end = buf;          同时将队列未同样设为buf
  chan->next = NULL;       
 }
 else {                     如果当前缓冲区不为NULL   
  if (chan->end == NULL)   /* In case of flushing */
     {}
  else {
   chan->end->next = buf; 将上一个buffer的next设置为buf    ????
   chan->end = buf;
  }
 }
 /* if necessary, update the next buffer field */
 if (chan->next == NULL)
  chan->next = buf;

/* check to see if we can load a buffer */
 if (chan->state == S3C_DMA_RUNNING) {          --如果当前DMA状态为RUNNING,则等待加载
  if (chan->load_state == S3C_DMALOAD_1LOADED && 1) { --如果有一个buffer以被加载,但是没有被确认。等待加载。
   if (s3c_dma_waitforload(chan, __LINE__) == 0) {
    local_irq_restore(flags);                 --如果加载超时返回错误值
    return -EINVAL;
   }
  }
 }
 else if (chan->state == S3C_DMA_IDLE) {      --如果通道空闲,并且被设置为自动启动传输,则直接开始传输
  if (chan->flags & S3C2410_DMAF_AUTOSTART) {
   s3c2410_dma_ctrl(channel, S3C2410_DMAOP_START);  
 }
 else {
   pr_debug("loading onto stopped channel\n");  --否则错误,在该dma中并没有实现暂停的功能
  }
 }
 local_irq_restore(flags);
 return 0;
}
如果将这几个函数分开理解比较困难,如果将中断处理函数加入一起比较好理解。
static int s3c_dma_waitforload(struct s3c2410_dma_chan *chan, int line)
{
 int timeout = chan->load_timeout;
 int took;
 if (chan->load_state != S3C_DMALOAD_1LOADED) {  --为什么还会检测load_state?因为中断随时都会发生,导致load_state的改变。
  return 0;
 }

if (chan->stats != NULL)
  chan->stats->loads++;         --跟新channel的加载次数

while (--timeout > 0) {
  if ((dma_rdreg(chan->dma_con, S3C_DMAC_ENBLD_CHANNELS)) & (0x1 << chan->number)) {   --检测DMACEnbldChns 寄存器,如果对应的通道被启动,则说明
                                                                                         前一个buffer被加载,并启动dma          
    took = chan->load_timeout - timeout;                         --跟新等待时间。
   s3c_dma_stats_timeout(chan->stats, took);
   switch (chan->load_state) {
   case S3C_DMALOAD_1LOADED:                                   --同时更改load_state
    chan->load_state = S3C_DMALOAD_1RUNNING;                  --同时将状态该为运行态
    break;
   default:
    dbg();
   }
   return 1;                                     --如果在限定时间内加载则返回1
  }
 }
 if (chan->stats != NULL) {
  chan->stats->timeout_failed++;                  --超时,则更新通道状态。
 }
 return 0;                                         --超时返回0
}
下面来看一下中断处理函数分析。
static irqreturn_t s3c_dma_irq(int irq, void *devpw)
{
 unsigned int channel = 0, dcon_num, i;
 unsigned long tmp;
 s3c_dma_controller_t *dma_controller = (s3c_dma_controller_t *) devpw;
 
 struct s3c2410_dma_chan *chan=NULL;
 struct s3c_dma_buf *buf;

dcon_num = dma_controller->number;
 tmp = dma_rdreg(dma_controller, S3C_DMAC_INT_TCSTATUS);  --读取当前控制器的中断状态,每一个控制器都有8个通道,每一个通道对应中断状态寄存器的每一位   
 if(tmp==0) {
  return IRQ_HANDLED;
 }
 for (i = 0; i < S3C_CHANNELS_PER_DMA; i++) {
  if (tmp & 0x01) {
   channel = i;
   chan = &s3c_dma_chans[channel + dcon_num * S3C_CHANNELS_PER_DMA];  --获取产生中断的通道信息。
   buf = chan->curr;                                        --获取当前buffer的信息
   /* modify the channel state */
   switch (chan->load_state) {             load_state状态的切换
    case S3C_DMALOAD_1RUNNING:               将运行态切换成,没有加载没有运行
     chan->load_state = S3C_DMALOAD_NONE;           
     break;
    case S3C_DMALOAD_1LOADED:                将运行态切换成,没有加载没有运行,抛弃更改状态
     chan->load_state = S3C_DMALOAD_NONE;
     break;
    case S3C_DMALOAD_1LOADED_1RUNNING:       一个加载一个运行,改为一个加载
     chan->load_state = S3C_DMALOAD_1LOADED;
     break;
    case S3C_DMALOAD_NONE:
     break;
    default:
     break;
   }
   if (buf != NULL) {    --进行二次切换将下一个缓冲区前置
    chan->curr = buf->next;
    buf->next = NULL;

if (buf->magic != BUF_MAGIC) {
     goto next_channel;
    }
    s3c_dma_buffdone(chan, buf, S3C2410_RES_OK);  --回调函数,会回调s3c2410_dma_set_buffdone_fn中设置的函数
    s3c_dma_freebuf(buf);
   }
   else {   
   }
   if (chan->next != NULL) { 
    unsigned long flags;
    switch (chan->load_state) {
    case S3C_DMALOAD_1RUNNING:
     break;
    case S3C_DMALOAD_NONE:
     break;
    case S3C_DMALOAD_1LOADED:
     if (s3c_dma_waitforload(chan, __LINE__) == 0) {
      goto next_channel;
     }
     break;
    case S3C_DMALOAD_1LOADED_1RUNNING:
     goto next_channel;

default:
     goto next_channel;
    }

local_irq_save(flags);
    s3c_dma_loadbuffer(chan, chan->next);  
    local_irq_restore(flags);
    
   } else {
    s3c_dma_lastxfer(chan);    
    if (chan->load_state == S3C_DMALOAD_NONE) {
     s3c2410_dma_ctrl(chan->index | DMACH_LOW_LEVEL, S3C2410_DMAOP_STOP);
    }
   }
  }
next_channel:
  tmp >>= 1;

}
 s3c_clear_interrupts(chan->dma_con->number, chan->number); 
 return IRQ_HANDLED;
}
其中主要是dma启动
static int s3c_dma_start(struct s3c2410_dma_chan *chan)
{
 unsigned long flags;
 local_irq_save(flags);

if (chan->state == S3C_DMA_RUNNING) {
  local_irq_restore(flags);
  return 0;
 }

chan->state = S3C_DMA_RUNNING;
 if (chan->load_state == S3C_DMALOAD_NONE) {
  if (chan->next == NULL) {
   chan->state = S3C_DMA_IDLE;
   local_irq_restore(flags);
   return -EINVAL;
  }
  s3c_dma_loadbuffer(chan, chan->next);
 }
 if (!chan->irq_enabled) {
  enable_irq(chan->irq);
  chan->irq_enabled = 1;
 }
 dma_wrreg(chan, S3C_DMAC_CxCONFIGURATION, chan->config_flags);
 s3c_dma_call_op(chan, S3C2410_DMAOP_START);
 local_irq_restore(flags);
 return 0;
}

static inline int s3c_dma_loadbuffer(struct s3c2410_dma_chan *chan,struct s3c_dma_buf *buf)
{
 writel(buf->data, chan->addr_reg);

dma_wrreg(chan, S3C_DMAC_CxCONTROL0, chan->dcon);
 dma_wrreg(chan, S3C_DMAC_CxCONTROL1, (buf->size / chan->xfer_unit));
 
 chan->next = buf->next;
 switch (chan->load_state) {
 case S3C_DMALOAD_NONE:
  chan->load_state = S3C_DMALOAD_1LOADED;
  break;

case S3C_DMALOAD_1RUNNING:
  chan->load_state = S3C_DMALOAD_1LOADED_1RUNNING;
  break;
 default:
  break;
 }
 return 0;
}


http://www.taodudu.cc/news/show-4902636.html

相关文章:

  • S3C6410开发(1)-初步入门
  • S3C6410移植linux4.17内核(一)
  • S3C6410 NAND启动流程
  • s3c6410时钟体系
  • s3c6410中断处理
  • S3C6410中断分类
  • s3c6410 时钟设置
  • s3c6410存储系统 (一)
  • qgis控制滚轮转动地图比例尺的变化幅度
  • 价格之战
  • TiDB中的混沌测试实践
  • 基于混沌的正余弦鲸鱼优化算法-附代码
  • 鸿蒙系统发红包,混沌鸿蒙录红包版
  • 基于异常注入(混沌工程)的测试思考
  • 很混沌
  • 混沌神经网络(Chaos Neural Network)
  • 【181023】VC++牛顿法解方程之混沌情况图形示例源代码
  • 伪随机序列调相位C语言,混沌通信实验报告范文
  • 混沌与秩序服务器维护,混沌与秩序2切换角色以及服务器教程 注销账号教程
  • 混沌策略和单纯形法改进的鲸鱼优化算法-附代码
  • 混沌策略和单纯形法改进的鲸鱼优化算法
  • 57-混沌操作法之我见:三、突破思想.(2015.2.10)
  • 55-混沌操作法之我见:一、逆势操作.(2015.2.7)
  • 56-混沌操作法之我见:二、AO、AC指标.(2015.2.9)
  • An Overview of TVM and Model Optimization TE
  • 非线性有限元
  • STM32cubeIDE “make: *** [Core/Src/subdir.mk:36: Core/Src/stm32f1xx_hal_msp.o] Error 2“
  • EasyPOI完美实现导入导出,实用简单,一行代码即可
  • 用matlab编程实现h鲁棒控制算法,利用matlab实现H-infinity鲁棒控制
  • 稀疏矩阵相乘mmult

S3c6410linux下DMA驱动相关推荐

  1. linux dma驱动,linux下DMA驱动测试代码

    DMA传输可以是内存到内存.内存到外设和外设到内存.这里的代码通过dma驱动实现了内存到内存的数据传输. /* Function description:When we call dmatest_re ...

  2. Linux下DMA驱动

    通用设备的动态DMA映射 DMA是数据传输的快速通道,不经过CPU,只占用总线周期. 设置需要用到DMA通道传输数据的源.目的.长度等参数,让DMA自行传输, 传输完毕后,比较源.目的的最终数据,检验 ...

  3. Linux下DMA驱动api 以及测试实列

    dmaengine framwork主要分为两部分:DMA controller 和DMA engine API.涉及内核相关文档:Documentation/damengine目录.Document ...

  4. Linux DMA 驱动学习总结

    Linux DMA驱动构架分析 以linux2.6.32中的S3C2440驱动为例进行分析,DMA驱动所对应的源码为linux-2.6.32.2\arch \arm\mach-s3c2440\dma. ...

  5. Linux DMA驱动构架分析

    转载于: http://www.voidcn.com/blog/bcbobo21cn/article/p-5777739.html 以linux2.6.32中的S3C2440驱动为例进行分析,DMA驱 ...

  6. linux 网络dma驱动,S3C2410的Linux下DMA驱动程序开发

    网上介绍Linux下的一般驱动程序开发示例浩如烟海,或是因为简单,关于DMA驱动的介绍却寥寥无几:近期因工作需要,花了几日时间开发了某设备在S3C2410处理器Linux下DMA通信的驱动程序,有感于 ...

  7. 2021-08-16Zynq linux系统下的AXI DMA驱动与应用程序简单Demo实现

    在参考网友(天使之猜)的例程(https://blog.csdn.net/hello_jinjin/article/details/102058119)中遇到了一些问题,导致内核崩溃. 在英勇无比的网 ...

  8. 米尔科技ZYNQ -Linux下的DMA驱动

    一.目标 在米尔科技的z-turn板上实现linux下的DMA驱动,同时对DMA中断进行测试. 二.分析 ZYNQ的AXIDMA有Direct Register Mode和Scatter/Gather ...

  9. linux zynq ps dma,Zynq PS侧DMA驱动

    Linux中,驱动必然会有驱动对应的设备类型.在linux4.4版本中,其设备是以设备树的形式展现的. PS端设备树的devicetree表示如下 324 dmac_s: dmac@f8003000 ...

最新文章

  1. 何为Java 中的多态?
  2. Spring 4.1和Java 8:java.util.Optional
  3. linux-x86_64 error,ORA-09817/Linux-x86_64 Error: 28: No space left on device/ORA-01075
  4. 【Swing 3】布局管理器与简单的聊天界面
  5. Linux之ansible 自动化运维工具
  6. QQ用户文件夹下即(user文件夹) 各个文件都是干什么的
  7. 最新米酷6.26影视源码+解析接口+步骤
  8. 武汉大学计算机国家网络安全学院怎么样,武汉大学国家网络安全学院怎么样?...
  9. ZYNQ下载程序出现错误Memory write error at 0x100000. APB
  10. 朴素贝叶斯和情感分类
  11. SQL server 期末复习
  12. 固态硬盘系统迁移踩过的坑
  13. 基于matlab介绍传感器融合和跟踪工具箱中用于评估跟踪器性能的不同定量分析工具(附源码)
  14. 深度学习Ubuntu20.04+CUDA+Pytorch环境配置+无显示器远程控制(1)
  15. 十年自学编程成才(编程小白必看)
  16. 23微分方程和exp(At)
  17. 新编好的大盘指数预测程序,欢迎关注
  18. unity学习 -- 游戏资源导入
  19. 问题:SQLite, Gears在9530上面可以运行,在9630上面就不行
  20. JGI Phytozome 批量下载的几种方法

热门文章

  1. Linux新手之路 - date cal 命令
  2. 2020北航,南大软院,计算所夏令营,北邮,西交人机所预推免记录
  3. 【无人机】无人机的气象数据采集系统设计(Matlab代码实现)
  4. 双连通分量(点-双连通分量边-双连通分量)
  5. HALCON几何变换
  6. Pytorch搭建LeNet5
  7. 数字化时代-16:生意人 商人 企业家 资本家 区别
  8. C++课设:汽车站车票管理系统
  9. EZDML 使用教程
  10. BSA-商业软件联盟