Linux 4.0的dmaengine編程

原文鏈接:https://blog.csdn.net/were0415/article/details/54095899

在Linux 4.0下進行dmaengine的編程主要分爲兩部分,DMA Engine控制器編程和DMA Engine API編程。

DMA Engine API編程

slave DMA用法包括以下的步驟: 
1. 分配一個DMA slave通道; 
2. 設置slave和controller特定的參數; 
3. 獲取一個傳輸描述符; 
4. 提交傳輸描述符; 
5. 發起等待的請求並等待回調通知。

下面是以上每一步的詳細說明。

1. 分配一個DMA slave通道 
在slave DMA上下文通道的分配略有不同,客戶端驅動通常需要一個通道,這個通道源自特定的DMA控制器,在某些情況甚至需要一個特定的通道。請求通道的API是channel dma_request_channel()。 
其接口如下:

struct dma_chan *dma_request_channel(dma_cap_mask_t mask,
            dma_filter_fn filter_fn,
            void *filter_param);
1
2
3
其中dma_filter_fn接口定義如下:

typedef bool (*dma_filter_fn)(struct dma_chan *chan, void *filter_param);
1
filter_fn是可選的,但是對於slave和cyclic通道我們強烈推薦使用,因爲它們需要獲取一個特定的DMA通道。

當filter_fn參數爲空,dma_request_channel()函數簡單地返回第一個滿足mask參數的通道。 
否則,filter_fn函數將會對每個空閒的通道調用一次,同樣這些通道也是要滿足mask參數。當filter_fn返回true的時候說明期望的通道已經找到。

通過這個API分配的通道在dma_release_channel()函數調用前對於其他調用者是互斥的。

2. 設置slave和controller特定的參數 
這一步通常是傳遞一些特定的信息到DMA驅動。大多數slave DMA使用到的通用信息都在結構體dma_slave_config中。它允許客戶端對外設指定DMA的方向、DMA地址、總線寬度、DMA突發長度等等。

如果一些DMA控制器有更多要發送的參數,那它們應該試圖把結構體dma_slave_config內嵌到控制器特定的結構體中。對於客戶端來說將有更多的靈活性來傳遞更多需要的參數。 
接口如下:

int dmaengine_slave_config(struct dma_chan *chan,
                  struct dma_slave_config *config)
1
2
對於dma_slave_config結構體成員的詳解可在dmaengine.h中查看。

3. 獲取一個傳輸描述符 
DMA-engine支持多種slave傳輸模式: 
- slave_sg:DMA一列聚散buffers from/to外設; 
- dma_cyclic:實現一個循環的DMA操作 from/to外設,直到操作被停止; 
- interleaved_dma:對於Slave客戶端和M2M客戶端都很常見。這種情況下驅動已知Slave設備FIFO的地址。可對dma_interleaved_template結構體的成員設置適當的值來表示多種類型的操作;

這個傳輸API的返回值就是一個傳輸描述符。 
其接口如下:

struct dma_async_tx_descriptor *dmaengine_prep_slave_sg(
        struct dma_chan *chan, struct scatterlist *sgl,
        unsigned int sg_len, enum dma_data_direction direction,
        unsigned long flags);

struct dma_async_tx_descriptor *dmaengine_prep_dma_cyclic(
        struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len,
        size_t period_len, enum dma_data_direction direction);

struct dma_async_tx_descriptor *dmaengine_prep_interleaved_dma(
        struct dma_chan *chan, struct dma_interleaved_template *xt,
        unsigned long flags);
1
2
3
4
5
6
7
8
9
10
11
12
在調用dmaengine_prep_slave_sg()函數前外設驅動必須已經映射scatterlist,該映射必須保持直到DMA操作完成。 
一般的步驟如下:

    nr_sg = dma_map_sg(chan->device->dev, sgl, sg_len);
    if (nr_sg == 0)
        /* error */

    desc = dmaengine_prep_slave_sg(chan, sgl, nr_sg, direction, flags);
