//*****************************************************************************
//* FILE NAME:       w2k_drv.c
//* FILE CONTAINS:
//* DESCRIPTION:     Win 2000 Driver provides standard PnP, PM and WMI support
//*                  for Win 2000.
//* NOTES:           This file is used for Win 2000 only.
//*****************************************************************************
//*                   INTEL CORPORATION PROPRIETARY INFORMATION
//*     This software is supplied under the terms of a license agreement or
//*     nondisclosure agreement with Intel Corporation and may not be copied
//*     or disclosed except in accordance with the terms of that agreement.
//*           Copyright (c) 1995-2000 Intel Corporation.  All Rights Reserved.
//*****************************************************************************

//*****************************************************************************
//* INCLUDES
//*****************************************************************************

//-----------------------------------------------------------------------------
// Local Includes
//-----------------------------------------------------------------------------

#define D_InitInterfaceClassGUID

#include "CompDiag.h"
#include "w2k_drv.h"
#include "w2k_pnp.h"
#include "w2k_pm.h"
#include "w2k_wmi.h"

#define  D_DiagMsgInit
#include "DiagMsg.h"

//*****************************************************************************
//* DEFINES
//*****************************************************************************

//-----------------------------------------------------------------------------
// Thread Priority Boost - Used for "IoCompleteRequest(...)".
//-----------------------------------------------------------------------------

#define D_ThreadPriorityBoost(a) a

//-----------------------------------------------------------------------------
// Microsoft Win 2000
//
// "Current Version" string identified in registry by Microsoft.  Used when
// determining if OS platform is Win 2000 or NT 4.
//-----------------------------------------------------------------------------

#define D_MicrosoftWin2000 "5.0"
#define D_MicrosoftWinNT   "4.0"

//-----------------------------------------------------------------------------
// Microsoft Win NT Current Version
//
// "Current Version" string identified in registry by Microsoft.  Used when
// determining if OS platform is Win 2000 or NT 4.
//-----------------------------------------------------------------------------

#define D_MicrosoftWinNT_CurrentVersion   L"CurrentVersion"

//-----------------------------------------------------------------------------
// OS Versoin Key Name
//
// Key in registry under the subkey for a specific driver, and specifies that
// the driver was installed on a Win 2000 OS platform.  This key is intended
// to be used during boot-time when the "Current Version" registry key is not
// available.
//-----------------------------------------------------------------------------

#define D_OS_VersionKeyName           L"OSVersion"

//-----------------------------------------------------------------------------
// Key values for key that created at driver subkey level to specify OS upon
// which the driver was installed.
//-----------------------------------------------------------------------------

#define D_IsWin2000                 (unsigned char)2
#define D_IsWinNT40                 (unsigned char)1
#define D_IsUnspecified             (unsigned char)0

//-----------------------------------------------------------------------------
// Registry Key Value Length
//
// Used to spec the number of bytes of a registry key being written by
// "RtlWriteRegistryValue(...)"
//-----------------------------------------------------------------------------

#define D_RegistryKeyValueLength(n) n

//-----------------------------------------------------------------------------
// Configuration registry key.
//-----------------------------------------------------------------------------

#define D_ConfigurationKeyName   L"Configuration"

//-----------------------------------------------------------------------------
// Diagnostics Code registry key.
//-----------------------------------------------------------------------------

#define D_DiagCodeKeyName   L"DiagCode"

//-----------------------------------------------------------------------------
// Debug level registry key.
//-----------------------------------------------------------------------------

#define D_DebugLevelKeyName      L"DebugLevel"

//*****************************************************************************
//* VARIABLES
//*****************************************************************************

//-----------------------------------------------------------------------------
// Saved Registry Subkey
//
// Used as the "base name" for WMI instances.
//-----------------------------------------------------------------------------

WCHAR *gwcpSavedRegistrySubkey = NULL;

//-----------------------------------------------------------------------------
// Saved Registry Path
//
// Used to save the registry path provided to "DriverEntry(...)" and to be
// used at some later time.
//
// NOTE: This is also used as the "base name" for WMI instances.
//-----------------------------------------------------------------------------

UNICODE_STRING gtSavedRegistryPath;

//-----------------------------------------------------------------------------
// Maximum Number of Devices
//
// This is the maximum number of devices that can be added via
// "stAddDevice(...)".  It is specified by the "DriverEntry(...)".
//-----------------------------------------------------------------------------

static unsigned suMaxNumberOfDevices = 0;

//-----------------------------------------------------------------------------
// Number of Devices Added
//
// This is the current number of devices that have been added thus far by
// "stAddDevice(...)".
//-----------------------------------------------------------------------------

static unsigned suNumberOfDevicesAdded = 0;

//-----------------------------------------------------------------------------
// Driver Configuration
//
// Used to save the driver configuration bits as read from the registry.
//-----------------------------------------------------------------------------

unsigned long gulDriverConfiguration = E_DrvConf_NULL;

//-----------------------------------------------------------------------------
// WMI GUID Registry Information
//-----------------------------------------------------------------------------

static WMIGUIDREGINFO *stp_WMIGUIDREGINFO = NULL;
static unsigned        suNumWMI_GUIDs = 0;

//*****************************************************************************
//* PROTOTYPES
//*****************************************************************************

static BOOL sbGetDiagCode(IN short *ashpDriverRegistrySubkey);

static BOOL sbCompareANSI_ToUnicode
  (
   char           *acpString,
   UNICODE_STRING *tpUNICODE_STRING,
   BOOL           *abpIsEqual
   );

static NTSTATUS stAddDevice
  (
   IN PDRIVER_OBJECT atpDriverObj, 
   IN PDEVICE_OBJECT atpPDO
   );

