telephonyserver/etelserverandcore/SETEL/ET_CORE.CPP
author ivan.fildichev@opencode.com
Thu, 07 Oct 2010 19:32:01 +0300
branchopencode
changeset 77 930a53cdc2d3
parent 24 6638e7f4bd8f
child 81 7f379d8ed02d
permissions -rw-r--r--
Intermidiate version

// Copyright (c) 1997-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:
//

#include "ET_SSTD.H"
#include "ETBUFFER.H"
#include <f32file.h>
#include "etsvr_slots.h"
#include "et_phone_util.h"
#include "et_patchdata.h"
//
//
// CFaxBase
//
//
EXPORT_C CFaxBase::CFaxBase()
//
//	C'Tor
//
	{
	__DECLARE_NAME(_S("CFaxBase"));
	}

EXPORT_C CFaxBase::~CFaxBase()
//
//	D'Tor
//
	{}

EXPORT_C CTelObject* CFaxBase::OpenNewObjectByNameL(const TDesC&)
//
// Always return NULL not allow CFaxBase Tsy object to create new object
//
	{
	User::Leave(KErrNotSupported);
	return NULL;
	}

EXPORT_C CTelObject* CFaxBase::OpenNewObjectL(TDes&)
//
// Always return NULL not allow CFaxBase Tsy object to create new object
//
	{
	User::Leave(KErrNotSupported);
	return NULL;
	}

EXPORT_C void CFaxBase::OpenPostProcessing(CTelSession*,const TInt)
//
// Perform post-processing after object has been added to session's CObjectIx.
//
	{}

EXPORT_C void CFaxBase::CloseSubSessionPreProcessing(CTelSession* aSession,const TInt aSubSessionHandle)
	{
	CreateDummySession(aSession,aSubSessionHandle);
	FlushReqs(aSession,aSubSessionHandle);
	}

EXPORT_C TInt CFaxBase::CancelService(const TInt aIpc,const TTsyReqHandle /*aTsyReqHandle*/)
//
// Cancel Service for Fax
//
	{
	__ASSERT_ALWAYS((aIpc==EEtelFaxRead) ||
					(aIpc==EEtelFaxWrite) ||
					(aIpc==EEtelFaxWaitForEndOfPage)
					,Fault(EEtelFaultInvalidIpcForCancel));

	switch (aIpc)
		{
	// this is a special case for Fax session - 
	// count already increment before calling susession
	// for those reqs below - server do expect Tsy to call ReqCompleted()
	// so the count should be decrement again !
	case EEtelFaxRead:
	case EEtelFaxWrite:
	case EEtelFaxWaitForEndOfPage:
		return KErrNone;	
		// There is no individual cancel associated with these requests
	default:
		return KErrGeneral;
		}
	}


EXPORT_C TInt CFaxBase::Service(const RMessage2& /*aMessage*/,CReqEntry* aReqEntry)
//
// The ServiceL functionality for the fax
//
	{
	__ASSERT_ALWAYS(aReqEntry!=NULL,Fault(EEtelFaultCallTsyServiceWithoutReqPackage));
	aReqEntry->iPlacedRequest=ETrue;
	TTsyReqHandle tsyReqHandle=aReqEntry->iTsyReqHandle;
	TDes8* des1 = BufferDes1(aReqEntry->iBuffer,CBuffer::ESlotWrite);

	switch (aReqEntry->iFunction)
		{
	case EEtelFaxRead:
		__ASSERT_ALWAYS(des1!=NULL,Fault(EETelFaultRequestWithoutBuffer));
		return Read(tsyReqHandle,des1);
	case EEtelFaxWrite:
		__ASSERT_ALWAYS(des1!=NULL,Fault(EETelFaultRequestWithoutBuffer));
		return Write(tsyReqHandle,des1);
	case EEtelFaxWaitForEndOfPage:
		return WaitForEndOfPage(tsyReqHandle);
	case EEtelFaxTerminateFaxSession:
		return TerminateFaxSession(tsyReqHandle);
	default:
		return KErrNotSupported;
		}
	}


EXPORT_C void CFaxBase::Init()
	{}

EXPORT_C CTelObject::TReqMode CFaxBase::ReqModeL(const TInt aIpc)
//
// Fax Request Inquiry Functions
//
    {
	CTelObject::TReqMode mode=0;
	switch (aIpc)
		{
	case EEtelFaxTerminateFaxSession:
		// if two clients call this, only want the first to be acted upon
		mode=KReqModeMultipleCompletionEnabled;	
		break;
	case EEtelFaxRead:
	case EEtelFaxWrite:
	case EEtelFaxWaitForEndOfPage:
		mode=KReqModeFlowControlObeyed;
		break;
	default:
		User::Leave(KErrNotSupported);
		break;
		}
	return mode;
	}

EXPORT_C TInt CFaxBase::NumberOfSlotsL(const TInt /*aIpc*/)
	{
	User::Leave(KErrNotSupported);	// there are no repost immediately functions for fax
	return KSlotNumbersDefault;
	}

//
//
// CCallBase
//
//
EXPORT_C CCallBase::CCallBase()
//
//	C'Tor
//
	:iOwnershipStatus(EOwnedUnowned)
	,iOwnerSession(NULL)
	,iOwnerSubSessionHandle(0)
	,iLoanDataPort(EFalse)
	{
	__DECLARE_NAME(_S("CCallBase"));
	}

EXPORT_C CCallBase::~CCallBase()
//
//	D'Tor
//	Attual closing of the owner is done in CSubSessionBase destructor
//	This is for all object inherit fron CSubSessionBase
//
	{
	LOGTEXT("~CCallBase");
	}

EXPORT_C TInt CCallBase::CancelService(const TInt aIpc,const TTsyReqHandle aTsyReqHandle)
//
// Cancel Service for Call
//
	{
	__ASSERT_ALWAYS((aIpc==EEtelCallNotifyHookChange) ||
					(aIpc==EEtelCallNotifyStatusChange) ||
					(aIpc==EEtelCallNotifyDurationChange) ||
					(aIpc==EEtelCallCapsChangeNotification) ||
					(aIpc==EEtelCallAcquireOwnership) ||
					(aIpc==EEtelCallDial) ||
					(aIpc==EEtelCallAnswer) ||
					(aIpc==EEtelCallHangUp) ||
					(aIpc==EEtelCallConnect)||
					(aIpc==EEtelCallLoanDataPort)
					,Fault(EEtelFaultInvalidIpcForCancel));
	switch (aIpc)
		{
	case EEtelCallCapsChangeNotification:
		return NotifyCapsChangeCancel(aTsyReqHandle);
	case EEtelCallNotifyHookChange:
		return NotifyHookChangeCancel(aTsyReqHandle);
	case EEtelCallNotifyStatusChange:
		return NotifyStatusChangeCancel(aTsyReqHandle);
	case EEtelCallNotifyDurationChange:
		return NotifyDurationChangeCancel(aTsyReqHandle);
	case EEtelCallAcquireOwnership:
		return AcquireOwnershipCancel(aTsyReqHandle);
	case EEtelCallDial:
		return DialCancel(aTsyReqHandle);
	case EEtelCallConnect:
		return ConnectCancel(aTsyReqHandle);
	case EEtelCallAnswer:
		return AnswerIncomingCallCancel(aTsyReqHandle);
	case EEtelCallHangUp:
		return HangUpCancel(aTsyReqHandle);
	case EEtelCallLoanDataPort:
		return LoanDataPortCancel(aTsyReqHandle);
	default:
		return KErrGeneral; // should never reached here
		}
	}

EXPORT_C void CCallBase::Init()
	{}

EXPORT_C CCallBase::TCallOwnership CCallBase::CheckOwnership(const TTsyReqHandle aTsyReqHandle) const
//
// Tsy upcall function - return the call ownership status
//
	{
	CReqEntry* reqEntry=PhoneOwner()->FindByTsyHandle(aTsyReqHandle);
	__ASSERT_ALWAYS(reqEntry!=NULL,Fault(EEtelFaultNotRecognisedTsyHandle));
	RCall::TOwnershipStatus owner=CheckOwnershipBySession(reqEntry->iSession,reqEntry->iMessage.Int3());

	if (owner==RCall::EOwnershipUnowned)
		return EOwnedUnowned;

	if (owner==RCall::EOwnershipOwnedByThisClient)
		return EOwnedTrue;

	if (owner==RCall::EOwnershipThisIsPriorityClient)
		return EOwnedPriorityClient; 

	return EOwnedFalse;
	}

