【引用】DMA内存申请--dma_alloc_coherent
来源:互联网 发布:cosplay相机软件 编辑:程序博客网 时间:2024/06/01 07:27
在项目驱动过程中会经常用到dma传输数据,而dma需要的内存有自己的特点,一般认为需要物理地址连续,并且内存是不可cache的,在linux内核中提供一个供dma所需内存的申请函数dma_alloc_coheren. 如下所述:
dma_alloc_coherent()
dma_alloc_coherent() -- 获取物理页,并将该物理页的总线地址保存于dma_handle,返回该物理页的虚拟地址
void *
dma_alloc_coherent(struct device *dev, size_t size,
dma_addr_t *dma_handle, gfp_t gfp)
{
void *ret;
if (!dev || *dev->dma_mask >= 0xffffffffUL)
gfp &= ~GFP_DMA;
ret = (void *)__get_free_pages(gfp, get_order(size)); //(1)
if (ret) {
memset(ret, 0, size);
*dma_handle = virt_to_bus(ret); //(2)
}
return ret;
}
(1) 将size转换成order, 即2^order
(2) 将虚拟地址ret转换成总线地址
这个函数是一个平台相关的函数,以上是在x86平台的实现细节,从这里我们可以看到该函数返回值为linux 内核线性地址,所以对于驱动开发过程的mmap函数实现提供了便利。
但是在powerpc平台却不是这样,笔者就曾经遇到在将pci驱动从x86平台移植到powerpc平台时出现问题。
首先我们来先看一下两个平台对于dma内存的处理。
x86:
linux内存区域分为DMA区域,Normal内存区域与高端内存区域,高端内存区域为当物理内存高于768M时使用,一般DMA区域为16M,这段空间由操作系统预留。DMA区域与Normal区域全部使用线性映射,采用逻辑地址使用,高端内存使用内核虚拟地址。其中内核空间的分部为:
物理区--8M隔离--vmalloc区--8k隔离--4M的高端映射区--固定映射区--128k
powerpc:
本节采用freescale的mpc5121芯片为例,内核没有采用Normal内存区域,只使用ZONE_DMA和ZONE_HIMEM两种类型的空间,其中ZONE_DMA存放低端内存, ZONE_HIMEN存放高端内存,整个内存不在采用逻辑地址这一概念。所以基于逻辑地址的操作没有可移植性。
下面看下具体的区别:
void * __dma_alloc_coherent(size_t size, dma_addr_t *handle, gfp_t gfp)
{
//物理空间页的申请
page = alloc_pages(gfp, order);
//对物理空间进行清零cache
{
unsigned long kaddr = (unsigned long)page_address(page);
memset(page_address(page), 0, size);
flush_dcache_range(kaddr, kaddr + size);
}
//申请虚拟空间
c = vm_region_alloc(&consistent_head, size, gfp &
~(__GFP_DMA | __GFP_HIGHMEM));
//实现虚拟地址与物理地址映射
if (c) {
unsigned long vaddr = c->vm_start;
pte_t *pte = consistent_pte + CONSISTENT_OFFSET(vaddr);
struct page *end = page + (1 << order);
split_page(page, order);
/*
* Set the \"dma handle\"
*/
*handle = page_to_bus(page);
do {
BUG_ON(!pte_none(*pte));
SetPageReserved(page);
set_pte_at(&init_mm, vaddr,
pte, mk_pte(page, pgprot_noncached(PAGE_KERNEL)));
page++;
pte++;
vaddr += PAGE_SIZE;
} while (size -= PAGE_SIZE);
// 返回值为内核虚拟地址。
return (void *)c->vm_start
dma_alloc_coherent()
dma_alloc_coherent() -- 获取物理页,并将该物理页的总线地址保存于dma_handle,返回该物理页的虚拟地址
void *
dma_alloc_coherent(struct device *dev, size_t size,
dma_addr_t *dma_handle, gfp_t gfp)
{
void *ret;
if (!dev || *dev->dma_mask >= 0xffffffffUL)
gfp &= ~GFP_DMA;
ret = (void *)__get_free_pages(gfp, get_order(size)); //(1)
if (ret) {
memset(ret, 0, size);
*dma_handle = virt_to_bus(ret); //(2)
}
return ret;
}
(1) 将size转换成order, 即2^order
(2) 将虚拟地址ret转换成总线地址
这个函数是一个平台相关的函数,以上是在x86平台的实现细节,从这里我们可以看到该函数返回值为linux 内核线性地址,所以对于驱动开发过程的mmap函数实现提供了便利。
但是在powerpc平台却不是这样,笔者就曾经遇到在将pci驱动从x86平台移植到powerpc平台时出现问题。
首先我们来先看一下两个平台对于dma内存的处理。
x86:
linux内存区域分为DMA区域,Normal内存区域与高端内存区域,高端内存区域为当物理内存高于768M时使用,一般DMA区域为16M,这段空间由操作系统预留。DMA区域与Normal区域全部使用线性映射,采用逻辑地址使用,高端内存使用内核虚拟地址。其中内核空间的分部为:
物理区--8M隔离--vmalloc区--8k隔离--4M的高端映射区--固定映射区--128k
powerpc:
本节采用freescale的mpc5121芯片为例,内核没有采用Normal内存区域,只使用ZONE_DMA和ZONE_HIMEM两种类型的空间,其中ZONE_DMA存放低端内存, ZONE_HIMEN存放高端内存,整个内存不在采用逻辑地址这一概念。所以基于逻辑地址的操作没有可移植性。
下面看下具体的区别:
void * __dma_alloc_coherent(size_t size, dma_addr_t *handle, gfp_t gfp)
{
//物理空间页的申请
page = alloc_pages(gfp, order);
//对物理空间进行清零cache
{
unsigned long kaddr = (unsigned long)page_address(page);
memset(page_address(page), 0, size);
flush_dcache_range(kaddr, kaddr + size);
}
//申请虚拟空间
c = vm_region_alloc(&consistent_head, size, gfp &
~(__GFP_DMA | __GFP_HIGHMEM));
//实现虚拟地址与物理地址映射
if (c) {
unsigned long vaddr = c->vm_start;
pte_t *pte = consistent_pte + CONSISTENT_OFFSET(vaddr);
struct page *end = page + (1 << order);
split_page(page, order);
/*
* Set the \"dma handle\"
*/
*handle = page_to_bus(page);
do {
BUG_ON(!pte_none(*pte));
SetPageReserved(page);
set_pte_at(&init_mm, vaddr,
pte, mk_pte(page, pgprot_noncached(PAGE_KERNEL)));
page++;
pte++;
vaddr += PAGE_SIZE;
} while (size -= PAGE_SIZE);
// 返回值为内核虚拟地址。
return (void *)c->vm_start
- 【引用】DMA内存申请--dma_alloc_coherent
- 【study】DMA内存申请--dma_alloc_coherent 及 寄存器 与 内存
- 怎样让 dma_alloc_coherent() 可以申请更大的内存
- dma_alloc_coherent (建立一致性 DMA 映射函数)
- 内核使用硬件ip的dma,dma_alloc_coherent 与 dma_alloc_writecombine
- 内核使用硬件ip的dma,dma_alloc_coherent 与 dma_alloc_writecombine
- 内核使用硬件ip的dma,dma_alloc_coherent 与 dma_alloc_writecombine
- dma_alloc_coherent() 函数开辟 DMA 连续内存空间失败 解决办法
- 大块数据申请及DMA
- 申请和释放DMA缓冲区
- 申请内存
- DMA直接内存访问
- 直接内存访问(DMA)
- 内存映射与DMA
- 直接内存访问(DMA)
- 内存映射和DMA
- 直接内存访问--DMA
- 内存映射和DMA
- U-Boot在MPC8265平台上的移植与分析【转】
- 移植uboot到powerpc(1)--配置头文件,u-boot,ppc ,mpc85 mpc83
- 移植uboot到powerpc(2)--start.s跟踪,u-boot,ppc ,mpc85 mpc83 【转】
- 【引用】专家详解:内存工作原理及发展历程[1]
- 直接内存访问(DMA)【转】
- 【引用】DMA内存申请--dma_alloc_coherent
- dma_addr_t与流式映射和一致性映射
- IO端口和IO内存
- linux中的 IO端口映射和IO内存映射【转】
- IORESOURCE_IO和IORESOURCE_MEM
- linux设备模型之pci设备的I/O和内存(一)
- linux设备模型之pci设备的I/O和内存(二)
- linux高端内存和低端内存
- Linux内存管理(上)