中断

NAME

interrupts - 用户模式 I/O 中断传递

SYNOPSIS

中断对象允许用户空间创建、发出硬件中断信号并等待硬件中断。

说明

中断对象是驱动程序通常使用的对象,用于接收待处理中断请求 (IRQ) 的通知,这些请求通常由硬件单元生成。 中断主要分为物理中断和虚拟中断两种,它们与其他 Zircon 内核对象略有不同,因为它们通过一组特殊的内核系统调用发出待处理 IRQ 信号,而不是依赖于标准信号机制。

物理中断

中断对象通常由具有高度特权的驱动程序进程(通常是平台总线驱动程序或 PCIe 总线驱动程序)在设备枚举期间创建,然后通过委托提供给相应的驱动程序。 表示物理边沿和电平触发中断的对象是使用 zx_interrupt_create() 创建的,并且需要传递各种平台特定的配置选项(例如中断是边沿触发还是电平触发,或者高电平有效还是低电平有效),以便正确配置系统的中断控制器。

消息信号中断(MSI,由 PCIe 设备使用)略有不同,必须使用对 zx_msi_allocate()zx_msi_create() 的调用进行分配和构造。

如需了解详情,请参阅这些系统调用的参考文档。

虚拟中断

虚拟中断也是使用 zx_interrupt_create() 并传递 ZX_INTERRUPT_VIRTUAL 选项创建的。 虚拟中断对象是纯软件构造,不会由基于硬件的中断请求发出信号。 相反,zx_interrupt_trigger() 系统调用用于为对象触发虚拟 IRQ。

虚拟中断通常用于以下两种情况之一。

第一种是测试。 虚拟中断使用与物理中断相同的特殊系统调用集进行等待和确认,但它们将 IRQ 的触发置于软件控制之下。 这意味着测试代码可以创建虚拟中断并将其传递给被测驱动程序,而驱动程序可以像往常一样使用中断对象,无需更改任何代码。 不同之处在于,现在测试框架可以控制对象的中断请求状态,从而在测试期间模拟各种硬件行为。

第二种是多路分解,通常用于 GPIO(通用输入输出)中断。 许多芯片允许将通用引脚配置为中断源,以连接到需要向系统传递 IRQ 的外部设备。 通常,除了控制配置为中断的引脚(屏蔽、确认等)之外,这些引脚的配置(是否用于中断)也是使用表示引脚组的共享寄存器完成的。通常,16 个或 32 个引脚共享一组寄存器,以及与系统中断控制器绑定的单个物理中断。

在这样的系统中,可能有一个驱动程序负责配置 GPIO 硬件并为常见的 GPIO 物理中断提供服务,而其他驱动程序负责可能使用共享通用 GPIO 组的不同引脚连接到主系统的外部硬件。 例如,假设一个系统有一个外部芯片来处理蓝牙,另一个芯片是加速度计。每个驱动程序都与 GPIO 驱动程序以及彼此分开,并且应进行进程隔离。 但是,即使 BT 和加速度计驱动程序实际上共享由 GPIO 驱动程序控制的单个物理中断,它们也各自需要一个单独的中断对象来等待。

在这种情况下,中断需要由 GPIO 驱动程序“多路分解”,并使用虚拟中断单独委托给相应的驱动程序。 对于配置为中断的每个引脚,GPIO 驱动程序都可以创建一个虚拟中断,该中断可以传递给相应的任务特定驱动程序。当常见的物理 GPIO 中断触发时,GPIO 驱动程序可以识别表示任何新断言的 IRQ 的虚拟中断,并使用 zx_interrupt_trigger() 触发它们。在虚拟中断的驱动程序用户为其硬件提供服务并确认中断后,GPIO 驱动程序可以重新启用相应 GPIO 寄存器组中的中断,并通过共享物理中断等待新的 IRQ。

等待和确认中断

用户可以通过中断对象同步等待中断请求,方法是在中断对象上阻止线程;也可以异步等待中断请求,方法是将中断绑定到 Zircon 端口对象,其中 IRQ 将使用端口数据包传递,该数据包可以与其他发送到端口的数据包一起读取。

只能使用一种方法。 中断对象不能同时绑定到端口并阻止线程。此外,以同步方式使用时,一次只能在一个中断对象上阻止一个线程。尝试阻止第二个线程会导致错误。同样,以异步方式使用时,中断对象一次只能绑定到一个端口,不能绑定到更多端口。

同步等待和确认

创建中断对象后,用户可以阻止线程并通过调用 zx_interrupt_wait() 等待 IRQ。如果中断对象已触发(通过物理中断的硬件 IRQ 或通过对虚拟中断的 zx_interrupt_trigger() 调用),则调用将立即返回。否则,线程将保持阻止状态,直到断言 IRQ 或使用对 zx_interrupt_destroy() 的调用销毁中断对象。