static NTSTATUS stDispatch_WDM_MJ
  (
   IN PDEVICE_OBJECT atpFDO,
   IN PIRP           atpIRP
   );

static BOOL sbDetermineIfWin2000
  (
   BOOL       *abpIsWin2000,
   IN short   *ashpDriverRegistrySubkey
   );

static BOOL gbGetConfiguration
  (
   unsigned long *aulpDriverConfiguration,
   IN short      *ashpDriverRegistrySubkey
   );

//-----------------------------------------------------------------------------
// Get Key Value
//
// Get the value for the specified key.
//
// Arguments:
//
//    Key Value - Receives the value for the sub key.
//
//    Driver Registry Subkey - Specifies the subkey for the driver that was or
//    is being loaded.
//
//    Key Name - Specifies actual registry key.
//-----------------------------------------------------------------------------

static BOOL sbGetKeyValue
  (
   void          *avpKeyValue,
   short         *ashpDriverRegistrySubkey,
   short         *ashpKeyName
   );

//-----------------------------------------------------------------------------
// Set Installed Key
//
// Set the key to specified value.
//
// Arguments:
//
//    Key Value - Specifies the value to be set for the sub key.
//
//    Driver Registry Subkey - Specifies the subkey for the driver that was or
//    is being loaded.
//-----------------------------------------------------------------------------

static BOOL sbSetInstalledKey
  (
   unsigned char  aucKeyValue,
   short         *ashpDriverRegistrySubkey,
   short         *ashpKeyName
   );

//*****************************************************************************
//* FUNCTIONS
//*****************************************************************************

//=============================================================================
// gbNewDriverConfiguration(...)
//-----------------------------------------------------------------------------
// If new driver configuration, then configure for new style driver as a
// function of the "Configuration" registry key?
//=============================================================================

BOOL gbNewDriverConfiguration
  (
   IN PDRIVER_OBJECT       atpDriverObject,
   IN PUNICODE_STRING      atpRegistryPath,
   IN short               *ashpDriverRegistrySubkey,
   unsigned                auMaxNumberOfDevices,
   IN CONST GUID          *atpInterfaceClassGUID,
   WMIGUIDREGINFO         *atp_WMIGUIDREGINFO,
   const unsigned          acuNumWMI_GUIDs
   )
  {
   BOOL bIsWin2000 = FALSE;

   D_DiagMsg1(D_Info,D_DC_MidLevelInfo,"Registry Path: %T",atpRegistryPath);

   gwcpSavedRegistrySubkey = ashpDriverRegistrySubkey;
   suMaxNumberOfDevices    = auMaxNumberOfDevices;
   gtpInterfaceClassGUID   = atpInterfaceClassGUID;

   //--------------------------------------------------------------------------
   // Get the diagnostics code from the registry.
   //--------------------------------------------------------------------------

   if (!sbGetDiagCode(ashpDriverRegistrySubkey))
      D_DiagMsg0(D_Error,D_DC_Errors,"Can't get diagnostics code from the registry");

   //--------------------------------------------------------------------------
   // Save the registry path for possible use later.
   //--------------------------------------------------------------------------

   gtSavedRegistryPath.Length          = 0;
   gtSavedRegistryPath.MaximumLength   = atpRegistryPath->Length;

   gtSavedRegistryPath.Buffer          = ExAllocatePool( NonPagedPool,
                                                         atpRegistryPath->Length+2
                                                         );

   if (!gtSavedRegistryPath.Buffer)
      D_DiagMsg0(D_Error,D_DC_Errors,"Unable to save registry path");

   RtlCopyUnicodeString(&gtSavedRegistryPath,atpRegistryPath);

   //-----------------------------------------------------------------------
   //
   //-----------------------------------------------------------------------

   if (!gbGetConfiguration(&gulDriverConfiguration,ashpDriverRegistrySubkey))
     {
      D_DiagMsg0(D_Error,D_DC_Errors,"Can't get driver configuration");
      return (FALSE);
      }

   //-----------------------------------------------------------------------
   // If interface GUID not specified nor is driver configured for legacy
   // interface, then there is no interface for the device, so force legacy.
   //-----------------------------------------------------------------------

   if (!gtpInterfaceClassGUID
       &&
       (gulDriverConfiguration & E_DrvConf_DisableLegacyInterface)
       )
     {
      D_DiagMsg0(D_Warn,D_DC_Warnings,"GUID i/f not speced and config to disable legacy, so force legacy");
      gulDriverConfiguration &= ~E_DrvConf_DisableLegacyInterface;
      return (FALSE);
      }

   //-----------------------------------------------------------------------
   // Configured as legacy driver, so nothing left to do.
   //-----------------------------------------------------------------------

   if (!(gulDriverConfiguration & E_DrvConf_Win2000Driver)
       &&
       (gulDriverConfiguration & E_DrvConf_DisableLegacyInterface)
       )
     {
      D_DiagMsg0(D_Warn,D_DC_Warnings,"Configured as legacy driver, buy legacy i/f not specified, so force legacy");
      gulDriverConfiguration &= ~E_DrvConf_DisableLegacyInterface;
      return (FALSE);
      }

   //-----------------------------------------------------------------------
   // If Win 2000, setup Win 2000 entry points for WDM dispatch functions.
   //-----------------------------------------------------------------------

   if (!sbDetermineIfWin2000(&bIsWin2000,ashpDriverRegistrySubkey))
     {
      D_DiagMsg0( D_Warn,D_DC_Warnings,"Unable to determine if platform OS is Win 2000"
                  " via \"gbDetermineIfWin2000(...)\""
                  );

      if (!(gulDriverConfiguration & E_DrvConf_DefaultToWin2000))
        {
         D_DiagMsg0(D_Warn,D_DC_Warnings,"Configuration is set to default to NT 4.0");
         return (FALSE);
         }

      D_DiagMsg0(D_Warn,D_DC_Warnings,"Configuration is set to default to Win 2000 (NT 5.0)");
      }

   if (!bIsWin2000)
      return (FALSE);

   //-----------------------------------------------------------------------
   // Setup Win 2000 entry points for WDM dispatch functions.
   //-----------------------------------------------------------------------

   D_DiagMsg0(D_Info,D_DC_Win2000LowLevelInfo,"Setup Win 2000 Major Functions and entry points");

   atpDriverObject->MajorFunction[IRP_MJ_PNP] = stDispatch_WDM_MJ;

   atpDriverObject->MajorFunction[IRP_MJ_POWER] = stDispatch_WDM_MJ;

   //-----------------------------------------------------------------------
   // WMI interface enabled?  Check if any GUID info speced and save GUID
   // info for later use.
   //-----------------------------------------------------------------------

   if (gulDriverConfiguration & E_DrvConf_WMI_Interface)
     {
      D_DiagMsg0(D_Info,D_DC_MidLevelInfo,"WMI interface enabled to replace IOCTL");

      if (!atp_WMIGUIDREGINFO)
         D_DiagMsg0(D_Warn,D_DC_Warnings,"No WMI GUIDs specified");

      stp_WMIGUIDREGINFO   = atp_WMIGUIDREGINFO;
      suNumWMI_GUIDs       = acuNumWMI_GUIDs;

      atpDriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = stDispatch_WDM_MJ;
      }

   atpDriverObject->DriverExtension->AddDevice = stAddDevice;

   return (TRUE);
   }

