Rav*_*pta 11 c linux linux-device-driver linux-kernel
我正在linux-2.6.26上编写设备驱动程序.我希望将dma缓冲区映射到用户空间,以便将数据从驱动程序发送到用户空间应用程序.请提供一些很好的教程.
谢谢
这是我用过的,简而言之......
get_user_pages固定用户页面并为您提供一系列struct page *指针.
dma_map_page在每个struct page *上获取页面的DMA地址(也称为"I/O地址").这也会创建一个IOMMU映射(如果您的平台需要).
现在告诉您的设备使用这些DMA地址将DMA执行到内存中.显然它们可能是不连续的; 内存只能保证在页面大小的倍数中是连续的.
dma_sync_single_for_cpu做任何必要的缓存刷新或弹跳缓冲区blitting或其他什么.此调用可确保CPU实际上可以看到DMA的结果,因为在许多系统上,修改CPU后面的物理RAM会导致过时的缓存.
dma_unmap_page 释放IOMMU映射(如果您的平台需要它).
put_page 取消固定用户页面.
请注意,您必须在此处检查错误,因为整个地方的资源都很有限. get_user_pages返回一个完整错误的负数(-errno),但它可以返回一个正数,告诉你它实际设置了多少页面(物理内存不是无限的).如果这比您要求的要少,您仍然必须遍历它所做的所有页面以便调用put_page它们.(否则你正在泄漏内核内存;非常糟糕.)
dma_map_page 也可以返回错误(-errno),因为IOMMU映射是另一个有限的资源.
dma_unmap_page和put_page返回void,像往常一样对Linux的"释放"的功能.(Linux内核资源管理例程只返回错误,因为实际上出了问题,不是因为你搞砸了并传递了一个坏指针或什么.基本的假设是你永远不会搞砸,因为这是内核代码.虽然get_user_pages确实检查以确保用户地址的有效性,如果用户向您发送错误的指针,将返回错误.)
如果您想要一个友好的界面来分散/收集,您还可以考虑使用_sg函数.然后你会打电话dma_map_sg代替dma_map_page,dma_sync_sg_for_cpu而不是dma_sync_single_for_cpu等.
另请注意,这些功能中的许多功能可能在您的平台上或多或少都是无操作,因此您可以经常摆脱草率.(特别是,dma_sync _...和dma_unmap _...在我的x86_64系统上什么都不做.)但是在这些平台上,调用本身都被编译成了任何东西,所以没有任何借口可以邋.
小智 8
好的,这就是我做的.
免责声明:我是一个纯粹意义上的黑客,我的代码不是最漂亮的.
我阅读了LDD3和infiniband源代码以及其他前任的东西,并决定get_user_pages并固定它们以及所有其他的装备是太痛苦而不能在宿醉时思考.此外,我正在与PCIe总线上的另一个人合作,我还负责"设计"用户空间应用程序.
我编写了驱动程序,以便在加载时通过调用函数myAddr[i] = pci_alloc_consistent(blah,size,&pci_addr[i])直到失败来预先分配尽可能多的缓冲区.(失败- > myAddr[i]是NULL我想,我忘了).我能够分配大约2.5GB的缓冲区,每个4MiB的大小在我的微型机器中,只有4GiB的内存.缓冲区总数取决于加载内核模块的时间.在引导时加载驱动程序并分配大多数缓冲区.在我的系统中,每个缓冲区的大小最大为4MiB.不知道为什么.我cat特德/proc/buddyinfo,以确保我没有做任何愚蠢的事,这当然是我一贯的起步模式.
然后驱动程序继续向pci_addrPCIe设备提供阵列及其大小.然后司机坐在那里等待中断风暴开始.同时在用户空间中,应用程序打开驱动程序,查询分配的缓冲区数量(n)及其大小(使用ioctls或reads等),然后继续mmap()多次(n)次调用系统调用.当然mmap()必须在驱动程序中正确实现,LDD3页面422-423都很方便.
用户空间现在有n个指向驱动程序内存区域的指针.当驱动程序被PCIe设备中断时,它会被告知哪些缓冲区"已满"或"可用"被吸干.该应用程序依次待定read()或被ioctl()告知哪些缓冲区充满了有用的数据.
棘手的部分是管理用户空间到内核空间同步,这样PCIe进入DMA的缓冲区也不会被用户空间修改,但这就是我们付出的代价.我希望这是有道理的,我很高兴被告知我是个白痴,但请告诉我原因.
顺便推荐这本书:http://www.amazon.com/Linux-Programming-Interface-System-Handbook/dp/1593272200.七年前,当我写第一个Linux驱动程序时,我希望我有那本书.
通过添加更多内存并且不让内核使用它并mmap在用户空间/内核空间划分的两侧进行ping操作,可以实现另一种类型的技巧,但PCI设备还必须支持高于32位的DMA寻址.我没有尝试,但如果我最终被迫,我不会感到惊讶.
好吧,如果您有LDD,您可以查看第15章,更准确地说是第435页,其中描述了直接I/O操作.
内核调用将帮助您实现这一目标get_user_pages.在您的情况下,因为您要将数据从内核发送到用户空间,所以应将write标志设置为1.
还要注意,异步I/O可能允许您实现相同的结果,但是您的用户空间应用程序不必等待读取完成,这可能会更好.