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


 

 

 

如果不曾相逢 也許 心緒永遠不會沉重 如果真的失之交臂 恐怕一生也不得輕松 一個眼神 便足以讓心海 掠過颶風 

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

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

只要彼此愛過一次 就是無憾的人生

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

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

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

也許有些事情早已經寫好,也許一顆心早已經注定,只是在騙自己,她還在愛着你~

 

DMA概述

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

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM