//+-------------------------------------------------------------------------
//
//  Microsoft Windows
//
//  Copyright (C) Microsoft Corporation, 1998 - 1999
//
//  File:       wmi.c
//
//--------------------------------------------------------------------------

#include "pch.h"
#include <wmistr.h>

#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGEPARWMI0, PptWmiInitWmi)
#pragma alloc_text(PAGEPARWMI0, PptWmiQueryWmiRegInfo)
#pragma alloc_text(PAGEPARWMI0, PptWmiQueryWmiDataBlock)
#endif


//
// Number of WMI GUIDs that we support
//
#define PPT_WMI_PDO_GUID_COUNT               1

//
// Index of GUID PptWmiAllocFreeCountsGuid in the array of supported WMI GUIDs
//
#define PPT_WMI_ALLOC_FREE_COUNTS_GUID_INDEX 0

//
// defined in wmidata.h:
//
// // {4BBB69EA-6853-11d2-8ECE-00C04F8EF481}
// #define PARPORT_WMI_ALLOCATE_FREE_COUNTS_GUID {0x4bbb69ea, 0x6853, 0x11d2, 0x8e, 0xce, 0x0, 0xc0, 0x4f, 0x8e, 0xf4, 0x81}
//
// typedef struct _PARPORT_WMI_ALLOC_FREE_COUNTS {
// 	ULONG PortAllocates;	// number of Port Allocate requests granted
// 	ULONG PortFrees;	// number of Port Free requests granted
// } PARPORT_WMI_ALLOC_FREE_COUNTS, *PPARPORT_WMI_ALLOC_FREE_COUNTS;
//


//
// Define the (only at the moment) WMI GUID that we support
//
GUID PptWmiAllocFreeCountsGuid = PARPORT_WMI_ALLOCATE_FREE_COUNTS_GUID;


//
// Array of WMI GUIDs supported by driver
//
WMIGUIDREGINFO PptWmiGuidList[ PPT_WMI_PDO_GUID_COUNT ] =
{
    { &PptWmiAllocFreeCountsGuid, 1, 0 }
};


//
// Initialize WMI Context that we pass to WMILIB during the handling of
//   IRP_MJ_SYSTEM_CONTROL. This context lives in our device extension
//
// Register w/WMI that we are able to process WMI IRPs
//
NTSTATUS
PptWmiInitWmi(PDEVICE_OBJECT DeviceObject)
{
    PFDO_EXTENSION devExt     = DeviceObject->DeviceExtension;
    PWMILIB_CONTEXT   wmiContext = &devExt->WmiLibContext;

    PAGED_CODE();

    wmiContext->GuidCount = sizeof(PptWmiGuidList) / sizeof(WMIGUIDREGINFO);
    wmiContext->GuidList  = PptWmiGuidList;

    wmiContext->QueryWmiRegInfo    = PptWmiQueryWmiRegInfo;   // required
    wmiContext->QueryWmiDataBlock  = PptWmiQueryWmiDataBlock; // required
    wmiContext->SetWmiDataBlock    = NULL; // optional
    wmiContext->SetWmiDataItem     = NULL; // optional
    wmiContext->ExecuteWmiMethod   = NULL; // optional
    wmiContext->WmiFunctionControl = NULL; // optional

    // Tell WMI that we can now accept WMI IRPs
    return IoWMIRegistrationControl( DeviceObject, WMIREG_ACTION_REGISTER );
}

NTSTATUS
//
// This is the dispatch routine for IRP_MJ_SYSTEM_CONTROL IRPs. 
//
// We call WMILIB to process the IRP for us. WMILIB returns a disposition
//   that tells us what to do with the IRP.
//
PptFdoSystemControl(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
    SYSCTL_IRP_DISPOSITION disposition;
    NTSTATUS status;
    PFDO_EXTENSION pDevExt = (PFDO_EXTENSION)DeviceObject->DeviceExtension;

    PAGED_CODE();

    status = WmiSystemControl( &pDevExt->WmiLibContext, DeviceObject, Irp, &disposition);
    switch(disposition) {
    case IrpProcessed:

        //
        // This irp has been processed and may be completed or pending.
        //
        break;
        
    case IrpNotCompleted:
    
        //
        // This irp has not been completed, but has been fully processed.
        // we will complete it now
        //
        P4CompleteRequest( Irp, Irp->IoStatus.Status, Irp->IoStatus.Information );
        break;
    
    case IrpForward:
    case IrpNotWmi:
    
        //
        // This irp is either not a WMI irp or is a WMI irp targetted
        // at a device lower in the stack.
        //
        IoSkipCurrentIrpStackLocation(Irp);
        status = IoCallDriver(pDevExt->ParentDeviceObject, Irp);
        break;
                                    
    default:

        //
        // We really should never get here, but if we do just forward....
        //
        ASSERT(FALSE);
        IoSkipCurrentIrpStackLocation(Irp);
        status = IoCallDriver(pDevExt->ParentDeviceObject, Irp);
        break;
    }
    
    return status;

}

//
// This is our callback routine that WMI calls when it wants to find out
//   information about the data blocks and/or events that the device provides.
//
NTSTATUS
PptWmiQueryWmiRegInfo(
    IN  PDEVICE_OBJECT  PDevObj, 
    OUT PULONG          PRegFlags,
    OUT PUNICODE_STRING PInstanceName,
    OUT PUNICODE_STRING *PRegistryPath,
    OUT PUNICODE_STRING MofResourceName,
    OUT PDEVICE_OBJECT  *Pdo 
)
{
    PFDO_EXTENSION devExt = PDevObj->DeviceExtension;

    UNREFERENCED_PARAMETER( PInstanceName );
    UNREFERENCED_PARAMETER( MofResourceName );

    PAGED_CODE();

    DD((PCE)devExt,DDT,"wmi::PptWmiQueryWmiRegInfo\n");
    
    *PRegFlags     = WMIREG_FLAG_INSTANCE_PDO;
    *PRegistryPath = &RegistryPath;
    *Pdo           = devExt->PhysicalDeviceObject;
    
    return STATUS_SUCCESS;
}

//
// This is our callback routine that WMI calls to query a data block
//
NTSTATUS
PptWmiQueryWmiDataBlock(
    IN PDEVICE_OBJECT   DeviceObject,
    IN PIRP             Irp,
    IN ULONG            GuidIndex,
    IN ULONG            InstanceIndex,
    IN ULONG            InstanceCount,
    IN OUT PULONG       InstanceLengthArray,
    IN ULONG            OutBufferSize,
    OUT PUCHAR          Buffer
    )
{
    NTSTATUS          status;
    ULONG             size   = sizeof(PARPORT_WMI_ALLOC_FREE_COUNTS);
    PFDO_EXTENSION devExt = DeviceObject->DeviceExtension;

    PAGED_CODE();

    //
    // Only ever registers 1 instance per guid
    //
#if DBG
    ASSERT(InstanceIndex == 0 && InstanceCount == 1);
#else
    UNREFERENCED_PARAMETER( InstanceCount );
    UNREFERENCED_PARAMETER( InstanceIndex );
#endif
    
    switch (GuidIndex) {
    case PPT_WMI_ALLOC_FREE_COUNTS_GUID_INDEX:

        //
        // Request is for ParPort Alloc and Free Counts
        //
        // If caller's buffer is large enough then return the info, otherwise
        //   tell the caller how large of a buffer is required so they can
        //   call us again with a buffer of sufficient size.
        //
        if (OutBufferSize < size) {
            status = STATUS_BUFFER_TOO_SMALL;
            break;
        }
        *( (PPARPORT_WMI_ALLOC_FREE_COUNTS)Buffer ) = devExt->WmiPortAllocFreeCounts;
        *InstanceLengthArray = size;
        status = STATUS_SUCCESS;
        break;

    default:

        //
        // Index value larger than our largest supported - invalid request
        //
        status = STATUS_WMI_GUID_NOT_FOUND;
        break;
    }

    status = WmiCompleteRequest( DeviceObject, Irp, status, size, IO_NO_INCREMENT );

    return status;
}