Catatan
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba masuk atau mengubah direktori.
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba mengubah direktori.
Dimulai dengan kerangka kerja versi 1.11, driver Kernel-Mode Driver Framework (KMDF) dan User-Mode Driver Framework (UMDF) yang berjalan pada Windows 8 atau versi yang lebih baru dari sistem operasi dapat membuat objek interupsi yang memerlukan penanganan tingkat pasif. Jika driver mengonfigurasi objek interupsi untuk penanganan interupsi tingkat pasif, kerangka kerja akan memanggil rutinitas layanan interupsi (ISR) driver dan fungsi callback peristiwa objek interupsi lainnya pada IRQL = PASSIVE_LEVEL sambil menahan kunci interupsi pada tingkat pasif.
Jika Anda mengembangkan driver berbasis kerangka kerja untuk platform System on a Chip (SoC), Anda dapat menggunakan gangguan mode pasif untuk berkomunikasi dengan perangkat off-SoC melalui bus berkecepatan rendah, seperti I²C, SPI, atau UART.
Jika tidak, Anda harus menggunakan gangguan-gangguan yang memerlukan penanganan di IRQL perangkat (DIRQL). Jika driver Anda mendukung interupsi yang diberi sinyal pesan (MSI), Anda harus menggunakan penanganan interupsi DIRQL. Dalam versi 1.9 dan yang lebih lama, kerangka kerja selalu memproses gangguan pada IRQL = DIRQL.
Topik ini menjelaskan cara membuat, melayani, dan menyinkronkan gangguan tingkat pasif.
Membuat Interupsi Passive-Level
Untuk membuat objek interupsi tingkat pasif, driver harus menginisialisasi struktur WDF_INTERRUPT_CONFIG dan meneruskannya ke metode WdfInterruptCreate. Dalam struktur konfigurasi, driver harus:
- Atur anggota PassiveHandling menjadi TRUE.
- Berikan fungsi pemanggilan balik EvtInterruptIsr, untuk dipanggil pada tingkat pasif.
- Opsional, atur AutomaticSerialization ke TRUE. Jika driver mengatur AutomaticSerialization ke TRUE, maka kerangka kerja menyinkronkan eksekusi dari fungsi panggilan balik EvtInterruptDpc atau EvtInterruptWorkItem dengan fungsi panggilan balik dari objek lain yang berada di bawah objek induk interupsi.
- Secara opsional, pengemudi dapat menyediakan fungsi callback EvtInterruptWorkItem, yang dipanggil pada IRQL = PASSIVE_LEVEL, atau fungsi callback EvtInterruptDpc, yang dipanggil pada IRQL = DISPATCH_LEVEL.
Untuk informasi tambahan tentang mengatur anggota struktur konfigurasi di atas, lihat WDF_INTERRUPT_CONFIG. Untuk informasi tentang mengaktifkan dan menonaktifkan interupsi tingkat pasif, lihat Mengaktifkan dan Menonaktifkan Interupsi.
Melayani Interupsi Passive-Level
Fungsi callback EvtInterruptIsr, yang berjalan di IRQL = PASSIVE_LEVEL dengan kunci interupsi tingkat pasif yang sedang aktif, biasanya menjadwalkan item kerja interupsi atau DPC interupsi untuk memproses informasi terkait interupsi pada waktu yang lain. Driver berbasis kerangka menerapkan rutinitas item kerja atau DPC sebagai fungsi panggilan balik EvtInterruptWorkItem atau EvtInterruptDpc.
Untuk menjadwalkan eksekusi fungsi callback EvtInterruptWorkItem, driver memanggil WdfInterruptQueueWorkItemForIsr dari dalam fungsi callback EvtInterruptIsr.
Untuk menjadwalkan eksekusi fungsi callback EvtInterruptDpc, driver memanggil WdfInterruptQueueDpcForIsr dari dalam fungsi callback EvtInterruptIsr. (Ingatlah bahwa fungsi callback EvtInterruptIsr dari driver dapat memanggil WdfInterruptQueueWorkItemForIsr atau WdfInterruptQueueDpcForIsr, tetapi tidak keduanya.)
Sebagian besar driver menggunakan fungsi panggilan balik EvtInterruptWorkItem atau EvtInterruptDpc untuk setiap jenis interupsi. Jika driver Anda membuat beberapa objek interupsi kerangka untuk setiap perangkat, pertimbangkan untuk menggunakan callback EvtInterruptWorkItem atau EvtInterruptDpc terpisah untuk setiap gangguan.
Driver biasanya menyelesaikan permintaan I/O dalam fungsi panggilan balik EvtInterruptWorkItem atau EvtInterruptDpc.
Contoh kode berikut menunjukkan bagaimana driver yang menggunakan interupsi pada tingkat pasif dapat menjadwalkan fungsi callback EvtInterruptWorkItem dari dalam fungsi 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);
}
}
Menyinkronkan interupsi Passive-Level
Untuk mencegah kebuntuan, ikuti panduan ini saat menulis driver yang menerapkan penanganan gangguan tingkat pasif:
Jika AutomaticSerialization diatur ke TRUE, jangan mengirim permintaan sinkronisasi dari dalam fungsi callback EvtInterruptDpc atau EvtInterruptWorkItem.
Lepaskan kunci interupsi tingkat pasif sebelum menyelesaikan permintaan I/O.
Sediakan EvtInterruptDisable, EvtInterruptEnable, dan EvtInterruptWorkItem seperlunya.
Jika driver Anda harus melakukan pekerjaan terkait interupsi dalam konteks utas sembarang, seperti dalam pengendali permintaan, gunakan WdfInterruptTryToAcquireLock dan WdfInterruptReleaseLock. Jangan memanggil WdfInterruptAcquireLock, WdfInterruptSynchronize, WdfInterruptEnable, atau WdfInterruptDisable dari konteks utas sembarang. Untuk contoh skenario deadlock yang dapat disebabkan oleh pemanggilan #B0 WdfInterruptAcquireLock #A3 dari konteks utas arbitrer, lihat bagian Keterangan dari #B2 #A3 WdfInterruptAcquireLock #A3.
Jika panggilan ke #B0 #A1 WdfInterruptTryToAcquireLock #A2 #C3 gagal, driver dapat menunda pekerjaan terkait interupsinya ke item kerja. Dalam item kerja tersebut, driver dapat dengan aman memperoleh kunci interupsi dengan memanggil fungsi WdfInterruptAcquireLock. Untuk informasi selengkapnya, lihat WdfInterruptTryToAcquireLock.
Dalam konteks utas non-arbitrer, seperti item pekerjaan, driver dapat memanggil WdfInterruptAcquireLock atau WdfInterruptSynchronize.
Untuk informasi selengkapnya tentang menggunakan kunci interupsi, lihat Menyinkronkan Kode Interupsi.