EXPORT_C RCall::TOwnershipStatus CCallBase::CheckOwnershipBySession(CTelSession* aSession,const TInt /*aSubSessionHandle*/) const
//
// Return true if the owner
//
	{
	if (iOwnershipStatus==EOwnedUnowned)
		return RCall::EOwnershipUnowned;

	if(iOwnershipStatus==EOwnedTrue && iOwnerSession==aSession /*&& iOwnerSubSessionHandle==aSubSessionHandle*/)	// ownership is session based!
		return RCall::EOwnershipOwnedByThisClient;

// Ann changed
	if (iOwnershipStatus==EOwnedTrue && aSession->TelServer()->IsPriorityClient(aSession))
		return RCall::EOwnershipThisIsPriorityClient;
// end

	return RCall::EOwnershipOwnedByAnotherClient;
	}

EXPORT_C TInt CCallBase::SetUnowned()
//
// Return Owner ship status
//
	{
	iOwnershipStatus=EOwnedUnowned;
	iOwnerSession=NULL; // set to invalid value
	iOwnerSubSessionHandle=0; 

	return KErrNone;
	}

EXPORT_C TInt CCallBase::SetOwnership(const TTsyReqHandle aTsyReqHandle)
//
// If owner ship if not own
//
	{
	CReqEntry* reqEntry=PhoneOwner()->FindByTsyHandle(aTsyReqHandle);
	__ASSERT_ALWAYS(reqEntry!=NULL,Fault(EEtelFaultNotRecognisedTsyHandle));

	iOwnershipStatus=EOwnedTrue;
	iOwnerSession=reqEntry->iSession;
	iOwnerSubSessionHandle=reqEntry->iMessage.Int3();
	return KErrNone;
	}

EXPORT_C TBool CCallBase::CheckPriorityClient(const TTsyReqHandle aTsyReqHandle) const
	{
	CReqEntry* reqEntry=PhoneOwner()->FindByTsyHandle(aTsyReqHandle);
	__ASSERT_ALWAYS(reqEntry!=NULL,Fault(EEtelFaultNotRecognisedTsyHandle));

	return reqEntry->iSession->TelServer()->IsPriorityClient(reqEntry->iSession);
	}		

EXPORT_C CCallBase* CCallBase::ResolveSubSessionHandle(const TTsyReqHandle aTsyReqHandle,
													   const TInt aSubSessionHandle)
//
//	expects the subsession handle of a call, which must have been opened by the same client session
//	which opened this call and called the current request (identified by aTsyReqHandle)
//
	{
	CReqEntry* reqEntry=PhoneOwner()->FindByTsyHandle(aTsyReqHandle);
	__ASSERT_ALWAYS(reqEntry!=NULL,Fault(EEtelFaultNotRecognisedTsyHandle));
	return REINTERPRET_CAST(CCallBase*,reqEntry->iSession->CObjectFromHandle(aSubSessionHandle));
	}

EXPORT_C void CCallBase::OpenPostProcessing(CTelSession* ,const TInt)
//
// Perform post-processing after object has been added to session's CObjectIx.
//
	{}

EXPORT_C void CCallBase::CloseSubSessionPreProcessing(CTelSession* aSession,const TInt aSubSessionHandle)
//
// Perform close post-processing before object being close
// if is owner then set to EUnOwned
//
	{
	LOGTEXT("CCallBase::CloseSubSessionPreProcessing() - about to create dummy session");
	CreateDummySession(aSession,aSubSessionHandle);
	RCall::TOwnershipStatus owner=CheckOwnershipBySession(aSession,aSubSessionHandle);
	if (owner==RCall::EOwnershipOwnedByThisClient)
		{
		CreateDummySession(aSession,aSubSessionHandle,ETrue);
		if (iLoanDataPort)  // data port being on loan by this owner
			RecoverDataPortAndRelinquishOwnership(); // inform tsy about this
		else
			RelinquishOwnership();
		}
	FlushReqs(aSession,aSubSessionHandle);	
	}

EXPORT_C void CCallBase::RelinquishOwnershipCompleted(const TInt aError)
//
// Server called Tsy with RelinquishOwnership()
// TSY should do a Up call with this function
//
	{
	__ASSERT_ALWAYS(aError==KErrNone,Fault(EEtelFaultCanNotRelinquishOwnership));
	CheckAndDestroyDummySubSession();
	}

EXPORT_C void CCallBase::RecoverDataPortAndRelinquishOwnershipCompleted(const TInt aError)
//
// Server called Tsy with RecoverDataPortAndRelinquishOwnership()
// TSY should do a Up call with this function
//
	{
	__ASSERT_ALWAYS(aError==KErrNone,Fault(EEtelFaultCanNotRelinquishOwnership));
	iLoanDataPort=EFalse;
	RelinquishOwnershipCompleted(aError);
	}

EXPORT_C TInt CCallBase::Service(const RMessage2& aMessage,CReqEntry* aReqEntry)
//
// The ServiceL functionality for the call
//
	{
	__ASSERT_ALWAYS(aReqEntry!=NULL,Fault(EEtelFaultCallTsyServiceWithoutReqPackage));
	aReqEntry->iPlacedRequest=ETrue;
	TTsyReqHandle tsyReqHandle=aReqEntry->iTsyReqHandle;
	TDes8* des1 = BufferDes1(aReqEntry->iBuffer,CBuffer::ESlotWrite);
	TUint8* ptr1=Ptr1(des1);
	switch (aReqEntry->iFunction)
		{

	// Asynchronous + Cancel
	case EEtelCallCapsChangeNotification:
		__ASSERT_ALWAYS(des1!=NULL,Fault(EETelFaultRequestWithoutBuffer));
		return NotifyCapsChange(tsyReqHandle,REINTERPRET_CAST(RCall::TCaps*,ptr1));
	case EEtelCallCapsChangeNotificationCancel:
		return NotifyCapsChangeCancel(tsyReqHandle);
	case EEtelCallNotifyHookChange:
		__ASSERT_ALWAYS(des1!=NULL,Fault(EETelFaultRequestWithoutBuffer));
		return NotifyHookChange(tsyReqHandle,REINTERPRET_CAST(RCall::THookStatus*,ptr1));
	case EEtelCallNotifyHookChangeCancel:
		return NotifyHookChangeCancel(tsyReqHandle);

	case EEtelCallNotifyStatusChange:
		__ASSERT_ALWAYS(des1!=NULL,Fault(EETelFaultRequestWithoutBuffer));
		return NotifyStatusChange(tsyReqHandle,REINTERPRET_CAST(RCall::TStatus*,ptr1));
	case EEtelCallNotifyStatusChangeCancel:
		return  NotifyStatusChangeCancel(tsyReqHandle);
	case EEtelCallNotifyDurationChange:
		return NotifyDurationChange(tsyReqHandle,REINTERPRET_CAST(TTimeIntervalSeconds*,ptr1));
	case EEtelCallNotifyDurationChangeCancel:
		return NotifyDurationChangeCancel(tsyReqHandle);
	case EEtelCallDial:
		{
		__ASSERT_ALWAYS(des1!=NULL,Fault(EETelFaultRequestWithoutBuffer));
		TDes* des2 = NULL;
		des2 = BufferDes2u(aReqEntry->iBuffer,CBuffer::ESlotWrite);
		__ASSERT_ALWAYS(des2!=NULL,Fault(EEtelFaultDes2DoesNotExist));
		return Dial(tsyReqHandle,des1,des2);
		}
	case EEtelCallDialCancel:
		return DialCancel(tsyReqHandle);

	case EEtelCallConnect:
		return Connect(tsyReqHandle,des1);
	case EEtelCallConnectCancel:
		return ConnectCancel(tsyReqHandle);

	case EEtelCallAnswer:
		return AnswerIncomingCall(tsyReqHandle,des1);
	case EEtelCallAnswerCancel:
		return AnswerIncomingCallCancel(tsyReqHandle);

	case EEtelCallHangUp:
		return HangUp(tsyReqHandle);
	case EEtelCallHangUpCancel:
		return HangUpCancel(tsyReqHandle);

	case EEtelCallAcquireOwnership:
		{
		if (RCall::EOwnershipOwnedByThisClient==
			CheckOwnershipBySession(aReqEntry->iSession,aMessage.Int3()))
			return KErrEtelAlreadyCallOwner;
		else
			return AcquireOwnership(tsyReqHandle);
		}
	case EEtelCallAcquireOwnershipCancel:
		return AcquireOwnershipCancel(tsyReqHandle);

	// Synchronous
	case EEtelCallGetInfo:
		__ASSERT_ALWAYS(des1!=NULL,Fault(EETelFaultRequestWithoutBuffer));
		return GetInfo(tsyReqHandle,REINTERPRET_CAST(RCall::TCallInfo*,ptr1));
	case EEtelCallGetStatus:
		__ASSERT_ALWAYS(des1!=NULL,Fault(EETelFaultRequestWithoutBuffer));
		return GetStatus(tsyReqHandle,REINTERPRET_CAST(RCall::TStatus*,ptr1));
	case EEtelCallGetCaps:
		__ASSERT_ALWAYS(des1!=NULL,Fault(EETelFaultRequestWithoutBuffer));
		return GetCaps(tsyReqHandle,REINTERPRET_CAST(RCall::TCaps*,ptr1));
	case EEtelCallLoanDataPort:
		{
		__ASSERT_ALWAYS(des1!=NULL,Fault(EETelFaultRequestWithoutBuffer));
		TInt ret=LoanDataPort(tsyReqHandle,REINTERPRET_CAST(RCall::TCommPort*,ptr1));
		if (ret==KErrNone)
			iLoanDataPort=ETrue;
		return ret;
		}
	case EEtelCallLoanDataPortCancel:
		return LoanDataPortCancel(tsyReqHandle);

	case EEtelCallRecoverDataPort:
		{
		TInt ret=RecoverDataPort(tsyReqHandle);
		if (ret==KErrNone)
			iLoanDataPort=EFalse;
		return ret;
		}

	case EEtelCallTransferOwnership:
		return TransferOwnership(tsyReqHandle);
	case EEtelCallGetBearerServiceInfo:
		__ASSERT_ALWAYS(des1!=NULL,Fault(EETelFaultRequestWithoutBuffer));
		return GetBearerServiceInfo(tsyReqHandle,REINTERPRET_CAST(RCall::TBearerService*,ptr1));
		
	case EEtelCallGetOwnershipStatus:
		{
		__ASSERT_ALWAYS(des1!=NULL,Fault(EETelFaultRequestWithoutBuffer));

		RCall::TOwnershipStatus* owner=REINTERPRET_CAST(RCall::TOwnershipStatus*,ptr1);
		*owner=CheckOwnershipBySession(aReqEntry->iSession,aMessage.Int3());
		ReqCompleted(tsyReqHandle,KErrNone);
		return KErrNone;
		}

	case EEtelCallGetCallParams:
		{
		__ASSERT_ALWAYS(des1!=NULL,Fault(EETelFaultRequestWithoutBuffer));
		return GetCallParams(tsyReqHandle,des1);
		}
	case EEtelCallGetCallDuration:
		{
		__ASSERT_ALWAYS(des1!=NULL,Fault(EETelFaultRequestWithoutBuffer));
		return GetCallDuration(tsyReqHandle,REINTERPRET_CAST(TTimeIntervalSeconds*,ptr1));
		}
	case EEtelCallGetFaxSettings:
		{
		__ASSERT_ALWAYS(des1!=NULL,Fault(EETelFaultRequestWithoutBuffer));
		return GetFaxSettings(tsyReqHandle,REINTERPRET_CAST(RCall::TFaxSessionSettings*,ptr1));
		}

	case EEtelCallSetFaxSettings:
		{
		__ASSERT_ALWAYS(des1!=NULL,Fault(EETelFaultRequestWithoutBuffer));
		return SetFaxSettings(tsyReqHandle,REINTERPRET_CAST(RCall::TFaxSessionSettings*,ptr1));
		}
	case EEtelCallReferenceCount:
 		{
 		__ASSERT_ALWAYS(des1!=NULL,Fault(EETelFaultRequestWithoutBuffer));
 
 		TInt* count=REINTERPRET_CAST(TInt*,ptr1);
 		*count=AccessCount();
 		ReqCompleted(tsyReqHandle,KErrNone);
 		return KErrNone;
 		}
 	case EEtelAdoptFaxSharedHeaderFile:
		{
		CFaxSharedFileHandles* fshare = NULL;
		TRAPD(err, fshare = CFaxSharedFileHandles::NewL(aMessage));
		if(err != KErrNone)
			{
			return err;	
			}
		//Pass the CFaxSharedFileHandles object to the TSY.  If call is successful then TSY assumes owenership of the object else we delete it.	
		TInt ret = SetFaxSharedHeaderFile(tsyReqHandle, fshare);
		if(ret!= KErrNone)
			{
			delete fshare;	
			}			
		return ret;	
		}
	default:
		return ServiceExtFunc(aMessage,aReqEntry);
		}
	}

EXPORT_C RHandleBase* CCallBase::GlobalKernelObjectHandle()
	{
	if (iChunk.Handle() == KNullHandle) return NULL;
	return &iChunk;	
	}

	
EXPORT_C RFax::TProgress* CCallBase::CreateFaxProgressChunk()
//
//	Even if already created, return pointer to it
//
	{
	if (iChunk.Handle() == KNullHandle)
 		{
			TInt r = iChunk.CreateGlobal(KNullDesC,sizeof (RFax::TProgress), sizeof (RFax::TProgress),EOwnerProcess); 
			if (r == KErrNone)
				{
				RFax::TProgress* progress = new(iChunk.Base()) RFax::TProgress;
				progress->iLastUpdateTime = 0;
				progress->iAnswerback.Zero ();
				progress->iPhase = ENotYetStarted;
				progress->iSpeed = 9600;
				progress->iResolution = EFaxNormal;
				progress->iCompression = EModifiedHuffman;
				progress->iECM = 0;
 				progress->iPage = 0;
				progress->iLines = 0;
				return progress;
				}	
		if (r != KErrNone)
			return NULL;
		
		}
	return (RFax::TProgress*)iChunk.Base();	
 	}

/**
Base implementation for virtual API declared in MCallBaseTSY.  This base implementation always returns KErrNotSupported and 
it is therefore necessary for TSYs who support this API to override this in their CCallBase derived class.

This function passes through a pointer to a heap-based CFaxSharedFileHandles object.  This object is instantiated by Etel but 
a successful return of this function will pass responsibility for deletion of aFaxSharedFileHandles from Etel server to the TSY.  
If an error code is returned from this function (e.g. KErrNotSupported) Etel maintains ownership of the aFaxSharedFileHandles and 
is responsible for its deletion.

TSY overrides of this function must correctly handle the cleanup of the aFaxSharedFileHandles object if they take ownership of it 
which is implied when returning from this function with KErrNone.

@publishedPartner
@param aTsyReqHandle Handle to request.
@param aFaxSharedFileHandles CFaxSharedFileHandles instance to be stored in TSY.
@released Released v9.0 to allow a fax client to pass a file handle to the Fax server.
*/
EXPORT_C TInt CCallBase::SetFaxSharedHeaderFile(const TTsyReqHandle /*aTsyReqHandle*/, CFaxSharedFileHandles* /*aFaxSharedFileHandles*/)
	{
	return KErrNotSupported;	
	}

EXPORT_C void CCallBase::DeleteFaxProgressChunk()
	{
	iChunk.Close();
	}

EXPORT_C CTelObject::TReqMode CCallBase::ReqModeL(const TInt aIpc)
//
//	Basic Request Mode for Call
//
	{
	CTelObject::TReqMode mode=0;
	switch (aIpc)
		{
	case EEtelCallNotifyHookChange:
	case EEtelCallNotifyStatusChange:
	case EEtelCallNotifyDurationChange:
	case EEtelCallCapsChangeNotification:
		mode=KReqModeMultipleCompletionEnabled | KReqModeRePostImmediately;
		break;
	case EEtelCallGetInfo:
	case EEtelCallRecoverDataPort:
	case EEtelCallGetStatus:
	case EEtelCallGetCaps:
	case EEtelCallTransferOwnership:
	case EEtelCallAcquireOwnership:
	case EEtelCallGetFaxSettings:
	case EEtelCallGetCallParams:
	case EEtelCallGetCallDuration:
	case EEtelCallAnswer:	// no longer obeys flow control since it may be placed long before
							// a call arrives
		break;
	case EEtelCallGetOwnershipStatus:
	case EEtelCallGetBearerServiceInfo:	
	case EEtelCallSetFaxSettings:
	case EEtelAdoptFaxSharedHeaderFile:
	case EEtelCallReferenceCount:
		break;
	case EEtelCallLoanDataPort:
	case EEtelCallDial:
	case EEtelCallConnect:
	case EEtelCallHangUp:
	
		mode=KReqModeFlowControlObeyed;
		break;

	default:
		User::Leave(KErrNotSupported);
		break;
		}
	return mode;
	}

EXPORT_C TInt CCallBase::NumberOfSlotsL(const TInt aIpc)
	{
	switch (aIpc)
		{
	case EEtelCallNotifyHookChange:
	case EEtelCallNotifyDurationChange:
		break;
	case EEtelCallNotifyStatusChange:
		return KSlotNumbersCallStatusChange;
	case EEtelCallCapsChangeNotification:
		return KSlotNumbersCallCapsChange;
	default:
		User::Leave(KErrNotSupported);
		break;
		}
	return KSlotNumbersDefault;
	}

//
//
// CLineBase
//
//
EXPORT_C CLineBase::CLineBase()
//
//	C'Tor
//
	{
	__DECLARE_NAME(_S("CLineBase"));
	}

EXPORT_C CLineBase::~CLineBase()
//
//	D'Tor
//
	{
	LOGTEXT("~CLineBase");
	}

EXPORT_C TInt CLineBase::CancelService(const TInt aIpc,const TTsyReqHandle aTsyReqHandle)
//
// Cancel Service for Line
//
	{
	__ASSERT_ALWAYS((	aIpc==EEtelLineNotifyIncomingCall	||
						aIpc==EEtelLineNotifyHookChange		||
						aIpc==EEtelLineNotifyStatusChange	||
						aIpc==EEtelLineNotifyCallAdded		||
						aIpc==EETelLineCapsChangeNotification)
						,Fault(EEtelFaultInvalidIpcForCancel));
	switch (aIpc)
		{
	case EEtelLineNotifyIncomingCall:
		return NotifyIncomingCallCancel(aTsyReqHandle);
	case EEtelLineNotifyHookChange:
		return NotifyHookChangeCancel(aTsyReqHandle);
	case EEtelLineNotifyStatusChange:
		return NotifyStatusChangeCancel(aTsyReqHandle);
	case EEtelLineNotifyCallAdded:
		return NotifyCallAddedCancel(aTsyReqHandle);
	case EETelLineCapsChangeNotification:
		return NotifyCapsChangeCancel(aTsyReqHandle);
	default:
		return KErrGeneral; // should never reaches here
		}
	}

EXPORT_C TInt CLineBase::Service(const RMessage2& aMessage, CReqEntry* aReqEntry)
//
// The ServiceL Functionality for the Line
//
	{
	__ASSERT_ALWAYS(aReqEntry!=NULL,Fault(EEtelFaultCallTsyServiceWithoutReqPackage));
	aReqEntry->iPlacedRequest=ETrue;
	TTsyReqHandle tsyReqHandle=aReqEntry->iTsyReqHandle;
	TDes8* des1 = BufferDes1(aReqEntry->iBuffer,CBuffer::ESlotWrite);
	TUint8* ptr1=Ptr1(des1);

	switch (aReqEntry->iFunction)
		{
	case EETelLineCapsChangeNotification:
		__ASSERT_ALWAYS(des1!=NULL,Fault(EETelFaultRequestWithoutBuffer));
		return NotifyCapsChange(tsyReqHandle,REINTERPRET_CAST(RLine::TCaps*,ptr1));
	case EETelLineCapsChangeNotificationCancel:
		return NotifyCapsChangeCancel(tsyReqHandle);
	case EEtelLineNotifyIncomingCall:
		__ASSERT_ALWAYS(des1!=NULL,Fault(EETelFaultRequestWithoutBuffer));
		return NotifyIncomingCall(tsyReqHandle,REINTERPRET_CAST(TName*,ptr1));
	case EEtelLineNotifyIncomingCallCancel:
		return NotifyIncomingCallCancel(tsyReqHandle);

	case EEtelLineNotifyHookChange:
		__ASSERT_ALWAYS(des1!=NULL,Fault(EETelFaultRequestWithoutBuffer));
		return NotifyHookChange(tsyReqHandle,REINTERPRET_CAST(RCall::THookStatus*,ptr1));
	case EEtelLineNotifyHookChangeCancel:
		return NotifyHookChangeCancel(tsyReqHandle);
	case EEtelLineNotifyStatusChange:
		__ASSERT_ALWAYS(des1!=NULL,Fault(EETelFaultRequestWithoutBuffer));
		return NotifyStatusChange(tsyReqHandle,REINTERPRET_CAST(RCall::TStatus*,ptr1));
	case EEtelLineNotifyStatusChangeCancel:
		return NotifyStatusChangeCancel(tsyReqHandle);
	case EEtelLineNotifyCallAdded:
		__ASSERT_ALWAYS(des1!=NULL,Fault(EETelFaultRequestWithoutBuffer));
		return NotifyCallAdded(tsyReqHandle,REINTERPRET_CAST(TName*,ptr1));
	case EEtelLineNotifyCallAddedCancel:
		return NotifyCallAddedCancel(tsyReqHandle);

	case EEtelLineGetHookStatus:
		__ASSERT_ALWAYS(des1!=NULL,Fault(EETelFaultRequestWithoutBuffer));
		return GetHookStatus(tsyReqHandle,REINTERPRET_CAST(RCall::THookStatus*,ptr1));

	case EEtelLineGetInfo:
		__ASSERT_ALWAYS(des1!=NULL,Fault(EETelFaultRequestWithoutBuffer));
		return GetInfo(tsyReqHandle,REINTERPRET_CAST(RLine::TLineInfo*,ptr1));
	case EEtelLineGetCaps:
		__ASSERT_ALWAYS(des1!=NULL,Fault(EETelFaultRequestWithoutBuffer));
		return GetCaps(tsyReqHandle,REINTERPRET_CAST(RLine::TCaps*,ptr1));
	case EEtelLineGetStatus:
		__ASSERT_ALWAYS(des1!=NULL,Fault(EETelFaultRequestWithoutBuffer));
		return GetStatus(tsyReqHandle,REINTERPRET_CAST(RCall::TStatus*,ptr1));
	case EEtelLineEnumerateCall:
		__ASSERT_ALWAYS(des1!=NULL,Fault(EETelFaultRequestWithoutBuffer));
		return EnumerateCall(tsyReqHandle,REINTERPRET_CAST(TInt*,ptr1));
	case EEtelLineGetCallInfo:
		__ASSERT_ALWAYS(des1!=NULL,Fault(EETelFaultRequestWithoutBuffer));
		return GetCallInfo(tsyReqHandle,REINTERPRET_CAST(TCallInfoIndex*,ptr1));
	default:
		return ServiceExtFunc(aMessage,aReqEntry);
		}
	}

EXPORT_C void CLineBase::Init()
//
// not used for Line
//
	{}

EXPORT_C void CLineBase::OpenPostProcessing(CTelSession*,const TInt)
//
// Perform post-processing after object has been added to session's CObjectIx.
//
	{}

EXPORT_C void CLineBase::CloseSubSessionPreProcessing(CTelSession* aSession,const TInt aSubSessionHandle)
//
// 
//
	{
	LOGTEXT("CLineBase::CloseSubSessionPreProcessing() - about to create dummy session");
	CreateDummySession(aSession,aSubSessionHandle);
	FlushReqs(aSession,aSubSessionHandle);
	}

EXPORT_C CTelObject::TReqMode CLineBase::ReqModeL(const TInt aIpc)
//
// Mode Request Inquiry Functions
//
	{
	CTelObject::TReqMode mode=0;
	switch (aIpc)
		{
	case EEtelLineGetCaps:
	case EEtelLineGetStatus:
	case EEtelLineGetHookStatus:
	case EEtelLineEnumerateCall:
	case EEtelLineGetCallInfo:
	case EEtelLineGetInfo:
	case EEtelLineNotifyIncomingCall:	// 3/12/98 Removed Repost ability.
		mode=KReqModeMultipleCompletionEnabled;
		break;
	case EEtelLineNotifyHookChange:
	case EEtelLineNotifyStatusChange:
	case EEtelLineNotifyCallAdded:
	case EETelLineCapsChangeNotification:
		mode=KReqModeMultipleCompletionEnabled | KReqModeRePostImmediately;
		break;
	default:
		User::Leave(KErrNotSupported);
		break;
		}
	return mode;
	}

EXPORT_C TInt CLineBase::NumberOfSlotsL(const TInt aIpc)
	{
	switch (aIpc)
		{
	case EEtelLineNotifyHookChange:
	case EETelLineCapsChangeNotification:
		break;
	case EEtelLineNotifyStatusChange:
		return KSlotNumbersLineStatusChange;
	case EEtelLineNotifyCallAdded:
		return KSlotNumbersLineCallAddedChange;
	default:
		User::Leave(KErrNotSupported);
		break;
		}
	return KSlotNumbersDefault;
	}

//
//
// CPhoneBase
//
//
EXPORT_C CPhoneBase::CPhoneBase()
//
// CPhoneBase constructor
//
	{
	__DECLARE_NAME(_S("CPhoneBase"));
	iReqWaitList.SetOffset(_FOFF(CReqEntry,iLink));
	iReqActiveList.SetOffset(_FOFF(CReqEntry,iLink));
	iTsyReqHandleCnt=TSY_HANDLE_INIT_VALUE;
	}

EXPORT_C CPhoneBase::~CPhoneBase()
//
//	CPhone destructor.
//	Attual closing of the owner is done in CSubSessionBase destructor
//	This is for all object inherit fron CSubSesissionBase
//
	{
	LOGTEXT("~CPhoneBase");
	}

//
// Request List Manipulation Functions
//
CReqEntry* CPhoneBase::ActivateNextWaitingReq()
//
// Use:		during completion to Activate the next waiting request
// Action:	Find next Waiting Req, move to active, and return.
//
	{
	if(iReqWaitList.IsEmpty())
		return NULL;

	CReqEntry* reqEntry=iReqWaitList.First();
	if(reqEntry==NULL)
		return NULL;
	reqEntry->iLink.Deque();
	iReqActiveList.AddLast(*reqEntry);
	if (reqEntry->iReqMode & KReqModeMultipleCompletionEnabled | KReqModeMultipleCompletionWithInterestLevel)
		{
		CReqEntry* otherClientsEntry;
		TDblQueIter<CReqEntry> iter(iReqWaitList);
		while(otherClientsEntry=iter++, otherClientsEntry!=NULL)
			{
			if(otherClientsEntry->iTsyReqHandle==reqEntry->iTsyReqHandle)
				{
				otherClientsEntry->iLink.Deque();
				iReqActiveList.AddLast(*otherClientsEntry);
				}
			}
		}
	return reqEntry;
	}

CReqEntry* CPhoneBase::FindByTsyHandleAndPlacedRequest(const TTsyReqHandle aTsyReqHandle)
//
// Use: to search active req list for a single req, remove and then return it. Can be called repeatedly for Notification
//
	{
	CReqEntry* reqEntry;
	TDblQueIter<CReqEntry> iter(iReqActiveList);
	while(reqEntry=iter++, reqEntry!=NULL)
		{
		if(reqEntry->iTsyReqHandle==aTsyReqHandle && reqEntry->iPlacedRequest)
			return reqEntry;
		}
	return NULL;
	}

CReqEntry* CPhoneBase::FindByTsyHandle(const TTsyReqHandle aTsyReqHandle)
//
// Use: to search active req list for a single req, remove and then return it. Can be called repeatedly for Notification
//
	{
	CReqEntry* reqEntry;
	TDblQueIter<CReqEntry> iter(iReqActiveList);
	while(reqEntry=iter++, reqEntry!=NULL)
		{
		if(reqEntry->iTsyReqHandle==aTsyReqHandle)
			return reqEntry;
		}
	return NULL;
	}

EXPORT_C TInt CPhoneBase::FindSessionByTsyHandle(const TTsyReqHandle aTsyReqHandle)
	{
	CReqEntry* reqEntry = FindByTsyHandle(aTsyReqHandle);
	if (!reqEntry)
		{
		// TODO we'd need to ensure the subsession handle was positive
		// to be able to return an error. Shifting the pointer >>1
		// would ensure this.
		return KErrNotFound;
		}
	return reinterpret_cast<TInt>(reqEntry->iSession);
	}

EXPORT_C TInt CPhoneBase::FindSubSessionByTsyHandle(const TTsyReqHandle aTsyReqHandle)
	{
	CReqEntry* reqEntry = FindByTsyHandle(aTsyReqHandle);
	if (!reqEntry)
		{
		return KErrNotFound;
		}
	return reqEntry->iMessage.Int3();
	}

void CPhoneBase::UpdateBuffer(CReqEntry* aUpdatedReqEntry,CReqEntry* aReqEntry)
//
//	Is passed both the TSY-updated request entry (which may be multi-buffered or not)
//	and another request entry which is to have the data copied into it (which again may or may
//	not be multi-buffered) copies the latest data into the target request entry. 
//
	{
	__ASSERT_ALWAYS(aUpdatedReqEntry->iFunction==aReqEntry->iFunction,Fault(EEtelFaultUpdatingBufferOfDifferentIpc));
	TInt msgType = aReqEntry->iMessage.Int1();
	if (msgType==EIsaNarrowAndUnicodeDoubleDesTobeRead)
		{
		TDes8* targetDes1 = BufferDes1(aReqEntry->iBuffer,CBuffer::ESlotWrite);
		TDes16* targetDes2 = BufferDes2u(aReqEntry->iBuffer,CBuffer::ESlotWrite);
		TDes8* sourceDes1 = BufferDes1(aUpdatedReqEntry->iBuffer,CBuffer::ESlotWrite);
		TDes16* sourceDes2 = BufferDes2u(aUpdatedReqEntry->iBuffer,CBuffer::ESlotWrite);
		if (sourceDes1!=targetDes1)
			{
			targetDes1->Copy(*sourceDes1);
			if (targetDes2)
				targetDes2->Copy(*sourceDes2);
			aReqEntry->iBuffer->IncWrite();
			}
		return;
		}
	if (aUpdatedReqEntry->iSession->IsUnicodeReq(msgType))
		{
		TDes16* targetDes1 = BufferDes1u(aReqEntry->iBuffer,CBuffer::ESlotWrite);
		TDes16* targetDes2 = BufferDes2u(aReqEntry->iBuffer,CBuffer::ESlotWrite);
		TDes16* sourceDes1 = BufferDes1u(aUpdatedReqEntry->iBuffer,CBuffer::ESlotWrite);
		TDes16* sourceDes2 = BufferDes2u(aUpdatedReqEntry->iBuffer,CBuffer::ESlotWrite);
		if (sourceDes1!=targetDes1)
			{
			targetDes1->Copy(*sourceDes1);
			if (targetDes2)
				targetDes2->Copy(*sourceDes2);
			aReqEntry->iBuffer->IncWrite();
			}
		}
	else		// this is narrow
		{
		TDes8* targetDes1 = BufferDes1(aReqEntry->iBuffer,CBuffer::ESlotWrite);
		TDes8* targetDes2 = BufferDes2(aReqEntry->iBuffer,CBuffer::ESlotWrite);
		TDes8* sourceDes1 = BufferDes1(aUpdatedReqEntry->iBuffer,CBuffer::ESlotWrite);
		TDes8* sourceDes2 = BufferDes2(aUpdatedReqEntry->iBuffer,CBuffer::ESlotWrite);
		if (sourceDes1!=targetDes1)
			{
			targetDes1->Copy(*sourceDes1);
			if (targetDes2)
				targetDes2->Copy(*sourceDes2);
			aReqEntry->iBuffer->IncWrite();
			}
		}
	}

CReqEntry* CPhoneBase::FindClientInWaiting(CTelSession* aSession,const TInt aSubSessionHandle,const TInt aIpc)
//
// Used to search waiting list for a req that's to be cancelled.
// If non-Null is returned it will have been removed by this function and can be completed by the caller.
//
	{
	CReqEntry* reqEntry;
	TDblQueIter<CReqEntry> iter(iReqWaitList);
	while(reqEntry = iter++, reqEntry!=NULL)
		{
		if((reqEntry->iSession==aSession)&&(reqEntry->iMessage.Int3()==aSubSessionHandle)&&(reqEntry->iFunction==aIpc))
			return reqEntry;
		}
	return NULL;
	}

CReqEntry* CPhoneBase::FindClientInActive(CTelSession* aSession,const TInt aSubSessionHandle,const TInt aIpc)
//
// Used to search the active req list for a req that's to be cancelled.
// The function will return any req entry found, by the caller does not know to pass the cancel down until a search has been done just on the aIpc
//
	{
	CReqEntry* reqEntry;
	TDblQueIter<CReqEntry> iter(iReqActiveList);
	while(reqEntry = iter++, reqEntry!=NULL)
		{
		if((reqEntry->iSession==aSession)&&(reqEntry->iMessage.Int3()==aSubSessionHandle)&&(reqEntry->iFunction==aIpc))
			return reqEntry;
		}
	return NULL;
	}

CReqEntry* CPhoneBase::FindByIpcAndTelObject(const TInt aIpc, const CTelObject* aTelObject, const TUint aBufSize)
//
//	Finds an active request by both IPC and by the TelObject it was placed on
//
	{
	CReqEntry* reqEntry;
	TDblQueIter<CReqEntry> iter(iReqActiveList);
	while(reqEntry = iter++, reqEntry!=NULL)
		{
		if(reqEntry->iFunction==aIpc && reqEntry->iTelObject==aTelObject
			&& reqEntry->iBuffer->Size() == aBufSize)	
			return reqEntry;
		}
	return NULL;
	}

CReqEntry* CPhoneBase::FindByIpcAndTelObjectInWaiting(const TInt aIpc, const CTelObject* aTelObject, const TUint aBufSize)
//
//	Finds a waiting request by both IPC and by the TelObject it was placed on
//
	{
	CReqEntry* reqEntry;
	TDblQueIter<CReqEntry> iter(iReqWaitList);
	while(reqEntry = iter++, reqEntry!=NULL)
		{
		if(reqEntry->iFunction==aIpc && reqEntry->iTelObject==aTelObject
			&& reqEntry->iBuffer->Size() == aBufSize)	
			return reqEntry;
		}
	return NULL;
	}				 

void CPhoneBase::AddReqToActive(CReqEntry* aReqEntry)
//
//	Add reqEntry to the 'active list'
//
	{
	LOGTEXT2("CPhoneBase::AddReqToActive with TsyHandle of %d", aReqEntry->iTsyReqHandle);
	iReqActiveList.AddLast(*aReqEntry);
	}

void CPhoneBase::AddReqToWaiting(CReqEntry* aReqEntry)
//
//	Add entry to the 'wait list'
//
	{
	LOGTEXT2("CPhoneBase::AddReqToWaiting with TsyHandle of %d", aReqEntry->iTsyReqHandle);
	iReqWaitList.AddLast(*aReqEntry);
	}

TTsyReqHandle CPhoneBase::TsyReqHandle()
//
// Return the request handle  if wrap round avoid assigned to TSY_HANDLE_INIT_VALUE
//
	{
	iTsyReqHandleCnt++ ;
	if (iTsyReqHandleCnt==TSY_HANDLE_INIT_VALUE)
		iTsyReqHandleCnt=(TSY_HANDLE_INIT_VALUE+1);
	return iTsyReqHandleCnt;
	}

CReqEntry* CPhoneBase::NewReqL(const RMessage2& aMessage, CTelSession* aSession,CBuffer* aBuffer,
							   const CTelObject* aTelObject,TInt aFunction)
//
// Create new req entry
//
	{
	return CReqEntry::NewL(TsyReqHandle(),aMessage,aSession,aBuffer,aTelObject,aFunction,aSession->EmergencyClientHeap(aMessage.Int1()));
	}

CReqEntry* CPhoneBase::FindSameClientEntry(CTelSession* aSession,const TInt aSubSessionHandle,const TInt aIpc)
//
// Seaching for duplicate request and panic client is return not NULL
//
	{
	CReqEntry* reqEntry=FindClientInWaiting(aSession,aSubSessionHandle,aIpc);

	if (reqEntry!=NULL)
		return reqEntry;
	else // not found in waiting
		reqEntry=FindClientInActive(aSession,aSubSessionHandle,aIpc);
	return reqEntry;

	}

EXPORT_C void CPhoneBase::OpenPostProcessing(CTelSession*,const TInt)
//
// Perform post-processing after object has been added to session's CObjectIx.
//
	{}

EXPORT_C void CPhoneBase::CloseSubSessionPreProcessing(CTelSession* aSession,const TInt aSubSessionHandle)
//
//  Find if any active request if so create a dummy session
//
	{
	LOGTEXT("CPhoneBase::CloseSubSessionPreProcessing");
	CreateDummySession(aSession,aSubSessionHandle);
	FlushReqs(aSession,aSubSessionHandle);
	}

EXPORT_C TInt CPhoneBase::Service(const RMessage2& aMessage, CReqEntry* aReqEntry)
//
// The ServiceL functionality for the phone
//
	{
	__ASSERT_ALWAYS(aReqEntry!=NULL,Fault(EEtelFaultCallTsyServiceWithoutReqPackage));
	aReqEntry->iPlacedRequest=ETrue;
	TTsyReqHandle tsyReqHandle=aReqEntry->iTsyReqHandle;
	TDes8* des1 = BufferDes1(aReqEntry->iBuffer,CBuffer::ESlotWrite);
	TUint8* ptr1=Ptr1(des1);
	switch (aReqEntry->iFunction)
		{
	case EETelPhoneCapsChangeNotification:
		__ASSERT_ALWAYS(des1!=NULL,Fault(EETelFaultRequestWithoutBuffer));
		return NotifyCapsChange(tsyReqHandle,REINTERPRET_CAST(RPhone::TCaps*,ptr1));
	case EETelPhoneCapsChangeNotificationCancel:
		return NotifyCapsChangeCancel(tsyReqHandle);
	case EEtelPhoneInitialise:
		return ControlledInitialisation(tsyReqHandle);
	case EEtelPhoneInitialiseCancel:
		return ControlledInitialisationCancel(tsyReqHandle);
	case EEtelPhoneNotifyModemDetected:
		__ASSERT_ALWAYS(des1!=NULL,Fault(EETelFaultRequestWithoutBuffer));
		return NotifyModemDetected(tsyReqHandle,REINTERPRET_CAST(RPhone::TModemDetection*,ptr1) );
	case EEtelPhoneNotifyModemDetectedCancel:
		return NotifyModemDetectedCancel(tsyReqHandle);
	case EEtelPhoneGetInfo:
		__ASSERT_ALWAYS(des1!=NULL,Fault(EETelFaultRequestWithoutBuffer));
		return GetInfo(tsyReqHandle,REINTERPRET_CAST(RPhone::TPhoneInfo*,ptr1));
	case EEtelPhoneGetCaps:
		__ASSERT_ALWAYS(des1!=NULL,Fault(EETelFaultRequestWithoutBuffer));
		return GetCaps(tsyReqHandle,REINTERPRET_CAST(RPhone::TCaps*,ptr1));
	case EEtelPhoneGetStatus:
		__ASSERT_ALWAYS(des1!=NULL,Fault(EETelFaultRequestWithoutBuffer));
		return GetStatus(tsyReqHandle,REINTERPRET_CAST(RPhone::TStatus*,ptr1));
	case EEtelPhoneEnumerateLines:
		__ASSERT_ALWAYS(des1!=NULL,Fault(EETelFaultRequestWithoutBuffer));
		return EnumerateLines(tsyReqHandle,REINTERPRET_CAST(TInt*,ptr1));
	case EEtelPhoneGetLineInfo:
		__ASSERT_ALWAYS(des1!=NULL,Fault(EETelFaultRequestWithoutBuffer));
		return GetLineInfo(tsyReqHandle,REINTERPRET_CAST(TLineInfoIndex*,ptr1));
	default:
		return ServiceExtFunc(aMessage,aReqEntry);
		}
	}

