Linux驱动技术(三) _DMA编程

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来实现三种地址间的转换:


  1. //虚拟->物理 
  2. virt_to_phys() 
  3. //物理->虚拟 
  4. ioremap() 
  5. //虚拟->总线 
  6. virt_to_bus() 
  7. //总线->虚拟 
  8. bus_to_virt()  

DMA地址掩码

DMA外设并不一定能在所有的内存地址上执行DMA操作,此时应该使用DMA地址掩码


  1. int dma_set_mask(struct device *dev,u64 mask); 

比如一个只能访问24位地址的DMA外设,就使用dma_set_mask(dev,0xffffff)

编程流程

下面是在内核程序中使用DMA内存的流程:  

一致性DMA

如果在驱动中使用DMA缓冲区,可以使用内核提供的已经考虑到一致性的API:


  1. /** 
  2.  * request_dma - 申请DMA通道 
  3.  * On certain platforms, we have to allocate an interrupt as well... 
  4.  */int request_dma(unsigned int chan, const char *device_id);/** 
  5.  * dma_alloc_coherent - allocate consistent memory for DMA 
  6.  * @dev: valid struct device pointer, or NULL for ISA and EISA-like devices 
  7.  * @size: required memory size 
  8.  * @handle: bus-specific DMA address * 
  9.  * Allocate some memory for a device for performing DMA.  This function 
  10.  * allocates pages, and will return the CPU-viewed address, and sets @handle 
  11.  * to be the device-viewed address. 
  12.  */ 
  13.  
  14. void * dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle, gfp_t flag)//申请PCI设备的DMA缓冲区 
  15. void *pci_alloc_consistent(struct pci_dev *hwdev, size_t size, dma_addr_t *dma_handle)//释放DMA缓冲区 
  16. void dma_free_coherent(struct device *dev, size_t size, void *cpu_addr, dma_addr_t dma_handle )//释放PCI设备的DMA缓冲区 
  17. void pci_free_consistent()/** 
  18.  * free_dma - 释放DMA通道 
  19.  * On certain platforms, we have to free interrupt as well... 
  20.  */ 
  21. void free_dma(unsigned int chan);  

流式DMA

如果使用应用层的缓冲区建立的DMA申请而不是驱动中的缓冲区,可能仅仅使用kmalloc等函数进行申请,那么就需要使用流式DMA缓冲区,此外,还要解决Cache一致性的问题。


  1. /** 
  2.  * request_dma - 申请DMA通道 
  3.  * On certain platforms, we have to allocate an interrupt as well... 
  4.  */ 
  5.  
  6. int request_dma(unsigned int chan, const char *device_id);//映射流式 
  7. DMAdma_addr_t dma_map_single(struct device *dev,void *buf, size_t size, enum dma_datadirection direction);//驱动获得DMA拥有权,通常驱动不该这么做 
  8.  
  9. 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拥有权还给设备 
  10.  
  11. 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);//去映射流式 
  12.  
  13. DMAdma_addr_t dma_unmap_single(struct device *dev,void *buf, size_t size, enum dma_datadirection direction); 
  14. /** 
  15.  * free_dma - 释放DMA通道 
  16.  * On certain platforms, we have to free interrupt as well... 
  17.  */ 
  18.  
  19. void free_dma(unsigned int chan);   

本文作者:佚名

来源:51CTO

时间: 2025-01-21 07:57:54

Linux驱动技术(三) _DMA编程的相关文章

Linux驱动技术(八) _并发控制技术

为了实现对临界资源的有效管理,应用层的程序有原子变量,条件变量,信号量来控制并发,同样的问题也存在与驱动开发中,比如一个驱动同时被多个应用层程序调用,此时驱动中的全局变量会同时属于多个应用层进程的进程空间,这种情况下也要使用一些技术来实现对并发的控制.本文将讨论内核中下述并发控制技术的技术特点和应用场景. 1.中断屏蔽 2.原子操作  a.原子变量操作  b.原子位操作 3.自旋锁  a.传统自旋锁  b.读写自旋锁  c.顺序锁  d.RCU 4.信号量  a.传统信号量  b.读写信号量  

Linux驱动技术(五) _设备阻塞/非阻塞读写

等待队列是内核中实现进程调度的一个十分重要的数据结构,其任务是维护一个链表,链表中每一个节点都是一个PCB(进程控制块),内核会将PCB挂在等待队列中的所有进程都调度为睡眠状态,直到某个唤醒的条件发生.应用层的阻塞IO与非阻塞IO的使用我已经在Linux I/O多路复用一文中讨论过了,本文主要讨论驱动中怎么实现对设备IO的阻塞与非阻塞读写.显然,实现这种与阻塞相关的机制要用到等待队列机制.本文的内核源码使用的是3.14.0版本 设备阻塞IO的实现 当我们读写设备文件的IO时,最终会回调驱动中相应

Linux驱动技术(七) _内核定时器与延迟工作