1
2
3
4
5
一旦傳輸描述符獲取成功,回調信息加入後描述符被提交。

注意:

對於Slave DMA,在回調函數被調用前接下來的傳輸描述符可能不能提交,因此slave DMA回調函數允許準備並提交一個新的傳輸描述符;
對於cyclic 
DMA,回調函數可被用來停止DMA操作,通過在回調函數內部調用dmaengine_terminate_all()函數完成;
因此,在調用回調函數前DMA engine驅動解鎖是非常重要的,因爲它會導致死鎖。
回調函數通常通過DMA engines的底半部tasklet方式調用,而不是直接通過中斷頂半部調用。 
4. 提交傳輸描述符 
一旦傳輸描述符準備好並且回調函數也加入後,下一步就是把傳輸描述符加入到DMA engine驅動的等待隊列。 
其接口如下:
dma_cookie_t dmaengine_submit(struct dma_async_tx_descriptor *desc)
1
返回值是一個cookie,主要用來檢查DMA engine活動的狀態過程,可通過其他的DMA engine API來實現。

dmaengine_submit()函數僅僅提交描述符到DMA engine的等待隊列,它不會啓動DMA操作。

5. 發起等待的請求並等待回調通知 
等待隊列裏面的傳輸描述符可通過調用issue_pending API激活。此時如果通道是空閒的,等待隊列中的第一個傳輸描述符將會啓動DMA操作。 
每次DMA操作完成後,等待隊列中的下一個描述符將會啓動DMA操作並且一個tasklet將會被處罰。如果我們在前面設定了客戶端驅動的回調通知函數,那麼tasklet將會調用這個函數。 
其接口如下:

void dma_async_issue_pending(struct dma_chan *chan);
1
6. 其他API接口

dma engine API    說明
int dmaengine_terminate_all(struct dma_chan *chan)    指定DMA通道的所有活動都會被停止,DMA FIFO裏面尚未傳輸完成的數據可能會丟失。未完成的DMA傳輸不會調用任何回調函數
int dmaengine_pause(struct dma_chan *chan)    指定DMA通道的活動將會被暫停,不會造成數據的丟失
int dmaengine_resume(struct dma_chan *chan)    指定DMA通道的活動將會被恢復
enum dma_status dma_async_is_tx_complete(struct dma_chan *chan,dma_cookie_t cookie, dma_cookie_t *last, dma_cookie_t *used)    這個函數用來檢查指定DMA通道的狀態。可在linux/dmaengine.h查看這個API更詳細的用法,這個函數可與dma_async_is_complete()函數一併使用,從dmaengine_submit()函數返回的cookie可用來檢查指定DMA傳輸是否完成。注意:不是所有的DMA engine驅動能在一個正在運行的DMA通道返回準確的信息。因此我們推薦用戶在使用這個API前先暫停或者停止指定DMA通道。
DMA Engine controller編程

和其他的內核框架類似,dmaengine的註冊依賴於填充一個結構並把它註冊到框架中。對於dmaengine,這個結構是dma_device。

1. 分配dma_device結構

2. 初始化dma_device結構 
dma_device結構的成員說明如下:

dma_device結構體成員    說明
channel    使用INIT_LIST_HEAD宏初始化
src_addr_widths    包含支持源傳輸寬度的bitmask
dst_addr_widths    包含支持目的傳輸寬度的bitmask
directions    包含一個支持從設備方向的bitmask
residue_granularity    使用dma_set_residue設置
Descriptor    你的設備不支持任何種類的residue報告。框架僅僅知道特定傳輸描述符完成
Segment    你的設備能報告哪個數據塊完成傳輸
Burst    你的設備能報告哪一次突發傳輸已經完成
dev    保存指向device的指針,這個指針和你當前的驅動實例相關
3. 設置設備支持的傳輸類型 
dma_device結構有一個域cap_mask,這個域保存支持的傳輸類型,可使用dma_cap_set()函數改變,支持的傳輸類型定義在dma_transaction_type中,它位於include/linux/dmaengine.h頭文件中:

DMA傳輸類型    說明
DMA_MEMCPY    內存到內存的拷貝
DMA_SG    設備支持內存到內存的分散/聚合傳輸
DMA_INTERLEAVE    內存到內存的交錯傳輸;交錯傳輸的定義:傳輸數據從一個非連續的buffer到一個非連續的buffer,和DMA_SLAVE相反。通常用在2D內容的傳輸,在那種場景下你想直接傳輸一部分未經壓縮的數據到顯示部分
DMA_XOR    設備能在內存區域執行XOR操作,用來加速對XOR敏感的任務,例如RAID5
DMA_XOR_VAL    使用XOR進行內存Buffer的奇偶校驗
DMA_PQ    內存到內存的P+Q 計算
DMA_PQ_VAL    設備能在內存buffer執行奇偶校驗使用RAID6 P+Q算法
DMA_ INTERRUPT    設備能觸發一個虛擬的傳輸,它將會產生一箇中斷
DMA_SLAVE    設備能處理設備到內存的傳輸,包括分散/聚合傳輸。在mem2mem情況下我們有兩種傳輸類型:一種是單數據塊拷貝,一種是數據塊的集合拷貝。這裏我們僅僅使用單傳輸類型來處理以上兩種情況。如果你想傳輸一個單獨的連續內存buffer,只需要簡單建立一個scatter鏈表,這個鏈表僅有一個項
DMA_CYCLIC    設備能處理循環傳輸;循環傳輸的定義:數據塊集合能循環遍歷它自己,並且最後的項指向第一項。通常用在音頻傳輸,操作一個環形buffer,你需要做的僅僅是往這個Buffer填充音頻數據
DMA_PRIVATE    不通過dma_request_channel函數請求的通道進行異步發送,使用隨機的信道進行傳輸
DMA_ASYNC_TX    不必由設備設置,如何需要將會由框架設置
以上的類型都將會影響源地址和目的地址如何隨着時間如何改變。

4. 設備的操作 
爲了完成實際的邏輯,dma_device結構需要有一些函數指針,現在我們開始討論有哪些操作我們可以實現的。我們必須填充的一些函數基於我們選擇的傳輸類型。

DMA設備操作函數    說明
device_alloc_chan_resources    當client驅動調用dma_request_channel的時候將會調用device_alloc_chan_resources,負責分配通道需要的資源
device_free_chan_resources    當client驅動調用dma_release_channel的時候將會調用device_free_chan_resources,負責釋放通道需要的資源
device_prep_dma_*    爲DMA傳輸準備傳輸描述符
device_issue_pending    從pending queue中取走第一個傳輸描述符並啓動傳輸。當傳輸完成後將會移到列表中的下一個傳輸描述符。這個函數可以在中斷上下文中使用
device_tx_status    報告一個通道還有多少字節數據要傳輸
device_config    使用給定的參數重新配置通道
device_pause    暫停通道的傳輸
device_resume    恢復通道的傳輸
device_terminate_all    停止通道中所有的(包括pending的和正在進行的傳輸)傳輸
5. 註冊DMA設備 
int dma_async_device_register(struct dma_device *device) 
利用這個函數把填充好的dma_device結構實體註冊到內核中。

註冊DMA控制器到DT DMA helpers
int of_dma_controller_register(struct device_node *np,
                struct dma_chan *(*of_dma_xlate)
                (struct of_phandle_args *, struct of_dma *),
                void *data);
 - @np:         device node of DMA controller
 - @of_dma_xlate:   translation function which converts a phandle
 -      arguments list into a dma_chan structure
 - @data        pointer to controller specific data to be used by
 -      translation function
 ———————————————— 
版權聲明:本文爲CSDN博主「were0415」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/were0415/article/details/54095899

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