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

Linux DMA详解 (5)

Linux DMA详解 (5)

//不是设备的DMA映射
        if (dev == NULL || (dev->coherent_dma_mask < 0xffffffff))
                gfp |= GFP_DMA;
//
分配空闲页
        ret = (void *)__get_free_pages(gfp, order);

        if (ret != NULL) {
                memset(ret, 0, size);//
0
                *dma_handle = virt_to_phys(ret);//
得到物理地址
        }
        return ret;
}

  

当不再需要缓冲区时(通常在模块卸载时),应该调用函数 pci_free_consitent 将它返还给系统

  

2)建立流式 DMA 映射

  

在流式 DMA 映射的操作中,缓冲区传送方向应匹配于映射时给定的方向值。缓冲区被映射后,它就属于设备而不再属于处理器了在缓冲区调用函数pci_unmap_single撤销映射之前,驱动程序不应该触及其内容

  

在缓冲区为 DMA 映射时,内核必须确保缓冲区中所有的数据已经被实际写到内存。可能有些数据还会保留在处理器的高速缓冲存储器中,因此必须显式刷新。在刷新之后,由处理器写入缓冲区的数据对设备来说也许是不可见的。

  

如果欲映射的缓冲区位于设备不能访问的内存区段时,某些体系结构仅仅会操作失败,而其它的体系结构会创建一个反弹缓冲区。反弹缓冲区是被设备访问的独立内存区域,反弹缓冲区复制原始缓冲区的内容。

  

函数pci_map_single映射单个用于传送的缓冲区,返回值是可以传递给设备的总线地址,如果出错的话就为 NULL。一旦传送完成,应该使用函数pci_unmap_single 删除映射。其中,参数direction为传输的方向,取值如下:

  

PCI_DMA_TODEVICE 数据被发送到设备。

  

PCI_DMA_FROMDEVICE如果数据将发送到 CPU

  

PCI_DMA_BIDIRECTIONAL数据进行两个方向的移动。

  

PCI_DMA_NONE 这个符号只是为帮助调试而提供.,

  

函数pci_map_single分析如下(在arch/i386/kernel/pci-dma.c中):

  

static inline dma_addr_t
pci_map_single(struct pci_dev *hwdev,
void *ptr, size_t size, int direction)
{
        return dma_map_single(hwdev == NULL ? NULL : &hwdev->dev, ptr, size,
                                      (enum ma_data_direction)direction);
}

  

函数dma_map_single映射一块处理器虚拟内存,这块虚拟内存能被设备访问,返回内存的物理地址,函数dma_map_single分析如下(在include/asm-i386/dma-mapping.h中):

  

static inline dma_addr_t dma_map_single(struct device *dev, void *ptr,

  

                        size_t size, enum dma_data_direction direction)

  

{
        BUG_ON(direction == DMA_NONE);
//
可能有些数据还会保留在处理器的高速缓冲存储器中,因此必须显式刷新
        flush_write_buffers();
        return virt_to_phys(ptr);//
虚拟地址转化为物理地址

  

}

  

3)分散/集中映射

  

分散/集中映射是流式 DMA 映射的一个特例。它将几个缓冲区集中到一起进行一次映射,并在一个 DMA 操作中传送所有数据。这些分散的缓冲区由分散表结构scatterlist来描述,多个分散的缓冲区的分散表结构组成缓冲区的struct scatterlist数组。

  

分散表结构列出如下(在include/asm-i386/scatterlist.h):

  

struct scatterlist {
    struct page                *page;
    unsigned int        offset;
    dma_addr_t                dma_address;  //
用在分散/集中操作中的缓冲区地址
    unsigned int        length;//
该缓冲区的长度
};

  

每一个缓冲区的地址和长度会被存储在 struct scatterlist 项中,但在不同的体系结构中它们在结构中的位置是不同的。下面的两个宏定义来解决平台移植性问题,这些宏定义应该在一个pci_map_sg 被调用后使用:

  

//从该分散表项中返回总线地址

  

#define sg_dma_address(sg)        ?sg)->dma_address)
//
返回该缓冲区的长度

  

#define sg_dma_len(sg)                ?sg)->length)

继承事业,薪火相传
返回列表