//=============================================================================
// sbDetermineIfWin2000(...)
//=============================================================================
// Determine If Win 2000?
//
// Determine if the run-time OS platform is Win 2000.
//
// Arguments:
//
//    Is Win 2000 - Receives TRUE or FALSE depending on whether OS is Win 2000
//    or NT 4 respectively.
//
//    Driver Registry Subkey - Specifies the registry subkey key name for the
//    particular driver being loaded.  This is used so that a key that is
//    specific to the driver can be establish to maintain specific info for
//    the driver in the future.
//
// Returns:
//
//    TRUE if it was possible to determine if OS was Win 2000 or not;
//    otherwise, FALSE indicates an error and thus unable to determine OS.
//
//=============================================================================

static BOOL sbDetermineIfWin2000
  (
   BOOL       *abpIsWin2000,
   IN short   *ashpDriverRegistrySubkey
   )
  {
   NTSTATUS       tStatus           = STATUS_SUCCESS;
   BOOL           bStatus           = TRUE;
   unsigned char  ucRegistryValue   = D_IsUnspecified;
   BOOL           bIsEqual          = FALSE;

   RTL_QUERY_REGISTRY_TABLE   taQueryTable[2];
   UNICODE_STRING             tUNICODE_STRING_CurrentVersion;

   //--------------------------------------------------------------------------
   // NOTE:
   //
   //    Apparently the product-name registry key as accessed via the registry
   //    path, "RTL_REGISTRY_WINDOWS_NT", is not accessable when this driver
   //    is being loaded boot-time, i.e. "RtlQueryRegistryValues(...)" returns
   //    "STATUS_OBJECT_NAME_NOT_FOUND".  So a registry local key is used to
   //    record the platform OS name when the driver is initially installed
   //    which is not at boot-time and thus the aforementioned product-name
   //    registry key is accessable.
   //
   //--------------------------------------------------------------------------

   if (sbGetKeyValue(&ucRegistryValue,ashpDriverRegistrySubkey,D_OS_VersionKeyName))
     { 
      if (ucRegistryValue == D_IsWinNT40)
        { 
         D_DiagMsg0(D_Info,D_DC_HighLevelInfo,"Already installed as Win NT 4.0");
         *abpIsWin2000 = FALSE;
         return (TRUE);
         }

      if (ucRegistryValue == D_IsWin2000)
        { 
         D_DiagMsg0(D_Info,D_DC_HighLevelInfo,"Already installed as Win 2000 (NT 5.0)");
         *abpIsWin2000 = TRUE;
         return (TRUE);
         }
      }

   //--------------------------------------------------------------------------
   // Query the registry value of the current version of the MS OS platform.
   // And, decode the "RtlQueryRegistryValues(...)" return value to determine
   // if the value was read successfully.
   //--------------------------------------------------------------------------

   tUNICODE_STRING_CurrentVersion.Buffer        = NULL;
   tUNICODE_STRING_CurrentVersion.MaximumLength = 0;
   tUNICODE_STRING_CurrentVersion.Length        = 0;

   RtlZeroMemory(&taQueryTable[0], sizeof(taQueryTable));

   taQueryTable[0].Flags           = RTL_QUERY_REGISTRY_DIRECT;
   taQueryTable[0].Name            = D_MicrosoftWinNT_CurrentVersion;
   taQueryTable[0].EntryContext    = &tUNICODE_STRING_CurrentVersion;
   taQueryTable[0].DefaultType     = REG_SZ;

   tStatus = RtlQueryRegistryValues(   RTL_REGISTRY_WINDOWS_NT,
                                       NULL,
                                       taQueryTable,
                                       NULL,
                                       NULL
                                       );

   switch (tStatus)
     {
      case STATUS_SUCCESS:

         D_DiagMsg1( D_Info,D_DC_Win2000LowLevelInfo,"Current Version: %S",
                     tUNICODE_STRING_CurrentVersion.Buffer
                     );

         //--------------------------------------------------------------------
         // If this is a Win 2000 OS platform, create a special key for this
         // driver that specifies that the OS platform is Win 2000; otherwise,
         // indicate that the platform is not Win 2000.
         //--------------------------------------------------------------------

         if (!sbCompareANSI_ToUnicode( D_MicrosoftWin2000,
                                      &tUNICODE_STRING_CurrentVersion,
                                      &bIsEqual
                                      )
             )
           {
            D_DiagMsg0(D_Error,D_DC_Errors,"Unable to compare Win 2000 ANSI string with UNICODE");
            bStatus = FALSE;
            break;
            }

         if (bIsEqual)
           {
            D_DiagMsg0(D_Info,D_DC_HighLevelInfo,"This is a Win 2000 (NT 5.0) platform");
            *abpIsWin2000 = TRUE;

            if (!sbSetInstalledKey(D_IsWin2000,ashpDriverRegistrySubkey,D_OS_VersionKeyName))
              {
               D_DiagMsg0(D_Error,D_DC_Errors,"Unable to set \"installed\" key value");
               bStatus = FALSE;
               }

            break;
            }

         bIsEqual = FALSE;

         if (!sbCompareANSI_ToUnicode( D_MicrosoftWinNT,
                                      &tUNICODE_STRING_CurrentVersion,
                                      &bIsEqual
                                      )
             )
           {
            D_DiagMsg0(D_Error,D_DC_Errors,"Unable to compare Win NT ANSI string with UNICODE");
            bStatus = FALSE;
            break;
            }

         if (bIsEqual)
           {
            D_DiagMsg0(D_Info,D_DC_HighLevelInfo,"This is a Win NT 4.0 platform");
            *abpIsWin2000 = FALSE;

            if (!sbSetInstalledKey(D_IsWinNT40,ashpDriverRegistrySubkey,D_OS_VersionKeyName))
              {
               D_DiagMsg0(D_Error,D_DC_Errors,"Unable to set \"installed\" key value");
               bStatus = FALSE;
               }

            break;
            }

         D_DiagMsg0(D_Error,D_DC_Errors,"Unable to determine if Win 2000 nor Win NT");
         bStatus = FALSE;

         break;

      case STATUS_INVALID_PARAMETER:

         D_DiagMsg0(D_Error,D_DC_Errors,"Invalid registry parameter");
         bStatus = FALSE;
         break;

      case STATUS_OBJECT_NAME_NOT_FOUND:

         D_DiagMsg0(D_Error,D_DC_Errors,"Registry object not found");
         bStatus = FALSE;
         break;

      default:

         D_DiagMsg1( D_Error,D_DC_Errors,"Unable to get registry value via "
                     "\"RtlQueryRegistryValues(...)\",%u",tStatus
                     );
         bStatus = FALSE;
         break;
      }

   //--------------------------------------------------------------------------
   // Free, as applicable, space that was allocated by
   // "RtlQueryRegistryValues(...)" to hold the roduct name registry value.
   //--------------------------------------------------------------------------

   if (tUNICODE_STRING_CurrentVersion.Buffer)
      ExFreePool(tUNICODE_STRING_CurrentVersion.Buffer);
   
   return (bStatus);
   }
      
//=============================================================================
// sbCompareANSI_ToUnicode(...)
//=============================================================================

static BOOL sbCompareANSI_ToUnicode
  (
   char           *acpString,
   UNICODE_STRING *tpUNICODE_STRING,
   BOOL           *abpIsEqual
   )
  { 
   UNICODE_STRING    t_UNICODE_STRING;
   ANSI_STRING       t_ANSI_STRING;

   t_ANSI_STRING.Length = sizeof(acpString) - 1;        // Less NULL term

   t_ANSI_STRING.MaximumLength = t_ANSI_STRING.Length;

   t_ANSI_STRING.Buffer = acpString;

   if (RtlAnsiStringToUnicodeString(   &t_UNICODE_STRING,
                                       &t_ANSI_STRING,
                                       TRUE
                                       )
       != STATUS_SUCCESS
       )
     {
      D_DiagMsg0(D_Error,D_DC_Errors,"Unable to convert ANSI string to UNICODE");
      return (FALSE);
      }

   *abpIsEqual = RtlEqualUnicodeString(   tpUNICODE_STRING,
                                         &t_UNICODE_STRING,
                                          TRUE
                                          );

   RtlFreeUnicodeString(&t_UNICODE_STRING);

   return (TRUE);
   }

//*****************************************************************************
//* Don't compile the following unless building for Win 2000.
//*****************************************************************************

//=============================================================================
// stAddDevice(...)
//-----------------------------------------------------------------------------
// Add Device
//
// Called by PnP Manager when a new device has been detected and thus a new
// Physical Device Object (PDO) has been created and, via INF registration,
// this driver is associated.
//
// NOTE:
//
//    * It is assumed that function is not being call in the same context as
//      "DriverEntry(...)".
//
//    * Non-PnP IRPs cannot be sent to the given driver stack, until
//      IRP_MN_START_DEVICE has been received and processed.
//
//    * This function should only be called once because, at present, there is
//      only one device.
//=============================================================================

