B.8. Kernel PlugIn Kernel-Mode Functions

The following functions are callback functions which are implemented in your Kernel PlugIn driver, and which will be called when their calling event occurs. For example: KP_Init [B.8.1] is the callback function that is called when the driver is loaded. Any code that you want to execute upon loading should be in this function.

KP_Init [B.8.1] sets the name of the driver and the KP_Open [B.8.2] function(s).

KP_Open [B.8.2] sets the rest of the driver's callback functions.

For example:

kpOpenCall->funcClose = KP_Close;
    kpOpenCall->funcCall = KP_Call;
    kpOpenCall->funcIntEnable = KP_IntEnable;
    kpOpenCall->funcIntDisable = KP_IntDisable;
    kpOpenCall->funcIntAtIrql = KP_IntAtIrql;
    kpOpenCall->funcIntAtDpc = KP_IntAtDpc;
    kpOpenCall->funcIntAtIrqlMSI = KP_IntAtIrqlMSI;
    kpOpenCall->funcIntAtDpcMSI = KP_IntAtDpcMSI;
    kpOpenCall->funcEvent = KP_Event;

[Note]
As explained in Section 11.6.2.1, it is the convention of this reference guide to mark the Kernel PlugIn callback functions as KP_<functionality> — e.g., KP_Open. However, you are free to select any name that you wish for your Kernel PlugIn callback functions, apart from KP_Init. The generated DriverWizard Kernel PlugIn code, for example, uses the selected driver name in the callback function names (e.g., for a <MyKP> driver it creates callbacks named KP_MyKP_Open, KP_MyKP_Call, etc.).

B.8.1. KP_Init

Purpose

Kernel PlugIn initialization function.
This function is called when the Kernel PlugIn driver is loaded.
The function sets the name of the Kernel PlugIn driver and the KP_Open callback function(s) [B.8.2].

Prototype
BOOL __cdecl KP_Init(KP_INIT *kpInit);
Parameters
NameTypeInput/Output
kpInitKP_INIT*Output
Description
NameDescription
kpInit Pointer to a pre-allocated Kernel PlugIn initialization information structure [B.9.4], whose fields should be updated by the function
Return Value

TRUE if successful. Otherwise FALSE.

Remarks

You must define the KP_Init function in your code in order to link the Kernel PlugIn driver to WinDriver. KP_Init is called when the driver is loaded. Any code that you want to execute upon loading should be in this function.

Example
BOOL __cdecl KP_Init(KP_INIT *kpInit)
{
    /* Check if the version of the WinDriver Kernel
       PlugIn library is the same version
       as windrvr.h and wd_kp.h */
    if (kpInit->dwVerWD != WD_VER)
    {
        /* You need to recompile your Kernel PlugIn
           with the compatible version of the WinDriver
           Kernel PlugIn library, windrvr.h and wd_kp.h */
       return FALSE;
    }

    kpInit->funcOpen = KP_Open;
    kpInit->funcOpen_32_64 = KP_PCI_VIRT_Open_32_64;
    strcpy (kpInit->cDriverName, "KPDriver"); /* Up to 12 chars */

  return TRUE;
}

B.8.2. KP_Open

Purpose

Kernel PlugIn open function.
This function sets the rest of the Kernel PlugIn callback functions (KP_Call [B.8.4], KP_IntEnable [B.8.6], etc.) and performs any other desired initialization (such as allocating memory for the driver context and filling it with data passed from the user mode).
The returned driver context (*ppDrvContext) will be passed to rest of the Kernel PlugIn callback functions.

The KP_Open callback is called when the WD_KernelPlugInOpen() function (see the WinDriver PCI Low-Level API Reference) is called from the user mode — either directly (when using the low-level WinDriver API [B.2]), or via a call to a high-level WDC function.
WD_KernelPlugInOpen() is called from the WDC_KernelPlugInOpen() [B.3.22], and from the WDC_xxxDeviceOpen() functions (PCI [B.3.17] / ISA [B.3.18]) when they are called with the name of a valid Kernel PlugIn driver (set in the pcKPDriverName parameter).

[Note]
The WDC_xxxDeviceOpen() functions cannot be used to open a handle to a 64-bit Kernel PlugIn function from a 32-bit application. For this purpose, use WDC_KernelPlugInOpen() (or the low-level WD_KernelPlugInOpen() function).

