Linux驱动技术(三) _DMA编程【转】
转自:https://www.cnblogs.com/xiaojiang1025/archive/2017/02/11/6389194.html
DMA即Direct Memory Access,是一种允许外设直接存取内存数据而没有CPU参与的技术,当外设对于该块内存的读写完成之后,DMAC通过中断通知CPU,这种技术多用于对数据量和数据传输速度都有很高要求的外设控制,比如显示设备等。
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);
最新文章
- python笔记7:日期和时间
- CYQ.Data 数据层框架 CYQ.Data 数据框架 使用篇四 MAction 增删改
- Windows Azure Virtual Machine 之用程序控制Azure VM
- frame里的链接跳转
- liunx打开指定端口
- [LeetCode] Search Insert Position
- Selenium测试规划
- 编写类String的构造函数、析构函数和赋值函数
- JavaWeb高性能开发(一)
- mysql max_allowed_packet错误
- 【模板】Big-Step-Giant-Step 大步小步
- hdu 畅通工程
- linode开通Paypal付款方式
- 用Open SSH生成公钥和私钥(Win)
- freeSSHD在windows环境下搭建SFTP服务器
- PhpStorm代码编辑区竖线的用途以及如何去掉
- Selenium爬取电影网页写成csv文件
- IntelliJ IDEA 常用插件
- Tomcat(三):tomcat处理连接的详细过程
- node基础—http模块
热门文章
- 自学Linux Shell14.2-在脚本中使用其他文件描述符
- loj #116. 有源汇有上下界最大流
- [转载]C++的顺序点(sequence point)和副作用(side effect)
- 【Maven】安装配置、目录结构、配置文件、常见命令
- ev的offsetX,pageX,clientX和screenX
- 继承之es5对比es6
- Java语法总结--Java集合类
- python---ORM之SQLAlchemy(2)外键使用
- golang struct tag
- Linux 命令详解(八)Systemd 入门教程:实战篇