名稱
interrupts - Usermode I/O interrupt delivery
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。如果中斷物件已觸發 (實體中斷的 HW IRQ,或虛擬中斷的 zx_interrupt_trigger() 呼叫),呼叫會立即傳回。否則,在系統判斷 IRQ 或使用 zx_interrupt_destroy() 呼叫毀損中斷物件之前,執行緒會維持封鎖狀態。
觸發中斷物件時,如果目前有執行緒正在等待該物件,系統會喚醒該執行緒。否則,如果中斷物件上沒有等待中的執行緒,則下一個在物件上呼叫 zx_interrupt_wait 的執行緒會立即喚醒。即使該執行緒喚醒後,中斷仍會以邏輯方式觸發,且在確認前不會重新啟動和啟用。中斷物件處於觸發狀態時,等待呼叫才會傳回。此外,執行緒從等待狀態返回後,後續對 wait 的呼叫會確認先前觸發的中斷,導致中斷再次變成未觸發狀態。中斷物件會在中斷控制器層級重新啟動,而下一個 IRQ 判斷為 true 時,執行緒就會解除封鎖。
非同步等待和確認
使用者也可以將中斷繫結至 Zircon 埠物件,以非同步方式等待 IRQ。如要執行這項操作,使用者會呼叫 zx_interrupt_bind(),並將中斷物件的控制代碼,以及使用 ZX_PORT_BIND_TO_INTERRUPT 選項建立的通訊埠控制代碼傳遞至該函式。中斷與連接埠繫結後:
系統會將中斷埠封包排入繫結埠的佇列,藉此發出 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,以便日後在適當的時間點重新聲明。
為提供這項知識,我們導入了 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 的私有物件,一般不適用於使用者空間程序。
SYSCALLS
zx_interrupt_create()- 建立中斷物件。zx_interrupt_destroy()- 終結中斷物件。zx_interrupt_bind()- 將中斷物件繫結至埠物件。zx_interrupt_unbind()- 從繫結的通訊埠解除繫結中斷物件。zx_interrupt_wait()- Wait for an IRQ to be asserted on an interrupt object.zx_interrupt_trigger()- 觸發虛擬中斷物件。zx_interrupt_ack()- 確認連接埠繫結的中斷物件並重新啟動。zx_msi_allocate()- 分配 MSI 中斷範圍。zx_msi_create()- 在已分配的範圍中建立 MSI 中斷物件。