免责声明
嗯,这是一个很好的架构问题,它暗示了一些关于您的硬件和所需用户空间界面的假设。因此,让我跳入结论进行更改,并尝试猜测哪种解决方案最适合您的情况。
设计
考虑到您没有提到write() 操作,我将进一步假设您的硬件一直在生成新数据。如果是这样,您提到的设计可能正是让您感到困惑的地方:
read 调用非常简单。它开始 DMA 写入,然后在等待队列中等待。
这正是阻止您以常规、常用(并且可能是您想要的)方式使用驱动程序的原因。让我们开箱即用,首先提出所需的用户界面(您希望如何从用户空间使用驱动程序)。下一种情况在这里常用且足够(从我的角度来看):
-
poll()您的设备文件等待新数据到达
-
read()您的设备文件获取到达数据
现在您可以看到数据请求(对 DMA)应该由read() 操作启动而不是。正确的解决方案是在驱动程序中连续读取数据(没有任何来自用户空间的触发)并将其存储在内部,当用户向驱动程序询问数据以 consume 时(通过read() 操作) -- 为用户提供内部存储的数据。如果驱动内部没有数据存储——用户可以使用poll()操作等待新数据到达。
如您所见,这是众所周知的producer-consumer problem。您可以使用circular buffer 将来自硬件的数据存储在驱动程序中(因此当缓冲区已满时,您会故意丢失旧数据以防止缓冲区溢出 情况)。因此生产者 (DMA) 写入该 RX 环形缓冲区的 head,而消费者(用户从用户空间执行 read())从该 RX 的 tail 读取环形缓冲区。
代码参考
这一切都让我想起了串行控制台 [1, 2] 驱动程序。因此,请考虑在您的驱动程序实现中使用Serial API(如果您的设备实际上是 串行控制台)。例如参见drivers/tty/serial/atmel_serial.c 驱动程序。我对 UART API 不是很熟悉,所以我不能准确地告诉你那里发生了什么,但乍一看并不太难,所以你可以从该代码中找出一两件事您的驱动程序设计。
如果您的驱动程序不应该使用串行 API,您可以使用下一个驱动程序作为参考:
互补
在评论中回答您的问题:
您是否建议read 在没有可用数据且read 应阻止时调用poll?
首先,你要决定,是否要提供:
让我们假设(为了争论)你想在你的驱动程序中提供这两个选项。在这种情况下,如果flags 参数包含O_NONBLOCK 标志,您应该检查open() 调用。来自man 2 open:
O_NONBLOCK 或 O_NDELAY
尽可能以非阻塞模式打开文件。 open() 和返回的文件描述符上的任何后续操作都不会导致调用进程等待。有关 FIFO(命名管道)的处理,另请参阅 fifo(7)。有关O_NONBLOCK 与强制文件锁定和文件租约结合的影响的讨论,请参阅fcntl(2)。
现在,当您知道用户选择的模式时,您可以执行下一步(在您的驱动程序中):
- 如果
open() 中的flags 不包含此类标志,则可以阻塞read()(即如果数据不可用,则等待DMA 事务完成,然后返回新数据)。
- 但是如果在
open() 标志中有O_NONBLOCK 并且在循环缓冲区中没有可用的数据- 你应该从read() 调用返回EWOULDBLOCK 错误代码。
来自man 2 read:
EAGAIN 或 EWOULDBLOCK
文件描述符fd 引用了一个套接字并被标记为非阻塞(O_NONBLOCK),读取会阻塞。 POSIX.1-2001 允许在这种情况下返回任一错误,并且不要求这些常量具有相同的值,因此可移植应用程序应检查这两种可能性。
您可能还想阅读下一篇文章以更好地掌握相应的接口:
[1]Serial Programming Guide for POSIX Operating Systems
[2]Serial Programming HOWTO
补充2
我需要某种后台任务来不断地从设备中读取数据并填充环形缓冲区。 poll 现在是微不足道的 - 只需检查该缓冲区中是否有任何内容,但 read 更困难,因为它可能需要等待某些内容发布到环形缓冲区。
例如查看drivers/char/virtio_console.c 驱动程序实现。
- 在poll()函数中:做
poll_wait()(等待新数据到达)
- 在receive data interrupt handler:做
wake_up_interruptible()(唤醒poll和read操作)
- 在read()函数中:
- 如果端口has no data:
- 如果设置了
O_NONBLOCK 标志(在open() 操作中):立即返回-EAGAIN = -EWOULDBLOCK
- 否则我们会阻塞读取:执行
wait_event_freezable() 以等待新数据到达
- 如果端口有数据:返回data from buffer
另请参阅相关问题:How to add poll function to the kernel module code?。