static NTSTATUS stAddDevice
  (
   IN PDRIVER_OBJECT atpDriverObj, 
   IN PDEVICE_OBJECT atpPDO
   )
  {
   NTSTATUS                   tStatus        = STATUS_SUCCESS;
   PDEVICE_OBJECT             tpFuncDevObj   = NULL;
   struct S_Win2000_DevExt   *stpFDO_DevExt  = NULL;

   D_DiagMsg0(D_Info,D_DC_HighLevelInfo,"\"stAddDevice(...)\"");

   //--------------------------------------------------------------------------
   // If a maximum has been specified and the current number of devices added
   // thus far has exceeded the maximum, do not add another device and return
   // error; othrewise, increment the current number of devices added for this
   // driver.
   //--------------------------------------------------------------------------

   if (   suMaxNumberOfDevices
       && (suNumberOfDevicesAdded >= suMaxNumberOfDevices)
       )
     { 
      D_DiagMsg1( D_Error,D_DC_Errors,"Attempt to add too many (%u) devices",
                  suNumberOfDevicesAdded+1
                  );

      return (STATUS_UNSUCCESSFUL);
      }
   else
      suNumberOfDevicesAdded++;

   //--------------------------------------------------------------------------
   // Initialize the driver.  If successfully initialized, a FDO is returned.
   //--------------------------------------------------------------------------

   tStatus = gtInitializeDriver(atpDriverObj,&tpFuncDevObj);

   if (!tpFuncDevObj)
     { 
      D_DiagMsg0(D_Error,D_DC_Errors,"FDO not created via \"gtInitializeDriver(...)\"");

      return (STATUS_UNSUCCESSFUL);
      }

   //--------------------------------------------------------------------------
   // Init the PnP state to "device not present".
   //--------------------------------------------------------------------------

   stpFDO_DevExt = gspGetWin2000_DevExt(tpFuncDevObj);

   stpFDO_DevExt->ePnP_DeviceState = E_PnP_State_DeviceNotPresent;

   stpFDO_DevExt->tpDriverObj       = atpDriverObj;
   stpFDO_DevExt->vp_WMI_Storage    = NULL;
   stpFDO_DevExt->tpPDO             = atpPDO;
   stpFDO_DevExt->bPaused           = FALSE;
   stpFDO_DevExt->bDeviceConnected  = FALSE;
   stpFDO_DevExt->bStopping         = FALSE;
   stpFDO_DevExt->bRemoveDevice     = FALSE;
	stpFDO_DevExt->lOpenHandleCount  = 0;
	stpFDO_DevExt->lPendingIO_Count  = 1;

//	KeInitializeEvent(&stpFDO_DevExt->t_KEVENT_Stopping,NotificationEvent,FALSE);
	
   //--------------------------------------------------------------------------
   // If interface class was specified, register and enable device interface.
   //--------------------------------------------------------------------------

   if (gtpInterfaceClassGUID)
     {
      D_DiagMsg0(D_Info,D_DC_Win2000LowLevelInfo,"Register interface GUID");

   	tStatus = IoRegisterDeviceInterface(   atpPDO,
                                             gtpInterfaceClassGUID,
                                             NULL,
                                             &stpFDO_DevExt->tInterfaceSymLinkName
                                             );

      	if (!NT_SUCCESS(tStatus))
   	  {
         D_DiagMsg0( D_Error,D_DC_Errors,"Unable to register device interface"
                     " via \"IoRegisterDeviceInterface(...)\""
                     );

   	   D_DiagMsg1( D_Info,D_DC_MidLevelInfo,"Symbolic Link Name is \"%T\"",
                     &stpFDO_DevExt->tInterfaceSymLinkName
                     );

		   IoDeleteDevice(tpFuncDevObj);
         tpFuncDevObj = NULL;
		   return (tStatus);
	      }

      }

   //--------------------------------------------------------------------------
   // Register WMI GUIDs?
   //--------------------------------------------------------------------------

   if (stp_WMIGUIDREGINFO)
      gbRegisterWMI(tpFuncDevObj,stp_WMIGUIDREGINFO,suNumWMI_GUIDs);

   //--------------------------------------------------------------------------
   // Attach FDO to underlying PDO.  If not successful, "unload" the driver.
   //--------------------------------------------------------------------------

   D_DiagMsg0(D_Info,D_DC_Win2000LowLevelInfo,"Attach FDO to PDO");

   stpFDO_DevExt->tpLowerDeviceObject
      = IoAttachDeviceToDeviceStack(  tpFuncDevObj,
                                      atpPDO
                                      );

   if (!stpFDO_DevExt->tpLowerDeviceObject)
     {
      D_DiagMsg0( D_Error,D_DC_Errors,"Unable to attach FDO to PDO"
                  " via \"IoAttachDeviceToDeviceStack(...)\""
                  );

      (*atpDriverObj->DriverUnload)(atpDriverObj);
      return(STATUS_UNSUCCESSFUL);
      }

   //--------------------------------------------------------------------------
   //  Clear the "device initializing" bit since the Device Object was created
   //  outside of "DriverEntry(...)".
   //--------------------------------------------------------------------------

   tpFuncDevObj->Flags &= ~DO_DEVICE_INITIALIZING;

   //--------------------------------------------------------------------------
   // Specify PnP state is now "stopped and awaiting resources".
   //--------------------------------------------------------------------------

   stpFDO_DevExt->ePnP_DeviceState
      = E_PnP_State_Stopped;

   return (tStatus);
   }

//=============================================================================
// stDispatch_WDM_MJ(...)
//-----------------------------------------------------------------------------
// Dispatch WDM Major Function 
//
// Called by Win 2000 OS in kernel mode to dispatch WDM IRP Major Functions.
//=============================================================================

