//
// 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.
//
// --------------------------------------------------------------------
//                                                                     
// 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;
//g_noRelease flag is used to copy the result log file to current directory instead of release directory.
BOOL g_noRelease = FALSE;
BOOL g_perfScenarioEnded = FALSE;

BOOL  g_ndisd = FALSE;
BOOL  g_RunAsPerfWinsock = FALSE;
DWORD g_AddendumHdrSize = 54;
TCHAR *g_szIpAddress = NULL;
TCHAR *g_szPort = NULL;

LPTSTR g_szAdapter = NULL;
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(""));
    LogMsg(_T("Options:"));
    LogMsg(_T(""));
    LogMsg(_T("-ip                     : (Required) IP address of the server"));
    LogMsg(_T("-port                 : (Required) Port number for the server"));
    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 = 0;

    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_noRelease is true if user enters "-l" option in command line.
    g_noRelease = GetOptionFlag(&argc, argv, _T("l"));
    g_receivePeer = GetOptionFlag(&argc, argv, _T("s"));
    g_ndisd = GetOptionFlag(&argc, argv, _T("ndisd"));
    g_unbindProtocols = FALSE;
    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"));
    g_szIpAddress = GetOptionString(&argc, argv, _T("ip"));
    if (g_szIpAddress == NULL)
    {
        LogErr(_T("-ip <ipaddress of desktop side> is a mandatory parameter"));
        goto cleanUp;
    }

    g_szPort = GetOptionString(&argc, argv, _T("port"));
    if (g_szPort == NULL)
    {
        LogErr(_T("-port <perf_ndis6 desktop port number> is a mandatory parameter"));
        goto cleanUp;
    }

    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;
    }
    g_szAdapter = new TCHAR[_tcslen(argv[0]) + 1];
    StringCchCopy(g_szAdapter, _tcslen(argv[0]) + 1, argv[0]);
    _tprintf(_T("Adapter name is %s"), g_szAdapter);

    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

    PtsNtEnumAda psNtEnumAda = NULL;
    PtsNtEnumAda pHead = NULL;

    HRESULT hr = NtEnumAdapters(&psNtEnumAda);
    if (hr == S_OK)
    {
        LogMsg(_T("Network Interface cards"));
        int i = 1;
        pHead = psNtEnumAda;
        while(psNtEnumAda)
        {
            PtsNtAda pNtAda = &(psNtEnumAda->sNtAda);
            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);
            i++;
            psNtEnumAda = psNtEnumAda->next;
        }

        //free the linked list
        while (pHead)
        {
            psNtEnumAda = pHead->next;
            free(pHead);
            pHead = psNtEnumAda;
        }
        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(g_szIpAddress, _ttoi(g_szPort)) == TPR_INFORMATIVE)
        {
            dwRetVal = TPR_INFORMATIVE;
        }
    }
    else if (SendPerf(g_szIpAddress, _ttoi(g_szPort)) == TPR_INFORMATIVE)
    {
        //
        //  test passed
        //
        dwRetVal = TPR_INFORMATIVE;
    }

cleanUp:
    // cleanup here
    if (g_hAdapter != NULL) 
    {
        CloseAdapter(g_hAdapter);
        CloseProtocol(g_hAdapter);
    }

    CloseBackChannel();

    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
                );
        }
    }

    if (g_szAdapter)
    {
        delete g_szAdapter;
    }

    LogCleanup();
    return dwRetVal;
}

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