/*++

Copyright (c) 1997 FORE Systems, Inc.
Copyright (c) 1997  Microsoft Corporation

Module Name:

	ioctl.c

Abstract:

	IOCTL Handler functions

Author:

	Larry Cleeton, FORE Systems	(v-lcleet@microsoft.com, lrc@fore.com)		

Notes:

--*/
#include <precomp.h>


PATMLANE_ADAPTER
AtmLaneIoctlNameToAdapter(
	IN	PUNICODE_STRING		pDeviceName
)
/*++

Routine Description:

	Given the name of an adapter, return a pointer to the corresponding
	adapter structure if one exists, else NULL.

	This routine also references the adapter.

Arguments:

	pDeviceName	- Pointer to Device name we are searching for.

Return Value:

	See above.

--*/
{
	PLIST_ENTRY				pListEntry;
	PATMLANE_ADAPTER		pAdapter;
	PATMLANE_ADAPTER		pAdapterToReturn = NULL_PATMLANE_ADAPTER;
	BOOLEAN                 bReferenced = FALSE;

	TRACEIN(IoctlNameToAdapter);

	// fixup the Buffer pointer
	
	pDeviceName->Buffer = (PWSTR)((PUCHAR)pDeviceName + sizeof(UNICODE_STRING));
	

	// loop thru the adapters looking for the ELAN

	ACQUIRE_GLOBAL_LOCK(pAtmLaneGlobalInfo);
	
	for (pListEntry = pAtmLaneGlobalInfo->AdapterList.Flink;
		 pListEntry != &(pAtmLaneGlobalInfo->AdapterList);
		 pListEntry = pListEntry->Flink)
	{
		// get pointer to Adapter
	
		pAdapter = CONTAINING_RECORD(pListEntry, ATMLANE_ADAPTER, Link);
		STRUCT_ASSERT(pAdapter, atmlane_adapter);
		
		// compare length first and then actual names

		if ((pDeviceName->Length == pAdapter->DeviceName.Length) &&
			(memcmp(pDeviceName->Buffer,
						pAdapter->DeviceName.Buffer,
						pDeviceName->Length) == 0))
		{
			// match - return this adapter
		
			pAdapterToReturn = pAdapter;

			ACQUIRE_ADAPTER_LOCK_DPC(pAdapter);
			bReferenced = AtmLaneReferenceAdapter(pAdapter, "ioctl");
			RELEASE_ADAPTER_LOCK_DPC(pAdapter);

			break;
		}
	}

	RELEASE_GLOBAL_LOCK(pAtmLaneGlobalInfo);

	TRACEOUT(IoctlNameToAdapter);
	return (bReferenced? pAdapterToReturn: NULL);
}


PATMLANE_ELAN
AtmLaneIoctlNameToElan(
	IN	PATMLANE_ADAPTER	pAdapter,
	IN	UNICODE_STRING UNALIGNED *		pDeviceName
)
/*++

Routine Description:

	Given a pointer to an adapter data structure and an 
	ELAN device name, return a pointer to the corresponding
	ELAN structure if one exists, else NULL.

	This also references the ELAN structure.

Arguments:
	pAdapter	- Pointer to Adapter data structure.
	pDeviceName	- Pointer to Device name we are searching for.

Return Value:

	See above.

--*/
{
	PLIST_ENTRY				pListEntry;
	PATMLANE_ELAN			pElan;
	PATMLANE_ELAN			pElanToReturn = NULL_PATMLANE_ELAN;

	TRACEIN(IoctlNameToElan);

	STRUCT_ASSERT(pAdapter, atmlane_adapter);

	// fixup the Buffer pointer

	pDeviceName->Buffer = (PWSTR)((PUCHAR)pDeviceName + sizeof(UNICODE_STRING));
	
	ACQUIRE_ADAPTER_LOCK(pAdapter);

	// loop thru the ELANs looking for the given name

	for (pListEntry = pAdapter->ElanList.Flink;
		 pListEntry != &(pAdapter->ElanList);
		 pListEntry = pListEntry->Flink)
	{
		// get pointer to ELAN
	
		pElan = CONTAINING_RECORD(pListEntry, ATMLANE_ELAN, Link);
		STRUCT_ASSERT(pElan, atmlane_elan);

		// compare length first and then actual names

		if ((pDeviceName->Length == pElan->CfgDeviceName.Length) &&
			(memcmp(pDeviceName->Buffer,
						pElan->CfgDeviceName.Buffer,
						pDeviceName->Length) == 0))
		{
			// match - return this ELAN
		
			pElanToReturn = pElan;

			ACQUIRE_ELAN_LOCK(pElan);
			AtmLaneReferenceElan(pElan, "iocnametoelan");
			RELEASE_ELAN_LOCK(pElan);

			break;
		}
	}
	
	RELEASE_ADAPTER_LOCK(pAdapter);

	TRACEOUT(IoctlNameToElan);

	return (pElanToReturn);
}

NTSTATUS
AtmLaneIoctlGetInfoVersion (
	IN	PUCHAR				pBuffer,
	IN 	UINT				InputBufferLength,
	IN	UINT				OutputBufferLength,
	IN OUT	UINT_PTR *		pBytesWritten
)
/*++

Routine Description:

	Return the version number of the information exported by
	these ioctl codes. 
	
Arguments:

	pBuffer				- Space for input/output
	InputBufferLength	- Length of input parameters
	OutputBufferLength	- Space available for output
	pBytesWritten		- Where we return the amount we actually used up

Return Value:

	Status code

--*/
{
	NTSTATUS			Status;
	
	TRACEIN(IoctlGetInfoVersion);

	// init

	*pBytesWritten = 0;
	Status = STATUS_SUCCESS;

	do
	{
		//	check for enough output space

		if (OutputBufferLength < sizeof(ULONG))
		{
			Status =  STATUS_BUFFER_OVERFLOW;
			break;
		}

		//	output the version

		*((PULONG)pBuffer) = ATMLANE_INFO_VERSION;
		
		*pBytesWritten = sizeof(ULONG);
	}
	while (FALSE);

	TRACEOUT(IoctlGetInfoVersion);

	return STATUS_SUCCESS;
}

NTSTATUS
AtmLaneIoctlEnumerateAdapters (
	IN	PUCHAR				pBuffer,
	IN 	UINT				InputBufferLength,
	IN	UINT				OutputBufferLength,
	IN OUT	UINT_PTR *		pBytesWritten
)
/*++

Routine Description:

	Return a list of adapters bound to the AtmLane protocol.
	We go through the list of Adapter structures and concatenate the
	device names stored in each into the output buffer.

Arguments:

	pBuffer				- Space for input/output
	InputBufferLength	- Length of input parameters
	OutputBufferLength	- Space available for output
	pBytesWritten		- Where we return the amount we actually used up

Return Value:

	Status code

--*/
{
	PATMLANE_ADAPTER		pAdapter;
	UINT					Remaining;
	PATMLANE_ADAPTER_LIST	pAdapterList;
	PUNICODE_STRING			pAdapterName;
	NTSTATUS				Status;
	PLIST_ENTRY				pListEntry;

	TRACEIN(IoctlEnumAdapters);

	// init

	*pBytesWritten = 0;
	Status = STATUS_SUCCESS;

	do
	{
		//	check for minimal output space

		Remaining = OutputBufferLength;
		if (Remaining < sizeof(ATMLANE_ADAPTER_LIST))
		{
			Status =  STATUS_BUFFER_OVERFLOW;
			break;
		}

		pAdapterList = (PATMLANE_ADAPTER_LIST)pBuffer;

		//	setup to return empty list

		pAdapterList->AdapterCountReturned = 0;
		*pBytesWritten = FIELD_OFFSET(ATMLANE_ADAPTER_LIST, AdapterList);
		pAdapterName = &pAdapterList->AdapterList;

		//  adjust space for output
		Remaining -= FIELD_OFFSET (ATMLANE_ADAPTER_LIST, AdapterList);

		//	loop thru the adapters

		ACQUIRE_GLOBAL_LOCK(pAtmLaneGlobalInfo);
	
		for (pListEntry = pAtmLaneGlobalInfo->AdapterList.Flink;
			 pListEntry != &(pAtmLaneGlobalInfo->AdapterList);
			 pListEntry = pListEntry->Flink)
		{
			//	get pointer to adapter struct
	
			pAdapter = CONTAINING_RECORD(pListEntry, ATMLANE_ADAPTER, Link);
			STRUCT_ASSERT(pAdapter, atmlane_adapter);

			//	quit loop if no more space

			if (Remaining < sizeof(NDIS_STRING) + pAdapter->DeviceName.Length)
			{
				Status = STATUS_BUFFER_OVERFLOW;
				break;
			}

			//	count and copy the adapter name

			pAdapterList->AdapterCountReturned++;
			pAdapterName->Buffer = (PWSTR)((PUCHAR)pAdapterName + sizeof(UNICODE_STRING));
			memcpy(pAdapterName->Buffer, pAdapter->DeviceName.Buffer, pAdapter->DeviceName.Length);
			pAdapterName->MaximumLength = pAdapterName->Length = pAdapter->DeviceName.Length;

			//  convert the Buffer pointer to an offset - caller expects it

			pAdapterName->Buffer = (PWSTR)((PUCHAR)pAdapterName->Buffer - (PUCHAR)pAdapterList);

			//	move ptr past the name we just copied

			pAdapterName = (PUNICODE_STRING)((PUCHAR)pAdapterName + sizeof(UNICODE_STRING)
							+ pAdapter->DeviceName.Length);

			//	update bytes written and remaining space

			*pBytesWritten += sizeof(UNICODE_STRING) + pAdapter->DeviceName.Length;
			Remaining -= sizeof(UNICODE_STRING) + pAdapter->DeviceName.Length;
		}
	

		//	check count available same as count returned

		pAdapterList->AdapterCountAvailable = pAdapterList->AdapterCountReturned;

		//	count any remaining adapters that there wasn't space for

		while (pListEntry != &(pAtmLaneGlobalInfo->AdapterList))
		{
			pAdapterList->AdapterCountAvailable++;
			pListEntry = pListEntry->Flink;
		}

		RELEASE_GLOBAL_LOCK(pAtmLaneGlobalInfo);

	} while (FALSE);
	
	TRACEOUT(IoctlEnumerateAdapters);
				
	return (Status);

}


NTSTATUS
AtmLaneIoctlEnumerateElans(
	IN	PUCHAR				pBuffer,
	IN 	UINT				InputBufferLength,
	IN	UINT				OutputBufferLength,
	IN OUT	UINT_PTR *		pBytesWritten
)
/*++

Routine Description:

	Return a list of adapters bound to the AtmLane protocol.
	We go through the list of Adapter structures and concatenate the
	device names stored in each into the output buffer.

Arguments:

	pBuffer				- Space for input/output
	InputBufferLength	- Length of input parameters
	OutputBufferLength	- Space available for output
	pBytesWritten		- Where we return the amount we actually used up

Return Value:

	Status code

--*/
{
	PATMLANE_ADAPTER		pAdapter;
	UINT					Remaining;
	PATMLANE_ELAN_LIST		pElanList;
	PUNICODE_STRING			pElanName;
	NTSTATUS				Status;
	PATMLANE_ELAN			pElan;
	PLIST_ENTRY				pListEntry;
	ULONG					rc;

	TRACEIN(IoctlEnumerateElans);

	// init
	
	*pBytesWritten = 0;
	Status = STATUS_SUCCESS;
	pAdapter = NULL;

	do
	{
		//	check if adapter string passed in

		if (InputBufferLength < sizeof(UNICODE_STRING))
		{
			Status = STATUS_BUFFER_OVERFLOW;
			break;
		}

		if (InputBufferLength < sizeof(UNICODE_STRING) + ((PUNICODE_STRING)pBuffer)->MaximumLength)
		{
			Status = STATUS_BUFFER_OVERFLOW;
			break;
		}

		//  sanity check

		if (((PUNICODE_STRING)pBuffer)->MaximumLength < ((PUNICODE_STRING)pBuffer)->Length)
		{
			Status = STATUS_BUFFER_OVERFLOW;
			break;
		}

		//	get the adapter struct from the name
	
		pAdapter = AtmLaneIoctlNameToAdapter((PUNICODE_STRING)pBuffer);

		if (pAdapter == NULL_PATMLANE_ADAPTER)
		{
			Status = STATUS_UNSUCCESSFUL;
			break;
		}

		//	check for minimal output space

		Remaining = OutputBufferLength;
		if (Remaining < sizeof(ATMLANE_ELAN_LIST))
		{
			Status = STATUS_BUFFER_OVERFLOW;
			break;
		}	

		pElanList = (PATMLANE_ELAN_LIST)pBuffer;

		//	setup to return empty list
	
		pElanList->ElanCountReturned = 0;
		*pBytesWritten = FIELD_OFFSET(ATMLANE_ELAN_LIST, ElanList);
		Remaining -= FIELD_OFFSET(ATMLANE_ELAN_LIST, ElanList);

		pElanName = &pElanList->ElanList;

		//	loop thru the Elans

		ACQUIRE_ADAPTER_LOCK(pAdapter);
	
		for (pListEntry = pAdapter->ElanList.Flink;
		 	pListEntry != &(pAdapter->ElanList);
		 	pListEntry = pListEntry->Flink)
		{
			//	get pointer to adapter struct
	
			pElan = CONTAINING_RECORD(pListEntry, ATMLANE_ELAN, Link);
			STRUCT_ASSERT(pElan, atmlane_elan);

			//	quit loop if no more space

			if (Remaining < sizeof(NDIS_STRING) + pElan->CfgDeviceName.Length)
			{
				Status = STATUS_BUFFER_OVERFLOW;
				break;
			}

			//	count and copy the adapter name

			pElanList->ElanCountReturned++;
			pElanName->Buffer = (PWSTR)((PUCHAR)pElanName + sizeof(UNICODE_STRING));
			memcpy(pElanName->Buffer, pElan->CfgDeviceName.Buffer, pElan->CfgDeviceName.Length);
			pElanName->MaximumLength = pElanName->Length = pElan->CfgDeviceName.Length;

			//  convert the Buffer pointer to an offset - caller expects it

			pElanName->Buffer = (PWSTR)((PUCHAR)pElanName->Buffer - (PUCHAR)pElanList);

			//	move ptr past the name we just copied

			pElanName = (PUNICODE_STRING)((PUCHAR)pElanName + sizeof(UNICODE_STRING)
							+ pElan->CfgDeviceName.Length);

			//	update bytes written and remaining space

			*pBytesWritten += (sizeof(UNICODE_STRING) + pElan->CfgDeviceName.Length);
			Remaining -= sizeof(UNICODE_STRING) + pElan->CfgDeviceName.Length;
		}

		//	set count available same as count returned

		pElanList->ElanCountAvailable = pElanList->ElanCountReturned;

		//	count any remaining adapters that there wasn't space for

		while (pListEntry != &(pAdapter->ElanList))
		{
			pElanList->ElanCountAvailable++;
			pListEntry = pListEntry->Flink;
		}

		RELEASE_ADAPTER_LOCK(pAdapter);

	} while (FALSE);
	
	if (pAdapter != NULL)
	{
		ACQUIRE_ADAPTER_LOCK(pAdapter);
		rc = AtmLaneDereferenceAdapter(pAdapter, "ioctl: enumelans");
		
		if (rc != 0)
		{
			RELEASE_ADAPTER_LOCK(pAdapter);
		}
	}

	TRACEOUT(IoctlEnumerateElans);

	return (Status);
}


NTSTATUS
AtmLaneIoctlGetElanInfo(
	IN	PUCHAR				pBuffer,
	IN 	UINT				InputBufferLength,
	IN	UINT				OutputBufferLength,
	OUT	UINT_PTR *			pBytesWritten
)
/*++

Routine Description:

	Return the state information about a specific Elan.

Arguments:

	pBuffer				- Space for input/output
	InputBufferLength	- Length of input parameters
	OutputBufferLength	- Space available for output
	pBytesWritten		- Where we return the amount we actually used up

Return Value:

	Status Code

--*/
{
	PATMLANE_ADAPTER					pAdapter;
	PATMLANE_ELAN						pElan;
	PUNICODE_STRING						pAdapterNameIn;
	PUNICODE_STRING						pElanNameIn;
	PATMLANE_ELANINFO					pElanInfo;
	NTSTATUS							Status;
	ULONG								rc;

	TRACEIN(IoctlGetElanInfo);

	// init

	*pBytesWritten = 0;
	Status = STATUS_SUCCESS;
	pAdapter = NULL;
	pElan = NULL;

	do
	{
	
		// check if adapter string passed in

		if (InputBufferLength < sizeof(UNICODE_STRING))
		{
			Status = STATUS_BUFFER_OVERFLOW;
			break;
		}

		// check if elan string passed in

		if (InputBufferLength < ((sizeof(UNICODE_STRING) * 2) + 
								((PUNICODE_STRING)pBuffer)->MaximumLength))
		{
			Status = STATUS_BUFFER_OVERFLOW;
			break;
		}

		//  sanity check

		if (((PUNICODE_STRING)pBuffer)->MaximumLength < ((PUNICODE_STRING)pBuffer)->Length)
		{
			Status = STATUS_BUFFER_OVERFLOW;
			break;
		}

		// check if minimal output space

		if (OutputBufferLength < sizeof(ATMLANE_ELANINFO))
		{
			Status = STATUS_BUFFER_OVERFLOW;
			break;
		}

		// setup ptrs to input names

		pAdapterNameIn = (PUNICODE_STRING)pBuffer;
		pElanNameIn = (PUNICODE_STRING)(pBuffer + sizeof(UNICODE_STRING) + 
						pAdapterNameIn->MaximumLength);

		// find adapter struct

		pAdapter = AtmLaneIoctlNameToAdapter(pAdapterNameIn);

		if (pAdapter == NULL_PATMLANE_ADAPTER)
		{
			Status = STATUS_UNSUCCESSFUL;
			break;
		}

		// find elan struct - check the lengths passed in first.

		InputBufferLength -= (sizeof(UNICODE_STRING) + pAdapterNameIn->MaximumLength);

		if (InputBufferLength < sizeof(UNICODE_STRING))
		{
			Status = STATUS_BUFFER_OVERFLOW;
			break;
		}

		if (InputBufferLength < sizeof(UNICODE_STRING) + pElanNameIn->MaximumLength)
		{
			Status = STATUS_BUFFER_OVERFLOW;
			break;
		}

		if (pElanNameIn->MaximumLength < pElanNameIn->Length)
		{
			Status = STATUS_BUFFER_OVERFLOW;
			break;
		}

		pElan = AtmLaneIoctlNameToElan(pAdapter, pElanNameIn);

		if (pElan == NULL_PATMLANE_ELAN)
		{
			Status = STATUS_UNSUCCESSFUL;
			break;
		}

		// setup to fill in ELAN info

		pElanInfo = (PATMLANE_ELANINFO)pBuffer;
	
		NdisZeroMemory(pElanInfo, sizeof(ATMLANE_ELANINFO));
			
		ACQUIRE_ELAN_LOCK(pElan);

		pElanInfo->ElanNumber 			= pElan->ElanNumber;
		pElanInfo->ElanState			= pElan->State;
		NdisMoveMemory(
			&pElanInfo->AtmAddress,	
			&pElan->AtmAddress.Address,
			ATM_ADDRESS_LENGTH);
		NdisMoveMemory(
			&pElanInfo->LecsAddress, 
			&pElan->LecsAddress.Address, 
			ATM_ADDRESS_LENGTH);
		NdisMoveMemory(
			&pElanInfo->LesAddress,	
			&pElan->LesAddress.Address,	 
			ATM_ADDRESS_LENGTH);
		NdisMoveMemory(
			&pElanInfo->BusAddress,	
			&pElan->BusAddress.Address,	 
			ATM_ADDRESS_LENGTH);
		pElanInfo->LanType				= pElan->LanType;
		pElanInfo->MaxFrameSizeCode 	= pElan->MaxFrameSizeCode;
		pElanInfo->LecId				= SWAPUSHORT(pElan->LecId);
		NdisMoveMemory(
			pElanInfo->ElanName,	
			pElan->ElanName, 
			pElan->ElanNameSize);
		if (pElan->LanType == LANE_LANTYPE_TR)
		{
			NdisMoveMemory(
				&pElanInfo->MacAddress, 
				&pElan->MacAddressTr, 
				sizeof(MAC_ADDRESS));
		}
		else
		{
			NdisMoveMemory(
				&pElanInfo->MacAddress, 
				&pElan->MacAddressEth, 
				sizeof(MAC_ADDRESS));
		}
		pElanInfo->ControlTimeout		= pElan->ControlTimeout;
		pElanInfo->MaxUnkFrameCount 	= pElan->MaxUnkFrameCount;
		pElanInfo->MaxUnkFrameTime		= pElan->MaxUnkFrameTime;
		pElanInfo->VccTimeout			= pElan->VccTimeout;
		pElanInfo->MaxRetryCount		= pElan->MaxRetryCount;
		pElanInfo->AgingTime			= pElan->AgingTime;
		pElanInfo->ForwardDelayTime 	= pElan->ForwardDelayTime;
		pElanInfo->TopologyChange 		= pElan->TopologyChange;
		pElanInfo->ArpResponseTime		= pElan->ArpResponseTime;
		pElanInfo->FlushTimeout			= pElan->FlushTimeout;
		pElanInfo->PathSwitchingDelay 	= pElan->PathSwitchingDelay;
		pElanInfo->LocalSegmentId		= pElan->LocalSegmentId;		
		pElanInfo->McastSendVcType		= pElan->McastSendVcType;	
		pElanInfo->McastSendVcAvgRate	= pElan->McastSendVcAvgRate; 
		pElanInfo->McastSendVcPeakRate	= pElan->McastSendVcPeakRate;
		pElanInfo->ConnComplTimer		= pElan->ConnComplTimer;
	
		RELEASE_ELAN_LOCK(pElan);

		*pBytesWritten = sizeof(ATMLANE_ELANINFO);

	} while (FALSE);

	if (pElan != NULL)
	{
		ACQUIRE_ELAN_LOCK(pElan);
		rc = AtmLaneDereferenceElan(pElan, "ioctl: getelaninfo");

		if (rc != 0)
		{
			RELEASE_ELAN_LOCK(pElan);
		}
	}

	if (pAdapter != NULL)
	{
		ACQUIRE_ADAPTER_LOCK(pAdapter);
		rc = AtmLaneDereferenceAdapter(pAdapter, "ioctl: getelaninfo");
		
		if (rc != 0)
		{
			RELEASE_ADAPTER_LOCK(pAdapter);
		}
	}

	TRACEOUT(IoctlGetElanInfo);
	
	return (Status);
}


