首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

Linux DMA详解 (4)

Linux DMA详解 (4)

DMA映射

  

一个DMA映射就是分配一个 DMA 缓冲区并为该缓冲区生成一个能够被设备访问的地址的组合操作一般情况下,简单地调用函数virt_to_bus 就设备总线上的地址,但有些硬件映射寄存器也被设置在总线硬件中。映射寄存器(mapping register)是一个类似于外围设备的虚拟内存等价物。在使用这些寄存器的系统上,外围设备有一个相对较小的、专用的地址区段,可以在此区段执行DMA。通过映射寄存器,这些地址被重映射到系统 RAM。映射寄存器具有一些好的特性,包括使分散的页面在设备地址空间看起来是连续的。但不是所有的体系结构都有映射寄存器,特别地,PC 平台没有映射寄存器。

  

在某些情况下,为设备设置有用的地址也意味着需要构造一个反弹(bounce)缓冲区。例如,当驱动程序试图在一个不能被外围设备访问的地址(一个高端内存地址)上执行 DMA 时,反弹缓冲区被创建。然后,按照需要,数据被复制到反弹缓冲区,或者从反弹缓冲区复制。

  

根据 DMA 缓冲区期望保留的时间长短,PCI 代码区分两种类型的 DMA 映射:

  

·
一致 DMA 映射
它们存在于驱动程序的生命周期内。一个被一致映射的缓冲区必须同时可被 CPU 和外围设备访问,这个缓冲区被处理器写时,可立即被设备读取而没有cache效应,反之亦然,使用函数pci_alloc_consistent建立一致映射

  

·
流式 DMA映射
流式DMA映射是为单个操作进行的设置。它映射处理器虚拟空间的一块地址,以致它能被设备访问。应尽可能使用流式映射,而不是一致映射。这是因为在支持一致映射的系统上,每个 DMA 映射会使用总线上一个或多个映射寄存器。具有较长生命周期的一致映射,会独占这些寄存器很长时间――即使它们没有被使用。使用函数dma_map_single建立流式映射。

  

1)建立一致 DMA 映射

  

函数pci_alloc_consistent处理缓冲区的分配和映射,函数分析如下(在include/asm-generic/pci-dma-compat.h中):

  

static inline void *pci_alloc_consistent(struct pci_dev *hwdev,
size_t size, dma_addr_t *dma_handle)
{
        return dma_alloc_coherent(hwdev == NULL ? NULL : &hwdev->dev,
                       size, dma_handle, GFP_ATOMIC);
}

  

结构dma_coherent_mem定义了DMA一致性映射的内存的地址、大小和标识等。结构dma_coherent_mem列出如下(在arch/i386/kernel/pci-dma.c中):

  

struct dma_coherent_mem {

  

void                *virt_base;
        u32                device_base;
        int                size;
        int                flags;
        unsigned long        *bitmap;

  

};

  

函数dma_alloc_coherent分配size字节的区域的一致内存,得到的dma_handle是指向分配的区域的地址指针,这个地址作为区域的物理基地址dma_handle是与总线一样的位宽的无符号整数。函数dma_alloc_coherent分析如下(在arch/i386/kernel/pci-dma.c中):

  void *dma_alloc_coherent(struct device *dev, size_t size,
                           dma_addr_t *dma_handle, int gfp)
{
        void *ret;
//
若是设备,得到设备的dma内存区域,即mem= dev->dma_mem
        struct dma_coherent_mem *mem = dev ? dev->dma_mem : NULL;
        int order = get_order(size);//
size转换成order,即
        //
忽略特定的区域,因而忽略这两个标识
        gfp &= ~(__GFP_DMA | __GFP_HIGHMEM);

        if (mem) {//
设备的DMA映射,mem= dev->dma_mem
//
找到mem对应的页
                int page = bitmap_find_free_region(mem->bitmap, mem->size,
                                                     order);
                if (page >= 0) {
                        *dma_handle = mem->device_base + (page << PAGE_SHIFT);
                        ret = mem->virt_base + (page << PAGE_SHIFT);
                        memset(ret, 0, size);
                        return ret;
                }
                if (mem->flags & DMA_MEMORY_EXCLUSIVE)
                        return NULL;
        }
继承事业,薪火相传
返回列表