static NTSTATUS stDispatch_WDM_MJ
  (
   IN PDEVICE_OBJECT atpFDO,
   IN PIRP           atpIRP
   )
  {
   PIO_STACK_LOCATION   tIRP_CurStackLoc;
   UCHAR                ucMajorFuncCode, ucMinorFuncCode;
   NTSTATUS             tStatus = STATUS_SUCCESS;

   struct S_Win2000_DevExt *stpFDO_DevExt = gspGetWin2000_DevExt(atpFDO);

   if (!atpFDO)
      D_DiagMsg0(D_Error,D_DC_Errors,"FDO is NULL");

// D_DiagMsg1(D_Info,D_DC_Win2000LowLevelInfo,"FDO=0x%x",atpFDO); 

   //--------------------------------------------------------------------------
   // Get access to actual IRP.
   //--------------------------------------------------------------------------

   tIRP_CurStackLoc = IoGetCurrentIrpStackLocation(atpIRP);

   ucMajorFuncCode = tIRP_CurStackLoc->MajorFunction;
   ucMinorFuncCode = tIRP_CurStackLoc->MinorFunction;

   D_DiagMsg2(D_Info,D_DC_Win2000LowLevelInfo,"stDispatch_WDM_MJ: ucMajorFuncCode=0x%x, ucMinorFuncCode=0x%x", 
            (ULONG)ucMajorFuncCode,
            (ULONG)ucMinorFuncCode
            ); 

   //--------------------------------------------------------------------------
   // Decode the IRP's major function code.
   //--------------------------------------------------------------------------

   switch (ucMajorFuncCode)
     {
      //-----------------------------------------------------------------------
      // PnP IRP.
      //-----------------------------------------------------------------------

      case IRP_MJ_PNP:

         tStatus = gtDecodePnP_IRP(ucMinorFuncCode,stpFDO_DevExt,atpIRP);

         break;

      //-----------------------------------------------------------------------
      // PM IRP.
      //-----------------------------------------------------------------------

      case IRP_MJ_POWER:

         tStatus = gtDecodePM_IRP(ucMinorFuncCode,stpFDO_DevExt,atpIRP);

         break;

      //-----------------------------------------------------------------------
      // WMI IRP.
      //-----------------------------------------------------------------------

      case IRP_MJ_SYSTEM_CONTROL:

       if (!(gulDriverConfiguration & E_DrvConf_WMI_Interface))
           {
            D_DiagMsg0(D_Error,D_DC_Errors,"WMI IRP received but WMI disabled");
            atpIRP->IoStatus.Status       = STATUS_UNSUCCESSFUL;
            atpIRP->IoStatus.Information  = 0;
            tStatus                       = STATUS_UNSUCCESSFUL;

            IoCompleteRequest(atpIRP,D_ThreadPriorityBoost(0));
            break;
            }

       tStatus = gtDecodeWMI_IRP(atpFDO,atpIRP);

         break;

      //-----------------------------------------------------------------------
      // Fail unsupported IRPs.
      //-----------------------------------------------------------------------

      default:

         D_DiagMsg0(D_Error,D_DC_Errors,"IRP not supported");

         atpIRP->IoStatus.Status       = STATUS_UNSUCCESSFUL;
         atpIRP->IoStatus.Information  = 0;
         tStatus                       = STATUS_UNSUCCESSFUL;

         IoCompleteRequest(atpIRP,D_ThreadPriorityBoost(0));
         break;
      }

   //--------------------------------------------------------------------------
   // Did device get removed as a function of processing an IRP?
   //--------------------------------------------------------------------------

   if (stpFDO_DevExt->bRemoveDevice)
     {
      D_DiagMsg0(D_Info,D_DC_Win2000LowLevelInfo,"Removing device");

      //-----------------------------------------------------------------------
      // If to few devices are being removed, do not remove this and return
      // error; othrewise, decrement the current number of devices added for this
      // driver.
      //-----------------------------------------------------------------------

      if (!suNumberOfDevicesAdded)
        { 
         D_DiagMsg0(D_Error,D_DC_Errors,"Attempt to remove more devices than were added");

         return (STATUS_UNSUCCESSFUL);
         }

      suNumberOfDevicesAdded--;

      //-----------------------------------------------------------------------
      // If interface class GUID was specified, then disable interface;
      // otherwise assume legacy interface.
      //-----------------------------------------------------------------------

      if (gtpInterfaceClassGUID)
        {
         D_DiagMsg0(D_Info,D_DC_Win2000LowLevelInfo,"Disabling GUID interface");
         IoSetDeviceInterfaceState(&stpFDO_DevExt->tInterfaceSymLinkName,FALSE);
	      RtlFreeUnicodeString(&stpFDO_DevExt->tInterfaceSymLinkName);
         }

      //-----------------------------------------------------------------------
      // Deregister WMI GUIDs?
      //-----------------------------------------------------------------------

      if (stp_WMIGUIDREGINFO)
         gbDeregisterWMI(atpFDO);

      //-----------------------------------------------------------------------
      // Detach lower device object.
      //-----------------------------------------------------------------------

      if (stpFDO_DevExt->tpLowerDeviceObject)
        {
         D_DiagMsg0(D_Info,D_DC_Win2000LowLevelInfo,"Detaching FDO from PDO");
         IoDetachDevice(stpFDO_DevExt->tpLowerDeviceObject);
         stpFDO_DevExt->tpLowerDeviceObject = NULL;
         }

      stpFDO_DevExt->bRemoveDevice = FALSE;   // Indicate removed

      D_DiagMsg1( D_Info,D_DC_MidLevelInfo,"Device being removed, %d devices will remain",
                  suNumberOfDevicesAdded
                  );

      //-----------------------------------------------------------------------
      // If not configured to support legacy interface, then delete FDO;
      // otherwise, deleting FDO causes BSOD.
      //-----------------------------------------------------------------------

      gvPrepareToUnload(stpFDO_DevExt->tpDriverObj);
      D_DiagMsg0(D_Info,D_DC_Win2000LowLevelInfo,"Deleting FDO");
      IoDeleteDevice(atpFDO);
      atpFDO = NULL;

      //-----------------------------------------------------------------------
      // Free saved registry path.
      //-----------------------------------------------------------------------

      if (gtSavedRegistryPath.Buffer)
         ExFreePool(gtSavedRegistryPath.Buffer);
      }

   return(tStatus);
   }