The Kernel PlugIn driver can implement two types of KP_Open callback functions —

  • A "standard" Kernel PlugIn open function, which is used whenever a user-mode application opens a handle to a Kernel PlugIn driver, except when a 32-bit applications opens a handle to a 64-bit driver.
    This callback function is set in the funcOpen field of the KP_INIT structure [B.9.4] that is passed as a parameter to KP_Init [B.8.1].
  • A function that will be used when a 32-bit user-mode application opens a handle to a 64-bit Kernel PlugIn driver.
    This callback function is set in the funcOpen_32_64 field of the KP_INIT structure [B.9.4] that is passed as a parameter to KP_Init [B.8.1].

A Kernel PlugIn driver can provide either one or both of these KP_Open callbacks, depending on the target configuration(s).

[Note]
The KP_PCI sample (WinDriver/samples/pci_diag/kp_pci/kp_pci.c) implements both types of KP_Open callbacks — KP_PCI_Open() (standard) and KP_PCI_Open_32_64() (for opening a handle to a 64-bit Kernel PlugIn from a 32-bit application).
The generated DriverWizard Kernel PlugIn code always implements a standard Kernel PlugIn open function — KP_XXX_Open(). When selecting the 32-bit application for a 64-bit Kernel PlugIn DriverWizard code-generation option (see Figure 4.10), the wizard also implements a KP_XXX_Open_32_64() function, for opening a handle to a 64-bit Kernel PlugIn driver from a 32-bit application.
Prototype
BOOL __cdecl KP_Open(
    KP_OPEN_CALL *kpOpenCall,
    HANDLE hWD,
    PVOID pOpenData,
    PVOID *ppDrvContext);
Parameters
NameTypeInput/Output
kpOpenCallKP_OPEN_CALLInput
hWDHANDLEInput
pOpenDataPVOIDInput
ppDrvContextPVOID*Output
Description
NameDescription
kpOpenCall Structure to fill in the addresses of the KP_xxx callback functions [B.9.5]
hWD The WinDriver handle that WD_KernelPlugInOpen() was called with
pOpenDataPointer to data passed from user mode
ppDrvContext Pointer to driver context data with which the KP_Close [B.8.3], KP_Call [B.8.4], KP_IntEnable [B.8.6] and KP_Event [B.8.5] functions will be called. Use this to keep driver-specific information that will be shared among these callbacks.
Return Value

TRUE if successful. If FALSE, the call to WD_KernelPlugInOpen() from the user mode will fail.

Example
BOOL __cdecl KP_Open(KP_OPEN_CALL *kpOpenCall, HANDLE hWD,
            PVOID pOpenData, PVOID *ppDrvContext)
{
    kpOpenCall->funcClose = KP_Close;
    kpOpenCall->funcCall = KP_Call;
    kpOpenCall->funcIntEnable = KP_IntEnable;
    kpOpenCall->funcIntDisable = KP_IntDisable;
    kpOpenCall->funcIntAtIrql = KP_IntAtIrql;
    kpOpenCall->funcIntAtDpc = KP_IntAtDpc;
    kpOpenCall->funcIntAtIrqlMSI = KP_IntAtIrqlMSI;
    kpOpenCall->funcIntAtDpcMSI = KP_IntAtDpcMSI;
    kpOpenCall->funcEvent = KP_Event;

    /* You can allocate driver context memory here: */
    *ppDrvContext = malloc(sizeof(MYDRV_STRUCT));
    return *ppDrvContext!=NULL;
}

B.8.3. KP_Close

Purpose

Called when WD_KernelPlugInClose() (see the WinDriver PCI Low-Level API Reference) is called from user mode.
The high-level WDC_xxxDeviceClose() functions (PCI [B.3.19] / ISA [B.3.20]) automatically call WD_KernelPlugInClose() for devices that contain an open Kernel PlugIn handle (see Section 12.4).

This functions can be used to perform any required clean-up for the Kernel PlugIn (such as freeing memory previously allocated for the driver context, etc.).

Prototype
void __cdecl KP_Close(PVOID pDrvContext);
KP_FUNC_CLOSE Kernel PlugIn callback function type.
Parameters
NameTypeInput/Output
pDrvContextPVOIDInput
Description
NameDescription
pDrvContextDriver context data that was set by KP_Open [B.8.2]
Return Value

None

Example
void __cdecl KP_Close(PVOID pDrvContext)
{
   if (pDrvContext)
        free(pDrvContext); /* Free allocated driver context memory */
}

B.8.4. KP_Call

Purpose

Called when the user-mode application calls WDC_CallKerPlug() [B.3.23] (or the low-level WD_KernelPlugInCall() function — see the WinDriver PCI Low-Level API Reference).

This function is a message handler for your utility functions.

Prototype
void __cdecl KP_Call(
    PVOID pDrvContext,
    WD_KERNEL_PLUGIN_CALL
    *kpCall,
    BOOL fIsKernelMode);
KP_FUNC_CALL Kernel PlugIn callback function type.
Parameters
NameTypeInput/Output
pDrvContextPVOIDInput/Output
kpCallWD_KERNEL_PLUGIN_CALL 
• dwMessageDWORDInput
• pDataPVOIDInput/Output
• dwResultDWORDOutput
fIsKernelModeBOOLInput
Description
NameDescription
pDrvContext Driver context data that was set by KP_Open [B.8.2] and will also be passed to KP_Close [B.8.3], KP_IntEnable [B.8.6] and KP_Event [B.8.5]
kpCall Structure with user-mode information received from the WDC_CallKerPlug() [B.3.23] (or from the low-level WD_KernelPlugInCall() function — see the WinDriver PCI Low-Level API Reference) and/or with information to return back to the user mode [B.9.3]
fIsKernelMode This parameter is passed by the WinDriver kernel — see the fIsKernelMode remark, below.
Return Value

None

Remarks
  • Calling WDC_CallKerPlug() [B.3.23] (or the low-level WD_KernelPlugInCall() function — see the WinDriver PCI Low-Level API Reference) in the user mode will call your KP_Call [B.8.4] callback function in the kernel mode. The KP_Call function in the Kernel PlugIn will determine which routine to execute according to the message passed to it.
  • The fIsKernelMode parameter is passed by the WinDriver kernel to the KP_Call routine. The user is not required to do anything about this parameter. However, notice how this parameter is passed in the sample code to the macro COPY_TO_USER_OR_KERNEL — This is required for the macro to function correctly. Please refer to Section B.8.12 for more details regarding the COPY_TO_USER_OR_KERNEL and COPY_FROM_USER_OR_KERNEL macros.
Example
void __cdecl KP_Call(PVOID pDrvContext,
   WD_KERNEL_PLUGIN_CALL *kpCall, BOOL fIsKernelMode)
{
    kpCall->dwResult = MY_DRV_OK;
    switch (kpCall->dwMessage)
    {
        /* In this sample we implement a GetVersion message */
        case MY_DRV_MSG_VERSION:
            {
              DWORD dwVer = 100;
              MY_DRV_VERSION *ver = (MY_DRV_VERSION *)kpCall->pData;
              COPY_TO_USER_OR_KERNEL(&ver->dwVer, &dwVer,
                  sizeof(DWORD), fIsKernelMode);
              COPY_TO_USER_OR_KERNEL(ver->cVer, "My Driver V1.00",
                  sizeof("My Driver V1.00")+1, fIsKernelMode);

              kpCall->dwResult = MY_DRV_OK;
            }
            break;
        /* You can implement other messages here */
        default:
            kpCall->dwResult = MY_DRV_NO_IMPL_MESSAGE;
    }
}

B.8.5. KP_Event

Purpose

Called when a Plug-and-Play or power management event for the device is received, provided the user-mode application first called WDC_EventRegister() [B.3.52] with fUseKP = TRUE (or the low-level EventRegister() function with a Kernel PlugIn handle — see WinDriver PCI Low-Level API Reference) (see the Remarks below).

Prototype
BOOL __cdecl KP_Event(
    PVOID pDrvContext,
    WD_EVENT *wd_event);
KP_FUNC_EVENT Kernel PlugIn callback function type.
Parameters
NameTypeInput/Output
pDrvContextPVOIDInput/Output
wd_eventWD_EVENT*Input
Description
NameDescription
pDrvContext Driver context data that was set by KP_Open [B.8.2] and will also be passed to KP_Close [B.8.3], KP_IntEnable [B.8.6] and KP_Call [B.8.4]
wd_event Pointer to the PnP/power management event information received from the user mode
Return Value

TRUE in order to notify the user about the event.

Remarks

KP_Event will be called if the user mode process called WDC_EventRegister() [B.3.52] with fUseKP= TRUE (or of the low-level EventRegister() function was called with a Kernel PlugIn handle — see the WinDriver PCI Low-Level API Reference).

Example
BOOL __cdecl KP_Event(PVOID pDrvContext, WD_EVENT *wd_event)
{
    /* Handle the event here */
    return TRUE; /* Return TRUE to notify the user about the event */
}

B.8.6. KP_IntEnable

Purpose

Called when WD_IntEnable() (see WinDriver PCI Low-Level API Reference) is called from the user mode with a Kernel PlugIn handle.
WD_IntEnable() is called automatically from WDC_IntEnable() [B.3.48] and InterruptEnable() (see WinDriver PCI Low-Level API Reference).

The interrupt context that is set by this function (*ppIntContext) will be passed to the rest of the Kernel PlugIn interrupt functions.

Prototype
BOOL __cdecl KP_IntEnable (
    PVOID pDrvContext,
    WD_KERNEL_PLUGIN_CALL *kpCall,
    PVOID *ppIntContext);
KP_FUNC_INT_ENABLE Kernel PlugIn callback function type.
Parameters
NameTypeInput/Output
pDrvContextPVOIDInput/Output
kpCallWD_KERNEL_PLUGIN_CALLInput
• dwMessageDWORDInput
• pDataPVOIDInput/Output
• dwResultDWORDOutput
ppIntContextPVOID*Input/Output
Description
NameDescription
pDrvContext Driver context data that was set by KP_Open [B.8.2] and will also be passed to KP_Close [B.8.3], KP_Call [B.8.4] and KP_Event [B.8.5]
kpCall Structure with information from WD_IntEnable() [B.9.3]
ppIntContext Pointer to interrupt context data that will be passed to KP_IntDisable [B.8.7] and to the Kernel PlugIn interrupt handler functions. Use this context to keep interrupt specific information.
Return Value

Returns TRUE if enable is successful; otherwise returns FALSE.

Remarks

This function should contain any initialization needed for your Kernel PlugIn interrupt handling.

Example
BOOL __cdecl KP_IntEnable(PVOID pDrvContext,
     WD_KERNEL_PLUGIN_CALL *kpCall, PVOID *ppIntContext)
{
    DWORD *pIntCount;
    /* You can allocate specific memory for each interrupt
       in *ppIntContext */
    *ppIntContext = malloc(sizeof (DWORD));
    if (!*ppIntContext)
        return FALSE;
    /* In this sample the information is a DWORD used to
       count the incoming interrupts */
    pIntCount = (DWORD *) *ppIntContext;
    *pIntCount = 0; /* Reset the count to zero */
    return TRUE;
}

B.8.7. KP_IntDisable

Purpose

Called when WD_IntDisable() (see WinDriver PCI Low-Level API Reference) is called from the user mode for interrupts that were enabled in the Kernel PlugIn.
WD_IntDisable() is called automatically from WDC_IntDisable() [B.3.49] and InterruptDisable() (see WinDriver PCI Low-Level API Reference).

This function should free any memory that was allocated in KP_IntEnable [B.8.6].

Prototype
void __cdecl KP_IntDisable(PVOID pIntContext);
KP_FUNC_INT_DISABLE Kernel PlugIn callback function type.
Parameters
NameTypeInput/Output
pIntContextPVOIDInput
Description
NameDescription
pIntContext Interrupt context data that was set by KP_IntEnable [B.8.6]
Return Value

None

Example
void __cdecl KP_IntDisable(PVOID pIntContext)
{
  /* You can free the interrupt specific memory
     allocated to pIntContext here */
  free(pIntContext);
}

B.8.8. KP_IntAtIrql

Purpose

High-priority legacy interrupt handler routine, which is run at high interrupt request level. This function is called upon the arrival of a legacy interrupt that has been enabled using a Kernel PlugIn driver — see the description of WDC_IntEnable() [B.3.48] or the low-level InterruptEnable() and WD_IntEnable() functions (see WinDriver PCI Low-Level API Reference).

Prototype
BOOL __cdecl KP_IntAtIrql(
    PVOID pIntContext,
    BOOL *pfIsMyInterrupt);
KP_FUNC_INT_AT_IRQL Kernel PlugIn callback function type.
Parameters
NameTypeInput/Output
pIntContextPVOIDInput/Output
pfIsMyInterruptBOOL*Output
Description
NameDescription
pIntContext Pointer to interrupt context data that was set by KP_IntEnable [B.8.6] and will also be passed to KP_IntAtDpc [B.8.9] (if executed) and KP_IntDisable [B.8.7]
pfIsMyInterrupt Set *pfIsMyInterrupt to TRUE if the interrupt belongs to this driver; otherwise set it to FALSE in order to enable the interrupt service routines of other drivers for the same interrupt to be called
Return Value

TRUE if deferred interrupt processing (DPC) is required; otherwise FALSE.

Remarks
  • Code running at IRQL will only be interrupted by higher priority interrupts.
  • Code running at high IRQL is limited in the following ways:
    • It may only access non-pageable memory.
    • It may only call the following functions (or wrapper functions that call these functions):
      • WDC_xxx() read/write address or configuration space functions.
      • WDC_MultiTransfer() [B.3.30], or the low-level WD_Transfer(), WD_MultiTransfer(), or WD_DebugAdd() functions (see the WinDriver PCI Low-Level API Reference).
      • Specific kernel OS functions (such as WDK functions) that can be called from high interrupt request level. Note that the use of such functions may break the code's portability to other operating systems.
    • It may not call malloc(), free(), or any WDC_xxx or WD_xxx API other than those listed above.
  • The code performed at high interrupt request level should be minimal (e.g., only the code that acknowledges level-sensitive interrupts), since it is operating at a high priority. The rest of your code should be written in KP_IntAtDpc [B.8.9], which runs at the deferred DISPATCH level and is not subject to the above restrictions.
Example
BOOL __cdecl KP_IntAtIrql(PVOID pIntContext,
    BOOL *pfIsMyInterrupt)
{
    DWORD *pdwIntCount = (DWORD*)pIntContext;

    /* Check your hardware here to see if the interrupt belongs to you.
       If it does, you must set *pfIsMyInterrupt to TRUE.
       Otherwise, set *pfIsMyInterrupt to FALSE. */
    *pfIsMyInterrupt = FALSE;

    /* In this example we will schedule a DPC once in every 5 interrupts */
    (*pdwIntCount) ++;
    if (*pdwIntCount==5)
    {
        *pdwIntCount = 0;
        return TRUE;
    }

    return FALSE;
}

B.8.9. KP_IntAtDpc

Purpose

Deferred processing legacy interrupt handler routine.
This function is called once the high-priority legacy interrupt handling is completed, provided that KP_IntAtIrql [B.8.8] returned TRUE.

Prototype
DWORD __cdecl KP_IntAtDpc(
    PVOID pIntContext,
    DWORD dwCount);
KP_FUNC_INT_AT_DPC Kernel PlugIn callback function type.
Parameters
NameTypeInput/Output
pIntContextPVOIDInput/Output
dwCountDWORDInput
Description
NameDescription
pIntContext Interrupt context data that was set by KP_IntEnable [B.8.6], passed to KP_IntAtIrql [B.8.8], and will be passed to KP_IntDisable [B.8.7]
dwCount The number of times KP_IntAtIrql [B.8.8] returned TRUE since the last DPC call. If dwCount is 1, KP_IntAtIrql requested a DPC only once since the last DPC call. If the value is greater than 1, KP_IntAtIrql has already requested a DPC a few times, but the interval was too short, therefore KP_IntAtDpc was not called for each DPC request.
Return Value

Returns the number of times to notify user mode (i.e., return from WD_IntWait() — see the WinDriver PCI Low-Level API Reference).

Remarks
  • Most of the interrupt handling should be implemented within this function, as opposed to the high-priority KP_IntAtIrql [B.8.8] interrupt handler.
  • If KP_IntAtDpc returns with a value greater than zero, WD_IntWait() returns and the user-mode interrupt handler will be called in the amount of times set in the return value of KP_IntAtDpc. If you do not want the user-mode interrupt handler to execute, KP_IntAtDpc should return zero.
Example
DWORD __cdecl KP_IntAtDpc(PVOID pIntContext, DWORD dwCount)
{
  /* Return WD_IntWait as many times as KP_IntAtIrql
     scheduled KP_IntAtDpc */
  return dwCount;
}

B.8.10. KP_IntAtIrqlMSI

Purpose

High-priority Message-Signaled Interrupts (MSI) / Extended Message-Signaled Interrupts (MSI-X) handler routine, which is run at high interrupt request level. This function is called upon the arrival of an MSI/MSI-X that has been enabled using a Kernel PlugIn — see the description of WDC_IntEnable() [B.3.48] or the low-level InterruptEnable() and WD_IntEnable() functions (see WinDriver PCI Low-Level API Reference).

