Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
Od rozhraní verze 1.11 můžou ovladače Kernel-Mode Driver Framework (KMDF) a User-Mode Driver Framework (UMDF) spuštěné ve Windows 8 nebo novějších verzích operačního systému vytvářet objekty přerušení, které vyžadují pasivní zpracování na úrovni. Pokud ovladač nakonfiguruje objekt přerušení pro zpracování přerušení na pasivní úrovni, architektura volá rutinu služby přerušení ovladače (ISR) a další funkce zpětného volání událostí přerušení objektu v IRQL = PASSIVE_LEVEL při zachování zámku přerušení na pasivní úrovni.
Pokud vyvíjíte ovladač založený na rozhraní pro systém na platformě Chip (SoC), můžete použít pasivní přerušení režimu ke komunikaci se zařízením mimo SoC přes nízkorychlostní sběrnici, jako je I²C, SPI nebo UART.
Jinak byste měli použít přerušení, která vyžadují zpracování v IRQL (DIRQL) zařízení. Pokud váš ovladač podporuje zprávově řízená přerušení (MSI), musíte použít zpracování přerušení DIRQL. Ve verzích 1.9 a starších framework vždy zpracovává přerušení na IRQL = DIRQL.
Toto téma popisuje, jak vytvářet, obsluhovat a synchronizovat přerušení na pasivní úrovni.
Vytvoření přerušení Passive-Level
Chcete-li vytvořit objekt přerušení na pasivní úrovni, ovladač musí inicializovat WDF_INTERRUPT_CONFIG strukturu a předat jej WdfInterruptCreate metoda. Ve struktuře konfigurace by ovladač měl:
- Nastavte PassiveHandling člen na TRUE.
- Poskytněte funkci zpětného volání EvtInterruptIsr, která se má volat na pasivní úrovni.
- Volitelně můžete nastavit funkci AutomaticSerialization na HODNOTU TRUE. Pokud ovladač nastaví AutomaticSerialization na TRUE, rámec synchronizuje provádění funkcí zpětného volání objektu přerušení EvtInterruptDpc nebo EvtInterruptWorkItem s funkcemi zpětného volání z dalších objektů, které jsou podřazené nadřazenému objektu přerušení.
- Volitelně může ovladač poskytnout funkci zpětného volání EvtInterruptWorkItem , která se má volat v IRQL = PASSIVE_LEVEL, nebo funkci zpětného volání EvtInterruptDpc , která se má volat v IRQL = DISPATCH_LEVEL.
Další informace o nastavení výše uvedených členů struktury konfigurace najdete v tématu WDF_INTERRUPT_CONFIG. Informace o povolení a zakázání přerušení na pasivní úrovni naleznete v tématu Povolení a zakázání přerušení.
Obsluha přerušení Passive-Level
Funkce zpětného volání EvtInterruptIsr , která běží v IRQL = PASSIVE_LEVEL se zamknutím přerušení na pasivní úrovni, obvykle naplánuje pracovní položku přerušení nebo přeruší DPC pro zpracování informací souvisejících s přerušením později. Ovladače založené na rozhraní implementují pracovní položky nebo rutiny DPC jako funkce zpětného volání EvtInterruptWorkItem nebo EvtInterruptDpc .
Chcete-li naplánovat spuštění funkce zpětného volání EvtInterruptWorkItem , ovladač volá WdfInterruptQueueWorkItemForIsr z funkce zpětného volání EvtInterruptIsr .
Chcete-li naplánovat spuštění zpětného volání funkce EvtInterruptDpc, ovladač volá WdfInterruptQueueDpcForIsr z funkce zpětného volání EvtInterruptIsr. (Vzpomeňte si, že funkce zpětného volání EvtInterruptIsr ovladače může volat WdfInterruptQueueWorkItemForIsr nebo WdfInterruptQueueDpcForIsr, ale ne obojí.)
Většina ovladačů používá jednu funkci zpětného volání EvtInterruptWorkItem nebo EvtInterruptDpc pro každý typ přerušení. Pokud váš ovladač pro každé zařízení vytvoří více objektů přerušení architektury, zvažte použití samostatného zpětného volání EvtInterruptWorkItem nebo EvtInterruptDpc pro každé přerušení.
Ovladače obvykle dokončují vstupně-výstupní požadavky ve svých funkcích zpětného volání EvtInterruptWorkItem nebo EvtInterruptDpc .
Následující příklad kódu ukazuje, jak ovladač používající přerušení pasivní úrovně může naplánovat callback EvtInterruptWorkItem z funkce EvtInterruptIsr.
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);
}
}
Synchronizace přerušení Passive-Level
Abyste zabránili vzájemnému zablokování, postupujte podle těchto pokynů při psaní ovladače, který zpracovává přerušení na pasivní úrovni:
Pokud je funkce AutomaticSerialization nastavená na HODNOTU TRUE, neodesílejte synchronní požadavky z funkce zpětného volání EvtInterruptDpc nebo EvtInterruptWorkItem .
Před dokončením vstupně-výstupních požadavků uvolněte zámek přerušení na pasivní úrovni.
Podle potřeby zadejte EvtInterruptDisable, EvtInterruptEnable a EvtInterruptWorkItem .
Pokud váš ovladač musí zpracovávat úkoly související s přerušením v libovolném kontextu vlákna, například v obslužné rutině požadavku, použijte WdfInterruptTryToAcquireLock a WdfInterruptReleaseLock. Nevolejte WdfInterruptAcquireLock, WdfInterruptSynchronize, WdfInterruptEnable nebo WdfInterruptDisable z libovolného kontextu vlákna. Příklad scénáře vzájemného zablokování, který může být způsoben voláním WdfInterruptAcquireLock z libovolného kontextu vlákna, naleznete v části Poznámky WdfInterruptAcquireLock.
Pokud volání WdfInterruptTryToAcquireLock selže, ovladač může odložit práci související s přerušením na pracovní položku. V této pracovní položce může ovladač bezpečně získat zámek přerušení voláním WdfInterruptAcquireLock. Další informace naleznete v tématu WdfInterruptTryToAcquireLock.
Ve ne-arbitrárním kontextu vlákna, jako je například pracovní položka, může ovladač volat WdfInterruptAcquireLock nebo WdfInterruptSynchronize.
Další informace o používání zámků přerušení naleznete v tématu Synchronizace kódu přerušení.