zoukankan      html  css  js  c++  java
  • dma子系统 dmac

    DMA子是CPU中实现数据传输的一种方式,CPU配置好DMA控制器之后发起数据传输,CPU本身不参与数据传输的动作中去。

    DMA种类:
    分为外设DMA和DMA控制器。其中外设DMA实现的为特定的外设与内存之间的数据传输,一般是外设向RAM单向传输数据。而DMA控制器则可以实现任意外设与内存之间的数据传输。此时外设跟CPU控制器之间通过流控制信号来保证传输通道的正常运行。

    mark

    mark

    DMA传输的数据宽度不固定。
    mark

    还可以实现任意长度的burst 操作。burst是DMA控制地址总线自行改变。

    DMA也支持分散集合模式,即内存中数据并非连续,而是分配在多个块中,块大小也不一样,这时候DMA可以根据Scatter Gather Descriptors来进行DMA数据传输。

    mark

    Descriptors是一个单向列表,描述了每块数据的位置和大小还有其他配置。DMA自行解析Descriptors的内容进行数据传输并寻找小一个链表节点,

    如果Descriptor 链表是一个循环链接,则传输被叫做环形传输(Cyclic Transfers)。
    mark

    linux实现了DMA框架,叫做DMA Engine,内核驱动开发者必须按照固定的流程编码才能正确的使用DMA。DMA Engine提供出来了Slave API供其它内核调用。这些API实现了复杂的scatter gather 传输模式,通过这些API实现DMA传输的驱动被叫做DMA client.

    DMA中调用API的顺序为:
    mark

    DMA通道申请

    DMA client的第一步就是要申请DMA 通道,有的DMA client 可能需要申请特殊的通道,而有的DMA client可以申请任意可用的通道。通道申请的API为dma_request_channel 或者他的某一变形API。

    struct dma_chan *dma_request_channel(dma_cap_mask_t mask, dma_filter_fn filter_fn, void *filter_param);

    其中dma_cap_mask_t是根据dma_cap_sets指定的DMA传输类型。
    如:

        dma_cap_mask_t mask;
    
        dma_cap_zero(mask);
        dma_cap_set(DMA_MEMCPY,mask);
        dma_chan1 = dma_request_channel(mask,0,NULL);

    而传输类型具体列为:

    enum dma_transaction_type {
             DMA_MEMCPY,
              DMA_XOR,
              DMA_PQ,
              DMA_XOR_VAL,
             DMA_PQ_VAL,
              DMA_MEMSET,
             DMA_INTERRUPT,
              DMA_SG,
              DMA_PRIVATE,
              DMA_ASYNC_TX,
              DMA_SLAVE,
              DMA_CYCLIC,
              DMA_INTERLEAVE,
              /* last transaction type for creation of the capabilities mask */
              DMA_TX_TYPE_END,
      };

    配置DMA通道

    dmaengine_slave_config
    static inline int dmaengine_slave_config(struct dma_chan *chan,struct dma_slave_config *config)

    获取一个传输描述符

    dmaengine_prep_slave_single()

    提交传输到挂起队列

    dmaengine_submit

    发起传输申请

    dmaengine_issue_pending调用会从第一个描述符开始进行传输。如果DMA client 驱动有回调函数的话,会在传输完成后执行。

    一个简单的DMA传输案例:

    #include <linux/module.h>
    #include <linux/kernel.h>    /* printk() */
    #include <linux/moduleparam.h>
    #include <asm/uaccess.h>
    #include <asm/pgtable.h>
    #include <linux/fs.h>
    #include <linux/gfp.h>
    #include <linux/cdev.h>
    #include <linux/sched.h>
    #include <linux/interrupt.h>
    #include <linux/mm.h>
    #include <linux/kdev_t.h>
    #include <linux/proc_fs.h>
    #include <linux/ioctl.h>
    #include <linux/slab.h>
    #include <linux/mempool.h>
    #include <linux/mm.h>
    #include <linux/device.h>
    #include <linux/kobject.h>
    #include <linux/sysfs.h>
    #include <linux/dma-mapping.h>
    #include <linux/dmaengine.h>
    #include <asm/types.h>
    #include <asm/io.h>
    #include <asm/dma-mapping.h>
    
    #define DEST_ADDRESS 0x73800000
    #define SRC_ADDRESS   0x10400000
    
    static volatile struct completion comp1;
    static volatile struct dma_chan *dma_chan1;
    
    static int device_mmap(struct file *file,struct vm_area_struct* vma)
    {
        int size;
        printk("mmap fpga
    ");
        size=vma->vm_end - vma->vm_start;
        if(remap_pfn_range(vma,vma->vm_start,DEST_ADDRESS>>PAGE_SHIFT,0x800000,vma->vm_page_prot))
            return -EAGAIN;
        return 0;
    }
    static void dma_complete_func(void *completion)
    {
        complete(completion);
    }
    
    struct dma_async_tx_descriptor *tx1 = NULL;
    
    static int device_ioctl(struct inode *inode,struct file *file,int num,int param)
    {
            struct dma_device *dma_dev;
            enum dma_ctrl_flags flags;
            dma_cookie_t cookie;
            dma_dev = dma_chan1->device;
            flags =  DMA_CTRL_ACK | DMA_COMPL_SKIP_DEST_UNMAP | DMA_COMPL_SKIP_SRC_UNMAP;
            tx1 = dma_dev->device_prep_dma_memcpy(dma_chan1, DEST_ADDRESS,
                        SRC_ADDRESS+0x40000*param, 0x40000, flags);
            if (!tx1) {
                printk("Failed to prepare DMA memcpy
    ");
                return -1;
            }
            init_completion(&comp1);
            tx1->callback = dma_complete_func;
            tx1->callback_param = &comp1;
    
            cookie = tx1->tx_submit(tx1);
            if (dma_submit_error(cookie)) {
                printk("Failed to do DMA tx_submit
    ");
                return -1;
            }
    
            dma_async_issue_pending(dma_chan1);
    
            wait_for_completion(&comp1);
    }
    
    static struct file_operations fpga_fops =
    {
        .owner = THIS_MODULE,
        .mmap = device_mmap,
        .ioctl = device_ioctl,
    };
    
    static int __init hello_init (void)
    {
        dev_t fpga_dev;
        struct cdev *fpga_cdev;
        struct device *dev;
        dma_addr_t dm;
        dma_cap_mask_t mask;
    
        dma_cap_zero(mask);
        dma_cap_set(DMA_MEMCPY,mask);
        dma_chan1 = dma_request_channel(mask,0,NULL);
    
        if(dma_chan1 == 0)
        {
            printk("fpga:failed to request DMA channel
    ");
        }
    
        if(register_chrdev_region(MKDEV(200,0),1,"fpga"))
        {
             printk (KERN_INFO "alloc chrdev error.
    ");
             return -1;
        }
    
        fpga_cdev=cdev_alloc();
        if(!fpga_cdev)
        {
            printk (KERN_INFO "cdev alloc error.
    ");
             return -1;
        }
        fpga_cdev->ops = &fpga_fops;
        fpga_cdev->owner = THIS_MODULE;
    
        if(cdev_add(fpga_cdev,MKDEV(200,0),1))
        {
            printk (KERN_INFO "cdev add error.
    ");
             return -1;
        }
        printk("fpga driver loaded
    ");
    
      return 0;
    
    }
    
    late_initcall(hello_init);
    MODULE_LICENSE("GPL");
  • 相关阅读:
    ProtoBuf开发者指南(转)
    kafka的c/c++高性能客户端librdkafka简介
    SQL的执行顺序:
    zookeeper与卡夫卡集群搭建
    记一次ping: unknown host错误
    nginx服务器的rewrite功能
    nginx做http向https的自动跳转
    jQuery数据缓存
    jQuery的无new构建
    位操作符的计算优势
  • 原文地址:https://www.cnblogs.com/zym0805/p/7660295.html
Copyright © 2011-2022 走看看