//=============================================================================
// sbGetKeyValue(...)
//=============================================================================

static BOOL sbGetKeyValue
  (
   void          *avpKeyValue,
   short         *ashpDriverRegistrySubkey,
   short         *ashpKeyName
   )
  {
   RTL_QUERY_REGISTRY_TABLE   taQueryTable[2];
   NTSTATUS                   tStatus = STATUS_SUCCESS;

   //--------------------------------------------------------------------------
   // Query registry value of of specified key.
   //--------------------------------------------------------------------------

   RtlZeroMemory(&taQueryTable[0], sizeof(taQueryTable));

   taQueryTable[0].Flags           = RTL_QUERY_REGISTRY_DIRECT;
   taQueryTable[0].Name            = ashpKeyName;
   taQueryTable[0].EntryContext    = avpKeyValue;

   tStatus = RtlQueryRegistryValues(   RTL_REGISTRY_SERVICES,
                                       ashpDriverRegistrySubkey,
                                       taQueryTable,
                                       NULL,
                                       NULL
                                       );

   switch (tStatus)
     {
      case STATUS_SUCCESS:
         return (TRUE);

      case STATUS_INVALID_PARAMETER:

         D_DiagMsg0(D_Error,D_DC_Errors,"Invalid registry parameter");
         break;
      
      case STATUS_OBJECT_NAME_NOT_FOUND:

         D_DiagMsg0(D_Error,D_DC_Errors,"Registry object not found");
         break;

      default:

         D_DiagMsg1( D_Error,D_DC_Errors,"Unable to get registry value via "
                     "\"RtlQueryRegistryValues(...)\",%u",tStatus
                     );
         break;
      }

   return (FALSE);
   } 

//=============================================================================
// sbSetInstalledKey(...)
//=============================================================================

static BOOL sbSetInstalledKey
  (
   unsigned char  aucpKeyValue,
   short         *ashpDriverRegistrySubkey,
   short         *ashpKeyName
   )
  {
   NTSTATUS tStatus = STATUS_SUCCESS;

   tStatus = RtlWriteRegistryValue( RTL_REGISTRY_SERVICES,
                                    ashpDriverRegistrySubkey,
                                    ashpKeyName,
                                    REG_BINARY,
                                    (PVOID)&aucpKeyValue,
                                    D_RegistryKeyValueLength(1)
                                    );

   if (tStatus != STATUS_SUCCESS)
     { 
      D_DiagMsg0( D_Error,D_DC_Errors,"Unable to write registry value via "
                  "\"RtlWriteRegistryValue(...)\""
                  );
      return (FALSE);
      }

   return (TRUE);
   }

//=============================================================================
// gbGetConfiguration(...)
//-----------------------------------------------------------------------------
// Get "Configuration" for driver from registry.
//=============================================================================

static BOOL gbGetConfiguration
  (
   unsigned long *aulpDriverConfiguration,
   IN short      *ashpDriverRegistrySubkey
   )
  {
   RTL_QUERY_REGISTRY_TABLE   taQueryTable[2];

   *aulpDriverConfiguration = E_DrvConf_NULL;

   if (!sbGetKeyValue(  aulpDriverConfiguration,
                        ashpDriverRegistrySubkey,
                        D_ConfigurationKeyName)
                        )
     { 
      D_DiagMsg0( D_Error,D_DC_Errors,"Unable to get driver configuration via "
                  "\"sbGetKeyValue(...)\""
                  );

      return (FALSE);
      }

   D_DiagMsg1(D_Info,D_DC_Win2000LowLevelInfo,"Driver Configuration is 0x%x",*aulpDriverConfiguration);

   return (TRUE);
   }

//=============================================================================
// sbGetDiagCode(...)
//-----------------------------------------------------------------------------
// Get "DiagCode" from registry.
//=============================================================================

static BOOL sbGetDiagCode(IN short *ashpDriverRegistrySubkey)
  {
   RTL_QUERY_REGISTRY_TABLE   taQueryTable[2];

   if (!sbGetKeyValue(  &gulDiagCode,
                        ashpDriverRegistrySubkey,
                        D_DiagCodeKeyName)
                        )
     {
      D_DiagMsg0( D_Error,D_DC_Errors,"Unable to get diagnostics code via "
                  "\"sbGetKeyValue(...)\""
                  );

      return (FALSE);
      }

   gbDiagCodeSet = TRUE;

   D_DiagMsg1(D_Info,D_DC_Win2000LowLevelInfo,"Diagnostics code is 0x%x",gulDiagCode);

   return (TRUE);
   }

//=============================================================================
// (...)
//-----------------------------------------------------------------------------
//
//=============================================================================

NTSTATUS gtCreateFile(IN PDEVICE_OBJECT atpFDO,IN PIRP atpIRP)
  {
   D_DiagMsg0(D_Info,D_DC_HighLevelInfo,"\"gtCreateFile(...)\"");
/*
   PIO_STACK_LOCATION         tIRP_CurStackLoc  = NULL;
   struct S_Win2000_DevExt   *stpFDO_DevExt     = NULL;

   stpFDO_DevExt = gspGetWin2000_DevExt(atpFDO);

	tIRP_CurStackLoc = IoGetCurrentIrpStackLocation(atpIRP);

   D_DiagMsg1(D_Info,D_DC_MidLevelInfo,"Create file: %T",tIRP_CurStackLoc->FileObject->FileName);

	if(!stpFDO_DevExt->bDeviceConnected)
     {
      D_DiagMsg0(D_Error,D_DC_Errors,"Device not connected");
		return (STATUS_DEVICE_NOT_CONNECTED);
      }

	InterlockedIncrement(&stpFDO_DevExt->lOpenHandleCount);
*/
	return (STATUS_SUCCESS);
   }

