Linux 內核 DMA 引擎框架採用 Platform Device/Driver 模式,將 DMA 控制器抽象為平台設備,通過 dmaengine 子系統統一管理通道分配、傳輸提交、中斷處理。本文以 Samsung S5P 系列 SoC 集成的 ARM PL330 DMA 控制器驅動(s3c-pl330.c)為例,剖析平台層數據結構、資源管理、通道綁定與傳輸流程。
s3c_pl330_dmacstruct s3c_pl330_dmac {
struct pl330_info *pi; // PL330 核心驅動上下文
unsigned busy_chan; // 忙碌通道計數
struct list_head node; // 全局 DMAC 鏈表節點
enum dma_ch *peri; // 支持的外設 ID 列表
struct kmem_cache *kmcache; // 傳輸描述符 slab 緩存
};
s3c_pl330_chanstruct s3c_pl330_chan {
enum dma_ch id; // 外設 ID(請求線)
struct list_head node; // 全局通道鏈表
struct pl330_req *lrq; // 最後提交的請求
struct list_head xfer_list; // 排隊傳輸鏈表
struct s3c_pl330_xfer *xfer_head; // 隊首傳輸
struct s3c_pl330_dmac *dmac; // 所屬 DMAC(NULL=空閒)
void *pl330_chan_id; // PL330 硬件線程 ID
struct pl330_req req[2]; // 雙缓衝請求(雙緩衝)
struct s3c2410_dma_client *client; // 老版客戶端接口兼容
s3c2410_dma_cbfn_t callback_fn; // 完成回調
unsigned int options; // 通道選項
struct pl330_reqcfg rqcfg; // 請求配置
unsigned long sdaddr; // 設備地址(s3c2410_dma_devconfig)
};
s3c_pl330_xferstruct s3c_pl330_xfer {
struct list_head node; // 掛載於通道 xfer_list
struct s3c_pl330_chan *chan; // 反向指針
void *token; // 客戶端標識
struct pl330_xfer px; // PL330 核心傳輸描述符
};
static LIST_HEAD(dmac_list); // 所有 DMAC
static LIST_HEAD(chan_list); // 所有通道
static DEFINE_SPINLOCK(res_lock); // 並發保護鎖
// 根據外設 ID 查找通道
static struct s3c_pl330_chan *id_to_chan(const enum dma_ch id) {
struct s3c_pl330_chan *ch;
list_for_each_entry(ch, &chan_list, node)
if (ch->id == id) return ch;
return NULL;
}
// 動態添加通道(平台初始化調用)
static void chan_add(const enum dma_ch id) {
if (id_to_chan(id)) return; // 已存在
struct s3c_pl330_chan *ch = kmalloc(sizeof(*ch), GFP_KERNEL);
if (!ch) return;
ch->id = id;
ch->dmac = NULL; // 標記空閒
list_add_tail(&ch->node, &chan_list);
}
// 判斷通道是否空閒
static bool chan_free(struct s3c_pl330_chan *ch) {
return ch && !ch->dmac; // dmac 為 NULL 表示未被獲取
}
機器初始化代碼(arch/arm/mach-s5pv210/dma.c)調用 platform_add_devices() 註冊多個 DMAC 設備:
static struct platform_device *s5pc110_dmacs[] __initdata = {
&s5pc11x_device_mdma, // 內存到內存 DMAC
&s5pc11x_device_pdma0, // 外設 DMAC 0
&s5pc11x_device_pdma1, // 外設 DMAC 1
};
static int __init s5pv210_dma_init(void) {
platform_add_devices(s5pc110_dmacs, ARRAY_SIZE(s5pc110_dmacs));
return 0;
}
arch_initcall(s5pv210_dma_init);
每個 platform_device 對應一個 DMAC 實例,probe 階段驅動創建 s3c_pl330_dmac、掃描硬件通道、調用 chan_add() 填充全局通道池。
為兼容舊版 S3C2410/6410 DMA API,驅動實現 s3c2410_dma_client 回調接口:
s3c2410_dma_request():請求通道 → 內部調用 dmaengine 申請。s3c2410_dma_enqueue():提交傳輸 → 構建 s3c_pl330_xfer 入隊。s3c2410_dma_ctrl():START/STOP/PAUSE/FLUSH → 映射到 dmaengine 控制命令。s3c2410_dma_devconfig():配置設備地址/傳輸方向 → 填充 sdaddr、rqcfg。dmaengine 提供統一 API 給上層驅動。chan->dmac 指針)。kmcache 專用於 s3c_pl330_xfer 分配,減少內存碎片與分配延遲。res_lock 確保多核下通道查找/添加/刪除原子性。req[2] 支持鏈式傳輸,硬件處理第一個請求時 CPU 準備下一個,實現零拷貝流水線。