EXPORT_C TInt CPhoneBase::CancelService(const TInt aIpc,const TTsyReqHandle aTsyReqHandle)
//
// Cancel Outstanding service for the phone
//
	{
	switch (aIpc)
		{
	case EEtelPhoneNotifyModemDetected:
		return NotifyModemDetectedCancel(aTsyReqHandle);
	case EETelPhoneCapsChangeNotification:
		return NotifyCapsChangeCancel(aTsyReqHandle);
	case EEtelPhoneInitialise:
		return ControlledInitialisationCancel(aTsyReqHandle);
	default:
		return KErrGeneral;
		}
	}

CReqEntry* CPhoneBase::FindClientReqInWaitList(CTelSession* aSession,const TInt aSubSessionHandle)
//
// Find first entry already logged by this client - in wait list
//
	{
	CReqEntry* reqEntry;
	TDblQueIter<CReqEntry> iter(iReqWaitList);
	while(reqEntry = iter++, reqEntry!=NULL)
		{
		if(aSession==reqEntry->iSession && aSubSessionHandle==reqEntry->iMessage.Int3())
			return reqEntry;
		}
	return NULL;
	}

CReqEntry* CPhoneBase::FindThisReqByAnotherClient(CTelSession* aSession,const TInt aSubSessionHandle,const TInt aIpc,const TUint aBufSize,const CTelObject* aThisTelObject)
//
// Find this IPC in active list, placed by a different client
//
	{
	CReqEntry* reqEntry;
	TDblQueIter<CReqEntry> iter(iReqActiveList);
	while(reqEntry=iter++,reqEntry!=NULL)
		{
		if((aSession!=reqEntry->iSession || aSubSessionHandle!=reqEntry->iMessage.Int3()) 
			&& reqEntry->iFunction==aIpc && reqEntry->iTelObject==aThisTelObject
			&& reqEntry->iBuffer->Size()==aBufSize)
			{
			return reqEntry;
			}
		}
	return NULL;
	}

CReqEntry* CPhoneBase::FindNonCancelledClientReq(CTelSession* aSession,const TInt aSubSessionHandle,const TInt aIpc)
//
// Find this IPC in active list, placed by this client but not the one which placed request on TSY
//
	{
	CReqEntry* reqEntry;
	TDblQueIter<CReqEntry> iter(iReqActiveList);
	while(reqEntry=iter++,reqEntry!=NULL)
		{
		if(aSession==reqEntry->iSession && aSubSessionHandle==reqEntry->iMessage.Int3() && reqEntry->iFunction==aIpc && reqEntry->iCancelFnCalled==EFalse)
			{
			return reqEntry;
			}
		}
	return NULL;
	}
  
void CPhoneBase::CheckAndCompleteAllActive(CReqEntry* aUpdatedReqEntry,const TReqMode aReqMode,const TInt aIpc,const TInt aError)
//
//	Cycles through active list, checking for any requests with the same IPC value and if 
//	registered, copying the data from the buffer that the TSY filled in to this buffer. All those
//	with the same IPC are completed up to the client (if the client is ready).
//
	{
	CReqEntry* reqEntry;
	TDblQueIter<CReqEntry> iter(iReqActiveList);
	while(reqEntry = iter++, reqEntry!=NULL)
		{
		if(reqEntry->iFunction==aIpc && reqEntry!=aUpdatedReqEntry 
			&& reqEntry->iTelObject==aUpdatedReqEntry->iTelObject 
			&& reqEntry->iBuffer->Size()==aUpdatedReqEntry->iBuffer->Size())	
			{						   
			UpdateBuffer(aUpdatedReqEntry,reqEntry);
			TInt error = ResolveError(reqEntry->iSession,aError);		// set error as either low or high byte
			if (aReqMode & KReqModeRePostImmediately)
				UpdateAndCompleteIfNecessary(reqEntry,error);
			else
				WriteBackAndCompleteReq(reqEntry,error);
			}
		}
	}


// The delivery object performs the task of delivering the request to the
// client. It loops through the clients in priority order and gives each
// client a fixed amount of time to either accept or reject. At the end
// the request will either be allocated or discarded (if no one wants it).

CMmDeliveryObject* CMmDeliveryObject::NewL( CTelObject& aTelObject )
	{
	CMmDeliveryObject* self=new(ELeave) CMmDeliveryObject( aTelObject );
	CleanupStack::PushL( self );
	self->ConstructL();
	CleanupStack::Pop( self );
	return self;
	}

CMmDeliveryObject::CMmDeliveryObject( CTelObject& aTelObject ) :
	CActive(EPriorityStandard), iTelObject(aTelObject)
	{
	CActiveScheduler::Add( this );
	}

void CMmDeliveryObject::ConstructL()
	{
	User::LeaveIfError( iDeliveryTimer.CreateLocal() );
	}

CMmDeliveryObject::~CMmDeliveryObject()
	{
	Cancel();
	iDeliveryTimer.Close();
	iPendingReqs.Close();
	}

void CMmDeliveryObject::DoCancel()
	{
	iDeliveryTimer.Cancel();
	}

// Called either when we need to attempt delivery of a request.
// Can be due to:
// 1) the start of the delivery process.
// 2) a client rejecting the request which makes us try the next one.
// 3) a client timing out which makes us try the next one.
void CMmDeliveryObject::RunL()
	{

    if ( iStatus == KErrNone && iUpdatedReqEntry)
		{
		
		TInterestCategory interestCategory;
		iCurrentReq = NULL;
		iCurrentReq = HighestPriorityReq(interestCategory);
		
		if ( iCurrentReq )
			{
			if (iUpdatedReqEntry!=iCurrentReq)
				{
				iTelObject.PhoneOwner()->UpdateBuffer(iUpdatedReqEntry,iCurrentReq);
				}

			// Offer incoming message to interested clients one at a time.
			// Initially the priority client followed by Standard category clients.
			OfferToClient( iCurrentReq );
			
			// Client will have a certain time in which to accept/reject the message.
			// After which it will be offered to the next client.
			switch ( interestCategory ) 
			{
			case EInterestCategoryPriority:
				iDeliveryTimer.After( iStatus, (KUssdOfferDialogueTimeout * KUssdPriorityTimeoutPercentage)/100 );
				SetActive();
				break;
			case EInterestCategoryStandard:
				iDeliveryTimer.After( iStatus, iTimeSlice );
				SetActive();
				break;
			case EInterestCategoryDefault:
				// wait on default client (no timeout)
				break;
			default:
                // No more clients
                Clear();
            } // end switch
			}
		else
			{
			iTelObject.RepostRequest( iUpdatedReqEntry, KErrNone );				

			// No more clients left to check
			Clear();
			}
		}
	else
		{
		// Delivery has failed.
		Clear();
		}
	}

TInt CMmDeliveryObject::DeliverReqL( TDblQue<CReqEntry>& aArray, CReqEntry* aUpdatedReqEntry,
                                                const CTelObject::TReqMode aReqMode, const TInt aIpc, TInt aError)
	{
	iPendingReqs.Reset();
	
	ASSERT(aUpdatedReqEntry != NULL);
	iUpdatedReqEntry = aUpdatedReqEntry;
	iReqMode = aReqMode;
	iError = aError;
	
	CReqEntry* reqEntry = NULL;
	TDblQueIter<CReqEntry> iter(aArray);
	while(reqEntry = iter++, reqEntry!=NULL)
		{
		if(reqEntry->iFunction==aIpc 
			&& reqEntry->iTelObject==iUpdatedReqEntry->iTelObject 
			&& reqEntry->iBuffer->Size()==iUpdatedReqEntry->iBuffer->Size())	
			{						
			switch (reqEntry->iInterestCategory) 
			{
            case EInterestCategoryPriority:
                {
                // If we don't already have a priority client registered,
                // then add this one as the priority client.
                if (iPriorityReq == NULL)
                    {
                    iPriorityReq = reqEntry;
                    }
                // Otherwise if there is already a registered priority client,
                // add this to the list of standard clients to be treated as such.
                else
                    {
                    iPendingReqs.AppendL( reqEntry );
                    }
                break;
                }
            case EInterestCategoryDefault:
                {
                // If we don't already have a default client registered,
                // then add this one as the default client.
                if (iDefaultReq == NULL)
                    {
                    iDefaultReq = reqEntry;
                    }
                // Otherwise if there is already a registered default client,
                // add this to the list of standard clients to be treated as such.
                else
                    {
                    iPendingReqs.AppendL( reqEntry );
                    }
                break;
                }
			default:
				{
				iPendingReqs.AppendL( reqEntry );
				}
			} //end switch
			} //end if 
		}

	// Calculate amount of time each client will get to accept the request
	TInt count = iPendingReqs.Count();
	if (count)
	    {
		if (iPriorityReq)
			{
			iTimeSlice = (KUssdOfferDialogueTimeout *  (100 - KUssdPriorityTimeoutPercentage)/(100 * count) );
			}
		else
			{
			iTimeSlice = KUssdOfferDialogueTimeout / count ;
			}	
	    }

	// now start looping through clients...
	DoStart();
	return KErrNone;
	}

