Linux--UIO机制

UIO(用户空间I/O)是一种在Linux中实现设备驱动的方法,尤其适用于无需内核资源的简单I/O设备。UIO允许驱动程序主要在用户空间运行,降低内核模块出错的风险。本文介绍了UIO的工作原理,包括设备文件、中断处理和内存映射。此外,还详细阐述了如何编写内核模块和用户空间驱动,以及通用PCI和Hyper-V UIO驱动的使用。
摘要由CSDN通过智能技术生成

kernel解释

前言

对于许多类型的设备,创建 Linux 内核驱动程序是大材小用。真正需要的只是某种处理中断并提供对设备内存空间的访问的方法。控制设备的逻辑不一定必须在内核中,因为设备不需要利用内核提供的任何其他资源。像这样的一类常见设备是用于工业I / O卡。

为了解决这种情况,设计了用户空间 I/O 系统 (UIO)。对于典型的工业I/O卡,只需要一个非常小的内核模块。驱动程序的主要部分将在用户空间中运行。这简化了开发,并降低了内核模块中出现严重错误的风险。

请注意,UIO 不是通用驱动程序接口。已经由其他内核子系统(如网络或串行或 USB)处理良好的设备不是 UIO 驱动程序的候选项。非常适合 UIO 驱动程序的硬件满足以下条件:

  • 设备具有可映射的内存。通过写入此内存可以完全控制设备。
  • 设备通常会生成中断。
  • 设备不适合标准内核子系统之一。

UIO 的工作原理

每个 UIO 设备都可通过一个设备文件和多个 sysfs 属性文件进行访问。第一台设备的设备文件叫/dev/uio0,并依此类推/dev/uio1/,dev/uio2

/dev/uioX用于访问卡的地址空间。只需用于访问卡的寄存器或RAM位置即可。mmap()

中断是通过读取/dev/uioX 来处理的。一旦发生中断,阻塞read()将立即返回。还可以在/dev/uioX上使用 select()来等待中断。从/dev/uioX中读取的整数值表示总中断计数。您可以使用此数字来确定是否错过了一些中断

对于某些在内部具有多个中断源但没有单独的 IRQ 掩码和状态寄存器的硬件,如果内核处理程序通过写入芯片的 IRQ 寄存器来禁用中断源,则用户空间可能无法确定中断源是什么。在这种情况下,内核必须完全禁用IRQ,以使芯片的寄存器保持不变。从而,用户空间部分可以确定中断的原因,但它无法重新启用中断

为了解决这些问题,UIO 还实现了一个 write() 函数。它通常不被使用,对于只有一个中断源或具有单独的 IRQ 掩码和状态寄存器的硬件,可以忽略它。但是,如果需要,则写入/dev/uioX操作将调用驱动程序实现的函数irqcontrol()。您必须写入一个通常为 0 或 1 的 32 位值才能禁用或启用中断。如果驱动程序未实现 irqcontrol(),write(),将返回 -ENOSYS。

为了正确处理中断,自定义内核模块可以提供自己的中断处理程序。它将自动由内置处理程序调用。

对于不生成中断但需要轮询的设备,可以设置一个计时器,以可配置的时间间隔触发中断处理程序。此中断模拟是通过从计时器的事件处理程序调用uio_event_notify()来完成的。

每个驱动程序都提供用于读取或写入变量的属性。这些属性可通过 sysfs 文件访问。自定义内核驱动程序模块可以将其自己的属性添加到 uio 驱动程序拥有的设备,但此时不会添加到 UIO 设备本身。如果发现有用,将来可能会更改。

UIO 框架提供以下标准属性:

  • name:设备的名称。建议为此使用内核模块的名称。
  • version:由驱动程序定义的版本字符串。这允许驱动程序的用户空间部分处理不同版本的内核模块
  • event:自上次读取设备节点以来驱动程序处理的中断总数。

这些属性显示在目录/sys/class/uio/uioX下。请注意,此目录可能是符号链接,而不是真正的目录。任何访问它的用户空间代码都必须能够处理这个问题。

每个 UIO 设备都可以使一个或多个内存区域可用于内存映射。这是必需的,因为某些工业 I/O 卡需要访问驱动程序中的多个 PCI 内存区域。

每个映射在 sysfs 中都有自己的目录,第一个映射显示为/sys/class/uio/uioX/maps/map0 。后续映射创建目录/map1、/map2/ 等。仅当映射的大小不为 0 时,才会显示这些目录。

每个mapX/目录包含四个只读文件,这些文件显示内存的属性:

  • name:此映射的字符串标识符。这是可选的,字符串可以为空。驱动程序可以对此进行设置,以便用户空间更容易找到正确的映射。
  • addr:可以映射的内存地址。
  • size:addr 所指向的内存的大小(以字节为单位)。
  • offset:必须添加到返回的指针以到达实际设备内存的偏移量(以字节为单位)。如果设备的内存未与页面对齐,这一点很重要。请记住,mmap()返回的指针始终与页面对齐,因此始终添加此偏移量是mmap()很好的样式

从用户空间中,通过调整调用的参数来区分不同的映射。要映射映射 N 的内存,必须使用 N 倍的页面大小作为偏移量:offsetmmap()

offset = N * getpagesize();
有时,有些硬件具有类似内存的区域,无法使用此处描述的技术进行映射,但仍有办法从用户空间访问它们。最常见的示例是 x86 ioport。在 x86 系统上,用户空间可以使用ioperm()、iopl()、inb()、outb()和类似函数访问这些 ioport。

由于这些ioport区域无法映射,因此它们不会像上面描述的正常内存那样出现在/sys/class/uio/uioX/maps/下。如果没有有关硬件必须提供的端口区域的信息,驱动程序的用户空间部分将很难找到哪些端口属于哪个 UIO 设备。

为了解决这种情况,添加了新目录/sys/class/uio/uioX/portio。仅当驱动程序要将有关一个或多个端口区域的信息传递到用户空间时,它才存在。如果是这种情况,名为/port0、/port1的子目录将出现在sys/class/uio/uioX/portio/下面。

每个目录包含四个只读文件,这些文件显示端口区域的名称、开始时间、大小和类型:portX/

  • name:此端口区域的字符串标识符。该字符串是可选的,可以为空。驱动程序可以对其进行设置,以便用户空间更轻松地找到某个端口区域。
  • start:此区域的第一个端口。
  • size:此区域中的端口数。
  • porttype:描述端口类型的字符串。

编写自己的内核模块

struct uio_info

This structure tells the framework the details of your driver, Some of the members are required, others are optional.

  • const char *name: Required. The name of your driver as it will appear in sysfs. I recommend using the name of your module for this.
  • const char *version: Required. This string appears in /sys/class/uio/uioX/version.
  • struct uio_mem mem[ MAX_UIO_MAPS ]: Required if you have memory that can be mapped with mmap(). For each mapping you need to fill one of the uio_mem structures. See the description below for details.
  • struct uio_port port[ MAX_UIO_PORTS_REGIONS ]: Required if you want to pass information about ioports to userspace. For each port region you need to fill one of the uio_port structures. See the description below for details.
  • long irq: Required. If your hardware generates an interrupt, it’s your modules task to determine the irq number during initialization. If you don’t have a hardware generated interrupt but want to trigger the interrupt handler in some other way, set irq to UIO_IRQ_CUSTOM. If you had no interrupt at all, you could set irq to UIO_IRQ_NONE, though this rarely makes sense.
  • unsigned long irq_flags: Required if you’ve set irq to a hardware interrupt number. The flags given here will be used in the call to
  • 1
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值