共用方式為


處理 Active-Both 插斷

注意 本主題僅適用於 Kernel-Mode Driver Framework (KMDF) 1.13 版和更早版本。

許多裝置都有硬體暫存器,用於控制中斷觸發和屏蔽。 一般而言,這類裝置的 KMDF 和 UMDF 驅動程式會使用架構的內建中斷支援。

不過,在系統單晶片(SoC)硬體平台上的簡單裝置可能沒有硬體暫存器來處理中斷。 因此,這類裝置的驅動程式可能無法控制何時產生中斷,或能夠在硬體中遮罩中斷。 如果裝置在連線時立即中斷,且驅動程式正在使用架構的中斷支援,則有可能在架構完全初始化架構中斷物件之前觸發中斷。 因此,KMDF 驅動程式必須直接呼叫 WDM 例程,以連接和斷開中斷。 因為 UMDF 驅動程式無法呼叫這些方法,所以您無法為這類裝置撰寫 UMDF 驅動程式。

本主題描述 KMDF 驅動程式如何處理這類裝置的中斷。

在SoC硬體平台上,雙主動中斷通常用於非常簡單的裝置,例如硬體按鈕。 當使用者按下按鈕時,裝置的中斷訊號線會從低變高或從高轉換為低。 當使用者放開按鈕時,插斷線會以相反的方向轉換。 配置為雙向觸發中斷輸入的 GPIO 針腳會在低到高和高到低轉換時產生中斷,導致系統在這兩種情況下都呼叫周邊設備驅動程序的中斷服務例程(ISR)。 不過,驅動程式不會收到轉換是低到高或高到低的指示。

若要區分低到高與高到低轉換,驅動程式必須追蹤每個中斷的狀態。 若要這樣做,您的驅動程式可能會維護布爾中斷狀態值,當中斷行狀態為低時為 FALSE ,當行狀態為高時為 TRUE

請考慮當系統啟動時,行狀態預設為低的範例。 驅動程式會在其 EvtDevicePrepareHardware 回呼函式中,將狀態值初始化為 FALSE。 然後,每次呼叫驅動程式的ISR時,都會發出狀態變更的訊號,驅動程式會反轉其ISR中的狀態值。

如果系統啟動時线路狀態處於高電平,則在中斷啟用後會立即觸發。 因為驅動程式會直接呼叫 IoConnectInterruptEx 例程,而不是呼叫 WdfInterruptCreate,所以可確保接收可能的立即中斷。

此解決方案要求 GPIO 控制器於硬體中支持啟動雙向中斷功能,或是 GPIO 控制器的驅動程式於軟體中模擬啟動雙向中斷功能。 如需瞭解有關模擬雙重主動中斷的資訊,請參閱CONTROLLER_ATTRIBUTE_FLAGS結構中EmulateActiveBoth 成員的描述。

下列程式代碼範例示範周邊裝置的 KMDF 驅動程式如何追蹤中斷極性。

typedef struct _INTERRUPT_CONTEXT INTERRUPT_CONTEXT, *PINTERRUPT_CONTEXT;
typedef struct _DEVICE_CONTEXT DEVICE_CONTEXT, *PDEVICE_CONTEXT;


struct _INTERRUPT_CONTEXT
{
               BOOLEAN State;
               PDEVICE_CONTEXT DeviceContext;
};

struct _DEVICE_CONTEXT
{
               PKINTERRUPT Interrupt;
               INTERRUPT_CONTEXT InterruptContext;
               PDEVICE_OBJECT PhysicalDeviceObject;
               KSPIN_LOCK SpinLock;
};

...

BOOLEAN
YourInterruptIsr(
               __in PKINTERRUPT Interrupt,
               __in PVOID ServiceContext
               )
{
               PINTERRUPT_CONTEXT InterruptContext = (PINTERRUPT_CONTEXT)ServiceContext;
               PDEVICE_CONTEXT DeviceContext = InterruptContext->DeviceContext;

               //
               // Flip the state.
               //
               InterruptContext->State = !InterruptContext->State;

               IoRequestDpc(DeviceContext->PhysicalDeviceObject, DeviceContext->PhysicalDeviceObject->CurrentIrp, InterruptContext);
}

VOID
YourInterruptDpc(
               __in PKDPC Dpc,
               __in PDEVICE_OBJECT DeviceObject,
               __inout PIRP Irp,
               __in_opt PVOID ContextPointer
               )
{
               PINTERRUPT_CONTEXT InterruptContext = (PINTERRUPT_CONTEXT)ContextPointer;

               ...
}

NTSTATUS
EvtDriverDeviceAdd(
               __in  WDFDRIVER Driver,
               __in  PWDFDEVICE_INIT DeviceInit
               )
{
               WDFDEVICE Device;
               PDEVICE_CONTEXT DeviceContext;

               ...

               DeviceContext->Interrupt = NULL;
               DeviceContext->PhysicalDeviceObject = WdfDeviceWdmGetPhysicalDevice(Device);
               KeInitializeSpinLock(&DeviceContext->SpinLock);

               IoInitializeDpcRequest(DeviceContext->PhysicalDeviceObject, YourInterruptDpc);
}

NTSTATUS
EvtDevicePrepareHardware(
               __in  WDFDEVICE Device,
               __in  WDFCMRESLIST ResourcesRaw,
               __in  WDFCMRESLIST ResourcesTranslated
               )
{
               PDEVICE_CONTEXT DeviceContext = YourGetDeviceContext(Device);

               for (ULONG i = 0; i < WdfCmResourceListGetCount(ResourcesTranslated); i++)
               {
                              PCM_PARTIAL_RESOURCE_DESCRIPTOR descriptor = WdfCmResourceListGetDescriptor(ResourcesTranslated, i);

                              if (descriptor->Type == CmResourceTypeInterrupt)
                              {
                                             IO_CONNECT_INTERRUPT_PARAMETERS params;
                                             RtlZeroMemory(&params, sizeof(params));

                                             params.Version = CONNECT_FULLY_SPECIFIED;
                                             params.FullySpecified.PhysicalDeviceObject = DeviceContext->PhysicalDeviceObject;
                                             params.FullySpecified.InterruptObject = &DeviceContext->Interrupt;
                                             params.FullySpecified.ServiceRoutine = YourInterruptIsr;
                                             params.FullySpecified.ServiceContext = (PVOID)&DeviceContext->InterruptContext;
                                             params.FullySpecified.SpinLock = &DeviceContext->SpinLock;
                                             params.FullySpecified.Vector = descriptor->u.Interrupt.Vector;
                                             params.FullySpecified.Irql = (KIRQL)descriptor->u.Interrupt.Level;
                                             params.FullySpecified.SynchronizeIrql = (KIRQL)descriptor->u.Interrupt.Level;
                                             params.FullySpecified.InterruptMode = (descriptor->Flags & CM_RESOURCE_INTERRUPT_LATCHED) ? Latched : LevelSensitive;
                                             params.FullySpecified.ProcessorEnableMask = descriptor->u.Interrupt.Affinity;
                                             params.FullySpecified.ShareVector = descriptor->ShareDisposition;

                                             //
                                             // Default state is low.
                                             //
                                             DeviceContext->InterruptContext.State = 0;
                                             DeviceContext->InterruptContext.DeviceContext = DeviceContext;

                                             return IoConnectInterruptEx(&params);
                              }
               }

               return STATUS_SUCCESS;
}

NTSTATUS
EvtDeviceReleaseHardware(
               __in  WDFDEVICE Device,
               __in  WDFCMRESLIST ResourcesTranslated
)
{
               PDEVICE_CONTEXT DeviceContext = YourGetDeviceContext(Device);

               if (NULL != DeviceContext->Interrupt)
               {
                              IO_DISCONNECT_INTERRUPT_PARAMETERS params;

                              params.Version = CONNECT_FULLY_SPECIFIED;
                              params.ConnectionContext.InterruptObject = DeviceContext->Interrupt;

                              IoDisconnectInterruptEx(&params);
               }

               return STATUS_SUCCESS;
}

在上述程式代碼範例中,驅動程式的 EvtDriverDeviceAdd 回呼函式會設定裝置內容,然後呼叫 IoInitializeDpcRequest 來註冊 DpcForIsr 例程。

驅動程式的 InterruptService 例程會反轉中斷狀態值,然後呼叫 IoRequestDpc 將 DPC 排入佇列。

在其 EvtDevicePrepareHardware 回呼函式中,驅動程式會將狀態值初始化為 FALSE ,然後呼叫 IoConnectInterruptEx。 在 EvtDeviceReleaseHardware 回呼函式中,驅動程式會呼叫 IoDisconnectInterruptEx 來取消註冊其 ISR。