處理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(¶ms, 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(¶ms);
}
}
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(¶ms);
}
return STATUS_SUCCESS;
}
在上述程式碼範例中,驅動程式的 EvtDriverDeviceAdd 回呼函式會設定裝置內容,然後呼叫 IoInitializeDpcRequest 來註冊 DpcForIsr 常式。
驅動程式的 InterruptService 常式會反轉中斷狀態值,然後呼叫 IoRequestDpc 將 DPC 排入佇列。
在其 EvtDevicePrepareHardware 回呼函式中,驅動程式會將狀態值初始化為 FALSE ,然後呼叫 IoConnectInterruptEx。 在 EvtDeviceReleaseHardware 回呼函式中,驅動程式會呼叫 IoDisconnectInterruptEx 來取消註冊其 ISR。