NTSTATUS
AtmLaneIoctlGetElanArpTable(
	IN	PUCHAR				pBuffer,
	IN 	UINT				InputBufferLength,
	IN	UINT				OutputBufferLength,
	OUT	UINT_PTR *			pBytesWritten
)
/*++

Routine Description:

	Return the ARP table for the specified ELAN.

Arguments:

	pBuffer				- Space for input/output
	InputBufferLength	- Length of input parameters
	OutputBufferLength	- Space available for output
	pBytesWritten		- Where we return the amount we actually used up

Return Value:
	Status code

--*/
{
	PATMLANE_ADAPTER		pAdapter;
	PATMLANE_ELAN			pElan;
	PUNICODE_STRING			pAdapterNameIn;
	PUNICODE_STRING			pElanNameIn;
	PATMLANE_ARPTABLE		pArpTable;
	PATMLANE_ARPENTRY		pArpEntry;
	UINT					Remaining;
	PATMLANE_MAC_ENTRY		pMacEntry;
	NTSTATUS				Status;
	UINT					i;
	ULONG					rc;

	TRACEIN(IoctlGetElanArpTable);

	// init

	*pBytesWritten = 0;
	Status = STATUS_SUCCESS;
	pAdapter = NULL;
	pElan = NULL;

	do
	{
		// check if adapter string passed in

		if (InputBufferLength < sizeof(UNICODE_STRING))
		{
			Status = STATUS_BUFFER_OVERFLOW;
			break;
		}

		// sanity check the unicode string fields.
		if (((PUNICODE_STRING)pBuffer)->MaximumLength < ((PUNICODE_STRING)pBuffer)->Length)
		{
			Status = STATUS_BUFFER_OVERFLOW;
			break;
		}

		// check if elan string passed in

		if (InputBufferLength < (((sizeof(UNICODE_STRING) * 2) + 
								((PUNICODE_STRING)pBuffer)->MaximumLength)))
		{
			Status = STATUS_BUFFER_OVERFLOW;
			break;
		}	

		// check if minimum output space

		if (OutputBufferLength < sizeof(ATMLANE_ARPTABLE))
		{
			Status = STATUS_BUFFER_OVERFLOW;
			break;
		}

		// setup ptrs to input names

		pAdapterNameIn = (PUNICODE_STRING)pBuffer;
		pElanNameIn = (PUNICODE_STRING)(pBuffer + sizeof(UNICODE_STRING) + 
						pAdapterNameIn->MaximumLength);

		// find adapter struct

		pAdapter = AtmLaneIoctlNameToAdapter(pAdapterNameIn);

		if (pAdapter == NULL_PATMLANE_ADAPTER)
		{
			Status = STATUS_UNSUCCESSFUL;
			break;
		}

		// find elan struct

		pElan = AtmLaneIoctlNameToElan(pAdapter, pElanNameIn);

		if (pElan == NULL_PATMLANE_ELAN)
		{
			Status = STATUS_UNSUCCESSFUL;
			break;
		}

		//	setup to return empty list

		pArpTable = (PATMLANE_ARPTABLE)pBuffer;
		pArpTable->ArpEntriesAvailable = pElan->NumMacEntries;
		pArpTable->ArpEntriesReturned = 0;
		*pBytesWritten = sizeof(ATMLANE_ARPTABLE);
		Remaining = OutputBufferLength - sizeof(ATMLANE_ARPTABLE);
	
		pArpEntry = (PATMLANE_ARPENTRY)(pBuffer + sizeof(ATMLANE_ARPTABLE));
	
		ACQUIRE_ELAN_MAC_TABLE_LOCK(pElan);

		//	loop thru array of lists
	
		for (i = 0; i < ATMLANE_MAC_TABLE_SIZE; i++)
		{
			pMacEntry = pElan->pMacTable[i];

			while (pMacEntry != NULL_PATMLANE_MAC_ENTRY)
			{

				//	check if enough space remaining

				if (Remaining < sizeof(ATMLANE_ARPENTRY))
				{
					Status = STATUS_BUFFER_OVERFLOW;
					break;
				}

				// 	output the entry

				NdisZeroMemory(pArpEntry, sizeof(ATMLANE_ARPENTRY));

				NdisMoveMemory(
					pArpEntry->MacAddress, 
					&pMacEntry->MacAddress, 
					sizeof(MAC_ADDRESS));

				if (pMacEntry->pAtmEntry != NULL_PATMLANE_ATM_ENTRY)
				{
					NdisMoveMemory(
						pArpEntry->AtmAddress,
						pMacEntry->pAtmEntry->AtmAddress.Address,
						ATM_ADDRESS_LENGTH);
				}

				//	update space used and space remaining

				*pBytesWritten += sizeof(ATMLANE_ARPENTRY);
				Remaining -= sizeof(ATMLANE_ARPENTRY);

				// 	increment in and out pointers
				
				pArpEntry++;
				pMacEntry = pMacEntry->pNextEntry;

				// 	add one to EntriesReturned
				
				pArpTable->ArpEntriesReturned++;
				
			}
		}
			
		RELEASE_ELAN_MAC_TABLE_LOCK(pElan);

	} while (FALSE);

	if (pElan != NULL)
	{
		ACQUIRE_ELAN_LOCK(pElan);
		rc = AtmLaneDereferenceElan(pElan, "ioctl: getelanarp");

		if (rc != 0)
		{
			RELEASE_ELAN_LOCK(pElan);
		}
	}

	if (pAdapter != NULL)
	{
		ACQUIRE_ADAPTER_LOCK(pAdapter);
		rc = AtmLaneDereferenceAdapter(pAdapter, "ioctl: getelanarp");
		
		if (rc != 0)
		{
			RELEASE_ADAPTER_LOCK(pAdapter);
		}
	}

	TRACEOUT(IoctlGetElanArpTable);

	return (Status);
}


