支援Passive-Level中斷
從架構 1.11 版開始,Kernel-Mode Driver Framework (KMDF) 和 User-Mode Driver Framework (UMDF) 作業系統 Windows 8上執行的驅動程式,可以建立需要被動層級處理的中斷物件。 如果驅動程式為被動層級中斷處理設定中斷物件,架構會在 IRQL = PASSIVE_LEVEL同時保留被動層級中斷鎖定時,呼叫驅動程式的插斷服務常式 (ISR) 和其他 中斷物件事件回 呼函式。
如果您要在晶片 (SoC) 平臺上開發系統的架構型驅動程式,您可以使用被動模式中斷,透過低速匯流排與非 SoC 裝置通訊,例如 IーC、SPI 或 UART。
否則,您應該使用 需要處理裝置 IRQL (DIRQL) 的中斷。 如果您的驅動程式支援訊息訊號中斷 (MSI) ,您必須使用 DIRQL 中斷處理。 在 1.9 版和更早版本中,架構一律會在 IRQL = DIRQL 處理中斷。
建立Passive-Level中斷
若要建立被動層級中斷物件,驅動程式必須初始化 WDF_INTERRUPT_CONFIG 結構,並將它傳遞至 WdfInterruptCreate 方法。 在組態結構中,驅動程式應該:
- 將 PassiveHandling 成員設定為 TRUE。
- 提供 EvtInterruptIsr 回呼函式,以在被動層級呼叫。
- 選擇性地將 AutomaticSerialization 設定為 TRUE。 如果驅動程式將 AutomaticSerialization 設定為 TRUE,則架構會將中斷物件的 EvtInterruptDpc 或 EvtInterruptWorkItem 回呼函式與中斷父物件下其他物件的回呼函式同步處理。
- 或者,驅動程式可以提供 EvtInterruptWorkItem 回呼函式,以在 IRQL = PASSIVE_LEVEL或 EvtInterruptDpc 回呼函式呼叫,以在 IRQL = DISPATCH_LEVEL呼叫。
如需設定上述組態結構成員的詳細資訊,請參閱 WDF_INTERRUPT_CONFIG。 如需啟用和停用被動層級中斷的相關資訊,請參閱 啟用和停用中斷。
維護Passive-Level中斷
EvtInterruptIsr回呼函式,其會在 IRQL = PASSIVE_LEVEL保留被動層級中斷鎖定,通常會排程中斷工作專案或中斷 DPC,以稍後處理中斷相關資訊。 架構型驅動程式會將工作專案或 DPC 常式實作為 EvtInterruptWorkItem 或 EvtInterruptDpc 回呼函式。
若要排程EvtInterruptWorkItem回呼函式的執行,驅動程式會從EvtInterruptIsr回呼函式內呼叫WdfInterruptQueueWorkItemForIsr。
若要排程EvtInterruptDpc回呼函式的執行,驅動程式會從EvtInterruptIsr回呼函式內呼叫WdfInterruptQueueDpcForIsr。 (回想一下,驅動程式的 EvtInterruptIsr 回呼函式可以呼叫 WdfInterruptQueueWorkItemForIsr 或 WdfInterruptQueueDpcForIsr,但不能同時呼叫兩者。)
大部分驅動程式都會針對每種中斷類型使用單一 EvtInterruptWorkItem 或 EvtInterruptDpc 回呼函式。 如果您的驅動程式為每個裝置建立多個架構中斷物件,請考慮針對每個中斷使用不同的 EvtInterruptWorkItem 或 EvtInterruptDpc 回呼。
驅動程式通常會在其 EvtInterruptWorkItem 或 EvtInterruptDpc 回呼函式中完成 I/O 要求。
下列程式碼範例示範使用被動層級中斷的驅動程式如何從其EvtInterruptIsr函式內排程EvtInterruptWorkItem回呼。
BOOLEAN
EvtInterruptIsr(
_In_ WDFINTERRUPT Interrupt,
_In_ ULONG MessageID
)
/*++
Routine Description:
This routine responds to interrupts generated by the hardware.
It stops the interrupt and schedules a work item for
additional processing.
This ISR is called at PASSIVE_LEVEL (passive-level interrupt handling).
Arguments:
Interrupt - a handle to a framework interrupt object
MessageID - message number identifying the device's
hardware interrupt message (if using MSI)
Return Value:
TRUE if interrupt recognized.
--*/
{
UNREFERENCED_PARAMETER(MessageID);
NTSTATUS status;
PDEV_CONTEXT devCtx;
WDFREQUEST request;
WDF_MEMORY_DESCRIPTOR memoryDescriptor;
INT_REPORT intReport = {0};
BOOLEAN intRecognized;
WDFIOTARGET ioTarget;
ULONG_PTR bytes;
WDFMEMORY reqMemory;
intRecognized = FALSE;
//
// Typically the pattern in most ISRs (DIRQL or otherwise) is to:
// a) Check if the interrupt belongs to this device (shared interrupts).
// b) Stop the interrupt if the interrupt belongs to this device.
// c) Acknowledge the interrupt if the interrupt belongs to this device.
//
//
// Retrieve device context so that we can access our queues later.
//
devCtx = GetDevContext(WdfInterruptGetDevice(Interrupt));
//
// Init memory descriptor.
//
WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(
&memoryDescriptor,
&intReport,
sizeof(intReport);
//
// Send read registers/data IOCTL.
// This call stops the interrupt and reads the data at the same time.
// The device will reinterrupt when a new read is sent.
//
bytes = 0;
status = WdfIoTargetSendIoctlSynchronously(
ioTarget,
NULL,
IOCTL_READ_REPORT,
&memoryDescriptor,
NULL,
NULL,
&bytes);
//
// Return from ISR if this is not our interrupt.
//
if (intReport->Interrupt == FALSE) {
goto exit;
}
intRecognized = TRUE;
//
// Validate the data received.
//
...
//
// Retrieve the next read request from the ReportQueue which
// stores all the incoming IOCTL_READ_REPORT requests
//
request = NULL;
status = WdfIoQueueRetrieveNextRequest(
devCtx->ReportQueue,
&request);
if (!NT_SUCCESS(status) || (request == NULL)) {
//
// No requests to process.
//
goto exit;
}
//
// Retrieve the request buffer.
//
status = WdfRequestRetrieveOutputMemory(request, &reqMemory);
//
// Copy the data read into the request buffer.
// The request will be completed in the work item.
//
bytes = intReport->Data->Length;
status = WdfMemoryCopyFromBuffer(
reqMemory,
0,
intReport->Data,
bytes);
//
// Report how many bytes were copied.
//
WdfRequestSetInformation(request, bytes);
//
// Forward the request to the completion queue.
//
status = WdfRequestForwardToIoQueue(request, devCtx->CompletionQueue);
//
// Queue a work-item to complete the request.
//
WdfInterruptQueueWorkItemForIsr(FxInterrupt);
exit:
return intRecognized;
}
VOID
EvtInterruptWorkItem(
_In_ WDFINTERRUPT Interrupt,
_In_ WDFOBJECT Device
)
/*++
Routine Description:
This work item handler is triggered by the interrupt ISR.
Arguments:
WorkItem - framework work item object
Return Value:
None
--*/
{
UNREFERENCED_PARAMETER(Device);
WDFREQUEST request;
NTSTATUS status;
PDEV_CONTEXT devCtx;
BOOLEAN run, rerun;
devCtx = GetDevContext(WdfInterruptGetDevice(Interrupt));
WdfSpinLockAcquire(devCtx->WorkItemSpinLock);
if (devCtx->WorkItemInProgress) {
devCtx->WorkItemRerun = TRUE;
run = FALSE;
}
else {
devCtx->WorkItemInProgress = TRUE;
devCtx->WorkItemRerun = FALSE;
run = TRUE;
}
WdfSpinLockRelease(devCtx->WorkItemSpinLock);
if (run == FALSE) {
return;
}
do {
for (;;) {
//
// Complete all report requests in the completion queue.
//
request = NULL;
status = WdfIoQueueRetrieveNextRequest(devCtx->CompletionQueue,
&request);
if (!NT_SUCCESS(status) || (request == NULL)) {
break;
}
WdfRequestComplete(request, STATUS_SUCCESS);
}
WdfSpinLockAcquire(devCtx->WorkItemSpinLock);
if (devCtx->WorkItemRerun) {
rerun = TRUE;
devCtx->WorkItemRerun = FALSE;
}
else {
devCtx->WorkItemInProgress = FALSE;
rerun = FALSE;
}
WdfSpinLockRelease(devCtx->WorkItemSpinLock);
}
while (rerun);
}
VOID
EvtIoInternalDeviceControl(
_In_ WDFQUEUE Queue,
_In_ WDFREQUEST Request,
_In_ size_t OutputBufferLength,
_In_ size_t InputBufferLength,
_In_ ULONG IoControlCode
)
{
NTSTATUS status;
DEVICE_CONTEXT devCtx;
devCtx = GetDeviceContext(WdfIoQueueGetDevice(Queue));
switch (IoControlCode)
{
...
case IOCTL_READ_REPORT:
//
// Forward the request to the manual ReportQueue to be completed
// later by the interrupt work item.
//
status = WdfRequestForwardToIoQueue(Request, devCtx->ReportQueue);
break;
...
}
if (!NT_SUCCESS(status)) {
WdfRequestComplete(Request, status);
}
}
同步處理Passive-Level中斷
若要防止死結,請在撰寫實作被動層級中斷處理的驅動程式時,遵循下列指導方針:
如果AutomaticSerialization設定為 TRUE,請勿從EvtInterruptDpc或EvtInterruptWorkItem回呼函式內傳送同步要求。
先釋放被動層級中斷鎖定,再 完成 I/O 要求。
視需要提供 EvtInterruptDisable、 EvtInterruptEnable和 EvtInterruptWorkItem 。
如果您的驅動程式必須在任意執行緒內容中執行中斷相關工作,例如 在要求處理常式中,請使用 WdfInterruptTryToAcquireLock 和 WdfInterruptReleaseLock。 請勿從任意執行緒內容呼叫WdfInterruptAcquireLock、WdfInterruptSynchronize、WdfInterruptEnable 或 WdfInterruptDisable。 如需從任意執行緒內容呼叫 WdfInterruptAcquireLock 所造成的死結案例範例,請參閱 WdfInterruptAcquireLock的一節。
如果呼叫 WdfInterruptTryToAcquireLock 失敗,驅動程式可以將中斷相關的工作延後至工作專案。 在該工作專案中,驅動程式可以藉由呼叫 WdfInterruptAcquireLock安全地取得中斷鎖定。 如需詳細資訊,請參閱 WdfInterruptTryToAcquireLock。
在非任意執行緒內容中,例如工作專案,驅動程式可以呼叫 WdfInterruptAcquireLock 或 WdfInterruptSynchronize。
如需使用中斷鎖定的詳細資訊,請參閱 同步處理中斷程式碼。