TBool CMmDeliveryObject::DeliveryInProgress() const
	{
	// In the case of delivery to the default client we won't be active when
	// delivery is carried out as no timeout is used for the default client
	return (IsActive() || iCurrentReq);
	}
CTelObject::TReqMode CMmDeliveryObject::GetReqMode()const 
	{
	return iReqMode;
	}
TInt CMmDeliveryObject::ClientsDecision( const RMessage2& aMessage, CTelSession* aSession, TBool aAccepted )
	{
	if (!DeliveryInProgress())
        {
        return KErrTimedOut;
        }

	const TInt currentreqsession = reinterpret_cast<TInt>(iCurrentReq->iSession);
	const TInt currentreqsubsession = iCurrentReq->iMessage.Int3(); 
	
	const TInt clientsession = reinterpret_cast<TInt>(aSession);
	const TInt clientsubsession = aMessage.Int3();
	
	if (currentreqsession != clientsession ||
		currentreqsubsession != clientsubsession)
		{
		// do nothing - client took too long to reply and we've moved 
		// onto another client now
		return KErrTimedOut;
		}
	
	if (aAccepted)
		{
		Cancel();
		iTelObject.SetSessionOwner(clientsession, clientsubsession);
		ASSERT(iUpdatedReqEntry != NULL);
		// We repost the request so we don't miss the next message in the dialog
		// in case the client is slow in calling ReceiveMessage again.
		iTelObject.RepostRequest(iUpdatedReqEntry, KErrNone);
		Clear();
		}
	else
		{
		Cancel();
		DoStart();
		}
	return KErrNone;
	}

void CMmDeliveryObject::OfferToClient( CReqEntry* aReq )
	{
	iTelObject.OfferToClient(aReq, iUpdatedReqEntry, iReqMode, iError);
	}


// CMmDeliveryObject is being notified that a client has called cancel. 
// The relevant CReqEntry is being passed as a parameter.
// Need to remove it from our list of interested  (in Receiving messages) clients.	

void CMmDeliveryObject::DeletedReqEntry( CReqEntry* aReqEntry )
	{	
	if (iCurrentReq == aReqEntry)
	    {
	    iCurrentReq = NULL;
	    }

	if (iPriorityReq == aReqEntry)
		{
		iPriorityReq = NULL;
		}	
		
	if (iDefaultReq == aReqEntry)
		{
		iDefaultReq = NULL;
 		}
		
	if ( iPendingReqs.Count() )
		{
		for (TInt i = iPendingReqs.Count()-1; i>=0; --i)
			{
			if (aReqEntry == iPendingReqs[i])
				{
				iPendingReqs.Remove(i);
				break;
				}
			}
		}
		
    if (aReqEntry == iUpdatedReqEntry)
        {
        // iUpdatedReqEntry is the client which cancelled. 
        // Copy data from the 'about to be destroyed' entry to 
        // one owned by the next client.

        CReqEntry*  req = NewUpdatedReqEntry();
        if (req == NULL)
            // No more clients left.
            {
            Cancel();
            iPendingReqs.Close();
            }
        else
            {
            ASSERT(aReqEntry != req);
            iTelObject.PhoneOwner()->UpdateBuffer(iUpdatedReqEntry,req);
            iUpdatedReqEntry = req;	
            }
        }
    }

CReqEntry* CMmDeliveryObject::NewUpdatedReqEntry()
	{
	if (iCurrentReq)
		return iCurrentReq;
		
	CReqEntry* req = NULL;
	if (iPriorityReq)
		{
		req = iPriorityReq;
		}
	else if ( iPendingReqs.Count() )
		{
		req = iPendingReqs[0];
		}
	else if (iDefaultReq)
		{
		req = iDefaultReq;
		}
	return req;	
	}

	
CReqEntry* CMmDeliveryObject::HighestPriorityReq( TInterestCategory& aInterestCategory )
	{
	aInterestCategory = EInterestCategoryStandard;
	CReqEntry* req = NULL;
	if (iPriorityReq)
		{
		req = iPriorityReq;
		iPriorityReq = NULL;
		aInterestCategory = EInterestCategoryPriority;
		}
	else if ( iPendingReqs.Count() )
		{
		req = iPendingReqs[0];
		iPendingReqs.Remove(0);
        aInterestCategory = EInterestCategoryStandard;
		}
	else if (iDefaultReq)
		{
		req = iDefaultReq;
		iDefaultReq = NULL;
        aInterestCategory = EInterestCategoryDefault;
		}
	return req;
	}

void CMmDeliveryObject::DoStart()
	{
	SetActive();
	TRequestStatus* status = &iStatus;
	User::RequestComplete( status, KErrNone );
	}

void CMmDeliveryObject::Clear()
	{
	iPendingReqs.Reset();	
	iCurrentReq = NULL;
	iPriorityReq = NULL;
	iDefaultReq = NULL;
	
	iUpdatedReqEntry = NULL;
	iReqMode = 0;
	iError = KErrNone;
	}

//
// CPhoneBase performs Flow Control
//
TInt CPhoneBase::FlowControl() const
//
// Return the value of iFlowControlCnt
//
 	{
	return iFlowControlCnt;
	}

void CPhoneBase::FlowControlInc()
//
// Increase iFlowControlCnt
//
	{
	LOGTEXT3("Incrementing Flow Control from %d to %d", iFlowControlCnt, iFlowControlCnt+1);
	iFlowControlCnt++;
	}

void CPhoneBase::FlowControlDec()
//
// Decrease iFlowControlCnt
//
	{
	__ASSERT_ALWAYS((iFlowControlCnt>0),Fault(EEtelFaultNegativeFlowcontrolCount));
	LOGTEXT3("Decrementing Flow Control from %d to %d", iFlowControlCnt, iFlowControlCnt-1);
	iFlowControlCnt--;
	}

EXPORT_C TBool CPhoneBase::CheckEmergencyClient(const TTsyReqHandle aTsyReqHandle) const
	{
	CReqEntry* reqEntry=PhoneOwner()->FindByTsyHandle(aTsyReqHandle);
	__ASSERT_ALWAYS(reqEntry!=NULL,Fault(EEtelFaultNotRecognisedTsyHandle));

	return reqEntry->iSession->TelServer()->IsEmergencyClient(reqEntry->iSession);
	}


EXPORT_C CTelObject::TReqMode CPhoneBase::ReqModeL(const TInt aIpc)
//
// Mode Request Inquiry Functions
//
	{
	CTelObject::TReqMode mode=0;
	switch (aIpc)
		{
	case EEtelPhoneInitialise:	//test
		mode=KReqModeFlowControlObeyed | KReqModeMultipleCompletionEnabled;
		break;
	case EEtelPhoneGetCaps:
	case EEtelPhoneGetStatus:
	case EEtelPhoneEnumerateLines:
	case EEtelPhoneGetLineInfo:
	case EEtelPhoneGetInfo:
		mode=KReqModeMultipleCompletionEnabled;
		break;	
	case EETelPhoneSetEmergencyClient:
		mode=KReqModeFlowControlObeyed;
		break;
	case EEtelPhoneNotifyModemDetected:
	case EETelPhoneCapsChangeNotification:
		mode=KReqModeMultipleCompletionEnabled | KReqModeRePostImmediately;
		break;
	default:
		User::Leave(KErrNotSupported);
		break;
		}
	return mode;
	}

EXPORT_C TInt CPhoneBase::NumberOfSlotsL(const TInt aIpc)
	{
	switch (aIpc)
		{
	case EEtelPhoneNotifyModemDetected:
	case EETelPhoneCapsChangeNotification:
		break;
	default:
		User::Leave(KErrNotSupported);
		break;
		}
	return KSlotNumbersDefault;
	}

TDblQue<CReqEntry>& CPhoneBase::ReqActiveList()
	{
	return iReqActiveList;
	}