Linux Kernel dmaengine 框架
簡介
Linux內核的dmaengine
框架是一個用于管理DMA(Direct Memory Access)操作的通用框架。它抽象了不同DMA控制器的實現,使得上層代碼可以方便地進行DMA傳輸。
初始化流程
1. 驅動注冊
每個DMA控制器驅動都需要注冊為一個dma_device
,并將其操作接口(如device_alloc_chan_resources
,device_free_chan_resources
等)填充到dma_device
結構中。使用dma_async_device_register
函數將dma_device
注冊到dmaengine
框架中。
struct dma_device *dma_dev = kzalloc(sizeof(*dma_dev), GFP_KERNEL);
dma_dev->dev = &pdev->dev; // Platform device's dev
dma_dev->device_alloc_chan_resources = my_alloc_chan_resources;
dma_dev->device_free_chan_resources = my_free_chan_resources;
dma_dev->device_prep_slave_sg = my_prep_slave_sg;
dma_async_device_register(dma_dev);
2. 通道初始化
DMA控制器通常包含多個DMA通道,每個通道需要注冊到dmaengine
框架。使用dma_async_tx_descriptor
結構描述每個傳輸通道的屬性。使用dma_cookie_t
機制跟蹤傳輸的狀態。
3. DMA過濾器函數
注冊DMA控制器時,可以提供一個過濾器函數,用于選擇合適的DMA通道。這在請求特定類型的DMA通道時特別有用。
使用方法
1. 請求DMA通道
使用dma_request_chan
函數請求一個DMA通道。可以指定一個過濾器函數來選擇通道。
struct dma_chan *chan;
chan = dma_request_chan(dev, "my_dma_channel");
if (IS_ERR(chan)) {// Handle error
}
2. 準備DMA傳輸
使用dmaengine_prep_slave_sg
函數準備DMA傳輸。這個函數會返回一個dma_async_tx_descriptor
結構。
struct dma_async_tx_descriptor *tx;
tx = dmaengine_prep_slave_sg(chan, sg, sg_len, DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
3. 提交傳輸并啟動
使用dmaengine_submit
提交傳輸,并使用dma_async_issue_pending
啟動傳輸。
dma_cookie_t cookie;
cookie = tx->tx_submit(tx);
dma_async_issue_pending(chan);
4. 傳輸完成回調
可以在dma_async_tx_descriptor
中設置回調函數,以便在傳輸完成時通知上層代碼。
tx->callback = my_dma_callback;
tx->callback_param = my_param;
注意事項
1. 錯誤處理
在請求DMA通道、準備傳輸和提交傳輸時都需要處理可能的錯誤。例如,dma_request_chan
返回ERR_PTR
類型的錯誤碼,必須檢查。
2. 緩存一致性
如果DMA傳輸涉及緩存(例如,DMA從內存中讀取數據),需要確保數據在傳輸前后的一致性。通常可以使用dma_map_single
和dma_unmap_single
等函數。
3. 資源釋放
使用完成后,需要釋放DMA通道資源,使用dma_release_channel
釋放請求的通道。
dma_release_channel(chan);
4. 中斷處理
DMA傳輸通常依賴中斷來通知傳輸完成,因此需要確保中斷處理正確配置。
5. 同步操作
DMA操作是異步的,因此在某些情況下需要同步等待傳輸完成,可以使用dma_sync_wait
函數。
dma_sync_wait(chan, cookie);
示例代碼
struct dma_chan *chan;
struct dma_async_tx_descriptor *tx;
dma_cookie_t cookie;
struct scatterlist sg;// 請求DMA通道
chan = dma_request_chan(dev, "my_dma_channel");
if (IS_ERR(chan)) {// 處理錯誤
}// 準備DMA傳輸
tx = dmaengine_prep_slave_sg(chan, &sg, 1, DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
if (!tx) {// 處理錯誤
}// 設置傳輸完成回調
tx->callback = my_dma_callback;
tx->callback_param = my_param;// 提交傳輸
cookie = tx->tx_submit(tx);
if (dma_submit_error(cookie)) {// 處理錯誤
}// 啟動傳輸
dma_async_issue_pending(chan);// 等待傳輸完成
dma_sync_wait(chan, cookie);// 釋放DMA通道
dma_release_channel(chan);