我写这篇文章是因为我对DMA的行为有些怀疑.
我正在阅读PCI布局以及设备驱动程序如何与卡交互,我读到了有关DMA的信息.
由于我的理解,PCI卡没有DMA控制器,而不是他们要求成为总线的主控制器,然后他们能够获取DMA地址并在存储器和设备之间进行传输(通过总线).
这个DMA地址是RAM的一部分,实际上它是一个物理地址,在无所事事之前你需要将它转换成你的驱动程序可以使用的东西,比如内核虚拟内存.
我用这段代码检查过:
/* Virtual kernel address */
kernel_buff = pci_alloc_consistent(dev, PAGE_SIZE, &dma_addr);
pr_info("Kernel buffer - %12p , Dma_addr - %12p\n", kernel_buff, (void *)dma_addr );
pr_info( "Kernelbuffer - dma_addr - %12p\n", kernel_buff - dma_addr);
strcpy(kernel_buff, "Test dma\n");
/* Test memory */
ptest = (void *)dma_addr;
ptest = phys_to_virt((unsigned long)ptest);
pr_info("Ptest virtual memory(%p) containts - %s\n", ptest, (char *)ptest);
输出是:
[425971.835669] Kernel buffer - ffff8800ca70a000 , Dma_addr - ca70a000
[425971.835671] Kernelbuffer - dma_addr - ffff880000000000
[425971.835673] Ptest virtual memory(ffff8800ca70a000) containts - Test dma
这就是我理解DMA是RAM的一部分的方式.
我怀疑这种转移是如何进行的.
我的意思是,每次我在这个缓冲区写入时,缓冲区的数据都会被转移到设备上?或者只是内存位置的地址,然后设备会从这个位置读取?
这是关于DMA的.
关于I / O内存映射:
当我们请求设备的I / O内存区域时,例如:
pci_resource_start
我们要求设备寄存器所在的存储区域?
那么这样我们将这个内存位置放入RAM中?我们将写/读作为正常的内存位置.
最后一点是,我们使用DMA,因为I / O内存映射每个周期只允许几个字节,因为这个过程涉及CPU,对吗?
因此,我们可以在没有cpu的情况下在内存位置(RAM和设备总线)之间传输大量数据.
解决方法:
将数据传输到设备所涉及的步骤可归纳如下:
1.假设您将数据放在缓冲区中.
2.驱动程序为此缓冲区创建DMA映射(例如使用pci_alloc_coherent()),并返回相应的DMA总线地址.
3.将该DMA总线地址通知给设备.这是通过writel()写入器件的正确DMA寄存器来完成的(假设器件寄存器是存储器映射的).
4.还需要通知设备正在传输的数据量等(通过使用writel()写入设备的相应寄存器)
4.现在向设备发出命令,通过写入其中一个控制寄存器(再次使用writel())来启动DMA事务.
5.数据事务完成后,设备发出中断.
6.在中断处理程序中,驱动程序可以取消分配用于事务的缓冲区,也可以执行DMA取消映射.
你有它..数据被传输到设备!
现在来讨论有关IO内存映射的问题:
首先,当我们调用pci_resource_start()时,我们不会“请求”IO端口.这是我们收集信息的方式.关于港口.请求使用pci_request_regions()完成.具体到您的问题:
我们要求设备寄存器所在的存储区域?
使用它,我们要求内核访问设备寄存器所在的内存区域(内存映射端口).
那么这样我们将这个内存位置放入RAM中?
不,我们在RAM中没有这个内存位置,它只是内存映射,这意味着设备与RAM共享相同的地址,数据和控制线,因此,用于访问RAM的相同指令也可以是用于访问设备寄存器.
你自己回答了你的上一个问题. DMA为要有效传输的数据提供了大量资金.但是,有些情况下您需要使用内存映射来传输数据.在DMA事务处理的说明中已经说明了最好的例子,您需要将地址和控制信息传输到设备.这只能通过内存映射IO来完成.
希望这可以帮助.