1 概要
Dmaengine是linux内核dma驱动框架,针对DMA驱动的混乱局面内核社区提出了一个全新的框架驱动,目标在统一dma API让各个模块使用DMA时不用关心硬件细节,同时代码复用提高,并且实现异步的数据传输,降低机器负载。
1.1 基本结构
dmaengine向其他模块提供接口;virt-dma,Virtual DMA向dmaengine提供初始化函数,传输各阶段状态登记链表,desc_free函数等;dma drivers为具体DMA控制器的驱动代码,其通过dma_async_device_register()注册到dmaengine。
2 代码架构
lli: linked list ltem, the DMA block descriptor
2.1 源码分析
linux-3.4/drivers/dma
dmaengine.c
注册dma类,在调用dma_async_device_register注册具体平台的DMA设备时会用到。
sunxi-dma.c
sunxi_probe解析:
1.1 ret = request_irq(irq, sunxi_dma_interrupt, IRQF_SHARED,dev_name(&pdev->dev), sunxi_dev);//注册中断
1.2 clk_get(&pdev->dev, "dma"); //获取时钟
drivers/clk/sunxi/ clk-sun8iw7.c
SUNXI_CLK_PERIPH (dma,0,0,0,0,0,0,0,0,0,0,BUS_RST0,BUS_GATE0,0,0,6, 6,0,&clk_lock,NULL, 0)
struct periph_init_data sunxi_periphs_init[] = {
{"dma",0,ahb1mod_parents,ARRAY_SIZE(ahb1mod_parents),&sunxi_clk_periph_dma}
}
drivers/clk/sunxi/clk-periph.h 定义SUNXI_CLK_PERIPH宏
sunxi_init_clocks -> sunxi_clk_register_periph -> clk_register(NULL, &periph->hw);
1.3 dma_pool_create, 创建一个一致内存块池,其参数name是DMA池的名字,用于诊断用,参数dev是将做DMA的设备,参数size是DMA池里的块的大小,参数align是块的对齐要求,是2的幂,参数allocation返回没有跨越边界的块数(或0)。
1.4 vchan_init初始化virt_dma_chan
1.5初始化sunxi_dev->dma_dev结构成员后,调用dma_async_device_register(&sunxi_dev->dma_dev)注册到dmaengine
1.5.1 get_dma_id(device) //建立idr机制,分配一个新idr entry,将返回值存储在&sunxi_dev->dma_dev->dev_id中。
1.5.2 在DMA完全准备好之前,已经有客户端在等待channel时,遍历所有的channel,调用dma_chan_get(chan)
1.5.3 list_add_tail_rcu(&device->global_node, &dma_device_list) //将& dma_device. global_node加入静态链表中。
1.5.4 dma_channel_rebalance
1.5.4.1 chan->device->device_alloc_chan_resources(chan); <=> sunxi_alloc_chan_resources //无实际作用,最后返回0
1.5.4.2 balance_ref_count
1.6最后调用sunxi_dma_hw_init(sunxi_dev);使能时钟, -> clk_prepare_enable(sunxi_dev->ahb_clk);
2.2 数据结构关系图
dma_device结构体集成DMA控制器的核心函数,实现最底层的具体操作; 新建立的sunxi_chan对应每个通道,该结构体一面存放用户配置的cfg成员,另外就是包含virt_dma_chan,它用virt-dma提供的函数初始,并将vc->chan.device_node链接到dma_device结构体的channels链表中,后续实际的DMA通道的申请就是申请vc->chan通道,而vc->chan在内核中建立相应的文件节点,即chan->dev