【问题标题】:How to implement a Linux Device Driver for Data Acquisition Hardware?如何为数据采集硬件实现 Linux 设备驱动程序?
【发布时间】:2013-03-25 09:39:02
【问题描述】:

我正在开发一种需要 DMA 操作才能将大数据帧传输到主存储器的采集设备。现在我假设目标是一个连续的内存区域,所以我在引导时分配它(比如 1 MB),如ldd2-ch13 的“自己动手分配”部分所述。然后驱动程序可以通过调用 ioremap() 访问该区域。

当前系统的工作方式如下:

  1. 内存映射控制寄存器使用户空间应用程序能够启动/停止设备

  2. 启动后,设备开始以约 8MB/s 的速度连续(循环)将采集的数据传输到分配的内存区域。

  3. 保留的内存区域大小为两帧,以便使用double-buffering technique
  4. 传输完整帧后,设备会触发中断。

我开发了一个简单的 char 驱动程序,它提供了一个阻塞的 read() 函数,这样用户空间就可以在每次收到中断时读取一个新的数据帧。

在运行了一些测试之后,我意识到用户空间应用程序在运行以下代码时会丢失一些帧:

    for(i=0;i<NUM_FRAMES;i++) {

       read(dev_d,buf,FRAME_SIZE);/*Read frame*/

       for(j=0;j<FRAME_SIZE;j++) /*File dump*/
          fprintf(fp,"%d",buf[j]);

        fprintf(fp,"\n");
    }

我怀疑应用程序的进程在两次后续读取之间处于休眠状态,从而允许设备重写应该已经读取的内存位置。

由于我没有内核开发经验,我想知道如何为此类设备实现驱动程序以确保同步的正确方法。基本上我正在尝试为实时采集设备实现一个简单的共享内存通信,我需要保证操作系统能够读取所有采集的数据帧。

【问题讨论】:

  • 您写道数据速率约为 8MB/s,但帧大小和帧速率是多少?您的read() 速率必须与该帧速率一样快。您的“文件转储”循环效率不高。这是一个经典的实时问题:您必须以比到达速度更快的速度处理数据,或者缓冲该数据。网络接口增加了对 N 缓冲区或环的双缓冲。也许您还必须使用多线程,即始终有一个待处理的read() 请求的专用输入线程。
  • 您能否详细说明splice() 解决方案?这是让驱动程序直接写入特定文件而无需任何用户空间干预的某种方式吗?
  • 是的,完全正确。如果你想传输到一个文件,那么你打开你的acquisition设备和一个文件。您可以控制读取/写入的数据量,因此如果您知道块大小,则可以插入/注释自己的框架。没有从内核到用户到内核内存的复制。数据将直接从驱动程序传输到文件。文件可以是串口等其他设备
  • " 我认为问题在于用户空间进程可能已进入睡眠状态" -- 是的,这是一个问题,因为您有 513 个系统调用执行 I /O 在每个 read() 之间。您所犯的“新手错误” 并未意识到执行低效 I/O 所消耗的周期。尝试fprintf()ing 仅前 4 个字节,看看是否仍有跳帧。然后评估需要进行什么样的实际处理,以及每帧可能需要多长时间。 在担心实施细节之前,请确保您有一个可行的顶层方案或全局。
  • fprintf() 仅用于调试,还是您个人将实时查看这些数据?为什么不能将这些帧中的每一帧写成一个块 512 字节的原始二进制数据,而不是将每个字节 实时转换为十进制?这就是我所说的“大图”:如果您要保存帧,只需将其保存为原始文件,然后将其转换为离线或低优先级的后台进程。 “你知道吗……”——我不得不承认我对splice()一无所知。 "读一帧..." -- 这是你的驱动程序;您告诉我们如何/何时满足读取请求!

标签: c linux linux-kernel operating-system linux-device-driver


【解决方案1】:

您正在阅读一本非常古老的书。这是本书最新版本的链接(它只是很旧):Linux Device Driver 3 - Memory Mapping。您还可以阅读内核文档中的DMA-API

做一种同步阅读Time, Delays and Deferred Work章节。您可以使用 waitqueue。您在 read() 上等待,并在新帧可用时 *wake_up*。

关于你的代码,仅仅理解你的问题是不够的。但是,如果您认为需要休眠/等待,您可以在驱动程序中实现 poll file_operation 并在用户空间中使用 select() 来询问是否有要读取的内容。

【讨论】:

  • 阻塞 read() 函数等待中断,然后将帧复制到用户空间缓冲区。但是,由于我丢失了一些帧,即:我读取了 frame1,frame2 , frame4... ,我想知道如何在不停止设备写入 DMA 区域的情况下同步通信。
  • 我的 read() 函数已经使用 wait_event_interruptible(wq,flag>0) 来等待中断,其中“wq”是一个等待队列,“flag”是一个原子变量,它在中断处理函数。我认为问题在于,有时用户空间程序在 for() 循环内的后续读取之间花费了过多的时间,并且由于设备永远不会停止写入内存,因此永远不会读取某些帧。一种可能的解决方案是增加保留内存区域的长度,但我想知道在这种情况下是否有更好的方法来确保同步。
  • 显然,如果你的消费者进程(用户空间程序)比你的生产者(驱动程序)慢,你迟早会丢失一些数据(这取决于缓冲区长度)。即使您有一个不断从内核缓冲区传输到用户空间缓冲区的进程,您也会遇到问题(可能永远不会发生,因为我们在用户空间中有 GB 的内存)。这不是同步问题,如果你将程序与驱动程序同步你会产生1帧,消耗1帧,但是如果驱动程序不断产生你无法服务的中断,无论如何都会丢帧
  • 当然可以,但是我已经检查过,用户空间应用程序可以跟上设备的数据速率。我可以增加缓冲区的数量并可能解决问题,我只是想知道我的驱动程序设计是否正确,或者我是否犯了一些新手错误。
  • 根据我掌握的信息,是的。但是代码比人说话更好:) 所以,展示你的代码:D
猜你喜欢
  • 2021-10-14
  • 1970-01-01
  • 2015-10-30
  • 2021-07-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-01
相关资源
最近更新 更多