Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
Vanaf frameworkversie 1.11 kunnen Kernel-Mode Driver Framework (KMDF)-stuurprogramma's en User-Mode Driver Framework (UMDF)-stuurprogramma's die draaien op Windows 8 of latere versies van het besturingssysteem, interruptobjecten creëren die een passieve verwerking vereisen. Als de driver een interruptobject configureert voor het afhandelen van interrupts op passief niveau, roept het framework de interrupt-serviceroutine (ISR) van de driver en andere callbackfuncties voor interruptobjectgebeurtenissen aan op IRQL = PASSIVE_LEVEL, terwijl een vergrendeling voor passief-niveau-interrupts actief is.
Als u een framework-stuurprogramma ontwikkelt voor een SoC-platform (System on a Chip), kunt u passieve-modus interrupts gebruiken om te communiceren met een off-SoC-apparaat via een bus met lage snelheid, zoals I²C, SPI of UART.
Anderszins dient u -interrupts te gebruiken die verwerking vereisen op de IRQL- (DIRQL) van het apparaat. Als uw stuurprogramma ondersteuning biedt voor msis's (message-signaled interrupts), moet u DIRQL-interruptafhandeling gebruiken. In versies 1.9 en eerder verwerkt het framework altijd interrupts op IRQL = DIRQL.
In dit onderwerp wordt beschreven hoe u maakt, servicet en passieve interrupts synchroniseert.
Een Passive-Level Interrupt maken
Als u een passief interrupt-object wilt maken, moet een stuurprogramma een WDF_INTERRUPT_CONFIG structuur initialiseren en doorgeven aan de methode WdfInterruptCreate. In de configuratiestructuur moet het stuurprogramma:
- Stel het PassiveHandling- lid in op TRUE.
- Geef een EvtInterruptIsr callback-functie op, die op passief niveau moet worden aangeroepen.
- Stel de AutomaticSerialization, indien gewenst, in op TRUE. Als het stuurprogramma AutomaticSerialization instelt op TRUE, synchroniseert het framework de uitvoering van de EvtInterruptDpc of EvtInterruptWorkItem callback-functies met callback-functies van andere objecten die zich onder het bovenliggende object van de interrupt bevinden.
- Desgewenst kan het stuurprogramma een EvtInterruptWorkItem callback-functie opgeven, die moet worden aangeroepen bij IRQL = PASSIVE_LEVEL, of een EvtInterruptDpc callback-functie, die moet worden aangeroepen op IRQL = DISPATCH_LEVEL.
Zie WDF_INTERRUPT_CONFIGvoor meer informatie over het instellen van de bovenstaande leden van de configuratiestructuur. Zie Het in- en uitschakelen van interruptsvoor meer informatie over het in- en uitschakelen van interrupts op passief niveau.
onderhoud van een Passive-Level Interrupt
De EvtInterruptIsr callback-functie, die wordt uitgevoerd op IRQL = PASSIVE_LEVEL met de passieve onderbrekingsvergrendeling, plant doorgaans een interruptwerkitem of onderbreekt DPC om op een later tijdstip interruptgerelateerde informatie te verwerken. Frameworkstuurprogramma's implementeren werkitem- of DPC-routines als EvtInterruptWorkItem of EvtInterruptDpc callbackfuncties.
Om de uitvoering van een EvtInterruptWorkItem callback-functie te plannen, roept een stuurprogramma WdfInterruptQueueWorkItemForIsr aan vanuit de EvtInterruptIsr callback-functie.
Als een driver de uitvoering van een EvtInterruptDpc callback-functie wil plannen, roept deze de WdfInterruptQueueDpcForIsr aan vanuit de EvtInterruptIsr callback-functie. Houd er rekening mee dat de EvtInterruptIsr callback-functie WdfInterruptQueueWorkItemForIsr of WdfInterruptQueueDpcForIsrkan aanroepen, maar niet beide.
De meeste stuurprogramma's gebruiken één EvtInterruptWorkItem of EvtInterruptDpc callback-functie voor elk type interrupt. Als uw stuurprogramma meerdere frameworkonderbrekingsobjecten voor ieder apparaat aanmaakt, overweeg dan een afzonderlijke EvtInterruptWorkItem- of EvtInterruptDpc-callback te gebruiken voor elke onderbreking.
Stuurprogramma's voltooien doorgaans I/O-aanvragen in hun EvtInterruptWorkItem of EvtInterruptDpc callback-functies.
In het volgende codevoorbeeld ziet u hoe een stuurprogramma dat passieve interrupts gebruikt, een EvtInterruptWorkItem callback vanuit de EvtInterruptIsr functie kan plannen.
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);
}
}
Het synchroniseren van een Passive-Level Interrupt
Als u impasses wilt voorkomen, volgt u deze richtlijnen bij het schrijven van een stuurprogramma dat de verwerking van passieve interrupts implementeert:
Als AutomaticSerialization is ingesteld op TRUE, synchrone aanvragen niet verzenden vanuit een EvtInterruptDpc of EvtInterruptWorkItem callback-functie.
Laat de interruptvergrendeling op passief niveau los voordat I/O-aanvragenvoltooit.
Geef EvtInterruptDisable, EvtInterruptEnableen EvtInterruptWorkItem indien nodig.
Als uw stuurprogramma interruptgerelateerd werk moet uitvoeren in een willekeurige threadcontext, zoals in een aanvraaghandler, gebruikt u WdfInterruptTryToAcquireLock en WdfInterruptReleaseLock. Roep WdfInterruptAcquireLock, WdfInterruptSynchronize, WdfInterruptEnableof WdfInterruptDisable niet aan vanuit een willekeurige threadcontext. Voor een voorbeeld van een deadlock-scenario dat kan worden veroorzaakt door het aanroepen van WdfInterruptAcquireLock vanuit een willekeurige threadcontext, zie de sectie Opmerkingen van WdfInterruptAcquireLock.
Als de aanroep naar WdfInterruptTryToAcquireLock mislukt, kan het besturingsprogramma het onderbrekingsgerelateerde werk naar een werkitem uitstellen. In dat werkitem kan de bestuurder de interruptvergrendeling veilig verkrijgen door WdfInterruptAcquireLockaan te roepen. Zie WdfInterruptTryToAcquireLockvoor meer informatie.
In een niet-willekeurige threadcontext, zoals een werkitem, kan het stuurprogramma WdfInterruptAcquireLock of WdfInterruptSynchronizeaanroepen.
Zie Synchroniseren van interruptcodevoor meer informatie over het gebruik van interruptvergrendelingen.