//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft shared
// source or premium shared source license agreement under which you licensed
// this source code. If you did not accept the terms of the license agreement,
// you are not authorized to use this source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the SOURCE.RTF on your install media or the root of your tools installation.
// THE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//
/*
 * tuxIltiming.cpp
 */

////////////////////////////////////////////////////////////////////////////////
/**********************************  Headers  *********************************/
#include "tuxIltiming.h"
#include <Iltiming.h>

/* common utils */
#include "..\..\..\common\commonUtils.h"
#include "..\..\..\common\commonIoctlTests.h"


////////////////////////////////////////////////////////////////////////////////
/******************************  Constants and Defines **************************/
#define DEFAULT_TIMING_INTERVAL	5


////////////////////////////////////////////////////////////////////////////////
/*****************************  Function Declarations  ************************/
/*
 * This function will test the incorrect outbound parameters for the
 * ILTIMING IOCTL. All combinations of incorrect values are passed into 
 * the IOCTL function and the return values are verified. Any incorrect 
 * outbound parameters should not crash the system.
 */
BOOL CheckIncorrectOutboundParameters(DWORD dwIoctlCode, VOID *pCorrectInBuf,
            DWORD dwCorrectInSize);

/*
 * This test fills ILTIMING_MESSAGE structure with invalid
 * iltiming messages, and verifies if the Ioctl can handle them
 */
BOOL testInvalidMessages();

/*
 * This function will test the incorrect inbound parameters for the
 * IOCTLs. All combinations of incorrect values are passed into the IOCTL 
 * function and the return values are verified. Any incorrect input parameters 
 * should not crash the system.
 */
BOOL CheckIncorrectInboundParameters(DWORD dwIoctlCode,
                                     VOID* pCorrectInBuf, DWORD dwCorrectInSize);

/*
 * This function runs a single parameter checking test on the IOCTL function 
 */
BOOL CheckInboundParamSingleTest(DWORD dwIoctlCode, 
            ioctlParamCheck *pIoctlParamTest, VOID *pCorrectInBuf,
            DWORD dwCorrectInSize);


////////////////////////////////////////////////////////////////////////////////
/********************************* TEST PROCS *********************************/

/*
 * Prints out the usage message for the iltiming tests. It tells the user 
 * what the tests do and also specifies the input that the user needs to 
 * provide to run the tests. 
 */
TESTPROCAPI UsageMessage(
			  UINT uMsg, 
			  TPPARAM tpParam, 
			  LPFUNCTION_TABLE_ENTRY lpFTE)
{
  /* only supporting executing the test */ 
  if (uMsg != TPM_EXECUTE) 
  { 
      return (TPR_NOT_HANDLED);
  }

  Info (_T(""));
  Info (_T("The tests verify if ILTIMING is supported on the platform and if"));
  Info (_T("calls with different inputs yeild results. They verify if"));
  Info (_T("ILTIMING_MSG_ENABLE and ILTIMING_MSG_DISABLE can be called"));
  Info (_T("multiple times and if errant calls to the Ioctl do not crash it."));
  Info (_T(""));
  Info (_T("The tests need to be run in kernel mode. To do this,"));
  Info (_T("append \"-n\" to the command line."));
  Info (_T("The tests do not take any command line parameters."));
  Info (_T(""));

  return TPR_PASS;
}


/*
 * This test verifies if the ILTIMING Ioctl is supported by the platform and
 * if the Ioctl is able to handle the necessary inputs.
 */
TESTPROCAPI testIfIltimingIoctlSupported(
			   UINT uMsg,  
			   TPPARAM tpParam,
			   LPFUNCTION_TABLE_ENTRY lpFTE)
{
  INT returnVal = TPR_FAIL;

  /* only supporting executing the test */ 
  if (uMsg != TPM_EXECUTE) 
  { 
      return (TPR_NOT_HANDLED);
  }
	
  ILTIMING_MESSAGE ITM;
  DWORD dwBytesRet;
  DWORD dwErrorCode;

  Info (_T(""));
  Info (_T("This test checks if ILTIMING Ioctl is supported by the platform"));
  Info (_T("and if the Ioctl is able to handle the necessary inputs and output the results."));	
  Info (_T("The four inputs that IOCTL_HAL_ILTIMING should handle are:"));
  Info (_T("ILTIMING_MSG_ENABLE, ILTIMING_MSG_DISABLE, ILTIMING_MSG_GET_TIMES"));
  Info (_T("and ILTIMING_MSG_GET_PFN."));

  // Set Frequency to the default value
  ITM.dwFrequency = DEFAULT_TIMING_INTERVAL;  

  // Input ILTIMING_MSG_ENABLE
  Info (_T(""));
  Info(_T("Calling the Ioctl with ILTIMING_MSG_ENABLE..."));
  ITM.wMsg = ILTIMING_MSG_ENABLE;
  if(!KernelIoControl( IOCTL_HAL_ILTIMING, & ITM, sizeof(ITM), NULL, 0, & dwBytesRet))
  {
     // retrieve the error code
     dwErrorCode = GetLastError();
	Error (_T("Calling the Ioctl returned FALSE, cannot enable ILTIMING."));
	Error (_T("GetLastError returned %u"), dwErrorCode);
	Error (_T(""));
	Error (_T("Check if IOCTL_HAL_ILTIMING is supported by your platform and"));
	Error (_T("if it is enabled. To enable set ENABLE_OAL_ILTIMING=1 when"));
	Error (_T("building your image."));
	Error (_T(""));
	Error (_T("Also, check if you are running the test in kernel mode. To do this,"));
       Error (_T("append \"-n\" to the command line."));
	goto  cleanAndReturn;    
  }
  Info (_T("Ioctl returned true.")); 


  // Input ILTIMING_MSG_GET_TIMES
  Info (_T(""));
  Info(_T("Calling the Ioctl with ILTIMING_MSG_GET_TIMES..."));
  ITM.wMsg = ILTIMING_MSG_GET_TIMES;
  if(!KernelIoControl(IOCTL_HAL_ILTIMING, &ITM, sizeof(ITM), NULL, 0, &dwBytesRet))
  {
     dwErrorCode = GetLastError();
     Error (_T("The Ioctl call failed, cannot get the ISR start and end times."));
     Error (_T("GetLastError returned %u."), dwErrorCode);
     Error (_T("Please check your implementation of IOCTL_HAL_ILTIMING."));
     goto cleanAndReturn;
  }
  Info (_T("Ioctl returned true."));
  Info (_T("The output is: dwIsrTime1 = %u, dwIsrTime2 = %u"),ITM.dwIsrTime1, ITM.dwIsrTime2);


  // Input ILTIMING_MSG_GET_PFN
  Info (_T(""));
  Info(_T("Calling the Ioctl with ILTIMING_MSG_GET_PFN..."));
  ITM.wMsg = ILTIMING_MSG_GET_PFN;
  if(!KernelIoControl(IOCTL_HAL_ILTIMING, &ITM, sizeof(ITM), NULL, 0, &dwBytesRet))
  {
     dwErrorCode = GetLastError();
     Error (_T("The Ioctl call failed, cannot get the pointer to PerfCountSinceTick function."));
     Error (_T("GetLastError returned %u."), dwErrorCode);
     Error (_T("Please check your implementation of IOCTL_HAL_ILTIMING."));
     goto cleanAndReturn;
  }
  Info (_T("Ioctl returned true."));
	

  // Input ILTIMING_MSG_DISABLE
  Info (_T(""));
  Info(_T("Calling the Ioctl with ILTIMING_MSG_DISABLE..."));	
  ITM.wMsg = ILTIMING_MSG_DISABLE;
  if(!KernelIoControl(IOCTL_HAL_ILTIMING, &ITM, sizeof(ITM), NULL, 0, &dwBytesRet))
  {
     dwErrorCode = GetLastError();
     Error (_T("The Ioctl call failed, cannot disable ILTIMING."));
     Error (_T("GetLastError returned %u."), dwErrorCode);
     Error (_T("Please check your implementation of IOCTL_HAL_ILTIMING."));
     goto  cleanAndReturn;
  }
  Info (_T("Ioctl returned true.")); 


  // The test passed if we got here
  returnVal = TPR_PASS;

cleanAndReturn:
      Info (_T(""));
      return (returnVal);
}


