中斷

中斷是指裝置在需要提供服務時產生的非同步事件。例如,當資料位於序列埠中或乙太網路封包抵達時,就會產生中斷。中斷可讓驅動程式庫在事件發生時立即得知,但驅動程式庫不必耗費時間輪詢 (主動等待)。

使用中斷的驅動程式庫一般架構是,系統會在驅動程式庫啟動 / 繫結作業期間建立背景中斷處理執行緒 (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 是您需要的中斷次數。

第二個引數是裝置支援的中斷模式專用的輸出參數。

已設定中斷支援後,您可以呼叫 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_irq 傳遞 0 是正常現象。如果您的裝置出現多次中斷,常見的做法是在 for 迴圈中執行 pci::MapInterrupt() 函式,並將控點繫結至每個中斷情形。

等待中斷

在 IHT 中,您會呼叫 zx::Break::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() 取得的物件。

邊緣與層級的中斷模式

中斷硬體可在「邊緣」或「等級」兩種模式中運作。

在邊緣模式中,幹擾會在主動式邊緣啟動 (硬體信號從非使用中改為使用中),且會做為單樣本運作。也就是說,信號必須回到停用狀態,系統才能再次識別。

在等級模式中,當硬體訊號處於活動狀態時,會啟用中斷功能。

一般而言,當專屬的中斷事件會使用邊緣模式,而當多部裝置共用中斷時,就會使用層級模式 (因為您希望中斷情形在「所有」裝置都失去要求行前維持運作狀態)。

Zircon 核心會自動遮蓋及隱藏中斷情形。對於因層級觸發的硬體中斷,zx::中斷::wait() 會在傳回前遮蓋中斷,並在下次呼叫時將其解除遮蓋。在邊緣觸發的中斷情形中,幹擾設定會保持未遮蓋狀態。

IHT 不應執行任何長時間執行的工作。如果是需要執行冗長工作的驅動程式,請使用背景工作執行緒。

關閉使用中斷的驅動程式庫

如要乾淨地關閉使用中斷的驅動程式庫,可以使用 zx::Break::destroy() 取消 zx::abuse::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::中斷::wait() 呼叫喚醒,並顯示錯誤代碼。IHT 會查看錯誤代碼 (在本範例中為 ZX_ERR_CANCELED) 並做出最終決定。同時,主要執行緒正在等待透過對 thread::join() 的呼叫加入 IHT。IHT 結束後,thread::join() 會傳回結果,主執行緒可以完成其處理作業。

進階讀取器會受邀查看其他可用的中斷相關函式: