中断

中断是一种异步事件,由设备在需要服务时生成。 例如,当串行端口上有可用的数据时,会生成中断, 或以太网数据包已送达。 打扰可让驾驶员在事件后立即了解事件 但不会让驱动程序花时间轮询(主动等待)它。

使用中断的驱动程序的一般架构是 中断处理线程 (IHT) 在驱动程序启动 / 绑定期间创建 操作。 此线程会等待中断发生,当中断发生时,它会执行一些 一种服务操作

以串行端口驱动程序为例。 它可以由于发生以下任一事件而收到中断:

  • 已到达一个或多个角色
  • 现在可以在聊天室中传输一个或多个字符
  • 控制线(例如 DTR)的状态发生变化。

中断唤醒 IHT。 IHT 通常通过读取某些状态寄存器来确定事件的原因。 然后,它会运行相应的服务函数来处理事件。 完成后,IHT 返回到休眠状态,等待下一次中断。

例如,如果一个字符到达,则 IHT 被唤醒,读取一个状态寄存器, “数据可用”表示“有可用数据”然后调用一个函数 字符从串行端口 FIFO 转移到驱动程序缓冲区。

无需内核级代码

你可能比较熟悉使用中断功能的其他操作系统 服务例程 (ISR)。 这些是内核级处理程序,在特权模式下运行,与 中断控制器硬件。

在 Fuchsia 中,内核处理中断的特权部分 处理,并提供线程级函数供驱动程序使用。

不同之处在于,IHT 在线程级别运行,而 ISR 在 。 它的主要优点是,如果 IHT 崩溃,它只会删除 驱动程序,而失败的 ISR 会切断整个操作系统。

附加到中断

目前,唯一提供中断功能的总线是 PCI 总线。 它支持两种类型:旧版和消息信号中断 (MSI)。

因此,要在 PCI 上使用中断,请执行以下操作:

  1. 确定您的设备支持哪种类型(旧版或 MSI)、
  2. 将中断模式设为匹配模式
  3. 获取设备中断矢量的句柄(通常有一个,但也可能是多个),
  4. 启动 IHT 后台线程
  5. 安排 IHT 线程等待中断(在步骤 3 中的句柄上)。

步骤 12 由“Pci::ConfigureInterruptMode”辅助函数处理:

// Configure interrupt mode.
uint32_t irq_cnt = 1;
fuchsia_hardware_pci::InterruptMode mode;
zx_status_t status = pci.ConfigureInterruptMode(irq_cnt, &mode);
if (status != ZX_OK) {
  // handle error
}

Pci::ConfigureInterruptMode() 采用两个参数:

#include <lib/device-protocol/pci.h>

zx_status_t Pci::ConfigureInterruptMode(uint32_t requested_irq_count,
                                        fpci::InterruptMode* out_mode);

第一个参数 requested_irq_count 是所需的中断数量。

第二个参数是 out 参数,用于表示设备支持的中断模式。

配置中断支持后,调用 Pci::MapInterrupt()。 为所选中断创建句柄请注意, Pci::MapInterrupt() 具有以下原型:

#include <lib/device-protocol/pci.h>

zx_status_t Pci::MapInterrupt(uint32_t which_irq, zx::interrupt* out_interrupt);

第一个参数 which_irq 表示与所需的设备相关中断编号,第二个参数 是指向所创建的中断对象的指针。

现在,您已经有了中断句柄。

请注意,绝大多数设备只有一个中断,因此只需传递 对于 which_irq0 是正常的。 如果您的设备有多个中断,常见做法是运行 for 循环中的 pci::MapInterrupt() 函数 并将句柄绑定到每个中断

等待中断

在 IHT 中,您调用 zx::interrupt::wait() 等待中断。 以下原型适用:

zx_status_t zx::interrupt::wait(zx::time* out_timestamp);

第一个参数可以是 nullptr(典型),也可以是指向时间的指针 指示中断触发时间的戳记(以纳秒为单位, 相对于使用以下代码提取的单调时钟源: zx_clock_get_monotonic())。

因此,典型的 IHT 可能具有以下形状:

int MyDevice::IrqThread() {
    for (;;) {
        zx_status_t status = dev->irq_.wait(nullptr);
        // do stuff
    }
}

设备类有一个成员(此处为 irq_),即您从中获取的对象 Pci::MapInterrupt()

边缘中断模式与关卡中断模式

中断硬件可以在以下两种模式下运行:"边缘"或“level”。

在边缘模式下,中断在活跃边缘(当硬件 信号从无效变为有效),并且一次性。 也就是说,系统必须将此信号恢复为无效状态,然后才能再次识别它。

在电平模式下,当硬件信号处于 活动状态。

通常,边缘模式在中断专用时使用,而级别模式则 当中断由多台设备共享时使用(因为您希望 中断以保持有效状态,直到所有设备均取消断言其请求行)。

Zircon 内核会视情况自动屏蔽和解除中断。 对于音频级触发的硬件中断, zx::interrupt::wait() 在返回之前屏蔽中断,并在下次调用时取消屏蔽。 对于边缘触发的中断,中断保持未屏蔽状态。

IHT 不应执行任何长时间运行的任务。 对于执行冗长任务的驱动程序,请使用工作线程。

关闭使用中断的驱动程序

如需彻底关闭使用中断的驱动程序,您可以使用 zx::interrupt::destroy() 来取消 zx::interrupt::wait() 调用。

具体想法是,当前台线程确定驱动程序应该 它就会销毁中断句柄,从而导致 IHT 关闭:

void MyDevice::DdkRelease() {
  // destroy the handle, this will cause zx_interrupt_wait() to pop
  irq_.destroy();
  irq_thread_.join();
  ...
}

int MyDevice::IrqThread() {
    ...
    for(;;) {
        zx_status_t status = irq_.wait(nullptr);
        if (status == ZX_ERR_CANCELED) {
            // we are being shut down, do any cleanups required
            ...
            break;
        }
        ...
    }
}

主线程在被请求关闭时,会销毁中断句柄。 这会导致 IHT 的 zx::interrupt::wait() 调用来唤醒并显示错误代码。 IHT 查看错误代码(在本例中为 ZX_ERR_CANCELED),并 决策。 同时,主线程正在等待加入 IHT 以参与通话 thread::join()。 IHT 退出后,thread::join() 返回,并且主线程 线程可以完成其处理。

我们邀请高级阅读器查看一些与中断相关的 可用的函数: