/*++

Copyright (c) 1997  Microsoft Corporation

Module Name:

	D:\nt\private\ntos\tdi\rawwan\core\info.c

Abstract:

	Routines for handling query/set information requests.

Revision History:

	Who         When        What
	--------    --------    ----------------------------------------------
	arvindm     06-09-97    Created

Notes:

--*/

#include <precomp.h>

#define _FILENUMBER 'OFNI'


//
//  Kludgy way to ensure we have enough space for a transport
//  address in the following INFO BUF structure.
//
#define MAX_RWAN_TDI_INFO_LENGTH		200


typedef union _RWAN_TDI_INFO_BUF
{
	TDI_CONNECTION_INFO			ConnInfo;
	TDI_ADDRESS_INFO			AddrInfo;
	TDI_PROVIDER_INFO			ProviderInfo;
	TDI_PROVIDER_STATISTICS		ProviderStats;
	UCHAR						Space[MAX_RWAN_TDI_INFO_LENGTH];

} RWAN_TDI_INFO_BUF, *PRWAN_TDI_INFO_BUF;



TDI_STATUS
RWanTdiQueryInformation(
    IN	PTDI_REQUEST				pTdiRequest,
    IN	UINT						QueryType,
    IN	PNDIS_BUFFER				pNdisBuffer,
    IN	PUINT						pBufferSize,
    IN	UINT						IsConnection
    )
/*++

Routine Description:

	This is the TDI entry point to handle a QueryInformation TDI request.

Arguments:

	pTdiRequest		- Pointer to the TDI Request
	QueryType		- Information being queried for
	pNdisBuffer		- Start of list of buffers containing query data
	pBufferSize		- Total space in above list
	IsConnection	- Is this query on a connection endpoint?

Return Value:

	TDI_STATUS:  TDI_SUCCESS if the query was processed
	successfully, TDI_STATUS_XXX for any error.

--*/
{
	TDI_STATUS				TdiStatus;
	RWAN_TDI_INFO_BUF		InfoBuf;
	PVOID					InfoPtr;
	UINT					InfoSize;
	UINT					Offset;
	UINT					Size;
	UINT					BytesCopied;
	PRWAN_TDI_PROTOCOL		pProtocol;
	PRWAN_TDI_ADDRESS		pAddrObject;
	PRWAN_TDI_CONNECTION		pConnObject;
	RWAN_CONN_ID				ConnId;

	TdiStatus = TDI_SUCCESS;
	InfoPtr = NULL;

	switch (QueryType)
	{
		case TDI_QUERY_BROADCAST_ADDRESS:

			TdiStatus = TDI_INVALID_QUERY;
			break;

		case TDI_QUERY_PROVIDER_INFO:

			pProtocol = pTdiRequest->Handle.ControlChannel;
			RWAN_STRUCT_ASSERT(pProtocol, ntp);

			InfoBuf.ProviderInfo = pProtocol->ProviderInfo;
			InfoSize = sizeof(TDI_PROVIDER_INFO);
			InfoPtr = &InfoBuf.ProviderInfo;
			break;
	
		case TDI_QUERY_ADDRESS_INFO:

			if (IsConnection)
			{
				ConnId = (RWAN_CONN_ID) PtrToUlong(pTdiRequest->Handle.ConnectionContext);

				RWAN_ACQUIRE_CONN_TABLE_LOCK();

				pConnObject = RWanGetConnFromId(ConnId);

				RWAN_RELEASE_CONN_TABLE_LOCK();

				if (pConnObject == NULL_PRWAN_TDI_CONNECTION)
				{
					TdiStatus = TDI_INVALID_CONNECTION;
					break;
				}

				pAddrObject = pConnObject->pAddrObject;

			}
			else
			{
				pAddrObject = (PRWAN_TDI_ADDRESS)pTdiRequest->Handle.AddressHandle;
			}

			if (pAddrObject == NULL_PRWAN_TDI_ADDRESS)
			{
				TdiStatus = TDI_INVALID_CONNECTION;
				break;
			}

			RWAN_STRUCT_ASSERT(pAddrObject, nta);

			RWAN_ACQUIRE_ADDRESS_LOCK(pAddrObject);

			RWAN_ASSERT(pAddrObject->AddressLength <=
							(sizeof(RWAN_TDI_INFO_BUF) - sizeof(TDI_ADDRESS_INFO)));

			InfoSize = sizeof(TDI_ADDRESS_INFO) - sizeof(TRANSPORT_ADDRESS) +
						pAddrObject->AddressLength;

			InfoBuf.AddrInfo.ActivityCount = 1;	// same as TCP
			InfoBuf.AddrInfo.Address.TAAddressCount = 1;
			InfoBuf.AddrInfo.Address.Address[0].AddressLength = pAddrObject->AddressLength;
			InfoBuf.AddrInfo.Address.Address[0].AddressType = pAddrObject->AddressType;
			RWAN_COPY_MEM(InfoBuf.AddrInfo.Address.Address[0].Address,
						 pAddrObject->pAddress,
						 pAddrObject->AddressLength);

			RWAN_RELEASE_ADDRESS_LOCK(pAddrObject);

			RWANDEBUGP(DL_LOUD, DC_DISPATCH,
						("RWanTdiQueryInfo: IsConn %d, Addr dump:\n", IsConnection));
			RWANDEBUGPDUMP(DL_LOUD, DC_DISPATCH, pAddrObject->pAddress, pAddrObject->AddressLength);
			InfoPtr = &InfoBuf.AddrInfo;

			TdiStatus = TDI_SUCCESS;

			break;
		
		case TDI_QUERY_CONNECTION_INFO:

			TdiStatus = TDI_INVALID_QUERY;
			break;
		
		case TDI_QUERY_PROVIDER_STATISTICS:

			pProtocol = pTdiRequest->Handle.ControlChannel;
			RWAN_STRUCT_ASSERT(pProtocol, ntp);

			InfoBuf.ProviderStats = pProtocol->ProviderStats;
			InfoSize = sizeof(TDI_PROVIDER_STATISTICS);
			InfoPtr = &InfoBuf.ProviderStats;
			break;
		
		default:

			TdiStatus = TDI_INVALID_QUERY;
			break;
	}

	if (TdiStatus == TDI_SUCCESS)
	{
		RWAN_ASSERT(InfoPtr != NULL);
		Offset = 0;
		Size = *pBufferSize;

		(VOID)RWanCopyFlatToNdis(
					pNdisBuffer,
					InfoPtr,
					MIN(InfoSize, Size),
					&Offset,
					&BytesCopied
					);
		
		if (Size < InfoSize)
		{
			TdiStatus = TDI_BUFFER_OVERFLOW;
		}
		else
		{
			*pBufferSize = InfoSize;
		}
	}

	return (TdiStatus);
	
}


