DMA驱动架构(系统DMA控制器驱动)
今天为大家介绍的内容是dma驱动架构(系统dma控制器驱动)下面跟着小编看看详细内容吧。
DMA的相关操作在《深入理解Linux内核》的545页有介绍。说到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函数一次最多可以申请1个
2.建立流映射。
对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是第一个要释放的页框的线性地址。
本文由91培训发布,不代表东辰网立场,转载联系作者并注明出处:https://www.ktwxcd.com/shzs/314643.html