/*++

Copyright (c) 1996  Microsoft Corporation

Module Name:

	utils.c		- Utility functions.

Abstract:

   Internal utility functions for ATMARP:

	- Allocation and deallocation of various structures
	- Timer management
	- Buffer/Packet management
	- Linking/unlinking ATMARP structures
	- Copy support functions


Revision History:

	Who         When        What
	--------    --------    ----------------------------------------------
	arvindm     07-15-96    Created

Notes:

--*/


#include <precomp.h>

#define _FILENUMBER 'LITU'

//
// AtmArpValidateTimerList and AtmArpValidateTimer are used in the checked
// build to validate the state of a timerlist and timer, respectively.
// They are not defined and used in the free build.
//

#if  DBG
void
AtmArpValidateTimerList(
	PATMARP_TIMER_LIST		pTimerList
	);

void
AtmArpValidateTimer(
	PATMARP_TIMER_LIST		pTimerList,
	PATMARP_TIMER			pTimer
	);

//
// AtmArpValidateTimerList is overkill for general use (even default
// free build) -- because it goes through the entire timer list --
// so disable it by default
//
#if 0
#define AA_VALIDATE_TIMER_LIST(_ptl) 	AtmArpValidateTimerList(_ptl)
#else
#define AA_VALIDATE_TIMER_LIST(_ptl) 	((void) 0)
#endif

#define AA_VALIDATE_TIMER(_ptl,_pt) 	AtmArpValidateTimer(_ptl,_pt)

#else // !DBG

#define AA_VALIDATE_TIMER_LIST(_ptl) 	((void) 0)
#define AA_VALIDATE_TIMER(_ptl,_pt) 	((void) 0)

#endif // !DBG




VOID
AtmArpSetMemory(
	IN	PUCHAR						pStart,
	IN	UCHAR						Value,
	IN	ULONG						NumberOfBytes
)
/*++

Routine Description:

	Set "NumberOfBytes" bytes starting from "pStart" to "Value".

Arguments:

	pStart			- where to start filling.
	Value			- the value to put everywhere
	NumberOfBytes	- how many bytes to fill in

Return Value:

	None

--*/
{
	while (NumberOfBytes--)
	{
		*pStart++ = Value;
	}
}



ULONG
AtmArpMemCmp(
	IN	PUCHAR						pString1,
	IN	PUCHAR						pString2,
	IN	ULONG						Length
)
/*++

Routine Description:

	Compare two byte strings.

Arguments:

	pString1		- Start of first string
	pString2		- Start of second string
	Length			- Length to compare

Return Value:

	0 if both are equal, -1 if string 1 is "smaller", +1 if string 1 is "larger".

--*/
{
	while (Length--)
	{
		if (*pString1 != *pString2)
		{
			return ((*pString1 > *pString2)? (ULONG)1 : (ULONG)-1);
		}
		pString1++;
		pString2++;
	}

	return (0);
}



LONG
AtmArpRandomNumber(
	VOID
)
/*++

Routine Description:

	Generate a positive pseudo-random number; simple linear congruential
	algorithm. ANSI C "rand()" function. Courtesy JameelH.

Arguments:

	None

Return Value:

	a random number.

--*/
{
	LARGE_INTEGER		Li;
	static LONG			seed = 0;

	if (seed == 0)
	{
		NdisGetCurrentSystemTime(&Li);
        seed = Li.LowPart;
	}

	seed *= (0x41C64E6D + 0x3039);
	return (seed & 0x7FFFFFFF);
}




VOID
AtmArpCheckIfTimerIsInActiveList(
	IN	PATMARP_TIMER				pTimerToCheck,
	IN	PATMARP_INTERFACE			pInterface,
	IN	PVOID						pStruct,
	IN	PCHAR						pStructName
	)
/*
	Instrumentation to catch a bug that causes the timer list to
	contain a pointer to an element that's been freed.
*/
{
	PATMARP_TIMER_LIST	pTimerList;
	PATMARP_TIMER		pTimer;
	ULONG				i, j;

	do
	{
		if (pInterface == NULL)
		{
			break;
		}

		if (pTimerToCheck->State == ATMARP_TIMER_STATE_RUNNING ||
			pTimerToCheck->State == ATMARP_TIMER_STATE_EXPIRING)
		{
			DbgPrint("ATMARPC: %s at %x contains timer %x still active on IF %x\n",
				pStructName,
				pStruct,
				pTimerToCheck,
				pInterface);

			DbgBreakPoint();
		}

		AA_STRUCT_ASSERT(pInterface, aai);

		AA_ACQUIRE_IF_TIMER_LOCK(pInterface);

		for (i = 0; i < AAT_CLASS_MAX; i++)
		{
			pTimerList = &pInterface->TimerList[i];

			for (j = 0; j < pTimerList->TimerListSize; j++)
			{
				for (pTimer = pTimerList->pTimers[j].pNextTimer;
					 pTimer != NULL_PATMARP_TIMER;
					 pTimer = pTimer->pNextTimer)
				{
					if (pTimer == pTimerToCheck)
					{
						DbgPrint("ATMARPC: %s at %x contains timer %x still active on IF %x, Head of list %x\n",
							pStructName,
							pStruct,
							pTimerToCheck,
							pInterface,
							&pTimerList->pTimers[j]);
						DbgBreakPoint();
					}
				}
			}
		}

		AA_RELEASE_IF_TIMER_LOCK(pInterface);
		break;
	}
	while (FALSE);

}



PATMARP_VC
AtmArpAllocateVc(
	IN	PATMARP_INTERFACE			pInterface
)
/*++

Routine Description:

	Allocate an ATMARP VC structure, initialize it, and return it.

Arguments:

	pInterface		- Interface for which this VC is created.

Return Value:

	Pointer to VC if allocated, NULL otherwise.

--*/
{
	PATMARP_VC			pVc;

	AA_STRUCT_ASSERT(pInterface, aai);

	AA_ALLOC_MEM(pVc, ATMARP_VC, sizeof(ATMARP_VC));

	if (pVc != NULL_PATMARP_VC)
	{
		AA_SET_MEM(pVc, 0, sizeof(ATMARP_VC));
#if DBG
		pVc->avc_sig = avc_signature;
#endif // DBG
		pVc->pInterface = pInterface;
		AA_INIT_VC_LOCK(pVc);
	}

	AADEBUGP(AAD_LOUD, ("Allocated Vc 0x%x\n", pVc));

	return (pVc);
}



VOID
AtmArpDeallocateVc(
	IN	PATMARP_VC					pVc
)
/*++

Routine Description:

	Deallocate an ATMARP VC structure. It is assumed that all references
	to this VC have gone, so there is no need to acquire a lock to the VC.

Arguments:

	pVc			- Pointer to the VC to be deallocated

Return Value:

	None

--*/
{
	AA_STRUCT_ASSERT(pVc, avc);
	AA_ASSERT(pVc->RefCount == 0);
	AA_ASSERT(!AA_IS_TIMER_ACTIVE(&pVc->Timer));

	AA_CHECK_TIMER_IN_ACTIVE_LIST(&pVc->Timer, pVc->pInterface, pVc, "VC");

#if DBG
	pVc->avc_sig++;
#endif
	AA_FREE_VC_LOCK(pVc);
	AA_FREE_MEM(pVc);

	AADEBUGP(AAD_LOUD, ("Deallocated Vc 0x%x\n", pVc));

}




VOID
AtmArpReferenceVc(
	IN	PATMARP_VC					pVc
)
/*++

Routine Description:

	Add a reference to the specified ATMARP VC.
	NOTE: The caller is assumed to possess a lock for the VC.

Arguments:

	pVc			- Pointer to the VC to be referenced

Return Value:

	None

--*/
{
	AA_STRUCT_ASSERT(pVc, avc);

	pVc->RefCount++;

	AADEBUGP(AAD_VERY_LOUD, ("Referencing Vc 0x%x, new count %d\n",
			 pVc, pVc->RefCount));
}




ULONG
AtmArpDereferenceVc(
	IN	PATMARP_VC					pVc
)
/*++

Routine Description:

	Subtract a reference from the specified ATMARP VC. If the VC's
	reference count becomes zero, deallocate it.

	NOTE: The caller is assumed to possess a lock for the VC.
	SIDE EFFECT: See Return Value below

Arguments:

	pVc			- Pointer to the VC to be dereferenced.

Return Value:

	Is the new reference count.
	[IMPORTANT] If the VC's reference count became zero, the VC will be
	deallocated -- the VC lock is, obviously, released in this case.

--*/
{
	ULONG		rv;
	NDIS_HANDLE	NdisVcHandle;
	BOOLEAN		bVcOwnerIsAtmArp;
	NDIS_STATUS	Status;

	AA_STRUCT_ASSERT(pVc, avc);
	AA_ASSERT(pVc->RefCount > 0);

	rv = --(pVc->RefCount);
	if (rv == 0)
	{
#ifdef VC_REFS_ON_SENDS
		NdisVcHandle = pVc->NdisVcHandle;
		bVcOwnerIsAtmArp = AA_IS_FLAG_SET(pVc->Flags,
										  AA_VC_OWNER_MASK,
										  AA_VC_OWNER_IS_ATMARP);
#endif // VC_REFS_ON_SENDS

		AA_RELEASE_VC_LOCK(pVc);
		AtmArpDeallocateVc(pVc);

#ifdef VC_REFS_ON_SENDS
		if ((NdisVcHandle != NULL) &&
			(bVcOwnerIsAtmArp))
		{
			Status = NdisCoDeleteVc(NdisVcHandle);
			AA_ASSERT(Status == NDIS_STATUS_SUCCESS);
			AADEBUGP(AAD_LOUD, ("DereferenceVc 0x%x, deleted NdisVcHandle 0x%x\n",
							pVc, NdisVcHandle));
		}
#endif // VC_REFS_ON_SENDS
	}

	AADEBUGP(AAD_VERY_LOUD, ("Dereference Vc 0x%x, New RefCount %d\n", pVc, rv));

	return (rv);
}




PATMARP_ATM_ENTRY
AtmArpAllocateAtmEntry(
	IN	PATMARP_INTERFACE			pInterface,
	IN	BOOLEAN						IsMulticast
)
/*++

Routine Description:

	Allocate an ATM Entry structure, initialize it, and return it.

Arguments:

	pInterface		- Pointer to ATMARP interface on which the entry is allocated
	IsMulticast		- Is this a Multicast entry?

Return Value:

	Pointer to allocated ATM Entry structure if successful, NULL otherwise.

--*/
{
	PATMARP_ATM_ENTRY			pAtmEntry;
	ULONG						Size;

	AA_STRUCT_ASSERT(pInterface, aai);

	Size = sizeof(ATMARP_ATM_ENTRY)
#ifdef IPMCAST
			 	+ (IsMulticast? sizeof(ATMARP_IPMC_ATM_INFO): 0);
#else
				;
#endif

	AA_ALLOC_MEM(pAtmEntry, ATMARP_ATM_ENTRY, Size);
	if (pAtmEntry != NULL_PATMARP_ATM_ENTRY)
	{
		AA_SET_MEM(pAtmEntry, 0, Size);
#if DBG
		pAtmEntry->aae_sig = aae_signature;
#endif
		pAtmEntry->Flags = AA_ATM_ENTRY_IDLE;
#ifdef IPMCAST
		if (IsMulticast)
		{
			pAtmEntry->Flags |= AA_ATM_ENTRY_TYPE_NUCAST;
			pAtmEntry->pMcAtmInfo = (PATMARP_IPMC_ATM_INFO)
										((PUCHAR)pAtmEntry + sizeof(ATMARP_ATM_ENTRY));
		}
#endif // IPMCAST
		AA_INIT_AE_LOCK(pAtmEntry);
		pAtmEntry->pInterface = pInterface;

	}

	AADEBUGP(AAD_INFO, ("Allocated ATM Entry: IF 0x%x, Entry 0x%x\n",
				pInterface, pAtmEntry));

	return (pAtmEntry);
}



