最近在研究一如何获得连续的物理地址用于DMA,看了很多博客,说是通过kmalloc,get_free_pages等等获取内存空间再通过mmap,ioremap等即可使用,但测试了很多代码,写到DMA寄存器进行DMA操作都没成功。最后发现,其实将获得的物理地址对应的虚拟地址转化成总线地址就可以进行DMA,基于DMA的硬件使用总线地址而非物理地址。
操作环境:Linux ubuntu 4.10.0-42-generic
具体操作如下:
1.网上能够搜到有很多分配一块连续物理地址的接口函数,http://blog.youkuaiyun.com/luckywang1103/article/details/48831433,这个博主写的很详细,各种分配方式,但是在虚拟机上跑的话就直接挂掉了。但是可以参照任何一种分配内存的方式,进行连续物理地址的获取。我采取的是get_free_pages,原型如下:
unsigned long __get_free_pages(unsigned int gfp_mask, unsigned intorder)——Allocates 2order number of pages and returns avirtual address。
gfp_mask没必要写GFP_DMA,填GFP_KERNEL即可,order是要分配的页的个数,取2的次方计算,ubantu支持最大order为10,也就是2的10次方个4K的pages,总共4M的物理内存,可以连续申请低于896M的内核物理空间。
2.get_free_pages返回值是虚拟地址,可以地址里面初始化一些值,再通过函数virt_to_bus转成总线地址,这个地址可以直接给硬件做DMA使用,至于写值是为了用DMA方式读看看是否正确,最后加几行读这个虚拟地址的代码,看进行了DMA写操作写进去的值是否正确。当然也可以通过mmap将对应的物理地址映射到用户虚拟地址进行使用。具体代码和流程以及效果图将后续贴出。