// Copyright (c) 2004-2009 Nokia Corporation and/or its subsidiary(-ies).
// All rights reserved.
// This component and the accompanying materials are made available
// under the terms of "Eclipse Public License v1.0"
// which accompanies this distribution, and is available
// at the URL "http://www.eclipse.org/legal/epl-v10.html".
//
// Initial Contributors:
// Nokia Corporation - initial contribution.
//
// Contributors:
//
// Description:
// loop6.cpp - loopback interface for IPv6
//

#include "in_fmly.h"
#include "loop6.h"
#include <in_iface.h>	// KSoIf*
#include "networkinfo.h"

CIfLoop6::CIfLoop6()
{
	__DECLARE_NAME(_S("CIfLoop6"));
}

void CIfLoop6::ConstructL(const TDesC& aTag)
{
	iIfaceName = aTag.AllocL();
	iIfaceMTU = 4096;

	TCallBack scb(SendCallBack, this);
	iSendCallBack = new(ELeave) CAsyncCallBack(scb, KInet6DefaultPriority);

	TCallBack rcb(RecvCallBack, this);
	iRecvCallBack = new(ELeave) CAsyncCallBack(rcb, KInet6DefaultPriority);
}

CIfLoop6::~CIfLoop6()
{
	iRecvQ.Free();
	iSendQ.Free();

	delete iRecvCallBack;
	delete iSendCallBack;
	delete iIfaceName;
}

CIfLoop6* CIfLoop6::NewL(const TDesC& aTag)
{
	CIfLoop6* p = new(ELeave) CIfLoop6;

	CleanupStack::PushL(p);
	p->ConstructL(aTag);
	CleanupStack::Pop();

	return p;
}

void CIfLoop6::BindL(TAny* aId)
{
	iNetwork = (CProtocolBase*)aId;
	iNetwork->StartSending((CProtocolBase *)this);
}

TInt CIfLoop6::State()
{
	return EIfUp;
}

TInt CIfLoop6::Control(TUint aLevel, TUint aName, TDes8& aOption, TAny*)
	{
	if (aLevel == KSOLInterface)
		{
		switch (aName)
			{
			case KSoIfInfo6:
				{
				__ASSERT_DEBUG(STATIC_CAST(TUint, aOption.MaxLength()) >= sizeof (TSoIfInfo6), User::Panic(_L("LOOP6"), 0));

				if (STATIC_CAST(TUint, aOption.MaxLength()) < sizeof (TSoIfInfo6))
					return KErrArgument;

				TSoIfInfo6& opt = *(TSoIfInfo6*)aOption.Ptr();
				opt.iRMtu = iIfaceMTU;
				}
				// FALL THROUGH AS TSoIfInfo6 IS AN EXTENSION OF TSoIfInfo

			case KSoIfInfo:
				{
				__ASSERT_DEBUG(STATIC_CAST(TUint, aOption.MaxLength()) >= sizeof (TSoIfInfo), User::Panic(_L("LOOP6"), 0));

				if (STATIC_CAST(TUint, aOption.MaxLength()) < sizeof (TSoIfInfo))
					return KErrArgument;

				TSoIfInfo& opt = *(TSoIfInfo*)aOption.Ptr();
	
				opt.iFeatures = KIfIsLoopback | KIfCanMulticast;
				opt.iMtu = iIfaceMTU;
				opt.iSpeedMetric = 30000;
				opt.iName = *iIfaceName;

				return KErrNone;
				}


			case KSoIfConfig:
				// PS: If KSoIfConfig returns KErrNotSupported, Update6 does not proceed
				// with setting routes, etc.
				{
				  TSoInet6IfConfig& cfg = *(TSoInet6IfConfig*)aOption.Ptr();	
				  if (!iIfaceName->Compare(_L("loop6")) && cfg.iFamily == KAfInet6)
				    {
				    
				      TSockAddr unspec(KAFUnspec);
				      cfg.iLocalId = unspec;
				      cfg.iRemoteId = unspec;
				      return KErrNone;
				    }
				}
				break;
			case KSoIfGetConnectionInfo:
				{
				if (STATIC_CAST(TUint, aOption.MaxLength()) < sizeof(TSoIfConnectionInfo))
					return KErrArgument;
				TSoIfConnectionInfo &opt = *(TSoIfConnectionInfo *)aOption.Ptr();
				opt.iIAPId = 0;
				opt.iNetworkId = 0;
				return KErrNone;
				}

			default:
				break;
			}
		}
	return KErrNotSupported;
}

void CIfLoop6::Info(TNifIfInfo& aInfo) const
{
	aInfo.iVersion = TVersion(KInet6MajorVersionNumber, KInet6MinorVersionNumber, KInet6BuildVersionNumber);
	aInfo.iFlags = KNifIfIsBase | KNifIfCreatedAlone;
	aInfo.iName = *iIfaceName;
	aInfo.iProtocolSupported = KProtocolInet6Ip;
}

TInt CIfLoop6::Send(RMBufChain &aPacket, TAny*)
{
	iSendQ.Append(aPacket);
	iSendCallBack->CallBack();

	return 1;
}

TInt CIfLoop6::RecvCallBack(TAny* aCProtocol)
{
	((CIfLoop6*)aCProtocol)->DoProcess();

	return 0;
}

TInt CIfLoop6::SendCallBack(TAny* aCProtocol)
{
	((CIfLoop6*)aCProtocol)->DoSend();

	return 0;
}

void CIfLoop6::DoSend()
{
	RMBufPacket send;
	RMBufPacket recv;

	while (iSendQ.Remove(send))
	{
		send.Unpack();
		Loop(send, recv);
		iRecvQ.Append(recv);
		iRecvCallBack->CallBack();
		send.Free();
	}
}

void CIfLoop6::DoProcess()
{
	RMBufPacket packet;

	while (iRecvQ.Remove(packet))
	{
		iNetwork->Process(packet, (CProtocolBase*)this);
	}
}
			
void CIfLoop6::Loop(RMBufPacket& aSend, RMBufPacket& aRecv)
{
	RMBufPktInfo* info = aSend.Info();

	info->iProtocol = KProtocolInet6Loop;

	aSend.SetInfo(NULL);
	aRecv.Assign(aSend);
	aRecv.SetInfo(info);
	aRecv.Pack();
}