VOID
AtmArpDeallocateAtmEntry(
	IN	PATMARP_ATM_ENTRY			pAtmEntry
)
/*++

Routine Description:

	Free an ATMARP ATM Entry structure. It is assumed that all references
	to the structure have gone. We don't need any locks here.

Arguments:

	pAtmEntry		- Pointer to ATMARP ATM Entry to be freed.

Return Value:

	None

--*/
{
	AA_STRUCT_ASSERT(pAtmEntry, aae);
	AA_ASSERT(pAtmEntry->RefCount == 0);
	AA_ASSERT(pAtmEntry->pVcList == NULL_PATMARP_VC);
	AA_ASSERT(!AA_AE_IS_ALIVE(pAtmEntry));


#if DBG
	pAtmEntry->aae_sig++;
#endif

	AA_FREE_AE_LOCK(pAtmEntry);
	AA_FREE_MEM(pAtmEntry);

	AADEBUGP(AAD_INFO, ("Deallocated ATM Entry: 0x%x\n", pAtmEntry));
}




VOID
AtmArpReferenceAtmEntry(
	IN	PATMARP_ATM_ENTRY			pAtmEntry
)
/*++

Routine Description:

	Add a reference to the specified ATMARP Entry.
	NOTE: The caller is assumed to possess a lock for the Entry.

Arguments:

	pAtmEntry			- Pointer to the Entry to be referenced

Return Value:

	None

--*/
{
	AA_STRUCT_ASSERT(pAtmEntry, aae);

	pAtmEntry->RefCount++;

	AADEBUGP(AAD_VERY_LOUD, ("Referencing AtmEntry 0x%x, new count %d\n",
			 pAtmEntry, pAtmEntry->RefCount));
}




ULONG
AtmArpDereferenceAtmEntry(
	IN	PATMARP_ATM_ENTRY			pAtmEntry
)
/*++

Routine Description:

	Subtract a reference from the specified ATM Entry. If the Entry's
	reference count becomes zero, deallocate it.

	NOTE: The caller is assumed to possess a lock for the Entry.
	SIDE EFFECT: See Return Value below

Arguments:

	pAtmEntry			- Pointer to the Entry to be dereferenced.

Return Value:

	Is the new reference count.
	[IMPORTANT] If the Entry's reference count became zero, the Entry will be
	deallocated -- the Entry lock is, obviously, released in this case.

--*/
{
	ULONG					rc;
	PATMARP_INTERFACE		pInterface;

	AA_STRUCT_ASSERT(pAtmEntry, aae);
	AA_ASSERT(pAtmEntry->RefCount > 0);

	rc = --(pAtmEntry->RefCount);
	if (rc == 0)
	{
		PATMARP_ATM_ENTRY *	ppAtmEntry;

		//
		// We are most likely going to delete this entry...
		//
		// We must observe the protocol of 1st locking the list lock then
		// the pAtmEntry's lock, so this requires us to do the
		// release/lock/lock sequence below.
		//
		// Temporarly addref it again, to make sure that when we
		// release the lock below someone else doesn't get confused.
		//
		pAtmEntry->RefCount++;

		pInterface = pAtmEntry->pInterface;
		AA_STRUCT_ASSERT(pInterface, aai);
		AA_RELEASE_AE_LOCK(pAtmEntry);

		//
		// No locks held at this time!
		//

		//
		// Acquire locks in the correct order...
		//
		AA_ACQUIRE_IF_ATM_LIST_LOCK(pInterface);
		AA_ACQUIRE_AE_LOCK(pAtmEntry);

		AA_ASSERT(pAtmEntry->RefCount > 0);
		rc = --(pAtmEntry->RefCount);

		//
		// We can't assume that the ref count is still zero -- in principle
		// someone may have addrefd this pAtmEntry while both locks
		// were released above...
		//
		if (rc == 0)
		{
			//
			//  Unlink this entry from the Interface's list of ATM Entries.
			//

			ppAtmEntry = &(pInterface->pAtmEntryList);
			while (*ppAtmEntry != pAtmEntry)
			{
				AA_ASSERT(*ppAtmEntry != NULL_PATMARP_ATM_ENTRY);
				ppAtmEntry = &((*ppAtmEntry)->pNext);
			}
	
			*ppAtmEntry = pAtmEntry->pNext;

			//
			// Set state back to idle -- AtmArpDeallocate checks this...
			//
			AA_SET_FLAG(
				pAtmEntry->Flags,
				AA_ATM_ENTRY_STATE_MASK,
				AA_ATM_ENTRY_IDLE
				);
		}
		AA_RELEASE_AE_LOCK(pAtmEntry);
		AA_RELEASE_IF_ATM_LIST_LOCK(pInterface);

		if (rc == 0)
		{
			AtmArpDeallocateAtmEntry(pAtmEntry);
		}
		else
		{
			//
			// Caller expects to still hold the lock on pAtmEntry!
			// if we return nonzero rc ...
			// We can't simply re-acquire the lock because the caller expects
			// the that lock was never released.
			// So, since the ref count had gone to zero, as far as the caller
			// is concerned this structure has gone away and so we lie
			// and return 0 here...
			//
			rc = 0;
		}
	}

	AADEBUGP(AAD_VERY_LOUD,
		 ("Dereference AtmEntry 0x%x, New RefCount %d\n", pAtmEntry, rc));

	return (rc);
}



PATMARP_IP_ENTRY
AtmArpAllocateIPEntry(
	IN	PATMARP_INTERFACE			pInterface
)
/*++

Routine Description:

	Allocate an ATMARP IP Entry structure, initialize it, and
	return it.

Arguments:

	pInterface		- Pointer to ATMARP Interface on which this IP
					  Entry is allocated.

Return Value:

	Pointer to allocated IP Entry structure if successful,
	NULL otherwise.

--*/
{
	PATMARP_IP_ENTRY		pIpEntry;

	AA_ALLOC_MEM(pIpEntry, ATMARP_IP_ENTRY, sizeof(ATMARP_IP_ENTRY));

	if (pIpEntry != NULL_PATMARP_IP_ENTRY)
	{
		AA_SET_MEM(pIpEntry, 0, sizeof(ATMARP_IP_ENTRY));
#if DBG
		pIpEntry->aip_sig = aip_signature;
#endif // DBG
		pIpEntry->pInterface = pInterface;
		pIpEntry->Flags = AA_IP_ENTRY_IDLE;
#ifdef IPMCAST
		pIpEntry->NextMultiSeq = AA_MARS_INITIAL_Y;	// Init on allocation
#endif
		AA_INIT_IE_LOCK(pIpEntry);
	}

	AADEBUGP(AAD_VERY_LOUD, ("Allocated IP Entry 0x%x\n", pIpEntry));
	return (pIpEntry);
}




VOID
AtmArpDeallocateIPEntry(
	IN	PATMARP_IP_ENTRY			pIpEntry
)
/*++

Routine Description:

	Deallocate an ATMARP IP Entry. It is assumed that all references
	to this IP Entry have gone, so there is no need to acquire its
	lock.

Arguments:

	pIpEntry			- Pointer to the IP Entry to be deallocated.

Return Value:

	None

--*/
{
	AA_STRUCT_ASSERT(pIpEntry, aip);
	AA_ASSERT(pIpEntry->RefCount == 0);
	AA_ASSERT(!AA_IE_IS_ALIVE(pIpEntry));
	AA_ASSERT(!AA_IS_TIMER_ACTIVE(&pIpEntry->Timer));

	AA_CHECK_TIMER_IN_ACTIVE_LIST(&pIpEntry->Timer, pIpEntry->pInterface, pIpEntry, "IP Entry");

#if DBG
	pIpEntry->aip_sig = ~(pIpEntry->aip_sig);
#endif // DBG

	AA_FREE_IE_LOCK(pIpEntry);
	AA_FREE_MEM(pIpEntry);

	AADEBUGP(AAD_LOUD, ("Deallocated IP Entry 0x%x\n", pIpEntry));

}




VOID
AtmArpReferenceIPEntry(
	IN	PATMARP_IP_ENTRY			pIpEntry
)
/*++

Routine Description:

	Add a reference to an ATMARP IP Entry.
	NOTE: The caller is assumed to possess a lock for the IP Entry.

Arguments:

	pIpEntry			- Pointer to an ATMARP IP Entry.

Return Value:

	None

--*/
{
	AA_STRUCT_ASSERT(pIpEntry, aip);

	pIpEntry->RefCount++;

	AADEBUGP(AAD_VERY_LOUD, ("Referenced IP Entry 0x%x, new count %d\n",
			pIpEntry, pIpEntry->RefCount));
}



ULONG
AtmArpDereferenceIPEntry(
	IN	PATMARP_IP_ENTRY			pIpEntry
)
/*++

Routine Description:

	Subtract a reference from an ATMARP IP Entry. If the reference
	count becomes zero, deallocate it.
	NOTE: It is assumed that the caller holds a lock to the IP Entry.
	See SIDE EFFECT below.

Arguments:

	pIpEntry			- Pointer to ATMARP IP Entry

Return Value:

	The resulting reference count. If this is zero, then there are two
	SIDE EFFECTS: (1) the IP Entry lock is released (2) the structure
	is freed.

--*/
{
	ULONG		rc;

	AA_STRUCT_ASSERT(pIpEntry, aip);

	rc = --(pIpEntry->RefCount);

	if (rc == 0)
	{
		AA_RELEASE_IE_LOCK(pIpEntry);
		AtmArpDeallocateIPEntry(pIpEntry);
	}

	AADEBUGP(AAD_VERY_LOUD, ("Dereference IP Entry 0x%x: new count %d\n",
			pIpEntry, rc));

	return (rc);
}




PATMARP_INTERFACE
AtmArpAllocateInterface(
	IN	PATMARP_ADAPTER				pAdapter
)
/*++

Routine Description:

	Allocate an ATMARP interface structure, initialize it, link it to
	the given adapter structure, and return it.

Arguments:

	None.

Return Value:

	Pointer to ATMARP interface structure, if successful, else NULL.

--*/
{
	PATMARP_INTERFACE		pInterface;
	PATMARP_IP_ENTRY *		pArpTable;
	PATMARP_TIMER_LIST		pTimerList;
	NDIS_STATUS				Status;
	PCO_SAP					pIfSap;
	ULONG					SapSize;
	PWSTR					pIPConfigBuffer;
	USHORT					ConfigBufferSize;
	INT						i;

	//
	//  Initialize
	//
	Status = NDIS_STATUS_SUCCESS;
	pInterface = NULL_PATMARP_INTERFACE;
	pArpTable = (PATMARP_IP_ENTRY *)NULL;
	pIfSap = (PCO_SAP)NULL;
	pIPConfigBuffer = (PWSTR)NULL;

	SapSize = sizeof(CO_SAP)+sizeof(ATM_SAP)+sizeof(ATM_ADDRESS);
	ConfigBufferSize = MAX_IP_CONFIG_STRING_LEN * sizeof(WCHAR);

	do
	{
		//
		//  Allocate everything.
		//
		AA_ALLOC_MEM(pInterface, ATMARP_INTERFACE, sizeof(ATMARP_INTERFACE));
		AA_ALLOC_MEM(pArpTable, PATMARP_IP_ENTRY, ATMARP_TABLE_SIZE*sizeof(PATMARP_IP_ENTRY));
		AA_ALLOC_MEM(pIfSap, CO_SAP, SapSize);

		if ((pInterface == NULL_PATMARP_INTERFACE) ||
			(pArpTable == (PATMARP_IP_ENTRY *)NULL) ||
			(pIfSap == (PCO_SAP)NULL))
		{
			Status = NDIS_STATUS_RESOURCES;
			break;
		}

		//
		//  Got (almost) everything allocated. Initialize the main IF structure
		//  first. IMPORTANT: Keep this SET_MEM right here! Otherwise, we will
		//  trash the timer list allocation coming right up.
		//
		AA_SET_MEM(pInterface, 0, sizeof(ATMARP_INTERFACE));

		//
		// Set up the Buffer pool quadword-aligned slist pointers.
		//
		{
			for (i=0;i<AA_HEADER_TYPE_MAX;i++)
			{
				//
				// Verify that HeaderBufList is 8-byte aligned...
				// (we fully expect it to be because HeaderBufList is of type
				// SLIST_HEADER which has longlong alignment) -- so that
				// if the INTERFACE structure in which it is embedded in is
				// 8-byte aligned, so will  HeaderBufList...).
				//
				ASSERT((((ULONG_PTR)&(pInterface->HeaderPool[i].HeaderBufList))
						& 0x7) == 0);

				//
				// Spec says you gotta init it...
				//
				AA_INIT_SLIST(&(pInterface->HeaderPool[i].HeaderBufList));
			}
		}

		//
		//  Allocate timer structures
		//
		for (i = 0; i < AAT_CLASS_MAX; i++)
		{
			pTimerList = &(pInterface->TimerList[i]);
#if DBG
			pTimerList->atl_sig = atl_signature;
#endif // DBG
			AA_ALLOC_MEM(
					pTimerList->pTimers,
					ATMARP_TIMER, 
					sizeof(ATMARP_TIMER) * AtmArpTimerListSize[i]
					);
			if (pTimerList->pTimers == NULL_PATMARP_TIMER)
			{
				Status = NDIS_STATUS_RESOURCES;
				break;
			}
		}

		if (Status != NDIS_STATUS_SUCCESS)
		{
			break;
		}
		
		//
		//  Continue initializing the IF structure.
		//
#if DBG
		//
		//  Signatures, for debugging.
		//
		pInterface->aai_sig =  aai_signature;
		pInterface->aaim_sig = aaim_signature;
		pInterface->aaia_sig = aaia_signature;
		pInterface->aait_sig =  aait_signature;
		pInterface->aaio_sig = aaio_signature;
		pInterface->aaic_sig = aaic_signature;

		pInterface->SapList.aas_sig = aas_signature;
#if ATMARP_WMI
		pInterface->aaiw_sig = aaiw_signature;
#endif
#endif // DBG


		//
		//  Initialize state fields.
		//
		pInterface->AdminState = IF_STATUS_DOWN;
		pInterface->State = IF_STATUS_DOWN;
		pInterface->LastChangeTime = GetTimeTicks();
		pInterface->ReconfigState = RECONFIG_NOT_IN_PROGRESS;

		//
		//  Initialize IP interface fields.
		//
		pInterface->BroadcastMask = 0;
		pInterface->BroadcastAddress = IP_LOCAL_BCST;
#ifndef OLD_ENTITY_LIST
		pInterface->ATInstance = INVALID_ENTITY_INSTANCE;
		pInterface->IFInstance = INVALID_ENTITY_INSTANCE;
#endif // OLD_ENTITY_LIST

		//
		//  Initialize spinlocks.
		//
		AA_INIT_IF_LOCK(pInterface);
		AA_INIT_IF_TABLE_LOCK(pInterface);
		AA_INIT_IF_ATM_LIST_LOCK(pInterface);
		AA_INIT_IF_TIMER_LOCK(pInterface);
		AA_INIT_BLOCK_STRUCT(&(pInterface->Block));
		NdisAllocateSpinLock(&(pInterface->BufferLock));

		//
		// Initialize list and table status
		//
		pInterface->AtmEntryListUp 	= TRUE;
		pInterface->ArpTableUp 		= TRUE;

		//
		//  Initialize timer wheels.
		//
		for (i = 0; i < AAT_CLASS_MAX; i++)
		{
			pTimerList = &(pInterface->TimerList[i]);
			AA_SET_MEM(
				pTimerList->pTimers,
				0,
				sizeof(ATMARP_TIMER) * AtmArpTimerListSize[i]
				);
			pTimerList->MaxTimer = AtmArpMaxTimerValue[i];
			pTimerList->TimerPeriod = AtmArpTimerPeriod[i];
			pTimerList->ListContext = (PVOID)pInterface;
			pTimerList->TimerListSize = AtmArpTimerListSize[i];

			AA_INIT_SYSTEM_TIMER(
						&(pTimerList->NdisTimer),
						AtmArpTickHandler,
						(PVOID)pTimerList
						);
		}


		//
		//  Initialize all sub-components.
		//
		AA_SET_MEM(pArpTable, 0, ATMARP_TABLE_SIZE*sizeof(PATMARP_IP_ENTRY));
		AA_SET_MEM(pIfSap, 0, SapSize);

		//
		//  Link all sub-components to the Interface structure.
		//
		pInterface->pArpTable = pArpTable;
		pInterface->SapList.pInfo = pIfSap;

		//
		//  Link the Interface to the Adapter.
		//
		pInterface->pAdapter = pAdapter;
		pInterface->pNextInterface = pAdapter->pInterfaceList;
		pAdapter->pInterfaceList = pInterface;

		//
		//  Cache the adapter handle.
		//
		pInterface->NdisAdapterHandle = pAdapter->NdisAdapterHandle;


		Status = NDIS_STATUS_SUCCESS;
		break;
	}
	while (FALSE);


	if (Status != NDIS_STATUS_SUCCESS)
	{
		//
		//  Failed to allocate atleast one component. Free the other(s).
		//
		if (pInterface != NULL_PATMARP_INTERFACE)
		{
			for (i = 0; i < AAT_CLASS_MAX; i++)
			{
				pTimerList = &(pInterface->TimerList[i]);
	
				if (pTimerList->pTimers != NULL_PATMARP_TIMER)
				{
					AA_FREE_MEM(pTimerList->pTimers);
					pTimerList->pTimers = NULL_PATMARP_TIMER;
				}
			}
	
			AA_FREE_MEM(pInterface);
			pInterface = NULL_PATMARP_INTERFACE;	// return value
		}

		if (pArpTable != (PATMARP_IP_ENTRY *)NULL)
		{
			AA_FREE_MEM(pArpTable);
		}

		if (pIfSap != (PCO_SAP)NULL)
		{
			AA_FREE_MEM(pIfSap);
		}
	}

	AADEBUGP(AAD_VERY_LOUD, ("Allocated ATMARP Interface 0x%x\n", pInterface));

	return (pInterface);

}




VOID
AtmArpDeallocateInterface(
	IN	PATMARP_INTERFACE			pInterface
)
/*++

Routine Description:

	Deallocate an ATMARP Interface structure. It is assumed that all
	references to this structure have gone, so it is not necessary
	to acquire a lock to it.

	Also delink this from the adapter structure it's linked to.

Arguments:

	pInterface		- Pointer to Interface structure to be deallocated.

Return Value:

	None

--*/
{
	PATMARP_INTERFACE	*	ppInterface;
	PATMARP_ADAPTER			pAdapter;
	PATMARP_SAP				pAtmArpSap;
	PIP_ADDRESS_ENTRY		pIpEntry;
	PATMARP_SERVER_ENTRY	pServerEntry;
	PPROXY_ARP_ENTRY		pProxyEntry;
	PATMARP_ATM_ENTRY		pAtmEntry;
	PATMARP_VC				pVc;
	INT						i;

	PVOID					pNext;		// Catch-all for all list traversals

	AA_STRUCT_ASSERT(pInterface, aai);
	AA_ASSERT(pInterface->RefCount == 0);

	AADEBUGP(AAD_INFO, ("Deallocate Interface 0x%x\n", pInterface));

#if DBG
	pInterface->aai_sig =  ~(pInterface->aai_sig);
#endif // DBG

	//
	//  Unlink from Adapter structure
	//
	AA_ACQUIRE_GLOBAL_LOCK(pAtmArpGlobalInfo);
	pAdapter = pInterface->pAdapter;
	if (pAdapter != NULL_PATMARP_ADAPTER)
	{
		ppInterface = &(pAdapter->pInterfaceList);
		while (*ppInterface != NULL_PATMARP_INTERFACE)
		{
			if (*ppInterface == pInterface)
			{
				*ppInterface = pInterface->pNextInterface;
				break;
			}
			else
			{
				ppInterface = &((*ppInterface)->pNextInterface);
			}
		}
	}
	AA_RELEASE_GLOBAL_LOCK(pAtmArpGlobalInfo);

	//
	//  Free all subcomponents
	//

	//
	//  ARP Table
	//
	if (pInterface->pArpTable != (PATMARP_IP_ENTRY *)NULL)
	{
		AA_FREE_MEM(pInterface->pArpTable);
		pInterface->pArpTable = (PATMARP_IP_ENTRY *)NULL;
	}

	//
	//  Local SAP list
	//
	for (pAtmArpSap = pInterface->SapList.pNextSap;
		 pAtmArpSap != NULL_PATMARP_SAP;
		 pAtmArpSap = (PATMARP_SAP)pNext)
	{
		pNext = (PVOID)(pAtmArpSap->pNextSap);
		if (pAtmArpSap->pInfo != (PCO_SAP)NULL)
		{
			AA_FREE_MEM(pAtmArpSap->pInfo);
		}
		AA_FREE_MEM(pAtmArpSap);
	}
	if (pInterface->SapList.pInfo != (PCO_SAP)NULL)
	{
		AA_FREE_MEM(pInterface->SapList.pInfo);
	}
	pInterface->SapList.pNextSap = NULL_PATMARP_SAP;


	//
	//  List of local IP addresses
	//
	for (pIpEntry = pInterface->LocalIPAddress.pNext;
		 pIpEntry != (PIP_ADDRESS_ENTRY)NULL;
		 pIpEntry = (PIP_ADDRESS_ENTRY)pNext)
	{
		pNext = (PVOID)pIpEntry->pNext;
		AA_FREE_MEM(pIpEntry);
	}


	//
	//  List of proxy ARP addresses
	//
	for (pProxyEntry = pInterface->pProxyList;
		 pProxyEntry != (PPROXY_ARP_ENTRY)NULL;
		 pProxyEntry = (PPROXY_ARP_ENTRY)pNext)
	{
		pNext = (PVOID)pProxyEntry->pNext;
		AA_FREE_MEM(pProxyEntry);
	}
	pInterface->pProxyList = (PPROXY_ARP_ENTRY)NULL;

	//
	//  List of ARP Server addresses
	//
	for (pServerEntry = pInterface->ArpServerList.pList;
		 pServerEntry != NULL_PATMARP_SERVER_ENTRY;
		 pServerEntry = (PATMARP_SERVER_ENTRY)pNext)
	{
		pNext = (PVOID)pServerEntry->pNext;
		AA_FREE_MEM(pServerEntry);
	}
	pInterface->ArpServerList.pList = NULL_PATMARP_SERVER_ENTRY;

#ifdef IPMCAST
	//
	//  List of MARS Server addresses
	//
	for (pServerEntry = pInterface->MARSList.pList;
		 pServerEntry != NULL_PATMARP_SERVER_ENTRY;
		 pServerEntry = (PATMARP_SERVER_ENTRY)pNext)
	{
		pNext = (PVOID)pServerEntry->pNext;
		AA_FREE_MEM(pServerEntry);
	}
	pInterface->MARSList.pList = NULL_PATMARP_SERVER_ENTRY;
#endif // IPMCAST

	//
	//  ARP Table
	//
	if (pInterface->pArpTable != (PATMARP_IP_ENTRY *)NULL)
	{
		AA_FREE_MEM(pInterface->pArpTable);
		pInterface->pArpTable = (PATMARP_IP_ENTRY *)NULL;
	}

	//
	//  ATM Entry List
	//
	for (pAtmEntry = pInterface->pAtmEntryList;
		 pAtmEntry != NULL_PATMARP_ATM_ENTRY;
		 pAtmEntry = (PATMARP_ATM_ENTRY)pNext)
	{
		pNext = (PVOID)pAtmEntry->pNext;
		AA_FREE_MEM(pAtmEntry);
	}
	pInterface->pAtmEntryList = NULL_PATMARP_ATM_ENTRY;

	//
	//  Unresolved VC list
	//
	for (pVc = pInterface->pUnresolvedVcs;
		 pVc != NULL_PATMARP_VC;
		 pVc = (PATMARP_VC)pNext)
	{
		pNext = (PVOID)pVc->pNextVc;
		AA_FREE_MEM(pVc);
	}
	pInterface->pUnresolvedVcs = (PATMARP_VC)NULL;

	//
	//  Timers
	//
	for (i = 0; i < AAT_CLASS_MAX; i++)
	{
		PATMARP_TIMER_LIST	pTimerList = &(pInterface->TimerList[i]);

		if (pTimerList->pTimers != NULL_PATMARP_TIMER)
		{
			AA_FREE_MEM(pTimerList->pTimers);
			pTimerList->pTimers = NULL_PATMARP_TIMER;
		}
	}

	//
	//  ProtocolPacketPool
	//  ProtocolBufferPool
	//  ProtocolBufList
	//
	AtmArpDeallocateProtoBuffers(pInterface);

	//
	//  HeaderBufList
	//  pHeaderTrkList
	//
	AtmArpDeallocateHeaderBuffers(pInterface);

	//
	//  Free all Interface locks.
	//
	AA_FREE_IF_LOCK(pInterface);
	AA_FREE_IF_TABLE_LOCK(pInterface);
	AA_FREE_IF_ATM_LIST_LOCK(pInterface);
	AA_FREE_IF_TIMER_LOCK(pInterface);
	AA_FREE_BLOCK_STRUCT(&(pInterface->Block));
	NdisFreeSpinLock(&(pInterface->BufferLock));

	//
	//  Free the Interface structure now
	//
	AA_FREE_MEM(pInterface);

	//
	//  If we just freed the last Interface structure on this
	//  adapter, and an Unbind operation was in progress, complete
	//  it now.
	//
	if ((pAdapter->pInterfaceList == NULL_PATMARP_INTERFACE) &&
		(pAdapter->Flags & AA_ADAPTER_FLAGS_UNBINDING))
	{
		AtmArpCompleteUnbindAdapter(pAdapter);
	}
}




VOID
AtmArpReferenceInterface(
	IN	PATMARP_INTERFACE			pInterface
)
/*++

Routine Description:

	Add a reference to an Interface structure.
	NOTE: The caller is assumed to possess a lock for the interface
	structure.

Arguments:

	pInterface		- Pointer to the ATMARP interface

Return Value:

	None

--*/
{
	AA_STRUCT_ASSERT(pInterface, aai);

	pInterface->RefCount++;

	AADEBUGP(AAD_VERY_LOUD, ("Reference Interface 0x%x, new count %d\n",
			pInterface, pInterface->RefCount));
}




ULONG
AtmArpDereferenceInterface(
	IN	PATMARP_INTERFACE			pInterface
)
/*++

Routine Description:

	Subtract a reference from an ATMARP Interface. If the reference
	count becomes zero, deallocate it.
	NOTE: It is assumed that the caller holds a lock to the Interface
	structure. See SIDE EFFECT below.

Arguments:

	pInterface		- Pointer to the ATMARP interface

Return Value:

	The resulting reference count. If this is zero, then there are two
	SIDE EFFECTS: (1) the Interface lock is released (2) the structure
	is freed.

--*/
{
	ULONG		rc;

	AA_STRUCT_ASSERT(pInterface, aai);
	AA_ASSERT(pInterface->RefCount > 0);

	rc = --(pInterface->RefCount);

	AADEBUGP(AAD_VERY_LOUD, ("Dereference Interface 0x%x, new count %d\n",
			pInterface, rc));

	if (rc == 0)
	{
		AA_RELEASE_IF_LOCK(pInterface);
		AtmArpDeallocateInterface(pInterface);
	}


	return (rc);
}



VOID
AtmArpReferenceJoinEntry(
	IN	PATMARP_IPMC_JOIN_ENTRY		pJoinEntry
)
/*++

Routine Description:

	Add a reference to a Join Entry.

Arguments:

	pJoinEntry		- Pointer to Join Entry

Return Value:

	None

--*/
{
	NdisInterlockedIncrement(&pJoinEntry->RefCount);
}


ULONG
AtmArpDereferenceJoinEntry(
	IN	PATMARP_IPMC_JOIN_ENTRY		pJoinEntry
)
/*++

Routine Description:

	Decrements the ref count on a Join Entry. If it goes down to zero,
	deallocates it.

Arguments:

	pJoinEntry		- Pointer to the Join Entry

Return Value:

	The final ref count

--*/
{
	ULONG		rc;

	rc = NdisInterlockedDecrement(&pJoinEntry->RefCount);

	if (rc == 0)
	{
		AA_CHECK_TIMER_IN_ACTIVE_LIST(&pJoinEntry->Timer, pJoinEntry->pInterface, pJoinEntry, "Join Entry");
		AA_FREE_MEM(pJoinEntry);
	}

	return (rc);
}


VOID
AtmArpStartTimer(
	IN	PATMARP_INTERFACE			pInterface,
	IN	PATMARP_TIMER				pTimer,
	IN	ATMARP_TIMEOUT_HANDLER		TimeoutHandler,
	IN	ULONG						SecondsToGo,
	IN	PVOID						Context
)
/*++

Routine Description:

	Start an ATMARP timer. Based on the length (SecondsToGo) of the
	timer, we decide on whether to insert it in the short duration
	timer list or in the long duration timer list in the Interface
	structure.

	NOTE: the caller is assumed to either hold a lock to the structure
	that contains the timer, or ensure that it is safe to access the
	timer structure.

Arguments:

	pInterface		- Pointer to the ATMARP Interface
	pTimer			- Pointer to ATMARP Timer structure
	TimeoutHandler	- Handler function to be called if this timer expires
	SecondsToGo		- When does this timer go off?
	Context			- To be passed to timeout handler if this timer expires

Return Value:

	None

--*/
{
	PATMARP_TIMER_LIST	pTimerList;		// List to which this timer goes
	PATMARP_TIMER		pTimerListHead; // Head of above list
	ULONG				Index;			// Into timer wheel
	ULONG				TicksToGo;
	INT					i;

	AA_STRUCT_ASSERT(pInterface, aai);


 	AADEBUGP(AAD_EXTRA_LOUD,
 	 ("StartTimer: pIf 0x%x, Secs %d, Handler 0x%x, Ctxt 0x%x, pTimer 0x%x\n",
 	 			pInterface, SecondsToGo, TimeoutHandler, Context, pTimer));


	if (AA_IS_TIMER_ACTIVE(pTimer))
	{
		AADEBUGP(AAD_ERROR,
			("Start timer: pTimer 0x%x: is active (list 0x%x, hnd 0x%x), stopping it\n",
				pTimer, pTimer->pTimerList, pTimer->TimeoutHandler));

		AtmArpStopTimer(pTimer, pInterface);
	}

	AA_ACQUIRE_IF_TIMER_LOCK(pInterface);
	AA_ASSERT(!AA_IS_TIMER_ACTIVE(pTimer));

	//
	//  Find the list to which this timer should go, and the
	//  offset (TicksToGo)
	//
	for (i = 0; i < AAT_CLASS_MAX; i++)
	{
		pTimerList = &(pInterface->TimerList[i]);
		if (SecondsToGo < pTimerList->MaxTimer)
		{
			//
			//  Found it.
			//
			TicksToGo = SecondsToGo / (pTimerList->TimerPeriod);
			break;
		}
	}
	
	AA_ASSERT(i < AAT_CLASS_MAX);

	AA_VALIDATE_TIMER_LIST(pTimerList);
	//
	//  Find the position in the list for this timer
	//
	Index = pTimerList->CurrentTick + TicksToGo;
	if (Index >= pTimerList->TimerListSize)
	{
		Index -= pTimerList->TimerListSize;
	}
	AA_ASSERT(Index < pTimerList->TimerListSize);

	pTimerListHead = &(pTimerList->pTimers[Index]);

	//
	//  Fill in the timer
	//
	pTimer->pTimerList = pTimerList;
	pTimer->LastRefreshTime = pTimerList->CurrentTick;
	pTimer->Duration = TicksToGo;
	pTimer->TimeoutHandler = TimeoutHandler;
	pTimer->Context = Context;
	pTimer->State = ATMARP_TIMER_STATE_RUNNING;
 
 	//
 	//  Insert this timer in the "ticking" list
 	//
 	pTimer->pPrevTimer = pTimerListHead;
 	pTimer->pNextTimer = pTimerListHead->pNextTimer;
 	if (pTimer->pNextTimer != NULL_PATMARP_TIMER)
 	{
 		pTimer->pNextTimer->pPrevTimer = pTimer;
 	}
 	pTimerListHead->pNextTimer = pTimer;

	//
	//  Start off the system tick timer if necessary.
	//
	pTimerList->TimerCount++;
	if (pTimerList->TimerCount == 1)
	{
		AADEBUGP(AAD_LOUD,
			 ("StartTimer: Starting system timer 0x%x, class %d on IF 0x%x\n",
					&(pTimerList->NdisTimer), i, pInterface));

		AA_START_SYSTEM_TIMER(&(pTimerList->NdisTimer), pTimerList->TimerPeriod);
	}
	AA_VALIDATE_TIMER_LIST(pTimerList);
	AA_VALIDATE_TIMER(pTimerList, pTimer);

	AA_RELEASE_IF_TIMER_LOCK(pInterface);

	//
	//  We're done
	//
	AADEBUGP(AAD_LOUD,
		 ("Started timer 0x%x, IF 0x%x, Secs %d, Index %d, Head 0x%x\n",
				pTimer,
				pInterface,
				SecondsToGo,
				Index,
				pTimerListHead));

	return;
}




BOOLEAN
AtmArpStopTimer(
	IN	PATMARP_TIMER				pTimer,
	IN	PATMARP_INTERFACE			pInterface
)
/*++

Routine Description:

	Stop an ATMARP timer, if it is running. We remove this timer from
	the active timer list and mark it so that we know it's not running.

	NOTE: the caller is assumed to either hold a lock to the structure
	that contains the timer, or ensure that it is safe to access the
	timer structure.

	SIDE EFFECT: If we happen to stop the last timer (of this "duration") on
	the Interface, we also stop the appropriate Tick function.

Arguments:

	pTimer			- Pointer to ATMARP Timer structure
	pInterface		- Pointer to interface to which the timer belongs

Return Value:

	TRUE if the timer was running, FALSE otherwise.

--*/
{
	PATMARP_TIMER_LIST	pTimerList;			// Timer List to which this timer belongs
	BOOLEAN				WasRunning;

	AADEBUGP(AAD_LOUD,
		 ("Stopping Timer 0x%x, IF 0x%x, List 0x%x, Prev 0x%x, Next 0x%x\n",
					pTimer,
					pInterface,
					pTimer->pTimerList,
					pTimer->pPrevTimer,
					pTimer->pNextTimer));

	AA_ACQUIRE_IF_TIMER_LOCK(pInterface);


	if (AA_IS_TIMER_ACTIVE(pTimer))
	{
		WasRunning = TRUE;

		AA_VALIDATE_TIMER_LIST(pTimer->pTimerList);
		AA_VALIDATE_TIMER(NULL, pTimer);

		//
		//  Unlink timer from the list
		//
		AA_ASSERT(pTimer->pPrevTimer);	// the list head always exists

		pTimer->pPrevTimer->pNextTimer = pTimer->pNextTimer;
		if (pTimer->pNextTimer)
		{
			pTimer->pNextTimer->pPrevTimer = pTimer->pPrevTimer;
		}

		pTimer->pNextTimer = pTimer->pPrevTimer = NULL_PATMARP_TIMER;

		//
		//  Update timer count on Interface, for this class of timers
		//
		pTimerList = pTimer->pTimerList;
		pTimerList->TimerCount--;

		//
		//  If all timers of this class are gone, stop the system tick timer
		//  for this class
		//
		if (pTimerList->TimerCount == 0)
		{
			AADEBUGP(AAD_LOUD, ("Stopping system timer 0x%x, List 0x%x, IF 0x%x\n",
						&(pTimerList->NdisTimer),
						pTimerList,
						pInterface));

			pTimerList->CurrentTick = 0;
			AA_STOP_SYSTEM_TIMER(&(pTimerList->NdisTimer));
		}

		//
		//  Mark stopped timer as not active
		//
		pTimer->pTimerList = (PATMARP_TIMER_LIST)NULL;

		pTimer->State = ATMARP_TIMER_STATE_IDLE;

		AA_VALIDATE_TIMER_LIST(pTimerList);

	}
	else
	{
		WasRunning = FALSE;
	}

	AA_RELEASE_IF_TIMER_LOCK(pInterface);

	return (WasRunning);
}




#ifdef NO_TIMER_MACRO

VOID
AtmArpRefreshTimer(
	IN	PATMARP_TIMER				pTimer
)
/*++

Routine Description:

	Refresh a timer that is already running.

	NOTE: The caller is assumed to possess a lock protecting the
	timer structure (i.e. to the structure containing the timer).

	NOTE: We don't acquire the IF Timer Lock here, to optimize
	the refresh operation. So, _within_ the confines of this routine,
	the tick handler may fire, and expire this timer. The only care
	that we take here is to make sure that we don't crash if the
	timer expires while we access the Timer list.

Arguments:

	pTimer		- Pointer to ATMARP_TIMER structure

Return Value:

	None

--*/
{
	PATMARP_TIMER_LIST	pTimerList;

	if ((pTimerList = pTimer->pTimerList) != (PATMARP_TIMER_LIST)NULL)
	{
		pTimer->LastRefreshTime = pTimerList->CurrentTick;
	}
	else
	{
		AADEBUGP(AAD_VERY_LOUD,
			 ("RefreshTimer: pTimer 0x%x not active: Hnd 0x%x, Cntxt 0x%x\n",
			 	pTimer,
			 	pTimer->TimeoutHandler,
			 	pTimer->Context
			 ));
	}

	AADEBUGP(AAD_LOUD,
		 ("Refreshed timer 0x%x, List 0x%x, hnd 0x%x, Cntxt 0x%x, LastRefresh %d\n",
				pTimer,
				pTimer->pTimerList,
				pTimer->TimeoutHandler,
				pTimer->Context,
				pTimer->LastRefreshTime));
}


#endif // NO_TIMER_MACRO


VOID
AtmArpTickHandler(
	IN	PVOID						SystemSpecific1,
	IN	PVOID						Context,
	IN	PVOID						SystemSpecific2,
	IN	PVOID						SystemSpecific3
)
/*++

Routine Description:

	This is the handler we register with the system for processing each
	Timer List. This is called every "tick" seconds, where "tick" is
	determined by the granularity of the timer type.

Arguments:

	Context				- Actually a pointer to a Timer List structure
	SystemSpecific[1-3]	- Not used

Return Value:

	None

--*/
{

	PATMARP_INTERFACE		pInterface;
	PATMARP_TIMER_LIST		pTimerList;

	PATMARP_TIMER			pExpiredTimer;		// Start of list of expired timers
	PATMARP_TIMER			pNextTimer;			// for walking above list
	PATMARP_TIMER			pTimer;				// temp, for walking timer list
	PATMARP_TIMER			pPrevExpiredTimer;	// for creating expired timer list

	ULONG					Index;			// into the timer wheel
	ULONG					NewIndex;		// for refreshed timers


	pTimerList = (PATMARP_TIMER_LIST)Context;
	AA_STRUCT_ASSERT(pTimerList, atl);

	pInterface = (PATMARP_INTERFACE)pTimerList->ListContext;
	AA_STRUCT_ASSERT(pInterface, aai);

	AADEBUGP(AAD_VERY_LOUD, ("Tick: pIf 0x%x, List 0x%x, Count %d\n",
				pInterface, pTimerList, pTimerList->TimerCount));

	pExpiredTimer = NULL_PATMARP_TIMER;

	AA_ACQUIRE_IF_TIMER_LOCK(pInterface);

	AA_VALIDATE_TIMER_LIST(pTimerList);

	if (pInterface->AdminState == IF_STATUS_UP)
	{
		//
		//  Pick up the list of timers scheduled to have expired at the
		//  current tick. Some of these might have been refreshed.
		//
		Index = pTimerList->CurrentTick;
		pExpiredTimer = (pTimerList->pTimers[Index]).pNextTimer;
		(pTimerList->pTimers[Index]).pNextTimer = NULL_PATMARP_TIMER;

		//
		//  Go through the list of timers scheduled to expire at this tick.
		//  Prepare a list of expired timers, using the pNextExpiredTimer
		//  link to chain them together.
		//
		//  Some timers may have been refreshed, in which case we reinsert
		//  them in the active timer list.
		//
		pPrevExpiredTimer = NULL_PATMARP_TIMER;

		for (pTimer = pExpiredTimer;
			 pTimer != NULL_PATMARP_TIMER;
			 pTimer = pNextTimer)
		{
			//
			// Save a pointer to the next timer, for the next iteration.
			//
			pNextTimer = pTimer->pNextTimer;

			AADEBUGP(AAD_EXTRA_LOUD, 
				("Tick Handler: pIf 0x%x, looking at timer 0x%x, next 0x%x\n",
					pInterface, pTimer, pNextTimer));

			//
			//  Find out when this timer should actually expire.
			//
			NewIndex = pTimer->LastRefreshTime + pTimer->Duration;
			if (NewIndex >= pTimerList->TimerListSize)
			{
				NewIndex -= pTimerList->TimerListSize;
			}

			//
			//  Check if we are currently at the point of expiry.
			//
			if (NewIndex != Index)
			{
				//
				//  This timer still has some way to go, so put it back.
				//
				AADEBUGP(AAD_LOUD,
				("Tick: Reinserting Timer 0x%x: Hnd 0x%x, Durn %d, Ind %d, NewInd %d\n",
					pTimer, pTimer->TimeoutHandler, pTimer->Duration, Index, NewIndex));

				//
				//  Remove it from the expired timer list. Note that we only
				//  need to update the forward (pNextExpiredTimer) links.
				//
				if (pPrevExpiredTimer == NULL_PATMARP_TIMER)
				{
					pExpiredTimer = pNextTimer;
				}
				else
				{
					pPrevExpiredTimer->pNextExpiredTimer = pNextTimer;
				}

				//
				//  And insert it back into the running timer list.
				//
				pTimer->pNextTimer = (pTimerList->pTimers[NewIndex]).pNextTimer;
				if (pTimer->pNextTimer != NULL_PATMARP_TIMER)
				{
					pTimer->pNextTimer->pPrevTimer = pTimer;
				}
				pTimer->pPrevTimer = &(pTimerList->pTimers[NewIndex]);
				(pTimerList->pTimers[NewIndex]).pNextTimer = pTimer;
			}
			else
			{
				//
				//  This one has expired. Keep it in the expired timer list.
				//
				pTimer->pNextExpiredTimer = pNextTimer;
				if (pPrevExpiredTimer == NULL_PATMARP_TIMER)
				{
					pExpiredTimer = pTimer;
				}
				pPrevExpiredTimer = pTimer;

				//
				//  Mark it as inactive.
				//
				AA_ASSERT(pTimer->pTimerList == pTimerList);
				pTimer->pTimerList = (PATMARP_TIMER_LIST)NULL;

				pTimer->State = ATMARP_TIMER_STATE_EXPIRING;

				//
				//  Update the active timer count.
				//
				pTimerList->TimerCount--;
			}
		}

		//
		//  Update current tick index in readiness for the next tick.
		//
		if (++Index == pTimerList->TimerListSize)
		{
			pTimerList->CurrentTick = 0;
		}
		else
		{
			pTimerList->CurrentTick = Index;
		}

		if (pTimerList->TimerCount > 0)
		{
			//
			//  Re-arm the tick handler
			//
			AADEBUGP(AAD_LOUD, ("Tick[%d]: Starting system timer 0x%x, on IF 0x%x\n",
						pTimerList->CurrentTick, &(pTimerList->NdisTimer), pInterface));
			
			AA_START_SYSTEM_TIMER(&(pTimerList->NdisTimer), pTimerList->TimerPeriod);
		}
		else
		{
			pTimerList->CurrentTick = 0;
		}

	}

	AA_RELEASE_IF_TIMER_LOCK(pInterface);

	//
	//  Now pExpiredTimer is a list of expired timers.
	//  Walk through the list and call the timeout handlers
	//  for each timer.
	//
	while (pExpiredTimer != NULL_PATMARP_TIMER)
	{
		pNextTimer = pExpiredTimer->pNextExpiredTimer;

		AADEBUGP(AAD_LOUD, ("Expired timer 0x%x: handler 0x%x, next 0x%x\n",
					pExpiredTimer, pExpiredTimer->TimeoutHandler, pNextTimer));

		pExpiredTimer->State = ATMARP_TIMER_STATE_EXPIRED;
		(*(pExpiredTimer->TimeoutHandler))(
				pExpiredTimer,
				pExpiredTimer->Context
			);

		pExpiredTimer = pNextTimer;
	}

}




PNDIS_PACKET
AtmArpAllocatePacket(
	IN	PATMARP_INTERFACE			pInterface
)
/*++

Routine Description:

	Allocate an NDIS packet for the specified Interface.
	Currently just a wrapper for the corresponding NDIS function.

Arguments:

	pInterface		- Pointer to ATMARP Interface structure

Return Value:

	Pointer to NDIS packet if allocated, NULL otherwise.

--*/
{
	NDIS_STATUS				Status;
	PNDIS_PACKET			pNdisPacket;
	struct PacketContext	*PC;

	NdisAllocatePacket(
			&Status,
			&pNdisPacket,
			pInterface->ProtocolPacketPool
		);

	if (pNdisPacket != (PNDIS_PACKET)NULL)
	{
		PC = (struct PacketContext *)pNdisPacket->ProtocolReserved;
		PC->pc_common.pc_owner = PACKET_OWNER_LINK;
	}

	AADEBUGP(AAD_EXTRA_LOUD, ("Allocate Packet: IF 0x%x, Status 0x%x, Packet 0x%x\n",
				pInterface,
				Status,
				pNdisPacket));

	return (pNdisPacket);
}



VOID
AtmArpFreePacket(
	IN	PATMARP_INTERFACE			pInterface,
	IN	PNDIS_PACKET				pPacket
)
/*++

Routine Description:

	Deallocate an NDIS packet on the specified Interface.
	Currently just a wrapper around the corresponding NDIS function.

Arguments:

	pInterface		- Pointer to ATMARP Interface structure
	pPacket			- Pointer to packet being freed.

Return Value:

	None

--*/
{
	NdisFreePacket(pPacket);

	AADEBUGP(AAD_EXTRA_LOUD, ("Free Packet: IF 0x%x, Packet 0x%x\n",
				pInterface,
				pPacket));
}