触发中断对象时,如果有线程当前正在等待该对象,则该线程将被唤醒。 否则,如果中断对象上还没有线程在等待,则下一个调用 zx_interrupt_wait 的线程将立即被唤醒。即使该线程唤醒后,中断在逻辑上仍处于触发状态,并且在确认之前不会重新武装和重新启用。在中断对象处于触发状态之前,等待调用不会返回。此外,在线程从等待返回后,后续的等待调用将确认先前触发的中断,从而导致中断再次变为未触发状态。 中断对象将在中断控制器级别重新武装,并且在断言下一个 IRQ 时,线程将被解除阻止。

异步等待和确认

用户还可以通过将中断绑定到 Zircon 端口对象来异步等待 IRQ。 为此,用户可以调用 zx_interrupt_bind(),传递中断对象的句柄以及使用 ZX_PORT_BIND_TO_INTERRUPT 选项创建的端口的句柄。 将中断绑定到端口后:

1) IRQ 将通过将中断端口数据包排队到 绑定端口来发出信号。 2) 与标准 Zircon 信号不同,将中断绑定到 端口后,无需调用zx_object_wait_async()。每当断言 IRQ 时,端口数据包 将自动传递到绑定端口。 2) 后续对 zx_interrupt_bind() 的调用将失败。 中断对象一次只能绑定到一个端口。 3) 对 zx_interrupt_wait() 的调用将失败。 中断对象可以 同步使用,也可以异步使用,但不能同时使用。 4) 中断对象可以使用 zx_interrupt_unbind() 与其绑定端口断开连接,之后可以重新绑定到其他 端口,也可以通过 zx_interrupt_wait() 同步使用。

触发绑定中断并传递端口数据包后,在确认中断之前,不会传递任何新数据包。 与同步模式(其中中断通过对 zx_interrupt_wait() 的下一次调用进行确认)不同,中断对象的异步用户必须使用对 zx_interrupt_ack() 的调用显式确认中断。确认中断后,可以在下次断言 IRQ 时传递新的端口数据包,如果已有其他 IRQ 待处理,则可以立即传递。

用户信号

虽然中断对象不使用标准 Zircon 信号来通知用户 IRQ,但它们仍然是 Zircon 内核对象。 因此,它们仍然具有一组标准的八个“用户信号”,可以使用对 zx_object_signal() 的调用进行设置和清除,并使用 zx_object_wait_one()zx_object_wait_many()zx_object_wait_async() 进行等待,前提是调用方具有足够权限的句柄。

虚拟中断和 UNTRIGGERED 信号

除了用户信号之外,虚拟中断对象还定义了另一个名为 ZX_VIRTUAL_INTERRUPT_UNTRIGGERED 的标准 Zircon 信号。

与在满足特定条件时由硬件自动触发请求的物理中断不同,虚拟中断对象需要由软件显式触发。触发虚拟中断后,负责触发的软件需要知道 IRQ 接收器何时处理并确认 IRQ,以便将来在适当的时间点重新断言 IRQ。

为了提供此知识,我们引入了 ZX_VIRTUAL_INTERRUPT_UNTRIGGERED 信号。

当虚拟中断用于测试时,此信号可用于将测试推进到下一阶段。 当用于多路分解中断时,此信号可供一组多路复用中断的所有者使用,以取消屏蔽和重新武装共享物理中断的中断,然后返回到等待共享物理中断。

ZX_VIRTUAL_INTERRUPT_UNTRIGGERED 信号相关的规则如下:

  • 创建后,虚拟中断对象立即处于“未触发”状态,并且将断言 ZX_VIRTUAL_INTERRUPT_UNTRIGGERED 信号。
  • 调用 zx_interrupt_trigger() 会导致中断对象进入触发状态,并取消断言 ZX_VIRTUAL_INTERRUPT_UNTRIGGERED 信号。
  • 该信号将保持取消断言状态,直到使用者确认该信号。 请参阅(“等待和确认中断”)[#waiting-for-and-acknowledging-interrupts]。
  • 信号的观察者使用标准 zx_object_wait_one()zx_object_wait_many()zx_object_wait_async() 系统调用。

请注意,当中断对象与端口一起使用时,虚拟中断可能处于触发状态,而另一个中断已处于待处理状态。对 zx_interrupt_trigger() 的初始调用将导致端口数据包立即发送到绑定端口,而后续调用将挂起另一个中断,但不会立即触发端口数据包。

在这种情况下,当用户使用对 zx_interrupt_ack() 的调用确认第一个端口数据包时,对象实际上会从触发状态移至未触发状态,但随后会立即再次触发,传递第二个端口数据包,并从未触发状态转换回触发状态。

在此过程中,ZX_VIRTUAL_INTERRUPT_UNTRIGGERED 信号基本上会被“选通”。 任何当前待处理的等待操作都将得到满足(阻止信号的线程将被解除阻止,发布的异步等待操作将传递端口数据包),但是,当对 zx_interrupt_ack() 的调用展开时,对象信号本身将立即取消断言。

物理中断对象 不实现 ZX_VIRTUAL_INTERRUPT_UNTRIGGERED,并且永远不会断言该信号。

备注

中断对象是 DDK 的私有对象,通常不适用于用户空间进程。

系统调用