Linux驱动技术(三) _DMA编程
2017-02-11 15:15
309 查看
DMA即Direct Memory Access,是一种允许外设直接存取内存数据而没有CPU参与的技术,当外设对于该块内存的读写完成之后,DMAC通过中断通知CPU,这种技术多用于对数据量和数据传输速度都有很高要求的外设控制,比如显示设备等。
比如一个只能访问24位地址的DMA外设,就使用
DMA和Cache一致性
我们知道,为了提高系统运行效率,现代的CPU都采用多级缓存结构,其中就包括使用多级Cache技术来缓存内存中的数据来缓解CPU和内存速度差异问题。在这种前提下,显而易见,如果DMA内存的数据已经被Cache缓存了,而外设又修改了其中的数据,这就会造成Cache数据和内存数据不匹配的问题,即DMA与Cache的一致性问题。为了解决这个问题,最简单的办法就是禁掉对DMA内存的Cache功能,显然,这会导致性能的降低虚拟地址 VS 物理地址 VS 总线地址
在有MMU的计算机中,CPU看到的是虚拟地址,发给MMU后转换成物理地址,虚拟地址再经过相应的电路转换成总线地址,就是外设看到的地址。所以,DMA外设看到的地址其实是总线地址。Linux内核提供了相应的API来实现三种地址间的转换://虚拟->物理 virt_to_phys() //物理->虚拟 ioremap() //虚拟->总线 virt_to_bus() //总线->虚拟 bus_to_virt()
DMA地址掩码
DMA外设并不一定能在所有的内存地址上执行DMA操作,此时应该使用DMA地址掩码int dma_set_mask(struct device *dev,u64 mask);
比如一个只能访问24位地址的DMA外设,就使用
dma_set_mask(dev,0xffffff)
编程流程
下面是在内核程序中使用DMA内存的流程:一致性DMA
如果在驱动中使用DMA缓冲区,可以使用内核提供的已经考虑到一致性的API:/** * request_dma - 申请DMA通道 * On certain platforms, we have to allocate an interrupt as well... */ int request_dma(unsigned int chan, const char *device_id); /** * dma_alloc_coherent - allocate consistent memory for DMA * @dev: valid struct device pointer, or NULL for ISA and EISA-like devices * @size: required memory size * @handle: bus-specific DMA address * * Allocate some memory for a device for performing DMA. This function * allocates pages, and will return the CPU-viewed address, and sets @handle * to be the device-viewed address. */ void * dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle, gfp_t flag) //申请PCI设备的DMA缓冲区 void *pci_alloc_consistent(struct pci_dev *hwdev, size_t size, dma_addr_t *dma_handle) //释放DMA缓冲区 void dma_free_coherent(struct device *dev, size_t size, void *cpu_addr, dma_addr_t dma_handle ) //释放PCI设备的DMA缓冲区 void pci_free_consistent() /** * free_dma - 释放DMA通道 * On certain platforms, we have to free interrupt as well... */ void free_dma(unsigned int chan);
流式DMA
如果使用应用层的缓冲区建立的DMA申请而不是驱动中的缓冲区,可能仅仅使用kmalloc等函数进行申请,那么就需要使用流式DMA缓冲区,此外,还要解决Cache一致性的问题。/** * request_dma - 申请DMA通道 * On certain platforms, we have to allocate an interrupt as well... */ int request_dma(unsigned int chan, const char *device_id); //映射流式DMA dma_addr_t dma_map_single(struct device *dev,void *buf, size_t size, enum dma_datadirection direction); //驱动获得DMA拥有权,通常驱动不该这么做 void dma_sync_single_for_cpu(struct device *dev,dma_addr_t dma_handle_t bus_addr,size_t size, enum dma_data_direction direction); //将DMA拥有权还给设备 void dma_sync_single_for_device(struct device *dev,dma_addr_t dma_handle_t bus_addr,size_t size, enum dma_data_direction direction); //去映射流式DMA dma_addr_t dma_unmap_single(struct device *dev,void *buf, size_t size, enum dma_datadirection direction); /** * free_dma - 释放DMA通道 * On certain platforms, we have to free interrupt as well... */ void free_dma(unsigned int chan);
相关文章推荐
- Linux驱动技术(三) _DMA编程
- socket编程的最简单实例 - linux系统编程及驱动开发 - 小超嵌入式工作室 嵌入式开发学习交流论坛 XC-STC XC2440技术支持 - Powered by Discuz!
- 【嵌入式Linux学习七步曲之第五篇 Linux内核及驱动编程】Linux内核模块LKM的动态加载技术分析
- linux 设备驱动编程
- Linux设备驱动编程之异步通知
- 【嵌入式Linux学习七步曲之第五篇 Linux内核及驱动编程】深入剖析Linux中断机制之目录
- 【嵌入式Linux学习七步曲之第五篇 Linux内核及驱动编程】Linux内核抢占实现机制分析
- 【嵌入式Linux学习七步曲之第五篇 Linux内核及驱动编程】深入剖析Linux中断机制之四--中断API
- Chap 7 学习笔记-事件驱动编程技术和回送
- 【嵌入式Linux学习七步曲之第五篇 Linux内核及驱动编程】Linux信号机制分析
- 【嵌入式Linux学习七步曲之第五篇 Linux内核及驱动编程】中断服务下半部之tasklet详解
- 【嵌入式Linux学习七步曲之第五篇 Linux内核及驱动编程】中断服务下半部之老大-软中断softirq
- linux 设备驱动编程
- 【嵌入式Linux学习七步曲之第五篇 Linux内核及驱动编程】详解Linux内核之双向循环链表
- Linux下面如何进行C语言编程技术教程
- 【嵌入式Linux学习七步曲之第五篇 Linux内核及驱动编程】中断服务下半部之工作队列详解
- Linux设备驱动编程之复杂设备驱动
- Linux下应用程序开发:QT国际化编程 -- 你的Linux技术站,技术支持站
- 【嵌入式Linux学习七步曲之第五篇 Linux内核及驱动编程】全面解析Linux内核的同步与互斥机制--同步篇
- 【嵌入式Linux学习七步曲之第五篇 Linux内核及驱动编程】深入剖析Linux中断机制之二--Linux中断的组织形式