NTSTATUS
AtmLaneIoctlGetElanConnectTable(
	IN	PUCHAR				pBuffer,
	IN 	UINT				InputBufferLength,
	IN	UINT				OutputBufferLength,
	OUT	UINT_PTR *			pBytesWritten
)
/*++

Routine Description:

	Return the Connection table for the specified ELAN.

Arguments:

	pBuffer				- Space for input/output
	InputBufferLength	- Length of input parameters
	OutputBufferLength	- Space available for output
	pBytesWritten		- Where we return the amount we actually used up

Return Value:
	Status code

--*/
{
	PATMLANE_ADAPTER		pAdapter;
	PATMLANE_ELAN			pElan;
	PUNICODE_STRING			pAdapterNameIn;
	PUNICODE_STRING			pElanNameIn;
	PATMLANE_CONNECTTABLE	pConnTable;
	PATMLANE_CONNECTENTRY	pConnEntry;
	UINT					Remaining;
	PATMLANE_ATM_ENTRY		pAtmEntry;
	NTSTATUS				Status;
	ULONG					rc;

	TRACEIN(IoctlGetElanConnectTable);

	// init

	*pBytesWritten = 0;
	Status = STATUS_SUCCESS;
	pAdapter = NULL;
	pElan = NULL;

	do
	{
		// check if adapter string passed in

		if (InputBufferLength < sizeof(UNICODE_STRING))
		{
			Status = STATUS_BUFFER_OVERFLOW;
			break;
		}

		// check if elan string passed in

		if (InputBufferLength < (((sizeof(UNICODE_STRING) * 2) + 
								((PUNICODE_STRING)pBuffer)->MaximumLength)))
		{
			Status = STATUS_BUFFER_OVERFLOW;
			break;
		}	

		//  sanity check

		if (((PUNICODE_STRING)pBuffer)->MaximumLength < ((PUNICODE_STRING)pBuffer)->Length)
		{
			Status = STATUS_BUFFER_OVERFLOW;
			break;
		}

		// check if minimum output space

		if (OutputBufferLength < sizeof(ATMLANE_CONNECTTABLE))
		{
			Status = STATUS_BUFFER_OVERFLOW;
			break;
		}

		// setup ptrs to input names

		pAdapterNameIn = (PUNICODE_STRING)pBuffer;
		pElanNameIn = (PUNICODE_STRING)(pBuffer + sizeof(UNICODE_STRING) + 
						pAdapterNameIn->MaximumLength);

		// How much of the input buffer do we have left?
		InputBufferLength -= (sizeof(UNICODE_STRING) + pAdapterNameIn->MaximumLength);

		// validate the ELAN name buffer
		if (pElanNameIn->MaximumLength < pElanNameIn->Length)
		{
			Status = STATUS_BUFFER_OVERFLOW;
			break;
		}

		if (InputBufferLength < sizeof(UNICODE_STRING) + pElanNameIn->MaximumLength)
		{
			Status = STATUS_BUFFER_OVERFLOW;
			break;
		}

		// find adapter struct

		pAdapter = AtmLaneIoctlNameToAdapter(pAdapterNameIn);

		if (pAdapter == NULL_PATMLANE_ADAPTER)
		{
			Status = STATUS_UNSUCCESSFUL;
			break;
		}

		// find elan struct

		pElan = AtmLaneIoctlNameToElan(pAdapter, pElanNameIn);

		if (pElan == NULL_PATMLANE_ELAN)
		{
			Status = STATUS_UNSUCCESSFUL;
			break;
		}

		//	setup to return empty list

		pConnTable = (PATMLANE_CONNECTTABLE)pBuffer;
		pConnTable->ConnectEntriesAvailable = pElan->NumAtmEntries;
		pConnTable->ConnectEntriesReturned = 0;
		*pBytesWritten = sizeof(ATMLANE_CONNECTTABLE);
		Remaining = OutputBufferLength - sizeof(ATMLANE_CONNECTTABLE);
	
		pConnEntry = 
			(PATMLANE_CONNECTENTRY)(pBuffer + sizeof(ATMLANE_CONNECTTABLE));
	
		ACQUIRE_ELAN_ATM_LIST_LOCK(pElan);

		//	loop thru the list
		
		pAtmEntry = pElan->pAtmEntryList;

		while (pAtmEntry != NULL_PATMLANE_ATM_ENTRY)
		{

			//	check if enough space for another entry
	
			if (Remaining < sizeof(ATMLANE_CONNECTENTRY))
			{
				Status = STATUS_BUFFER_OVERFLOW;
				break;
			}

			//	fill in entry
		
			NdisMoveMemory(
				pConnEntry->AtmAddress, 
				&pAtmEntry->AtmAddress.Address,
				ATM_ADDRESS_LENGTH
				);
			pConnEntry->Type = pAtmEntry->Type;
			pConnEntry->Vc = (pAtmEntry->pVcList!=NULL_PATMLANE_VC);
			pConnEntry->VcIncoming = (pAtmEntry->pVcIncoming!=NULL_PATMLANE_VC);

			// 	update space used and space remaining

			*pBytesWritten += sizeof(ATMLANE_CONNECTENTRY);
			Remaining -= sizeof(ATMLANE_CONNECTENTRY);
			
			//	increment in and out pointers

			pConnEntry++;
			pAtmEntry = pAtmEntry->pNext;

			// 	add one to EntriesReturned

			pConnTable->ConnectEntriesReturned++;
		}

		RELEASE_ELAN_ATM_LIST_LOCK(pElan);

	} while (FALSE);

	if (pElan != NULL)
	{
		ACQUIRE_ELAN_LOCK(pElan);
		rc = AtmLaneDereferenceElan(pElan, "ioctl: getelanconntab");

		if (rc != 0)
		{
			RELEASE_ELAN_LOCK(pElan);
		}
	}

	if (pAdapter != NULL)
	{
		ACQUIRE_ADAPTER_LOCK(pAdapter);
		rc = AtmLaneDereferenceAdapter(pAdapter, "ioctl: getelanconntab");
		
		if (rc != 0)
		{
			RELEASE_ADAPTER_LOCK(pAdapter);
		}
	}

	TRACEOUT(IoctlGetElanConnectTable);

	return (Status);
}


