WinCE 的DMA内存操作函数

来源:互联网 发布:广西金岸网络科技集团 编辑:程序博客网 时间:2024/05/22 18:23

HalAllocateCommonBuffer(..)HalFreeCommonBuffer(..)分别用于为DMA申请和释放内存。

1. DMA内存分配函数

PVOID HalAllocateCommonBuffer(PDMA_ADAPTER_OBJECT DmaAdapter, ULONG Length, PPHYSICAL_ADDRESS LogicalAddress, BOOLEAN CacheEnabled)

    DmaAdapter        DMA适配器结构指针

    Length                要分配的内存的大小

    LogicalAddress    分配成功后,内存的物理起始地址

    CacheEnabled    是否使用Cache

 

实际上该函数通过调用AllocPhysMem函数来分配一段物理地址连续的内存,这段内存默认是64KB字节对齐的,这完全可以理解,DMA操作的物理内存必须是连续的。该函数调用成功以后,返回值是虚拟地址,可以在驱动中访问其中的内容,函数的第三个参数返回内存的物理地址,可以赋值给DMA控制器来完成DMA操作。

 

2. DMA内存释放函数:

VOID HalFreeCommonBuffer(PDMA_ADAPTER_OBJECT DmaAdapter, ULONG Length, PHYSICAL_ADDRESS LogicalAddress, PVOID VirtualAddress, BOOLEAN CacheEnabled)

    DmaAdapter        DMA适配器结构指针

    Length                内存的大小

    LogicalAddress    内存的物理起始地址

    VirtualAddress    内存的虚拟地址

    CacheEnabled     是否使用Cache

 

该函数通过调用FreePhysMem函数来完成内存的释放,所以在使用该函数的时候,只有函数的第四个参数是必须的,也就是内存的虚拟地址,其他的都可以忽略。

 

下面给个使用上面两个函数的例子:

  1. DMA_ADAPTER_OBJECT dmaAdapter;
  2. //初始化DMA适配器
  3. dmaAdapter.ObjectSize = sizeof(dmaAdapter);
  4. dmaAdapter.InterfaceType = Internal;
  5. dmaAdapter.BusNumber = 0;
  6. //分配DMA内存
  7. m_pDMABuf = (PBYTE)HalAllocateCommonBuffer( &dmaAdapter, 256 * 1024, &m_pDMABufPhys, FALSE );
  8. //将物理地址赋值给DMA控制器
  9. vm_pDMAreg->DST = (int)m_pDMABufPhys.LowPart;
  10. ...
  11. //释放DMA内存
  12. if( m_pDMABuf != NULL )
  13. {
  14.      HalFreeCommonBuffer( NULL, 0, 0, m_pDMABuffer, FALSE);
  15.      m_pDMABuf = NULL;
  16. }

一般来说,DMA驱动会配合其他设备驱动来完成数据传输,所以很少会被单独作为一个设备来使用,大多数情况我们开发设备驱动时需要用到DMA的时候,会用到上面两个函数来申请和释放内存。

 

补充:AllocPhysMem函数分析

数据区的申请可以使用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函数进行释放。

 

 

 附注:

VirtualAlloc与VirtualFree用法:

           VirtualAlloc(
                LPVOID    lpAddress,
                SIZE_T     dwSize,
                DWORD   flAllocationType,
                DWORD   flProtect
                );
            lpAddress,指定内存开始的地址。
            dwSize,分配内存的大小。
            flAllocationType,分配内存的类型。
            flProtect,访问这块分配内存的权限。

            例:
            // 申请虚拟内存
            void* pMem = ::VirtualAlloc(NULL, 4096,  MEM_RESERVE | MEM_COMMIT,
            PAGE_READWRITE ); 
             // 释放虚拟内存。注意:1)第3个参数一定要用MEM_RELEASE,而不能用MEM_DECOMMIT;2)第二个参数一定要用0)
            ::VirtualFree(pMem, 0, MEM_RELEASE);