DMA驱动开发(2,资料参考)

1、 芯片DMA的使用要点:

AK3224芯片的DMA使用中,RAM的地址作为DMA传输的目标地址、源地址,必须要4字节对齐。而且DMA的操作长度以内的RAM地址,必须连续。

不过在使用中发现:Nandflash驱动中RAM地址作为目标地址时,只需要2字节对齐。RAM地址作为源地址可不需对齐。(其他情况需要逐一验证)

2、 wince中的DMA使用:

根据DMA一次操作的RAM地址必须连续的特性,在驱动DMA使用时,我们需要确保虚拟地址映射的物理地址是连续的。有3个途径:

1:数据区地址是由应用层或者其他进程、线程传入的,驱动并不知道其虚拟地址对应的物理地址是否一直连续。

由于wince的内存申请,是以4K字节为一个页,一段数据的内存申请可能跨越多个页。因此,只要数据区长度大于1字节,就有可能其物理地址是跨越的、不连续的。为了确保DMA操作,我们必须查询这段数据区在RAM上的物理分布。

首先,得到数据区所在的虚拟页

VirPageStart = (ULONG)pSourceBuffer & 0xFFFFF000;

其次,得到数据区在页内的偏移地址

offset = (ULONG)pSourceBuffer & 0x0FFF;

计算数据区是否跨越页段

if(offset + NumberOfBytes > 4096)

PageSize = WCE_UNIFORM_SIZE - offset; //整个数据跨越此页,则DMA传输需要分多个部分,一次一个页段的传

else

PageSize = NumberOfBytes; //数据区没有跨越页

由得到的页地址,查询映射的物理地址。

if(!LockPages((LPVOID)VirPageStart, 4096, &TransAddr, LOCKFLAG_READ))

{

//异常处理

}

UnlockPages((LPVOID)VirPageStart, 4096);

得到了映射的物理地址TransAddr后,根据RAM是目标地址还是源地址,做进一步的处理。

假设一个数据区作为DMA源地址,大小为9K。在虚拟地址首页的偏移为4K。那么它必然跨越3个页段。

如图,我们的DMA操作也要分解3次。

首先查询第一页的物理地址发送,第一个页的2K数据。然后查询第二页的物理地址,发送4K数据。最后查询第三页的物理地址,发送3K数据。

2:数据区的申请可以使用AllocPhysMem函数申请。

LPVOID AllocPhysMem( 
  DWORD cbSize,                 参数1:数据区大小
  DWORD fdwProtect,             参数2:保护标记
  DWORD dwAlignmentMask,         参数3:0(default system)
  DWORD dwFlags,                参数4:0(Reserved for future use)
  PULONG pPhysicalAddress         参数5:得到数据区对应的物理地址

);

AllocPhysMem函数返回值为指向申请后的虚拟地址指针。

如:

pSerialHead->RxBufferInfo.RxCharBuffer = //alloc physical memory

AllocPhysMem(pSerialHead->RxBufferInfo.Length + 16, PAGE_READWRITE, 0, 0, &RX_PhyAddr);

由于此函数必定申请到一片连续的物理地址,因此pSerialHead->RxBufferInfo.RxCharBuffer的使用不再需要查询是否跨越多个页段。

但是,AllocPhysMem函数申请的物理地址可能会跨越多个RAM CHIP。因此,在使用1片以上RAM芯片的系统中,依然需要查询是否跨越CHIP

AllocPhysMem函数使用后,需要使用FreePhysMem函数进行释放。

3:数据区可以在系统config.bib文件中,预先定义好一片连续、不跨越CHIP的RAM空间。

如下,系统保留了虚拟地址0x80024000开始,大小为0x3000的一段RAM。

SER_DMA 80024000 00003000 RESERVED

那么驱动DMA使用中,不再需要对这段内存,进行任何的查询动作。我们只需要在进程空间中做映射即可。

pSerialHead->RxBufferInfo.RxCharBuffer = VirtualAlloc(0, RX_PhySize,

MEM_RESERVE, PAGE_NOACCESS);

if (pSerialHead->RxBufferInfo.RxCharBuffer == NULL)

{

DEBUGMSG(ZONE_ERROR, (TEXT("COM_Init:: VirtualAlloc failed!\r\n")));

return(NULL);

}

else

{

if (!VirtualCopy((PVOID)pSerialHead->RxBufferInfo.RxCharBuffer, (PVOID)(RX_PhyAddr),

RX_PhySize, (PAGE_READWRITE | PAGE_NOCACHE)))

{

DEBUGMSG(ZONE_ERROR, (TEXT("COM_Init:: VirtualCopy failed!\r\n")));

return(NULL);

}

}

上面这段程序中,先使用函数VirtualAlloc,在进程空间中申请一段保留的虚拟地址空间。然后使用VirtualCopy,把需要使用的物理地址空间,映射到已经申请好的虚拟地址上。使用完毕,必须使用函数VirtualFree进行释放。

posted @ 2011-08-18 21:07  keeppoised  阅读(456)  评论(0编辑  收藏  举报