//
// 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.
//
// --------------------------------------------------------------------
//                                                                     
// 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.                                                 
//                                                                     
// --------------------------------------------------------------------

#include <windows.h>
#include <tchar.h>
#include "utils.h"
#include "log.h"
#include "ndp_protocol.h"
#include "ndp_lib.h"
#include "globals.h"

// TBD: Add any test specific
//			globals
//			function prototypes

//
// TBD: Define your test procs here
//

LPCTSTR g_szTestName = _T("perf_ndis");

DWORD g_logVerbosity = LOG_PASS;
BOOL  g_unbindProtocols = TRUE;
DWORD g_unbindDelay = 1000;
BOOL  g_xlsResults = FALSE;
BOOL  g_receivePeer = FALSE;
BOOL  g_ndisd = FALSE;
BOOL  g_RunAsPerfWinsock = FALSE;
DWORD g_AddendumHdrSize = 54;

TCHAR  g_szAdapter[256] = _T("");
HANDLE g_hAdapter = NULL;

DWORD g_packetsSend = 100000;
DWORD g_minSize = 64;
DWORD g_maxSize = ETH_MAX_FRAME_SIZE;
DWORD g_sizeStep = 64;
DWORD g_poolSize = 64;
BOOL  g_fUseNdisSendOnly = FALSE;
teNDPTestType g_eTestType = SEND_THROUGHPUT;

BOOL EnumNPrintAdapters();
//------------------------------------------------------------------------------

void PrintUsage()
{
   LogMsg(_T("perf_ndis: Usage: tux -o -d perf_ndis -c\"[...options...] adapterName\""));
   //LogMsg(_T("ndp_send [...options...] adapterName"));
   LogMsg(_T(""));
   LogMsg(_T("Options:"));
   LogMsg(_T(""));
   LogMsg(_T("-<?|help>                 : Get these options/help"));
   LogMsg(_T("-enum                     : Enumerate the adapters"));
   LogMsg(_T("-mode <send|recv|ping>    : Find send or recv or ping throughput"));
   LogMsg(_T("-wsock [-hsize <n>]       : Run the test in the same fashion to perf_winsock &"));
   LogMsg(_T("                            take <n> as total header size (default 40+14)"));
   LogMsg(_T("                            For TCP:n=54 & UDP:n=42"));
   LogMsg(_T("-ndisd                    : Run as receive peer/partner <ndisd>"));
   LogMsg(_T("-s                        : Find and use receive peer/partner <ndisd>"));
   LogMsg(_T("-v <n>                    : Log verbosity (0 to 15, default 10)"));   
   LogMsg(_T("-packets <n>              : Packets to send (default 10000)"));
   LogMsg(_T("-size <n>                 : Packets size (min/max/step option are ignored)"));
   LogMsg(_T("-min <n>                  : Minimal packets size (default 64)"));
   LogMsg(_T("-max <n>                  : Maximal packets size (default 1514)"));
   LogMsg(_T("-step <n>                 : Packets size increment (default 64)"));
   LogMsg(_T("-pool <n>                 : Pool of packets used in test (default 64)"));
   LogMsg(_T("-nounbind                 : Suppress protocol unbind"));
   LogMsg(_T("-ubdelay                  : Delay after protocol unbind (default 1000 ms)"));
   LogMsg(_T("-apacketsend              : Uses NdisSend instead of NdisSendPackets for send"));
   LogMsg(_T(""));
   LogMsg(_T(""));
}

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