内核定时器 软件上的定时器最终要依靠硬件时钟来实现,简单的说,内核会在时钟中断发生后检测各个注册到内核的定时器是否到期,如果到期,就回调相应的注册函数,将其作为中断底半部来执行.实际上,时钟中断处理程序会触发TIMER_SOFTIRQ软中断,运行当前处理器上到期的所有定时器. 设备驱动程序如要获得时间信息以及需要定时服务,都可以使用内核定时器. jiffies 要说内核定时器,首先就得说说内核中关于时间的一个重要的概念:jiffies变量,作为内核时钟的基础,jiffies每隔一个固定的时间就会

Linux驱动技术(二) _访问I/O内存

ARM是对内存空间和IO空间统一编址的,所以,通过读写SFR来控制硬件也就变成了通过读写相应的SFR地址来控制硬件.这部分地址也被称为I/O内存.x86中对I/O地址和内存地址是分开编址的,这样的IO地址被称为I/O端口.本文只讨论IO内存的访问. IO内存访问流程 我们知道,为了管理最重要的系统资源并让物理地址对进程透明,Linux使用了内存映射机制,就是一个进程如果想访问一个物理内存地址(eg.SFR地址),那么首先就是将其映射成虚拟地址.   IO内存申请/归还 Linux提供一组函数用于

《UNIX/Linux 系统管理技术手册(第四版)》——2.4 Perl编程

2.4 Perl编程 UNIX/Linux 系统管理技术手册(第四版) Larry Wall发明了Perl语言,它第一种真正伟大的脚本编程语言.它的能耐要比bash大得多,而且编写良好的Perl代码也相当容易阅读.另一方面,Perl没有给开发人员强加太多的风格规范,所以不考虑可读性的Perl代码显得很神秘.Perl也被诟病为只适合写(不适合读)的语言. 我们在这里介绍Perl 5,这个版本成为标准已经有10年了.Perl 6是一个仍处在开发之中的主要版本.参考perl6.org了解详情. 对于系

linux编程-关于Linux的三个编程题,想了半天毫无头绪,感觉Linux编程好复杂。求大家帮助帮助我,谢谢。

问题描述 关于Linux的三个编程题,想了半天毫无头绪,感觉Linux编程好复杂.求大家帮助帮助我,谢谢. 1:子进程每隔一秒向文件写入信息,父进程每隔三秒读出子进程所写的信息并输出到屏幕. 2:模拟shell,设计一个交互式命令处理程序,注意对命令参数和环境参数的处理. 3:编写一个守护进程,实现功能为:每隔一秒,向当前目录下的hello文件里写入一行helloworld. 解决方案 Linux设备驱动编程之复杂设备驱动25岁了,是学linux运维还是编程好呢?求指点下 .. 解决方案二: 楼

《UNIX/Linux 系统管理技术手册(第四版)》——2.2 bash脚本编程

2.2 bash脚本编程 UNIX/Linux 系统管理技术手册(第四版) bash特别适合编写简单的脚本,用来自动执行那些以往在命令行输入的操作.在命令行用的技巧也能用在bash的脚本里,反之亦然,这让用户在bash上投入的学习时间获得了最大的回报.不过,一旦bash脚本超过了100行,或者需要的特性bash没有,那么就要换到Perl或者Python上了. bash脚本的注释以一个井号(#)开头,并且注释一直延续到行尾.和命令行中一样,可以把逻辑上的一行分成多个物理上的多行来写,每行末尾用反斜

《UNIX/Linux 系统管理技术手册(第四版)》——2.6 脚本编程的最佳实践

2.6 脚本编程的最佳实践 UNIX/Linux 系统管理技术手册(第四版) 虽然本章里的代码片段几乎不带注释,而且很少打印用法说明,只是因为我们已经列出了每个例子的大纲,从而体现出若干关键点.实际的脚本应该有更好的表现.有几本书通篇就讲编码的最佳实践,不过其中的基本指导原则如下. 如果运行脚本时带了不合适的参数,脚本应该打印一则用法说明,然后再退出.更好的做法是,也以这样的方式实现--help选项. 验证输入的有效性,并检查获得的输入值.例如,在对算出来的一个路径执行rm -fr操作之前,可能

《UNIX/Linux 系统管理技术手册(第四版)》——2.5 Python脚本编程

2.5 Python脚本编程 UNIX/Linux 系统管理技术手册(第四版) 随着项目变得越来越大.越来越复杂,面向对象的设计和实现所带来的好处,也就变得越来越清楚.Perl错过了大概5年时间,没有提供OO特性,虽然它后来又拼命去追赶,但Perl版的面向对象编程仍然显得有点儿牵强. 本节介绍Python 2.Python 3尚在开发之中,可能在本书没过时之前就能发布.但是和Perl 6不一样的是,它看上去更像是一种增量更新. 有着很强OO背景的工程师通常会喜欢Python和Ruby,这两种脚本