PNDIS_BUFFER
AtmArpGrowHeaders(
	IN	PATMARP_INTERFACE			pInterface,
	IN	AA_HEADER_TYPE				HdrType
)
/*++

Routine Description:

	Allocate a bunch of header buffers on the specified ATMARP interface.
	Return one of them.

	We allocate a new Buffer tracker structure, a new NDIS Buffer pool, and
	finally a chunk of system memory that we break down into header buffers.
	These header buffers are then attached to NDIS Buffers before they are
	inserted into the list of free header buffers for this Interface.

Arguments:

	pInterface		- Pointer to ATMARP Interface structure
	HdrType			- Unicast or Nonunicast

Return Value:

	Pointer to allocated NDIS buffer if successful, NULL otherwise.

--*/
{
	PATMARP_BUFFER_TRACKER		pTracker;		// for new set of buffers
	PUCHAR						pSpace;
	PNDIS_BUFFER				pNdisBuffer;
	PNDIS_BUFFER				pReturnBuffer;
	PNDIS_BUFFER				pBufferList;	// allocated list
	INT							i;				// iteration counter
	NDIS_STATUS					Status;

	AA_ASSERT(HdrType < AA_HEADER_TYPE_MAX);

	//
	//  Initialize
	//
	pTracker = NULL_PATMARP_BUFFER_TRACKER;
	pReturnBuffer = (PNDIS_BUFFER)NULL;


	NdisAcquireSpinLock(&pInterface->BufferLock);

	do
	{
		if (pInterface->HeaderPool[HdrType].CurHeaderBufs >= 
					pInterface->HeaderPool[HdrType].MaxHeaderBufs)
		{
			AADEBUGP(AAD_WARNING,
				("Grow Hdrs: IF 0x%x, Type %d, CurHdrBufs %d > MaxHdrBufs %d\n",
						pInterface,
						HdrType,
						pInterface->HeaderPool[HdrType].CurHeaderBufs,
						pInterface->HeaderPool[HdrType].MaxHeaderBufs));
			break;
		}

		//
		//  Allocate and initialize Buffer tracker
		//
		AA_ALLOC_MEM(pTracker, ATMARP_BUFFER_TRACKER, sizeof(ATMARP_BUFFER_TRACKER));
		if (pTracker == NULL_PATMARP_BUFFER_TRACKER)
		{
			AADEBUGP(AAD_WARNING, ("Grow Hdrs: IF 0x%x, alloc failed for tracker\n",
					pInterface));
			break;
		}

		AA_SET_MEM(pTracker, 0, sizeof(ATMARP_BUFFER_TRACKER));

		//
		//  Get the NDIS Buffer pool
		//
		NdisAllocateBufferPool(
				&Status,
				&(pTracker->NdisHandle),
				AA_DEF_HDRBUF_GROW_SIZE
			);

		if (Status != NDIS_STATUS_SUCCESS)
		{
			AADEBUGP(AAD_WARNING,
				 ("Grow Hdrs: IF 0x%x, NdisAllocateBufferPool err status 0x%x\n",
					pInterface, Status));
			break;
		}

		//
		//  Allocate system space for a bunch of header buffers
		//
		AA_ALLOC_MEM(pTracker->pPoolStart, 
					 UCHAR,
					 pInterface->HeaderPool[HdrType].HeaderBufSize *
						 AA_DEF_HDRBUF_GROW_SIZE);

		if (pTracker->pPoolStart == (PUCHAR)NULL)
		{
			AADEBUGP(AAD_WARNING,
				 ("Grow Hdrs: IF 0x%x, could not alloc buf space %d bytes\n",
					pInterface,
			 		pInterface->HeaderPool[HdrType].HeaderBufSize *
						 AA_DEF_HDRBUF_GROW_SIZE));
			break;
		}

		//
		//  Make NDIS buffers out of the allocated space, and put them
		//  into the free header buffer list. Retain one for returning
		//  to caller.
		//
		//  We also fill in the contents of the buffers right away, so
		//  that we don't have to prepare them afresh for each transmit.
		//
		pBufferList = (PNDIS_BUFFER)NULL;
		pSpace = pTracker->pPoolStart;
		for (i = 0; i < AA_DEF_HDRBUF_GROW_SIZE; i++)
		{
			if (HdrType == AA_HEADER_TYPE_UNICAST)
			{
				//
				//  Fill in the (Unicast) LLC/SNAP header
				//
				AA_COPY_MEM(pSpace,
							&AtmArpLlcSnapHeader,
							pInterface->HeaderPool[HdrType].HeaderBufSize);
			}
			else
			{
				AA_ASSERT(HdrType == AA_HEADER_TYPE_NUNICAST);
				//
				//  Fill in the (Multicast) Type 1 short form header
				//
#ifdef IPMCAST
				AA_COPY_MEM(pSpace,
							&AtmArpMcType1ShortHeader,
							pInterface->HeaderPool[HdrType].HeaderBufSize);
#else
				AA_ASSERT(FALSE);
#endif // IPMCAST
			}


			NdisAllocateBuffer(
					&Status,
					&pNdisBuffer,
					pTracker->NdisHandle,
					pSpace,
					pInterface->HeaderPool[HdrType].HeaderBufSize
				);

			if (Status != NDIS_STATUS_SUCCESS)
			{
				AADEBUGP(AAD_WARNING,
					 ("Grow Hdrs: NdisAllocateBuffer failed: IF 0x%x, status 0x%x\n",
							pInterface, Status));
				break;
			}

			if (i == 0)
			{
				pReturnBuffer = pNdisBuffer;
			}
			else
			{
				NDIS_BUFFER_LINKAGE(pNdisBuffer) = pBufferList;
				pBufferList = pNdisBuffer;
			}
			pSpace += pInterface->HeaderPool[HdrType].HeaderBufSize;
		}

		if (i > 0)
		{
			//
			//  Successfully allocated atleast one more header buffer
			//
			pTracker->pNext = pInterface->HeaderPool[HdrType].pHeaderTrkList;
			pInterface->HeaderPool[HdrType].pHeaderTrkList = pTracker;
			pInterface->HeaderPool[HdrType].CurHeaderBufs += i;

			NdisReleaseSpinLock(&pInterface->BufferLock);

			pNdisBuffer = pBufferList;
			while (pNdisBuffer != (PNDIS_BUFFER)NULL)
			{
				pBufferList = NDIS_BUFFER_LINKAGE(pNdisBuffer);
				NDIS_BUFFER_LINKAGE(pNdisBuffer) = NULL;
				AtmArpFreeHeader(pInterface, pNdisBuffer, HdrType);
				pNdisBuffer = pBufferList;
			}
		}

	} while (FALSE);

	if (pReturnBuffer == (PNDIS_BUFFER)NULL)
	{
		//
		//  Failed to allocate. Undo all.
		//
		NdisReleaseSpinLock(&pInterface->BufferLock);

		if (pTracker != NULL_PATMARP_BUFFER_TRACKER)
		{
			if (pTracker->pPoolStart != (PUCHAR)NULL)
			{
				AA_FREE_MEM(pTracker->pPoolStart);
			}
			if (pTracker->NdisHandle != (NDIS_HANDLE)NULL)
			{
				NdisFreeBufferPool(pTracker->NdisHandle);
			}
			AA_FREE_MEM(pTracker);
		}
	}

	AADEBUGP(AAD_INFO, ("Grow ARP Headers: IF 0x%x, RetBuf 0x%x, New Tracker 0x%x\n",
				pInterface, pReturnBuffer, pTracker));

	return (pReturnBuffer);

}




PNDIS_BUFFER
AtmArpAllocateHeader(
	IN	PATMARP_INTERFACE			pInterface,
	IN	AA_HEADER_TYPE				HdrType,
	OUT	PUCHAR *					pBufferAddress
)
/*++

Routine Description:

	Allocate an NDIS Buffer to be used as an LLC/SNAP header prepended
	to an IP packet. We pick up the buffer at the top of the pre-allocated
	buffer list, if one exists. Otherwise, we try to grow this list and
	allocate.

Arguments:

	pInterface		- Pointer to ATMARP Interface
	HdrType			- Unicast or Nonunicast
	pBufferAddress	- Place to return virtual address of allocated buffer

Return Value:

	Pointer to NDIS buffer if successful, NULL otherwise.

--*/
{
	PNDIS_BUFFER			pNdisBuffer;
	NDIS_STATUS				Status;
	ULONG					Length;
	PAA_SINGLE_LIST_ENTRY	pListEntry;

	pListEntry = AA_POP_FROM_SLIST(
						&(pInterface->HeaderPool[HdrType].HeaderBufList),
						&(pInterface->BufferLock.SpinLock)
					);
	if (pListEntry != NULL_PAA_SINGLE_LIST_ENTRY)
	{
		pNdisBuffer = STRUCT_OF(NDIS_BUFFER, pListEntry, Next);
		NDIS_BUFFER_LINKAGE(pNdisBuffer) = NULL;
		*pBufferAddress = NdisBufferVirtualAddress(pNdisBuffer);
	}
	else
	{
		pNdisBuffer = AtmArpGrowHeaders(pInterface, HdrType);
		if (pNdisBuffer != (PNDIS_BUFFER)NULL)
		{
			NDIS_BUFFER_LINKAGE(pNdisBuffer) = NULL;
			NdisQueryBuffer(pNdisBuffer, (PVOID)pBufferAddress, &Length);
			AADEBUGP(AAD_INFO,
				("After growing hdrs: Type %d, returning pNdisBuf 0x%x, Start 0x%x, Len %d\n",
					HdrType, pNdisBuffer, *pBufferAddress, Length));
		}
	}

	AADEBUGP(AAD_VERY_LOUD, ("Allocated Header Buffer: 0x%x, IF: 0x%x\n",
					pNdisBuffer, pInterface));
	return (pNdisBuffer);
}




VOID
AtmArpFreeHeader(
	IN	PATMARP_INTERFACE			pInterface,
	IN	PNDIS_BUFFER				pNdisBuffer,
	IN	AA_HEADER_TYPE				HdrType
)
/*++

Routine Description:

	Deallocate a header buffer.

Arguments:

	pInterface		- Pointer to ATMARP interface from which the buffer came
	pNdisBuffer		- Pointer to NDIS buffer being freed
	HdrType			- Unicast or Nonunicast

Return Value:

	None

--*/
{
	AA_PUSH_TO_SLIST(
			&(pInterface->HeaderPool[HdrType].HeaderBufList),
			STRUCT_OF(AA_SINGLE_LIST_ENTRY, &(pNdisBuffer->Next), Next),
			&(pInterface->BufferLock.SpinLock)
		);

	AADEBUGP(AAD_VERY_LOUD, ("Freed Header Buffer: 0x%x, IF: 0x%x, HdrType %d\n",
					pNdisBuffer, pInterface, HdrType));
}



VOID
AtmArpDeallocateHeaderBuffers(
	IN	PATMARP_INTERFACE			pInterface
)
/*++

Routine Description:

	Deallocate everything pertaining to header buffers on an Interface.

Arguments:

	pInterface			- Pointer to ATMARP Interface

Return Value:

	None

--*/
{
	PNDIS_BUFFER				pNdisBuffer;
	NDIS_STATUS					Status;
	PAA_SINGLE_LIST_ENTRY		pListEntry;
	PATMARP_BUFFER_TRACKER		pTracker;
	PATMARP_BUFFER_TRACKER		pNextTracker;
	AA_HEADER_TYPE				HdrType;

	for (HdrType = 0; HdrType < AA_HEADER_TYPE_MAX; HdrType++)
	{
		//
		//  Free all NDIS buffers in the header buffer list.
		//
		do
		{
			pListEntry = AA_POP_FROM_SLIST(
								&(pInterface->HeaderPool[HdrType].HeaderBufList),
								&(pInterface->BufferLock.SpinLock)
							);
			if (pListEntry != NULL_PAA_SINGLE_LIST_ENTRY)
			{
				pNdisBuffer = STRUCT_OF(NDIS_BUFFER, pListEntry, Next);
				NDIS_BUFFER_LINKAGE(pNdisBuffer) = NULL;
				NdisFreeBuffer(pNdisBuffer);
			}
			else
			{
				//
				//  No more NDIS buffers.
				//
				break;
			}
		}
		while (TRUE);

		//
		//  Now free all the buffer trackers.
		//
		pTracker = pInterface->HeaderPool[HdrType].pHeaderTrkList;

		while (pTracker != NULL_PATMARP_BUFFER_TRACKER)
		{
			pNextTracker = pTracker->pNext;
			if (pTracker->pPoolStart != (PUCHAR)NULL)
			{
				AA_FREE_MEM(pTracker->pPoolStart);
				pTracker->pPoolStart = (PUCHAR)NULL;
			}
			if (pTracker->NdisHandle != (NDIS_HANDLE)NULL)
			{
				NdisFreeBufferPool(pTracker->NdisHandle);
				pTracker->NdisHandle = (NDIS_HANDLE)NULL;
			}
			AA_FREE_MEM(pTracker);
			pTracker = pNextTracker;
		}

	} // for
}