BOOL ParseCommands(INT argc, LPTSTR argv[])
{
   BOOL ok = FALSE;
   DWORD value;
   
   //g_receivePeer = GetOptionFlag(&argc, argv, _T("peer"));
   BOOL bHelpReqd = GetOptionFlag(&argc, argv, _T("?"));
   bHelpReqd = GetOptionFlag(&argc, argv, _T("help"));
   if (bHelpReqd) goto cleanUp;

   BOOL bEnum = GetOptionFlag(&argc, argv, _T("enum"));
   if (bEnum) 
   {
	   if (!EnumNPrintAdapters())
	   {
		   LogErr(_T("Error in enumerating the adapters"));
	   }
	   return FALSE;
   }

   g_receivePeer = GetOptionFlag(&argc, argv, _T("s"));
   g_ndisd = GetOptionFlag(&argc, argv, _T("ndisd"));
   g_unbindProtocols = !GetOptionFlag(&argc, argv, _T("nounbind"));
   g_fUseNdisSendOnly = GetOptionFlag(&argc, argv, _T("apacketsend"));
   g_unbindDelay = GetOptionLong(&argc, argv, g_unbindDelay, _T("ubdelay"));
   g_packetsSend = GetOptionLong(&argc, argv, g_packetsSend, _T("packets"));
   g_poolSize = GetOptionLong(&argc, argv, g_poolSize, _T("pool"));

   g_RunAsPerfWinsock = GetOptionFlag(&argc, argv, _T("wsock"));
   if (g_RunAsPerfWinsock)
	   g_AddendumHdrSize = GetOptionLong(&argc, argv, g_AddendumHdrSize, _T("hsize"));

   TCHAR * pwsz = GetOptionString(&argc, argv, _T("mode"));
   if (pwsz)
   {
	   if (_tcsicmp(pwsz,_T("send")) == 0)
		   g_eTestType = SEND_THROUGHPUT;
	   else if (_tcsicmp(pwsz,_T("recv")) == 0)
		   g_eTestType = RECV_THROUGHPUT;
	   else if (_tcsicmp(pwsz,_T("ping")) == 0)
		   g_eTestType = PING_THROUGHPUT;
	   else
		   goto cleanUp;
   }

   if (g_RunAsPerfWinsock)
   {
	   g_sizeStep = 16;
	   g_minSize = g_sizeStep + g_AddendumHdrSize;
	   g_packetsSend = 10000;
   }
   else 
   {
	   value = GetOptionLong(&argc, argv, 0, _T("size"));
	   if (value > ETH_MAX_FRAME_SIZE) goto cleanUp;
	   if (value > 0) {
			  g_minSize = g_maxSize = value;
			  g_sizeStep = ETH_MAX_FRAME_SIZE;
	   } else  {
		   g_minSize = GetOptionLong(&argc, argv, g_minSize, _T("min"));
		   if (g_minSize > ETH_MAX_FRAME_SIZE) goto cleanUp;
		   g_maxSize = GetOptionLong(&argc, argv, g_maxSize, _T("max"));
		   if (g_minSize > ETH_MAX_FRAME_SIZE) goto cleanUp;
		   g_sizeStep = GetOptionLong(&argc, argv, g_sizeStep, _T("step"));
		   if (g_sizeStep == 0) goto cleanUp;
	   }
   }
   if (argc != 1) goto cleanUp;
   StringCchCopy(g_szAdapter, sizeof(g_szAdapter)/sizeof(g_szAdapter[0]), argv[0]);

   ok = TRUE;

cleanUp:
   if (!ok) PrintUsage();
   return ok;
}

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

int SetOptions(INT argc, LPTSTR argv[])
{
	int rc = ERROR_SUCCESS;

	// This parameter we need there
	g_logVerbosity = GetOptionLong(&argc, argv, g_logVerbosity, _T("v"));

	// Initialize the log subsystem
	LogStartup(g_szTestName, g_logVerbosity, LogKato);
	
	// Let parse test specific options
	if (!ParseCommands(argc, argv))
	{
		rc = ERROR_BAD_ARGUMENTS;
	}
	
	return rc;
}

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