Prototype
BOOL __cdecl KP_PCI_IntAtIrqlMSI(
    PVOID pIntContext,
    ULONG dwLastMessage,
    DWORD dwReserved);
KP_FUNC_INT_AT_IRQL_MSI Kernel PlugIn callback function type.
Parameters
NameTypeInput/Output
pIntContextPVOIDInput/Output
dwLastMessageDWORDInput
dwReservedDWORDInput
Description
NameDescription
pIntContext Pointer to interrupt context data that was set by KP_IntEnable [B.8.6] and will also be passed to KP_IntAtDpcMSI [B.8.11] (if executed) and KP_IntDisable [B.8.7]
dwLastMessage The message data for the last received interrupt (applicable only on Windows 7 and higher)
dwReservedReserved for future use. Do not use this parameter.
Return Value

TRUE if deferred MSI/MSI-X processing (DPC) is required; otherwise FALSE.

Remarks
  • Code running at IRQL will only be interrupted by higher priority interrupts.
  • Code running at high IRQL is limited in the following ways:
    • It may only access non-pageable memory.
    • It may only call the following functions (or wrapper functions that call these functions):
      • WDC_xxx() read/write address or configuration space functions.
      • WDC_MultiTransfer() [B.3.30], or the low-level WD_Transfer(), WD_MultiTransfer(), or WD_DebugAdd() functions (see the WinDriver PCI Low-Level API Reference).
      • Specific kernel OS functions (such as WDK functions) that can be called from high interrupt request level. Note that the use of such functions may break the code's portability to other operating systems.
    • It may not call malloc(), free(), or any WDC_xxx or WD_xxx API other than those listed above.
  • The code performed at high interrupt request level should be minimal, since it is operating at a high priority. The rest of your code should be written in KP_IntAtDpcMSI [B.8.11], which runs at the deferred DISPATCH level and is not subject to the above restrictions.
Example
BOOL __cdecl KP_PCI_IntAtIrqlMSI(PVOID pIntContext,
    ULONG dwLastMessage, DWORD dwReserved)
{
    return TRUE;
}

B.8.11. KP_IntAtDpcMSI

Purpose

Deferred processing Message-Signaled Interrupts (MSI) / Extended Message-Signaled Interrupts (MSI-X) handler routine.
This function is called once the high-priority MSI/MSI-X handling is completed, provided that KP_IntAtIrqlMSI [B.8.10] returned TRUE.

Prototype
DWORD __cdecl KP_IntAtDpcMSI(
  PVOID pIntContext,
  DWORD dwCount,
  ULONG dwLastMessage,
  DWORD dwReserved);
KP_FUNC_INT_AT_DPC_MSI Kernel PlugIn callback function type.
Parameters
NameTypeInput/Output
pIntContextPVOIDInput/Output
dwCountDWORDInput
dwLastMessageDWORDInput
dwReservedDWORDInput
Description
NameDescription
pIntContext Interrupt context data that was set by KP_IntEnable [B.8.6], passed to KP_IntAtIrqlMSI [B.8.10], and will be passed to KP_IntDisable [B.8.7]
dwCount The number of times KP_IntAtIrqlMSI [B.8.10] returned TRUE since the last DPC call. If dwCount is 1, KP_IntAtIrqlMSI requested a DPC only once since the last DPC call. If the value is greater than 1, KP_IntAtIrqlMSI has already requested a DPC a few times, but the interval was too short, therefore KP_IntAtDpcMSI was not called for each DPC request.
dwLastMessage The message data for the last received interrupt (applicable only on Windows 7 and higher)
dwReservedReserved for future use. Do not use this parameter.
Return Value

Returns the number of times to notify user mode (i.e., return from WD_IntWait() — see the WinDriver PCI Low-Level API Reference).

Remarks
  • Most of the MSI/MSI-X handling should be implemented within this function, as opposed to the high-priority KP_IntAtIrqlMSI [B.8.10] interrupt handler.
  • If KP_IntAtDpcMSI returns with a value greater than zero, WD_IntWait() returns and the user-mode interrupt handler will be called in the amount of times set in the return value of KP_IntAtDpcMSI. If you do not want the user-mode interrupt handler to execute, KP_IntAtDpcMSI should return zero.
Example
DWORD __cdecl KP_IntAtDpcMSI(PVOID pIntContext, DWORD dwCount,
  ULONG dwLastMessage, DWORD dwReserved)
{
  /* Return WD_IntWait as many times as KP_IntAtIrqlMSI
     scheduled KP_IntAtDpcMSI */
  return dwCount;
}

