/*++

Copyright (c) 2000  Microsoft Corporation

Module Name:

    cpuevent.c

Abstract:

    Monitor Processor Driver Events

Author:

    Todd Carpenter

Environment:

    user mode

Notes:

  THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
  KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
  PURPOSE.

  Copyright (c) 2000 Microsoft Corporation.  All Rights Reserved.


Revision History:

    03-15-2001 : ToddCar - created

--*/
#include "cpuevent.h"




VOID
__cdecl
_tmain(
  IN INT    argc,
  IN TCHAR *argv[]
  )
/*++

Routine Description:

  Main()


Arguments:



Return Value:


--*/
{
  ULONG  status;
  PTSTR  instanceName = NULL;
 
  status = GetInstanceName(&instanceName);
  _tprintf(TEXT("InstanceName == %s\n"), instanceName);
    
  if (status != ERROR_SUCCESS) {

    if (status == ERROR_WMI_GUID_NOT_FOUND) {
    
      _tprintf(TEXT("\n"));
      _tprintf(TEXT("This processor driver does not support WMI interface\n"));
      _tprintf(TEXT("\n"));

    } else {

      _tprintf(TEXT("GetInstanceName() Failed! rc=0x%x\n"), status);
      
    }
    return;
  }

  
  
  //
  // Register for Notifications
  //
  
  _tprintf(TEXT("Registering for events...\n"));
  status = WmiNotificationRegistration((LPGUID) &ProcessorPStateEventGuid,
                                       (BOOLEAN) TRUE,
                                       EventCallBackRoutine,
                                       PStateEventId,
                                       NOTIFICATION_CALLBACK_DIRECT);

  if (status != ERROR_SUCCESS) {
    _tprintf(TEXT("WmiNotificationRegistration() Failed! rc=0x%x\n"), status);
  }

  status = WmiNotificationRegistration((LPGUID) &ProcessorNewPStatesEventGuid,
                                       (BOOLEAN) TRUE,
                                       EventCallBackRoutine,
                                       NewPStatesEventId,
                                       NOTIFICATION_CALLBACK_DIRECT);

  if (status != ERROR_SUCCESS) {
    _tprintf(TEXT("WmiNotificationRegistration() Failed! rc=0x%x\n"), status);
  }

  status = WmiNotificationRegistration((LPGUID) &ProcessorNewCStatesEventGuid,
                                       (BOOLEAN) TRUE,
                                       EventCallBackRoutine,
                                       NewCStatesEventId,
                                       NOTIFICATION_CALLBACK_DIRECT);

  if (status != ERROR_SUCCESS) {
    _tprintf(TEXT("WmiNotificationRegistration() Failed! rc=0x%x\n"), status);
  }

  _tprintf(TEXT("Waiting.... press 'q' to quit\n\n"));
  while(_getch() != 'q');
 
  return;
  
}


ULONG
GetInstanceName(
  OUT PTSTR *Buffer
  )
/*++

  Routine Description:
  
    GetInstanceName()
  
  
  Arguments:
  
  
  
  Return Value:


--*/
{
  ULONG     status;
  ULONG     sizeNeeded;
  PUCHAR    buffer;
  PTSTR     instanceName;
  ULONG     instanceNameOffset;
  WMIHANDLE wmiStatusHandle;
  


  //
  // NOTE: must use ProcessorMethodGuid to get Instance Name ... not
  //       sure why others guid don't work.
  //
    
  //
  // Open connection
  //
  
  status = WmiOpenBlock(&ProcessorMethodGuid,
                        GENERIC_READ,
                        &wmiStatusHandle);


  if (status != ERROR_SUCCESS) {
    //_tprintf(TEXT("GetInstanceName: WmiOpenBlock() Failed! rc=0x%x\n"), status);
    goto GetProcessorPerformanceStatusDataExit;
  }

  

  //
  // Query for buffer size needed
  //
  
  status = WmiQueryAllData(wmiStatusHandle,
                           &sizeNeeded,
                           0);

  if (status != ERROR_INSUFFICIENT_BUFFER) {
    _tprintf(TEXT("GetInstanceName:WmiQueryAllData(0) Failed! rc=0x%x\n"), status);
    goto GetProcessorPerformanceStatusDataExit;
  }


  //
  // Allocate buffer
  //
  
  buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeNeeded);

  if (!buffer) {
    _tprintf(TEXT("GetInstanceName: HeapAlloc(size=0x%x) Failed!\n"), sizeNeeded);
    status = ERROR_INSUFFICIENT_BUFFER;
    goto GetProcessorPerformanceStatusDataExit;
  }


  //
  // Retrieve Data
  //
  
  status = WmiQueryAllData(wmiStatusHandle,
                           &sizeNeeded,
                           buffer);

  if (status != ERROR_SUCCESS) {
    _tprintf(TEXT("GetInstanceName: WmiQueryAllData() Failed! rc=0x%x\n"), status);
    goto GetProcessorPerformanceStatusDataExit;
  }
  
  //
  // Get instance name.
  //

  instanceNameOffset = (ULONG) buffer[((PWNODE_ALL_DATA)buffer)->OffsetInstanceNameOffsets];
  instanceName = (PTSTR) &buffer[instanceNameOffset + sizeof(USHORT)];

  
  //
  // Copy instance name
  //
  
  if (instanceName && Buffer) {

    ULONG nameSize = lstrlen(instanceName) + sizeof(TCHAR);
    
    *Buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, nameSize);

    if (!(*Buffer)) {
      status = ERROR_INSUFFICIENT_BUFFER;
      goto GetProcessorPerformanceStatusDataExit;
    }
    
    lstrcpyn(*Buffer, instanceName, nameSize);
  }


  //
  // Close connection
  //

  WmiCloseBlock(wmiStatusHandle);
  
  
  
GetProcessorPerformanceStatusDataExit:

  if (buffer) {
    //HeapFree(GetProcessHeap(), 0, buffer);
  }
  
  return status;
  
}


VOID
EventCallBackRoutine(
  IN PWNODE_HEADER WnodeHeader,
  IN UINT_PTR Context
  )
{

  PWNODE_SINGLE_INSTANCE wnode = (PWNODE_SINGLE_INSTANCE) WnodeHeader;
  

  switch (Context) {

    case PStateEventId:
    {
      PPSTATE_EVENT data = (PPSTATE_EVENT)((ULONG_PTR)wnode + (ULONG_PTR)wnode->DataBlockOffset);

      _tprintf(TEXT("Perf State Transition:\n"));
      _tprintf(TEXT("  New State = %u\n"), data->State);
      _tprintf(TEXT("  Status    = 0x%x\n"), data->Status);
      _tprintf(TEXT("  Cpu Speed = %u mhz\n\n"), data->Speed);
      break;
    }
    

    case NewPStatesEventId:
    {
      PNEW_PSTATES_EVENT data = (PNEW_PSTATES_EVENT)((ULONG_PTR)wnode + (ULONG_PTR)wnode->DataBlockOffset);

      _tprintf(TEXT("New Perf States:\n"));
      _tprintf(TEXT("  Highest Available State = %u\n\n"), data->HighestState);
      break;
    }
    
    case NewCStatesEventId:
    
      _tprintf(TEXT("New CStates!\n"));
      break;
    
    
    default:
      _tprintf(TEXT("Unknown Event Id = %u\n\n"), Context);
  }
 
}