/*
 * This test checks if enabling and disabling ILTIMING multiple times
 * does not cause a problem.
 */
TESTPROCAPI testEnableDisableSequence(
			   UINT uMsg,  
			   TPPARAM tpParam,
			   LPFUNCTION_TABLE_ENTRY lpFTE)
{
  INT returnVal = TPR_FAIL;

  /* only supporting executing the test */ 
  if (uMsg != TPM_EXECUTE) 
  { 
      return (TPR_NOT_HANDLED);
  }
	
  ILTIMING_MESSAGE ITM;
  DWORD dwBytesRet;
  DWORD dwErrorCode;

  Info (_T(""));
  Info (_T("The test calls IOCTL_HAL_ILTIMING multiple times with ILTIMING_MSG_ENABLE"));
  Info (_T("and ILTIMING_MSG_DISABLE set in the iltiming message. The test verifies if all"));
  Info (_T("calls succeed. The test sequence is as follows: enable, disable, enable,"));
  Info (_T("enable, disable, disable."));

  // Set Frequency to the default value
  ITM.dwFrequency = DEFAULT_TIMING_INTERVAL;  

  // Input ILTIMING_MSG_ENABLE
  Info (_T(""));
  Info(_T("Calling the Ioctl with ILTIMING_MSG_ENABLE..."));
  ITM.wMsg = ILTIMING_MSG_ENABLE;
  if(!KernelIoControl( IOCTL_HAL_ILTIMING, & ITM, sizeof(ITM), NULL, 0, & dwBytesRet ))
  {
    // retrieve the error code
     dwErrorCode = GetLastError();
	Error (_T("Calling the Ioctl returned FALSE, cannot enable ILTIMING."));
	Error (_T("GetLastError returned %u"), dwErrorCode);
	Error (_T(""));
	Error (_T("Check if IOCTL_HAL_ILTIMING is supported by your platform and"));
	Error (_T("if it is enabled. To enable set ENABLE_OAL_ILTIMING=1 when"));
	Error (_T("building your image."));
	Error (_T(""));
	Error (_T("Also, check if you are running the test in kernel mode. To do this,"));
       Error (_T("append \"-n\" to the command line."));
     goto  cleanAndReturn;
  }
  Info (_T("Ioctl returned true.")); 

  // Input ILTIMING_MSG_DISABLE
  Info (_T(""));
  Info(_T("Calling the Ioctl with ILTIMING_MSG_DISABLE..."));	
  ITM.wMsg = ILTIMING_MSG_DISABLE;
  if(!KernelIoControl(IOCTL_HAL_ILTIMING, &ITM, sizeof(ITM), NULL, 0, &dwBytesRet))
  {
     dwErrorCode = GetLastError();
     Error (_T("The Ioctl call failed, cannot disable ILTIMING."));
     Error (_T("GetLastError returned %u."), dwErrorCode);
     Error (_T("Please check your implementation of IOCTL_HAL_ILTIMING."));
     goto  cleanAndReturn;
  }
  Info (_T("Ioctl returned true.")); 

  // Input ILTIMING_MSG_ENABLE
  Info (_T(""));
  Info(_T("Calling the Ioctl with ILTIMING_MSG_ENABLE..."));
  ITM.wMsg = ILTIMING_MSG_ENABLE;
  if(!KernelIoControl( IOCTL_HAL_ILTIMING, & ITM, sizeof(ITM), NULL, 0, & dwBytesRet ))
  {
     dwErrorCode = GetLastError();
     Error (_T("Calling the Ioctl returned FALSE while the expected value is TRUE."));
     Error (_T("Cannot enable ILTIMING. GetLastError returned %u"), dwErrorCode);
     Error (_T("Check your implementation of IOCTL_HAL_ILTIMING."));
     goto  cleanAndReturn;
  }
  Info (_T("Ioctl returned true.")); 

  // Input ILTIMING_MSG_ENABLE
  Info (_T(""));
  Info(_T("Calling the Ioctl with ILTIMING_MSG_ENABLE..."));
  ITM.wMsg = ILTIMING_MSG_ENABLE;
  if(!KernelIoControl( IOCTL_HAL_ILTIMING, & ITM, sizeof(ITM), NULL, 0, & dwBytesRet ))
  {
     dwErrorCode = GetLastError();
     Error (_T("Calling the Ioctl returned FALSE while the expected value is TRUE."));
     Error (_T("GetLastError returned %u"), dwErrorCode);
     Error (_T("Check your implementation of IOCTL_HAL_ILTIMING."));
     Error (_T("Multiple calls to Enable Iltiming should return TRUE."));
     goto  cleanAndReturn;
  }
  Info (_T("Ioctl returned true.")); 

  // Input ILTIMING_MSG_DISABLE
  Info (_T(""));
  Info(_T("Calling the Ioctl with ILTIMING_MSG_DISABLE..."));	
  ITM.wMsg = ILTIMING_MSG_DISABLE;
  if(!KernelIoControl(IOCTL_HAL_ILTIMING, &ITM, sizeof(ITM), NULL, 0, &dwBytesRet))
  {
     dwErrorCode = GetLastError();
     Error (_T("Calling the Ioctl returned FALSE while the expected value is TRUE."));
     Error (_T("Cannot disable ILTIMING. GetLastError returned %u."), dwErrorCode);
     Error (_T("Check your implementation of IOCTL_HAL_ILTIMING."));
     goto  cleanAndReturn;
  }
  Info (_T("Ioctl returned true.")); 

  // Input ILTIMING_MSG_DISABLE
  Info (_T(""));
  Info(_T("Calling the Ioctl with ILTIMING_MSG_DISABLE..."));	
  ITM.wMsg = ILTIMING_MSG_DISABLE;
  if(!KernelIoControl(IOCTL_HAL_ILTIMING, &ITM, sizeof(ITM), NULL, 0, &dwBytesRet))
  {
     dwErrorCode = GetLastError();
     Error (_T("Calling the Ioctl returned FALSE while the expected value is TRUE."));
     Error (_T("GetLastError returned %u."), dwErrorCode);
     Error (_T("Check your implementation of IOCTL_HAL_ILTIMING."));
     Error (_T("Multiple calls to Disable Iltiming should return TRUE."));
     goto  cleanAndReturn;
  }
  Info (_T("Ioctl returned true.")); 

  // If we got here, the test passed.
  returnVal = TPR_PASS;

cleanAndReturn:

	Info (_T(""));
	return (returnVal);
}



/*
 * This test verifies calling the Ioctl with incorrect inbound and outbound parameters.
 * The Ioctl should handle them correctly. 
 */
TESTPROCAPI
testIltimingInOutParam(
	   UINT uMsg,  
	   TPPARAM tpParam,
	   LPFUNCTION_TABLE_ENTRY lpFTE)
{
  INT returnVal = TPR_PASS;

  /* only supporting executing the test */
  if (uMsg != TPM_EXECUTE)
  {
	return (TPR_NOT_HANDLED);
  }

  ILTIMING_MESSAGE ITM; 
  DWORD dwBytesRet, dwErrorCode;
	
  Info (_T(""));
  Info (_T("This test checks all the incorrect inbound/outbound parameters."));
  Info (_T("All combinations of incorrect values are passed into the IOCTL "));
  Info (_T("function and the return values are verified."));    
  Info (_T("Any incorrect parameters should not crash the system."));

  Info (_T(""));
  Info(_T("Calling the Ioctl with ILTIMING_MSG_ENABLE..."));
  ITM.wMsg = ILTIMING_MSG_ENABLE;
  ITM.dwFrequency = DEFAULT_TIMING_INTERVAL;  
  if(!KernelIoControl( IOCTL_HAL_ILTIMING, & ITM, sizeof(ITM), NULL, 0, & dwBytesRet ))
  {
    // retrieve the error code
     dwErrorCode = GetLastError();
	Error (_T("Calling the Ioctl returned FALSE, cannot enable ILTIMING."));
	Error (_T("GetLastError returned %u"), dwErrorCode);
	Error (_T(""));
	Error (_T("Check if IOCTL_HAL_ILTIMING is supported by your platform and"));
	Error (_T("if it is enabled. To enable set ENABLE_OAL_ILTIMING=1 when"));
	Error (_T("building your image."));
	Error (_T(""));
	Error (_T("Also, check if you are running the test in kernel mode. To do this,"));
       Error (_T("append \"-n\" to the command line."));
     returnVal = TPR_FAIL;
     goto  cleanAndReturn;
  }
  Info (_T("The Ioctl is supported.")); 

  // Sample parameter to test: enable iltiming
  ITM.wMsg = ILTIMING_MSG_ENABLE;
  ITM.dwFrequency = DEFAULT_TIMING_INTERVAL;  

  // Test incorrect outbound parameters
  Info (_T(""));
  Info (_T("Checking incorrect outbound parameters..."));
  if((!CheckIncorrectOutboundParameters(IOCTL_HAL_ILTIMING,&ITM, sizeof(ITM))))
  {
     Error (_T(""));
     Error (_T("The Ioctl did not handle incorrect outbound parameters correctly."));
     returnVal = TPR_FAIL;
  }

  // Test incorrect inbound parameters
  Info (_T(""));
  Info (_T("Checking incorrect inbound parameters..."));
  if(!testInvalidMessages())
  {
	Error (_T(""));
	Error (_T("The Ioctl did not handle invalid iltiming messages correctly.")); 
	returnVal = TPR_FAIL;
  }
  if((!CheckIncorrectInboundParameters(IOCTL_HAL_ILTIMING,&ITM, sizeof(ITM))))
  {
	Error (_T(""));
	Error (_T("The Ioctl did not handle incorrect inbound parameters correctly."));
		returnVal = TPR_FAIL;
  }

cleanAndReturn:

	Info (_T(""));
	return (returnVal);
}