B.8.12. COPY_TO_USER_OR_KERNEL, COPY_FROM_USER_OR_KERNEL

Purpose

Macros for copying data from the user mode to the Kernel PlugIn and vice versa.

Remarks
  • The COPY_TO_USER_OR_KERNEL and COPY_FROM_USER_OR_KERNEL are macros used for copying data (when necessary) to/from user-mode memory addresses (respectively), when accessing such addresses from within the Kernel PlugIn. Copying the data ensures that the user-mode address can be used correctly, even if the context of the user-mode process changes in the midst of the I/O operation. This is particularly relevant for long operations, during which the context of the user-mode process may change. The use of macros to perform the copy provides a generic solution for all supported operating systems.
  • Note that if you wish to access the user-mode data from within the Kernel PlugIn interrupt handler functions, you should first copy the data into some variable in the Kernel PlugIn before the execution of the kernel-mode interrupt handler routines.
  • The COPY_TO_USER_OR_KERNEL and COPY_FROM_USER_OR_KERNEL macros are defined in the WinDriver\include\kpstdlib.h header file.
  • For an example of using the COPY_TO_USER_OR_KERNEL macro, see the KP_Call [B.8.4] implementation (KP_PCI_Call()) in the sample WinDriver/samples/pci_diag/kp_pci/kp_pci.c Kernel PlugIn file.
  • To safely share a data buffer between the user-mode and Kernel PlugIn routines (e.g., KP_IntAtIrql [B.8.8] and KP_IntAtDpc [B.8.9]), consider using the technique outlined in the technical document titled "How do I share a memory buffer between Kernel PlugIn and user-mode projects for DMA or other purposes?" found under the "Kernel PlugIn" technical documents section of the "Support" section.

B.8.13. Kernel PlugIn Synchronization APIs

This section describes the Kernel Plug-In synchronization APIs.
These APIs support the following synchronization mechanisms:

  • Spinlocks [B.8.13.2B.8.13.5], which are used to synchronize between threads on a single or multiple CPU system.
    [Note]
    The Kernel PlugIn spinlock functions can be called from any context apart from high interrupt request level. Hence, they can be called from any Kernel PlugIn function except for KP_IntAtIrql [B.8.8] and KP_IntAtIrqlMSI [B.8.10].
    Note that the spinlock functions can be called from the deferred processing interrupt handler functions — KP_IntAtDpc [B.8.9] and KP_IntAtDpcMSI [B.8.11].
  • Interlocked operations [B.8.13.6B.8.13.7], which are used for synchronizing access to a variable that is shared by multiple threads by performing complex operations on the variable in an atomic manner.
    [Note]
    The Kernel PlugIn interlocked functions can be called from any context in the Kernel PlugIn, including from high interrupt request level. Hence, they can be called from any Kernel PlugIn function, including the Kernel PlugIn interrupt handler functions.

B.8.13.1. Kernel PlugIn Synchronization Types

The Kernel PlugIn synchronization APIs use the following types:

  • KP_SPINLOCK — A Kernel PlugIn spinlock object structure:

    typedef struct _KP_SPINLOCK KP_SPINLOCK;

    _KP_SPINLOCK is an internal WinDriver spinlock object structure, opaque to the user.

  • KP_INTERLOCKED — a Kernel PlugIn interlocked operations counter:
    typedef volatile int KP_INTERLOCKED;

B.8.13.2. kp_spinlock_init()

Purpose

Initializes a new Kernel PlugIn spinlock object.

Prototype
KP_SPINLOCK * kp_spinlock_init(void);
Return Value

If successful, returns a pointer to the new Kernel PlugIn spinlock object [B.8.13.1], otherwise returns NULL.

B.8.13.3. kp_spinlock_wait()

Purpose

Waits on a Kernel PlugIn spinlock object.

Prototype
void kp_spinlock_wait(KP_SPINLOCK *spinlock);
Parameters
NameTypeInput/Output
spinlockKP_SPINLOCK*Input
Description
NameDescription
spinlock Pointer to the Kernel PlugIn spinlock object [B.8.13.1] on which to wait
Return Value

None

B.8.13.4. kp_spinlock_release()

Purpose

Releases a Kernel PlugIn spinlock object.

Prototype
void kp_spinlock_release(KP_SPINLOCK *spinlock);
Parameters
NameTypeInput/Output
spinlockKP_SPINLOCK*Input
Description
NameDescription
spinlock Pointer to the Kernel PlugIn spinlock object [B.8.13.1] to release
Return Value

