//*****************************************************************************
//* FILE NAME:       w2k_wmi.c
//* FILE CONTAINS:
//* DESCRIPTION:     Win 2000 WMI support.
//* 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
//-----------------------------------------------------------------------------

#include "CompDiag.h"
#include "w2k_drv.h"
#include "w2k_wmi.h"
#include "DiagMsg.h"

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

//=============================================================================
// gbRegisterWMI(...)
//=============================================================================

BOOL gbRegisterWMI
  (
   IN PDEVICE_OBJECT atpFDO,
   IN WMIGUIDREGINFO asa_WMIGUIDREGINFO[],
   const unsigned    acuNumWMI_GUIDs
   )
  {
   NTSTATUS t_NTSTATUS = STATUS_SUCCESS;

   struct S_Win2000_DevExt *stpFDO_DevExt = gspGetWin2000_DevExt(atpFDO);

   D_DiagMsg0(D_Info,D_DC_MidLevelInfo,"\"gbSetupWMI(...)\"");

   D_DiagMsg1(D_Info,D_DC_MidLevelInfo,"There are %d WMI GUIDs",acuNumWMI_GUIDs);

	stpFDO_DevExt->t_WMILIB_CONTEXT.GuidCount          = acuNumWMI_GUIDs;
	stpFDO_DevExt->t_WMILIB_CONTEXT.GuidList           = asa_WMIGUIDREGINFO;

	stpFDO_DevExt->t_WMILIB_CONTEXT.QueryWmiRegInfo    = gtQueryWMI_RegInfo;
	stpFDO_DevExt->t_WMILIB_CONTEXT.QueryWmiDataBlock  = gtQueryWMI_DataBlock;
	stpFDO_DevExt->t_WMILIB_CONTEXT.SetWmiDataBlock    = gtSetWMI_DataBlock;
	stpFDO_DevExt->t_WMILIB_CONTEXT.SetWmiDataItem     = gtSetWMI_DataItem;
	stpFDO_DevExt->t_WMILIB_CONTEXT.ExecuteWmiMethod   = gtExecuteWMI_Method;
	stpFDO_DevExt->t_WMILIB_CONTEXT.WmiFunctionControl = gtWMI_FunctionControl;

   //--------------------------------------------------------------------------
   // Register FDO as a provider of WMI information.
   //--------------------------------------------------------------------------

  	t_NTSTATUS = IoWMIRegistrationControl(atpFDO,WMIREG_ACTION_REGISTER);

   if (t_NTSTATUS != STATUS_SUCCESS)
     {
      D_DiagMsg1( D_Error,D_DC_Errors,"Unable to register FDO as WMI provider"
                  " via \"IoWMIRegistrationControl(...)\", %x",
                  t_NTSTATUS
                  );
      
      return (FALSE);
      }

   return (TRUE);
   }

//=============================================================================
// gbDeregisterWMI(...)
//=============================================================================

BOOL gbDeregisterWMI(IN PDEVICE_OBJECT atpFDO)
  {
   NTSTATUS t_NTSTATUS = STATUS_SUCCESS;

   D_DiagMsg0(D_Info,D_DC_MidLevelInfo,"\"gbDeregisterWMI(...)\"");

   //--------------------------------------------------------------------------
   // Deregister FDO as a provider of WMI information.
   //--------------------------------------------------------------------------

  	t_NTSTATUS = IoWMIRegistrationControl(atpFDO,WMIREG_ACTION_DEREGISTER);

   if (t_NTSTATUS != STATUS_SUCCESS)
     {
      D_DiagMsg1( D_Error,D_DC_Errors,"Unable to deregister FDO as WMI provider"
                  " via \"IoWMIRegistrationControl(...)\", %x",
                  t_NTSTATUS
                  );
      
      return (FALSE);
      }

   return (TRUE);
   }

//=============================================================================
// gtDecodeWMI_IRP(...)
//-----------------------------------------------------------------------------
// Decode WMI IRP
//
// Decode the WMI (IRP_MJ_SYSTEM_CONTROL) minor function IRP.  Called when WMI
// IRP is received.
//
// Arguments:
//
//    Minor Function Code - Specifies the specific minor function code.
//
//    Default Win 2000 Device Extension - This contains the info specific to
//    the Win 2000 implementation.
//
//    IRP pointer - This is the IRP to be decoded.
//
// Returns:
//
//    STATUS_SUCCESS if successful.
//=============================================================================

NTSTATUS gtDecodeWMI_IRP
  (
   IN PDEVICE_OBJECT atpFDO,
   IN PIRP           atpIRP
   )
  {
   NTSTATUS                         t_NTSTATUS = STATUS_SUCCESS;
   SYSCTL_IRP_DISPOSITION           tIRP_Disposition = IrpProcessed;

   struct S_Win2000_DevExt *stpFDO_DevExt = gspGetWin2000_DevExt(atpFDO);

   D_DiagMsg0(D_Info,D_DC_Win2000LowLevelInfo,"WMI IRP");

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

   if (gtLockDevice(atpFDO,atpIRP) != STATUS_SUCCESS)
     {
      D_DiagMsg0(D_Error,D_DC_Errors,"Unable to decode WMI IRP");
      t_NTSTATUS = STATUS_UNSUCCESSFUL;
      }

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

	t_NTSTATUS = WmiSystemControl(  &stpFDO_DevExt->t_WMILIB_CONTEXT,
                                    atpFDO,
                                    atpIRP,
                                    &tIRP_Disposition
                                    );

	switch(tIRP_Disposition)
	  {
	   case IrpProcessed:

         //--------------------------------------------------------------------
		   // Completed or pending.
         //--------------------------------------------------------------------

   		break;

	   case IrpNotCompleted:

         //--------------------------------------------------------------------
   		// Not completed, but fully processed.  Now complete it.
         //--------------------------------------------------------------------

   		IoCompleteRequest(atpIRP,IO_NO_INCREMENT);
	   	break;

	   case IrpNotWmi:
	   case IrpForward:

         //--------------------------------------------------------------------
   		// Not a WMI IRP or WMI IRP targeted at lower device in DevNode stack.
         //--------------------------------------------------------------------

		   IoSkipCurrentIrpStackLocation(atpIRP);
		   t_NTSTATUS = IoCallDriver(stpFDO_DevExt->tpLowerDeviceObject,atpIRP);
		   break;

	   default:
   
         D_DiagMsg1(D_Error,D_DC_Errors,"Illegal disposition, %d",tIRP_Disposition);
   		IoCompleteRequest(atpIRP,IO_NO_INCREMENT);
         t_NTSTATUS = STATUS_UNSUCCESSFUL;
	   }
    
   if (gtUnlockDevice(atpFDO,atpIRP) != STATUS_SUCCESS)
     {
      D_DiagMsg0(D_Error,D_DC_Errors,"Unable to decode WMI IRP");
      t_NTSTATUS = STATUS_UNSUCCESSFUL;
      }

   return (t_NTSTATUS);
   }

//=============================================================================
// gtFailWMI_Request(...)
//=============================================================================

NTSTATUS gtFailWMI_Request
  (
   IN PDEVICE_OBJECT atpFDO,
   IN PIRP           atpIRP,
	IN ULONG          aulGUID_Index
   )
  {
   NTSTATUS tStatus = STATUS_SUCCESS;

   struct S_Win2000_DevExt *stpFDO_DevExt = gspGetWin2000_DevExt(atpFDO);

   D_DiagMsg1(D_Error,D_DC_Errors,"Fail WMI requst GUID index, %d",aulGUID_Index);

	if(aulGUID_Index >= stpFDO_DevExt->t_WMILIB_CONTEXT.GuidCount)
		tStatus = STATUS_WMI_GUID_NOT_FOUND;
	else
		tStatus = STATUS_INVALID_DEVICE_REQUEST;

	tStatus = WmiCompleteRequest(atpFDO,atpIRP,tStatus,0,IO_NO_INCREMENT);

   return (tStatus);
   }

//=============================================================================
// stSetWMI_DataItem(...)
//-----------------------------------------------------------------------------
// Call-back to set item in a data block.
//=============================================================================

NTSTATUS gtSetWMI_DataItem
  (
   IN PDEVICE_OBJECT atpFDO,
   IN PIRP           atpIRP,
   IN ULONG          aulGUID_Index,
   IN ULONG          aulInstanceIndex,
   IN ULONG          aulDataItemID,
   IN ULONG          aulBufferSize,
   IN PUCHAR         aucpBuffer
   )
  {
   D_DiagMsg3( D_Info,
               D_DC_Win2000LowLevelInfo,
               "Set WMI data item GUID index, %u, instance index, %u, data item ID, %u",
               aulGUID_Index,aulInstanceIndex,aulDataItemID
               );

   return (gtFailWMI_Request(atpFDO,atpIRP,aulGUID_Index));
   }

//=============================================================================
// gtSetWMI_DataBlock(...)
//-----------------------------------------------------------------------------
// Call-back to store properties for a data block.
//=============================================================================

NTSTATUS gtSetWMI_DataBlock
  (
   IN PDEVICE_OBJECT atpFDO,
   IN PIRP           atpIRP,
   IN ULONG          aulGUID_Index,
   IN ULONG          aulInstanceIndex,
   IN ULONG          aulBufferSize,
   IN PUCHAR         aucpBuffer
   )
  {
   D_DiagMsg2( D_Info,
               D_DC_Win2000LowLevelInfo,
               "Set WMI data block GUID index, %u, instance index, %u",
               aulGUID_Index,aulInstanceIndex
               );

   return (gtFailWMI_Request(atpFDO,atpIRP,aulGUID_Index));
   }

//=============================================================================
// gtQueryWMI_DataBlock(...)
//-----------------------------------------------------------------------------
// Call-back to return the properties of data block(s).
//=============================================================================

NTSTATUS gtQueryWMI_DataBlock
  (
   IN PDEVICE_OBJECT atpFDO,
   IN PIRP           atpIRP,
   IN ULONG          aulGUID_Index,
   IN ULONG          aulInstanceIndex,
   IN ULONG          aulInstanceCount,
   IN OUT PULONG     aulpInstanceLengthArray,
   IN ULONG          aulOutputBufferSize,
   IN PUCHAR         aucpBuffer
   )
  {
   NTSTATUS t_NTSTATUS = STATUS_SUCCESS;

   struct S_Win2000_DevExt *stpFDO_DevExt = gspGetWin2000_DevExt(atpFDO);

   D_DiagMsg3( D_Info,
               D_DC_Win2000LowLevelInfo,
               "Query WMI data blk GUID index, %u, instance index, %u, instance cnt, %u",
               aulGUID_Index,aulInstanceIndex,aulInstanceCount
               );

	if(aulGUID_Index >= stpFDO_DevExt->t_WMILIB_CONTEXT.GuidCount)
     {
      D_DiagMsg2( D_Error,D_DC_Errors,
                  "Invalid WMI GUID index, %u, for instance index, %u, ",
                  aulGUID_Index,aulInstanceIndex
                  );

      t_NTSTATUS = STATUS_WMI_GUID_NOT_FOUND;

      return (WmiCompleteRequest(atpFDO,atpIRP,t_NTSTATUS,0,IO_NO_INCREMENT));
      }

   //--------------------------------------------------------------------------
   // Methods do not have data within them, but still must repond successfully
   // to queries for proper WMI method operation.
   //--------------------------------------------------------------------------

   return (WmiCompleteRequest(   atpFDO,
                                 atpIRP,
                                 t_NTSTATUS,
                                 0,
                                 IO_NO_INCREMENT
                                 )
           );
   }

//=============================================================================
// gtQueryWMI_RegInfo(...)
//-----------------------------------------------------------------------------
// Call-back to retrieve list of GUIDs or data blocks that the driver wants
// to register with WMI.  This function may not pend or block. Driver can
// NOT call "WmiCompleteRequest(...)".
//=============================================================================

#define D_MOF_ResourceNameText L"MofResource"

NTSTATUS gtQueryWMI_RegInfo
  (
   IN PDEVICE_OBJECT    atpFDO,
   OUT PULONG           atpRegFlags,
   OUT PUNICODE_STRING  atpInstanceName,
   OUT PUNICODE_STRING *atppRegistryPath,
   OUT PUNICODE_STRING  atpMOF_ResourceName,
   OUT PDEVICE_OBJECT  *atppPDO
   )
/*
  {
   struct S_Win2000_DevExt *stpFDO_DevExt = gspGetWin2000_DevExt(atpFDO);

   D_DiagMsg0(D_Info,D_DC_Win2000LowLevelInfo,"Query WMI registry information");

   *atpRegFlags      = WMIREG_FLAG_INSTANCE_PDO;
   *atppPDO          = stpFDO_DevExt->tpPDO;

   //--------------------------------------------------------------------------
   // Return registry path for this driver, so WMI can find your driver image
   // and attribute any eventlog messages to the driver.
   //--------------------------------------------------------------------------

   *atppRegistryPath = &gtSavedRegistryPath;

   #ifndef USE_BINARY_MOF_QUERY

      //-----------------------------------------------------------------------
      // Return name specified in ".rc" file of resource which contains the
      // bianry MOF data.  By default, WMI looks for this resource in the
      // driver image (.sys) file; however, if the value "MofImagePath" is
      // specified in the driver's registry key, WMI will look for the
      // resource in the file specified there.
      //-----------------------------------------------------------------------

      RtlInitUnicodeString(atpMOF_ResourceName,D_MOF_ResourceNameText);

   #else

      #pragma D_CompErrorMsg("\"USE_BINARY_MOF_QUERY\" is defined")

   #endif

   return (STATUS_SUCCESS);
   }
*/
  {
   struct S_Win2000_DevExt *stpFDO_DevExt = gspGetWin2000_DevExt(atpFDO);

   D_DiagMsg0(D_Info,D_DC_Win2000LowLevelInfo,"Query WMI registry information");

   *atpRegFlags      = WMIREG_FLAG_INSTANCE_BASENAME;

   //--------------------------------------------------------------------------
   // Return registry path for this driver, so WMI can find your driver image
   // and attribute any eventlog messages to the driver.
   //--------------------------------------------------------------------------

   *atppRegistryPath = &gtSavedRegistryPath;

   #ifndef USE_BINARY_MOF_QUERY

      //-----------------------------------------------------------------------
      // Return name specified in ".rc" file of resource which contains the
      // bianry MOF data.  By default, WMI looks for this resource in the
      // driver image (.sys) file; however, if the value "MofImagePath" is
      // specified in the driver's registry key, WMI will look for the
      // resource in the file specified there.
      //-----------------------------------------------------------------------

      RtlInitUnicodeString(atpMOF_ResourceName,D_MOF_ResourceNameText);

   #else

      #pragma D_CompErrorMsg("\"USE_BINARY_MOF_QUERY\" is defined")

   #endif

	atpInstanceName->Buffer = (PWCHAR) ExAllocatePool(PagedPool,sizeof(gwcpSavedRegistrySubkey));

	if (!atpInstanceName->Buffer)
     {
      D_DiagMsg1( D_Error,D_DC_Errors,
                  "Unable to query WMI reg info for instance name, %S, ",
                  atpInstanceName
                  );

		return (STATUS_INSUFFICIENT_RESOURCES);
      }

	atpInstanceName->MaximumLength   = sizeof(gwcpSavedRegistrySubkey);
	atpInstanceName->Length          = sizeof(gwcpSavedRegistrySubkey) - 2;

	RtlCopyMemory(atpInstanceName->Buffer,gwcpSavedRegistrySubkey,sizeof(gwcpSavedRegistrySubkey));

   return (STATUS_SUCCESS);
   }

//=============================================================================
// gtWMI_FunctionControl(...)
//-----------------------------------------------------------------------------
// Call-back to enable events or expensive block collection.
//=============================================================================

NTSTATUS gtWMI_FunctionControl
  (
   IN PDEVICE_OBJECT          atpFDO,
   IN PIRP                    atpIRP,
   IN ULONG                   aulGUID_Index,
   IN WMIENABLEDISABLECONTROL tFunction,
   IN BOOLEAN                 bEnable
   )
  {
   D_DiagMsg3( D_Info,
               D_DC_Win2000LowLevelInfo,
               "WMI function control GUID index, %u, Function, %u, Enable, %u",
               aulGUID_Index,tFunction,bEnable
               );

   return (gtFailWMI_Request(atpFDO,atpIRP,aulGUID_Index));
   }

//=============================================================================
// gtWMI_FireEvent(...)
//-----------------------------------------------------------------------------
// Cause event with specified message.
//=============================================================================

NTSTATUS gtWMI_FireEvent
  (
   IN PDEVICE_OBJECT    atpFDO,
   wchar_t             *wcpMsg
   )
  {
   D_DiagMsg1(D_Info,D_DC_Win2000LowLevelInfo,"WMI fire event message, %S",wcpMsg);


   return (STATUS_UNSUCCESSFUL);
   }

//=============================================================================
// gtExecuteWMI_Method(...)
//-----------------------------------------------------------------------------
// Call-back to execute the specified method
//=============================================================================

