//
// 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.
//
#include "StdAfx.h"
#include "ShellProc.h"
#include "NDTNdis.h"
#include "NDTMsgs.h"
#include "NDTError.h"
#include "NDTLog.h"
#include "NDTLib.h"
#include "NDTLibEx.h"
#include "ndt_1c.h"
#include <ndis.h>

extern BOOL  g_bWirelessMiniport;
// 10 sec Max Link up time.
// Here aim is not to test link up & down time of wired NIC but to allow it enough time
// to regain its link back after reset.
#define MAX_LINK_UP_TIME	(10)

HRESULT WaitForMediaConnect(HANDLE hAdapter, DWORD dwSecondsToWait, UINT * puiConnectStatus);

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

TEST_FUNCTION(TestReset)
{
   TEST_ENTRY;

   INT rc = TPR_PASS;
   HRESULT hr = S_OK;
   BOOL bForce30 = FALSE;
   NDIS_MEDIUM ndisMedium = g_ndisMedium;

   UINT   nAdapters = 2;
   HANDLE ahAdapter[2];
   HANDLE ahSend[2];
   UINT   ixAdapter = 0;
   
   ULONG aulPacketsSent[2];
   ULONG aulPacketsCompleted[2];
   ULONG aulPacketsCanceled[2]; 
   ULONG aulPacketsUncanceled[2];
   ULONG aulPacketsReplied[2];
   ULONG aulTime[2];
   ULONG aulBytesSent[2];
   ULONG aulBytesReceived[2];

   UINT  cbAddr = 0;
   UINT  cbHeader = 0;
   BYTE* pucRandomAddr = NULL;
   BYTE* pucBroadcastAddr = NULL;

   UINT  nDestAddrs = 8;
   BYTE* apucDestAddr[8];
   UINT  lxDestAddr = 0;

   UINT  uiMinimumPacketSize = 64;
   UINT  uiMaximumPacketSize = 0;
   UINT  uiPacketSize = 64;
   UINT  uiPacketsToSend = 100;
   UINT  uiBeatDelay = 100;
   UINT  uiNumberOfResets = 1;
   DWORD dwResetSleep = 1000;

   UINT  uiConnectStatus = 0;
   BOOL  bFixedSize = FALSE;
   UINT  ui = 0;

   UINT uiPhyMeduim = NdisPhysicalMediumUnspecified;

   // Zero structures
   memset(ahAdapter, 0, sizeof(ahAdapter));
   memset(ahSend, 0, sizeof(ahSend));
   memset(aulPacketsSent, 0, sizeof(aulPacketsSent));
   memset(aulPacketsCompleted, 0, sizeof(aulPacketsCompleted));
   memset(aulPacketsCanceled, 0, sizeof(aulPacketsCanceled));
   memset(aulPacketsUncanceled, 0, sizeof(aulPacketsUncanceled));
   memset(aulPacketsReplied, 0, sizeof(aulPacketsReplied));
   memset(aulTime, 0, sizeof(aulTime));
   memset(aulBytesReceived, 0, sizeof(aulBytesReceived));
   memset(aulBytesSent, 0, sizeof(aulBytesSent));
   memset(apucDestAddr, 0, sizeof(apucDestAddr));

   // Log intro
   NDTLogMsg(_T("Start 1c_Reset test on the adapter %s"), g_szTestAdapter);

   // Bind adapters
   NDTLogMsg(_T("Opening adapters"));
   for (ixAdapter = 0; ixAdapter < nAdapters; ixAdapter++) {
      hr = NDTOpen(g_szTestAdapter, &ahAdapter[ixAdapter]);
      if (FAILED(hr)) {
         NDTLogErr(g_szFailOpen, g_szTestAdapter, hr);
         goto cleanUp;
      }
   }

   NDTLogMsg(_T("Getting basic info about the adapter"));

   // Get some information about a media
   NDTGetMediumInfo(ndisMedium, &cbAddr, &cbHeader);

   // Bind 
   hr = NDTBind(ahAdapter[0], bForce30, ndisMedium);
   if (FAILED(hr)) {
      NDTLogErr(g_szFailBind, hr);
      goto cleanUp;
   }

   // Lets find out if miniport driver is wireless or not.
   NDTLogMsg(_T("Querying if miniport is wireless"));
   hr = NDTQueryInfo(
	   ahAdapter[0], OID_GEN_PHYSICAL_MEDIUM, &uiPhyMeduim,
	   sizeof(UINT), NULL, NULL
	   );
   
   // Get maximum frame size
   hr = NDTGetMaximumFrameSize(ahAdapter[0], &uiMaximumPacketSize);
   if (FAILED(hr)) {
      NDTLogErr(g_szFailGetMaximumFrameSize, hr);
      goto cleanUp;
   }

   // Unbind
   hr = NDTUnbind(ahAdapter[0]);
   if (FAILED(hr)) {
      NDTLogErr(g_szFailUnbind, hr);
      goto cleanUp;
   }

   // Generate address used later
   pucRandomAddr = NDTGetRandomAddr(ndisMedium);
   pucBroadcastAddr = NDTGetBroadcastAddr(ndisMedium);

   apucDestAddr[0] = pucRandomAddr;
   apucDestAddr[1] = pucRandomAddr;
   apucDestAddr[2] = pucBroadcastAddr;
   apucDestAddr[3] = pucBroadcastAddr;
   apucDestAddr[4] = pucRandomAddr;
   apucDestAddr[5] = pucRandomAddr;
   apucDestAddr[6] = pucBroadcastAddr;
   apucDestAddr[7] = pucBroadcastAddr;

   switch (ndisMedium) {
   case NdisMedium802_3:
      uiNumberOfResets = 70;
      dwResetSleep = 500;
      break;
   case NdisMedium802_5:
   case NdisMediumFddi:
      uiNumberOfResets = 30;
      dwResetSleep = 1000;
      break;
   }

   uiPacketsToSend = 600;
   uiBeatDelay = 100;
   
   // Open adapters
   NDTLogMsg(_T("Binding adapters"));
   for (ixAdapter = 0; ixAdapter < nAdapters; ixAdapter++) {
      hr = NDTBind(ahAdapter[ixAdapter], bForce30, ndisMedium);
      if (FAILED(hr)) {
         NDTLogErr(g_szFailBind, hr);
         rc = TPR_FAIL;
         goto cleanUp;
      }
   }

   NDTLogMsg(_T("Sending packets"));
   for (ixAdapter = 0; ixAdapter < nAdapters; ixAdapter++) {
      hr = NDTSend(
         ahAdapter[ixAdapter], cbAddr, NULL, nDestAddrs, apucDestAddr, 
         NDT_RESPONSE_NONE, NDT_PACKET_FLAG_GROUP | NDT_PACKET_TYPE_FIXED, 
         uiPacketSize, uiPacketsToSend, uiBeatDelay, 1, &ahSend[ixAdapter]
      );
      if (FAILED(hr)) {
         NDTLogErr(g_szFailSend, hr);
         rc = TPR_FAIL;
         goto cleanUp;
      }
   }
   
   for (ui = 0; ui < uiNumberOfResets; ui++) {

      Sleep(dwResetSleep);

      NDTLogMsg(_T("Reset"));
      hr = NDTReset(ahAdapter[0]);

      if (FAILED(hr)) {
		 HRESULT hrErr = NDIS_FROM_HRESULT(hr);
		 if (hrErr==NDIS_STATUS_NOT_RESETTABLE)
		 {
			 NDTLogWrn(_T("%s adapter is ** NOT RESETTABLE **"), g_szTestAdapter);
			 rc = TPR_SKIP;
		 }
		 else
		 {
			 NDTLogErr(_T("Failed to reset %s Error : 0x%x"), g_szTestAdapter,hrErr);
			 rc = TPR_FAIL;
		 }
         goto cleanUp;
      }

      NDTLogMsg(_T("Querying connect status"));
      hr = NDTQueryInfo(
         ahAdapter[0], OID_GEN_MEDIA_CONNECT_STATUS, &uiConnectStatus,
         sizeof(UINT), NULL, NULL
      );

      if (FAILED(hr)) {
         NDTLogErr(g_szFailQueryInfo, hr);
         rc = TPR_FAIL;
      } 
	  else if (uiConnectStatus != NdisMediaStateConnected) 
	  {
		 if (!g_bWirelessMiniport)
		 {
			 HRESULT hr = WaitForMediaConnect(ahAdapter[0], MAX_LINK_UP_TIME, &uiConnectStatus);
			 if (uiConnectStatus != NdisMediaStateConnected)
			 {
				 NDTLogErr(_T("Link is not established when reset is complete"));
				 rc = TPR_FAIL;
			 }
		 }
		 else
		 {
			 //Wireless miniport driver may not indicate NDIS_STATUS_MEDIA_CONNECT immediately.
			 //Wait for atleast 2 min to have MEDIA_CONNECT signal
			 //Loop till we get MEDIA_CONNECT signal
			 for (INT j=0;j<24;++j)
			 {
				 Sleep(5000); //Sleep for 5 second
				 uiConnectStatus = NdisMediaStateDisconnected;
				 hr = NDTQueryInfo(
					 ahAdapter[0], OID_GEN_MEDIA_CONNECT_STATUS, &uiConnectStatus,
					 sizeof(UINT), NULL, NULL
					 );
				 
				 if (FAILED(hr)) {
					 NDTLogErr(g_szFailQueryInfo, hr);
					 rc = TPR_FAIL;
					 break;
				 } else if (uiConnectStatus == NdisMediaStateConnected)
					 break;
			 }

			 if (uiConnectStatus != NdisMediaStateConnected)
			 {
				 NDTLogErr(_T("Wireless adapter is not yet associated even waiting for 1 min after RESET complete"));
				 rc = TPR_FAIL;
			 }
		 }
      }
   }

   for (ixAdapter = 0; ixAdapter < nAdapters; ixAdapter++) {
      hr = NDTSendWait(
         ahAdapter[ixAdapter], ahSend[ixAdapter], INFINITE, 
         &aulPacketsSent[ixAdapter], &aulPacketsCompleted[ixAdapter], 
         &aulPacketsCanceled[ixAdapter], &aulPacketsUncanceled[ixAdapter],
         &aulPacketsReplied[ixAdapter], &aulTime[ixAdapter], 
         &aulBytesSent[ixAdapter], &aulBytesReceived[ixAdapter]
      );
      if (FAILED(hr)) {
         NDTLogErr(g_szFailSendWait, hr);
         rc = TPR_FAIL;
         goto cleanUp;
      }
   }

   NDTLogMsg(_T("Unbinding adapters"));
   for (ixAdapter = 0; ixAdapter < nAdapters; ixAdapter++) {
      hr = NDTUnbind(ahAdapter[ixAdapter]);
      if (FAILED(hr)) {
         NDTLogErr(g_szFailUnbind, hr);
         rc = TPR_FAIL;
         goto cleanUp;
      }
   }

cleanUp:
  
   NDTLogMsg(_T("Closing adapters"));
   for (ixAdapter = 0; ixAdapter < nAdapters; ixAdapter++) {
      hr = NDTClose(&ahAdapter[ixAdapter]);
      if (FAILED(hr)) NDTLogErr(g_szFailClose, hr);
   }
   delete pucRandomAddr;
   delete pucBroadcastAddr;

   return rc;
}

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

HRESULT WaitForMediaConnect(HANDLE hAdapter, DWORD dwSecondsToWait, UINT * puiConnectStatus)
{
	HRESULT hr;
	UINT uiConnectStatus = NdisMediaStateDisconnected;

	for (DWORD j=0;j<dwSecondsToWait;++j)
	{
		uiConnectStatus = NdisMediaStateDisconnected;
		hr = NDTQueryInfo(
			hAdapter, OID_GEN_MEDIA_CONNECT_STATUS, &uiConnectStatus,
			sizeof(UINT), NULL, NULL);
		 
		if (FAILED(hr)) 
		{
			NDTLogErr(g_szFailQueryInfo, hr);
			break;
		} else if (uiConnectStatus == NdisMediaStateConnected)
			break;

		Sleep(1000); //Sleep for 1 second
	 }

	*puiConnectStatus = uiConnectStatus;
	return hr;
}

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