None

B.8.13.5. kp_spinlock_uninit()

Purpose

Uninitializes a Kernel PlugIn spinlock object.

Prototype
void kp_spinlock_uninit(KP_SPINLOCK *spinlock);
Parameters
NameTypeInput/Output
spinlockKP_SPINLOCK*Input
Description
NameDescription
spinlock Pointer to the Kernel PlugIn spinlock object [B.8.13.1] to uninitialize
Return Value

None

B.8.13.6. kp_interlocked_init()

Purpose

Initializes a Kernel PlugIn interlocked counter.

Prototype
void kp_interlocked_init(KP_INTERLOCKED *target);
Parameters
NameTypeInput/Output
targetKP_INTERLOCKED*Input/Output
Description
NameDescription
target Pointer to the Kernel PlugIn interlocked counter [B.8.13.1] to initialize
Return Value

None

B.8.13.7. kp_interlocked_uninit()

Purpose

Uninitializes a Kernel PlugIn interlocked counter.

Prototype
void kp_interlocked_uninit(KP_INTERLOCKED *target);
Parameters
NameTypeInput/Output
targetKP_INTERLOCKED*Input/Output
Description
NameDescription
target Pointer to the Kernel PlugIn interlocked counter [B.8.13.1] to uninitialize
Return Value

None

B.8.13.8. kp_interlocked_increment()

Purpose

Increments the value of a Kernel PlugIn interlocked counter by one.

Prototype
int kp_interlocked_increment(KP_INTERLOCKED *target);
Parameters
NameTypeInput/Output
targetKP_INTERLOCKED*Input/Output
Description
NameDescription
target Pointer to the Kernel PlugIn interlocked counter [B.8.13.1] to increment
Return Value

Returns the new value of the interlocked counter (target).

B.8.13.9. kp_interlocked_decrement()

Purpose

Decrements the value of a Kernel PlugIn interlocked counter by one.

Prototype
int kp_interlocked_decrement(KP_INTERLOCKED *target);
Parameters
NameTypeInput/Output
targetKP_INTERLOCKED*Input/Output
Description
NameDescription
target Pointer to the Kernel PlugIn interlocked counter [B.8.13.1] to decrement
Return Value

Returns the new value of the interlocked counter (target).

B.8.13.10. kp_interlocked_add()

Purpose

Adds a specified value to the current value of a Kernel PlugIn interlocked counter.

Prototype
int kp_interlocked_add(
    KP_INTERLOCKED *target,
    int val);
Parameters
NameTypeInput/Output
targetKP_INTERLOCKED*Input/Output
valvalInput
Description
NameDescription
target Pointer to the Kernel PlugIn interlocked counter [B.8.13.1] to which to add
val The value to add to the interlocked counter (target)
Return Value

Returns the new value of the interlocked counter (target).

B.8.13.11. kp_interlocked_read()

Purpose

Reads to the value of a Kernel PlugIn interlocked counter.

Prototype
int kp_interlocked_read(KP_INTERLOCKED *target);
Parameters
NameTypeInput/Output
targetKP_INTERLOCKED*Input
Description
NameDescription
target Pointer to the Kernel PlugIn interlocked counter [B.8.13.1] to read
Return Value

Returns the value of the interlocked counter (target).

B.8.13.12. kp_interlocked_set()

Purpose

Sets the value of a Kernel PlugIn interlocked counter to the specified value.

Prototype
void kp_interlocked_set(
    KP_INTERLOCKED *target,
    int val);
Parameters
NameTypeInput/Output
targetKP_INTERLOCKED*Input/Output
valvalInput
Description
NameDescription
target Pointer to the Kernel PlugIn interlocked counter [B.8.13.1] to set
val The value to set for the interlocked counter (target)
Return Value

None

B.8.13.13. kp_interlocked_exchange()

Purpose

Sets the value of a Kernel PlugIn interlocked counter to the specified value and returns the previous value of the counter.

Prototype
int kp_interlocked_exchange(
    KP_INTERLOCKED *target,
    int val);
Parameters
NameTypeInput/Output
targetKP_INTERLOCKED*Input/Output
valvalInput
Description
NameDescription
target Pointer to the Kernel PlugIn interlocked counter [B.8.13.1] to exchange
val The new value to set for the interlocked counter (target)
Return Value

Returns the previous value of the interlocked counter (target).