//=============================================================================
// (...)
//-----------------------------------------------------------------------------
//
//=============================================================================

NTSTATUS gtCloseFile(IN PDEVICE_OBJECT atpFDO,IN PIRP atpIRP)
  {
   D_DiagMsg0(D_Info,D_DC_HighLevelInfo,"\"gtCloseFile(...)\"");

/*
   PIO_STACK_LOCATION         tIRP_CurStackLoc  = NULL;
   struct S_Win2000_DevExt   *stpFDO_DevExt     = NULL;

   stpFDO_DevExt = gspGetWin2000_DevExt(atpFDO);

	tIRP_CurStackLoc = IoGetCurrentIrpStackLocation(atpIRP);

   D_DiagMsg0(D_Info,D_DC_MidLevelInfo,"Close file");

	if(!stpFDO_DevExt->bDeviceConnected)
      D_DiagMsg0(D_Info,D_DC_MidLevelInfo,"Device never connected");

	InterlockedDecrement(&stpFDO_DevExt->lOpenHandleCount);
*/
	return (STATUS_SUCCESS);
   }

//=============================================================================
// (...)
//-----------------------------------------------------------------------------
//
//=============================================================================

NTSTATUS gtLockDevice(IN PDEVICE_OBJECT atpFDO,IN PIRP atpIRP)
  {
/*
   struct S_Win2000_DevExt *stpFDO_DevExt = NULL;

   stpFDO_DevExt = gspGetWin2000_DevExt(atpFDO);

	if(!stpFDO_DevExt->bDeviceConnected)
     {
      D_DiagMsg0(D_Error,D_DC_Errors,"Device not connected");
		return (STATUS_DEVICE_NOT_CONNECTED);
      }

	InterlockedIncrement(&stpFDO_DevExt->lPendingIO_Count);

	if(stpFDO_DevExt->bStopping)
	  {
		if(InterlockedDecrement(&stpFDO_DevExt->lPendingIO_Count) == 0)
        {
         D_DiagMsg0(D_Info,D_DC_Win2000LowLevelInfo,"Set \"t_KEVENT_Stopping\"");
//			KeSetEvent(&stpFDO_DevExt->t_KEVENT_Stopping,0,FALSE);
         }

		return (STATUS_DELETE_PENDING);
	   }
*/
	return (STATUS_SUCCESS);
   }

//=============================================================================
// (...)
//-----------------------------------------------------------------------------
//
//=============================================================================

NTSTATUS gtUnlockDevice(IN PDEVICE_OBJECT atpFDO,IN PIRP atpIRP)
  {
/*
   struct S_Win2000_DevExt  *stpFDO_DevExt = NULL;

   stpFDO_DevExt = gspGetWin2000_DevExt(atpFDO);

	if (InterlockedDecrement(&stpFDO_DevExt->lPendingIO_Count) == 0)
     {
      D_DiagMsg0(D_Info,D_DC_Win2000LowLevelInfo,"Set \"t_KEVENT_Stopping\"");
//      KeSetEvent(&stpFDO_DevExt->t_KEVENT_Stopping,0,FALSE);
      }
*/
	return (STATUS_SUCCESS);
   }

//=============================================================================
// (...)
//-----------------------------------------------------------------------------
//
//=============================================================================
/*
NTSTATUS gtStopDevice(struct S_Win2000_DevExt *astpFDO_DevExt)
  {
   NTSTATUS tStatus = STATUS_SUCCESS;

   D_DiagMsg0(D_Info,D_DC_MidLevelInfo,"Stop device");

	astpFDO_DevExt->bDeviceConnected = FALSE;

	if(!astpFDO_DevExt->bHaveResources)                 // Already stopped?
   	return (STATUS_SUCCESS);

	astpFDO_DevExt->bStopping = TRUE;

//   KeResetEvent(&stpFDO_DevExt->t_KEVENT_Stopping,0,FALSE);

   if (   (gtUnlockDevice(IN PDEVICE_OBJECT atpFDO,IN PIRP atpIRP) != STATUS_SUCCESS)
       || (gtUnlockDevice(IN PDEVICE_OBJECT atpFDO,IN PIRP atpIRP) != STATUS_SUCCESS)
       )
     {
      D_DiagMsg0(D_Warn,D_DC_Warnings,"Can't stop device, continuing");
      tStatus = STATUS_UNSUCCESSFUL;
      }

	tStatus = KeWaitForSingleObject( &astpFDO_DevExt->t_KEVENT_Stopping,
                                    Executive,KernelMode,
                                    FALSE,
                                    NULL
                                    );

   if (tStatus != STATUS_SUCCESS)
      D_DiagMsg0(D_Warn,D_DC_Warnings,"Can't stop device, continuing");

   D_DiagMsg0(D_Info,D_DC_Win2000LowLevelInfo,"Pending I/O complete");

	astpFDO_DevExt->bStopping = FALSE;

	if(astpFDO_DevExt->bHaveResources)
	   astpFDO_DevExt->bHaveResources = FALSE;

   if (   (gtLockDevice(IN PDEVICE_OBJECT atpFDO,IN PIRP atpIRP) != STATUS_SUCCESS)
       || (gtLockDevice(IN PDEVICE_OBJECT atpFDO,IN PIRP atpIRP) != STATUS_SUCCESS)
       )
     {
      D_DiagMsg0(D_Warn,D_DC_Warnings,"Can't stop device, continuing");
      tStatus = STATUS_UNSUCCESSFUL;
      }

   return (tStatus);
   }
*/