RWAN_STATUS
RWanHandleGenericConnQryInfo(
    IN	HANDLE						ConnectionContext,
    IN	PVOID						pInputBuffer,
    IN	ULONG						InputBufferLength,
    OUT	PVOID						pOutputBuffer,
    IN OUT	PVOID					pOutputBufferLength
    )
/*++

Routine Description:

	Handle a generic QueryInformation command on a Connection Object.

Arguments:

	AddrHandle			- Pointer to our address object structure
	ConnectionContext	- TDI Connection ID
	pInputBuffer		- Query Info structure
	InputBufferLength	- Length of the above
	pOutputBuffer		- Output buffer
	pOutputBufferLength	- Space available/bytes filled in.

Return Value:

	RWAN_STATUS_SUCCESS if the command was processed successfully,
	RWAN_STATUS_XXX if not.

--*/
{
	PRWAN_TDI_CONNECTION		pConnObject;
	RWAN_STATUS					RWanStatus;
	PRWAN_QUERY_INFORMATION_EX	pQueryInfo;

	RWanStatus = RWAN_STATUS_SUCCESS;

	do
	{
		if (InputBufferLength < sizeof(RWAN_QUERY_INFORMATION_EX) ||
			pOutputBuffer == NULL)
		{
			RWanStatus = RWAN_STATUS_RESOURCES;
			break;
		}

		RWAN_ACQUIRE_CONN_TABLE_LOCK();

		pConnObject = RWanGetConnFromId((RWAN_CONN_ID)PtrToUlong(ConnectionContext));

		RWAN_RELEASE_CONN_TABLE_LOCK();

		if (pConnObject == NULL)
		{
			RWanStatus = RWAN_STATUS_BAD_PARAMETER;
			break;
		}

		RWAN_STRUCT_ASSERT(pConnObject, ntc);

		pQueryInfo = (PRWAN_QUERY_INFORMATION_EX)pInputBuffer;

		if (InputBufferLength < sizeof(RWAN_QUERY_INFORMATION_EX) + pQueryInfo->ContextLength - sizeof(UCHAR))
		{
			RWanStatus = RWAN_STATUS_RESOURCES;
			break;
		}

		switch (pQueryInfo->ObjectId)
		{
			case RWAN_OID_CONN_OBJECT_MAX_MSG_SIZE:

				if (*(PULONG)(ULONG_PTR)pOutputBufferLength < sizeof(ULONG))
				{
					RWanStatus = RWAN_STATUS_RESOURCES;
					break;
				}

				RWAN_ACQUIRE_CONN_LOCK(pConnObject);

				if (pConnObject->NdisConnection.pNdisVc)
				{
					*(PULONG)(ULONG_PTR)pOutputBuffer = pConnObject->NdisConnection.pNdisVc->MaxSendSize;
					*(PULONG)(ULONG_PTR)pOutputBufferLength = sizeof(ULONG);
				}
				else
				{
					RWanStatus = RWAN_STATUS_BAD_PARAMETER;
				}

				RWAN_RELEASE_CONN_LOCK(pConnObject);
				break;
			
			default:

				RWanStatus = RWAN_STATUS_BAD_PARAMETER;
				break;
		}

		break;
	}
	while (FALSE);

	RWANDEBUGP(DL_LOUD, DC_BIND,
		("RWanHandleGenericConnQry: returning status %x\n", RWanStatus));

	return (RWanStatus);
}


RWAN_STATUS
RWanHandleGenericAddrSetInfo(
    IN	HANDLE						AddrHandle,
    IN	PVOID						pInputBuffer,
    IN	ULONG						InputBufferLength
    )
/*++

Routine Description:

	Handle a non-media specific SetInformation command on an Address Object.

Arguments:

	AddrHandle			- Pointer to our address object structure
	pInputBuffer		- Set Info structure
	InputBufferLength	- Length of the above

Return Value:

	RWAN_STATUS_SUCCESS if the command was processed successfully,
	RWAN_STATUS_XXX if not.

--*/
{
	PRWAN_TDI_ADDRESS			pAddrObject;
	PRWAN_TDI_CONNECTION		pConnObject;
	PRWAN_SET_INFORMATION_EX	pSetInfo;
	RWAN_STATUS					RWanStatus;
	ULONG						Flags;

	RWanStatus = RWAN_STATUS_SUCCESS;
	pAddrObject = (PRWAN_TDI_ADDRESS)AddrHandle;

	do
	{
		if (pAddrObject == NULL)
		{
			RWanStatus = RWAN_STATUS_BAD_ADDRESS;
			break;
		}

		RWAN_STRUCT_ASSERT(pAddrObject, nta);

		if (InputBufferLength < sizeof(RWAN_SET_INFORMATION_EX))
		{
			RWanStatus = RWAN_STATUS_RESOURCES;
			break;
		}

		pSetInfo = (PRWAN_SET_INFORMATION_EX)pInputBuffer;

		if (InputBufferLength < sizeof(RWAN_SET_INFORMATION_EX) + pSetInfo->BufferSize - sizeof(UCHAR))
		{
			RWanStatus = RWAN_STATUS_RESOURCES;
			break;
		}

		switch (pSetInfo->ObjectId)
		{
			case RWAN_OID_ADDRESS_OBJECT_FLAGS:

				if (pSetInfo->BufferSize < sizeof(ULONG))
				{
					RWanStatus = RWAN_STATUS_RESOURCES;
					break;
				}

				Flags = *((PULONG)&pSetInfo->Buffer[0]);

				if (Flags & RWAN_AOFLAG_C_ROOT)
				{
					//
					//  This Address Object is designated as the Root of
					//  an outgoing Point to Multipoint connection.
					//

					RWAN_ACQUIRE_ADDRESS_LOCK(pAddrObject);

					RWAN_SET_BIT(pAddrObject->Flags, RWANF_AO_PMP_ROOT);

					if (pAddrObject->pRootConnObject != NULL)
					{
						RWAN_RELEASE_ADDRESS_LOCK(pAddrObject);
						RWanStatus = RWAN_STATUS_BAD_ADDRESS;
						break;
					}

					//
					//  There should be a single Connection Object associated
					//  with this Address Object. That should now be designated
					//  the Root Connection Object.
					//
					RWAN_ASSERT(!RWAN_IS_LIST_EMPTY(&pAddrObject->IdleConnList));
					pConnObject = CONTAINING_RECORD(pAddrObject->IdleConnList.Flink, RWAN_TDI_CONNECTION, ConnLink);

					RWAN_STRUCT_ASSERT(pConnObject, ntc);

					pAddrObject->pRootConnObject = pConnObject;

					RWAN_ACQUIRE_CONN_LOCK_DPC(pConnObject);

					RWAN_SET_BIT(pConnObject->Flags, RWANF_CO_ROOT);

					RWAN_RELEASE_CONN_LOCK_DPC(pConnObject);

					RWAN_RELEASE_ADDRESS_LOCK(pAddrObject);

					RWANDEBUGP(DL_LOUD, DC_ADDRESS,
						("Marked PMP Root: AddrObj x%x, ConnObj x%x\n",
							pAddrObject, pConnObject));

				}

				break;

			default:

				RWanStatus = RWAN_STATUS_BAD_PARAMETER;
				break;
		}

		break;
	}
	while (FALSE);

	RWANDEBUGP(DL_VERY_LOUD, DC_DISPATCH,
			("Generic Set Addr: AddrObj x%x, returning x%x\n", pAddrObject, RWanStatus));

	return (RWanStatus);
}