BOOL EnumNPrintAdapters()
{
	BOOL bRet = FALSE;

#ifdef UNDER_CE

	TCHAR szListOfNICs[256]=_T("");
	TCHAR szListOfPROTOs[256]=_T("");
	TCHAR szList[512]=_T("");

	LogMsg(_T("NDISConfig:****************************************"));

	DWORD dwSize = sizeof(szListOfNICs);
	DWORD dwRet = QueryAdapters(szListOfNICs,dwSize);
	LogMsg(_T("Adapters: %s"),MultiSzToSz(szList,szListOfNICs));

	dwRet = QueryProtocols(szListOfPROTOs,dwSize);
	LogMsg(_T("Protocols: %s"),MultiSzToSz(szList,szListOfPROTOs));

	for (TCHAR * wszAdapterName = &szListOfNICs[0];
			 *wszAdapterName != _T('\0');
			 wszAdapterName += _tcslen(wszAdapterName) + 1)
			 {
				 TCHAR szBindings[512]=_T("");
				 dwRet = QueryBindings(wszAdapterName, szBindings, dwSize);
				 LogMsg(_T("%s <--> %s"),wszAdapterName,MultiSzToSz(szList,szBindings));			 
			 }
	 LogMsg(_T("NDISConfig:****************************************"));
	 bRet = TRUE;

#else UNDER_CE

	tsNtEnumAda sNtEnumAda = {{0}};

	HRESULT hr = NtEnumAdapters(&sNtEnumAda,MAX_ENUM_NT_ADAPTERS);
	if (hr == S_OK)
	{
		LogMsg(_T("Network Interface cards"));
		for(DWORD i=0;i<sNtEnumAda.dwNosOfAda;++i)
		{
			PtsNtAda pNtAda = &(sNtEnumAda.sNtAdaArray[i]);
			LogMsg(_T("Adapter #%2d"),i);
			LogMsg(_T("Display Name : %s"),pNtAda->szwDisplayName);
			LogMsg(_T("Bind Name    : \\Device\\%s"),pNtAda->szwBindName);
			LogMsg(_T("Help Text    : %s"),pNtAda->szwHelpText);
			LogMsg(_T("Id           : %s"),pNtAda->szwId);
		}

		bRet = TRUE;
	}

#endif

	return bRet;
}

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

TESTPROCAPI Perf_ndis(UINT uMsg, TPPARAM tpParam, LPFUNCTION_TABLE_ENTRY lpFTE )
{
    DWORD dwRetVal = TPR_FAIL;

    // process incoming tux messages
    //
    if( TPM_QUERY_THREAD_COUNT == uMsg )
    {
        // return the thread count that should be used to run this test
        ((LPTPS_QUERY_THREAD_COUNT)tpParam)->dwThreadCount = 0;
        return TPR_HANDLED;
    }
    else if( TPM_EXECUTE != uMsg )
        return TPR_NOT_HANDLED;

	int rc = ERROR_SUCCESS;

	if (g_unbindProtocols) {

#ifdef UNDER_CE
		rc = UnbindProtocol(g_szAdapter, NULL);
#else
		rc = ERROR_NOT_SUPPORTED;
#endif

		if (rc != ERROR_SUCCESS) {
			LogErr(
				_T("Failed unbind protocols from adapter %s with code %d"), 
				g_szAdapter, rc
				);
			goto cleanUp;
		}
		Sleep(g_unbindDelay);
	}
	
	// Load protocol driver...
	g_hAdapter = OpenProtocol();
	if (g_hAdapter == NULL) {
		rc = GetLastError();
		LogErr(_T("Failed open protocol driver with code %d"), rc);
		goto cleanUp;
	}
	
	// Open adapter
	if (!OpenAdapter(g_hAdapter, g_szAdapter)) {
		LogErr(_T("Failed open adapter %s"), g_szAdapter);
		goto cleanUp;
	}

	// Fill in the test proc here
	if (g_ndisd)
	{
		if (ndisd() == ERROR_SUCCESS)
			dwRetVal = TPR_PASS;
	}
	else if (SendPerf() == ERROR_SUCCESS)
	{
		//
		//  test passed
		//
		dwRetVal = TPR_PASS;
	}
    
cleanUp:
	// cleanup here
	if (g_hAdapter != NULL) {
		CloseAdapter(g_hAdapter);
		CloseProtocol(g_hAdapter);
	}
	
	if (g_unbindProtocols) {

#ifdef UNDER_CE
		rc = BindProtocol(g_szAdapter, NULL);
#else
		rc = ERROR_NOT_SUPPORTED;
#endif

		if (rc != ERROR_SUCCESS) {
			LogErr(
				_T("Failed bind protocols to adapter %s with code %d"), 
				g_szAdapter, rc
				);
		}
	}
	LogCleanup();
    return dwRetVal;
}

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

#if 0

int WINAPI WinMain(
   HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR szCmdLine, int nShowCmd
)
{
   int rc = ERROR_SUCCESS;
   LPTSTR argv[64];
   int argc = 64;

   // Divide command line to token
   CommandLineToArgs(szCmdLine, &argc, argv);

   if ((rc=SetOptions(argc,argv))!= ERROR_SUCCESS)
   {
	   //Error
	   return rc;
   }

   return SendPerf();
}

#endif

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