蛻變成蝶~Linux設備驅動之DMA

 若是未曾相逢 也許 心緒永遠不會沉重 若是真的失之交臂 恐怕一輩子也不得輕鬆 一個眼神 便足以讓心海 掠過颶風 html

在貧瘠的土地上 更深地懂得風景 一次遠行 便足以憔悴了一顆 羸弱的心 linux

每望一眼秋水微瀾 便巴不得 淚水盈盈 死怎能不 從容不迫 愛又怎能 無動於衷 緩存

只要彼此愛過一次 就是無憾的人生數據結構

也許 也許,永遠沒有那一天 前程如朝霞般絢爛 也許,永遠沒有那一天併發

成功如燈火般輝煌 也許,只能是這樣 攀援卻達不到峯頂 也許,只能是這樣 奔流卻掀不起波浪app

也許,咱們能給予你的 只有一顆 飽經滄桑的心 和滿臉風霜ide

也許有些事情早已經寫好,也許一顆心早已經註定,只是在騙本身,她還在愛着你~函數

 

DMA概述ui

  DMA是一種無需CPU的參加就可讓外設與系統內存之間進行雙向數據傳輸的硬件機制。它可使系統CPU從實際的I/O數據傳輸過程當中擺脫出來,大大提升系統的吞吐率,而且在傳輸期間,CPU還能夠併發執行其餘任務。spa

DMA與cache的一致性

  cache用做CPU針對內存的緩存,避免CPU每一次都要與相對來講慢點的內存交互數據,從而來提升數據的訪問速率,而DMA能夠用做內存與外設之間傳輸數據的方式,數據不須要通過CPU週轉。

「假設設備驅動程序把一些數據填充到內存緩衝區中,而後馬上命令硬件設備利用DMA傳送方式讀取該數據。若是DMA訪問這些物理RAM內存單元,而相應的硬件高速緩存行的內容尚未寫入RAM中,那麼硬件設備所讀取的至就是內存緩衝區中的舊值。」

如今有兩種方法來處理DMA緩衝區:
一致性DMA映射:
書上講的比較抽象,通俗地所就是任何對DMA緩衝區的改寫都會直接更新到內存中,也稱之爲「同步的」或者「一致的」。
流式DMA映射:
根據我的的理解,這裏的流即輸入輸出流,咱們須要事先指定DMA緩衝區的方向。
啓動一次流式DMA數據傳輸分爲以下步驟( DMA驅動開發介紹僅適合S3C2410處理器類型):
1. 分配DMA緩衝區。
  在DMA設備不採用S/G(分散/彙集)模式的狀況下,必須保證緩衝區是物理上連續的,linux內核有兩個函數用來分配連續的內存:kmalloc()和__get_free_pages()。這兩個函數都有分配連續內存的最大值,kmalloc以分配字節爲單位,最大約爲64KB,__get_free_pages()以分配頁爲單位,最大能分配2^order數目的頁,order參數的最大值由include/linux/Mmzone.h文件中的MAX_ORDER宏決定(在默認的2.6.18內核版本中,該宏定義爲10。也就是說在理論上__get_free_pages函數一次最多能申請1<<10 * 4KB也就是4MB的連續物理內存,在Xilinx Zynq Linux內核中,該宏定義爲11)。
2. 創建流式映射
  在對DMA衝區進行讀寫訪問以後,且在啓動DMA設備傳輸以前,啓用dma_map_single()函數創建流式DMA映射,這兩個函數接受緩衝區的線性地址做爲其參數並返回相應的總線地址。
3. 釋放流式映射
  當DMA傳輸結束以後咱們須要釋放該映射,這時調用dma_unmap_single()函數。
注意:
(1). 爲了不高速緩存一致性問題,驅動程序在開始從RAM到設備的DMA數據傳輸以前,若是有必要,應該調用dma_sync_single_for_device()函數刷新與DMA緩衝區對應的高速緩存行。
(2). 從設備到RAM的一次DMA數據傳送完成以前設備驅動程序是不能夠訪問內存緩衝區的,但若是有必要的話,驅動程序在讀緩衝區以前,應該調用dma_sync_single_for_cpu()函數使相應的硬件高速緩存行無效。
(3). 雖然kmalloc底層也是用__get_free_pages實現的,不過kmalloc對應的釋放緩衝區函數爲kfree,而__get_free_pages對應的釋放緩衝區函數爲free_pages。具體與__get_free_pages有關係的幾個申請與釋放函數以下:
申請函數:
alloc_pages(gfp_mask,order)
返回第一個所分配頁框描述符的地址,或者若是分配失敗則返回NULL。
__get_free_pages(gfp_mask,order)
相似於alloc_pages(),但它返回第一個所分配頁的線性地址。若是須要得到線性地址對應的頁框號,那麼須要調用virt_to_page(addr)宏產生線性地址。

釋放函數:

__free_pages(page,order)
這裏主要強調page是要釋放緩衝區的線性首地址所在的頁框號
free_pages(page,order)
這個函數相似於__free_pages(page,order),可是它接收的參數爲要釋放的第一個頁框的線性地址addr

