中断

姓名

中断 - 用户模式 I/O 中断传送

SYNOPSIS

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

说明

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

物理中断

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

消息信号中断(由 PCIe 设备使用的 MSI)略有不同,必须通过对 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() 的调用销毁中断对象。

当中断对象被触发时,它会释放正在等待它的当前线程或下一个等待它的线程。不过,即使该线程取消阻塞,中断仍会在逻辑上触发,并且在被确认之前不会重新设置和重新启用。中断对象的同步用户通过再次等待中断对象来确认其 IRQ。中断对象将在中断控制器级别重新启用,并且在下次 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() 进行等待(前提是调用方具有具有足够权限的句柄)。

虚拟中断和 ZX_VIRTUAL_INTERRUPT_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 信号。
  • 信号将保持未断言状态,直到通过下一次调用 zx_interrupt_wait()(同步使用)或调用 zx_interrupt_ack()(异步使用)由使用方确认为止。
  • 信号的观察器使用标准的 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 的专有对象,通常不向用户空间进程提供。

SYSCALLS