B.6  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.6.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 sets the name of the driver and the KP_Open function.

KP_Open 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]
It is the convention of this reference guide to mark the Kernel PlugIn callback functions as KP_XXX – i.e. KP_Open, KP_Call, etc. However, you are free to select any name that you wish for your Kernel PlugIn callback functions, apart from KP_Init, provided you implement relevant callback functions in your Kernel PlugIn. The generated DriverWizard Kernel PlugIn code, for example, uses the selected driver name in the callback function names (e.g. for a <MyKP> driver: KP_MyKP_Open, KP_MyKP_Call, etc.).

B.6.1  KP_Init()

PURPOSE
• Called when the Kernel PlugIn driver is loaded.
Sets the name of the Kernel PlugIn driver and the KP_Open [B.6.2] callback function.
PROTOTYPE
BOOL __cdecl KP_Init(KP_INIT *kpInit);
PARAMETERS
NameTypeInput/Output
kpInitKP_INIT* 
• dwVerWDDWORDOutput
• cDriverNameCHAR[12]Output
• funcOpenKP_FUNC_OPENOutput
DESCRIPTION
NameDescription
kpInitPointer to a Kernel PlugIn initialization information structure [B.7.4]
• dwVerWDThe version of the WinDriver Kernel PlugIn library
• cDriverNameThe device driver name (up to 12 characters)
• funcOpenThe KP_Open callback function, which will be executed when WD_KernelPlugInOpen (see the WinDriver PCI Low-Level API Reference) is called. WD_KernelPlugInOpen is called from the WDC_xxxDeviceOpen functions (PCI [B.3.10] / PCMCIA [B.3.11] / ISA [B.3.12]) when these functions are called with a valid Kernel PlugIn driver (set in the pcKPDriverName parameter).
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 re-compile 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;
  strcpy (kpInit->cDriverName, "KPDriver");  /* Up to 12 chars */

  return TRUE;
}

B.6.2  KP_Open()

PURPOSE
• Called when WD_KernelPlugInOpen (see the WinDriver PCI Low-Level API Reference) is called from user mode.
WD_KernelPlugInOpen is automatically called from the WDC_xxxDeviceOpen functions (PCI [B.3.10] / PCMCIA [B.3.11] / ISA [B.3.12]) when these functions are called with a valid Kernel PlugIn driver (set in the pcKPDriverName parameter).

This function sets the rest of the Kernel PlugIn callback functions (KP_Call [B.6.4], KP_IntEnable [B.6.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, etc.).
The returned driver context (*ppDrvContext) will be passed to rest of the Kernel PlugIn callback functions.

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
kpOpenCallStructure to fill in the addresses of the KP_xxx callback functions [B.7.5]
hWDThe WinDriver handle that WD_KernelPlugInOpen was called with
pOpenDataPointer to data passed from user mode
ppDrvContextPointer to driver context data with which the KP_Close [B.6.3], KP_Call [B.6.4], KP_IntEnable [B.6.6] and KP_Event [B.6.5] functions will be called. Use this to keep driver specific information, which 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.6.3  KP_Close()

PURPOSE
• Called when WD_KernelPlugInClose (see the WinDriver PCI Low-Level API Reference) is called from user mode.
For devices that have been opened with a Kernel PlugIn driver – i.e. WDC_xxxDeviceOpen (PCI [B.3.10] / PCMCIA [B.3.11] / ISA [B.3.12]) was called with a valid Kernel PlugIn driver (set in the pcKPDriverName parameter) – the WDC_xxxDeviceClose functions (PCI [B.3.13] / PCMCIA [B.3.14] / ISA [B.3.15]) automatically call WD_KernelPlugInClose in order to close the handle to the Kernel PlugIn driver.

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.6.2]
RETURN VALUE
None
EXAMPLE
void __cdecl KP_Close(PVOID pDrvContext)
{
   if (pDrvContext)
        free(pDrvContext); /* Free allocated driver context memory */
}

B.6.4  KP_Call()

