Linux下DMA驱动如何实现

  • 格式:doc
  • 大小:23.00 KB
  • 文档页数:4

Linux下DMA驱动如何实现
在《深入理解Linux内核》中的第545页介绍了DMA
的相关操作。说道DMA,那就不得不提到Cache(高速缓存)
的问题。书中引用了如下一段例子来描述了Cache一致性问
题:“假设设备驱动程序把一些数据填充到内存缓冲区中,
然后立刻命令硬件设备利用DMA传送方式读取该数据。如
果DMA访问这些物理RAM内存单元,而相应的硬件高速
缓存行的内容还没有写入RAM中,那么硬件设备所读取的
至就是内存缓冲区中的旧值。”现在有两种方法来处理DMA
缓冲区:一致性DMA映射:书上讲的比较抽象,通俗地所
就是任何对DMA缓冲区的改写都会直接更新到内存中,也
称之为“同步的”或者“一致的”。流式DMA映射:根据个
人的理解,这里的流即输入输出流,我们需要事先指定DMA
缓冲区的方向,比如是”读缓冲区”还是“写缓冲区”。也
称之为“异步的”或“非一致性的”,详细的内容请看下文。
由于x86体系结构中,硬件设备驱动程序本身会“窥探”所
访问的硬件告诉缓存,因此x86体系结构中不存在DMA一
致性问题。而对于其他一些架构如MIPS,SPARC以及
POWERPC(包括ARM在内)需要在软件上保证其DMA一
致性。
对于以上两者如何选择,书中有一个合适的建议,如果CPU
和DMA处理器以不可预知的方式去访问一个缓冲区,那么
必须强制使用一致性DMA映射方式(这里我对不可预知的
理解是,不能确定在何时它们访问缓冲区),其他情形下,
流式DMA映射方式更可取,因为在一些体系结构中处理一
致性DMA映射是很麻烦的,并且可能导致更低的系统性能。
这里详细介绍流式DMA:需要访问的缓冲区需要在数据传
送之前被映射(这里的映射也就是需要调用一些函数告知内
核,该缓冲区进行流式映射),在传送之后被取消映射。
启动一次流式DMA数据传输分为如下步骤:1. 分配DMA
缓冲区。在DMA设备不采用S/G(分散/聚集)模式的情况
下,必须保证缓冲区是物理上连续的,linux内核有两个函数
用来分配连续的内存:kmalloc()和__get_free_pages()。这两
个函数都有分配连续内存的最大值,kmalloc以分配字节为
单位,最大约为64KB,__get_free_pages()以分配页为单位,
最大能分配2^order数目的页,order参数的最大值由
include/linux/Mmzone.h文件中的MAX_ORDER宏决定(在
默认的2.6.18内核版本中,该宏定义为10。也就是说在理论
上__get_free_pages函数一次最多能申请12. 建立流式映射。
在对DMA冲区进行读写访问之后,且在启动DMA设备传
输之前,启用dma_map_single()函数建立流式DMA映射,
这两个函数接受缓冲区的线性地址作为其参数并返回相应
的总线地址。3. 释放流式映射。当DMA传输结束之后我们
需要释放该映射,这时调用dma_unmap_single()函数。注意:
(1). 为了避免高速缓存一致性问题,驱动程序在开始从RAM
到设备的DMA数据传输之前,如果有必要,应该调用
dma_sync_single_for_device()函数刷新与DMA缓冲区对应
的高速缓存行。(2). 从设备到RAM的一次DMA数据传送
完成之前设备驱动程序是不可以访问内存缓冲区的,但如果
有必要的话,驱动程序在读缓冲区之前,应该调用
dma_sync_single_for_cpu()函数使相应的硬件高速缓存行无
效。(3). 虽然kmalloc底层也是用__get_free_pages实现的,
不过kmalloc对应的释放缓冲区函数为kfree,而
__get_free_pages对应的释放缓冲区函数为free_pages。具体
与__get_free_pages有关系的几个申请与释放函数如下:申请
函数:alloc_pages(gfp_mask,order)返回第一个所分配页框描
述符的地址,或者如果分配失败则返回NULL。
__get_free_pages(gfp_mask,order)类似于alloc_pages(),但它
返回第一个所分配页的线性地址。如果需要获得线性地址对
应的页框号,那么需要调用virt_to_page(addr)宏产生线性地
址。释放函数:__free_pages(page,order)这里主要强调page
是要释放缓冲区的线性首地址所在的页框号
free_pages(page,order)这个函数类似于
__free_pages(page,order),但是它接收的参数为要释放的第一
个页框的线性地址addr