NTSTATUS
AtmLaneIoctlRequest(
	IN	PIRP					pIrp
)
/*++

Routine Description:

	Starting point for all IOCTL Requests.

Arguments:

	pIrp			: Pointer to the IRP
	pHandled		: If request handled TRUE otherwise FALSE

Return Value:

	Status of the request

--*/
{
	NTSTATUS            Status = STATUS_SUCCESS;
	PUCHAR				pBuf;
	UINT				BufLen;
	UINT				OutBufLen;
	UNICODE_STRING		IfName;
    PIO_STACK_LOCATION 	pIrpSp;

	TRACEIN(IoctlRequest);
	
	pIrpSp = IoGetCurrentIrpStackLocation(pIrp);

	pBuf = pIrp->AssociatedIrp.SystemBuffer;
	BufLen = pIrpSp->Parameters.DeviceIoControl.InputBufferLength;
	OutBufLen = pIrpSp->Parameters.DeviceIoControl.OutputBufferLength;

	switch (pIrpSp->Parameters.DeviceIoControl.IoControlCode)
	{
		case ATMLANE_IOCTL_GET_INFO_VERSION:
			DBGP((3, "IoctlRequest: Get Info Version\n"));

			Status = AtmLaneIoctlGetInfoVersion(
						pBuf,
						BufLen,
						OutBufLen,
						&pIrp->IoStatus.Information
						);
			break;
	
		case ATMLANE_IOCTL_ENUM_ADAPTERS:

			DBGP((3, "IoctlRequest: Enum Adapters\n"));
		
			Status = AtmLaneIoctlEnumerateAdapters(
						pBuf,
						BufLen,
						OutBufLen,
						&pIrp->IoStatus.Information
						);
			break;
			
		case ATMLANE_IOCTL_ENUM_ELANS:

			DBGP((3, "IoctlRequest: Enum ELANs\n"));
		
			Status = AtmLaneIoctlEnumerateElans(
						pBuf,
						BufLen,
						OutBufLen,
						&pIrp->IoStatus.Information
						);
			break;
		
		case ATMLANE_IOCTL_GET_ELAN_INFO:

			DBGP((3, "IoctlRequest: Get ELAN Info\n"));
		
			Status = AtmLaneIoctlGetElanInfo(
						pBuf,
						BufLen,
						OutBufLen,
						&pIrp->IoStatus.Information
						);
			break;
		
		case ATMLANE_IOCTL_GET_ELAN_ARP_TABLE:

			DBGP((3, "IoctlRequest: Getl ELAN ARP table\n"));
		
			Status = AtmLaneIoctlGetElanArpTable(
						pBuf,
						BufLen,
						OutBufLen,
						&pIrp->IoStatus.Information
						);
			break;
		
		case ATMLANE_IOCTL_GET_ELAN_CONNECT_TABLE:

			DBGP((3, "IoctlRequest: Get ELAN Connection table\n"));
		
			Status = AtmLaneIoctlGetElanConnectTable(
						pBuf,
						BufLen,
						OutBufLen,
						&pIrp->IoStatus.Information
						);
			break;
		
		default:
		
			DBGP((0, "IoctlRequest: Unknown control code %x\n", 
				pIrpSp->Parameters.DeviceIoControl.IoControlCode));
			break;
	}
	
	TRACEOUT(IoctlRequest);

	return (Status);

}


