serialserver/c32serialserver/SCOMM/cs_roles.cpp
changeset 0 dfb7c4ff071f
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/serialserver/c32serialserver/SCOMM/cs_roles.cpp	Thu Dec 17 09:22:25 2009 +0200
@@ -0,0 +1,3221 @@
+// Copyright (c) 2005-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:
+//
+
+/**
+ @file
+ @internalComponent
+*/
+
+#include <cfextras.h>
+#include <comms-infras/c32startcli.h>
+#include <e32property.h>
+#ifdef SYMBIAN_ENABLE_SPLIT_HEADERS
+#include <cflog.h>
+#endif
+#include "cs_roles.h"
+#include "C32LOG.H"
+#include "CS_STD.H"
+#include "cs_msgs.h"
+#include "cs_glob.h"
+
+using namespace RootServer;
+
+#define MSG_PRM(prmIndex)		(prmIndex)		// subsession is passed in message as parameter 3
+
+const TInt KIniDataBufSizeIncrement = 60;   // Initial buffer of 60 chars - after finding 
+                                           // usual iniData section uses about 40 chars to describe workerId,Role and CSYlist
+
+//
+// CC32WorkerDataGlobals
+//
+
+CC32WorkerDataGlobals* CC32WorkerDataGlobals::NewL()
+	{
+	CC32WorkerDataGlobals* self = new(ELeave) CC32WorkerDataGlobals();
+	CleanupStack::PushL(self);
+	self->ConstructL(sizeof(TC32WorkerThreadRegister), KMaxWorkerThreadId);
+	CleanupStack::Pop(self);
+	return self;
+	}
+
+//
+// CCSYInfo
+//
+
+CCSYInfo::CCSYInfo(TInt aThreadNum)
+: iThreadNum(aThreadNum),
+iLoaded(EFalse),
+iLoadIndex(-1)
+	{
+	}
+
+CCSYInfo* CCSYInfo::NewL(const TDesC8& aCSYName, TInt aThreadNum)
+	{
+	CCSYInfo* self = new (ELeave) CCSYInfo(aThreadNum);
+	CleanupStack::PushL(self);
+	self->ConstructL(aCSYName);
+	CleanupStack::Pop(self);
+	return self;
+	}
+	
+	
+void CCSYInfo::ConstructL(const TDesC8& aCSYName)
+	{
+	iCSYName=aCSYName.AllocL();
+	}
+
+CCSYInfo::~CCSYInfo()
+	{
+	delete iCSYName;
+	iCSYPortPrefix.Close();
+	}
+	
+	
+TInt CCSYInfo::CompareCSYInfo(const CCSYInfo& aFirst,const CCSYInfo& aSecond)
+// compares CSY records by comparing the csy filenames
+    {
+    //-ve if first is less than second
+    return aFirst.iCSYName->CompareF(*(aSecond.iCSYName));
+    }
+    	
+TInt CCSYInfo::SetPortNameSizeToMaximum()
+	{
+	__ASSERT_DEBUG(iCSYPortPrefix.Length()==0,Fault(EBadState,_L8("CCSYInfo: attempt to grow portname for CSY for a non-null string (ie, module looks loaded)")));
+	return iCSYPortPrefix.ReAlloc(KMaxFullName);	
+	}
+
+    	
+void CCSYInfo::SetPortName(const TDesC& aPortName)
+	{
+	// since this may be called at end of csy load process, we have no reasonable way to deal with OOM,
+	// so before the csy load process started, the portname
+	// was grown to max. After setting the string we resize the portname so that the remaining
+	// memory is freed. An RBuf allows us to resize in-place.
+	
+	iCSYPortPrefix = aPortName;
+	
+#ifdef _DEBUG
+	TInt ret = iCSYPortPrefix.ReAlloc(aPortName.Length());
+	__ASSERT_DEBUG(ret == KErrNone,Fault(EBadState,_L8("CCSYInfo::SetPortName: failed to shrink port prefix. Ret: %d"),ret));
+#else
+	(void)iCSYPortPrefix.ReAlloc(aPortName.Length()); // on failure, continue with current size.
+#endif
+	
+	}
+	
+	
+	
+void CCSYInfo::SetCSYHandle(CSerial* aSerialHandle)
+//this handle is never actually used by CCSYInfo, just passed back to player later
+	{
+	iCSYHandle=aSerialHandle;
+	}
+
+
+
+
+
+
+//
+// CC32ThreadManager::CC32ParsedIniData
+//
+
+CC32ThreadManager::CC32ParsedIniData::CC32ParsedIniData()
+	{
+	}
+
+
+CC32ThreadManager::CC32ParsedIniData* CC32ThreadManager::CC32ParsedIniData::NewL(const TDesC8& aIniData, const TBool aIsDealer)
+
+// aIsDealer specifies whether this data was sent to us from the configurator as it spawned the
+// dealer thread
+	{
+	CC32ParsedIniData* self = new(ELeave) CC32ParsedIniData();
+	CleanupStack::PushL(self);
+	self->ConstructL(aIniData,aIsDealer);
+	CleanupStack::Pop(self);
+	return self;
+	}
+	
+
+CC32ThreadManager::CC32ParsedIniData::~CC32ParsedIniData()
+	{
+	iCSYs.Close();
+	}
+
+void CC32ThreadManager::CC32ParsedIniData::ConstructL(const TDesC8& aIniData, const TBool aIsDealer)
+// we could have had the NewL perform this function, but because that would men creating and destroying
+// this object for each data read, we chose to save that by making this operation separate
+// aIsDealer - true if this is the inidata for the dealer loading all the cmi files. This allows
+//             us to know this is a dealer even if nothing of the sort is in the [iniData] section and
+//             thus we can preserve backwards compat.
+
+
+// on exit the data should be:
+// workerID = always correct - if we are dealer it is automatically 0 regardless of what config says. If we
+//            are player we leave unless this is provided. We leave if it is zero and we are player.
+// isPlayer - is false if we know we are dealer (aIsDealer) regardless of aIniData. Is true if we
+//            see Role=player. If role missing and we are not Dealer (!aIsDealer) then we don't try
+//            any recovery since a workerthread assumes it is a dealer if no role.
+// isWildcard - true if a wildcard is provided in the csy list
+// iCSYs - list of the csys in CSYList. It is valid that a dealer may not have a CSY list
+	{
+	TPtrC8 roleValue;
+	TPtrC8 workerIdValue;
+	TPtrC8 csyListValue;
+	
+	if (&aIniData==NULL || aIniData.Length()==0)
+		{
+		if (!aIsDealer)
+			{
+			C32LOG(KC32Warning,_L8("CC32ParsedIniData: Corrupt CMI file that references C32.DLL: Missing [IniData] section."));
+			C32LOG(KC32Warning,_L8("CC32ParsedIniData: Please migrate all *.cmi files that reference C32.DLL to include Group token and [IniData] section."));
+			// we don't panic in this scenario since this could be an old ini file we've found
+			// in future we will panic here once licencees have migrated any cmi files they need to
+			User::Leave(KErrCorrupt);
+			}
+		else
+			{
+			// dealers are allowed to not have CSYLists and we know what the role & workerId should be so continue
+			// in future this will be a config error but during migration we continue.
+   		    C32LOG(KC32Detail,_L8("CC32ParsedIniData: Supplied inidata for dealer is empty - must be old CMI file. Skipping."));
+			return;
+			}
+		}
+
+
+	if (!GetVarFromIniData(aIniData, KNullDesC8, KWorkerIdLabel, workerIdValue) )
+		{
+		C32LOG(KC32Warning,_L8("CC32ParsedIniData: Corrupt [IniData] section - missing workerId value."));
+		
+		if (aIsDealer)
+			{
+			C32LOG(KC32Warning,_L8("CC32ParsedIniData: As we are dealer, ignoring corruption by setting workerId to 0 and continuing"));
+			iCMIWorkerId = KMainThreadId;
+			}
+		else
+			{
+#ifdef _DEBUG
+			Fault(EBadIniData); // panic
+#endif
+			User::Leave(KErrCorrupt);
+			}
+		}
+	else
+		{
+		// we accept whatever we get as the workerId
+		TLex8 lex(workerIdValue);
+		(void)lex.Val(iCMIWorkerId);		
+		}
+		
+	
+		
+	
+	if (!GetVarFromIniData(aIniData, KNullDesC8, KRoleLabel, roleValue))
+		{
+		C32LOG(KC32Warning,_L8("CC32ParsedIniData: Corrupt [IniData] section - missing Role label."));
+		if (!aIsDealer)
+			{
+#ifdef _DEBUG
+		Fault(EBadIniData); // panic
+#endif
+		User::Leave(KErrCorrupt);
+			}
+		}
+	else if(roleValue.CompareF(KPlayerRole)==0)
+		{
+		iIsPlayer = ETrue;
+		}
+	else if(roleValue.CompareF(KDealerRole)!=0)
+		{
+		C32LOG2(KC32Warning,_L8("CC32ParsedIniData: Corrupt [IniData] section: Specified Role not recognised; must be Dealer or Player: %d "),&roleValue);
+		
+#ifdef _DEBUG
+		Fault(EBadIniData); // panic
+#endif
+		User::Leave(KErrCorrupt);
+		}
+		
+	
+	if (iIsPlayer && (iCMIWorkerId < 0 || iCMIWorkerId > KMaxWorkerThreadId))
+		{
+		C32LOG2(KC32Warning,_L8("CC32ParsedIniData: Corrupt [IniData] section: Role=Player and WorkerId invalid: %d"),iCMIWorkerId);
+#ifdef _DEBUG
+		Fault(EBadIniData); // panic
+#endif
+		User::Leave(KErrCorrupt);
+		}
+		
+		
+	if (!GetVarFromIniData(aIniData, KNullDesC8, KCSYListLabel, csyListValue))
+		{
+		if (iIsPlayer)
+			{
+			C32LOG(KC32Warning,_L8("CC32ParsedIniData: Corrupt [IniData] section - missing CSYList definition."));
+#ifdef _DEBUG
+		Fault(EBadIniData); // panic
+#endif
+			User::Leave(KErrCorrupt);
+			}
+		}
+	else
+		{
+		
+		TInt sectionLen = 0;	
+		TPtrC8 section(csyListValue);
+
+		
+		while (section.Length() > 0)
+			{
+			// assume that a CSY name is at least 1 characters long
+			sectionLen = section.Find(TPtrC8(_S8(",")));
+			if (sectionLen > 0)
+				{
+				iCSYs.AppendL(section.Mid(0,sectionLen));
+				
+				if (sectionLen+1 < section.Length())
+					{
+					section.Set(section.Mid(sectionLen+1));
+					} 
+				else
+					{
+					section.Set(KNullDesC8);
+					}
+				} 
+			else if ((sectionLen==0) && (section.Length() > 0))
+				{
+				//possibly a double comma - we'll be nice and ignore it
+				section.Set(section.Mid(sectionLen+1));
+				} 
+			else if (section[0]=='*')
+				{
+				iIsDefault = ETrue;
+				if (section.Length() > 1)
+					{
+					section.Set(section.Mid(1));
+					} 
+				else
+					{
+					section.Set(KNullDesC8);
+					}
+				}
+			else
+				{
+				iCSYs.AppendL(section);
+				section.Set(KNullDesC8);
+				}
+			}
+		}		
+		
+
+	}
+
+
+
+
+
+
+
+
+
+//
+// CC32ThreadManager::CC32ThreadInfo
+//
+
+CC32ThreadManager::CC32ThreadInfo::CC32ThreadInfo()
+	{
+	}
+	
+
+
+CC32ThreadManager::CC32ThreadInfo* CC32ThreadManager::CC32ThreadInfo::NewL(const TDesC8& aModuleName)
+	{
+	CC32ThreadInfo* self = new (ELeave) CC32ThreadInfo();
+	CleanupStack::PushL(self);
+	self->ConstructL(aModuleName);
+	CleanupStack::Pop(self);
+	return self;	
+	}
+	
+	
+void CC32ThreadManager::CC32ThreadInfo::ConstructL(const TDesC8& aModuleName)
+	{
+	iModuleName = aModuleName.AllocL();
+	}
+	
+void CC32ThreadManager::CC32ThreadInfo::SetModuleNameL(const TDesC8& aModuleName)
+	{
+	delete iModuleName;
+	iModuleName = NULL;
+	iModuleName = aModuleName.AllocL();
+	}
+	
+CC32ThreadManager::CC32ThreadInfo::~CC32ThreadInfo()
+	{
+	delete iModuleName;
+	}
+
+
+
+TBool CC32ThreadManager::CC32ThreadInfo::CompareThreadInfo(const CC32ThreadInfo& aFirst,const CC32ThreadInfo& aSecond)
+// aFirst should always be non-empty. aSecond may be an empty string if it is the dealer entry
+	{
+	return (aFirst.iModuleName==aSecond.iModuleName);
+	}
+
+
+
+
+
+
+
+
+//
+// CCPMLoader
+//
+
+
+CC32ThreadManager::CCPMLoader::CCPMLoader()
+    : CActive(EPriorityStandard)
+    {
+    // server is at a super-high priority, so whatever priority we choose is not going to make any difference
+    CActiveScheduler::Add(this);
+
+    }
+    
+CC32ThreadManager::CCPMLoader* CC32ThreadManager::CCPMLoader::NewL()
+	{
+    CCPMLoader* self = new(ELeave) CC32ThreadManager::CCPMLoader();
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	CleanupStack::Pop(self);    
+    return self;
+	}
+
+void CC32ThreadManager::CCPMLoader::ConstructL()
+	{
+	TInt ret = iCfgSvr.Connect();
+	if (ret != KErrNone)
+ 		 {
+ 		 C32LOG2(KC32Dealer,_L8("CC32Dealer::LoadModule failed during connect to configurator. Code: %d"),ret);
+ 		 User::Leave(ret);
+ 		 }
+	}
+
+	
+void CC32ThreadManager::CCPMLoader::DoCancel()
+	{
+	C32LOG(KC32Detail,_L8("CC32Dealer::CCPMLoader: DoCancel called"));
+	
+	iCfgSvr.CancelLoadCpm(iName);
+	}
+	
+CC32ThreadManager::CCPMLoader::~CCPMLoader()
+    {
+    Cancel();
+    iCfgSvr.Close();
+    // deques on delete of parent
+    }	
+    
+void CC32ThreadManager::CCPMLoader::RequestModuleLoad(CC32ThreadManager* aThreadMgr, CommsFW::TWorkerId aWorker, const TCFModuleName& aName)
+// Begin the asynchronous process of loading a player
+	{
+	__ASSERT_DEBUG(!IsActive(),Fault(EBadState,_L8("CCPMLoader: Loader already busy with another request when loading module %S. Panicking"),&aName));
+	
+	iName = TCFModuleName(aName);    // store so that when we lose stack at this function exit, RRs server can still read data
+	iThreadManager = aThreadMgr;
+	iWorkerId = aWorker;
+
+	iCfgSvr.LoadCpm(iName,iStatus);
+	
+	SetActive();
+		
+	C32LOG2(KC32Dealer,_L8("CCPMLoader: Load request for module %S sent to Configurator"),&iName);
+	}
+
+void CC32ThreadManager::CCPMLoader::RunL()
+// this normally never runs in the success case since new player's response beats it and dealer kills the loader.
+	{
+	TInt res = iStatus.Int();
+
+	C32LOG4(KC32Dealer,_L8("CC32Dealer: Configurator has completed LoadCPM (%S)(WorkId%d) request with %d"),&iName,iWorkerId,res);
+	
+	// assert_debug in here that status is never "already running"
+	__ASSERT_DEBUG(res != KErrRSModuleAlreadyExist, Fault(EBadState,_L8("CC32Dealer: LoadCPM returned KErrRSModuleAlreadyExists - this is unexpected so Panicking.")));
+	__ASSERT_DEBUG(res != KErrRSModuleUnknown, Fault(EBadState,_L8("CC32Dealer: LoadCPM returned KErrRSModuleUnknown - probably incorrect CMI file. Panicking.")));
+	
+	
+	// on success there is nothing we need to do with this response - just capture it and log it.
+	// on failure we clean up.
+	
+	if (res==KErrNone)
+		{
+		C32LOG2(KC32Detail,_L8("CC32Dealer::LoadCPM: Load for (%S) has been successful"),&iName);
+		}
+	else
+		{
+		__ASSERT_DEBUG(!iThreadManager->Dealer()->WorkerExists(iWorkerId),Fault(EBadState,_L8("CCPMLoader::RunL - WorkerExists(%d) but configurator returned a failure: %d"),iWorkerId,res));
+		C32LOG2(KC32Dealer,_L8("CC32Dealer::LoadCPM (%S) has apparently failed so cleaning up pending requests and threadmanager"),&iName);
+
+		// give threadmanager a chance to remove any memory we allocated before 
+		// commencing the module load.
+		iThreadManager->ProcessModuleLoadFailed(iWorkerId);
+		
+		// we now leave ourselves inactive but still valid since we will be needed on the next attempt to load this cpm
+		// and there is no obvious place to delete ourselves
+		}
+	}
+	
+
+
+
+//
+// CC32ThreadManager
+//
+
+CC32ThreadManager::CC32ThreadManager(CC32Dealer* aDealer)
+	{
+	iDealer = aDealer;
+	}
+	
+	
+CC32ThreadManager* CC32ThreadManager::NewL(CC32Dealer* aDealer)
+	{
+	CC32ThreadManager* self = new (ELeave) CC32ThreadManager(aDealer);
+	return self;
+
+	}
+
+CC32ThreadManager::~CC32ThreadManager()
+	{
+	iCSYList.ResetAndDestroy();
+	iC32ThreadList.ResetAndDestroy();
+	
+	for (TInt i=0;i <= KMaxWorkerThreadId ; i++)
+		{
+		delete iCPMLoader[i];
+		}
+	}
+	
+CC32Dealer* CC32ThreadManager::Dealer()
+	{
+	__ASSERT_DEBUG(iDealer!=NULL,Fault(EBadState,_L8("CC32ThreadManager: Dealer() - iDealer is NULL. Panicking.")));
+	return iDealer;
+	}
+
+
+TWorkerId CC32ThreadManager::DefaultThread()
+	{
+	return iDefaultThreadIndex;
+	}
+
+
+TBool CC32ThreadManager::FindThreadByPortPrefix(const TDesC& aPortName, TWorkerId& aWorker) const
+// returns EFalse if not found, ETrue otherwise
+// unloaded csys or csys yet to be loaded have an empty port prefix so will not be found
+// searching is done in folded way to avoid case problems.
+	{
+	TInt idx = 0;
+	if (&aPortName==NULL)
+		{
+		return KErrNotFound;
+		}
+		
+	if (aPortName.Length()==0)
+		{
+		return KErrNotFound;
+		}
+	
+	while (idx < iCSYList.Count())
+		{
+		if (aPortName.CompareF(iCSYList[idx]->GetPortName())==0)
+			{
+			__ASSERT_DEBUG(iCSYList[idx]->IsLoaded(),Fault(EBadState,_L("Panicking due to finding CSY by PortName (%S) that is inactive at index: %d" ),&aPortName,idx));
+			aWorker=iCSYList[idx]->WorkerId();
+			return ETrue;
+			}
+		idx++;
+		}
+		
+	return EFalse;
+	}
+	
+	
+	
+TInt CC32ThreadManager::MapPortprefixToCSYFileName(const TDesC& aPortName, TDes& aCSYFileName)
+// aPortName is the port prefix format name to search on.
+// unloaded csys or csys yet to be loaded have an empty port prefix so will not be found.
+// aCSYFileName - on return contains the filename for the csy with the portprefix
+// Return: KErrNone if the csy was found, KErrNotFound otherwise.
+// Panics:
+//    if aCSYFileName is not long enough to take result.
+// Returns KErrNotFound in the case of port prefix being null
+// 
+	{
+	TInt idx = 0;
+	if (&aPortName==NULL)
+		{
+		return KErrNotFound;
+		}
+	if (aPortName.Length()==0)
+		{
+		return KErrNotFound;
+		}
+
+	while (idx < iCSYList.Count())
+		{
+		if (aPortName.CompareF(iCSYList[idx]->GetPortName())==0)
+			{
+			__ASSERT_DEBUG(iCSYList[idx]->IsLoaded(),Fault(EBadState,_L("Panicking due to finding CSY by PortName (%S) that is inactive at index: %d" ),&aPortName,idx));
+			aCSYFileName.Copy(*iCSYList[idx]->GetCSYName());
+			return KErrNone;
+			}
+		idx++;
+		}
+		
+	return KErrNotFound;
+	}
+	
+	
+	
+TBool CC32ThreadManager::FindThreadByFileName(const TDesC8& aCSYFileName8, TWorkerId &aWorkerId) const
+// Find thread that would house this CSY, if no match found, return EFalse
+// CSYFilename - CSY name with extension
+	{
+	TInt idx = 0;
+	while (idx < iCSYList.Count())
+		{
+		if (aCSYFileName8.CompareF(*iCSYList[idx]->GetCSYName())==0)
+			{
+			aWorkerId = iCSYList[idx]->WorkerId();
+			return ETrue;
+			}
+		idx++;
+		}
+		
+	return EFalse;	
+	}
+
+TBool CC32ThreadManager::FindThreadOfActiveCSYByName(const TDesC& aName, TWorkerId& aWorkerId) const	// GetPortInfo(aName) version, TWorkerId filled with worker
+// find thread by filename of CSY, if not found try by portnam of CSY.
+// only searches active CSYs
+// aName - portprefix or filename of csy to find
+// aWorkerId - on return contains workerId, unchanged if result is EFalse
+// returns: ETrue if found, EFalse otherwise
+	{
+	TBuf8<KMaxFileName> nameWithExt8;
+	nameWithExt8.Copy(aName);
+	
+	 TInt r=nameWithExt8.LocateReverse('.');
+	 if (r==KErrNotFound)
+ 		 {
+ 		 if (nameWithExt8.Length()>KMaxFileName - (TInt)KCSYExtension8.iTypeLength)		 // valid filename is checked here
+	 		 {
+	 		 return EFalse;
+	 		 }
+ 		 nameWithExt8.Append(KCSYExtension8);
+ 		 }
+	
+	
+	TInt idx = 0;
+	while (idx < iCSYList.Count())
+		{
+		if (iCSYList[idx]->IsLoaded() && ((aName.CompareF(iCSYList[idx]->GetPortName())==0) || (nameWithExt8.CompareF(*iCSYList[idx]->GetCSYName())==0)))
+			{
+			aWorkerId = iCSYList[idx]->WorkerId();
+			return ETrue;
+			}
+		idx++;
+		}
+
+	return EFalse;
+	}
+
+TInt CC32ThreadManager::FindThreadByGlobalIndex(const TInt aIndex) const
+// aIndex is the array position the CSY would be in if it were in the old portmanager structure
+// This function returns zero or a positive integer if the global index refers to a loaded
+// csy. If not, then KErrNotFound is returned.
+
+	{
+	__ASSERT_DEBUG(aIndex >= 0,Fault(EBadState));
+	TInt absIdx = 0;
+	while (absIdx < iCSYList.Count())
+		{
+		if (iCSYList[absIdx]->IsLoaded())
+			{
+			if (iCSYList[absIdx]->LoadIndex()==aIndex)
+				{
+				return iCSYList[absIdx]->WorkerId();
+				}
+			}
+		absIdx++;
+		}
+		
+	
+	return KErrNotFound;
+	}
+
+CSerial* CC32ThreadManager::GetSerialObjectFromCSYFileName(const TDesC& aCSYFileName)
+// We might be asked to do an update even for a CSY that has been loaded since all CSY load requests are
+// passed through to the player. This is because player needs to record each session that loads the module.
+	{
+	TBuf8<KMaxFileName> fileName8;   // convert 16 to 8-bit string. huge stack usage, but no other way
+	fileName8.Copy(aCSYFileName);
+	TInt idx = 0;
+	
+	while (idx < iCSYList.Count())
+		{
+		if (fileName8.CompareF(*iCSYList[idx]->GetCSYName())==0)
+			{
+			C32LOG1(KC32Detail, _L8("CC32ThreadManager::GetSerialObjectFromCSYFileName()"));
+			return iCSYList[idx]->GetCSYHandle();
+			}
+		else
+			{
+			idx++;
+			}
+		}	
+	return NULL;
+	}
+
+CSerial* CC32ThreadManager::GetSerialL(const TDesC& aPortName)
+	{
+	TInt idx;
+	User::LeaveIfError(GetIndexFromPortPrefix(aPortName, idx));
+	return GetSerialObjectByIndex(idx);
+	}
+
+TInt CC32ThreadManager::GetIndexFromPortPrefix(const TDesC& aPortName, TInt& aIndex)
+	{
+	TInt idx = 0;
+	
+	while (idx < iCSYList.Count())
+		{
+		if (aPortName.CompareF(iCSYList[idx]->GetPortName())==0)
+			{
+			C32LOG1(KC32Detail, _L8("CC32ThreadManager::GetIndexFromPortPrefix()"));
+			aIndex = idx;
+			return KErrNone;
+			}
+		else
+			{
+			idx++;
+			}
+		}	
+	return KErrNotFound;
+	}
+
+CSerial* CC32ThreadManager::GetSerialObjectByIndex(TInt aIndex)
+	{
+	return iCSYList[aIndex]->GetCSYHandle();
+	}
+
+TInt CC32ThreadManager::IncrementCountOnLoad(const TDesC& aCSYFileName)
+	{
+	TBuf8<KMaxFileName> fileName8;   // convert 16 to 8-bit string. 
+	fileName8.Copy(aCSYFileName);
+	TInt idx = 0;
+	
+	while (idx < iCSYList.Count())
+		{
+		if (fileName8.CompareF(*iCSYList[idx]->GetCSYName())==0)
+			{
+			iCSYList[idx]->IncAccessCount();
+			C32LOG3(KC32Detail, _L8("CC32ThreadManager::IncrementCountOnCSYLoad() AccessCount for %S CSY = %d"), &fileName8, iCSYList[idx]->AccessCount() );
+			return KErrNone;
+			}
+		else
+			{
+			idx++;
+			}
+		}	
+	return KErrNotFound;
+	}
+
+TInt CC32ThreadManager::DecrementCountOnCSYUnLoad(const TDesC& aCSYFileName)
+	{
+	TBuf8<KMaxFileName> fileName8;   // convert 16 to 8-bit string. 
+	fileName8.Copy(aCSYFileName);
+	TInt idx = 0;
+	
+	while (idx < iCSYList.Count())
+		{
+		if (fileName8.CompareF(*iCSYList[idx]->GetCSYName())==0)
+			{
+			if(iCSYList[idx]->AccessCount()==1)
+				{
+				// unload CSY as all sesions have unloaded CSYs or closed now.
+				UpdateThreadManagerOnCSYUnload(fileName8);
+				}
+			iCSYList[idx]->DecAccessCount();
+			C32LOG3(KC32Detail, _L8("CC32ThreadManager::DecrementCountOnCSYUnLoad() AccessCount for %S CSY = %d"), &fileName8, iCSYList[idx]->AccessCount() );
+			return KErrNone;
+			}
+		else
+			{
+			idx++;
+			}
+		}	
+	return KErrNotFound;
+	}
+
+
+TInt CC32ThreadManager::GrowCSYPortNameToMaximumIfNotLoaded(const TDesC& aCSYFileName)
+	{
+	TBuf8<KMaxFileName> fileName8;   // convert 16 to 8-bit string. 
+	fileName8.Copy(aCSYFileName);
+	TInt idx = 0;
+	
+	while (idx < iCSYList.Count())
+		{
+		if (fileName8.CompareF(*iCSYList[idx]->GetCSYName())==0)
+			{
+			if (!iCSYList[idx]->IsLoaded())
+				{
+				return iCSYList[idx]->SetPortNameSizeToMaximum();
+				}
+			else
+				{
+				return KErrNone;
+				}
+			}
+		else
+			{
+			idx++;
+			}
+		}	
+	return KErrNotFound;
+	}
+
+
+TInt CC32ThreadManager::RequestLoadModule(CommsFW::TWorkerId aWorker)
+	{
+	TInt ret = KErrNone;
+	
+	__ASSERT_DEBUG(!Dealer()->WorkerExists(aWorker),Fault(EBadState,_L8("CC32ThreadManager::RequestLoadModule: Worker already Exists! Panicking")));
+
+	
+	// loader may already exist but be inactive if a previous load attempt failed
+	if (iCPMLoader[aWorker] == NULL)
+		{
+		C32LOG3(KC32Detail,_L8("ThreadMgr::RequestLoadModule: Loader doesn't exist for worker %d (%S). Starting worker"),aWorker,GetModuleName(aWorker));
+		TRAP(ret,iCPMLoader[aWorker] = CCPMLoader::NewL());
+		if (ret != KErrNone)
+			{
+			C32LOG2(KC32Detail,_L8("CCPMLoader::NewL() leave occured (%d)"),ret);
+			return ret;
+			}
+		iCPMLoader[aWorker]->RequestModuleLoad(this,aWorker,*GetModuleName(aWorker));
+		}
+	else if (!iCPMLoader[aWorker]->IsActive())
+		{
+		C32LOG3(KC32Detail,_L8("CC32ThreadManager: Inactive Loader already exists - reusing to start worker %d (%S)."),aWorker,GetModuleName(aWorker));
+		
+		iCPMLoader[aWorker]->RequestModuleLoad(this,aWorker,*GetModuleName(aWorker));
+		}
+	else
+		{
+		C32LOG2(KC32Detail,_L8("ThreadMgr: RequestLoadModule: Attempt to start worker %d ignored due to already outstanding load- dealer should queue this req."),aWorker);
+ 		// if timer is already running, we assume load still in progress so we just exit
+ 		// and caller can queue request
+		ret=KErrInUse;
+		}
+	return ret;
+	}
+
+
+void CC32ThreadManager::ProcessModuleLoadFailed(CommsFW::TWorkerId aWorker)
+// called from LoadCPM when configurator reports that player has failed to start
+// Gives thread manager a chance to do any cleanup.
+	{
+	C32LOG2(KC32Dealer,_L8("CC32ThreadManager:ProcessModuleLoadFailed: Failed worker was %d. Cleaning up pending requests."),aWorker);
+	
+	// give dealer a chance to remove any pending requests for the module which did not start
+	// and undo any memory allocations done as part of load
+	Dealer()->ProcessFailedPlayerLoad(aWorker);
+
+	}
+
+
+void CC32ThreadManager::ProcessModuleLoadSuccess(CommsFW::TWorkerId aWorker)
+// called when dealer receives introduction msg from player, regardless of how player started
+	{
+	C32LOG2(KC32Detail, _L8("CC32ThreadManager::ProcessModuleLoadSuccess for worker %d. Deleting CPMLoader."), aWorker);
+	
+	// Delete the load Active object. It turns out that normally by this point the configurator has yet to complete the request,
+	// even though the player is already running, so by removing the loader here, we will be sending a Cancel to the configurator
+	// for an operation we know has already completed successfully. This is safe (view a request as consisting of two parts
+	// - 1) ask a server to make a request and 2) hope the server will let you know how it goes - we are
+	// now simply cancelling part 2 since we know how part 1 went)
+	
+	delete iCPMLoader[aWorker];
+	iCPMLoader[aWorker] = NULL;
+	
+	}
+
+
+
+void CC32ThreadManager::UpdateThreadManagerOnCSYUnload(const TDesC8& aCSYFileName8)
+// called when we predict Player will unload CSY, since the indexing of the CSYs and the auto-close on index 0 behaviour
+// that the player uses (preserved legacy C32 behaviour) does not give an opportunity for this process to be intercepted
+// on player side and a mesg sent to dealer.
+	{
+	TInt idx = 0;	
+	
+	// NULL CSerial* and Portname
+	C32LOG2(KC32Detail, _L8("CC32ThreadManager::UpdateThreadManagerOnCSYUnLoad() for %S"), &aCSYFileName8);
+	while (idx < iCSYList.Count())
+		{
+		if (aCSYFileName8.CompareF(*iCSYList[idx]->GetCSYName())==0)
+			{
+			__ASSERT_DEBUG(iCSYList[idx]->IsLoaded(),Fault(EBadState,_L8("UpdateThreadManagerOnCSYUnLoad: CSY marked as not loaded on call to update after unload. Panicking.")));
+			iCSYList[idx]->SetPortName(KNullDesC16);
+			iCSYList[idx]->SetCSYHandle(NULL);
+			iCSYList[idx]->SetLoadState(EFalse);
+			
+			// run thru list and adjust index numbers now that this csy has unloaded
+			for (TInt absIdx = 0; absIdx < iCSYList.Count(); ++absIdx)
+				{
+				if ((iCSYList[absIdx]->IsLoaded() && (iCSYList[absIdx]->LoadIndex() > iCSYList[idx]->LoadIndex())))
+					{
+					iCSYList[absIdx]->SetLoadIndex(iCSYList[absIdx]->LoadIndex()-1);
+					}
+				}
+			
+			iCSYList[idx]->SetLoadIndex(-1);
+			--iNumLoadedModules;
+
+			return;
+			}
+		else
+			{
+			idx++;
+			}
+		}	
+	
+	C32LOG2(KC32Warning,_L8("UpdateThreadManagerOnCSYUnload: Could not find CSY supposedly unloaded: %S. Will Panic in debug."),&aCSYFileName8);
+	__ASSERT_DEBUG(0,Fault(EBadState));
+	}
+
+void CC32ThreadManager::UpdateThreadManagerOnCSYLoad(const TDesC& aCSYFileName, const TDesC& aPortPrefix, CSerial* aSerial)
+// We might be asked to do an update even for a CSY that has been loaded since all CSY load requests are
+// passed through to the player. This is because player needs to record each session that loads the module.
+	{
+	TBuf8<KMaxFileName> fileName8;   // convert 16 to 8-bit string. huge stack usage, but no other way
+	fileName8.Copy(aCSYFileName);
+	TInt idx = 0;
+	
+
+	// we update the csyinfo with the new serial and port prefix and mark it active
+
+	while (idx < iCSYList.Count())
+		{
+		if (fileName8.CompareF(*iCSYList[idx]->GetCSYName())==0)
+			{
+			if (iCSYList[idx]->IsLoaded()==EFalse)
+				{
+				C32LOG5(KC32Detail, _L("C32ThrdMg::UpdateThreadManagerOnCSYLoad() for CSY[%d](%S).CSerial=%x. PortPrefix=%S"),idx,&aCSYFileName,aSerial,&aPortPrefix);
+				iCSYList[idx]->SetPortName(aPortPrefix);
+				iCSYList[idx]->SetCSYHandle(aSerial);
+				iCSYList[idx]->SetLoadState(ETrue);
+				
+				iCSYList[idx]->SetLoadIndex(iNumLoadedModules);
+				++iNumLoadedModules;
+				}
+			else
+				{
+				C32LOG3(KC32Detail, _L8("CC32ThreadManager::UpdateThreadManagerOnCSYLoad(): CSY[%d] already updated. Access count is %d"),idx,iCSYList[idx]->AccessCount());
+				}
+			return;
+			}
+		else
+			{
+			idx++;
+			}
+		}
+		
+		
+	C32LOG2(KC32Warning,_L8("UpdateThreadManagerOnCSYLoad: Could not find CSY supposedly loaded: %S. Will panic in debug."),&fileName8);
+	__ASSERT_DEBUG(0,Fault(EBadState));
+	}
+
+void CC32ThreadManager::UpdateThreadManagerOnCSYLoadFailure(const TDesC& aCSYFilename)
+// called to allow thread manager to clean up any allocations made prior to attempting CSY load.
+// aCSYFilename - name with extension of CSY that player failed to load
+	{
+	CommsFW::TWorkerId worker;
+	
+	TBuf8<KMaxFileName> fileName8;   // convert 16 to 8-bit string. huge stack usage, but no other way
+	fileName8.Copy(aCSYFilename);
+
+	
+	TBool found = FindThreadByFileName(fileName8,worker);
+	
+	// it is possible we would not find it if two sessions had requested this csy load at same time and
+	// this is now the completion of the second sessions load attempt and the first session cleanup
+	// had destroyed the csyInfo.
+	if (found)
+		{
+		// if this was default thread we might have
+		// added an unknown CSY so in this case remove it. It does no harm if this CSY was specified in CMI
+		// file since on next attempt thread manager will place the CSY in same thread anyway.
+		
+		if (worker == iDefaultThreadIndex)
+			{
+			for (TInt i=0; i < iCSYList.Count(); i++)
+				{
+				// comparing the worker Ids first is a bit quicker
+				if (iCSYList[i]->WorkerId()==worker)
+					{
+					if (iCSYList[i]->GetCSYName()->Compare(fileName8)==0)
+						{
+						__ASSERT_DEBUG(!iCSYList[i]->IsLoaded(),Fault(EBadState,_L8("CC32ThreadManager::UpdateThreadManagerOnCSYLoadFailure: CSY[%d] is marked as loaded! Panicking."),i));
+						C32LOG3(KC32Dealer,_L("CC32ThreadManager::UpdateThreadManagerOnCSYLoadFailure: Failed CSY (%S) was for default thread. Deleting CSY[%d]"),&aCSYFilename,i);
+						delete iCSYList[i];
+						iCSYList.Remove(i);
+						return;
+						}
+					}
+				}
+			C32LOG2(KC32Dealer,_L("CC32ThreadManager::UpdateThreadManagerOnCSYLoadFailure: Failed CSY (%S) was for default thread but could not find. Will panic in debug."),&aCSYFilename);
+			__ASSERT_DEBUG(0,User::Invariant());
+			}
+		}
+	else
+		{
+		C32LOG2(KC32Dealer,_L("CC32ThreadManager::UpdateThreadManagerOnCSYLoadFailure: Failed CSY record (%S) was not found so assuming another request deleted it"),&aCSYFilename);
+		}
+	}
+
+
+CSerial* CC32ThreadManager::FindSerialObjectByGlobalIndex(const TInt aIndex) const
+// aIndex is the array position the CSY would be in if it were in the old portmanager structure
+// this is now the position of the csy in the active array
+// index should be valid so panic if not
+	{
+		
+	TInt absIdx = 0;
+	while (absIdx < iCSYList.Count())
+		{
+		if (iCSYList[absIdx]->IsLoaded())
+			{
+			if (iCSYList[absIdx]->LoadIndex()==aIndex)
+				{
+				return iCSYList[absIdx]->GetCSYHandle();
+				}
+			else
+				{
+				absIdx++;
+				}
+			}
+		else
+			{
+			absIdx++;
+			}
+		}
+		
+	C32LOG2(KC32Warning,_L8("FindSerialObjectByGlobalIndex - Panicking due to index out of range: %d"),aIndex);
+	__ASSERT_ALWAYS(0,Fault(EBadState));
+	
+	return NULL;
+	}
+	
+const HBufC8* CC32ThreadManager::GetModuleName(TWorkerId aWorkerId)
+// aWorkerId assumed to be valid
+	{
+	return iC32ThreadList[aWorkerId]->ModuleName();
+	}
+	
+
+#ifdef __FLOG_ACTIVE
+void CC32ThreadManager::DumpThreadInfoAndCSYLists()
+	{
+	//log in Detail mode only
+	
+	C32LOG(KC32Detail,_L8("---- Dump of C32 Thread Manager ----"));
+	C32LOG2(KC32Detail,_L8("Default Thread: %d"),iDefaultThreadIndex);
+	TBuf8<KMaxFullName> shortName8; // TPortName is, rather annoyingly given it probably never needs to be, a 16-bit descriptor
+	
+	for (TInt i=0;i < iCSYList.Count();i++)
+		{
+		shortName8.Copy(iCSYList[i]->GetPortName());
+		C32LOG8(KC32Detail,_L8("CSY List [%d].CSY Name:%S,ShrtNm:%S,ThrdID:%d,PlyrHdl:%x,Active:%d,Idx:%d"),i,iCSYList[i]->GetCSYName(),&shortName8,iCSYList[i]->WorkerId(),iCSYList[i]->GetCSYHandle(),iCSYList[i]->IsLoaded(),iCSYList[i]->LoadIndex());
+		}
+
+	TBool globalsExist = Dealer()->WorkerDataGlobalsExist();
+	if (globalsExist)
+		{
+		C32LOG(KC32Detail,_L8("Thread List start."));		
+		}
+	else
+		{
+		C32LOG(KC32Detail,_L8("Thread List start. Still booting so don't know player thread status yet."));
+		}
+		
+	for (TInt k=0;k < iC32ThreadList.Count(); k++)
+		{
+		if (iC32ThreadList[k]==NULL)
+			{
+			C32LOG2(KC32Warning,_L8("DumpThreadInfo: Internal check failure: Found NULL record at WorkerId: %d"),k);
+#ifdef _DEBUG
+			Fault(EBadState); // panic
+#endif
+			}
+		else if (iC32ThreadList[k]->ModuleName()->CompareF(KNullDesC8)==0)
+			{
+			if (k==0)
+				{
+				C32LOG(KC32Detail,_L8("Thread List[0]:Blank (This is normal)"));
+				}
+			else
+				{
+				C32LOG2(KC32Warning,_L8("DumpThreadInfo: Internal check failure: Found blank record for a player at WorkerId: %d"),k);
+				C32LOG1(KC32Warning,_L8("DumpThreadInfo: This indicates missing cmi file for this workerId. Continuing since this could be a test scenario."));
+				// there is no panic here since we allow blanks in case user is mix-and-matching csys to threads
+				}
+			}
+		else
+			{
+			if (globalsExist)
+				{
+				C32LOG4(KC32Detail,_L8("Thread List[%d]:Name:%S. Running:%d"),k,iC32ThreadList[k]->ModuleName(),Dealer()->WorkerExists(k));
+				}
+			else
+				{
+				C32LOG3(KC32Detail,_L8("Thread List[%d]:Name:%S. Dealer still starting so Running Status unknown."),k,iC32ThreadList[k]->ModuleName());
+				}
+			}
+		}
+
+	C32LOG(KC32Detail,_L8("---- End Dump of C32 Thread Manager ----"));
+	
+	
+	
+	}
+#endif	
+
+
+void CC32ThreadManager::LoadCMIDataL(const TDesC8& aDealerIniData)
+// load all the data from the CMI files.
+// aCSYList provides us with the data for the dealer, but we have to query the Configurator for all 
+// the data for the player cmis.
+	{
+
+	TInt ret;
+	TRSIter iter;
+
+
+	TCFModuleName moduleName;    // as specified in CMI file
+	RArray<TInt>		numCSYsPerCMI;  // used if we find two CSYs the same. Filled in as each
+	                                    // cmi processed so that index of element is threadnum, 
+	                                    // and value is number of CSYs in that threadnum.
+	CleanupClosePushL(numCSYsPerCMI);
+	
+	TBool skipThisCMI;    // the CMI is a dealer or somehow invalid beyond redemption
+	
+	CC32ParsedIniData* iniDataParser;
+
+	RBuf8 iniData;      // to accept data from the configurator
+	TInt iniBufSize;   // tracks size of ini data buffer as we grow it
+	
+	RRsConfigurator cfgSvr;
+	
+	User::LeaveIfError(cfgSvr.Connect());
+	CleanupClosePushL(cfgSvr); 
+	
+
+	iniBufSize=KIniDataBufSizeIncrement;
+	iniData.CreateL(iniBufSize);
+	iniData.CleanupClosePushL();
+	
+	CC32ThreadInfo* newThreadRec;
+	CC32ThreadInfo* blankThreadRec; // we used to insert NULLs, but inserting this blank is a bit safer/easier to work with.
+	
+	// KMaxTUint16 magic value indicates no default thread has been identified - need to use unsigned value because
+	// TWorkerID is now an unsigned type.
+	iDefaultThreadIndex = KMaxTUint16;
+	
+	// process the dealer's own cmi data. Will leave if the cmi data is bad, unless
+	// we are the dealer.
+	iniDataParser = CC32ParsedIniData::NewL(aDealerIniData,ETrue);
+	CleanupStack::PushL(iniDataParser);
+	
+
+	// add a record to the threadlist so that we have index 0 - it is irrelevant what we put in here
+	// so we will make the string an empty string since we won't ever use it - Dealer is always running!
+	// It has to be a valid string (so not null) otherwise the comparison (CompareThreadInfo) fails the type check.
+	blankThreadRec = CC32ThreadInfo::NewL(KNullDesC8);
+	ret = iC32ThreadList.Append(blankThreadRec); //transfer ownership
+	if (ret != KErrNone)
+		{
+		delete blankThreadRec;
+		User::Leave(ret);
+		}
+	blankThreadRec=NULL;
+	
+	__ASSERT_DEBUG(iC32ThreadList.Count()==1,Fault(EBadState));
+	
+	if (iniDataParser->IsDefaultThread())
+		{
+		iDefaultThreadIndex = KMainThreadId;
+		}
+		
+	
+	
+	for (TInt i=0 ; i < iniDataParser->NumCSYs(); i++)
+		{
+		TBuf8<KMaxFileName> csyFilename;
+		csyFilename.Copy(iniDataParser->iCSYs[i]);
+		TInt r=csyFilename.LocateReverse('.');
+	 	if (r==KErrNotFound)
+			{
+			if (csyFilename.Length()>KMaxFileName - (TInt)KCSYExtension8.iTypeLength)
+				{
+				C32LOG2(KC32Warning,_L("LoadCMIDataL: Corrupt [IniData] section: Dealer has a CSY that is too big: %S. Will panic in debug."),&iniDataParser->iCSYs[i]);
+				__ASSERT_DEBUG(0,Fault(EBadIniData));
+				_LIT8(KCSYDudName, "BadCsy" );
+				csyFilename.Copy(KCSYDudName());  // continue with a dud record
+				}
+			csyFilename.Append(KCSYExtension8);
+			}
+		
+		CCSYInfo* csyInfoRec = CCSYInfo::NewL(csyFilename, KMainThreadId);
+		CleanupStack::PushL(csyInfoRec);
+		
+		ret = iCSYList.InsertInOrder(csyInfoRec, TLinearOrder<CCSYInfo>(&CCSYInfo::CompareCSYInfo));
+		
+		if (ret == KErrAlreadyExists)
+			{
+			C32LOG2(KC32Warning,_L8("LoadCMIDataL: Corrupt [IniData] section: Dealer has the same CSY listed twice: %S"),&iniDataParser->iCSYs[i]);
+#ifdef _DEBUG
+			Fault(EBadIniData); // panic
+#endif
+
+			// ignore and carry on
+			CleanupStack::PopAndDestroy(csyInfoRec); // dont need duplicate further
+			}
+		else if (ret != KErrNone)
+			{
+			User::Leave(ret);
+			} 
+		else
+			{
+			// ownership transferred
+			CleanupStack::Pop(csyInfoRec);
+			}
+		}
+
+	CleanupStack::PopAndDestroy(iniDataParser);
+
+	
+	// iterate through all the CMI files
+	// Any CMI data sections that don't have "Role=Player" are ignored. Dealer CMIs can be ignored
+	// since we processed our dealer's cmi above.
+
+	// If we find two CMI files with the same module name we fault.
+	// The order we find them is the order that Configurator supplies them to us - it is probably alphabetical.
+	// If we see the wildcard twice, we process it only the first time.
+	// If we do not see the wildcard we make the first player the wildcard thread, or the dealer if no players.
+	// If a player has no CSYs, we ignore it.
+	
+	// We do this in one pass:
+	// 1. Read records from each CMI file as they come and add them to the structure. If the thread has no number,
+	//    or a duplicate number, or "0" (which should be the dealer) we fault.
+	//    After each
+	//    CMI file is complete we record the total number of CSYs added for that thread. Before adding
+	//    each CSY we scan the list to ensure that CSY has not already been added. If so, then we
+	//    move it to whichever of the two threads it is allocated to that has the least number of CSYs.
+	//
+	// As a small efficiency gain, since we may be searching the CSY names many times we order the 
+	// CSY list by CSY name. This order is not preserved once we've finished parsing the CMI files - ie,
+	// further discovered csys through loading are simply appended to the end.
+	// An efficincy improvement would be to parse the ini data sequentially. At the moment we reuse the commsfw ini
+	// file parsing routine but this would re-read the data each time.
+	// Also, a bit of string copying goes on so this could potentially be reduced.
+	
+	
+	while ( cfgSvr.EnumerateModules(_L8("C32SerComms"),iter,moduleName) == KErrNone)
+		{
+	
+		//keep enlarging inidata buffer until we get it big enough
+		ret=cfgSvr.GetModuleIniData(moduleName, iniData, iniBufSize);
+		if (ret==KErrOverflow)
+			{
+			iniData.ReAllocL(iniBufSize);
+			ret=cfgSvr.GetModuleIniData(moduleName, iniData, iniBufSize);
+			}
+		User::LeaveIfError(ret);
+
+		skipThisCMI=EFalse;
+		
+		// if it leaves we check it was due to corrupt data and continue since we can cope
+		// with all corrupt data cases
+		C32LOG2(KC32Bootup,_L8("LoadCMIDataL: Parsing inidata for module %S"),&moduleName);
+		TRAP(ret,iniDataParser = CC32ParsedIniData::NewL(iniData,EFalse));
+		CleanupStack::PushL(iniDataParser);
+
+		
+		if ((ret != KErrNone) && (ret != KErrCorrupt))
+			{
+			User::Leave(ret);
+			}
+		else
+			{
+			__ASSERT_DEBUG(iniDataParser,Fault(ESvrStartServer));
+			}
+
+		if (iniDataParser->IsPlayer())
+			{
+			newThreadRec = CC32ThreadInfo::NewL(moduleName);
+			CleanupStack::PushL(newThreadRec);
+			
+			if (iC32ThreadList.Find(newThreadRec, TIdentityRelation<CC32ThreadInfo>(&CC32ThreadInfo::CompareThreadInfo)) != KErrNotFound)
+				{
+				// unfortunately there is a rare circumstance where we may miss a duplicate. If the dealer
+				// is not part of the group, and then a member in the group has
+				// the same name, we won't spot this. only configurator can detect this.
+				C32LOG(KC32Warning,_L8("LoadCMIDataL: Corrupt IniData found - duplicate module name."));
+#ifdef _DEBUG
+				Fault(EBadIniData); // panic
+#endif
+				User::Leave(KErrCorrupt);
+				}
+				
+			if ((iniDataParser->NumCSYs() < 1) && !iniDataParser->IsDefaultThread())
+				{
+				C32LOG(KC32Warning,_L8("LoadCMIDataL: Corrupt [IniData] section: Player has no CSY list."));
+#ifdef _DEBUG
+				Fault(EBadIniData); // panic
+#endif
+				skipThisCMI=ETrue;
+				C32LOG(KC32Bootup,_L8("Skipping CMI file due to no CSYs found"));
+				}
+			
+			
+		
+			if (!skipThisCMI)
+				{
+				// workerID range is checked in parser so we don't re-check here
+				
+							
+				if ((iDefaultThreadIndex <= KMainThreadId) && (iniDataParser->IsDefaultThread()))
+					{
+					// if dealer was default, we override here since we now have a default player
+					iDefaultThreadIndex = iniDataParser->WorkerId();
+					}
+					
+				if (iniDataParser->WorkerId() >= iC32ThreadList.Count())
+					{
+					// expand the array if necessary
+					// these elements may be filled in later
+					while (iniDataParser->WorkerId() > iC32ThreadList.Count())
+						{
+						blankThreadRec = CC32ThreadInfo::NewL(KNullDesC8);
+						ret = iC32ThreadList.Append(blankThreadRec); //transfer ownership
+						if (ret != KErrNone)
+							{
+							delete blankThreadRec;
+							User::Leave(ret);
+							}
+						blankThreadRec=NULL;
+						}
+					iC32ThreadList.AppendL(newThreadRec);
+					CleanupStack::Pop(newThreadRec); //ownership transferred
+					} 
+				else
+					{
+					// we are using up an existing element
+					if (iC32ThreadList[iniDataParser->WorkerId()]->ModuleName()->CompareF(KNullDesC8)!=0)
+						{
+						C32LOG2(KC32Warning,_L8("LoadCMIDataL: Corrupt IniData found - player workerId is a duplicate: %d."),iniDataParser->WorkerId());
+#ifdef _DEBUG
+						Fault(EBadIniData); // panic
+#endif
+						User::Leave(KErrCorrupt);
+						}
+					else
+						{
+						delete iC32ThreadList[iniDataParser->WorkerId()]; //blank record
+						iC32ThreadList[iniDataParser->WorkerId()] = newThreadRec;
+						CleanupStack::Pop(newThreadRec); //ownership transferred
+						}
+					}
+					
+				if (iniDataParser->WorkerId() >= numCSYsPerCMI.Count())
+					{
+					// expand the array if neceesary
+					while (iniDataParser->WorkerId() > numCSYsPerCMI.Count())
+						{
+						numCSYsPerCMI.AppendL(-1);
+						}
+					numCSYsPerCMI.AppendL(iniDataParser->NumCSYs());
+					}
+				else
+					{
+					if (numCSYsPerCMI[iniDataParser->WorkerId()]>0)
+						{
+						C32LOG4(KC32Warning,_L8("LoadCMIDataL: Internal check failure: attempted to add CSYs for workerID twice. WorkerId: %d, CurrNumCSYs: %d, NewNumCSYs: %d"),iniDataParser->WorkerId(),numCSYsPerCMI[iniDataParser->WorkerId()],iniDataParser->NumCSYs());
+#ifdef _DEBUG
+						Fault(EBadState); // panic
+#endif
+						User::Leave(KErrCorrupt);
+						}
+					else
+						{
+						numCSYsPerCMI[iniDataParser->WorkerId()]=iniDataParser->NumCSYs();
+						}
+					}
+					
+				for (TInt i=0 ; i < iniDataParser->NumCSYs(); i++)
+					{
+					TBuf8<KMaxFileName> csyFilename;
+					csyFilename.Copy(iniDataParser->iCSYs[i]);
+					TInt r=csyFilename.LocateReverse('.');
+				 	if (r==KErrNotFound)
+						{
+						if (csyFilename.Length()>KMaxFileName - (TInt)KCSYExtension8.iTypeLength)
+							{
+							C32LOG2(KC32Warning,_L("LoadCMIDataL: Corrupt [IniData] section: Player has a CSY that is too big: %S. Will panic in debug."),&iniDataParser->iCSYs[i]);
+							__ASSERT_DEBUG(0,Fault(EBadIniData));
+							_LIT8(KCSYDudName, "BadCsy" );
+							csyFilename.Copy(KCSYDudName());  // continue with a dud record
+							}
+						csyFilename.Append(KCSYExtension8);
+						}
+					
+					CCSYInfo* csyInfoRec = CCSYInfo::NewL(csyFilename, iniDataParser->WorkerId());
+					CleanupStack::PushL(csyInfoRec);
+					
+					ret = iCSYList.InsertInOrder(csyInfoRec, TLinearOrder<CCSYInfo>(&CCSYInfo::CompareCSYInfo));
+					if (ret == KErrAlreadyExists)
+						{
+						// duplicate discovered
+						ret = iCSYList.FindInOrder(csyInfoRec, TLinearOrder<CCSYInfo>(&CCSYInfo::CompareCSYInfo));
+						if (numCSYsPerCMI[iniDataParser->WorkerId()] > numCSYsPerCMI[iCSYList[ret]->WorkerId()])
+							{
+							C32LOG4(KC32Bootup,_L8("Reallocating ThreadId for CSY %S [%d -> %d] due to duplicate appearance in CMI files"),iCSYList[ret]->GetCSYName(),iCSYList[ret]->WorkerId(),iniDataParser->WorkerId());
+							numCSYsPerCMI[iCSYList[ret]->WorkerId()]--;
+							iCSYList[ret]->SetWorkerId(iniDataParser->WorkerId());
+							}
+						else
+							{
+							C32LOG3(KC32Bootup,_L8("CSY %S appears in two CMI files. Leaving in current thread (%d) due to equal CSY balance across threads."),iCSYList[ret]->GetCSYName(),iCSYList[ret]->WorkerId());
+							numCSYsPerCMI[iniDataParser->WorkerId()]--;
+							}
+						CleanupStack::PopAndDestroy(csyInfoRec); // dont need duplicate further
+						}
+					else if (ret != KErrNone)
+						{
+						User::Leave(ret);
+						} 
+					else
+						{
+						// ownership transferred
+						CleanupStack::Pop(csyInfoRec);
+						}
+			
+					}
+				} // if skip
+			else
+				{
+				CleanupStack::PopAndDestroy(&newThreadRec);
+				}
+			
+			} //if is player
+			CleanupStack::PopAndDestroy(iniDataParser);
+	
+		}//while enumerate modules
+	
+		
+	if (iDefaultThreadIndex == KMaxTUint16)
+		{
+		// try to find any usable players for the default. otherwise, we use dealer.
+		TInt k=TC32WorkerThreadPublicInfo::EFirstPlayerThread;  // 
+		while (k < iC32ThreadList.Count())
+			{
+			if (iC32ThreadList[k]!=blankThreadRec)
+				{
+				iDefaultThreadIndex = k;
+				}
+			k++;
+			}
+		if (iDefaultThreadIndex == KMaxTUint16)
+			{
+			iDefaultThreadIndex = KMainThreadId;
+			}
+		}
+				
+	CleanupStack::PopAndDestroy(&iniData);
+	CleanupStack::PopAndDestroy(&cfgSvr);
+	CleanupStack::PopAndDestroy(); // numCSYsPerCMI
+	 
+
+
+#ifdef __FLOG_ACTIVE
+	DumpThreadInfoAndCSYLists();
+#endif
+
+	}
+
+void CC32ThreadManager::SetDefaults()
+// called if the initialisation of the threadmanager goes wrong (LoadCMIDataL leaves), which is probably when
+// there is no memory. So this function sets up the thread manager into a state so that it is still
+// internally consistent and able to load modules so C32 can keep booting. If memory is available
+// by the time a module load request is received, it can be loaded successfully.
+	{
+	iDefaultThreadIndex = KMainThreadId;
+	
+	// by destroying whatever is in the csylist, all CSYs will default to the main thread
+	iCSYList.ResetAndDestroy();
+
+	// we don't need the thread list anymore - all csy load attempts will go to default thread
+	// so this list will never be checked. If a player is loading at boot as well,
+	// the bind at that stage will see the list is destroyed and the player will just sit there unused.
+	iC32ThreadList.ResetAndDestroy();
+	
+	// iCPMLoader will be empty at this point, so we leave it null.
+	}
+
+
+//
+//	CC32Dealer
+//
+
+
+CC32Dealer* CC32Dealer::NewL(CC32WorkerThread* aOwnerThread, const TDesC8& aDealerIniData)
+	{
+	CC32Dealer* self = new(ELeave) CC32Dealer(aOwnerThread);
+	CleanupStack::PushL(self);
+	self->ConstructL(aDealerIniData);
+	CleanupStack::Pop(self);
+	return self;
+	}
+	
+CC32Dealer::CC32Dealer(CC32WorkerThread* aOwnerThread) 
+: CC32Server(aOwnerThread, EPriority), 
+iOwnerThread(aOwnerThread)
+	{
+	}
+
+// aCSYList is the CSY list as extracted from the ini data that the CWorkerThread receives from the
+// configurator.
+void CC32Dealer::ConstructL(const TDesC8& aDealerIniData)
+	{
+	// set up the thread manager, which will load the cmi data
+	// if this fails we have two possible methods of recovery.
+	// The first is to attempt to set some defaults, which at least allows c32 to provide service
+	// If this fails too (possibly low memory) then we mark the Dealer as having failed to start
+	// which then means it will not start its server.
+
+	TRAPD(ret,iThreadManager = CC32ThreadManager::NewL(this));
+	if (ret!=KErrNone)
+		{
+		C32LOG2(KC32Bootup,_L8("CC32Dealer::ConstructL: Could not create thread manager so setting iFailedStartup TRUE. Ret=%d"),ret);
+		iFailedStartup=ETrue;
+		} 
+	else
+		{
+		
+		TRAP(ret,iThreadManager->LoadCMIDataL(aDealerIniData));
+		if (ret != KErrNone)
+			{
+			C32LOG2(KC32Bootup,_L8("CC32Dealer: CMI data load left with %d. Ignoring CMI files and defaulting to loading all CSYs into main thread."),ret);
+			iThreadManager->SetDefaults();
+			}
+		
+		}
+	
+	
+	if(!iFailedStartup)
+		{
+		iWorkerDataGlobals = CC32WorkerDataGlobals::NewL();
+
+		TC32WorkerThreadRegister& mainProperties = *WorkerDataGlobals().GetWorkerGlobals(TC32WorkerThreadPublicInfo::EMainThread);
+		// players set their heap when they get introduction from main thread, but
+		// as this does not happen for main thread we set main thread's heap here separately
+		mainProperties.iHeap = &User::Heap();
+		mainProperties.iDealer = this;
+		if(!iOwnerThread->Player())
+			{
+			mainProperties.iPlayer = iOwnerThread->Player();	
+			}
+
+#ifdef _DEBUG
+		if(iOwnerThread->AllocFailType() != RAllocator::ENone)
+			{
+			WorkerDataGlobals().GetWorkerGlobals(TC32WorkerThreadPublicInfo::EMainThread)->iHeap->__DbgSetAllocFail(iOwnerThread->AllocFailType(), iOwnerThread->AllocFailRate());
+			}
+#endif
+
+		C32LOG1(KC32Dealer,_L8("calling inherited ConstructL()"));
+		inherited::ConstructL(iThreadManager);
+		}
+	}
+
+
+/**
+When deleting a session it will be in response to a disconnect from the client, so we
+signal that to the session. If this thread is in the process of shutting down we need to
+check whether it was just waiting for this session (if this was the last one) and if this is
+the case we can initiate the asynchronous process of shutting down the worker thread.
+*/
+void CC32Dealer::DeleteSession(CCommSession* aSession) 
+	{ 
+	// If we ever see evidence of shutdowns nested inside active calls (ie "this" deleted prematurely then consider
+	// switching handling to use a CAsyncOneShot)
+	aSession->CompleteDisconnect();
+	if(WorkerThread().ShuttingDown() && CanShutdown())
+		{
+		WorkerThread().SetDealerShutdownComplete(ETrue);
+		WorkerThread().MaybeTriggerThreadShutdownCallback();
+		}
+	}
+
+
+/**
+Iterate through all sessions and make sure they deploy the SubSessionProcessor on each owned sub-session.
+It is not known here what the SubSessionProcessor actually does as it is implemented by the caller.
+*/
+void CC32Dealer::ProcessSubSessions(TWorkerId aPeerId, CCommSession::TSubSessionProcessor aSubSessionProcessor, TAny* aPtr)
+	{
+	iSessionIter.SetToFirst();
+	CCommSession* sess;
+	while((sess = static_cast<CCommSession*>(iSessionIter++)) != NULL)
+		{
+		sess->ProcessSubSessions(aPeerId, aSubSessionProcessor, aPtr);
+		}
+	}
+
+TInt CC32Dealer::SubsessionCountInPlayer(TWorkerId aPeerId)
+	{
+	TInt numSubSessions = 0;
+	ProcessSubSessions(aPeerId, CCommSession::CountSubSessions, &numSubSessions);
+	return numSubSessions;
+	}
+	
+CC32Dealer::~CC32Dealer()
+	{
+	C32LOG1(KC32Shutdown,_L8("CC32Dealer::~CC32Dealer()."));
+
+#ifdef __FLOG_ACTIVE
+	iSessionIter.SetToFirst();
+	CCommSession* pSession;
+	// we log any sessions remaining
+	while((pSession=static_cast<CCommSession*>(iSessionIter++))!=NULL)
+		{
+		C32LOG2(KC32Shutdown, _L8("CC32Dealer::~CC32Dealer(): Session(%08x): Remaining. WARNING: this will cause objects to leak."), pSession);
+		}
+#endif
+	
+	iParkedRequests.Close();
+	delete iThreadManager;
+	iThreadManager = NULL;
+
+	if (WorkerDataGlobalsExist())    //won't exist if we've OOM'd during construction and reached here during cleanup.
+		{
+		for(TWorkerId id = TC32WorkerThreadPublicInfo::EMaxWorkerThreadId; id >=  TC32WorkerThreadPublicInfo::EFirstPlayerThread; --id)
+			{
+			if(WorkerExists(id))
+				{
+				FreeWorkerReferences(id);
+				}
+			}
+		}
+
+		
+	delete iWorkerDataGlobals;
+	iWorkerDataGlobals = NULL;
+	C32LOG(KC32Shutdown,_L8("CC32Dealer::~CC32Dealer() complete."));
+
+	}
+
+void CC32Dealer::ShutdownIfReady()
+	{
+	ASSERT(iOwnerThread->WorkerId() == TC32WorkerThreadPublicInfo::EMainThread); // protect against this being called for a dealerPlayer
+	C32LOG2(KC32Shutdown, _L("CC32Dealer::ShutdownIfReady() - iSessionShutdownComplete = %d"), iSessionShutdownComplete );
+	if(iSessionShutdownComplete)
+		{
+		iOwnerThread->TriggerThreadShutdownCallback();
+		}
+	}
+
+
+/** 
+Used during binding when the Dealer receives a introduction response message from a worker.
+The Dealer will set-up housekeeping datastructures for the worker
+@see TC32WorkerMsg::EMainIntroductionResp
+*/
+
+void CC32Dealer::ProcessWorkerIntroductionResponse(const TC32WorkerMainIntroductionRespMsg& aMsg)
+	{
+	const TC32WorkerThreadPublicInfo& msgInfo = aMsg.WorkerInfo();
+	C32LOG2(KC32Dealer, _L("Process Worker Introduction Response for worker: %d"),msgInfo.iWorkerId);
+	ASSERT(msgInfo.iWorkerId > TC32WorkerThreadPublicInfo::EMainThread && msgInfo.iWorkerId <= TC32WorkerThreadPublicInfo::EMaxWorkerThreadId);
+	ASSERT(!WorkerDataGlobals().WorkerPresent(msgInfo.iWorkerId));
+	TC32WorkerThreadRegister& workerReg = *WorkerDataGlobals().GetWorkerGlobals(msgInfo.iWorkerId);
+	static_cast<TC32WorkerThreadPublicInfo&>(workerReg) = msgInfo;
+	workerReg.iDealer = workerReg.iWorker->Dealer();
+	workerReg.iPlayer = workerReg.iWorker->Player();
+	RemovePendingIntroductionResponse();
+
+	// process response message. Process parked request here
+    ProcessParkedRequest();
+
+#ifdef _DEBUG
+  	// We only switch on the configured simulated allocation failures once the bindings are complete, because
+  	// it's too hard to recover from them earlier. This is a regrettable but hopefully harmless limitation in
+  	// practice, ie if we're OOM during boot then recovery strategies aren't obvious.
+  	workerReg.iHasGlobalAllocFails = aMsg.FailType() != RAllocator::ENone;
+  	if(workerReg.iHasGlobalAllocFails)
+  		{
+  		workerReg.iHeap->__DbgSetAllocFail(aMsg.FailType(), aMsg.FailRate());
+  		}
+#endif
+
+	}
+
+void CC32Dealer::DeleteCPMLoader(CommsFW::TWorkerId aWorkerId)
+	{
+	// remove waiting LoadCPM object
+	iThreadManager->ProcessModuleLoadSuccess(aWorkerId);
+	}
+
+void CC32Dealer::RemoveParkedRequestsOnSessionClose(CCommSession* aSession)
+	{
+	const TInt parkedCount = iParkedRequests.Count();
+	for(TInt i = parkedCount - 1; i >= 0; --i)
+ 		{
+		const TSessionMessagePair& pair = iParkedRequests[i];
+		if(pair.iSession == aSession)
+			{
+			C32LOG2(KC32Dealer, _L8("CC32Dealer::RemoveParkedRequest() - found parked for closing session %08x "), aSession);
+			SafeComplete(pair.iMessage, KErrSessionClosed);		// session is being closed
+			iParkedRequests.Remove(i);
+		 	}
+ 		}
+	}
+
+void CC32Dealer::ProcessParkedRequest()
+	{
+	 const TInt parkedCount = iParkedRequests.Count();
+	 C32LOG2(KC32Bootup, _L8("CC32Dealer::ProcessParkedRequestL() - %d parked messages to process"), parkedCount);
+	 for(TInt i = parkedCount - 1; i >= 0; --i)
+ 		 {
+ 		 const TSessionMessagePair& pair = iParkedRequests[i];
+ 		 // Check that the session still exists
+ 		 iSessionIter.SetToFirst();
+ 		 CSession2* ss;
+ 		 while((ss = iSessionIter++) != NULL && ss != pair.iSession)
+	 		 {
+	 		 }
+		 if(ss && !pair.iMessage.IsNull())
+	 		 {
+	 		 C32LOG2(KC32Bootup, _L8("CC32Dealer::ProcessParkedRequestL() - parked messages for sess %08x"), ss);
+	 		 RMessage2 tempMsg = pair.iMessage;
+	 		 iParkedRequests.Remove(i);		// remove parked request
+		 	 TRAPD(res, ss->ServiceL(tempMsg));
+		 	 if(res != KErrNone)
+				{
+		 	 	SafeComplete(tempMsg, res);
+		 	 	}
+	 		 }
+ 		 }
+	 }
+
+TInt CC32Dealer::ParkRequest(CCommSession* aSession, const RMessage2& aMessage)
+	 {
+	 __ASSERT_DEBUG(aMessage.Function()==ECommLoadCommModule,Fault(EBadState,_L8("CC32Dealer::ParkRequest - Attempt to park a non-moduleLoad request. Panicking. Session(%08x) Message(%08x)"), aSession, aMessage.Handle()));
+	 
+	 C32LOG3(KC32Bootup, _L8("CC32Dealer::ParkRequest Session(%08x) Message(%08x)"), aSession, aMessage.Handle());
+	 return iParkedRequests.Append(TSessionMessagePair(aSession, aMessage));
+	 }
+
+
+TInt CC32Dealer::LoadCPMOnLoadCommModule(CommsFW::TWorkerId aWorker)
+	 {
+	 return iThreadManager->RequestLoadModule(aWorker);
+	 }
+	 
+	 
+void CC32Dealer::ProcessFailedPlayerLoad(CommsFW::TWorkerId aWorker)
+// called from CPMLoader if configurator reported load failed.
+// gives dealer a chance to do any cleanup.
+	{
+	// sweep parked requests array and remove any CSY load requests that were hoping to be loaded into the
+	// failed player
+	// They will be completed with appropriate KErrNoMemory (most likely reason for player load to fail)
+	// error code. Transversing the array is somewhat expensive since we're reprocessing all queued messages.
+	TFileName csyfilename;
+	C32LOG2(KC32Dealer,_L8("CC32Dealer:ProcessFailedPlayerLoad: Sweeping parked requests array to complete(w/KErrNoMemory) and remove any waiting on failed player %d"),aWorker);
+	const TInt parkedCount = iParkedRequests.Count();
+	for (TInt i = parkedCount -1 ;i >= 0; --i)
+		{
+	    const TSessionMessagePair& pair = iParkedRequests[i];
+		TInt res = pair.iMessage.Read(0,csyfilename);
+		
+		TBuf8<KMaxFileName> fileName8;
+		fileName8.Copy(csyfilename);
+		
+		
+		if (res==KErrNone)
+			{
+			TInt r=csyfilename.LocateReverse('.');
+		 	if (r==KErrNotFound)
+				{
+				// length was checked when message first processed before queuing
+				__ASSERT_DEBUG(csyfilename.Length() <= KMaxFileName - (TInt)KCSYExtension.iTypeLength, Fault(EBadState,_L("CC32Dealer::ProcessFailedPlayerLoad: Message(%08x) filename is too long. Panicking."),&pair.iMessage));
+				csyfilename.Append(KCSYExtension);
+				}
+
+			CommsFW::TWorkerId worker;
+			TBool found = iThreadManager->FindThreadByFileName(fileName8,worker);
+			
+
+			
+			if (!found)
+				{
+				// CSY must have been destined for the default thread
+				worker = iThreadManager->DefaultThread();
+				}
+			
+			if (worker == aWorker)
+				{
+				C32LOG3(KC32Detail,_L8("CC32Dealer:ProcessFailedPlayerLoad: Completing and removing message(%08x) at index %d"),&pair.iMessage,i);
+				SafeComplete(pair.iMessage,KErrNoMemory);
+				iParkedRequests.Remove(i);
+				}
+			
+			}
+		else
+			{
+			__ASSERT_DEBUG(res==KErrNone,Fault(EBadState,_L8("CC32Dealer::ProcessFailedPlayerLoad: Could not read filename from Message at iParkedRequests[%d]. Res=%d Panicking."),i,res));
+			// in release mode just ignore mesg and keep going
+			}
+
+		}
+		
+	}
+
+
+/** ThreadManager is updated with CSerial* and PortPrefix. PortPrefix is mapped to CSYFileName 
+* and this mapping then provides CSYFileName, which is searched in CSYcontainer in session to 
+* test whether the CSY in question is loaded in this session or not - this mapping is used while
+* closing Comm Module
+*/
+void CC32Dealer::ProcessLoadCommModuleSuccessResponse(const RMessage2& aMessage, CSerial* aSerial)
+// called from workerthread dispatchMsg, or directly if load is for co-resident player
+	{
+	/** Map find CSYFileName in ThreadManager and update PortPrefix and CSerial* field against it. In case of duplication, 
+	* the CSerial* will be updated again though it'll be point to the same physical address
+	*/
+	
+	TFileName csyFilename;
+	Read(0, aMessage, csyFilename, 0);
+	TInt r=csyFilename.LocateReverse('.');
+ 	if (r==KErrNotFound)
+		{
+		csyFilename.Append(KCSYExtension);
+		}
+	C32LOG2(KC32Dealer, _L("CC32Dealer::ProcessLoadCommModuleSuccessResponse, insert CSerial* and PortPrefix for: %S"),&csyFilename);
+
+	iThreadManager->UpdateThreadManagerOnCSYLoad(csyFilename, aSerial->Name(), aSerial);
+	
+	// complete message in dealer after update is done in ThreadManager
+	SafeComplete(aMessage, KErrNone);
+	}
+
+void CC32Dealer::ProcessLoadCommModuleFailureResponse(const RMessage2& aMessage, TInt aFailReason)
+	{
+	TBool dummy;
+	TFileName csyFilename;
+	Read(0, aMessage, csyFilename, 0);
+	
+	TInt ret = AddCSYExtension(csyFilename,aMessage);
+	__ASSERT_DEBUG(ret == KErrNone, Fault(EBadState,_L("CC32Dealer::ProcessLoadCommModuleFailureResponse: Could not add extension to CSY filename. This is inconsistent since should not get here in such case. Panicking.")));
+		
+
+	C32LOG1(KC32Dealer, _L("CC32Dealer::ProcessLoadCommModuleFailureResponse, remove CSY from Session csy container and de-allocate memory for this csy in ThreadManager"));
+	static_cast<CCommSession*>(aMessage.Session())->RemoveCSYFromSession(csyFilename,dummy);
+	// de-allocate memory on this call
+	iThreadManager->UpdateThreadManagerOnCSYLoadFailure(csyFilename);
+	// complete message after removing csy from session CSY container and de-allocating memory
+	SafeComplete(aMessage, aFailReason);
+	}
+	
+
+TInt CC32Dealer::Read(TInt aPos, const RMessagePtr2& aMessage , TDes16& aDes, TInt aOffset)
+	{
+	C32LOG3(KC32Dealer, _L8("CC32Dealer::Read(), Pos (%d), Offset (%d)"), aPos, aOffset);
+
+	TInt ret = aMessage.Read(aPos, aDes, aOffset);
+	if (ret!=KErrNone)
+		{
+		C32LOG1(KC32Dealer, _L8("Error at the time of reading data from client"));
+		PanicClient(EBadDescriptor,aMessage);
+		}
+	return ret;
+	}
+
+void CC32Dealer::FreeWorkerReferences(TWorkerId aWorkerId)
+	{	
+	C32LOG2(KC32Shutdown, _L("CC32Dealer::FreeWorkerReferences(%d)"), aWorkerId );
+	TC32WorkerThreadRegister& properties(*WorkerDataGlobals().GetWorkerGlobals(aWorkerId));
+#ifdef _DEBUG
+	// The RootServer normally checks the heap for leaks when the module unloads
+	// but for C32 CSY modules this is commonly too early, since the Dealer (acting as PitBoss) holds
+	// its reference open until the cleanup completes. Hence here we check for 
+	// remaining allocations if we hold the last reference and if no thread 
+	// which used it died involuntarily
+	RCFSharedHeap* heap = static_cast<RCFSharedHeap*>(properties.iHeap);
+	C32LOG4(KC32Warning, _L8("~~~CC32Dealer::FreeWorkerReferences heap(%08x).AccessCount()==%d, cell count=%d"), (TUint) heap, heap->AccessCount(), heap->Count());
+	if(heap->AccessCount() <= 2)
+		{
+		if(heap->Count() > 0)
+			{
+			//C32LOG2(KRSModule,KRSModuleLeak,_L8("~~~SerComms-CC32Dealer::FreeWorkerReferences.Following leaks are from Ser-Comms C32 (more info logged under: C32SerComms *)"));
+			C32LOG1(KRSModule,KRSModuleLeak);
+			heap->LogAllocatedCells(RootServer::KLogSubSysRSModule, RootServer::KLogRSLeakTag);
+			RProperty pubsub;
+			TInt res = pubsub.Attach(RootServer::KUidCommsProcess, RootServer::KUidCommsModuleLeakCounter);
+			//No nead for cleanup stack, cannot leave before Close
+			if (res == KErrNone)
+				{
+				TInt count;
+				res =pubsub.Get(count);
+				if (res == KErrNone)
+					{
+					count += heap->Count();
+					res =pubsub.Set(count);
+					}
+				}
+			pubsub.Close();
+			if (res != KErrNone)
+				{
+				__CFLOG_1(RootServer::KLogSubSysRSModule, RootServer::KLogRSLeakTag, _L8("Unable to report leaks. Error: %d"), res);
+				}
+			// As much as anything this log line is here to make it apparent that the breakpoint above was hit
+			C32LOG1(KC32Warning, _L8("--- end of leaked cell log. If leaks not shown enable logging for: RSModule *"));
+			}
+		}
+#endif
+	properties.Clear();
+	}			
+
+/** 
+Check if we can unbind from a worker. This is only possible if the local Dealer doesn't 
+have any outstanding sub-sessions terminating in the peer and it doesn't have any sessions 
+with outstanding closes against the peer. Otherwise the channel is still needed. 
+*/
+TBool CC32Dealer::CanUnbindFromWorker(TWorkerId aWorker)
+	{
+	if (!WorkerThread().DealerByRef().Player(aWorker))
+		{
+		return ETrue;
+		}
+	if(SubsessionCountInPlayer(aWorker) == 0)
+		{
+		// Check for any sessions which have outstanding session closes against the Worker
+		iSessionIter.SetToFirst();
+		CCommSession* sess;
+		while((sess = static_cast<CCommSession*>(iSessionIter++)) != NULL)
+			{
+			if(sess->IsPlayerInDisconnectList(aWorker))
+				{
+				return EFalse;
+				}
+			}
+		return ETrue;
+		}
+	return EFalse;
+	}
+	
+
+/** 
+Check whether a worker Id is legal and a worker with that Id is installed.
+*/
+TBool CC32Dealer::WorkerExists(TWorkerId aId) const
+	{
+	return WorkerDataGlobals().WorkerPresent(aId);
+	}
+
+void CC32Dealer::SessionShutdownComplete()
+	{
+	iSessionShutdownComplete = ETrue;
+	ShutdownIfReady();
+	}
+
+
+void CC32Dealer::PostMessage(TWorkerId aWorkerId, TCFMessage& aMessage)
+	{
+	iOwnerThread->PostMessage(aWorkerId, aMessage);
+	}
+
+#ifdef _DEBUG
+  // If a heap has been configured from boot to have a failure mode then we don't override this here as
+  // the lifetime failure testing is more important than the specific test case doing a SetFailNext
+  void CC32Dealer::SetFailNextForAllHeaps(TInt aFailNext)
+  	{
+  	for(TWorkerId workerId = TC32WorkerThreadPublicInfo::EMainThread; workerId <= TC32WorkerThreadPublicInfo::EMaxWorkerThreadId; ++workerId)
+  		{
+  		const TC32WorkerThreadRegister& worker = *WorkerDataGlobals().GetWorkerGlobals(workerId);
+  		if(WorkerExists(workerId) && !worker.iHasGlobalAllocFails)
+  			{
+  			worker.iHeap->__DbgSetAllocFail((aFailNext < 0)? RAllocator::EReset: RAllocator::EFailNext, aFailNext);
+  			}
+  		}
+  	}
+#endif
+
+/** 
+Given the "local" worker thread's id (normally but not necessarily the current worker
+thread and that of another "foreign" worker thread, if they have different heaps then 
+switch heaps and return the previous one, otherwise return NULL. 
+*/
+RAllocator* CC32Dealer::MaybeSwitchHeap(TWorkerId aForeignWorkerId)
+	{
+	
+	const TC32WorkerThreadPublicInfo& foreignInfo = *WorkerDataGlobals().GetWorkerGlobals(aForeignWorkerId);
+	ASSERT(foreignInfo.iHeap);
+	RHeap* heap = &User::Heap();
+	if(heap != foreignInfo.iHeap)
+		{
+		C32LOG2(KC32Detail, _L8("CC32Dealer::MaybeSwitchHeap - Switching heap to %08x."),foreignInfo.iHeap);
+		return User::SwitchAllocator(foreignInfo.iHeap);
+		}
+	else
+		{
+		C32LOG2(KC32Detail, _L8("CC32Dealer::MaybeSwitchHeap - No heap switch happened - Heap %08x."),heap);
+		}	
+		
+	return NULL;
+	}
+
+/**
+Called by any Player/thread to add a sub-session to a session. It will switch the local heap to that 
+of the peer before performing operations on the session pointer. It is essential that the session lock is used
+around this call.
+@see CC32SubSessionIx::Lock
+@see CC32SubSessionIx::UnLock
+*/
+TInt CC32Dealer::AddSubSession(CCommSubSession* aSubSession, CCommSession* aSession, TInt& aHandle)
+	{
+	RAllocator* prevAllocator = MaybeSwitchHeap(aSession->WorkerId());
+	TInt err = aSession->iSubSessions.Add(aSubSession, aHandle);
+	C32LOG4(KC32Detail, _L8("CC32Dealer::AddSubSession(%08x, %08x, => %08x)"), aSubSession, aSession, aHandle);
+	if(prevAllocator)
+		{
+		User::SwitchAllocator(prevAllocator);
+		}
+	return err;
+	}
+
+/**
+Called by any Player/thread to remove a sub-session from a session. It will switch the local heap to that 
+of the peer before performing operations on the session pointer. It is essential that the session lock is used
+around this call.
+@see CC32SubSessionIx::Lock
+@see CC32SubSessionIx::UnLock
+*/
+void CC32Dealer::RemoveSubSession(TInt aHandle, CCommSession* aSession)
+	{
+	RAllocator* prevAllocator = MaybeSwitchHeap(aSession->WorkerId());
+	C32LOG3(KC32Detail, _L8("CC32Dealer::RemoveSubSession(%08x, %08x"), aHandle, aSession);
+	VERIFY(aSession->iSubSessions.Remove(aHandle) != NULL);
+	if(prevAllocator)
+		{
+		User::SwitchAllocator(prevAllocator);
+		}
+	}
+
+/**
+Called by any Player/thread to find handle for a sub-session. It will switch the local heap to that 
+of the peer before performing operations on the session pointer. It is essential that the session lock is used
+around this call.
+@see CC32SubSessionIx::Lock
+@see CC32SubSessionIx::UnLock
+*/
+TInt CC32Dealer::FindSubSession(CCommSubSession* aSubSession, CCommSession* aSession, TInt& aHandle)
+	{
+	RAllocator* prevAllocator = MaybeSwitchHeap(aSession->WorkerId());
+	TInt err = aSession->iSubSessions.Find(aSubSession, aHandle);
+	C32LOG4(KC32Detail, _L8("CC32Dealer::FindSubSession(%08x, %08x, => %08x)"), aSubSession, aSession, aHandle);
+	if(prevAllocator)
+		{
+		User::SwitchAllocator(prevAllocator);
+		}
+	return err;
+	}
+
+CC32Dealer* CC32Dealer::Dealer(TWorkerId aWorkerId) const
+	{
+	return WorkerDataGlobals().GetWorkerGlobals(aWorkerId)->iDealer;
+	}
+
+CC32Player* CC32Dealer::Player(TWorkerId aWorkerId) const
+	{
+	return WorkerDataGlobals().GetWorkerGlobals(aWorkerId)->iPlayer;
+	}
+
+void CC32Dealer::SendIntroductionToWorker(CommsFW::TWorkerId aWorkerId)
+	{
+	// Send the worker peer the Dealer introduction message
+	TC32WorkerMainIntroductionMsg msg(this);
+	PostMessage(aWorkerId, msg);
+	AddPendingIntroductionResponse();
+	}
+
+void CC32Dealer::AddPendingIntroductionResponse()
+	{
+	++iPendingIntroResponses;
+	}
+
+void CC32Dealer::RemovePendingIntroductionResponse()
+	{
+	--iPendingIntroResponses;
+	}
+
+/**
+This is a simple check shutting down is not possible if there are still open sessions, unless
+shutdown type is EImmediate but then this method is not called. 
+*/
+TBool CC32Dealer::CanShutdown()
+	{
+	CC32Data* globals = CC32DataInTls();
+    C32LOG2(KC32Detail, _L8("CC32Dealer::CanShutdown() \t iNumSessions = %d "), globals->iNumSessions);
+	return globals->iNumSessions <= 0;
+	}
+
+
+void CC32Dealer::ProcessShutdownRequest(CommsFW::TCFShutdownType aType)
+// Since the c32 framework mimmicks esock's but does not yet contain a DealerPlayer concept,
+// this function will be called by whichever thread sees a shutdown first.
+	{
+	TBool commenceShutdown = CanShutdown();
+
+	// If have to do now and there are still sessions then we exit anyway but suppress the heap check and log a rude message. We
+	// used to delete the sessions but that isn't safe
+	if(EImmediate==aType)
+		{
+    	commenceShutdown = ETrue;
+
+#ifdef __FLOG_ACTIVE
+		iSessionIter.SetToFirst();
+		if(iSessionIter++ != NULL)
+			{
+			TInt cnt = 0;
+			iSessionIter.SetToFirst();
+			CSession2* ss;
+			while((ss = iSessionIter++) != NULL)
+				{
+				C32LOG2(KC32Detail, _L8("<==Session(%08x): remaining"), ss);
+				++cnt;
+				}
+			C32LOG2(KC32Detail, _L8("NB! Immediate shutdown commanded but #%d client sessions remaining (bad test code?)"), cnt);
+			}
+#endif
+		}
+
+	if(WorkerThread().IsMainThread())
+		{
+		// this section will always be called by each thread since no dealerPlayer yet
+		
+		// Even the Dealer (acting as pitboss) yields to the immediate shutdown
+		if(commenceShutdown)
+			{
+			WorkerThread().DealerByRef().SessionShutdownComplete();
+			}
+		}
+	WorkerThread().SetDealerShutdownComplete(commenceShutdown);
+	}
+
+//
+// TC32WorkerThreadRegister
+//
+
+TC32WorkerThreadRegister::TC32WorkerThreadRegister()
+	{
+#ifdef _DEBUG
+  	iHasGlobalAllocFails = EFalse;
+#endif
+	}
+
+void TC32WorkerThreadRegister::Clear()
+	{
+	inherited::Clear();
+	iDealer = NULL;
+	iPlayer = NULL;
+	}
+
+//
+// CC32SubSessionIx
+//
+
+void CC32SubSessionIx::InitialiseL()
+	{
+	User::LeaveIfError(iLock.CreateLocal());
+	}
+
+CC32SubSessionIx::~CC32SubSessionIx()
+	{
+	iLock.Close(); 
+	User::Free(iIx);
+	}
+
+TInt CC32SubSessionIx::Find(CCommSubSession* aSubSession, TSubSessionHandle& aHandle) const
+	{
+	iLock.AssertLockHeld();
+	for(TInt ix = 0; ix < iSize; ++ix)
+		{
+		TEntry& e = iIx[ix];
+		if(e.iType != CCommSubSession::ENull && e.iObject == aSubSession)
+			{
+			aHandle = MakeHandle(ix, e.iMagic, e.iType);
+			return KErrNone;
+			}
+		}
+	return KErrNotFound;
+	}
+
+TInt CC32SubSessionIx::Add(CCommSubSession* aSubSession, TSubSessionHandle& aHandle)
+	{
+	iLock.AssertLockHeld();
+	if(TUint(iFreeListHead) >= TUint(iSize))
+		{
+		TInt err = ExpandArray();
+		if(err != KErrNone)
+			{
+			return err;
+			}
+		}
+	TInt ix = iFreeListHead;
+	TEntry& e = iIx[ix];
+	ASSERT(e.iType == CCommSubSession::ENull);
+	iFreeListHead = e.iNextFree;
+	ASSERT(TUint(iFreeListHead) <= TUint(iSize));
+	e.iObject = aSubSession;
+	e.iType = TUint8(aSubSession->Type());
+	TInt magic = (e.iMagic + 1) & KMagicMask;
+	e.iMagic = TUint16(magic);
+	aHandle = MakeHandle(ix, magic, e.iType);
+	++iActiveCount;
+	return KErrNone;
+	}
+
+CCommSubSession* CC32SubSessionIx::Remove(TSubSessionHandle aHandle)
+	{
+	iLock.AssertLockHeld();
+	ASSERT(TUint(iFreeListHead) <= TUint(iSize));
+	TEntry* e = At(aHandle);
+	ASSERT(e);
+	CCommSubSession* subSess = e->iObject;
+	e->iType = TUint8(CCommSubSession::ENull);
+	e->iNextFree = iFreeListHead;
+	iFreeListHead = e - &iIx[0];
+	ASSERT(TUint(iFreeListHead) < TUint(iSize));
+	--iActiveCount;
+	return subSess;
+	}
+
+CCommSubSession* CC32SubSessionIx::At(TInt aHandle, CCommSubSession::TSubSessionType aType) const
+	{
+	iLock.AssertLockHeld();
+	TEntry* e = At(aHandle);
+	if(e && (e->iType == aType || aType == CCommSubSession::EAny))
+		{
+		return e->iObject;
+		}
+	return NULL;
+	}
+
+CC32SubSessionIx::TEntry* CC32SubSessionIx::At(TSubSessionHandle aHandle) const
+	{
+	TEntry* e = 0;
+	TInt ix = IndexFromHandle(aHandle);
+	if(TUint(ix) < TUint(iSize))
+		{
+		e = &iIx[ix];
+		if(MakeHandle(ix, e->iMagic, e->iType) != aHandle)
+			{
+			e = 0;
+			}
+		}
+	return e;
+	}
+
+TInt CC32SubSessionIx::ExpandArray()
+	{
+	ASSERT(TUint(iFreeListHead) <= TUint(iSize));
+	TInt size = iSize + EIndexGranularity;
+	if(size > KIndexLimit)
+		{
+		return KErrNoMemory;
+		}
+	TEntry* ix = reinterpret_cast<TEntry*>(User::ReAlloc(iIx, size * sizeof(TEntry)));
+	if(!ix)
+		{
+		return KErrNoMemory;
+		}
+	iIx = ix;
+	iSize = size;
+	for(TInt ii = iFreeListHead; ii < size;)
+		{
+		TEntry& e = ix[ii];
+		e.iNextFree = ++ii;
+		e.iType = TUint8(CCommSubSession::ENull);
+		}
+	return KErrNone;
+	}
+
+CC32SubSessionIx::TIter::TIter(CC32SubSessionIx& aContainer)
+: iContainer(aContainer)
+	{
+	SetToFirst();
+	}
+
+void CC32SubSessionIx::TIter::SetToFirst()
+	{
+	iPos = 0;
+	}
+
+CCommSubSession* CC32SubSessionIx::TIter::operator++(TInt)
+	{
+	TSubSessionHandle dummyHandle;
+	return Next(dummyHandle);
+	}
+
+CCommSubSession* CC32SubSessionIx::TIter::Next(TSubSessionHandle& aHandle)
+	{
+	iContainer.iLock.AssertLockHeld();
+	TEntry* e = &iContainer.iIx[iPos];
+	while(iPos < iContainer.iSize)
+		{
+		TInt oldPos = iPos++;
+		if(e->iType != CCommSubSession::ENull)
+			{
+			aHandle = iContainer.MakeHandle(oldPos, e->iMagic, e->iType);
+			return e->iObject;
+			}
+		++e;
+		}
+	return NULL;
+	}
+
+
+//
+//	CC32Player class definitions
+//
+
+
+CC32Player* CC32Player::NewL(CC32WorkerThread* aOwnerThread)
+	{
+	CC32Player* self = new(ELeave) CC32Player(aOwnerThread);
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	CleanupStack::Pop(self);
+	return self;
+	}
+
+CC32Player::CC32Player(CC32WorkerThread* aOwnerThread)
+: iOwnerThread(aOwnerThread)
+	{
+	}
+
+void CC32Player::ConstructL()
+	{
+	iPortManager = CPortManager::NewL();
+	}
+
+/** 
+The Player destructor doesn't have much to do as a lot of the cleanup is done during the
+normal shutdown routines. Here the Player merely deletes all sub-sessions it owns. 
+*/
+CC32Player::~CC32Player()
+	{
+	// The object container is stored as a packed array, so working backwards through it avoids invalidating
+	// the iterator when removing entries (and as a bonus is more efficient)
+	C32LOG1(KC32Shutdown, _L8("CC32Player::~CC32Player()"));
+	for(TInt i = iSubSessions.Count() - 1; i >= 0; --i)
+		{
+		CCommSubSession* subSession = iSubSessions[i];
+		C32LOG2(KC32Shutdown, _L8("-- destroying -- %08x"), subSession );
+		// This subsession needs deletion, which we do by closing it until it disappears
+		// (generally this should only require a single close, but it's possible that the
+		// Dealer died with it additionally open)
+		ASSERT(subSession);
+		ASSERT(subSession->AccessCount() > 0);
+		// Following logic presumes that each Close() decrements AccessCount() by one;
+		// anything else is too broken for words
+		for(TInt cnt = subSession->AccessCount(); cnt > 0; --cnt)
+			{
+			CPort* p = CPortFromSubSession(subSession);		// subSession->iPort
+			p->Close();							// close CPort
+			subSession->Close();				// close CCommSubSession
+			}
+		}
+	iSubSessions.ResetAndDestroy();
+	delete iPortManager;
+	iPortManager = NULL;
+	}
+
+/**
+The Player can unbind from another worker thread if it doesn't have any sub-sessions 
+belonging to a session in the peer. 
+*/
+TBool CC32Player::CanUnbindFromWorker(TWorkerId aWorker)
+	{
+	C32LOG3(KC32Bootup, _L8("CC32Player::CanUnbindFromWorker(%d): %d subsess"), aWorker, iSubSessions.Count() );
+	for(TInt idx = iSubSessions.Count() - 1; idx >= 0; --idx)
+		{
+		CCommSubSession* ss = iSubSessions[idx];
+		C32LOG3(KC32Bootup, _L8("-- subsess %08x worker=%d"), ss, ss->Session()->WorkerId());
+		if(iSubSessions[idx]->Session()->WorkerId() == aWorker)
+			{
+			return EFalse;
+			}
+		}
+	return ETrue;
+	}
+
+/** 
+Check whether the Player is ready to shut down and if so, tells the Worker Thread who owns it. 
+*/
+void CC32Player::MaybeSetPlayerShutdownComplete(TBool aForceShutdownNow)
+	{
+	TBool shutdownNow = aForceShutdownNow || (iSubSessions.Count() == 0);
+	C32LOG4(KC32Bootup, _L8("CC32Player::MaybeSetPlayerShutdownComplete(), shutdownNow = %d [forced=%d, #subSess=%d]"), 
+		shutdownNow, aForceShutdownNow, iSubSessions.Count() );
+	WorkerThread().SetPlayerShutdownComplete(shutdownNow);
+	}
+
+/**
+If an incoming shutdown request is of type EImmediate, informs the
+Worker Thread that Player shutdown is complete, otherwise do nothing here. 
+*/
+void CC32Player::ProcessShutdownRequest(CommsFW::TCFShutdownType aType)
+	{
+	C32LOG2(KC32Bootup, _L8("CC32Player::ProcessShutdownRequest(%d)"), aType );
+	WorkerThread().SetPlayerShutdownComplete(aType == EImmediate);
+	}
+
+void CC32Player::ProcessUnLoadCommModuleMsg(CSerial* aSerial)
+	{
+	__ASSERT_DEBUG(aSerial != NULL, Fault(EBadState, _L8("NULL CSerial* passed to player !")));
+	__ASSERT_DEBUG(aSerial->AccessCount() > 0, Fault(EBadState, _L8("CC32Player::ProcessUnLoadCommModuleMsg - AccessCount < 0 before Close()")));
+	if(aSerial->AccessCount() > 0)
+		{
+		aSerial->Close();
+		}
+	else
+		{
+		C32LOG2(KC32Warning, _L8("CC32Player::ProcessUnLoadCommModuleMsg - AccessCount negative  %d, Cannot call close()"), aSerial->AccessCount() );
+		}
+	return;
+	}
+
+/**
+Write a handle back to Ptr3 of the current message
+*/
+TInt CC32Player::WriteSubSessionHandle(TInt aHandle)
+	{
+	TPckgC<TInt> pH(aHandle);
+	return iSession->Write(MSG_PRM(3), Message(),pH);
+	}
+
+// CC32Player::CloseSubSession closes a CPort so CommCancel is not used.
+void CC32Player::CloseSubSession(const RMessage2& aMessage, CCommSubSession* aSubSession)
+	{
+	ASSERT(aSubSession);
+	CPort* p = CPortFromSubSession(aSubSession);
+	C32LOG1(KC32Player, _L8("CC32Player::CloseSubSession"));
+	p->FreeSession(aSubSession->Session());		// check if CPort can be freed in this session
+	p->Close();								// close CPort
+	if(aSubSession->AccessCount()==1)
+	// the access count == 1 and subsession is being closed. Remove subsession from session's CC32SubSessionIx
+		{
+		aSubSession->Session()->SubSessions().Lock();
+		TInt handle;
+		TInt res = CC32DealerByRef().FindSubSession(aSubSession, aSubSession->Session(), handle); 
+		if(res == KErrNone)
+			{
+			CC32DealerByRef().RemoveSubSession(handle, aSubSession->Session());
+			}
+		else
+			{
+			__ASSERT_DEBUG(EFalse, Fault(EFindSubSessionFailed, _L8("Could not find subsession in CC32SubSessionIx while subsession count is 1")));
+			}
+		aSubSession->Session()->SubSessions().Unlock();
+		}
+	aSubSession->Close();	// close CCommSubSession, removes from TSubSessionContainer if access count drops to zero
+	SafeComplete(aMessage, KErrNone);
+	}
+
+/**
+This method is uses for example when the session is closed by the client whilst still having
+active sub-sessions. All outstanding requests on CPort are cancelled using CPort::CommCancel
+@see CC32Player::CloseSession
+*/
+void CC32Player::CloseAllOwnedSubSessions(CCommSession* aSession)
+	{
+	// The object container is stored as a packed array, so working backwards through it avoids invalidating
+	// the iterator when removing entries (and as a bonus is more efficient)
+	C32LOG2(KC32Player, _L8("CC32Player::CloseAllOwnedSubSessions() subsessions in TSubSessionContainer = %d"), iSubSessions.Count() );
+	for(TInt i = iSubSessions.Count() - 1; i >= 0; --i)
+		{
+		CCommSubSession* subSession = iSubSessions[i];
+		if(subSession->Session() == aSession)		// close subsessions belonging to the closing session
+			{
+			C32LOG2(KC32Player, _L8("CC32Player::CloseAllOwnedSubSessions()-closing subsession %x"), iSubSessions[i] );
+			// close all subSessions in 'this' aSession as session is being closed.
+			// may not lead to ~CPort being called if CPort is being shared across sessions
+			// AccessCount of all subsession = AccessCount of CPort
+			while(subSession->AccessCount() >= 1)
+				{
+				CPort* p = CPortFromSubSession(subSession);		// subSession->iPort
+				C32LOG3(KC32Player, _L8("CC32Player::CloseAllOwnedSubSessions()-Closing CPort %x for subsession %x"),p, iSubSessions[i] );
+				p->CommCancel(0, aSession);	// cancel all outstanding requests
+				p->Close();					// close all CPorts in this session
+				if (subSession->AccessCount() == 1)
+                    {
+                    subSession->Close();        // close CCommSubSession. This also deletes object
+                    break; //avoid accessing deleted subSession object 
+                    }
+                else
+                    {
+                    subSession->Close();        // close CCommSubSession
+                    }
+				}
+			}
+		}
+	}
+
+/** 
+Used when the dealer signals it is closing a session and the Player needs to cleanup 
+all resources related to/owned by the session.
+@see TPlayerMsg::ESessionClose
+*/
+void CC32Player::CloseSession(CCommSession* aSession)
+	{
+	// Remove all subsessions in this aSession
+	CloseAllOwnedSubSessions(aSession);
+	}
+
+#ifdef _DEBUG
+// I'm worried that we'll have dangling session usage, hence this debug-build deliberate
+// corruption of it
+static void CorruptSessionPointer(TAny* aSessionPtr)
+	{
+	CCommSession** sessPtr = reinterpret_cast<CCommSession** >(aSessionPtr);
+	*sessPtr = reinterpret_cast<CCommSession* >(-1);
+	}
+#endif
+
+CPort* CC32Player::CPortFromSubSession(CCommSubSession* aSubSession)
+	{
+	return aSubSession->iPort;
+	}
+
+/** 
+Process the client message forwarded from the Dealer. For subsession-Close commands
+the Dealer provides the subsession pointer explicitly as it has already been removed from
+the index. 
+*/
+void CC32Player::ProcessMessageL(const RMessage2& aMessage, CCommSubSession* aSubSession)
+	{
+	iSession = static_cast<CCommSession*>(aMessage.Session());
+	iCurrentMessage = &aMessage;
+	C32LOG4(KC32Player, _L8("CC32Player:\tProcessMessageL: session=%08x, subsess=%08x, Message(%08x) "), iSession, aSubSession, aMessage.Handle());	
+	iComplete = ETrue;
+#ifdef _DEBUG
+	CleanupStack::PushL(TCleanupItem(CorruptSessionPointer, &iSession));
+#endif
+
+	switch (aMessage.Function())
+        {
+	case ECommLoadCommModule:
+		LoadCommModule(aMessage);	
+#ifdef _DEBUG
+		CleanupStack::PopAndDestroy();
+#endif
+		return;
+	case ECommCloseCommModule:
+		CloseCommModule(aMessage);
+#ifdef _DEBUG
+		CleanupStack::PopAndDestroy();
+#endif
+		return;
+	case ECommPortInfoByName:
+		{
+		TPortName name;
+		Read(1,aMessage,name);
+		PortInfo(aMessage,name);
+#ifdef _DEBUG
+		CleanupStack::PopAndDestroy();
+#endif
+		return;
+		}
+	//case ECommPortInfoByNumber:	as the global index is not valid in player !
+	// instead CSerial* is forwarded to player to obtain PortInfo through TC32PlayerGetPortInfoMsg msg
+        }
+
+	if((aMessage.Function()==ECommOpen)
+	   ||
+	   (aMessage.Function()==ECommOpenWhenAvailable))
+		{
+		NewPortL(aMessage);
+#ifdef _DEBUG
+		CleanupStack::PopAndDestroy();
+#endif
+		return;
+		}
+
+	if (aMessage.Function()==ECommClose)
+		{
+		CloseSubSession(aMessage, aSubSession);
+#ifdef _DEBUG
+		CleanupStack::PopAndDestroy();
+#endif
+		return;
+		}
+	
+	CPort *p = CPortFromSubSession(aSubSession);
+	
+	if(p->SessionHasBeenPreempted(iSession))
+		{
+		SafeComplete(aMessage, KErrCancel);
+#ifdef _DEBUG
+		CleanupStack::PopAndDestroy();
+#endif
+		return;
+		}
+
+	// These functions need to be dispatched before the check below is made
+	if(aMessage.Function()==ECommSetAccess)
+		{
+		p->CommSetAccess(aMessage, *iSession);
+#ifdef _DEBUG
+		CleanupStack::PopAndDestroy();
+#endif
+		return;
+		}
+	else if(aMessage.Function()==ECommOpenWhenAvailableCancel)
+		{
+		if (p->IsBlockedSetAccessWaiting(*iSession))
+			{
+			p->CommSetAccessCancel(0, iSession);
+			SafeComplete(aMessage, KErrNone);
+			}
+		else
+			{
+			SafeComplete(aMessage, KErrNotFound);
+			}
+
+#ifdef _DEBUG
+		CleanupStack::PopAndDestroy();
+#endif
+		return;
+		}
+
+	// Any other requests will be denied until the port is fully open
+	if(p->SessionIsAwaitingOpen(iSession))
+		{
+		SafeComplete(aMessage, KErrNotReady);
+#ifdef _DEBUG
+		CleanupStack::PopAndDestroy();
+#endif
+		return;
+		}
+
+	switch (aMessage.Function())
+       {
+    case ECommRead:
+    	p->CommRead(aMessage, iSession);
+    	break;
+	case ECommReadCancel:
+		p->CommReadCancel(aMessage.Int3(), iSession);
+		SafeComplete(aMessage, KErrNone);
+		break;
+	case ECommQueryReceiveBuffer:
+		p->CommQueryReceiveBuffer(aMessage, *iSession);
+		break;
+	case ECommResetBuffers:
+		p->CommResetBuffers(aMessage, *iSession);
+		break;
+	case ECommWrite:
+		p->CommWrite(aMessage, iSession);
+		break;
+	case ECommWriteCancel:
+		p->CommWriteCancel(aMessage.Int3(), iSession);
+		SafeComplete(aMessage, KErrNone);
+		break;
+	case ECommBreak:
+		p->CommBreak(aMessage, iSession);
+		break;
+	case ECommBreakCancel:
+		p->CommBreakCancel(aMessage.Int3(), iSession);
+		SafeComplete(aMessage, KErrNone);
+		break;
+	case ECommCancel:
+		p->CommCancel(aMessage.Int3(), iSession);
+		SafeComplete(aMessage, KErrNone);
+		break;
+	case ECommConfig:
+		p->CommConfig(aMessage, *iSession);
+		break;
+	case ECommSetConfig:
+		p->CommSetConfig(aMessage, *iSession);
+		break;
+	case ECommCaps:
+		p->CommCaps(aMessage, *iSession);
+		break;
+	case ECommSetMode:
+		p->CommSetServerConfig(aMessage, *iSession);
+		break;
+	case ECommGetMode:
+		p->CommGetServerConfig(aMessage, *iSession);
+		break;
+	case ECommSignals:
+		p->CommSignals(aMessage, *iSession);
+		break;
+	case ECommSetSignalsToMark:
+		p->CommSetSignalsToMark(aMessage, *iSession);
+		break;
+	case ECommSetSignalsToSpace:
+		p->CommSetSignalsToSpace(aMessage, *iSession);
+		break;
+	case ECommReceiveBufferLength:
+		p->CommReceiveBufferLength(aMessage, *iSession);
+		break;
+	case ECommSetReceiveBufferLength:
+		p->CommSetReceiveBufferLength(aMessage, *iSession);
+		break;
+	case ECommSetAccess:
+		p->CommSetAccess(aMessage, *iSession);
+		break;
+
+#ifdef _DEBUG
+	case ECommDebugState:
+		p->CommDebugState( aMessage, *iSession);
+		break;
+#endif
+
+#ifdef _DEBUG_DEVCOMM
+	case ECommDbgDoDumpDebugInfo:
+		p->CommDumpDebugInfo(aMessage);
+		break;
+#endif
+       }
+
+	// Extensions to the CCommSession starts from here
+   	switch (aMessage.Function())
+        {
+	case ECommNotifySignals:
+		p->CommNotifySignalChange(aMessage, iSession);
+		break;
+	case ECommNotifySignalsCancel:
+		p->CommNotifySignalChangeCancel(aMessage.Int3(), iSession);
+		SafeComplete(aMessage, KErrNone);
+		break;
+	case ECommNotifyFlowControl:
+		p->CommNotifyFlowControlChange(aMessage, iSession);
+		break;
+	case ECommNotifyFlowControlCancel:
+		p->CommNotifyFlowControlChangeCancel(aMessage.Int3(), iSession);
+		SafeComplete(aMessage, KErrNone);	
+		break;
+	case ECommGetFlowControl:
+		p->CommGetFlowControlStatus(aMessage, iSession);
+		break;			
+	case ECommNotifyConfigChange:
+		p->CommNotifyConfigChange(aMessage, iSession);
+		break;
+	case ECommNotifyConfigChangeCancel:
+		p->CommNotifyConfigChangeCancel(aMessage.Int3(), iSession);
+		SafeComplete(aMessage, KErrNone);
+		break;
+	case ECommNotifyBreak:
+		p->CommNotifyBreak(aMessage, iSession);
+		break;
+	case ECommNotifyBreakCancel:
+		p->CommNotifyBreakCancel(aMessage.Int3(), iSession);
+		SafeComplete(aMessage, KErrNone);
+		break;
+	case ECommGetRole:
+		p->CommGetRole(aMessage, iSession);
+		break;
+	case ECommNotifyDataAvailable:
+		p->CommNotifyDataAvailable(aMessage, iSession);
+		break;
+	case ECommNotifyDataAvailableCancel:
+		p->CommNotifyDataAvailableCancel(aMessage.Int3(), iSession);
+		SafeComplete(aMessage, KErrNone);
+		break;
+	case ECommNotifyOutputEmpty:
+		p->CommNotifyOutputEmpty(aMessage, iSession);
+		break;
+	case ECommNotifyOutputEmptyCancel:
+		p->CommNotifyOutputEmptyCancel(aMessage.Int3(), iSession);
+		SafeComplete(aMessage, KErrNone);	
+		break;
+	// case default: 	// no default case here as it is dealt with in CCommSession::ServiceL
+		}
+	// Extensions to the CCommSession ends to here
+#ifdef _DEBUG
+	CleanupStack::PopAndDestroy();  // TCleanupItem(CorruptSessionPointer, &iSession));
+#endif
+	}
+
+void CC32Player::SafeComplete(const RMessagePtr2& aMessage, TInt aCompletionCode)
+	{
+	if(iComplete)
+		{
+		::SafeComplete(aMessage, aCompletionCode);
+		}
+	}
+
+void CC32Player::DontCompleteCurrentRequest()
+	{
+	iComplete = EFalse;
+	}
+
+void CC32Player::LoadCommModule(const RMessage2& aMessage)
+/**
+ * Load a comm module
+ */
+	{
+	TFileName csyfilename;
+	Read(0,aMessage,csyfilename);
+
+	TInt r=csyfilename.LocateReverse('.');
+ 	if (r==KErrNotFound)
+		{
+		csyfilename.Append(KCSYExtension);
+		}
+
+	C32LOG3(KC32Player, _L("CC32Player::LoadCommModule : %S. aMessage:%08x"), &csyfilename,aMessage.Handle());
+	CSerial *s=NULL;
+	TRAPD(res,s=iPortManager->LoadCommModuleL(csyfilename)); // if you see a crash with (iPortManager == null) here, you might have mis-configured cmi files
+	if(res == KErrNone)
+		{
+		// no leave occurred, library loaded successfully ! Send pointers to message, CsyFilename, and portPrefix
+		// back to dealer, dealer updates the information in ThreadManager and completes the message
+		CC32WorkerThread& owner = WorkerThread();
+		if(owner.WorkerId()==TC32WorkerThreadPublicInfo::EMainThread)
+			{
+			// co-resident player, make direct function call
+			owner.Dealer()->ProcessLoadCommModuleSuccessResponse(aMessage, s);
+			DontCompleteCurrentRequest();
+			}
+		else if(owner.PeerReachable(TC32WorkerThreadPublicInfo::EMainThread))
+			{
+			// different thread, send via transports
+			TC32PlayerLoadCommModuleSuccessResp respMsg(aMessage, s);
+			owner.PostMessage(TC32WorkerThreadPublicInfo::EMainThread, respMsg);
+			DontCompleteCurrentRequest();
+			const RNullableMessage& forwardedMsg = static_cast<const RNullableMessage&>(aMessage);
+			forwardedMsg.NullHandle();
+			}
+		else
+			{
+			C32LOG1(KC32Warning, _L8("CC32Player::LoadCommModule() Peer Thread Unreachable"));
+			DontCompleteCurrentRequest();
+			__ASSERT_DEBUG(0, User::Panic(KCFTransportPanic, ECFTransAbsentWorker));
+			}
+		C32LOG1(KC32Player, _L("CC32Player::LoadCommModule : Load completed successfully - message sent to Dealer"));
+			
+		}
+	else
+		{
+		// library load failure ! Mesg sent back to dealer to inform of failed load attempt, dealer 
+		// removes CSY from session CSY container and de-allocates memory allocated to this to-be-loaded
+		// CSY, and completes message with reason of load failure
+		CC32WorkerThread& owner = WorkerThread();
+		if(owner.WorkerId()==TC32WorkerThreadPublicInfo::EMainThread)
+			{
+			// co-resident player, make direct function call
+			owner.Dealer()->ProcessLoadCommModuleFailureResponse(aMessage, res);
+			DontCompleteCurrentRequest();
+			}
+		else if(owner.PeerReachable(TC32WorkerThreadPublicInfo::EMainThread))
+			{
+			// different thread, send via transports
+			TC32PlayerLoadCommModuleFailureResp respMsg(aMessage, res);
+			owner.PostMessage(TC32WorkerThreadPublicInfo::EMainThread, respMsg);
+			DontCompleteCurrentRequest();
+			const RNullableMessage& forwardedMsg = static_cast<const RNullableMessage&>(aMessage);
+			forwardedMsg.NullHandle();
+			}
+		else
+			{
+			C32LOG1(KC32Warning, _L8("CC32Player::LoadCommModule() Peer Thread Unreachable"));
+			DontCompleteCurrentRequest();
+			__ASSERT_DEBUG(0, User::Panic(KCFTransportPanic, ECFTransAbsentWorker));
+			}
+		}
+	}
+
+void CC32Player::CloseCommModule(const RMessage2& aMessage)
+/**
+ * Close a comm module
+ */
+	{
+	TFullName name;      // not a TPortName for backwards compat reasons
+	Read(0,aMessage,name);
+	C32LOG2(KC32Player, _L("CC32Player::CloseCommModule() called, CommModule : %S"), &name);
+	CSerial* s=NULL;
+	TRAPD(res,s=iPortManager->GetSerialL(name));
+	if (res==KErrNone)
+		{
+		s->Close();		// call Close() on CSerial object
+		}
+	SafeComplete(aMessage, res);
+	}
+
+void CC32Player::NewPortL(const RMessage2& aMessage)	// do we need to pass CCommSession* here ?
+/**
+ * Ask the port manager to open a port in the CSY which is then added
+ * to this session's port list (CC32SubSessionIx). If another session 
+ * has already opened the same port, the port manager will still give 
+ * us a reference if the port is not being used exclusively.
+ *
+ * @param aMessage handle to the IPC message from the client
+ */
+	{
+	C32LOG1(KC32Player, _L8("CC32Player::NewPortL()"));
+	iSession = static_cast<CCommSession*>(aMessage.Session());
+	C32LOG3(KC32Player, _L8("CC32Player:NewPort: session=%08x, Message(%08x) "), iSession, aMessage.Handle());	
+	CPort *p=NULL;
+	CSerial *s=NULL;
+	TFullName name;
+	TUint port;
+	TInt len;
+	TInt handle;
+
+	// Extract port name and number
+	VERIFY_RESULT(ExtractPortNameAndNumber(aMessage, name, port, len), KErrNone);	 // must be valid as it is already checked in dealer
+	TRAPD(res,s=iPortManager->GetSerialL(name.Left(len)));
+	/**
+	We can't ASSERT here as the client should have loaded the CSY before opening the port.
+	This will fail only in the circumstance when the other session unloads the Comm Module and
+	by the  time message is received on player, the GetSerial fails due to module not present.
+	*/
+	if (res!=KErrNone)
+		{
+		SafeComplete(aMessage, res);
+		return;
+		}
+	if(aMessage.Function()==ECommOpenWhenAvailable)
+		{
+		// This default role, as specified in aMessage.Int2(), will only be used
+		// if the port is being opened for the first time
+		TRAP(res,p=iPortManager->GetPortL(name,port,s,(TUint)EIntCommWaitUntilAvailable,
+												(TUint)aMessage.Int2(), iSession));
+		}
+	else
+		{
+		TRAP(res,p=iPortManager->GetPortL(name,port,s,aMessage.Int1(),aMessage.Int2(), iSession));
+		}
+
+	if(res!=KErrNone)
+		{
+		SafeComplete(aMessage, res);
+		return;
+		}
+	else
+		{
+		/** no leaves occurred, either new or existing CPort opened successfully
+		* Iterate through TSubSessionContainer if (p==CPortFromSubSession() && subSess->Session() == iSession)
+		* call subSess->Open() so that its AccessCount is incremented else 
+		* create a "new" subsession. Iterating the TSubSessionContainer won't
+		* be a performance bottleneck as RComm::Open() call is made only once
+		*/
+		TBool existingSubSess = EFalse;			// Set ETrue when existing subsession found
+		TInt idxFound = 0;			// stores the index of existing sub-session from TSubSessionContainer
+		for(TInt i = iSubSessions.Count() - 1; i >= 0; --i)
+			{
+			if(CPortFromSubSession(iSubSessions[i]) == p && iSubSessions[i]->Session() == iSession)	// check for existing CPort* && check for session
+				{
+				existingSubSess=ETrue;		// existing CPort* found which is this session
+				idxFound = i;				// store index
+				break;
+				}
+			}
+
+		if(existingSubSess == EFalse)
+			{
+			/** new SubSession
+			* create "new" subsession
+			* Add CPort* (p) to this newly created subsession
+			* Add CCommSubSession to TSubSessionContainer
+			* Add CCommSubSession* to CC32SubSessionIx to obtain subsession handle
+			* Write the handle back to client
+			* if writing handle back to client fails, remove subsession from CC32SubSessionIx
+			*/
+			
+			C32LOG1(KC32Player, _L8("CC32Player::NewPortL() - NEW SUBSESSION"));
+			CCommSubSession* css = CCommSubSession::NewL(iSession, p, this); 	// create CCommSubSession	
+			res = iSubSessions.Append(css);		// add to TSubSessionContainer
+			if(res != KErrNone)
+				{
+				css->Close();		// Close() newly created sub-session (which will delete it as accesscount = 1) this could not be appended to TSubSessionContainer
+				SafeComplete(aMessage, res);	// complete message with error returned from Append
+				return;
+				}
+			// lock SubSessionIx container in dealer and obtain "new" TSubSessionHandle - TInt
+			iSession->SubSessions().Lock();
+			res = CC32DealerByRef().AddSubSession(css, iSession, handle);
+			if(res == KErrNone)
+				{
+				// write subsession handle to client
+				res = WriteSubSessionHandle(handle);
+				if(res != KErrNone)	// if cannot write handle to client
+					{
+					// we can remove the index from CC32SubSessionIx for this session as its a new subSess
+					CC32DealerByRef().RemoveSubSession(handle, iSession);
+					}
+				}
+			iSession->SubSessions().Unlock();
+			
+			if(res != KErrNone)		// if cannot write handle to client
+				{
+				p->Close();			// close CPort
+				css->Close();	// close CCommSubSession, removes from TSubSessionContainer if access count drops to zero.
+				}
+			
+			SafeComplete(aMessage, res);
+			return;
+			}
+		else
+			{
+			/** existing SubSession, 
+			* call Open() on subSess to increment AccessCount
+			* CC32Dealer::FindSubSession() to find subsession aHandle
+			* write the aHandle back to client
+			* if writing handle back to client, call Close() on subsession and CPort to decrement reference counts
+			*/
+			C32LOG1(KC32Player, _L8("CC32Player::NewPortL() - EXISTING SUBSESSION"));		
+			iSubSessions[idxFound]->Open();		// increment AccessCount
+
+			// lock SubSessionIx container in dealer
+			iSession->SubSessions().Lock();
+			// find existing sub-session from this session's CC32SubSessionIx
+			res = CC32DealerByRef().FindSubSession(iSubSessions[idxFound], iSession, handle); // should not fail
+			if(res != KErrNone)
+				{
+				C32LOG1(KC32Warning, _L8("CC32Player::NewPortL() - Failed to find existing sub-session in this session "));
+				}
+			__ASSERT_DEBUG(res == KErrNone, Fault(EFindSubSessionFailed));
+			// write existing or new subsession handle back to client
+			res = WriteSubSessionHandle(handle);
+
+			iSession->SubSessions().Unlock();			
+
+			if(res != KErrNone)		// if cannot write handle to client
+				{
+				p->Close();			// close CPort
+				iSubSessions[idxFound]->Close();	// close CCommSubSession
+				}
+			
+			SafeComplete(aMessage, res);
+			return;
+			}
+		}
+	}
+
+TInt CC32Player::ExtractPortNameAndNumber(const RMessagePtr2& aMessage, TDes& aPortName, TUint& aPortNumber, TInt& aLength) 
+/**
+Extract the port name and number from RMessage
+*/
+	{
+  	Read(0,aMessage, aPortName);
+	
+	_LIT(KDoubleColon, "::");
+	aLength = aPortName.Find(KDoubleColon);
+	if (aLength == KErrNotFound)
+		{
+		return KErrNotFound;
+		}
+	// extract the numeric value after ::
+	TInt numPos = aLength + KDoubleColon.iTypeLength;
+	TPtrC numPtr(&aPortName[numPos], aPortName.Length() - numPos);
+	TLex lexer(numPtr);
+	TInt ret = lexer.Val(aPortNumber);
+	
+	return ret;
+	}
+
+void CC32Player::PortInfo(const RMessage2& aMessage,const TPortName& aPortName)
+/**
+ * Write back the port info to the client for a specified port
+ *
+ * @param aPortName name of the port to get information about
+ */
+	{
+	TSerialInfo port;
+	TInt ret=0;
+	if((ret=iPortManager->PortInfo(aPortName,port))==KErrNone)
+		{
+		TPckgC<TSerialInfo> p(port);
+		Write(0,aMessage,p);
+		}
+	SafeComplete(aMessage, ret);
+	}
+
+void CC32Player::PortInfo(const RMessage2& aMessage, CSerial* aSerialPtr)
+/**
+ * Write back the port info to the client for a specified port
+ *
+ * @param aSerial CSerial Pointer obtained by lookup in CC32ThreadManager
+ */
+	{
+	iComplete = ETrue;
+	TFileName name;
+	TSerialInfo port;
+	TInt ret=0;
+	ASSERT(aSerialPtr);
+	aSerialPtr->Info(port);
+	aSerialPtr->ModuleName(name);
+	TPckgC<TSerialInfo> p(port);
+	ret = Write(0,aMessage,p);
+	if(ret == KErrNone)	//if not KErrNone then client will already be panic'd so do not atttempt further write as this will panic the server
+		{
+		Write(1,aMessage,name);	
+		}
+	SafeComplete(aMessage, ret);
+	}
+
+TInt CC32Player::Write(TInt aPos, const RMessagePtr2& aMessage , const TDesC8& aDes, TInt aOffset)
+/**
+ * Write and kill the client if it leaves.
+ *
+ * Copies data from an 8 bit descriptor in the server address space to the client
+ * thread's address space. The target location must be a valid modifiable descriptor.
+ * Data is copied from the source descriptor to the specified offset position within
+ * the target descriptor data area. The length of data copied is the length of the
+ * source descriptor. The length of the target descriptor is set to the length of
+ * the source descriptor plus the value of the offset.
+ *
+ * @param aPtr    A pointer to a valid address within the client thread's address space.
+ *                The data type at this location must be a modifiable descriptor, i.e. aTDes8 type.
+ * @param aDes    An 8 bit descriptor in the server address space. This is the source of the copy operation.
+ * @param aOffset The offset from the start of the target descriptor data area where copying is to begin.
+ *                This value must be greater than or equal to zero.
+ *
+ * @panic This function will panic the client if the WriteL() leaves
+ */	
+	{
+	//C32LOG4(KC32Player, _L8("CC32Player::Write() Data = (%s), Pos (%d) Offset (%d)"), aDes.Ptr(), aPos, aOffset);
+
+	TInt ret = aMessage.Write(aPos, aDes, aOffset);
+	if (ret!=KErrNone)
+		{
+		C32LOG1(KC32Player, _L8("CC32Player::Write \t Error at the time of writing data to client"));
+		PanicClient(EBadDescriptor,aMessage);
+		}
+	return ret;
+	}
+
+
+TInt CC32Player::Read(TInt aPos, const RMessagePtr2& aMessage , TDes8& aDes, TInt aOffset)
+/**
+ * Read and kill the client if it leaves.
+ * 
+ * Copies data from the client thread's address space into an 8 bit descriptor
+ * in the server address space. The source data must be a valid descriptor.
+ * Data is copied from the specified offset position within the source descriptor
+ * data area. The length of data copied is the length of source descriptor data
+ * minus the offset value. If the offset value is greater than the length of the
+ * source descriptor, then no data is copied. The length of data copied is limited
+ * to the maximum length of the target descriptor.
+ *
+ * @param aPtr    A pointer to a valid address within the client thread's address space.
+ *                The data at this pointer must be a descriptor, i.e. a TDesC8 type.
+ * @param aDes    An 8 bit descriptor in the server address space. This is the target
+ *                of the copy operation.
+ * @param aOffset The offset from the start of the source descriptor data area from where
+ *                copying is to begin. This value must be greater than or equal to zero.
+ *
+ * @panic This function will panic the client if the ReadL() leaves
+ */
+	{
+	C32LOG3(KC32Player, _L8("CC32Player::Read(), Pos (%d), Offset (%d)"), aPos, aOffset);
+
+	TInt ret = aMessage.Read(aPos, aDes, aOffset);
+	if (ret!=KErrNone)
+		{
+		C32LOG1(KC32Player, _L8("CC32Player::Read \t Error at the time of reading data from client"));
+		PanicClient(EBadDescriptor,aMessage);
+		}
+	return ret;
+	}
+
+
+TInt CC32Player::Write(TInt aPos, const RMessagePtr2& aMessage , const TDesC16& aDes, TInt aOffset)
+/**
+ * Write and kill the client if it leaves.
+ *
+ * (see CC32Player::Write() with 8-bit descriptor)
+ *
+ * @param aPtr    A pointer to a valid address within the client thread's address space.
+ *                The data type at this location must be a modifiable descriptor, i.e. aTDes16 type.
+ * @param aDes    A 16 bit descriptor in the server address space. This is the source of the copy operation.
+ * @param aOffset The offset from the start of the target descriptor data area where copying is to begin.
+ *                This value must be greater than or equal to zero.
+ *
+ * @panic This function will panic the client if the WriteL() leaves
+ */	
+	{
+	//C32LOG4(KC32Player, _L8("CC32Player::Write(), Data = (%s), Pos (%d), Offset (%d)"), aDes.Ptr(), aPos, aOffset);
+
+	TInt ret = aMessage.Write(aPos, aDes, aOffset);
+	if (ret!=KErrNone)
+		{
+		C32LOG1(KC32Player, _L8("CC32Player::Write \t Error at the time of writing data to client"));
+		PanicClient(EBadDescriptor,aMessage);
+		}
+	return ret;
+	}
+
+
+TInt CC32Player::Read(TInt aPos, const RMessagePtr2& aMessage , TDes16& aDes, TInt aOffset)
+/**
+ * Read and kill the client if it leaves.
+ *
+ * (see CC32Player::Write() with 8-bit descriptor)
+ *
+ * @param aPtr    A pointer to a valid address within the client thread's address space.
+ *                The data at this pointer must be a descriptor, i.e. a TDesC16 type.
+ * @param aDes    A 16 bit descriptor in the server address space. This is the target
+ *                of the copy operation.
+ * @param aOffset The offset from the start of the source descriptor data area from where
+ *                copying is to begin. This value must be greater than or equal to zero.
+ *
+ * @panic This function will panic the client if the ReadL() leaves
+ */	
+	{
+	C32LOG3(KC32Player, _L8("CC32Player::Read(), Pos (%d), Offset (%d)"), aPos, aOffset);
+
+	TInt ret = aMessage.Read(aPos, aDes, aOffset);
+	if (ret!=KErrNone)
+		{
+		C32LOG1(KC32Player, _L8("CC32Player::Read \t Error at the time of reading data from client"));
+		PanicClient(EBadDescriptor,aMessage);
+		}
+	return ret;
+	}
+
+
+// EOF - CS_ROLES.CPP
+
+