//不是设备的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)
|