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

#ifndef __COMMSCHAN_H__
#define __COMMSCHAN_H__

#include "elements/cftransportmacro.h"

#include <e32test.h>
#include <e32msgqueue.h>
#ifdef SYMBIAN_NETWORKING_CFTRANSPORT
#include <elements/metadata.h>
#include <elements/metatype.h>
#endif

/**
@file
Definition of the Comms Channels. The Channels are an abstraction of the SymbianOS kernel queues.

@publishedPartner
@released
*/

namespace CommsFW
//CommsFW namespace doxygen is supplied in cfmsgs.h. The namespace is @publishedPartner, @released
//Namespace for the Comms Framework to which the Comms Channels belong.
{

/**
   Comms Channel PANIC category
*/
_LIT(KCFChannelPanic, "CommsChan");

/** Panics generated by the Comms Channels
@publishedPartner
@released
*/
enum TCFChannelPanics
	{
	/** Trying to use non-existent queue on a channel.
	*/
	ECFChanBadQueue = 0,
	/** Trying to start an already pending request on a channel, e.g. listen for data twice.
	*/
	ECFChanNestedRequest = 1,
	/** Creating queues on a channel that already has queues.
	*/
	ECFChanMultiCreate = 2,
	/** Trying to initialise a message with a bigger data section than the message class permits.
	*/
	ECFChanMsgTooBig = 3,
	/** Trying to destroy a channel that has not been closed.
	*/
	ECFChanOrphanedDrain = 4,
	/** Trying to destroy a channel that has not been closed.
	*/
	ECFChanNotClosed = 5,
	/** Trying to gracefully close on a channel pair that only implements non-graceful closing.
	*/
	ECFChanNoGracefulClose = 6
	};

/** The timeout, if the peer doesn't respond,  when when doing a graceful close of a channel.
*/
const TInt KDefaultGracefulCloseTimeout = 2000000;

/** Trying send(), close(), etc. on a fully closed channel.
*/
const TInt KErrChannelFullyClosed = -17201;

/** Trying send(), close(), etc. on a channel that has already been closed from one end.
*/
const TInt KErrChannelHalfClosed = -17202;

/** Trying receive on a channel that were unable to initialize the needed
drain used for reading data out of multiple queues in the right priority. Possibly this is due
to an OOM condition.
*/
const TInt KErrChannelNoDrain = -17203;

template<TInt aMaxParamBlockSize>
class TCFMessageBase
/** Base class for all Comms Framework messages. Contains the message code and the
fixed size parameter buffer.  This class is meant to be derived to set and get
parameters in the buffer. The parameter layout is defined in the derived
classes. Note: Derived message classes must not add data members or virtual funcions
: the channel mechanism deliberately uses fixed size non-virtual classes efficiency gains.
@publishedPartner
@released
*/
	{
public:
	typedef TInt TCode;

    /** Special reserved message type, used by the framework as last message when closing a channel.
    once a PIG has been sent through the channel no more data messages are allowed, this is part
    of the graceful close process and prevents data loss. When using the data block directly via RawBlock()
	the first 32 bit word must NOT be -1.
    */
	enum { EPIG = -1 };

	inline TUint8* ParamBlock();
#ifdef SYMBIAN_NETWORKING_CFTRANSPORT
	inline TUint8* RawBlock();
#endif
	inline const TUint8* ParamBlock() const;
#ifdef SYMBIAN_NETWORKING_CFTRANSPORT
	static TInt MaxParamBlockSize()
		{
		return aMaxParamBlockSize;
		}
	inline const TUint8* RawBlock() const;
#endif
	inline TCode Code() const;
	inline void Code(TCode);
	inline void ParamBlock(TInt aParamBlockSize, TAny* aParamBlock);
#ifdef SYMBIAN_NETWORKING_CFTRANSPORT
	inline void RawBlock(TInt aRawBlockSize, const TAny* aRawBlock);
#endif
	inline void SetPIGCode();
	inline TBool IsPIGCode() const;

protected:
	inline TCFMessageBase(TCode aCode, TInt aParamBlockSize, TAny* aParamBlock);
#ifdef SYMBIAN_NETWORKING_CFTRANSPORT
	inline TCFMessageBase(TInt aRawBlockSize, const TAny* aRawBlock);
#endif
	inline TCFMessageBase();

private:
#ifdef SYMBIAN_NETWORKING_CFTRANSPORT
	union TCFMessageData
		{
		struct TMessageDataV1
			{
#endif
			TCode iCode;
#ifdef SYMBIAN_NETWORKING_CFTRANSPORT
			TCode iSubCode;
#endif
			TUint8 iParamBlock[aMaxParamBlockSize];
#ifdef SYMBIAN_NETWORKING_CFTRANSPORT
			} iV1;
		TUint8 iRawBlock[sizeof(TMessageDataV1)];
		} iMsg;
public:
	enum { KMaxRawBlockSize = sizeof(TCFMessageData) };
#endif
	};


/** Maximum size of messages used for the specific Comms Framework messages.
*/
class TMessageCarrier
	{
	union
		{
		TInt iCode;
		TAny* iExtra;
#ifdef SYMBIAN_NETWORKING_CFTRANSPORT
		TUint8 iMessageType[sizeof(Meta::STypeId )];
#endif
		};
	RMessage2 iMsg;
#ifdef SYMBIAN_NETWORKING_CFTRANSPORT
	TInt iSpare1;
	TInt iSpare2;
	TInt iSpare3;
#endif
	};

const TInt KCFMessageParamBlockSize = sizeof(TMessageCarrier);

class TCFMessage : public TCFMessageBase<KCFMessageParamBlockSize>
/** General Comms Framework message; primarily used between bind manager and (sub)
modules.
@publishedPartner
@released
*/
	{
	public:
		inline TCFMessage(TCode aCode, TInt aParamBlockSize, TAny* aParamBlock);
		inline TCFMessage();
	};

#ifdef SYMBIAN_NETWORKING_CFTRANSPORT
__ASSERT_COMPILE(sizeof(TCFMessage) == TCFMessage::KMaxRawBlockSize);	// Class must be entirely member data; no vtbl, no padding
__ASSERT_COMPILE(TCFMessage::KMaxRawBlockSize % 4 == 0);				// Size must be word-multiple for efficient pointer handling
#endif


/*
naming note: RCFChannelBase should be RCFChannelTpl and RCFChannelRawBase should be
RCFChannelBase but we're stuck with the names now

class RCFChannelRawBase
- has pointer to CChannelDrain (NULL if single-priority)
- gets passed the array of RMsgQueueBase and number of priorities (size of array) on all operations

class RCFChannelBase<TMsg, TPriorities>
- thin template, inheriting protected from RCFChannelBase
- has array of RMsgQueueBase[TPriorities]

class CChannelDrain
- last member is array of CQueueDrains[1]. Factory function allocs enough for the real array
- keeps a pointer to RMsgQueueBase[] of the template
*/

class CChannelDrain;
class RCFChannelPairRawBase;

class RCFChannelRawBase
	{
public:
	IMPORT_C RCFChannelRawBase();
	IMPORT_C ~RCFChannelRawBase();

	IMPORT_C TInt Create(TInt aSlots, TInt aSize, RMsgQueueBase* aQueues, TInt aPriorities);
	IMPORT_C TInt Create(const RMsgQueueBase* aForeignQueues, TOwnerType aOwner, RMsgQueueBase* aQueues, TInt aPriorities);
	IMPORT_C void GetMsgQueues(RMsgQueueBase* aForeignQueues, const RMsgQueueBase* aQueues, TInt aPriorities) const;
	IMPORT_C TBool IsOpen(const RMsgQueueBase* aQueues) const;
	IMPORT_C void Close(RMsgQueueBase* aQueues, TInt aPriorities);

	IMPORT_C TInt Send(const TAny* aMsg, TInt aPriority, TInt aSize, RMsgQueueBase* aQueues);
	IMPORT_C void NotifySpaceAvailable(TRequestStatus& aStatus, TInt aPriority, RMsgQueueBase* aQueues);
	IMPORT_C void CancelSpaceAvailable(TInt aPriority,  RMsgQueueBase* aQueues);

	IMPORT_C TInt Receive(TAny* aMsg, TInt aSize, RMsgQueueBase* aQueues, TInt aPriorities);
	IMPORT_C void NotifyDataAvailable(TRequestStatus& aStatus, RMsgQueueBase* aQueues);
	IMPORT_C void CancelDataAvailable(RMsgQueueBase* aQueues);
private:
	CChannelDrain* iDrain;

	friend class RCFChannelPairRawBase;
	};

template <class T, TInt TPriorities>
class RCFChannelPairBase;

template <class T, TInt TPriorities>
class RCFChannelBase : protected RCFChannelRawBase
	{
/** Base class for all Comms Framework channels.  A Comms Channel is a
unidirectional message passing pipe for asynchronous communication between
Comms modules. Internally it contains a number of message queues (one for each
priority); the general RCFChannel has one but this may be overridden by the
derived private Comms Channels. More than one queue will be necessary for
modules that need to prioritise between messages. Comms Channels are "peer-to-
peer", i.e every channel has only one producer and one consumer. Also, every
two endpoints that need to communicate over a module or submodule boundary need
to have a dedicated comms channel in each direction.
@see RCFChannelPairBase
@publishedPartner
@released
*/
	friend class RCFChannelPairBase<T, TPriorities>;
public:

    /** Defines message queues data structure for the array of internal message queues in a channel.
    Used when getting the array of message queues in and out of the channel instance.
    */
	typedef struct TMsgQueuesTAG { RMsgQueue<T> i[TPriorities]; } TMsgQueues;

	inline ~RCFChannelBase()
		{
#ifdef _DEBUG
		for (TInt q = 0; q < TPriorities; ++q)
			{
			__ASSERT_DEBUG(iQueues[q].Handle() == 0, User::Panic(KCFChannelPanic, ECFChanNotClosed));
			}
#endif
		}

	inline TInt Create(TInt aSlots)
		{
		return RCFChannelRawBase::Create(aSlots, sizeof(T), iQueues, TPriorities);
		}
	inline TInt Create(const TMsgQueues& aMsgQueues, TOwnerType aOwner = EOwnerThread)
		{
		return RCFChannelRawBase::Create(reinterpret_cast<const RMsgQueueBase*>(&aMsgQueues), aOwner, iQueues, TPriorities);
		}
	inline void Close()
		{
		RCFChannelRawBase::Close(iQueues, TPriorities);
		}

	inline TInt Send(const T& aMsg, TInt aPriority)
		{
		__ASSERT_DEBUG(aPriority >= 0 && aPriority < TPriorities, User::Panic(KCFChannelPanic, ECFChanBadQueue));
		return RCFChannelRawBase::Send(&aMsg, aPriority, sizeof(aMsg), iQueues);
		}
	inline void NotifySpaceAvailable(TRequestStatus& aStatus, TInt aPriority)
		{
		__ASSERT_DEBUG(aPriority >= 0 && aPriority < TPriorities, User::Panic(KCFChannelPanic, ECFChanBadQueue));
		RCFChannelRawBase::NotifySpaceAvailable(aStatus, aPriority, iQueues);
		}
	inline void CancelSpaceAvailable(TInt aPriority)
		{
		__ASSERT_DEBUG(aPriority >= 0 && aPriority < TPriorities, User::Panic(KCFChannelPanic, ECFChanBadQueue));
		RCFChannelRawBase::CancelSpaceAvailable(aPriority, iQueues);
		}

	inline TInt Receive(T& aMsg)
		{
		return RCFChannelRawBase::Receive(&aMsg, sizeof(aMsg), iQueues, TPriorities);
		}
	inline void NotifyDataAvailable(TRequestStatus& aStatus)
		{
		RCFChannelRawBase::NotifyDataAvailable(aStatus, iQueues);
		}
	inline void CancelDataAvailable()
		{
		RCFChannelRawBase::CancelDataAvailable(iQueues);
		}

	inline void GetMsgQueues(TMsgQueues& aMsgQueues) const
		{
		RCFChannelRawBase::GetMsgQueues(reinterpret_cast<RMsgQueueBase*>(&aMsgQueues), iQueues, TPriorities);
		}
	inline TBool IsOpen() const
		{
		return RCFChannelRawBase::IsOpen(iQueues);
		}
private:
	RMsgQueueBase iQueues[TPriorities];
	};



class CCFChannelPairRawBase : public CActive
	{
public:
    /** Defines message queues data structure for the array of internal message queues in a channel.
    Used when getting the array of message queues in and out of the channel instance.
    */
	IMPORT_C CCFChannelPairRawBase(TInt aAOPriority, RMsgQueueBase* aRxQueues, RCFChannelRawBase& aRxChannel,
					RMsgQueueBase* aTxQueues, RCFChannelRawBase& aTxChannel, TInt aSize, TInt aChannelPriorities);
	IMPORT_C ~CCFChannelPairRawBase();

	IMPORT_C TInt CreateRecv(const RMsgQueueBase* aForeignQueues, TOwnerType aOwner);
	IMPORT_C TInt CreateSend(const RMsgQueueBase* aForeignQueues, TOwnerType aOwner);
	IMPORT_C TInt CreateRecv(TInt aSlots);
	IMPORT_C TInt CreateSend(TInt aSlots);

	IMPORT_C TInt Send(const TAny* aMsg, TInt aPriority);
//	void NotifySpaceAvailable(CActive& aRequester, TInt aPriority);
	IMPORT_C void NotifySpaceAvailable(TRequestStatus& aStatus, TInt aPriority);
	IMPORT_C void CancelSpaceAvailable(TInt aPriority);

	IMPORT_C TInt Receive(TAny* aMsg);
	
//	void NotifyDataAvailable(CActive& aRequester);
	IMPORT_C void NotifyDataAvailable(TRequestStatus& aStatus);
	IMPORT_C void CancelDataAvailable();

	IMPORT_C TInt CloseGracefully(TInt aGracefulCloseTimeout);
	IMPORT_C void CloseNonGracefully();

	IMPORT_C void GetRecvMsgQueues(RMsgQueueBase* aForeignQueues) const;
	IMPORT_C void GetSendMsgQueues(RMsgQueueBase* aForeignQueues) const;

	/**
	   @internalTechnology
	*/
	TInt RawReceive(TAny* aMsg);
protected:
	/**
	   @publishedPartner
	   @released
	*/
	// Derived channel pairs that support graceful close must overide these
	IMPORT_C virtual TBool IsMsgPIG(const TAny* aMsg);
	IMPORT_C virtual TBool SendPIG();

	IMPORT_C TBool DoSendPIG(const TAny* aMsgPIG);
	IMPORT_C void RunL();
	IMPORT_C void DoCancel();
private:
	static TInt TimerElapsed(TAny* aAnyThis);

	void HaltRx();
	TBool RxHalted();
	void HaltTx();
	TBool TxHalted();

private:
	enum { KRxHalted = 1, KTxHalted = 2 };
	RMsgQueueBase* iRxQueues;
	RCFChannelRawBase& iRxChannel;
	RMsgQueueBase* iTxQueues;
	RCFChannelRawBase& iTxChannel;
	CPeriodic* iCloseTimer;
	TUint16 iSize;
	TUint8 iPriorities;
	TUint8 iFlags;
	};




template <class T, TInt TPriorities>
class RCFChannelPairBase : public CCFChannelPairRawBase
/** Active object encapsulating channels in opposed pairs so as to get bi-
directional communication. Supports graceful close to protect against race
conditions when closing the two channel endpoints.
@publishedPartner
@released
*/
	{
public:
    /** Defines message queues data structure for the array of internal message queues in a channel.
    Used when getting the array of message queues in and out of the channel instance.
    */
	typedef struct RCFChannelBase<T, TPriorities>::TMsgQueuesTAG TMsgQueues;

	inline RCFChannelPairBase(TInt aAOPriority)
		: CCFChannelPairRawBase(aAOPriority,
								iRxChannel.iQueues, iRxChannel,
								iTxChannel.iQueues, iTxChannel,
								sizeof(T), TPriorities)
		{
		}

	inline TInt CreateRecv(const TMsgQueues& aRxQueues, TOwnerType aOwner = EOwnerThread)
		{
		return CCFChannelPairRawBase::CreateRecv(reinterpret_cast<const RMsgQueueBase*>(&aRxQueues), aOwner);
		}
	inline TInt CreateSend(const TMsgQueues& aTxQueues, TOwnerType aOwner = EOwnerThread)
		{
		return CCFChannelPairRawBase::CreateSend(reinterpret_cast<const RMsgQueueBase*>(&aTxQueues), aOwner);
		}
	inline TInt CreateRecv(TInt aSlots)
		{
		return CCFChannelPairRawBase::CreateRecv(aSlots);
		}
	inline TInt CreateSend(TInt aSlots)
		{
		return CCFChannelPairRawBase::CreateSend(aSlots);
		}

	inline TInt Send(const T& aMsg, TInt aPriority)
		{
		return CCFChannelPairRawBase::Send(&aMsg, aPriority);
		}
	inline void NotifySpaceAvailable(TRequestStatus& aStatus, TInt aPriority)
		{
		CCFChannelPairRawBase::NotifySpaceAvailable(aStatus, aPriority);
		}
	inline void NotifySpaceAvailable(CActive& aRequester, TInt aPriority)
		{
		CCFChannelPairRawBase::NotifySpaceAvailable(aRequester.iStatus, aPriority);
		}
	inline void CancelSpaceAvailable(TInt aPriority)
		{
		CCFChannelPairRawBase::CancelSpaceAvailable(aPriority);
		}

	inline TInt Receive(T& aMsg)
		{
		return CCFChannelPairRawBase::Receive(&aMsg);
		}

	
	inline void NotifyDataAvailable(TRequestStatus& aStatus)
		{
		CCFChannelPairRawBase::NotifyDataAvailable(aStatus);
		}
	inline void NotifyDataAvailable(CActive& aRequester)
		{
		CCFChannelPairRawBase::NotifyDataAvailable(aRequester.iStatus);
		}
	inline void CancelDataAvailable()
		{
		CCFChannelPairRawBase::CancelDataAvailable();
		}

	inline TInt CloseGracefully(TInt aGracefulCloseTimeout)
		{
		return CCFChannelPairRawBase::CloseGracefully(aGracefulCloseTimeout);
		}
	inline void CloseNonGracefully()
		{
		CCFChannelPairRawBase::CloseNonGracefully();
		}

	inline void GetRecvMsgQueues(TMsgQueues& aMsgQueues) const
		{
		CCFChannelPairRawBase::GetRecvMsgQueues(reinterpret_cast<RMsgQueueBase*>(&aMsgQueues));
		}
	inline void GetSendMsgQueues(TMsgQueues& aMsgQueues) const
		{
		CCFChannelPairRawBase::GetSendMsgQueues(reinterpret_cast<RMsgQueueBase*>(&aMsgQueues));
		}

	/**
	   @internalTechnology
	*/
	inline TInt RawReceive(T& aMsg)
		{
		return CCFChannelPairRawBase::RawReceive(&aMsg);
		}

protected:
	/**
	   @publishedPartner
	   @released
	*/
	inline TBool IsMsgPIG(const TAny* aMsg)
		{
		const T* msg = reinterpret_cast<const T*>(aMsg);
		return msg->IsPIGCode();
		}
	inline TBool SendPIG()
		{
		T msg;
		msg.SetPIGCode();
		return DoSendPIG(&msg);
		}

private:
	RCFChannelBase<T, TPriorities> iRxChannel;
	RCFChannelBase<T, TPriorities> iTxChannel;
	};



class RCFChannel : public RCFChannelBase<TCFMessage, 1>
/** A one queue (priority) version channel for messages of type TCFMessage.
@publishedPartner
@released
*/
	{
public:
	inline TInt Send(const TCFMessage& aMsg)
		{
		return RCFChannelBase<TCFMessage, 1>::Send(aMsg, 0);
		}
	inline void NotifySpaceAvailable(TRequestStatus& aStatus)
		{
		RCFChannelBase<TCFMessage, 1>::NotifySpaceAvailable(aStatus,  0);
		}
	inline void CancelSpaceAvailable()
		{
		RCFChannelBase<TCFMessage, 1>::CancelSpaceAvailable(0);
		}

	};

class RCFChannelMsgQueues : public RCFChannel::TMsgQueues
/** Msg queue bundle for RCFChannel. Exposed as an external class to enable forwards declarations
*/
	{
	};

class RCFChannelPair : public RCFChannelPairBase<TCFMessage, 1>
/** A one queue (priority) version channel pair for messages of type TCFMessage.
@publishedPartner
@released
*/
	{
public:
	RCFChannelPair(TInt aAOPriority = CActive::EPriorityStandard)
		: RCFChannelPairBase<TCFMessage, 1>(aAOPriority)
		{
		}
	inline TInt Send(const TCFMessage& aMsg)
		{
		return RCFChannelPairBase<TCFMessage, 1>::Send(aMsg, 0);
		}
	inline void NotifySpaceAvailable(TRequestStatus& aStatus)
		{
		RCFChannelPairBase<TCFMessage, 1>::NotifySpaceAvailable(aStatus, 0);
		}
	inline void NotifySpaceAvailable(CActive& aRequester)
		{
		RCFChannelPairBase<TCFMessage, 1>::NotifySpaceAvailable(aRequester.iStatus, 0);
		}
	inline void CancelSpaceAvailable()
		{
		RCFChannelPairBase<TCFMessage, 1>::CancelSpaceAvailable(0);
		}

	};

struct TCFModuleInfo
/** Used to pass thread creation arguments to the new thread .
@publishedPartner
@released
*/
	{
public:
	/** Array of queue handles for outgoing messages.
	It is recommended to use the CommsChannel classes for handling of these.
    @see RCFChannel
	@see RCFChannelPair
    */
	RCFChannel::TMsgQueues iTxQueues;
	/** Array of queue handles for incoming messages.
	It is recommended to use the CommsChannel classes for handling of these.
    @see RCFChannel
	@see RCFChannelPair
    */
	RCFChannel::TMsgQueues iRxQueues;
	/** Initialisation data specific to module.
    */
	HBufC8* iIniData;
private:
	/** Spares against future needs
	*/
	TUint32 iSpare1;
	TUint32 iSpare2;
	};

#ifdef SYMBIAN_NETWORKING_CFTRANSPORT
#include <commschan.inl>
#else
#include <commschan_v1.inl>
#endif

}

#endif // __COMMSCHAN_H__

