DMA即DirectMemoryAccess,是一种无需CPU参与,让外设直接访问内存数据的技术。外设读写内存块后,DMAC通过中断通知CPU。该技术多用于对数据量和数据传输速度有较高要求的外设控制,如显示设备。DMA和CacheConsistency我们知道,现代CPU为了提高系统运行效率,采用了多级缓存结构,其中包括使用多级Cache技术将数据缓存在内存中,以缓解CPU和内存速度差异的问题。在这个前提下,很明显,如果DMA内存中的数据已经被Cache缓存了,而外围设备修改了其中的数据,这就会造成Cache数据和内存数据不匹配,即DMA和Cache问题之间的一致性。为了解决这个问题,最简单的方法是禁用DMA内存的Cache功能。显然,这会导致性能下降。虚拟地址VS物理地址VS总线地址在有MMU的计算机中,CPU看到的是虚拟地址,送到MMU后转换成物理地址,虚拟地址通过对应电路,也就是外设看到的地址。因此,DMA外设看到的地址实际上就是总线地址。Linux内核提供了相应的API来实现这三个地址之间的转换)DMA地址掩码DMA外设可能无法对所有内存地址执行DMA操作。在这种情况下,应使用DMA地址掩码intdma_set_mask(structdevice*dev,u64mask);例如一个只能访问24位地址的DMA外设,使用dma_set_mask(dev,0xffffff)编程过程下面是在内核程序中使用DMA内存的过程:ConsistentDMA如果在驱动中使用DMAbuffer,你可以使用内核提供的考虑了一致性的API:/***request_dma-申请DMA通道*Onceplatforms,wehavetoallocateaninterruptaswell...*/intrequest_dma(unsignedintchan,constchar*device_id);/***dma_alloc_coherent-分配一致的内存对于DMA*@dev:validstructdevicepointer,orNULLforISAandEISA-likesized:required*@sized*handle:bus-specificDMAaddress**AllocatesomememoryforadeviceforperformingDMA.Thisfunction*allocatespages,andwillreturntheCPU-viewedaddress,andsets@handle*tobethedevice-viewedaddress.*/void*dma_alloc_coherent(structdevice*dev,size_tsize,PCI_addr_t*gma_handle)/fl_handlebuffervoid*pci_alloc_consistent(structpci_dev*hwdev,size_tsize,dma_addr_t*dma_handle)//释放DMAbuffervoiddma_free_coherent(structdevice*dev,size_tsize,void*cpu_addr,dma_addr_tdma_handle)//释放PCI设备的DMAbuffervoidpci_free_consistent()/***free_dma-releasetheDMAchannel*Onceplatforms,wehavetofreeinterruptaswell...*/voidfree_dma(unsignedintchan);如果streamingDMA使用的是应用层的buffer创建的DMA应用,而不是driver中的buffer,可能只能使用kmalloc等函数来申请,那么就需要使用streamingDMAbuffer。另外,还解决了Cache一致性问题/***request_dma-ApplyforDMAchannel*Onceplatforms,wehavetoallocateaninterruptaswell...*/intrequest_dma(unsignedintchan,constchar*device_id);//MapstreamingDMAdma_addr_tdma_map_single(structdevice*dev,void*buf,size_tsize,enumdma_datadirectiondirection);//driver获得DMA拥有权,通常驱动不该这么做voiddma_sync_single_for_cpu(structdevice*dev,dma_addr_tdma_handle_tbus_addr,size_tsize,enumdma_data_directiondirection);//将DMA拥有权还给设备voiddma_sync_single_for_device(structdevice*dev,dma_addr_tdma_handle_tbus_addr,size_tsize,enumdma_data_directiondirection);//去映射StreamingDMAdma_addr_tdma_unmap_single(structdevice*dev,void*buf,size_tsize,enumdma_datadirectiondirection);/***free_dma-releaseDMAchannel*Onceplatforms,wehavetofreeinterruptaswell...*/voidfree_dma(unsignedintchan);