PNDIS_BUFFER
AtmArpAllocateProtoBuffer(
	IN	PATMARP_INTERFACE			pInterface,
	IN	ULONG						Length,
	OUT	PUCHAR *					pBufferAddress
)
/*++

Routine Description:

	Allocate a buffer to be used for an ATM ARP protocol message. Attach
	it to an NDIS_BUFFER structure and return a pointer to this.

Arguments:

	pInterface		- Pointer to ATMARP Interface
	Length			- Length, in bytes, of the buffer.
	pBufferAddress	- Place to return virtual address of allocated buffer.

Return Value:

	Pointer to NDIS Buffer if successful, NULL otherwise.

--*/
{
	PNDIS_BUFFER		pNdisBuffer;
	NDIS_STATUS			Status;

	//
	//  Initialize
	//
	pNdisBuffer = NULL;

	AA_ASSERT(Length <= pInterface->ProtocolBufSize);

	NdisAcquireSpinLock(&pInterface->BufferLock);

	*pBufferAddress = pInterface->ProtocolBufList;
	if (*pBufferAddress != (PUCHAR)NULL)
	{
		NdisAllocateBuffer(
				&Status,
				&pNdisBuffer,
				pInterface->ProtocolBufferPool,
				*pBufferAddress,
				Length
			);

		if (Status == NDIS_STATUS_SUCCESS)
		{
			pInterface->ProtocolBufList = *((PUCHAR *)*pBufferAddress);
		}
	}

	NdisReleaseSpinLock(&pInterface->BufferLock);

	AADEBUGP(AAD_LOUD,
		("Allocated protocol buffer: IF 0x%x, pNdisBuffer 0x%x, Length %d, Loc 0x%x\n",
				pInterface, pNdisBuffer, Length, *pBufferAddress));

	return (pNdisBuffer);
}



VOID
AtmArpFreeProtoBuffer(
	IN	PATMARP_INTERFACE			pInterface,
	IN	PNDIS_BUFFER				pNdisBuffer
)
/*++

Routine Description:

	Free an NDIS buffer (and associated memory) used for a protocol
	packet. We return the associated memory to the ProtocolBufList
	in the interface structure, and the NDIS buffer to NDIS.

Arguments:

	pInterface		- Pointer to ATMARP interface structure
	pNdisBuffer		- Pointer to NDIS buffer to be freed

Return Value:

	None

--*/
{
	PUCHAR *		pBufferLinkage;
	ULONG			Length;

	NdisQueryBuffer(pNdisBuffer, (PVOID)&pBufferLinkage, &Length);

	NdisAcquireSpinLock(&pInterface->BufferLock);

	*pBufferLinkage = pInterface->ProtocolBufList;
	pInterface->ProtocolBufList = (PUCHAR)pBufferLinkage;

	NdisReleaseSpinLock(&pInterface->BufferLock);

	NdisFreeBuffer(pNdisBuffer);

	AADEBUGP(AAD_LOUD, ("Freed Protocol Buf: IF 0x%x, pNdisBuffer 0x%x, Loc 0x%x\n",
			pInterface, pNdisBuffer, pBufferLinkage));

}



NDIS_STATUS
AtmArpInitProtoBuffers(
	IN	PATMARP_INTERFACE			pInterface
)
/*++

Routine Description:

	Initialize the protocol buffer pool for an interface.

	Allocate a chunk of memory to be used for ATMARP protocol messages.
	We prepare a linked list of protocol buffers, and attach it to the
	Interface structure.

Arguments:

	pInterface		- Pointer to Interface on which we need to allocate
					  protocol buffers.
Return Value:

	NDIS_STATUS_SUCCESS if successful, NDIS_STATUS_RESOURCES if we run
	into a resource failure.

--*/
{
	NDIS_STATUS			Status;
	PUCHAR				pSpace;
	ULONG				i;

	do
	{
		NdisAllocatePacketPool(
				&Status,
				&(pInterface->ProtocolPacketPool),
				pInterface->MaxProtocolBufs,
				sizeof(struct PCCommon)
				);

		if (Status != NDIS_STATUS_SUCCESS)
		{
			break;
		}

		NdisAllocateBufferPool(
				&Status,
				&(pInterface->ProtocolBufferPool),
				pInterface->MaxProtocolBufs
				);

		if (Status != NDIS_STATUS_SUCCESS)
		{
			break;
		}

		//
		//  Allocate a big chunk of system memory that we can divide up into
		//  protocol buffers.
		//
		AA_ALLOC_MEM(
				pInterface->ProtocolBufTracker,
				UCHAR,
				(pInterface->ProtocolBufSize * pInterface->MaxProtocolBufs)
				);

		if (pInterface->ProtocolBufTracker == (PUCHAR)NULL)
		{
			Status = NDIS_STATUS_RESOURCES;
			break;
		}

		Status = NDIS_STATUS_SUCCESS;

		//
		//  Make all protocol buffers free.
		//
		pSpace = pInterface->ProtocolBufTracker;
		{
			PUCHAR	LinkPtr;

			LinkPtr = (PUCHAR)NULL;
			for (i = 0; i < pInterface->MaxProtocolBufs; i++)
			{
				*((PUCHAR *)pSpace) = LinkPtr;
				LinkPtr = pSpace;
				pSpace += pInterface->ProtocolBufSize;
			}
			pSpace -= pInterface->ProtocolBufSize;
			pInterface->ProtocolBufList = pSpace;
		}
	}
	while (FALSE);

	if (Status != NDIS_STATUS_SUCCESS)
	{
		//
		//  Undo everything.
		//
		AtmArpDeallocateProtoBuffers(pInterface);
	}


	return (Status);
}



VOID
AtmArpDeallocateProtoBuffers(
	IN	PATMARP_INTERFACE			pInterface
)
/*++

Routine Description:

	Free the protocol buffer pool for an interface.

Arguments:

	pInterface		- Pointer to ATMARP interface structure

Return Value:

	None

--*/
{
	if (pInterface->ProtocolPacketPool != (NDIS_HANDLE)NULL)
	{
		NdisFreePacketPool(pInterface->ProtocolPacketPool);
		pInterface->ProtocolPacketPool = NULL;
	}

	if (pInterface->ProtocolBufferPool != (NDIS_HANDLE)NULL)
	{
		NdisFreeBufferPool(pInterface->ProtocolBufferPool);
		pInterface->ProtocolBufferPool = NULL;
	}

	if (pInterface->ProtocolBufTracker != (PUCHAR)NULL)
	{
		AA_FREE_MEM(pInterface->ProtocolBufTracker);
		pInterface->ProtocolBufTracker = (PUCHAR)NULL;
	}
}


VOID
AtmArpLinkVcToAtmEntry(
	IN	PATMARP_VC					pVc,
	IN	PATMARP_ATM_ENTRY			pAtmEntry
)
/*++

Routine Description:

	Link an ATMARP VC to an ATM Entry. The caller is assumed to
	hold locks to both structures.

	If this VC is a "best effort" VC, and there is no "best effort"
	VC linked to the ATM Entry, we make this as the "best effort VC"
	on this ATM Entry.

Arguments:

	pVc					- Pointer to ATMARP VC structure
	pAtmEntry			- Pointer to ATMARP ATM Entry structure

Return Value:

	None

--*/
{
	PATMARP_VC		*ppNext;
	ULONG			SendBandwidth;

	AADEBUGP(AAD_EXTRA_LOUD, ("Link VC: 0x%x to AtmEntry 0x%x\n",
			pVc, pAtmEntry));

	//
	//  Back pointer from VC to ATM Entry.
	//
	pVc->pAtmEntry = pAtmEntry;

	//
	//  Find the position in which this VC should appear in the ATM Entry's
	//  VC list. We maintain this list in descending order of send bandwidth,
	//  so that the largest bandwidth VC to a destination appears first.
	//
	SendBandwidth = pVc->FlowSpec.SendPeakBandwidth;
	ppNext = &(pAtmEntry->pVcList);
	while (*ppNext != NULL_PATMARP_VC)
	{
		if (SendBandwidth >= (*ppNext)->FlowSpec.SendPeakBandwidth)
		{
			break;
		}
		else
		{
			ppNext = &((*ppNext)->pNextVc);
		}
	}

	//
	//  Found the place we were looking for. Insert the VC here.
	//
	pVc->pNextVc = *ppNext;
	*ppNext = pVc;

	if ((pAtmEntry->pBestEffortVc == NULL_PATMARP_VC) &&
			AA_IS_BEST_EFFORT_FLOW(&(pVc->FlowSpec)))
	{
		pAtmEntry->pBestEffortVc = pVc;
	}

	AA_REF_AE(pAtmEntry, AE_REFTYPE_VC);	// VC reference
}




VOID
AtmArpUnlinkVcFromAtmEntry(
	IN	PATMARP_VC					pVc,
	IN	BOOLEAN						bDerefAtmEntry
)
/*++

Routine Description:

	Unlink an ATMARP VC from the ATM Entry it is linked to.
	The caller is assumed to hold a lock for the VC structure.

Arguments:

	pVc				- Pointer to ATMARP VC structure
	bDerefAtmEntry	- Should we deref the ATM entry or not.

Return Value:

	None

--*/
{
	PATMARP_ATM_ENTRY			pAtmEntry;
	PATMARP_VC *				ppVc;

	AADEBUGP(AAD_EXTRA_LOUD, ("Unlink VC: 0x%x from AtmEntry 0x%x\n",
			pVc, pVc->pAtmEntry));

	pAtmEntry = pVc->pAtmEntry;
	AA_ASSERT(pAtmEntry != NULL_PATMARP_ATM_ENTRY);
	pVc->pAtmEntry = NULL_PATMARP_ATM_ENTRY;

	//
	//  Reacquire locks in the right order.
	//
	AA_RELEASE_VC_LOCK(pVc);

	AA_ACQUIRE_AE_LOCK(pAtmEntry);
	AA_ACQUIRE_VC_LOCK_DPC(pVc);

	//
	//  Search for the position of this VC in the ATM Entry's VC list
	//
	ppVc = &(pAtmEntry->pVcList);
	while (*ppVc != pVc)
	{
		AA_ASSERT(*ppVc != NULL_PATMARP_VC);
		ppVc = &((*ppVc)->pNextVc);
	}

	//
	//  Make the predecessor point to the next VC in the list.
	//
	*ppVc = pVc->pNextVc;

	AA_RELEASE_VC_LOCK_DPC(pVc);

	//
	//  If this was the Best Effort VC for this ATM Entry, try
	//  to find a replacement
	//
	if (pAtmEntry->pBestEffortVc == pVc)
	{
		//
		//  Yes, it was. Walk through the list of remaining VCs,
		//  if we find another Best Effort VC, make that the
		//  BestEffortVc for this ATM Entry
		//
		pAtmEntry->pBestEffortVc = NULL_PATMARP_VC;

		ppVc = &(pAtmEntry->pVcList);
		while (*ppVc != NULL_PATMARP_VC)
		{
			if (AA_IS_BEST_EFFORT_FLOW(&((*ppVc)->FlowSpec)))
			{
				pAtmEntry->pBestEffortVc = *ppVc;
				break;
			}
			else
			{
				ppVc = &((*ppVc)->pNextVc);
			}
		}
		AADEBUGP(AAD_LOUD, ("Atm Entry 0x%x, new Best Effort VC: 0x%x\n",
				pAtmEntry, pAtmEntry->pBestEffortVc));
	}

	if (bDerefAtmEntry)
	{
		if (AA_DEREF_AE(pAtmEntry, AE_REFTYPE_VC) != 0)
		{
			AA_RELEASE_AE_LOCK(pAtmEntry);
		}
	}
	else
	{
		AA_RELEASE_AE_LOCK(pAtmEntry);
	}

	//
	//  Acquire the VC lock again for the caller's sake
	//
	AA_ACQUIRE_VC_LOCK(pVc);
}



