一、DMA 介紹(直接存儲器通路)
DMA(Direct Memory Access,直接記憶體存取) 是所有現代電腦的重要特色,它允許不同速度的硬體裝置來溝通,而不需要依賴于 CPU 的大量中斷負載。否則,CPU 需要從來源把每一片段的資料複制到暫存器,然後把它們再次寫回到新的地方。在這個時間中,CPU 對于其他的工作來說就無法使用。
DMA 傳輸将資料從一個位址空間複制到另外一個位址空間。當CPU 初始化這個傳輸動作,傳輸動作本身是由 DMA 控制器來實行和完成。典型的例子就是移動一個外部記憶體的區塊到晶片内部更快的記憶體區。像是這樣的操作并沒有讓處理器工作拖延,反而可以被重新排程去處理其他的工作。DMA 傳輸對于高效能 嵌入式系統算法和網絡是很重要的。
在實作DMA傳輸時,是由DMA控制器直接掌管總線,是以,存在着一個總線控制權轉移問題。即DMA傳輸前,CPU要把總線控制權交給DMA控制器,而在結束DMA傳輸後,DMA控制器應立即把總線控制權再交回給CPU。一個完整的DMA傳輸過程必須經過DMA請求、DMA響應、DMA傳輸、DMA結束4個步驟。
- 請求CPU對DMA控制器初始化,并向I/O接口發出操作指令,I/O接口提出DMA請求。
- 響應DMA控制器對DMA請求判别優先級及屏蔽,向總線裁決邏輯提出總線請求。當CPU執行完目前總線周期即可釋放總線控制權。此時,總線裁決邏輯輸出總線應答,表示DMA已經響應,通過DMA控制器通知I/O接口開始DMA傳輸。
-
傳輸DMA控制器獲得總線控制權後,CPU即刻挂起或隻執行内部操作,由DMA控制器輸出讀寫指令,直接控制RAM與I/O接口進行DMA傳輸。
在DMA控制器的控制下,在存儲器和外部裝置之間直接進行資料傳送,在傳送過程中不需要中央處理器的參與。開始時需提供要傳送的資料的起始位置和資料長度。
- 結束
當完成規定的成批資料傳送後,DMA控制器即釋放總線控制權,并向I/O接口發出結束信号。當I/O接口收到結束信号後,一方面停 止I/O裝置的工作,另一方面向CPU提出中斷請求,使CPU從不介入的狀态解脫,并執行一段檢查本次DMA傳輸操作正确性的代碼。最後,帶着本次操作結果及狀态繼續執行原來的程式。
由此可見,DMA傳輸方式無需CPU直接控制傳輸,也沒有中斷處理方式那樣保留現場和恢複現場的過程,通過硬體為RAM與I/O裝置開辟一條直接傳送資料的通路,使CPU的效率大為提高。
二、Linux下核心DMA相關函數介紹
2.1 配置設定一塊實體位址連續的記憶體
static inline void *dma_alloc_writecombine(struct device *dev, size_t size,dma_addr_t *dma_handle, gfp_t flag) |
- 功能介紹:配置設定一塊實體位址連續的記憶體,供後續DMA傳輸使用。
-
函數參數:struct device *dev : 傳入裝置指針,沒有填NULL。
size_t size : 配置設定的空間大小。
dma_addr_t *dma_handle :存放配置設定之後的實體位址。
gfp_t flag :配置設定的屬性。常用: GFP_KERNEL
- 傳回值: 實體位址對應的虛拟位址,核心代碼本身隻能操作虛拟位址。
2.2 釋放配置設定的記憶體
static inline void dma_free_writecombine(struct device *dev, size_t size,void *cpu_addr, dma_addr_t dma_handle) |
- 功能介紹:釋放之前申請的記憶體空間。
- 函數參數:
struct device *dev : 傳入裝置指針,沒有填NULL。
size_t size : 釋放空間的大小。
dma_addr_t *dma_handle :空間的虛拟位址(dma_alloc_writecombine傳回值)。
dma_addr_t dma_handle :空間實體位址
2.3 配置設定記憶體示例
#include <linux/kernel.h> #include <linux/module.h> #include <linux/fs.h> #include <linux/uaccess.h> #include <linux/io.h> #include <linux/dma-mapping.h> #define BUF_SIZE (512*1024) static u32 src_phys; 存放配置設定的實體起始位址 static unsigned char *src; //存放配置設定的虛拟起始位址 static u8 buff[]="配置設定一塊連續的實體位址,可供DMA使用"; static int __init tiny4412_dma_dev_init(void) { /*配置設定一塊連續的實體位址,可供DMA使用*/ src=dma_alloc_writecombine(NULL,BUF_SIZE,&src_phys,GFP_KERNEL); if(src==NULL) { printk("空間配置設定失敗!\n"); } else { printk("驅動安裝成功!\n"); /*提示語句*/ printk("配置設定的實體位址:0x%X\n",&src_phys); printk("配置設定的虛拟位址:0x%X\n",src); memcpy(src,buff,sizeof(buff)); } /* 說明: Tiny4412開發闆記憶體條大小為1G */ return 0; } static void __exit tiny4412_dma_dev_exit(void) { if(src) { printk("buff=%s\n",buff); /*釋放配置設定的空間*/ dma_free_writecombine(NULL,BUF_SIZE,src,src_phys); } printk("驅動解除安裝成功!\n"); } module_init(tiny4412_dma_dev_init); module_exit(tiny4412_dma_dev_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("tiny4412 wbyq"); |