RWAN_STATUS
RWanHandleMediaSpecificAddrSetInfo(
    IN	HANDLE						AddrHandle,
    IN	PVOID						pInputBuffer,
    IN	ULONG						InputBufferLength
    )
/*++

Routine Description:

	Handle a media specific SetInformation command on an Address Object.

Arguments:

	AddrHandle			- Pointer to our address object structure
	pInputBuffer		- Set Info structure
	InputBufferLength	- Length of the above

Return Value:

	RWAN_STATUS_SUCCESS if the command was processed successfully,
	RWAN_STATUS_XXX if not.

--*/
{
	PRWAN_NDIS_AF_CHARS			pAfChars;
	PRWAN_TDI_ADDRESS			pAddrObject;
	RWAN_STATUS					RWanStatus;
	ULONG						Flags;

	RWanStatus = RWAN_STATUS_SUCCESS;
	pAddrObject = (PRWAN_TDI_ADDRESS)AddrHandle;

	do
	{
		if (pAddrObject == NULL)
		{
			RWanStatus = RWAN_STATUS_BAD_ADDRESS;
			break;
		}

		RWAN_STRUCT_ASSERT(pAddrObject, nta);

		pAfChars = &(pAddrObject->pProtocol->pAfInfo->AfChars);

		if (pAfChars->pAfSpSetAddrInformation != NULL)
		{
			RWanStatus = (*pAfChars->pAfSpSetAddrInformation)(
							pAddrObject->AfSpAddrContext,
							pInputBuffer,
							InputBufferLength
							);
		}
		else
		{
			RWanStatus = RWAN_STATUS_FAILURE;
		}

		break;
	}
	while (FALSE);

	return (RWanStatus);
}


RWAN_STATUS
RWanHandleMediaSpecificConnQryInfo(
    IN	HANDLE						ConnectionContext,
    IN	PVOID						pInputBuffer,
    IN	ULONG						InputBufferLength,
    OUT	PVOID						pOutputBuffer,
    IN OUT	PVOID					pOutputBufferLength
    )