PURPOSE
• Called when the user-mode application calls WDC_CallKerPlug [B.3.18] (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
pDrvContextDriver context data that was set by KP_Open [B.6.2] and will also be passed to KP_Close [B.6.3], KP_IntEnable [B.6.6] and KP_Event [B.6.5]
kpCallStructure with user-mode information received from the WDC_CallKerPlug [B.3.18] (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.7.3]
fIsKernelModeThis parameter is passed by the WinDriver kernel – see remark 2, below.
RETURN VALUE
None
REMARKS
  1. Calling WDC_CallKerPlug [B.3.18] (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.6.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.
  2. 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.6.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.6.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.49] 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
pDrvContextDriver context data that was set by KP_Open [B.6.2] and will also be passed to KP_Close [B.6.3], KP_IntEnable [B.6.6] and KP_Call [B.6.4]
wd_eventPointer 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.49] 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.6.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.46] 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
pDrvContextDriver context data that was set by KP_Open [B.6.2] and will also be passed to KP_Close [B.6.3], KP_Call [B.6.4] and KP_Event [B.6.5]
kpCallStructure with information from WD_IntEnable [B.7.3]
ppIntContextPointer to interrupt context data that will be passed to KP_IntDisable [B.6.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.6.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.47] and InterruptDisable (see WinDriver PCI Low-Level API Reference). • This function should free any memory that was allocated in KP_IntEnable [B.6.6].
PROTOTYPE
void __cdecl KP_IntDisable(PVOID pIntContext);
KP_FUNC_INT_DISABLE Kernel PlugIn callback function type.
PARAMETERS
NameTypeInput/Output
pIntContextPVOIDInput
DESCRIPTION
NameDescription
pIntContextInterrupt context data that was set by KP_IntEnable [B.6.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.6.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.46] 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
pIntContextPointer to interrupt context data that was set by KP_IntEnable [B.6.6] and will also be passed to KP_IntAtDpc [B.6.9] (if executed) and KP_IntDisable [B.6.7]
pfIsMyInterruptSet *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.25], 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.6.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.6.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.6.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
pIntContextInterrupt context data that was set by KP_IntEnable [B.6.6], passed to KP_IntAtIrql [B.6.8], and will be passed to KP_IntDisable [B.6.7]
dwCountThe number of times KP_IntAtIrql [B.6.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.6.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.6.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.46] 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
pIntContextPointer to interrupt context data that was set by KP_IntEnable [B.6.6] and will also be passed to KP_IntAtDpcMSI [B.6.11] (if executed) and KP_IntDisable [B.6.7]
dwLastMessageThe message data for the last received interrupt (applicable only on Windows Vista 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.25], 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.6.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.6.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.6.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
pIntContextInterrupt context data that was set by KP_IntEnable [B.6.6], passed to KP_IntAtIrqlMSI [B.6.10], and will be passed to KP_IntDisable [B.6.7]
dwCountThe number of times KP_IntAtIrqlMSI [B.6.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.
dwLastMessageThe message data for the last received interrupt (applicable only on Windows Vista 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.6.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.6.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.6.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.6.8] and KP_IntAtDpc [B.6.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.6.13  Kernel PlugIn Synchronization APIs

This section describes the Kernel Plug-In synchronization APIs.
These APIs support the following synchronization mechanisms:
  • Spinlocks [B.6.13.2 – B.6.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.6.8] and KP_IntAtIrqlMSI [B.6.10]. Note that the spinlock functions can be called from the deferred processing interrupt handler functions – KP_IntAtDpc [B.6.9] and KP_IntAtDpcMSI [B.6.11].
  • Interlocked operations [B.6.13.6 – B.6.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.6.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.6.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.6.13.1], otherwise returns NULL.

B.6.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
spinlockPointer to the Kernel PlugIn spinlock object [B.6.13.1] on which to wait
RETURN VALUE
None

B.6.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
spinlockPointer to the Kernel PlugIn spinlock object [B.6.13.1] to release
RETURN VALUE
None

B.6.13.5  kp_spinlock_uninit()

PURPOSE
• Un-initializes a Kernel PlugIn spinlock object.
PROTOTYPE
void kp_spinlock_uninit(KP_SPINLOCK *spinlock);
PARAMETERS
NameTypeInput/Output
spinlockKP_SPINLOCK*Input
DESCRIPTION
NameDescription
spinlockPointer to the Kernel PlugIn spinlock object [B.6.13.1] to un-initialize
RETURN VALUE
None

B.6.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
targetPointer to the Kernel PlugIn interlocked counter [B.6.13.1] to initialize
RETURN VALUE
None

B.6.13.7  kp_interlocked_uninit()

PURPOSE
• Un-initializes a Kernel PlugIn interlocked counter.
PROTOTYPE
void kp_interlocked_uninit(KP_INTERLOCKED *target);
PARAMETERS
NameTypeInput/Output
targetKP_INTERLOCKED*Input/Output
DESCRIPTION
NameDescription
targetPointer to the Kernel PlugIn interlocked counter [B.6.13.1] to un-initialize
RETURN VALUE
None

B.6.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
targetPointer to the Kernel PlugIn interlocked counter [B.6.13.1] to increment
RETURN VALUE
Returns the new value of the interlocked counter (target).

B.6.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
targetPointer to the Kernel PlugIn interlocked counter [B.6.13.1] to decrement
RETURN VALUE
Returns the new value of the interlocked counter (target).

B.6.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
targetPointer to the Kernel PlugIn interlocked counter [B.6.13.1] to which to add
valThe value to add to the interlocked counter (target)
RETURN VALUE
Returns the new value of the interlocked counter (target).

B.6.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
targetPointer to the Kernel PlugIn interlocked counter [B.6.13.1] to read
RETURN VALUE
Returns the value of the interlocked counter (target).

B.6.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
targetPointer to the Kernel PlugIn interlocked counter [B.6.13.1] to set
valThe value to set for the interlocked counter (target)
RETURN VALUE
None

B.6.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
targetPointer to the Kernel PlugIn interlocked counter [B.6.13.1] to exchange
valThe new value to set for the interlocked counter (target)
RETURN VALUE
Returns the previous value of the interlocked counter (target).