DMA驅動主要數據結構:

1DMA單個內核緩衝區數據結構:

typedef struct dma_buf_s {
int size;    /* buffer size:緩衝大小 */
dma_addr_t dma_start;    /* starting DMA address :緩衝區起始物理地址*/
int ref;    /* number of DMA references 緩衝區起始虛擬地址*/
void *id;    /* to identify buffer from outside 標記 */
int write;    /* 1: buf to write , 0: buf to read DMA讀仍是寫*/
struct dma_buf_s *next;    /* next buf to process 指向下一個緩衝區結構*/
} dma_buf_t;

2DMA寄存器數據結構:

/* DMA control register structure */
typedef struct {
volatile u_long DISRC;/源地址寄存器
volatile u_long DISRCC;//源控制寄存器
volatile u_long DIDST;//目的寄存器
volatile u_long DIDSTC;//目的控制寄存器
volatile u_long DCON;//DMA控制寄存器
volatile u_long DSTAT;//狀態寄存器
volatile u_long DCSRC;//當前源
volatile u_long DCDST;//當前目的
volatile u_long DMASKTRIG;//觸發掩碼寄存器
} dma_regs_t;

3DMA設備數據結構

/* DMA device structre */
typedef struct {
dma_callback_t callback;//DMA操做完成後的回調函數,在中斷處理例程中調用
u_long dst;//目的寄存器內容
u_long src;//源寄存器內容
u_long ctl;//此設備的控制寄存器內容
u_long dst_ctl;//目的控制寄存器內容
u_long src_ctl;//源控制寄存器內容
} dma_device_t;

4DMA通道數據結構

/* DMA channel structure */
typedef struct {
dmach_t channel;//通道號:可爲0,1,2,3
unsigned int in_use;    /* Device is allocated 設備是否已*/
const char *device_id;    /* Device name 設備名*/
dma_buf_t *head;    /* where to insert buffers 該DMA通道緩衝區鏈表頭*/
dma_buf_t *tail;    /* where to remove buffers該DMA通道緩衝區鏈表尾*/
dma_buf_t *curr;    /* buffer currently DMA'ed該DMA通道緩衝區鏈表中的當前緩衝區*/
unsigned long queue_count;    /* number of buffers in the queue 鏈表中緩衝區個數*/
int active;    /* 1 if DMA is actually processing data 該通道是否已經在使用*/
dma_regs_t *regs;    /* points to appropriate DMA registers 該通道使用的DMA控制寄存器*/
int irq;    /* IRQ used by the channel //通道申請的中斷號*/
dma_device_t write;    /* to write //執行讀操做的DMA設備*/
dma_device_t read;    /* to read 執行寫操做的DMA設備*/
} s3c2410_dma_t;

DMA驅動主要函數功能分析:

寫一個DMA驅動的主要工做包括:DMA通道申請、DMA中斷申請、控制寄存器設置、掛入DMA等待隊列、清除DMA中斷、釋放DMA通道.

int s3c2410_request_dma(const char *device_id, dmach_t channel,
dma_callback_t write_cb, dma_callback_t read_cb) (s3c2410_dma_queue_buffer);

函數描述:申請某通道的DMA資源,填充s3c2410_dma_t 數據結構的內容,申請DMA中斷。

輸入參數:device_id DMA 設備名;channel 通道號;

write_cb DMA寫操做完成的回調函數;read_cb DMA讀操做完成的回調函數

輸出參數:若channel通道已使用,出錯返回;不然,返回0

int s3c2410_dma_queue_buffer(dmach_t channel, void *buf_id,
dma_addr_t data, int size, int write) (s3c2410_dma_stop);

函數描述:這是DMA操做最關鍵的函數,它完成了一系列動做:分配並初始化一個DMA內核緩衝區控制結構,並將它插入DMA等待隊列,設置DMA控制寄存器內容,等待DMA操做觸發

輸入參數: channel 通道號;buf_id,緩衝區標識

dma_addr_t data DMA數據緩衝區起始物理地址;size DMA數據緩衝區大小;write 是寫仍是讀操做

輸出參數:操做成功,返回0;不然,返回錯誤號

int s3c2410_dma_stop(dmach_t channel)

函數描述:中止DMA操做。

int s3c2410_dma_flush_all(dmach_t channel)

函數描述:釋放DMA通道所申請的全部內存資源

void s3c2410_free_dma(dmach_t channel)

函數描述:釋放DMA通道

  由於各函數功能強大,一個完整的DMA驅動程序中通常只需調用以上3個函數便可。可在驅動初始化中調用s3c2410_request_dma,開始DMA傳輸前調用s3c2410_dma_queue_buffer,釋放驅動模塊時調用s3c2410_free_dma

 

   版權全部,轉載請註明轉載地址:http://www.cnblogs.com/lihuidashen/p/4470678.html

相關文章
相關標籤/搜索