/*
 * This function will test the incorrect outbound parameters for the
 * ILTIMING IOCTL. All combinations of incorrect values are passed into 
 * the IOCTL function and the return values are verified. Any incorrect 
 * outbound parameters should not crash the system.
 *
 * Arguments: 
 * 		Input: IOCTL code, Input Buffer, Input Buffer Size
 *     Output: None
 *
 * Return value:
 *    	TRUE if successful, FALSE otherwise
 */
BOOL CheckIncorrectOutboundParameters(DWORD dwIoctlCode, VOID *pCorrectInBuf,
            DWORD dwCorrectInSize)
{
  BOOL returnVal = TRUE;
  DWORD dwBytesRet; 

  // Outbound parameters aren't required for IOCTL_HAL_ILTIMING.
  Info (_T(""));
  Info (_T("The ILTIMING IOCTL does not use the outbound buffer."));
  Info (_T("We will set the outbound parameters same as the inbound"));
  Info (_T("parameters and verify that this does not affect the Ioctl."));
  Info (_T(""));

  if (!KernelIoControl (dwIoctlCode,pCorrectInBuf, dwCorrectInSize,
  				pCorrectInBuf, dwCorrectInSize, &dwBytesRet))
  {
     Error (_T("")); //Blank line
     Error (_T("Calling the ILTIMING IOCTL function with outbound parameters."));
     Error (_T("set returned FALSE, while the expected value is TRUE."));     
     returnVal = FALSE;
  }
  else
  {
     Info (_T("Passing outbound parameters to IOCTL_HAL_ILTIMING did not"));
     Info (_T("have any effect on the KernelIoControl() call."));
  }

  return (returnVal);
}


