11.5. The Kernel PlugIn Architecture

11.5.1. Architecture Overview

A driver written in user mode uses WinDriver's API (WDC_xxx and/or WD_xxx [B.2]) to access devices. If a certain function that was implemented in the user mode requires kernel performance (the interrupt handler, for example), that function is moved to the WinDriver Kernel PlugIn. Generally it should be possible to move code that uses WDC_xxx / WD_xxx function calls from the user mode to the kernel without modification, since the same WinDriver API is supported both in the user mode and in the Kernel PlugIn.

Figure 11.1. Kernel PlugIn Architecture

Kernel PlugIn Architecture

11.5.2. WinDriver's Kernel and Kernel PlugIn Interaction

There are two types of interaction between the WinDriver kernel and the WinDriver Kernel PlugIn:

  • Interrupt handling: When WinDriver receives an interrupt, by default it will activate the caller's user-mode interrupt handler. However, if the interrupt was set to be handled by a Kernel PlugIn driver, then once WinDriver receives the interrupt, it activates the Kernel PlugIn driver's kernel-mode interrupt handler. Your Kernel PlugIn interrupt handler could essentially consist of the same code that you wrote and debugged in the user-mode interrupt handler, before moving to the Kernel PlugIn, although some of the user-mode code should be modified. We recommend that you rewrite the interrupt acknowledgment and handling code in the Kernel PlugIn to utilize the flexibility offered by the Kernel PlugIn (see Section 11.6.5).
  • Message passing: To execute functions in kernel mode (such as I/O processing functions), the user-mode driver simply passes a message to the WinDriver Kernel PlugIn. The message is mapped to a specific function, which is then executed in the kernel. This function can typically contain the same code as it did when it was written and debugged in user mode.
    You can also use messages to pass data from the user-mode application to the Kernel PlugIn driver.

11.5.3. Kernel PlugIn Components

At the end of your Kernel PlugIn development cycle, your driver will have the following components:

  • User-mode driver application (<application name>/.exe), written with the WDC_xxx / WD_xxx API.
  • The WinDriver kernel module — windrvr1260.sys/.o/.ko, depending on the operating system.
  • Kernel PlugIn driver (<Kernel PlugIn driver name>/.sys/.o/.ko/.kext), which was also written with the WDC_xxx / WD_xxx API, and contains the driver functionality that you have selected to bring down to the kernel level.

11.5.4. Kernel PlugIn Event Sequence

The following is a typical event sequence that covers all the functions that you can implement in your Kernel PlugIn:

11.5.4.1. Opening a Handle from the User Mode to a Kernel PlugIn Driver

Event/CallbackNotes
Event: Windows loads your Kernel PlugIn driver. This takes place at boot time, by dynamic loading, or as instructed by the registry.
Callback: Your KP_Init Kernel PlugIn routine [B.8.1] is called KP_Init informs WinDriver of the name(s) of your KP_Open routine(s) [B.8.2]. WinDriver calls the relevant open routine when there is a user-mode request to open a handle to your Kernel PlugIn driver.
Event: Your user-mode driver application requests a handle to your Kernel PlugIn driver, by calling one of the following functions:
WDC_KernelPlugInOpen() [B.3.22]
WDC_xxxDeviceOpen() (PCI [B.3.17] / ISA [B.3.18]) with the name of the Kernel PlugIn driver
WD_KernelPlugInOpen() — when using the low-level WinDriver API (see the WinDriver PCI Low-Level API Reference)
 
Callback: The relevant KP_Open Kernel PlugIn callback routine [B.8.2] is called. The KP_Open [B.8.2] callback is used to inform WinDriver of the names of all the callback functions that you have implemented in your Kernel PlugIn driver, and to initiate the Kernel PlugIn driver, if needed.

11.5.4.2. Handling User-Mode Requests from the Kernel PlugIn

Event/CallbackNotes
Event: Your application calls WDC_CallKerPlug() [B.3.23], or the low-level WD_KernelPlugInCall() function (see the WinDriver PCI Low-Level API Reference). Your application calls WDC_CallKerPlug() / WD_KernelPlugInCall() to execute code in the kernel mode (in the Kernel PlugIn driver). The application passes a message to the Kernel PlugIn driver. The Kernel PlugIn driver will select the code to execute according to the message sent.
Callback: Your KP_Call Kernel PlugIn routine [B.8.4] is called. KP_Call [B.8.4] executes code according to the message passed to it from the user mode.