PNDIS_BUFFER
AtmArpCopyToNdisBuffer(
	IN	PNDIS_BUFFER				pDestBuffer,
	IN	PUCHAR						pDataSrc,
	IN	UINT						LenToCopy,
	IN OUT	PUINT					pOffsetInBuffer
)
/*++

Routine Description:

	Copy data into an NDIS buffer chain. Use up as much of the given
	NDIS chain as needed for "LenToCopy" bytes. After copying is over,
	return a pointer to the first NDIS buffer that has space for writing
	into (for the next Copy operation), and the offset within this from
	which to start writing.

Arguments:

	pDestBuffer		- First NDIS buffer in a chain of buffers
	pDataSrc		- Where to copy data from
	LenToCopy		- How much data to copy
	pOffsetInBuffer	- Offset in pDestBuffer where we can start copying into.

Return Value:

	The NDIS buffer in the chain where the next Copy can be done. We also
	set *pOffsetInBuffer to the write offset in the returned NDIS buffer.

	Note: if we are low on memory and run into a failure, we return NULL.

--*/
{
	//
	//  Size and destination for individual (contiguous) copy operations
	//
	UINT			CopySize;
	PUCHAR			pDataDst;

	//
	//  Start Virtual address for each NDIS buffer in chain.
	//
	PUCHAR			VirtualAddress;

	//
	//  Offset within pDestBuffer
	//
	UINT			OffsetInBuffer = *pOffsetInBuffer;

	//
	//  Bytes remaining in current buffer
	//
	UINT			DestSize;

	//
	//  Total Buffer Length
	//
	UINT			BufferLength;


	AA_ASSERT(pDestBuffer != (PNDIS_BUFFER)NULL);
	AA_ASSERT(pDataSrc != NULL);

#ifdef ATMARP_WIN98
	NdisQueryBuffer(
			pDestBuffer,
			&VirtualAddress,
			&BufferLength
			);
#else
	NdisQueryBufferSafe(
			pDestBuffer,
			&VirtualAddress,
			&BufferLength,
			NormalPagePriority
			);

	if (VirtualAddress == NULL)
	{
		return (NULL);
	}
#endif // ATMARP_WIN98
	
	AA_ASSERT(BufferLength >= OffsetInBuffer);

	pDataDst = VirtualAddress + OffsetInBuffer;
	DestSize = BufferLength - OffsetInBuffer;

	for (;;)
	{
		CopySize = MIN(LenToCopy, DestSize);
		AA_COPY_MEM(pDataDst, pDataSrc, CopySize);

		pDataDst += CopySize;
		pDataSrc += CopySize;

		LenToCopy -= CopySize;
		if (LenToCopy == 0)
		{
			break;
		}

		DestSize -= CopySize;

		if (DestSize == 0)
		{
			//
			//  Out of space in the current buffer. Move to the next.
			//
			pDestBuffer = NDIS_BUFFER_LINKAGE(pDestBuffer);
			AA_ASSERT(pDestBuffer != (PNDIS_BUFFER)NULL);
#ifdef ATMARP_WIN98
			NdisQueryBuffer(
					pDestBuffer,
					&VirtualAddress,
					&BufferLength
					);
#else
			NdisQueryBufferSafe(
					pDestBuffer,
					&VirtualAddress,
					&BufferLength,
					NormalPagePriority
					);

			if (VirtualAddress == NULL)
			{
				return (NULL);
			}
#endif // ATMARP_WIN98

			pDataDst = VirtualAddress;
			DestSize = BufferLength;
		}
	}

	*pOffsetInBuffer = (UINT) (pDataDst - VirtualAddress);

	return (pDestBuffer);
}


PATMARP_INTERFACE
AtmArpAddInterfaceToAdapter (
	IN	PATMARP_ADAPTER				pAdapter,
	IN	NDIS_HANDLE					LISConfigHandle, // Handle to per-LIS config
	IN	NDIS_STRING					*pIPConfigString
	)
{
	NDIS_STATUS					Status;
	struct LLIPBindInfo			BindInfo;
	PATMARP_INTERFACE			pInterface;
#ifdef ATMARP_WIN98
	ANSI_STRING					AnsiConfigString;
#endif

	do
	{
		//
		//  Create an ATMARP Interface structure to represent this LIS.
		//
		pInterface = AtmArpAllocateInterface(pAdapter);
		if (pInterface == NULL_PATMARP_INTERFACE)
		{
			AADEBUGP(AAD_WARNING, ("NotifyRegAfHandler: could not allocate Interface\n"));
			Status = NDIS_STATUS_RESOURCES;
			break;
		}

		//
		//  Adapter Binding Reference:
		//
		AtmArpReferenceInterface(pInterface);

		//
		//  Get all configuration information for this LIS.
		//
		Status = AtmArpCfgReadLISConfiguration(
									LISConfigHandle,
									pInterface
									);

		if (Status != NDIS_STATUS_SUCCESS)
		{
			AADEBUGP(AAD_WARNING, ("AddInterfaceToAdapter: bad status (0x%x) reading LIS cfg\n",
						Status));
			break;
		}

#ifndef ATMARP_WIN98
		pInterface->IPConfigString = *pIPConfigString; // struct copy.
#else
		//
		//  Win98: Convert IPConfig string from Unicode to ANSI.
		//
		AnsiConfigString.MaximumLength = pIPConfigString->MaximumLength / sizeof(WCHAR) + sizeof(CHAR);
		AA_ALLOC_MEM(AnsiConfigString.Buffer, CHAR, AnsiConfigString.MaximumLength);
		if (AnsiConfigString.Buffer == NULL)
		{
			AADEBUGP(AAD_WARNING, ("NotifyRegAfHandler: couldn't alloc Ansi string (%d)\n",
				AnsiConfigString.MaximumLength));
			Status = NDIS_STATUS_RESOURCES;
			break;
		}
		AnsiConfigString.Length = 0;
		NdisUnicodeStringToAnsiString(&AnsiConfigString, pIPConfigString);
		AnsiConfigString.Buffer[AnsiConfigString.Length] = '\0';
#endif // !ATMARP_WIN98

		//
		//  Allocate protocol buffers for this LIS.
		//
		Status = AtmArpInitProtoBuffers(pInterface);
		if (Status != NDIS_STATUS_SUCCESS)
		{
			AADEBUGP(AAD_WARNING, ("AddInterfaceToAdapter: bad status (0x%x) from InitBufs\n",
						Status));
			break;
		}

		//
		//  Initialize IP/ATM data structures for this LIS.
		//
		Status = AtmArpInitIpOverAtm(pInterface);
		if (Status != NDIS_STATUS_SUCCESS)
		{
			AADEBUGP(AAD_WARNING, ("AddInterfaceToAdapter: bad status (0x%x) from InitIP/ATM\n",
						Status));
			break;
		}

		//
		//  Initialize the Call Manager interface for this LIS.
		//
		Status = AtmArpOpenCallMgr(pInterface);
		if (Status != NDIS_STATUS_SUCCESS)
		{
			AADEBUGP(AAD_WARNING, ("AddInterfaceToAdapter: bad status (0x%x) from OpenCallMgr\n",
						Status));
			break;
		}

		//
		//  Announce this new interface to IP, along with our BindInfo
		//  structure.
		//
		AA_SET_MEM(&BindInfo, 0, sizeof(BindInfo));

#if ATMOFFLOAD
		//
		// Query and set NIC offload capabilities.
		//
		BindInfo.lip_OffloadFlags 	= pAdapter->Offload.Flags;
		BindInfo.lip_MaxOffLoadSize = pAdapter->Offload.MaxOffLoadSize;
		BindInfo.lip_MaxSegments 	= pAdapter->Offload.MinSegmentCount;
#endif // ATMOFFLOAD

		BindInfo.lip_context = (PVOID)pInterface;
#ifdef ATMARP_WIN98
		BindInfo.lip_transmit = AtmArpIfTransmit;
#else
		BindInfo.lip_transmit = AtmArpIfMultiTransmit;
#endif
		BindInfo.lip_transfer = AtmArpIfTransfer;
		BindInfo.lip_close = AtmArpIfClose;
		BindInfo.lip_addaddr = AtmArpIfAddAddress;
		BindInfo.lip_deladdr = AtmArpIfDelAddress;
		BindInfo.lip_invalidate = AtmArpIfInvalidate;
		BindInfo.lip_open = AtmArpIfOpen;
		BindInfo.lip_qinfo = AtmArpIfQueryInfo;
		BindInfo.lip_setinfo = AtmArpIfSetInfo;
		BindInfo.lip_getelist = AtmArpIfGetEList;
		BindInfo.lip_mss = pInterface->MTU;
		BindInfo.lip_speed = pInterface->Speed;
		//
		//  Set LIP_COPY_FLAG to avoid having TransferData
		//  called all the time.
		//
		BindInfo.lip_flags = LIP_COPY_FLAG;
		BindInfo.lip_addrlen = AA_ATM_PHYSADDR_LEN;
		BindInfo.lip_addr = &(pInterface->LocalAtmAddress.Address[AA_ATM_ESI_OFFSET]);
#ifdef _PNP_POWER_
		BindInfo.lip_pnpcomplete = AtmArpIfPnPComplete;
#endif // _PNP_POWER_

#ifdef PROMIS
		BindInfo.lip_setndisrequest = AtmArpIfSetNdisRequest;
#endif // PROMIS

#ifdef ATMARP_WIN98
#if DBG
		AADEBUGP(AAD_FATAL, ("Will call AddIF: DeviceName [%ws]\n",
							&(pInterface->pAdapter->DeviceName.Buffer)));
		AADEBUGP(AAD_FATAL, ("And ConfigString: [%s]\n", AnsiConfigString.Buffer));
#endif
#endif // ATMARP_WIN98

		Status = (*(pAtmArpGlobalInfo->pIPAddInterfaceRtn))(
							&(pInterface->pAdapter->DeviceName),

#ifndef ATMARP_WIN98
#if IFCHANGE1
							NULL, // IfName (unused) --  See 10/14/1998 entry
								  // in notes.txt
#endif // IFCHANGE1
							pIPConfigString,
						
#else
							(PNDIS_STRING)&AnsiConfigString,
#endif
							pAdapter->SystemSpecific2,
							(PVOID)pInterface,
							AtmArpIfDynRegister,
							&BindInfo
#if IFCHANGE1
#ifndef ATMARP_WIN98
							,0,	// RequestedIndex (unused) --  See 10/14/1998 entry
								// in notes.txt

                            IF_TYPE_IPOVER_ATM,
                            IF_ACCESS_BROADCAST,
                            IF_CONNECTION_DEDICATED
#endif
#endif // IFCHANGE1
							);

		if (Status == IP_SUCCESS)
		{
			Status = NDIS_STATUS_SUCCESS;
		}
		else
		{
			AADEBUGP(AAD_ERROR, ("AddInterface: IPAddInterface ret 0x%x\n",
						Status));

			Status = NDIS_STATUS_FAILURE;
		}
		break;
	}
	while (FALSE);

	if (Status != NDIS_STATUS_SUCCESS)
	{
		//
		//  There was a failure in processing this LIS.
		//
		if (pInterface != NULL_PATMARP_INTERFACE)
		{
			if (pInterface->NdisAfHandle != NULL)
			{
				(VOID)AtmArpCloseCallMgr(pInterface);
			}

			pInterface->RefCount = 0;
			AtmArpDeallocateInterface(pInterface);

			pInterface = NULL_PATMARP_INTERFACE;
		}
	}

	return pInterface;

}

#if DBG

void
AtmArpValidateTimerList(
	PATMARP_TIMER_LIST		pTimerList
	)
/*++

Routine Description:


Arguments:

	pTimerList		- Timer list 

Return Value:

	None -- will assert if timer is not valid.

--*/

{
	PATMARP_TIMER			pTimer;
	UINT u;
	UINT cTimers=0;

	AA_ASSERT(pTimerList->atl_sig == atl_signature);
	AA_ASSERT(pTimerList->CurrentTick < pTimerList->TimerListSize);

	for (u=0;u<pTimerList->TimerListSize;u++)
	{
		for ( 	pTimer = pTimerList->pTimers[u].pNextTimer;
  				pTimer;
  				pTimer = pTimer->pNextTimer)
		{
			AtmArpValidateTimer(pTimerList, pTimer);
			cTimers++;
		}
	}

	AA_ASSERT(pTimerList->TimerCount == cTimers);

}

void
AtmArpValidateTimer(
	PATMARP_TIMER_LIST		pTimerList, // OPTIONAL
	PATMARP_TIMER			pTimer
	)
/*++

Routine Description:


Arguments:

	pTimer			- Timer

Return Value:

	None -- will assert if timer is not valid.

--*/

{
	if (pTimerList)
	{
		AA_ASSERT(pTimerList == pTimer->pTimerList);
	}

	if (pTimer->pPrevTimer)
	{
		AA_ASSERT(pTimer->pPrevTimer->pNextTimer == pTimer);
	}

	if (pTimer->pNextTimer)
	{
		AA_ASSERT(pTimer->pNextTimer->pPrevTimer == pTimer);
	}
}

#endif // DBG