/*
 * This test fills ILTIMING_MESSAGE structure with invalid
 * iltiming messages, and verifies if the Ioctl can handle them
 */
BOOL testInvalidMessages()
{
	INT returnVal = TRUE;
	ILTIMING_MESSAGE ITM; 
	DWORD dwBytesRet;
	WORD wMsg;

	Info (_T(""));
	Info (_T("Calling IOCTL_HAL_ILTIMING with invalid iltiming messages.")); 

	// Test invalid wMsgs
	for(wMsg = 5; wMsg < 100; wMsg++)  // skip 1-4 as they're defined
	{
		ITM.wMsg = wMsg;  
		if(KernelIoControl(IOCTL_HAL_ILTIMING, &ITM, sizeof(ITM), NULL, 0, &dwBytesRet))
		{
		     	Error (_T(""));
			Error (_T("The Ioctl returned TRUE for the case where ILTIMING_MESSAGE.wMsg = %u."), ITM.wMsg);
			Error (_T("This is an invalid input and the Ioctl is expected to return FALSE."));
			returnVal = FALSE;
		}
	}
	return returnVal;
}


/*
 * This function will test the incorrect inbound parameters for the
 * IOCTLs. All combinations of incorrect values are passed into the IOCTL 
 * function and the return values are verified. Any incorrect input parameters 
 * should not crash the system.
 *
 * Arguments: 
 * 		Input: IOCTL code, Input Buffer, Input Buffer Size
 *     Output: None
 *
 * Return value:
 *    	TRUE if successful, FALSE otherwise
 */
BOOL CheckIncorrectInboundParameters(DWORD dwIoctlCode,
                                     VOID* pCorrectInBuf, DWORD dwCorrectInSize)
{
  // Assume TRUE, until proven otherwise.
  BOOL returnVal = TRUE;

  Info (_T("")); 
  Info (_T("Calling the ILTIMING Ioctl with incorrect inbound parameters."));
   
  ///////////////////////////Verify the parameters ////////////////////////////
  DWORD dwCount;
  DWORD dwNumTests;
  ioctlParamCheck *pIoctlParamTest;

  dwNumTests = dwNumParamTests-1;
  Info (_T("A total of %u tests will be run on the Inbound parameters."),
			                                  dwNumTests);

  // Iterate through all the tests in the array  
  for(dwCount=0; dwCount<dwNumTests; dwCount++)
  {
    Info (_T("")); //Blank line
    Info (_T("This is test %u"),dwCount + 1);
		 
    pIoctlParamTest = &ioctlInParamTests[dwCount];

    if(!CheckInboundParamSingleTest(dwIoctlCode, pIoctlParamTest, pCorrectInBuf, 
		            dwCorrectInSize))
    {
    	Error (_T("The parameter checking test failed."));
	returnVal = FALSE;
    }
    else
    {
    	Info (_T("The parameter checking test passed."));
    }
  }

  return returnVal;
}


/*
 * This function runs a single parameter checking test on the IOCTL function 
 *
 * Arguments: 
 *		Input: IOCTL code, pointer to the test struct, Input Buffer,
 *             Input Buffer Size, Output Buffer, Output Buffer Size
 *      Output: None
 * Return value:
 *    	TRUE if print successful, FALSE otherwise
 */
