繁体   English   中英

数据采集​​设备的Linux驱动程序和API架构

[英]Linux Driver and API architecture for a data acquisition device

我们正在尝试为自定义数据获取设备编写驱动程序/ API,该设备可捕获多个数据“通道”。 为了便于讨论,我们假设这是一个多通道视频捕获设备。 该设备通过8xPCIe Gen-1链路连接到系统,其理论吞吐量为16Gbps。 我们的实际数据速率约为2.8Gbps(〜350MB /秒)。

由于数据速率要求,我们认为我们必须对驱动程序/ API体系结构保持谨慎。 我们已经实现了基于描述符的DMA机制和相关的驱动程序。 例如,我们可以从设备启动一个256KB的DMA事务,该事务成功完成。 但是,在此实现中,我们仅在内核驱动程序中捕获数据,然后将其删除,并且根本不将数据流传输到用户空间。 本质上,这只是一个小的DMA测试实现。

我们认为我们必须将问题分为三个部分:1.内核驱动程序2.用户空间API 3.用户代码

采集设备在PCIe地址空间中有一个寄存器,该寄存器指示是否有数据要从该设备的任何通道读取。 因此,我们的内核驱动程序必须轮询此位向量。 当内核驱动程序看到该位置1时,它将启动DMA事务。 但是,在准备好整个数据块之前,用户应用程序不需要了解所有这些DMA事务和数据(例如,假定该设备每个事务为我们提供16行视频数据,但是我们需要通知仅当整个视频帧就绪时才可以使用)。 我们只需要将整个帧传输到用户应用程序。

这是我们的首次尝试:

  1. 我们的用户端API允许用户应用程序为“通道”注册函数回调。
  2. 用户端API具有“启动”功能,可由用户应用程序调用,该应用程序使用ioctl向内核驱动程序发送启动消息。
  3. 在内核驱动程序中,接收到启动消息后,我们启动了一个内核线程,该线程连续监视“数据就绪”位向量,并在看到新数据时将其复制到驱动程序分配的(kmalloc)缓冲区中。 它会一直这样做,直到收集到的数据大小达到“帧大小”为止。
  4. 此时,将自定义linux SIGNAL(类似于SIGINT,SIGHUP等)发送到运行驱动程序的进程。 我们的API会捕获此信号,然后回调相应的用户回调函数。
  5. 用户回调函数调用API中的函数(transfer_data),该函数使用ioctl调用将用户空间缓冲区地址发送到内核,内核通过将通道帧数据复制到用户空间来完成数据传输。

上面的所有方法都可以正常工作,只是性能很差。 我们只能达到大约2MB /秒的传输速率。 我们需要完全重写它,我们对任何建议或示例指针都持开放态度。

其他说明:

  • 不幸的是,我们无法更改硬件设备中的任何内容。 因此,我们必须轮询“数据就绪”位并基于该位启动DMA。

  • 有人建议参考Infiniband驱动程序作为参考,但是我们完全不了解该代码。

您现在可能已经过去了,但如果没有,这是我的2分。

  1. 很难相信您的卡在传输数据后不会产生中断。 它具有DMA引擎,并且可以处理“描述符”,这大概是分散聚集列表的元素。 我假设它会产生PCIe“中断”。 YMMV。
  2. 不要为现有的类似驱动程序而拖曳内核。 您可能会很幸运,但我怀疑不是。

您需要写一个阻塞读取,您将为其提供大的内存缓冲区。 读取操作op(a)的驱动程序获取用户缓冲区的用户页面列表,并将其锁定在内存中( get_user_pages ); (b)使用pci_map_sg创建一个分散列表; (c)遍历列表( for_each_sg ); (d)对于每个条目,将相应的物理总线地址和数据长度写入DMA控制器,就像我假设您所说的“描述符”一样。

该卡现在具有一个描述符列表,这些描述符与大型用户缓冲区的物理总线地址相对应。 当数据到达卡时,它将数据直接写到用户空间,用户缓冲区中,而用户级别的读取仍被阻止。 完成描述符列表后,该卡必须能够中断,否则就没用了。 驱动程序响应该中断并取消阻止您的用户级读取。

就是这样。 当然,这些细节是令人讨厌的,并且文档记录很少,但这应该是基本的体系结构。 如果您确实没有中断,则可以在内核中设置一个计时器以轮询传输是否完成,但是如果它确实是定制卡,则应该退还您的钱。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM