共用方式為


處理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結構的模擬ActiveBoth成員描述。

下列程式碼範例示範周邊裝置的 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。