DMA子是CPU中實現數據傳輸的一種方式,CPU配置好DMA控制器以後發起數據傳輸,CPU自己不參與數據傳輸的動做中去。node
DMA種類:
分爲外設DMA和DMA控制器。其中外設DMA實現的爲特定的外設與內存之間的數據傳輸,通常是外設向RAM單向傳輸數據。而DMA控制器則能夠實現任意外設與內存之間的數據傳輸。此時外設跟CPU控制器之間經過流控制信號來保證傳輸通道的正常運行。linux
DMA傳輸的數據寬度不固定。
cookie
還能夠實現任意長度的burst 操做。burst是DMA控制地址總線自行改變。app
DMA也支持分散集合模式,即內存中數據並不是連續,而是分配在多個塊中,塊大小也不同,這時候DMA能夠根據Scatter Gather Descriptors來進行DMA數據傳輸。框架
Descriptors是一個單向列表,描述了每塊數據的位置和大小還有其餘配置。DMA自行解析Descriptors的內容進行數據傳輸並尋找小一個鏈表節點,async
若是Descriptor 鏈表是一個循環連接,則傳輸被叫作環形傳輸(Cyclic Transfers)。
函數
linux實現了DMA框架,叫作DMA Engine,內核驅動開發者必須按照固定的流程編碼才能正確的使用DMA。DMA Engine提供出來了Slave API供其它內核調用。這些API實現了複雜的scatter gather 傳輸模式,經過這些API實現DMA傳輸的驅動被叫作DMA client.ui
DMA中調用API的順序爲:
編碼
DMA client的第一步就是要申請DMA 通道,有的DMA client 可能須要申請特殊的通道,而有的DMA client能夠申請任意可用的通道。通道申請的API爲dma_request_channel 或者他的某一變形API。spa
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, };
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\n"); 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\n"); 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\n"); 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\n"); } if(register_chrdev_region(MKDEV(200,0),1,"fpga")) { printk (KERN_INFO "alloc chrdev error.\n"); return -1; } fpga_cdev=cdev_alloc(); if(!fpga_cdev) { printk (KERN_INFO "cdev alloc error.\n"); 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.\n"); return -1; } printk("fpga driver loaded\n"); return 0; } late_initcall(hello_init); MODULE_LICENSE("GPL");