BOOL CheckInboundParamSingleTest(DWORD dwIoctlCode, 
            ioctlParamCheck *pIoctlParamTest, VOID *pCorrectInBuf,
            DWORD dwCorrectInSize)
{
  BOOL returnVal = TRUE;

  DWORD dwInBufSize;
  DWORD dwBufSize;

  VOID *pInBuf, *pAllocateInBuf = NULL;

  DWORD dwBytesRet = 0;
  DWORD *pdwBytesRet = NULL;
  BOOL bOutValue;

  // Check for null pointer in the input
  if(!pIoctlParamTest)
  {
  	Error (_T("Invalid pointer in input arguments."));
	Error (_T("Cannot perform the tests."));
	return FALSE;
  }

  // Get the test description
  Info (_T("Description: %s"), pIoctlParamTest->szComment); 
  Info (_T("Set up the inbound parameters..."));
  
  // Get the input buffer
  // Allocate memory for the max size of input buffer we will use for the test (dwCorrectInSize + 1)
  if(pIoctlParamTest ->dwInBuf != BUF_NULL)
  {
	  DWORD dwMaxTestInBufSize = dwCorrectInSize + 1;
	  if(dwMaxTestInBufSize < dwCorrectInSize)
	  {
	     Error (_T("Overflow occured when calculating the Maximum Test Input Buffer Size."));
	     Error (_T("The input buffer size is too large %u."), dwCorrectInSize);
	     returnVal = FALSE;
	     goto cleanAndExit;
	  }
	  
	  pAllocateInBuf = (VOID*)malloc (dwMaxTestInBufSize * sizeof(BYTE));

	  if(!pAllocateInBuf)
	  {
	     Error (_T("Could not allocate memory for the input buffer."));
	     returnVal = FALSE;
	     goto cleanAndExit;
	  }

	  if(!memcpy(pAllocateInBuf, pCorrectInBuf, dwCorrectInSize))
	  {
	     Error (_T("Could not copy input to the buffer."));
	     returnVal = FALSE;
	     goto cleanAndExit;
	  }
  }

  pInBuf = pAllocateInBuf;
  
  switch (pIoctlParamTest ->dwInBuf)
  {
  	case BUF_NULL:
		dwBufSize = 0;
		break;
			
	case BUF_CORRECT:
		dwBufSize = dwCorrectInSize;
		break;
			
	case BUF_CORRECTSIZE_MINUS_ONE:
		dwBufSize = dwCorrectInSize - 1;
		break;
		
	case BUF_CORRECTSIZE_PLUS_ONE:
		dwBufSize = dwCorrectInSize + 1;
		break;
  }

  // Get the input size
  if(pIoctlParamTest -> dwInBufSize == SIZE_CORRECT)
  {
	dwInBufSize = dwCorrectInSize;
  }
  else if (pIoctlParamTest -> dwInBufSize == SIZE_BUF)
  {
  	dwInBufSize = dwBufSize;
  }
  else
  {
	dwInBufSize = 0;
  }
	  
	
  //Call IOCTL function now
  Info (_T("Call the IOCTL using KernelIoControl function now..."));
  bOutValue = KernelIoControl (dwIoctlCode, pInBuf, dwInBufSize, NULL, 
  	                                           0, pdwBytesRet);	
    DWORD dwGetLastError = GetLastError();
	   
  //Check returned value
  Info (_T("Check the returned value...")); 
  if(bOutValue != pIoctlParamTest->bOutput)
  {
     if(bOutValue)
     {
        Error (_T("The IOCTL returned TRUE while the expected value is FALSE."));
	returnVal = FALSE;
     }
     else
     {
	Error (_T("The IOCTL returned FALSE while the expected value is TRUE."));
	returnVal = FALSE;
     }		   	
  }	

  //Check GetLastError
  if(pIoctlParamTest->dwErrorCode)
  { 
    Info (_T("Check GetLastError..."));

    Info (_T("GetLastError is %u"),dwGetLastError);

    if(pIoctlParamTest->dwErrorCode != dwGetLastError)
    {
       Error (_T("The error code returned is %u while the expected value is %u."), 
   	                            dwGetLastError, pIoctlParamTest->dwErrorCode);
       returnVal = FALSE;
    }	
  }

cleanAndExit:
		
  return returnVal;

}


