linux dmaengine編程

原文鏈接:https://blog.csdn.net/u012247418/article/details/82313959

開發板:A33,運行linux-3.4.39

主機:Ubuntu 14.04

----------------------------------------------

 

DMA是Direct Memory Access的縮寫,顧名思義,就是繞開CPU直接訪問memory的意思。在計算機中,相比CPU,memory和外設的速度是非常慢的,因而在memory和memory(或者memory和設備)之間搬運數據,非常浪費CPU的時間,造成CPU無法及時處理一些實時事件。因此,工程師們就設計出來一種專門用來搬運數據的器件----DMA控制器,協助CPU進行數據搬運。

DMA傳輸可以是內存到內存、內存到外設和外設到內存。這裏的代碼通過dma驅動實現了內存到內存的數據傳輸。linux實現了DMA框架,叫做DMA Engine,內核驅動開發者必須按照固定的流程編碼才能正確的使用DMA。

 

1. DMA用法包括以下的步驟: 

1)分配一個DMA通道; 

dma_request_channel()

2)設置controller特定的參數; 

none

3)獲取一個傳輸描述符;

device_prep_dma_memcpy() 

4)提交傳輸描述符; 

tx_submit();

5)dma_async_issue_pending()

 

2. 測試:

1)交叉編譯成ko模塊,下載到A33開發板

2)加載模塊:insmod dma.ko

3)執行:cat /dev/dma_test

執行此操作會產生一次DMA請求,將src內存數據複製到dst內存區域,複製完後會調用回調函數,複製過程中不需要CPU的參與。

注:在Ubuntu下dma_request_channel()失敗,原因未知,所以轉到A33下測試。

 

3. 其它

申請DMA緩衝區:

void *dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle, int flag);

該函數實際獲得兩個地址, 

1、函數的返回值是一個 void *,代表緩衝區的內核虛擬地址 

2、相關的總線地址(物理地址),保存在dma_handle中

物理地址和虛擬地址存在映射關係。DMA傳輸使用物理地址,而CPU操作的是虛擬地址。

如在tx = dev->device_prep_dma_memcpy(chan, dma_dst, dma_src, 512, flags);使用了物理地址

在dma_callback_func()中使用了虛擬地址。

使用DMA傳輸數據必須要基於DMA緩衝區,不能使用其他的內存區域,如kmalloc開闢的內存。

 

4. 源碼dma.c

/*
Function description:When we call dmatest_read(),it will transmit src memory data
to dst memory,then print dst memory data by dma_callback_func(void) function.
*/
#include<linux/module.h>
#include<linux/init.h>
#include<linux/fs.h>
#include<linux/sched.h>
 
#include<linux/device.h>
#include<linux/string.h>
#include<linux/errno.h>
 
#include<linux/types.h>
#include<linux/slab.h>
#include<linux/dmaengine.h>
#include<linux/dma-mapping.h>
 
#include<asm/uaccess.h>
 
 
 
#define DEVICE_NAME "dma_test"
 
unsigned char dmatest_major;
static struct class *dmatest_class;
 
struct dma_chan *chan;
 //bus address
dma_addr_t dma_src;
dma_addr_t dma_dst;
//virtual address
char *src = NULL;
char *dst = NULL ;
struct dma_device *dev;
struct dma_async_tx_descriptor *tx = NULL;
enum dma_ctrl_flags flags;
dma_cookie_t cookie;
 
 
//When dma transfer finished,this function will be called.
void dma_callback_func(void)
{
    int i;
    printk("dma transfer ok.\n");
 
    for (i = 0; i < 512; ){
        printk("dst[%d]:%c ", i, dst[i]);
        i += 10;
    }
    printk("\n");
}
 
int dmatest_open(struct inode *inode, struct file *filp)
{
    return 0;
}
 
int dmatest_release(struct inode *inode, struct file *filp)
{
    return 0;
}
 
static ssize_t dmatest_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos)
{
    int ret = 0;
    //alloc a desc,and set dst_addr,src_addr,data_size.
    tx = dev->device_prep_dma_memcpy(chan, dma_dst, dma_src, 512, flags);
    if (!tx){
        printk("Failed to prepare DMA memcpy");
    }
    
    tx->callback = dma_callback_func;//set call back function
    tx->callback_param = NULL;
    cookie = tx->tx_submit(tx); //submit the desc
    if (dma_submit_error(cookie)){
        printk("Failed to do DMA tx_submit");
    }
    
    printk("begin dma transfer.\n");    
    dma_async_issue_pending(chan);    //begin dma transfer
    
    return ret;
}
 
static ssize_t dmatest_write(struct file *filp, const char __user *buf, size_t size, loff_t *ppos)
{
    int ret = 0;
    return ret;
}
 
static const struct file_operations dmatest_fops = {
    .owner = THIS_MODULE,
    .read = dmatest_read,
    .write = dmatest_write,
    .open = dmatest_open,
    .release = dmatest_release,
};
 
 
int dmatest_init(void)
{
    int i;
    dma_cap_mask_t mask;
    
    //the first parameter 0 means allocate major device number automatically
    dmatest_major = register_chrdev(0, DEVICE_NAME, &dmatest_fops);
    if (dmatest_major < 0) 
        return dmatest_major;
    //create a dmatest class
    dmatest_class = class_create(THIS_MODULE,DEVICE_NAME);
    if (IS_ERR(dmatest_class))
        return -1;
    //create a dmatest device from this class
    device_create(dmatest_class,NULL,MKDEV(dmatest_major,0),NULL,DEVICE_NAME);
    printk("device node create ok.\n");
 
    //alloc 512B src memory and dst memory
    src = dma_alloc_coherent(NULL, 512, &dma_src, GFP_KERNEL);
    printk("src = 0x%x, dma_src = 0x%x\n",src, dma_src);
    
    dst = dma_alloc_coherent(NULL, 512, &dma_dst, GFP_KERNEL);
    printk("dst = 0x%x, dma_dst = 0x%x\n",dst, dma_dst);
        
    for (i = 0; i < 512; i++){
        *(src + i) = 'a';
    }
 
    dma_cap_zero(mask);
    dma_cap_set(DMA_MEMCPY, mask);    //direction:memory to memory
    chan = dma_request_channel(mask,NULL,NULL);     //request a dma channel
    if(chan)
        printk("dma channel id = %d\n",chan->chan_id);
    else{
        printk("dma_request_channel faild.\n");
        return -1;
    }
    
    flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT;
    dev = chan->device;
    
    return 0;
}
 
void dmatest_exit(void)
{
    unregister_chrdev(dmatest_major,DEVICE_NAME);//release major device number
    device_destroy(dmatest_class,MKDEV(dmatest_major,0));//destroy globalmem device
    class_destroy(dmatest_class);//destroy globalmem class
    
    //free memory and dma channel
    dma_free_coherent(NULL, 512, src, &dma_src);
    dma_free_coherent(NULL, 512, dst, &dma_dst);
    
    dma_release_channel(chan);
}
 
 
module_init(dmatest_init);
module_exit(dmatest_exit);
 
MODULE_LICENSE("GPL");
 
 ———————————————— 
版權聲明:本文爲CSDN博主「crazy_baoli」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/u012247418/article/details/82313959

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章