NTSTATUS gtExecuteWMI_Method
  (
   IN PDEVICE_OBJECT atpFDO,
   IN PIRP           atpIRP,
   IN ULONG          aulGUID_Index,
   IN ULONG          aulInstanceIndex,
   IN ULONG          aulMethodID,
   IN ULONG          aulInputBufferSize,
   IN ULONG          aulOutputBufferSize,
   IN OUT PUCHAR     aucpBuffer
   )
  {
   NTSTATUS                t_NTSTATUS     = STATUS_SUCCESS;
	ULONG                   ulBufferUsed   = 0;
   struct S_Win2000_DevExt *stpFDO_DevExt = gspGetWin2000_DevExt(atpFDO);
   struct S_WMI_MethodArg  s_WMI_MethodArg;
   unsigned                u;

   D_DiagMsg3( D_Info,
               D_DC_Win2000LowLevelInfo,
               "Execute WMI method GUID index, %u, instance index, %u, method ID, %u",
               aulGUID_Index,aulInstanceIndex,aulMethodID
               );

   //--------------------------------------------------------------------------
   // Valid GUID?
   //--------------------------------------------------------------------------

	if(aulGUID_Index >= stpFDO_DevExt->t_WMILIB_CONTEXT.GuidCount)
     {
      D_DiagMsg2( D_Error,D_DC_Errors,
                  "Invalid WMI GUID index, %u, for instance index, %u, ",
                  aulGUID_Index,aulInstanceIndex
                  );

      t_NTSTATUS = STATUS_WMI_GUID_NOT_FOUND;

      return (WmiCompleteRequest(atpFDO,atpIRP,t_NTSTATUS,0,IO_NO_INCREMENT));
      }

   //--------------------------------------------------------------------------
   // Find valid method.
   //--------------------------------------------------------------------------

   t_NTSTATUS = STATUS_UNSUCCESSFUL;

   if (!gsa_QueryWMI_DataBlock[aulGUID_Index].sp_WMI_Method)
     {
      D_DiagMsg1( D_Error,D_DC_Errors,
                  "WMI \"sp_WMI_Method\" struct NULL for WMI GUID index, %u",
                  aulGUID_Index
                  );

      return (WmiCompleteRequest(atpFDO,atpIRP,t_NTSTATUS,0,IO_NO_INCREMENT));
      }

   for (u = 0; u < gsa_QueryWMI_DataBlock[aulGUID_Index].cuNumWMI_Methods; u++)
      if (aulMethodID == gsa_QueryWMI_DataBlock[aulGUID_Index].sp_WMI_Method[u].ulID)
        {
         if (aulInstanceIndex != 0)
           {
            t_NTSTATUS = STATUS_WMI_INSTANCE_NOT_FOUND;

            D_DiagMsg2( D_Error,D_DC_Errors,
                        "WMI instance not found for instance index, %u, for WMI GUID index, %u",
                        aulInstanceIndex,aulGUID_Index
                        );

            return (WmiCompleteRequest(atpFDO,atpIRP,t_NTSTATUS,0,IO_NO_INCREMENT));
            }

         s_WMI_MethodArg.ulMethodID          = aulMethodID;
         s_WMI_MethodArg.ulInputBufferSize   = aulInputBufferSize;
         s_WMI_MethodArg.ulOutputBufferSize  = aulOutputBufferSize;
         s_WMI_MethodArg.ucpBuffer           = aucpBuffer;
         s_WMI_MethodArg.stpFDO_DevExt       = stpFDO_DevExt;

         if (!gsa_QueryWMI_DataBlock[aulGUID_Index].sp_WMI_Method[u].tpMethod)
           {
            D_DiagMsg1( D_Error,D_DC_Errors,
                        "WMI method NULL for WMI GUID index, %u",
                        aulGUID_Index
                        );

            return (WmiCompleteRequest(atpFDO,atpIRP,t_NTSTATUS,0,IO_NO_INCREMENT));
            }

         if (aulOutputBufferSize < gsa_QueryWMI_DataBlock[aulGUID_Index].sp_WMI_Method[u].ulReqOutputBufSize)
           {
            t_NTSTATUS = STATUS_BUFFER_TOO_SMALL;

            D_DiagMsg2( D_Error,D_DC_Errors,
                        "Bufer too small for instance index, %u, for WMI GUID index, %u",
                        aulInstanceIndex,aulGUID_Index
                        );
            }

         //--------------------------------------------------------------------
         // Execute method.
         //--------------------------------------------------------------------

         t_NTSTATUS = (*gsa_QueryWMI_DataBlock[aulGUID_Index].sp_WMI_Method[u].tpMethod)(&s_WMI_MethodArg);

         break;
         }

   if (t_NTSTATUS != STATUS_SUCCESS)
     {
      D_DiagMsg3( D_Error,D_DC_Errors,
                  "Invalid Method ID, %u, for instance index, %u, for WMI GUID index, %u",
                  aulMethodID,aulInstanceIndex,aulGUID_Index
                  );

      t_NTSTATUS = STATUS_WMI_ITEMID_NOT_FOUND;

      return (WmiCompleteRequest(atpFDO,atpIRP,t_NTSTATUS,0,IO_NO_INCREMENT));
      }

   ulBufferUsed = gsa_QueryWMI_DataBlock[aulGUID_Index].sp_WMI_Method[u].ulReqOutputBufSize;

   return (WmiCompleteRequest(atpFDO,atpIRP,t_NTSTATUS,ulBufferUsed,IO_NO_INCREMENT));
   }
