//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// This source code is licensed under Microsoft Shared Source License
// Version 1.0 for Windows CE.
// For a copy of the license visit http://go.microsoft.com/fwlink/?LinkId=3223.
//
#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 <Pkfuncs.h>
#include <Pm.h>
#include <nkintr.h>
#include <ndis.h>

BOOL SetUpMcastAddrs(PBYTE pbListMacAddr,DWORD dwNosOfAddrs,NDIS_MEDIUM ndisMedium);
extern BOOL  g_bWirelessMiniport;

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

TEST_FUNCTION(TestResetOnResume)
{
	TEST_ENTRY;
	
	HRESULT hr = S_OK;
	DWORD rc = TPR_FAIL;
	
	NDTLogMsg(
		_T("Start 1c_ResetOnResume (Reset on Resume) test on the adapter %s"), g_szTestAdapter
		);
	
	NDTLogMsg(
		_T("This test will test if %s restores its settings after Reset"), g_szTestAdapter
		);
	
	DWORD dwRet =0;
	PBYTE pbMcastMacAddr = NULL;
	PBYTE pbMcastMacAddrVerify = NULL;
	HANDLE hAdapter = NULL;

	do
	{
		hr = NDTOpen(g_szTestAdapter, &hAdapter);
		if (FAILED(hr)) 
		{
			NDTLogErr(g_szFailOpen, g_szTestAdapter, hr);
			break;
		}
		
		NDIS_MEDIUM ndisMedium = g_ndisMedium;
		UINT  cbAddr = 0; UINT  cbHeader = 0;
		
		// Get some information about a media
		NDTGetMediumInfo(ndisMedium, &cbAddr, &cbHeader);
		NDTLogVbs(_T("ProcessNIC: NDT Opened: cbAddr:%d cbHeader:%d"),cbAddr,cbHeader);
		
		BOOL bForce30 = FALSE;
		
		// Bind
		hr = NDTBind(hAdapter, bForce30, ndisMedium, &ndisMedium);
		if (FAILED(hr)) {
			NDTLogErr(g_szFailBind, hr);
			break;		
		}
		
		NDIS_PNP_CAPABILITIES sPNP = {{0}};
		DWORD dwSize=sizeof(sPNP);
		UINT cbUsed = 0; UINT cbRequired = 0;
		
		hr = NDTQueryInfo(
			hAdapter, OID_PNP_CAPABILITIES, PBYTE(&sPNP), dwSize,
			&cbUsed, &cbRequired
			);
		
		if (FAILED(hr)) {
			NDTLogMsg(_T("%s does not support Power Management"), g_szTestAdapter);
		}
		else
		{
			if (sPNP.Flags)
			{
				NDTLogMsg(_T("%s supports Power Management and")
					_T(" one or more wake-up capabilities"), g_szTestAdapter);
			}
			else
				NDTLogMsg(_T("%s supports only Power Management, but")
				_T(" does not have wake-up capabilities"), g_szTestAdapter);
		}
		
		//Set the filter for binding.
		DWORD dwPktFilter = NDIS_PACKET_TYPE_MULTICAST|NDIS_PACKET_TYPE_DIRECTED;
		dwSize=sizeof(dwPktFilter);
		
		hr = NDTSetInfo(hAdapter, OID_GEN_CURRENT_PACKET_FILTER, PBYTE(&dwPktFilter),
			dwSize,&cbUsed,&cbRequired);
		
		if (FAILED(hr)) {
			NDTLogErr(_T("Error:Setting OID_GEN_CURRENT_PACKET_FILTER")
				_T(" for adapter %s failed:NDIS_STATUS= 0x%x"),g_szTestAdapter,hr);
			break;
		}
		
		NDTLogMsg(_T("Set CURRENT_PACKET_FILTER = NDIS_PACKET_TYPE_MULTICAST|NDIS_PACKET_TYPE_DIRECTED for adapter %s"),
			g_szTestAdapter);

		//Get the maximum list size of mcast addresses.
		DWORD dwMcastListSize;
		dwSize = sizeof(dwMcastListSize);
		
		hr = NDTQueryInfo(
			hAdapter, OID_802_3_MAXIMUM_LIST_SIZE, PBYTE(&dwMcastListSize), dwSize,
			&cbUsed, &cbRequired
			);
		
		if (FAILED(hr)) {
			NDTLogErr(_T("Error:Quering OID_802_3_MAXIMUM_LIST_SIZE")
				_T(" for adapter %s failed:NDIS_STATUS= 0x%x"),g_szTestAdapter,hr);
			break;
		}

		NDTLogMsg(_T("%s supports max %d Multicast addresses"),
			g_szTestAdapter, dwMcastListSize);

		if (dwMcastListSize < 32) {
			NDTLogWrn(_T("Miniport driver must support at least 32 multicast addresses"));
		}

		//Allocate the memory for the mcast mac addresses.
		DWORD dwMcastListSizeInBytes = dwMcastListSize * 6;
		pbMcastMacAddr = (PBYTE) new char[dwMcastListSizeInBytes];
		dwSize = dwMcastListSizeInBytes;
		
		//Get the current list of mcast addresses.
		hr = NDTQueryInfo(
			hAdapter, OID_802_3_MULTICAST_LIST, pbMcastMacAddr, dwSize,
			&cbUsed, &cbRequired
			);
		
		if (FAILED(hr)) {
			NDTLogErr(_T("Error:Quering OID_802_3_MULTICAST_LIST")
				_T(" for adapter %s failed:NDIS_STATUS= 0x%x"),g_szTestAdapter,hr);
			break;
		}

		//Get the current number of mcast addresses set.
		dwSize = cbUsed/6;

		NDTLogMsg(_T("Got the current complete list of multicast addresses for %s "),g_szTestAdapter);
		NDTLogMsg(_T("It has %d multicast addresses set"),dwSize);

		ASSERT(dwSize == 0);
		
		//Now Setup Mcast addresses in a buffer
		//If we setup Max nos. of Mcast addresses, chances are there that the list may overflow,
		//as the list is global for all the protocol bindings to a NIC.
		dwMcastListSize = dwMcastListSize - (2 + dwSize) ;
		
		// We should take care when the test is run with -nounbind.
		if (!SetUpMcastAddrs(pbMcastMacAddr,dwMcastListSize,ndisMedium)) 
		{
			NDTLogErr(_T("Failed to create multicast addresses in the array"));
			break;
		}
		
		dwSize = dwMcastListSize * 6; //This is the size in bytes reqd. for setting OID.
		
		//Now set the Mcast addresses in the list of current NIC.
		//The deal with "bOneMoreTime" is it helps to reissue set multicast list if it fails one time.
		//Note that Multicast list is global one & hence other protocol drivers can change it.
		BOOL bOneMoreTime = FALSE;
		do
		{
			hr = NDTSetInfo(hAdapter, OID_802_3_MULTICAST_LIST, pbMcastMacAddr,
				dwSize,&cbUsed,&cbRequired);
			
			if (NDIS_FROM_HRESULT(hr) == NDIS_STATUS_MULTICAST_FULL)
			{
				dwMcastListSize = 4;
				dwSize = dwMcastListSize * 6;
				bOneMoreTime = !bOneMoreTime;
			}
			else
				break;
			
		} while(bOneMoreTime);
		
		if (FAILED(hr)) {
			NDTLogErr(_T("Error:Setting OID_802_3_MULTICAST_LIST")
				_T(" for adapter %s failed:NDIS_STATUS= 0x%x"),g_szTestAdapter,hr);
			break;
		}
		
		NDTLogMsg(_T("Set %d multicast addresses for %s"),dwMcastListSize, g_szTestAdapter);
		NDTLogMsg(_T("We'll RESET the device & at the end of every RESET we'll verify if %s restores its settings or not\n"),
			g_szTestAdapter);

		for (INT i=0;i<5;++i)
		{
			HANDLE hStatus = NULL;

			//First start the waiting for event notification.
			hr = NDTStatus(hAdapter, NDIS_STATUS_RESET_END, &hStatus);
			
			if (FAILED(hr)) {
				NDTLogErr(_T("Failed to start receiving notifications on %s Error : 0x%x"), g_szTestAdapter,NDIS_FROM_HRESULT(hr));
				break;
			}

			NDTLogMsg(_T("Reseting the network adapter %s %d time"),g_szTestAdapter,i+1);

			hr = NDTReset(hAdapter);
			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;
				}
				break;
			}
			
			NDTLogMsg(_T("Waiting to receive NDIS_STATUS_RESET_END notification on the miniport adapter %s"), g_szTestAdapter);
			
			hr = NDTStatusWait(hAdapter, hStatus, 30000);
			
			if ( FAILED(hr) || (hr == NDT_STATUS_PENDING) ) {
				NDTLogErr(_T("Failed to receive NDIS_STATUS_RESET_END Error : 0x%x"), NDIS_FROM_HRESULT(hr));
				break;
			}

			NDTLogMsg(_T("Received NDIS_STATUS_RESET_END notification on %s"), g_szTestAdapter);

			if (g_bWirelessMiniport)
			{
				UINT  uiConnectStatus;

				NDTLogMsg(_T("Querying connect status & waiting for NdisMediaStateConnected"));

				for (INT j=0;j<24;++j)
				{
					Sleep(5000); //Sleep for 5 second
					uiConnectStatus = NdisMediaStateDisconnected;
					hr = NDTQueryInfo(
						hAdapter, 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 2 min after RESET complete"));
					rc = TPR_FAIL;
					break;
				}
			}

			////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			// Now its time to verify if "Packet filter settings" and "multicast list" are unchanged.
			////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			
			//Get the packet filter settings
			DWORD dwPktFilterVerify;
			dwSize=sizeof(dwPktFilterVerify);
			
			hr = NDTQueryInfo(
				hAdapter, OID_GEN_CURRENT_PACKET_FILTER, PBYTE(&dwPktFilterVerify), dwSize,
				&cbUsed, &cbRequired
				);
			
			if (FAILED(hr)) {
				//An Error.
				NDTLogErr(_T("Error:Quering OID_GEN_CURRENT_PACKET_FILTER")
					_T(" for adapter %s failed:NDIS_STATUS= 0x%x"),g_szTestAdapter,hr);
				break;
			}
			
			ASSERT(dwPktFilterVerify == dwPktFilter);
			if (dwPktFilterVerify != dwPktFilter)
			{
				NDTLogErr(_T("%s failed to restore Packet Filter settings."),g_szTestAdapter);
				NDTLogErr(_T("%s has failed ResetOnResume test"),g_szTestAdapter);
				continue;
			}
			
			NDTLogMsg(_T("%s has restored/retained its Packet Filter settings."),g_szTestAdapter);

			//Now Query again for Mcast addresses.
			pbMcastMacAddrVerify = (PBYTE) new char[dwMcastListSizeInBytes];
			dwSize = dwMcastListSizeInBytes;
			
			//Get the current list of mcast addresses.
			hr = NDTQueryInfo(
				hAdapter, OID_802_3_MULTICAST_LIST, pbMcastMacAddrVerify, dwSize,
				&cbUsed, &cbRequired
				);
			
			if (FAILED(hr)) {
				//An Error.
				NDTLogErr(_T("Error:Quering OID_802_3_MULTICAST_LIST")
					_T(" for adapter %s failed:NDIS_STATUS= 0x%x"),g_szTestAdapter,hr);
				break;
			}
			
			//Now lets get the current number of multicast addresses
			dwSize = cbUsed/6;

			ASSERT(dwSize != 0);
			
			if (dwSize != dwMcastListSize)
			{
				NDTLogMsg(_T("Number Multicast addresses: before Suspend/Resume = %d after = %d"),
					dwMcastListSize,dwSize);
				NDTLogErr(_T("%s failed to restore size of Multicast Addresses list."),g_szTestAdapter);
				NDTLogErr(_T("%s has failed ResetOnResume test"),g_szTestAdapter);
				continue;
			}
			
			INT iRet = memcmp(pbMcastMacAddr,pbMcastMacAddrVerify,dwSize);
			ASSERT(iRet == 0);
			
			if (iRet!= 0)
			{
				NDTLogErr(_T("%s failed to restore %d Multicast Addresses"),g_szTestAdapter,dwMcastListSize);
				NDTLogErr(_T("%s has failed ResetOnResume test"),g_szTestAdapter);
				continue;
			}
			
			rc = TPR_PASS;
			NDTLogMsg(_T("%s has restored/retained its multicast addresses list."),g_szTestAdapter);
			NDTLogMsg(_T("%s has passed ResetOnResume test"),g_szTestAdapter);
		} // for { suspend/resume 5 times }

	} while (0);
	
	if (pbMcastMacAddr)
		delete [] pbMcastMacAddr;
	
	if (pbMcastMacAddrVerify)
		delete [] pbMcastMacAddrVerify;
	
	hr = NDTUnbind(hAdapter);
	if (FAILED(hr)) NDTLogErr(g_szFailUnbind, hr);
	
	hr = NDTClose(&hAdapter);
	if (FAILED(hr)) NDTLogErr(g_szFailClose, hr);

	return rc;
}

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

BOOL FillUpMcastAddr(PBYTE pucAddr,DWORD dwVar,NDIS_MEDIUM ndisMedium)
{
	if (pucAddr == NULL)
		return FALSE;

   switch (ndisMedium) {
   case NdisMedium802_3:
   case NdisMediumFddi:
	   pucAddr[0] = 0x01; pucAddr[1] = 0x02; pucAddr[2] = 0x03;
	   pucAddr[3] = 0x04; pucAddr[4] = (UCHAR)(dwVar/256);
	   pucAddr[5] = (UCHAR)dwVar;
	   break;
   case NdisMedium802_5:
	   pucAddr[0] = 0xC0; pucAddr[1] = 0x00; pucAddr[2] = 0x01;
	   pucAddr[3] = 0x02; pucAddr[4] = (UCHAR)(dwVar/256);
	   pucAddr[5] = (UCHAR)dwVar;
	   break;
   default:
	   return FALSE;
   }

	return TRUE;
}

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

BOOL SetUpMcastAddrs(PBYTE pbListMacAddr,DWORD dwNosOfAddrs,NDIS_MEDIUM ndisMedium)
{
	if (pbListMacAddr == NULL)
		return FALSE;

	PBYTE pb = pbListMacAddr;

	for (DWORD i=0;i<dwNosOfAddrs;++i)
	{
		if (!FillUpMcastAddr(pb,i,ndisMedium))
			return FALSE;
		pb=pb+6;
	}
	return TRUE;
}

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