我正在写一个内核模块,它将触发和外部PCIe设备从我的内部存储器读取数据块 . 为此,我需要向PCIe设备发送指向我想要发送的数据的物理内存地址的指针 . 最终,这些数据将使用 write()
函数(用户空间)和 copy_from_user()
(内核空间)从用户空间写入内核 . 据我了解,我的内核模块将看到的地址仍然是虚拟内存地址 . 我需要一种方法来获取它的物理地址,以便PCIe设备可以找到它 .
1)我可以从用户空间使用 mmap()
并将我的数据放在DDR内存中的已知位置,而不是使用 copy_from_user()
吗?我不想意外地在内存中覆盖另一个进程数据 .
2)我的内核模块在初始化时使用 ioremap_nocache()
保留PCIe数据空间,我是否可以从我的内核模块执行相同的操作,或者将此内存视为io内存是一个坏主意?如果可以的话,如果我试图保留的内存已经被使用会发生什么?我不想硬编码静态内存位置,然后发现它正在使用中 .
在此先感谢您的帮助 .
1 回答
您没有选择内存位置并将数据放在那里 . 相反,您要求内核告诉您数据在物理内存中的位置,并告诉电路板读取该位置 . 每页内存(4KB)将位于不同的物理位置,因此如果您发送的数据多于此数据,则您的设备可能支持“分散聚集”DMA,因此它可以读取内存中不同位置的一系列页面 .
API是这样的:
dma_map_page()
返回类型为dma_addr_t
的值,您可以将其提供给电路板 . 转移完成后dma_unmap_page()
. 如果您将该值放入您提供给电路板的描述符列表中 . 如果支持scatter-gather,dma_map_sg()
和朋友将帮助将大缓冲区映射到一组页面 . 您仍有责任以设备所需的格式设置页面描述符 .这些都在Linux设备驱动程序(第15章)中写得很好,这是必读的 . http://lwn.net/images/pdf/LDD3/ch15.pdf . 一些API在书写之后发生了变化,但概念保持不变 .
最后,
mmap()
:当然,你可以分配一个内核缓冲区,mmap()
将它填充到用户空间并填充它,然后dma_map缓冲区以便传输到设备 . 事实上,这可能是避免copy_from_user()
的最简洁方法 .