11.5.4.3. Interrupt Handling — Enable/Disable and High Interrupt Request Level Processing

Event/CallbackNotes
Event: Your application calls WDC_IntEnable() [B.3.48] with the fUseKP parameter set to TRUE (after having opened a handle to the Kernel PlugIn), or calls the low-level InterruptEnable() or WD_IntEnable() functions (see the WinDriver PCI Low-Level API Reference) with a handle to a Kernel PlugIn driver (set in the hKernelPlugIn field of the WD_INTERRUPT structure passed to the function).  
Callback: Your KP_IntEnable Kernel PlugIn routine [B.8.6] is called. This function should contain any initialization required for your Kernel PlugIn interrupt handling.
Event: Your hardware creates an interrupt. 
Callback: Your high-IRQL Kernel PlugIn interrupt handler routine — KP_IntAtIrql [B.8.8] (legacy interrupts) or KP_IntAtIrqlMSI [B.8.10] (MSI/MSI-X) — is called. KP_IntAtIrql [B.8.8] and KP_IntAtIrqlMSI [B.8.10] run at a high priority, and therefore should perform only the basic interrupt handling, such as lowering the HW interrupt signal of level-sensitive interrupts to acknowledge the interrupt.
If more interrupt processing is required, KP_IntAtIrql (legacy interrupts) or KP_IntAtIrqlMSI (MSI/MSI-X) can return TRUE in order to defer additional processing to the relevant deferred processing interrupt handler — KP_IntAtDpc [B.8.9] or KP_IntAtDpcMSI [B.8.11].
Event: Your application calls WDC_IntDisable() [B.3.49], or the low-level InterruptDisable() or WD_IntDisable() functions (see the WinDriver PCI Low-Level API Reference), when the interrupts were previously enabled in the Kernel PlugIn (see the description of the interrupt enable event above).  
Callback: Your KP_IntDisable Kernel PlugIn routine [B.8.7] is called. This function should free any memory that was allocated by the KP_IntEnable callback [B.8.6].

11.5.4.4. Interrupt Handling — Deferred Procedure Calls

Event/CallbackNotes
Event: The Kernel PlugIn high-IRQL interrupt handler — KP_IntAtIrql [B.8.8] or KP_IntAtIrqlMSI [B.8.10] — returns TRUE. This informs WinDriver that additional interrupt processing is required as a Deferred Procedure Call (DPC) in the kernel.
Callback: Your Kernel PlugIn DPC interrupt handler — KP_IntAtDpc [B.8.9] (legacy interrupts) or KP_IntAtDpcMSI [B.8.11] (MSI/MSI-X) — is called. Processes the rest of the interrupt code, but at a lower priority than the high-IRQL interrupt handler.
Event: The DPC interrupt handler — KP_IntAtDpc [B.8.9] or KP_IntAtDpcMSI [B.8.11] — returns a value greater than 0. This informs WinDriver that additional user-mode interrupt processing is required.
Callback: WD_IntWait() (see the WinDriver PCI Low-Level API Reference) returns. Your user-mode interrupt handler routine is executed.

11.5.4.5. Plug-and-Play and Power Management Events

Event/CallbackNotes
Event: Your application registers to receive Plug-and-Play and power management notifications using a Kernel PlugIn driver, by calling WDC_EventRegister() [B.3.52] with the with the fUseKP parameter set to TRUE (after having opened the device with a Kernel PlugIn), or calls the low-level EventRegister() (see the WinDriver PCI Low-Level API Reference) or WD_EventRegister() functions with a handle to a Kernel PlugIn driver (set in the hKernelPlugIn field of the WD_EVENT structure that is passed to the function).
Event: A Plug-and-Play or power management event (to which the application registered to listen) occurs.
Callback: Your KP_Event Kernel PlugIn routine [B.8.5] is called. KP_Event receives information about the event that occurred and can proceed to handle it as needed.
Event: KP_Event [B.8.5] returns TRUE. This informs WinDriver that the event also requires user-mode handling.
Callback: WD_IntWait() (see the WinDriver PCI Low-Level API Reference) returns. Your user-mode event handler routine is executed.