/*++

Routine Description:

	Handle a media specific QueryInformation command on a Connection Object.

Arguments:

	AddrHandle			- Pointer to our address object structure
	ConnectionContext	- TDI Connection ID
	pInputBuffer		- Query Info structure
	InputBufferLength	- Length of the above
	pOutputBuffer		- Output buffer
	pOutputBufferLength	- Space available/bytes filled in.

Return Value:

	RWAN_STATUS_SUCCESS if the command was processed successfully,
	RWAN_STATUS_XXX if not.

--*/
{
	PRWAN_NDIS_AF_CHARS			pAfChars;
	PRWAN_TDI_CONNECTION		pConnObject;
	RWAN_STATUS					RWanStatus;
	ULONG						Flags;

	RWanStatus = RWAN_STATUS_SUCCESS;

	do
	{
		RWAN_ACQUIRE_CONN_TABLE_LOCK();

		pConnObject = RWanGetConnFromId((RWAN_CONN_ID)PtrToUlong(ConnectionContext));

		RWAN_RELEASE_CONN_TABLE_LOCK();

		if ((pConnObject == NULL) ||
			(pConnObject->pAddrObject == NULL))
		{
			RWanStatus = RWAN_STATUS_BAD_PARAMETER;
			break;
		}

		RWAN_STRUCT_ASSERT(pConnObject, ntc);

		pAfChars = &(pConnObject->pAddrObject->pProtocol->pAfInfo->AfChars);

		if (pAfChars->pAfSpQueryConnInformation != NULL)
		{
			RWanStatus = (*pAfChars->pAfSpQueryConnInformation)(
							pConnObject->AfSpConnContext,
							pInputBuffer,
							InputBufferLength,
							pOutputBuffer,
							pOutputBufferLength
							);
		}
		else
		{
			RWanStatus = RWAN_STATUS_FAILURE;
		}

		break;
	}
	while (FALSE);

	return (RWanStatus);
}



PNDIS_BUFFER
RWanCopyFlatToNdis(
    IN	PNDIS_BUFFER				pDestBuffer,
    IN	PUCHAR						pSrcBuffer,
    IN	UINT						LengthToCopy,
    IN OUT	PUINT					pStartOffset,
    OUT	PUINT						pBytesCopied
    )
/*++

Routine Description:

	Copy from a flat memory buffer to an NDIS buffer chain. It is assumed
	that the NDIS buffer chain has enough space.

	TBD: Use the TDI function for copying from flat mem to MDL.

Arguments:

	pDestBuffer		- First buffer in the destination NDIS buffer chain.
	pSrcBuffer		- Pointer to start of flat memory
	LengthToCopy	- Max bytes to copy
	pStartOffset	- Copy offset in first buffer
	pBytesCopied	- Place to return actual bytes copied

Return Value:

	Pointer to buffer in chain where data can be copied into next.
	Also, *pStartOffset and *pBytesCopied are set.

--*/
{
	UINT		CopyLength;
	PUCHAR		pDest;
	UINT		Offset;
	UINT		BytesCopied;
	UINT		DestSize;
	UINT		CopySize;

	BytesCopied = 0;
	Offset = *pStartOffset;

	pDest = (PUCHAR)NdisBufferVirtualAddress(pDestBuffer) + Offset;
	DestSize = NdisBufferLength(pDestBuffer) - Offset;

	for (;;)
	{
		CopySize = MIN(DestSize, LengthToCopy);

		RWAN_COPY_MEM(pDest, pSrcBuffer, CopySize);

		pDest += CopySize;
		pSrcBuffer += CopySize;
		BytesCopied += CopySize;

		LengthToCopy -= CopySize;

		if (LengthToCopy == 0)
		{
			break;
		}

		DestSize -= CopySize;

		if (DestSize == 0)
		{
			pDestBuffer = NDIS_BUFFER_LINKAGE(pDestBuffer);
			RWAN_ASSERT(pDestBuffer != NULL);

			pDest = NdisBufferVirtualAddress(pDestBuffer);
			DestSize = NdisBufferLength(pDestBuffer);
		}
	}

	//
	//  Prepare return values.
	//
	*pStartOffset = (UINT)(pDest - (PUCHAR)NdisBufferVirtualAddress(pDestBuffer));
	*pBytesCopied = BytesCopied;

	return (pDestBuffer);
}