kernel/eka/drivers/pbus/mmc/stack.cpp
changeset 0 a41df078684a
child 39 5d2844f35677
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kernel/eka/drivers/pbus/mmc/stack.cpp	Mon Oct 19 15:55:17 2009 +0100
@@ -0,0 +1,6797 @@
+// Copyright (c) 1999-2009 Nokia Corporation and/or its subsidiary(-ies).
+// All rights reserved.
+// This component and the accompanying materials are made available
+// under the terms of the License "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:
+// \e32\drivers\pbus\mmc\stack.cpp
+// 
+//
+
+#include <drivers/mmc.h>
+#include <kernel/kern_priv.h>
+#include <drivers/locmedia.h>
+#include "stackbody.h"
+
+#ifdef __SMP__
+TSpinLock MMCLock(TSpinLock::EOrderGenericIrqHigh0);
+#endif
+
+#define ASSERT_NOT_ISR_CONTEXT	__ASSERT_DEBUG(NKern::CurrentContext()!=NKern::EInterrupt,DMMCSocket::Panic(DMMCSocket::EMMCUnblockingInWrongContext));
+
+#if !defined(__WINS__)
+#define DISABLEPREEMPTION TUint irq = __SPIN_LOCK_IRQSAVE(MMCLock);
+#define RESTOREPREEMPTION __SPIN_UNLOCK_IRQRESTORE(MMCLock,irq);
+#else
+#define DISABLEPREEMPTION
+#define RESTOREPREEMPTION										   
+#endif
+
+//#define ENABLE_DETAILED_SD_COMMAND_TRACE
+
+// default length of minor buffer - must have at least enough space for one sector
+const TInt KMinMinorBufSize = 512;
+
+//	MultiMedia Card Controller - Generic level code for controller, intermediate
+//	level code for media change and power supply handling
+
+EXPORT_C TUint TCSD::CSDField(const TUint& aTopBit, const TUint& aBottomBit) const
+/**
+ * Extract bitfield from CSD
+ */
+	{
+	const TUint indexT=KMMCCSDLength-1-aTopBit/8;
+	const TUint indexB=KMMCCSDLength-1-aBottomBit/8;
+	return(((indexT==indexB ? iData[indexT]
+			: (indexT+1)==indexB ? ((iData[indexT]<<8) | iData[indexT+1])
+			: ((iData[indexT]<<16) | (iData[indexT+1]<<8) | iData[indexT+2])
+			) >> (aBottomBit&7)) & ((1<<(aTopBit-aBottomBit+1))-1));
+	}
+
+
+//	--------  class TCSD  --------
+// Raw field accessor functions are defined in mmc.inl.  These functions return
+// values that require extra computation, such as memory capacity.
+
+
+EXPORT_C TUint TCSD::DeviceSize() const
+/**
+ *
+ * Calculate device capacity from CSD
+ * 
+ * Section 5.3, MMCA Spec 2.2 (Jan 2000)
+ *
+ * memory capacity = BLOCKNR * BLOCK_LEN
+ * where
+ *	BLOCKNR = (C_SIZE + 1) * MULT; MULT = 2 ** (C_MULT_SIZE + 2);
+ *	BLOCK_LEN = 2 ** (READ_BL_LEN)
+ *
+ * memory capacity	= (C_SIZE + 1) * (2 ** (C_MULT_SIZE + 2)) * (2 ** READ_BL_LEN)
+ *					= (C_SIZE + 1) * (2 ** (C_MULT_SIZE + 2 + READ_BL_LEN))
+ *
+ * @return Device Capacity
+ */
+	{
+	__KTRACE_OPT(KPBUS1, Kern::Printf("csd:ds:0x%x,0x%x,0x%x", ReadBlLen(), CSize(), CSizeMult()));
+
+	const TUint blockLog = ReadBlLen();
+	if( blockLog > 11 )
+		return( 0 );
+
+	const TUint size = (CSize() + 1) << (2 + CSizeMult() + blockLog);
+
+	if( size == 0 )
+		return( 0xFFF00000 );
+
+	return( size );
+	}
+
+EXPORT_C TMMCMediaTypeEnum TCSD::MediaType() const
+/**
+ * This function makes a rough approximation if media type based on supported
+ * command classes (CCC).
+ *
+ * @return TMMCMediaTypeEnum describing the type of media.
+ */
+	{
+	struct mediaTableEntry
+		{
+		TUint iMask;
+		TUint iValue;
+		TMMCMediaTypeEnum iMedia;
+		};
+
+	const TUint testMask = (KMMCCmdClassBlockRead|KMMCCmdClassBlockWrite|KMMCCmdClassErase|KMMCCmdClassIOMode);
+	static const mediaTableEntry mediaTable[] = 
+		{
+		{KMMCCmdClassBasic, 0,																  EMultiMediaNotSupported},
+		{testMask,			(KMMCCmdClassBlockRead|KMMCCmdClassBlockWrite|KMMCCmdClassErase), EMultiMediaFlash},
+		{testMask,			KMMCCmdClassBlockRead|KMMCCmdClassBlockWrite,					  EMultiMediaFlash},
+		{testMask,			KMMCCmdClassBlockRead|KMMCCmdClassErase,						  EMultiMediaROM},
+		{testMask,			KMMCCmdClassBlockRead,											  EMultiMediaROM},
+		{KMMCCmdClassIOMode,KMMCCmdClassIOMode,												  EMultiMediaIO},
+		{0,					0,																  EMultiMediaOther}
+		};
+
+	const TUint ccc = CCC();
+	const mediaTableEntry* ptr = mediaTable;
+
+	while( (ccc & ptr->iMask) != (ptr->iValue) )
+		ptr++;
+
+    if (ptr->iMedia == EMultiMediaFlash)
+        {
+        // Further check PERM_WRITE_PROTECT and TMP_WRITE_PROTECT bits
+        if (PermWriteProtect() || TmpWriteProtect())
+            return EMultiMediaROM;
+        }
+
+	return( ptr->iMedia );
+	}
+
+EXPORT_C TUint TCSD::ReadBlockLength() const
+/**
+ * Calculates the read block length from the CSD.
+ * READ_BL_LEN is encoded as a logarithm.
+ *
+ * @return The read block length 
+ */
+	{
+	const TUint blockLog = ReadBlLen();
+	
+	//SD version 2.0 or less the range is 0-11
+	//MMC version 4.1 or less the range is 0-11
+	//MMC version 4.2 the range is 0-14 (15 is reserved for future use)
+	//But we cannot differentiate among 4.x
+	//Hence , 0-14 is supported for 4.x
+	if (SpecVers() < 4)
+		{
+		if( blockLog > 11 )
+			return( 0 );
+		}
+	else
+		{
+		if(blockLog > 14)
+			return ( 0 );
+		}
+
+	return( 1 << blockLog );
+	}
+
+EXPORT_C TUint TCSD::WriteBlockLength() const
+/**
+ * Calculates the write block length from the CSD.
+ * WRITE_BL_LEN is encoded as a logarithm.
+ *
+ * @return The write block length 
+ */
+	{
+	const TUint blockLog = WriteBlLen();
+	if( blockLog > 11 )
+		return( 0 );
+
+	return( 1 << blockLog );
+	}
+
+EXPORT_C TUint TCSD::EraseSectorSize() const
+/**
+ * Calculates the erase sector size from the CSD.
+ * SECTOR_SIZE is a 5 bit value, which is one less than the number of write
+ *
+ * @return The erase sector size
+ */
+	{
+	if (SpecVers() < 3)
+		{
+		// V2.2 and earlier supports erase sectors. Read sector size from CSD(46:42) - confusingly now reassigned as
+		// erase group size. 
+		return( (EraseGrpSize()+1) * WriteBlockLength() );
+		}
+	else
+		{
+		// Support for erase sectors removed from V3.1 onwards
+		return(0);	
+		}
+
+	}
+
+EXPORT_C TUint TCSD::EraseGroupSize() const
+/**
+ * Calculates the erase group size from the CSD.
+ * ERASE_GRP_SIZE is a 5 bit value, which is one less than the number of erase
+ * sectors in an erase group.
+ *
+ * @return The erase group size
+ */
+	{
+	if (SpecVers() < 3)
+		{
+		// For V2.2 and earlier, the erase group size is held in CSD(41:37)  - confusingly now reassigned as the erase
+		// group multiplier. The units for this are erase sectors, so need to convert to write blocks and then bytes.
+		TUint erSecSizeInBytes=(EraseGrpSize()+1) * WriteBlockLength();
+		return( (EraseGrpMult()+1) * erSecSizeInBytes );
+		}
+	else
+		{
+		// For V3.1 onwards, the erase group size is determined by multiplying the erase group size - CSD(41:37) by the
+		// erase group multiplier - CSD(46:42)). The units for this are write blocks, so need to convert to bytes.  
+		TUint erGrpSizeInWrBlk = (EraseGrpSize()+1) * (EraseGrpMult()+1);
+		return(erGrpSizeInWrBlk * WriteBlockLength());
+		}	
+	}
+
+EXPORT_C TUint TCSD::MinReadCurrentInMilliamps() const
+/**
+ * Calculates the minimum read current from the CSD.
+ * VDD_R_CURR_MIN is a three bit value which is mapped to a number of mA.
+ * 0 actually maps to 0.5mA, but has been rounded up.
+ *
+ * @return The minimum read current, in Milliamps
+ */
+	{
+	static const TUint8 minConsumptionTable[] = {1,1,5,10,25,35,60,100};
+	return( minConsumptionTable[VDDRCurrMin()] );
+	}
+
+EXPORT_C TUint TCSD::MinWriteCurrentInMilliamps() const
+/**
+ * Calculates the minimum write current from the CSD.
+ * VDD_W_CURR_MIN is a three bit value which is mapped to a number of mA.
+ *
+ * @return The minimum write current, in Milliamps
+ */
+	{
+	static const TUint8 minConsumptionTable[] = {1,1,5,10,25,35,60,100};
+	return( minConsumptionTable[VDDWCurrMin()] );
+	}
+
+EXPORT_C TUint TCSD::MaxReadCurrentInMilliamps() const
+/**
+ * Calculates the maximum read current from the CSD.
+ * VDD_R_CURR_MAX is a three bit value which is mapped to a number of mA.
+ * 0 actually maps to 0.5mA, but has been rounded up.
+ *
+ * @return The maximum read current, in Milliamps
+ */
+	{
+	static const TUint8 maxConsumptionTable[] = {1,5,10,25,35,45,80,200};
+	return( maxConsumptionTable[VDDRCurrMax()] );
+	}
+
+EXPORT_C TUint TCSD::MaxWriteCurrentInMilliamps() const
+/**
+ * Calculates the maximum write current from the CSD.
+ * VDD_W_CURR_MAX is a three bit value which is mapped to a number of mA.
+ *
+ * @return The maximum write current, in Milliamps
+ */
+	{
+	static const TUint8 maxConsumptionTable[] = {1,5,10,25,35,45,80,200};
+	return( maxConsumptionTable[VDDWCurrMax()] );
+	}
+
+EXPORT_C TUint TCSD::MaxTranSpeedInKilohertz() const
+/**
+ * TRAN_SPEED is an eight bit value which encodes three fields.
+ * Section 5.3, MMCA Spec 2.2 (Jan 2000)
+ *
+ *	2:0	transfer rate unit	values 4 to 7 are reserved.
+ *	6:3	time value
+ *
+ * @return Speed, in Kilohertz
+ */
+	{
+	// tranRateUnits entries are all divided by ten so tranRateValues can be integers
+	static const TUint tranRateUnits[8] = {10,100,1000,10000,10,10,10,10};
+	static const TUint8 tranRateValues[16] = {10,10,12,13,15,20,25,30,35,40,45,50,55,60,70,80};
+	const TUint ts = TranSpeed();
+	return( tranRateUnits[ts&7] * tranRateValues[(ts>>3)&0xF] );
+	}
+
+//	--------  class TMMCard  --------
+
+TMMCard::TMMCard()
+: iIndex(0), iUsingSessionP(0), iFlags(0), iBusWidth(1)
+	{
+	// empty.
+	}
+
+EXPORT_C TBool TMMCard::IsReady() const
+/**
+ * Predicate for if card is mounted and in standby/transfer/sleep state.
+ *
+ * @return ETrue if ready, EFalse otherwise.
+ */
+	{
+	const TUint state = iStatus.State();
+	__KTRACE_OPT(KPBUS1, Kern::Printf("=mcc:ir:%d,0x%08x", IsPresent(), state));
+	return IsPresent() && (state == ECardStateStby || state == ECardStateTran || state == ECardStateSlp);
+	}
+
+EXPORT_C TBool TMMCard::IsLocked() const
+/**
+ * Predicate for if card is locked
+ * 
+ * It would be useful to check if the CSD supports the password protection
+ * feature.  Password protection was introduced in c3.1, 05/99 and SPEC_VERS
+ * is encoded 0 |-> 1.0 - 1.2, 1 |-> 1.4, 3 |-> 2.2.  Some cards support
+ * password locking but their CSD reports SPEC_VERS == 1.
+ *
+ * @return ETrue if locked, EFalse otherwise.
+ */
+	{
+	if ( !IsPresent() ) 
+		return( EFalse );
+
+	return( (TUint32(iStatus) & KMMCStatCardIsLocked) != 0 );
+	}
+
+TInt64 TMMCard::DeviceSize64() const
+/**
+ * Returns the size of the MMC card in bytes
+ * @return The size of the MMC card in bytes.
+ */
+	{
+	const TBool highCapacity = IsHighCapacity();
+	const TUint32 sectorCount = ExtendedCSD().SectorCount();
+	
+	return ((highCapacity && sectorCount) ? (((TInt64)ExtendedCSD().SectorCount()) * 512) : (TInt64)CSD().DeviceSize());
+	}
+
+TUint32 TMMCard::PreferredWriteGroupLength() const
+/**
+ * Returns the write group length.  Provided by the variant.
+ * Default implementation returns a multiple of the write block length, as indicated by the CSD.
+ * @return The preferred write group length.
+ */
+	{
+	return(CSD().WriteBlockLength() << 5);	// 16K for a standard 512byte block length
+	}
+
+TInt TMMCard::GetFormatInfo(TLDFormatInfo& /*aFormatInfo*/) const
+/**
+ * Returns the preferred format parametersm for the partition.
+ * Implemented at the Variant layer.
+ * @return Standard Symbian OS error code.
+ */
+	{
+	return KErrNotSupported;
+	}
+
+TUint32 TMMCard::MinEraseSectorSize() const
+/**
+ * Returns the minimum erase sector size.  Provided by the variant.
+ * Default implementation returns the erase sector size, as indicated by the CSD.
+ * @return The minimum erase sector size.
+ */
+	{
+	return CSD().EraseSectorSize();
+	}
+
+TUint32 TMMCard::EraseSectorSize() const
+/**
+ * Returns the recommended erase sector size.  Provided by the variant.
+ * Default implementation returns the erase sector size, as indicated by the CSD.
+ * @return The recommended erase sector size.
+ */
+	{
+	return CSD().EraseSectorSize();
+	}
+
+LOCAL_C TBool IsPowerOfTwo(TInt aNum)
+//
+// Returns ETrue if aNum is a power of two
+//
+	{
+	return (aNum != 0 && (aNum & -aNum) == aNum);
+	}
+	
+TInt TMMCard::GetEraseInfo(TMMCEraseInfo& aEraseInfo) const
+/**
+ * Return info. on erase services for this card
+ * @param aEraseInfo A reference to the TMMCEraseInfo to be filled in with the erase information.
+ * @return Symbian OS error code.
+ */
+	{
+	
+	// Check whether this card supports Erase Class Commands. Also, validate the erase group size
+	if ((CSD().CCC() & KMMCCmdClassErase) && IsPowerOfTwo(CSD().EraseGroupSize()))
+		{
+		// This card supports erase cmds. Also, all versions of MMC cards support Erase Group commands (i.e. CMD35, CMD36).
+		aEraseInfo.iEraseFlags=(KMMCEraseClassCmdsSupported|KMMCEraseGroupCmdsSupported); 
+		
+		// Return the preferred size to be used as the unit for format operations. We need to return a sensible
+		// multiple of the erase group size - as calculated by the CSD. A value around 1/32th of the total disk
+		// size generally results in an appropriate number of individual format calls.
+		const TInt64 devSizeDividedBy32=(DeviceSize64()>>5);
+		aEraseInfo.iPreferredEraseUnitSize=CSD().EraseGroupSize();
+		while (aEraseInfo.iPreferredEraseUnitSize < devSizeDividedBy32)
+			aEraseInfo.iPreferredEraseUnitSize<<=1;
+	
+		// Return the smallest size that can be used as the unit for erase operations. For erase group commands, this
+		// is the erase group size. 
+		aEraseInfo.iMinEraseSectorSize=CSD().EraseGroupSize();
+		} 
+	else	
+		aEraseInfo.iEraseFlags=0;
+	
+	return(KErrNone);
+	}
+
+TUint TMMCard::MaxTranSpeedInKilohertz() const
+/**
+ * Returns the maximum supported clock rate for the card, in Kilohertz.
+ * @return Speed, in Kilohertz
+ */
+	{
+	// Default implementation obtains the transaction speed from the CSD
+	TUint32 highSpeedClock = HighSpeedClock();
+	return(highSpeedClock ? highSpeedClock : iCSD.MaxTranSpeedInKilohertz());
+	}
+
+
+TInt TMMCard::MaxReadBlLen() const
+/**
+ * Returns the maximum read block length supported by the card encoded as a logarithm
+ * Normally this is the same as the READ_BL_LEN field in the CSD register,
+ * but for high capacity cards (>- 2GB) this is set to a maximum of 512 bytes,
+ * if possible, to try to avoid compatibility issues.
+ */
+	{
+	const TInt KDefaultReadBlockLen = 9;	// 2^9 = 512 bytes
+	const TCSD& csd = CSD();
+
+	TInt blkLenLog2 = csd.ReadBlLen();
+
+	if (blkLenLog2 > KDefaultReadBlockLen)
+		{
+		__KTRACE_OPT(KPBUS1, Kern::Printf("=mmc:mrbl %d", blkLenLog2));
+		if (csd.ReadBlPartial() || CSD().SpecVers() >= 4)
+			{
+			//
+			// MMC System Spec 4.2 states that 512 bytes blocks are always supported, 
+			// regardless of the state of READ_BL_PARTIAL 
+			//
+			blkLenLog2 = KDefaultReadBlockLen;	
+			__KTRACE_OPT(KPBUS1, Kern::Printf("=mmc:mrbl -> %d", blkLenLog2));
+			}
+		}
+
+	return blkLenLog2;
+
+	}
+
+TInt TMMCard::MaxWriteBlLen() const
+/**
+ * Returns the maximum write block length supported by the card encoded as a logarithm
+ * Normally this is the same as the WRITE_BL_LEN field in the CSD register,
+ * but for high capacity cards (>- 2GB) this is set to a maximum of 512 bytes,
+ * if possible, to try to avoid compatibility issues.
+ */
+	{
+	const TInt KDefaultWriteBlockLen = 9;	// 2^9 = 512 bytes
+	const TCSD& csd = CSD();
+
+	TInt blkLenLog2 = csd.WriteBlLen();
+
+	if (blkLenLog2 > KDefaultWriteBlockLen)
+		{
+		__KTRACE_OPT(KPBUS1, Kern::Printf("=mmc:mrbl %d", blkLenLog2));
+		if (csd.WriteBlPartial() || CSD().SpecVers() >= 4)
+			{
+			//
+			// MMC System Spec 4.2 states that 512 bytes blocks are always supported, 
+			// regardless of the state of READ_BL_PARTIAL 
+			//
+			blkLenLog2 = KDefaultWriteBlockLen;	
+			__KTRACE_OPT(KPBUS1, Kern::Printf("=mmc:mrbl -> %d", blkLenLog2));
+			}
+		}
+
+	return blkLenLog2;
+
+	}
+
+//	--------  class TMMCardArray  --------
+
+EXPORT_C TInt TMMCardArray::AllocCards()
+/** 
+ * Allocate TMMCard objects for iCards and iNewCardsArray.
+ * This function is called at bootup as part of stack allocation so there
+ * is no cleanup if it fails.
+ *
+ * @return KErrNone if successful, Standard Symbian OS error code otherwise.
+ */
+	{
+	for (TUint i = 0; i < KMaxMMCardsPerStack; ++i)
+		{
+		// zeroing the card data used to be implicit because embedded in
+		// CBase-derived DMMCStack.
+		if ((iCards[i] = new TMMCard) == 0)
+			return KErrNoMemory;
+		iCards[i]->iUsingSessionP = 0;
+		if ((iNewCards[i] = new TMMCard) == 0)
+			return KErrNoMemory;
+		iNewCards[i]->iUsingSessionP = 0;
+		}
+
+	return KErrNone;
+	}
+
+void TMMCardArray::InitNewCardScan()
+/**
+ * Prepare card array for new scan.
+ */
+	{
+	iNewCardsCount=0;
+	}
+
+void TMMCardArray::MoveCardAndLockRCA(TMMCard& aSrcCard,TMMCard& aDestCard,TInt aDestIndex)
+/**
+ * Copy card object and lock RCA.
+ */
+	{
+	__KTRACE_OPT(KPBUS1, Kern::Printf("=mca:mclr:%d", aDestIndex));
+
+	aDestCard.iCID=aSrcCard.iCID;
+	aDestCard.iRCA=aSrcCard.iRCA;
+	aDestCard.iCSD=aSrcCard.iCSD;
+	aDestCard.iIndex=aDestIndex;		// Mark card as being present
+	aDestCard.iFlags=aSrcCard.iFlags;
+	aDestCard.iBusWidth=aSrcCard.iBusWidth;
+	aDestCard.iHighSpeedClock = aSrcCard.iHighSpeedClock;
+
+	iOwningStack->iRCAPool.LockRCA(aDestCard.iRCA);
+
+	// Now that we have transferred ownership, reset the source card
+	aSrcCard.iRCA = aSrcCard.iIndex = aSrcCard.iFlags = 0;
+	aSrcCard.iBusWidth = 1;
+	aSrcCard.iHighSpeedClock = 0;
+
+	aSrcCard.iUsingSessionP = NULL;
+	}
+
+EXPORT_C void TMMCardArray::AddNewCard(const TUint8* aCID,TRCA* aNewRCA)
+/**
+ * Found a new card to add to the array. Add it to a separate array for now
+ * since we need to know all the cards present before we start replacing old
+ * entries.
+ */
+	{
+	// Store the CID in the next free slot
+	NewCard(iNewCardsCount).iCID = aCID;
+
+	*aNewRCA=0;
+
+	// Now let's look if we've seen this card before
+	for ( TUint i=0 ; i<iOwningStack->iMaxCardsInStack ; i++ )
+		{
+		if ( Card(i).iCID==NewCard(iNewCardsCount).iCID )
+			{
+			*aNewRCA=Card(i).iRCA;
+			NewCard(iNewCardsCount).iIndex=(i+1);
+			break;
+			}
+		}
+
+	if ( *aNewRCA==0 )
+		{
+		// Not seen this one before so get a new RCA for the card
+		NewCard(iNewCardsCount).iIndex=0;
+		__ASSERT_ALWAYS( (*aNewRCA=iOwningStack->iRCAPool.GetFreeRCA())!=0,DMMCSocket::Panic(DMMCSocket::EMMCNoFreeRCA) );
+		}
+
+	__KTRACE_OPT(KPBUS1, Kern::Printf("mca:adn: assigning new card %d rca 0x%04x", iNewCardsCount, TUint16(*aNewRCA) ));
+	NewCard(iNewCardsCount).iRCA=*aNewRCA;
+	iNewCardsCount++;
+	}
+
+TInt TMMCardArray::MergeCards(TBool aFirstPass)
+/**
+ * This function places newly acquired cards from the new card array into free
+ * slots of the main card array.
+ * Returns KErrNotFound if not able to successfully place all the new cards.
+ */
+	{
+
+	__KTRACE_OPT(KPBUS1,Kern::Printf("=mca:mc:%d,%d", aFirstPass, iNewCardsCount));
+	TUint i;	// New card index
+	TUint j;	// Main card index
+
+	// Only do this on first pass. Setup any new cards which were already there
+	if (aFirstPass)
+		{
+		for ( i=0 ; i<iNewCardsCount ; i++ )
+			{
+			__KTRACE_OPT(KPBUS1, Kern::Printf("-mca:fp,i=%d,idx=0x%x", i, NewCard(i).iIndex));
+			if( NewCard(i).iIndex != 0 ) // Signifies card was here before (iIndex has old slot number +1)
+				{
+				// Put it in the same slot as before
+				j=(NewCard(i).iIndex-1);
+				MoveCardAndLockRCA(NewCard(i),Card(j),(j+1));
+				}
+			}
+		}
+
+	for ( i=0,j=0 ; i<iNewCardsCount ; i++ )
+		{
+		__KTRACE_OPT(KPBUS1, Kern::Printf("-mca:i=%d,j=%d,rca=0x%4x", i, j, TUint16(NewCard(i).iRCA) ));
+		if ( NewCard(i).iRCA != 0 )
+			{
+			// Find a spare slot in main array for this new card
+			while ( Card(j).IsPresent() )
+				if ( ++j==iOwningStack->iMaxCardsInStack )
+					return(KErrNotFound);
+
+			// Found a free slot; move the card info there
+			__KTRACE_OPT(KPBUS1, Kern::Printf("-mca:freej=%d,rca=0x%04x", j, TUint16(Card(j).iRCA) ));
+			if ( Card(j).iRCA != 0 )
+				iOwningStack->iRCAPool.UnlockRCA(Card(j).iRCA);
+
+			__KTRACE_OPT(KPBUS1, Kern::Printf("merging new card %d to card %d dest index %d", i, j, j+1));
+			MoveCardAndLockRCA(NewCard(i),Card(j),(j+1));
+			}
+		}
+	return(KErrNone);
+	}
+
+void TMMCardArray::UpdateAcquisitions(TUint* aMaxClock)
+/**
+ * Called when we have successfully stored a new set of cards in the card array.
+ * This performs final initialisation of the card entries and determines the
+ * maximum bus clock that can be employed - by checking the CSD of each card.
+ */
+	{
+
+	__KTRACE_OPT(KPBUS1,Kern::Printf("=mca:uda"));
+	iCardsPresent=0;
+	TUint maxClk = iOwningStack->iMultiplexedBus ? 1 : 800000; // ???
+	for ( TUint i=0 ; i < (iOwningStack->iMaxCardsInStack) ; i++ )
+		{
+		if ( Card(i).IsPresent() )
+			{
+			// General initialisation
+			iCardsPresent++;
+			Card(i).iSetBlockLen=0;
+			Card(i).iLastCommand=ECmdSendStatus;
+
+			// Check each card present to determine appropriate bus clock
+			TUint maxTS = iOwningStack->MaxTranSpeedInKilohertz(Card(i));
+			if(iOwningStack->iMultiplexedBus)
+				{
+				if ( maxTS > maxClk )
+					maxClk = maxTS;
+				}
+			else
+				{
+				if ( maxTS < maxClk )
+					maxClk = maxTS;
+				}
+			}
+		}
+	// ??? Should also calculate here and return the data timeout and busy timeout 
+	// instead of relying on ASSP defaults.
+
+	*aMaxClock=maxClk;
+	}
+
+EXPORT_C void TMMCardArray::DeclareCardAsGone(TUint aCardNumber)
+/**
+ * Clears up a card info object in the main card array
+ */
+	{
+
+	__KTRACE_OPT(KPBUS1,Kern::Printf("=mca:dcag"));
+	// If we thought this one was present then mark it as not present
+	TMMCard& card = Card(aCardNumber);
+	if (card.IsPresent())
+		{
+		card.iIndex=0; // Mark card as not present
+		iCardsPresent--;
+		}
+
+	// If this card is in use by a session then flag that card has now gone
+	if( card.iUsingSessionP != NULL )
+		card.iUsingSessionP->iState |= KMMCSessStateCardIsGone;
+
+	card.iUsingSessionP=NULL;
+	card.iSetBlockLen=0;
+	card.iFlags=0; 		// Reset 'has password' and 'write protected' bit fields
+	card.iHighSpeedClock=0;
+	card.iBusWidth=1;
+	}
+
+// return this card's index in the array or KErrNotFound if not found
+TInt TMMCardArray::CardIndex(const TMMCard* aCard)
+	{
+	TInt i;
+	for (i = KMaxMMCardsPerStack-1; i>= 0; i--)
+		{
+		if (iCards[i] == aCard)
+			break;
+		}
+	return i;
+	}
+
+//	--------  class TMMCCommandDesc  --------
+
+EXPORT_C TInt TMMCCommandDesc::Direction() const
+/**
+ * returns -1, 0 or +1 for DT directions read, none or write respectively
+ */
+	{
+	TUint dir = iSpec.iDirection;
+	TInt result = dir;
+
+	if( dir == 0 )
+		return( 0 );
+
+	if( dir & KMMCCmdDirWBitArgument )
+		result = TUint(iArgument) >> (dir & KMMCCmdDirIndBitPosition);
+
+	if( dir & KMMCCmdDirNegate )
+		result = ~result;
+
+	return( ((result&1)-1)|1 );
+	}
+
+
+TBool TMMCCommandDesc::AdjustForBlockOrByteAccess(const DMMCSession& aSession)
+	{
+/**
+ * The MMC session provides both block and byte based IO methods, all of which can
+ * be used on both block and byte based MMC cards.  This method adjusts the command
+ * arguments so that they match the underlying cards access mode.
+ *
+ * @return ETrue if the address is valid or successfully converted, EFalse otherwise
+ */
+	TUint32 blockLength = BlockLength();
+
+	if(iTotalLength == 0	||
+	   blockLength == 0	||
+	   iTotalLength % KMMCardHighCapBlockSize != 0	||	// always aligned on 512 bytes
+	   blockLength % KMMCardHighCapBlockSize != 0)
+		{
+		return(EFalse);
+		}
+
+	if(aSession.CardP()->IsHighCapacity())
+		{	
+		// high capacity (block-based) card
+		if((iFlags & KMMCCmdFlagBlockAddress) == 0)
+			{	
+			// The command arguments are using byte based addressing
+			//  - adjust to block-based addressing
+			if(iArgument % KMMCardHighCapBlockSize != 0)
+				{
+				// Block based media does not support misaligned access
+				return(EFalse);
+				}
+
+			// adjust for block based access
+			iArgument = iArgument >> KMMCardHighCapBlockSizeLog2;
+			iFlags |= KMMCCmdFlagBlockAddress;
+			}
+		}
+	else
+		{
+		// standard (byte based) card
+		if((iFlags & KMMCCmdFlagBlockAddress) != 0)
+			{
+			// The command arguments are using block based addressing
+			//  - adjust to byte-based addressing
+			const TUint32 maxBlocks = 4 * 1024 * ((1024 * 1024) >> KMMCardHighCapBlockSizeLog2);
+			
+			if(iArgument > maxBlocks)
+				{
+				// The address is out of range (>2G) - cannot convert
+				return(EFalse);
+				}
+
+			// adjust for byte-based access
+			iArgument = iArgument << KMMCardHighCapBlockSizeLog2;
+			iFlags &= ~KMMCCmdFlagBlockAddress;
+			}
+		else if(iArgument % KMMCardHighCapBlockSize != 0)
+			{
+			// byte addressing, unaligned address
+			return(EFalse);
+			}
+		}
+
+	return(ETrue);
+	}
+
+void TMMCCommandDesc::Dump(TUint8* aResponseP, TMMCErr aErr)
+	{
+
+	Kern::Printf("------------------------------------------------------------------");
+	Kern::Printf("CMD %02d(0x%08x) - ",TUint(iCommand),TUint(iArgument));
+
+	switch(iCommand)
+		{
+		case 0  : Kern::Printf("                   | GO_IDLE_STATE");			break;
+		case 1  : Kern::Printf("                   | SEND_OP_COND");			break;
+		case 2  : Kern::Printf("                   | ALL_SEND_CID");			break;
+		case 3  : Kern::Printf("                   | SET_RELATIVE_ADDR");		break;
+		case 4  : Kern::Printf("                   | SET_DSR");					break;
+		case 5  : Kern::Printf("                   | SLEEP/AWAKE");				break;
+		case 6  : Kern::Printf("                   | SWITCH");					break;
+		case 7  : Kern::Printf("                   | SELECT/DESELECT_CARD");	break;
+		case 8  : Kern::Printf("                   | SEND_EXT_CSD");			break;
+		case 9  : Kern::Printf("                   | SEND_CSD");				break;
+		case 10 : Kern::Printf("                   | SEND_CID");				break;
+		case 11 : Kern::Printf("                   | READ_DAT_UNTIL_STOP");		break;
+		case 12 : Kern::Printf("                   | STOP_TRANSMISSION");		break;
+		case 13 : Kern::Printf("                   | SEND_STATUS");				break;
+		case 14 : Kern::Printf("                   | BUSTEST_R");				break;
+		case 15 : Kern::Printf("                   | GO_INACTIVE_STATE");		break;
+		case 16 : Kern::Printf("                   | SET_BLOCKLEN");			break;
+		case 17 : Kern::Printf("                   | READ_SINGLE_BLOCK");		break;
+		case 18 : Kern::Printf("                   | READ_MULTIPLE_BLOCK");		break;
+		case 19 : Kern::Printf("                   | BUSTEST_W");				break;
+		case 20 : Kern::Printf("                   | WRITE_DAT_UNTIL_STOP");	break;
+		case 23 : Kern::Printf("                   | SET_BLOCK_COUNT");			break;
+		case 24 : Kern::Printf("                   | WRITE_BLOCK");				break;
+		case 25 : Kern::Printf("                   | WRITE_MULTIPLE_BLOCK");	break;
+		case 26 : Kern::Printf("                   | PROGRAM_CID");				break;
+		case 27 : Kern::Printf("                   | PROGRAM_CSD");				break;
+		case 28 : Kern::Printf("                   | SET_WRITE_PROT");			break;
+		case 29 : Kern::Printf("                   | CLR_WRITE_PROT");			break;
+		case 30 : Kern::Printf("                   | SEND_WRITE_PROT");			break;
+		case 32 : Kern::Printf("                   | ERASE_WR_BLK_START");		break;	// SD
+		case 33 : Kern::Printf("                   | ERASE_WR_BLK_END");		break;	// SD
+		case 35 : Kern::Printf("                   | ERASE_GROUP_START");		break;
+		case 36 : Kern::Printf("                   | ERASE_GROUP_END");			break;
+		case 38 : Kern::Printf("                   | ERASE");					break;
+		case 39 : Kern::Printf("                   | FAST_IO");					break;
+		case 40 : Kern::Printf("                   | GO_IRQ_STATE");			break;
+		case 42 : Kern::Printf("                   | LOCK_UNLOCK");				break;
+		case 55 : Kern::Printf("                   | APP_CMD");					break;
+		case 56 : Kern::Printf("                   | GEN_CMD");					break;
+		default : Kern::Printf("                   | *** UNKNOWN COMMAND ***"); break;
+		}
+
+	switch(iSpec.iResponseType)
+		{
+		case ERespTypeNone:		Kern::Printf("               RSP - NONE");		break;
+		case ERespTypeUnknown:	Kern::Printf("               RSP - UNKNOWN");	break;
+		case ERespTypeR1:		Kern::Printf("               RSP - R1");		break;
+		case ERespTypeR1B:		Kern::Printf("               RSP - R1b");		break;
+		case ERespTypeR2:		Kern::Printf("               RSP - R2");		break;
+		case ERespTypeR3:		Kern::Printf("               RSP - R3");		break;
+		case ERespTypeR4:		Kern::Printf("               RSP - R4");		break;
+		case ERespTypeR5:		Kern::Printf("               RSP - R5");		break;
+		case ERespTypeR6:		Kern::Printf("               RSP - R6");		break;
+		default :				Kern::Printf("               RSP - *** UNKNOWN RESPONSE ***"); break;
+		}
+
+	switch(iSpec.iResponseLength)
+		{
+		case 0  :																				break;
+		case 4  : Kern::Printf("                   | 0x%08x", TMMC::BigEndian32(aResponseP));	break;
+		case 16 : Kern::Printf("                   | 0x%08x 0x%08x 0x%08x 0x%08x", ((TUint32*)aResponseP)[0], ((TUint32*)aResponseP)[1], ((TUint32*)aResponseP)[2], ((TUint32*)aResponseP)[3]); break;
+		default : Kern::Printf("                   | *** RESPONSE NOT PARSED ***");				break;
+		}
+									  Kern::Printf("               ERR - 0x%08x", aErr);
+	if(aErr & KMMCErrResponseTimeOut) Kern::Printf("                   | KMMCErrResponseTimeOut");
+	if(aErr & KMMCErrDataTimeOut)	  Kern::Printf("                   | KMMCErrDataTimeOut");
+	if(aErr & KMMCErrBusyTimeOut)	  Kern::Printf("                   | KMMCErrBusyTimeOut");
+	if(aErr & KMMCErrBusTimeOut)	  Kern::Printf("                   | KMMCErrBusTimeOut");
+	if(aErr & KMMCErrTooManyCards)	  Kern::Printf("                   | KMMCErrTooManyCards");
+	if(aErr & KMMCErrResponseCRC)	  Kern::Printf("                   | KMMCErrResponseCRC");
+	if(aErr & KMMCErrDataCRC)		  Kern::Printf("                   | KMMCErrDataCRC");
+	if(aErr & KMMCErrCommandCRC)	  Kern::Printf("                   | KMMCErrCommandCRC");
+	if(aErr & KMMCErrStatus)		  Kern::Printf("                   | KMMCErrStatus");
+	if(aErr & KMMCErrNoCard)		  Kern::Printf("                   | KMMCErrNoCard");
+	if(aErr & KMMCErrBrokenLock)	  Kern::Printf("                   | KMMCErrBrokenLock");
+	if(aErr & KMMCErrPowerDown)		  Kern::Printf("                   | KMMCErrPowerDown");
+	if(aErr & KMMCErrAbort)			  Kern::Printf("                   | KMMCErrAbort");
+	if(aErr & KMMCErrStackNotReady)	  Kern::Printf("                   | KMMCErrStackNotReady");
+	if(aErr & KMMCErrNotSupported)	  Kern::Printf("                   | KMMCErrNotSupported");
+	if(aErr & KMMCErrHardware)		  Kern::Printf("                   | KMMCErrHardware");
+	if(aErr & KMMCErrBusInconsistent) Kern::Printf("                   | KMMCErrBusInconsistent");
+	if(aErr & KMMCErrBypass)		  Kern::Printf("                   | KMMCErrBypass");
+	if(aErr & KMMCErrInitContext)	  Kern::Printf("                   | KMMCErrInitContext");
+	if(aErr & KMMCErrArgument)		  Kern::Printf("                   | KMMCErrArgument");
+	if(aErr & KMMCErrSingleBlock)	  Kern::Printf("                   | KMMCErrSingleBlock");
+	if(aErr & KMMCErrUpdPswd)		  Kern::Printf("                   | KMMCErrUpdPswd");
+	if(aErr & KMMCErrLocked)		  Kern::Printf("                   | KMMCErrLocked");
+	if(aErr & KMMCErrNotFound)		  Kern::Printf("                   | KMMCErrNotFound");
+	if(aErr & KMMCErrAlreadyExists)	  Kern::Printf("                   | KMMCErrAlreadyExists");
+	if(aErr & KMMCErrGeneral)		  Kern::Printf("                   | KMMCErrGeneral");
+
+
+	if(iSpec.iResponseType == ERespTypeR1 || iSpec.iResponseType == ERespTypeR1B)
+		{
+		const TUint32 stat = TMMC::BigEndian32(aResponseP);
+		
+											 Kern::Printf("              STAT - 0x%08x", stat);
+		if(stat & KMMCStatAppCmd)			 Kern::Printf("                   | KMMCStatAppCmd");
+		if(stat & KMMCStatSwitchError)		 Kern::Printf("                   | KMMCStatSwitchError");
+		if(stat & KMMCStatReadyForData)		 Kern::Printf("                   | KMMCStatReadyForData");
+		if(stat & KMMCStatCurrentStateMask){ Kern::Printf("                   | KMMCStatCurrentStateMask");
+											 const TMMCardStateEnum cardState = (TMMCardStateEnum)(stat & KMMCStatCurrentStateMask);
+											 switch (cardState){
+												 case ECardStateIdle  : Kern::Printf("                     | ECardStateIdle"); break;
+												 case ECardStateReady : Kern::Printf("                     | ECardStateReady"); break;
+												 case ECardStateIdent : Kern::Printf("                     | ECardStateIdent"); break;
+												 case ECardStateStby  : Kern::Printf("                     | ECardStateStby"); break;
+												 case ECardStateTran  : Kern::Printf("                     | ECardStateTran"); break;
+												 case ECardStateData  : Kern::Printf("                     | ECardStateData"); break;
+												 case ECardStateRcv   : Kern::Printf("                     | ECardStateRcv"); break;
+												 case ECardStatePrg   : Kern::Printf("                     | ECardStatePrg"); break;
+												 case ECardStateDis   : Kern::Printf("                     | ECardStateDis"); break;
+												 case ECardStateBtst  : Kern::Printf("                     | ECardStateBtst"); break;
+												 case ECardStateSlp   : Kern::Printf("                     | ECardStateSlp"); break;
+												 default   	 		  : Kern::Printf("                     | ECardStateUnknown"); break;
+											 }
+										   }
+		if(stat & KMMCStatEraseReset)		 Kern::Printf("                   | KMMCStatEraseReset");
+		if(stat & KMMCStatCardECCDisabled)	 Kern::Printf("                   | KMMCStatCardECCDisabled");
+		if(stat & KMMCStatWPEraseSkip)		 Kern::Printf("                   | KMMCStatWPEraseSkip");
+		if(stat & KMMCStatErrCSDOverwrite)	 Kern::Printf("                   | KMMCStatErrCSDOverwrite");
+		if(stat & KMMCStatErrOverrun)		 Kern::Printf("                   | KMMCStatErrOverrun");
+		if(stat & KMMCStatErrUnderrun)		 Kern::Printf("                   | KMMCStatErrUnderrun");
+		if(stat & KMMCStatErrUnknown)		 Kern::Printf("                   | KMMCStatErrUnknown");
+		if(stat & KMMCStatErrCCError)		 Kern::Printf("                   | KMMCStatErrCCError");
+		if(stat & KMMCStatErrCardECCFailed)  Kern::Printf("                   | KMMCStatErrCardECCFailed");
+		if(stat & KMMCStatErrIllegalCommand) Kern::Printf("                   | KMMCStatErrIllegalCommand");
+		if(stat & KMMCStatErrComCRCError)	 Kern::Printf("                   | KMMCStatErrComCRCError");
+		if(stat & KMMCStatErrLockUnlock)	 Kern::Printf("                   | KMMCStatErrLockUnlock");
+		if(stat & KMMCStatCardIsLocked)		 Kern::Printf("                   | KMMCStatCardIsLocked");
+		if(stat & KMMCStatErrWPViolation)	 Kern::Printf("                   | KMMCStatErrWPViolation");
+		if(stat & KMMCStatErrEraseParam)	 Kern::Printf("                   | KMMCStatErrEraseParam");
+		if(stat & KMMCStatErrEraseSeqError)  Kern::Printf("                   | KMMCStatErrEraseSeqError");
+		if(stat & KMMCStatErrBlockLenError)  Kern::Printf("                   | KMMCStatErrBlockLenError");
+		if(stat & KMMCStatErrAddressError)	 Kern::Printf("                   | KMMCStatErrAddressError");
+		if(stat & KMMCStatErrOutOfRange)	 Kern::Printf("                   | KMMCStatErrOutOfRange");
+		}
+
+	Kern::Printf("                   -----------------------------------------------");
+	}
+
+//	--------  class TMMCRCAPool  --------
+
+TRCA TMMCRCAPool::GetFreeRCA()
+/**
+ * Returns a free RCA number from the pool or zero if none is available
+ */
+	{
+	TUint32 seekm = (iPool | iLocked) + 1;
+	iPool |= (seekm & ~iLocked);
+
+	if( (seekm & 0xFFFFFFFF) == 0 )
+		return( 0 );
+
+	TUint16 pos = 1;
+
+	if ((seekm & 0xFFFF) == 0)	{ seekm >>= 16;	pos = 17; }
+	if ((seekm & 0xFF) == 0)	{ seekm >>= 8;	pos += 8; }
+	if ((seekm & 0xF) == 0)		{ seekm >>= 4;	pos += 4; }
+	if ((seekm & 0x3) == 0)		{ seekm >>= 2;	pos += 2; }
+	if ((seekm & 0x1) == 0)		pos++;
+
+	// Multiply return value by 257 so that 1 is never returned.  (0x0001 is the default RCA value.)
+	// The RCA integer value is divided by 257 in LockRCA() and UnlockRCA() to compensate
+	// for this adjustment.  These functions are only ever called in this file with the iRCA
+	// field of a TMMCard object, and not with arbitrary values.
+	// The iRCA field itself is only assigned values from iNewCards[] or zero.  iNewCards
+	// in turn is fed values from this function, in DMMCStack::CIMUpdateAcqSM() / EStSendCIDIssued.
+
+	return TUint16(pos << 8 | pos);
+	}
+
+
+
+//	--------  class TMMCSessRing  --------
+
+TMMCSessRing::TMMCSessRing()
+/**
+ * Constructor
+ */
+	: iPMark(NULL),iPoint(NULL),iPrevP(NULL),iSize(0)
+	{}
+
+
+void TMMCSessRing::Erase()
+/**
+ * Erases all the ring content
+ */
+	{iPMark = iPoint = iPrevP = NULL; iSize = 0;}
+
+
+DMMCSession* TMMCSessRing::operator++(TInt)
+/**
+ * Post increment of Point
+ */
+	{
+	if( iPoint == NULL )
+		return( NULL );
+
+	if( (iPrevP=iPoint) == iPMark )
+		iPoint = NULL;
+	else
+		iPoint = iPoint->iLinkP;
+
+	return( iPrevP );
+	}
+
+
+TBool TMMCSessRing::Point(DMMCSession* aSessP)
+/**
+ * Finds aSessP and sets Point to that position
+ */
+	{
+	Point();
+
+	while( iPoint != NULL )
+		if( iPoint == aSessP )
+			return( ETrue );
+		else
+			this->operator++(0);
+
+	return( EFalse );
+	}
+
+void TMMCSessRing::Add(DMMCSession* aSessP)
+/**
+ * Inserts aSessP before Marker. Point is moved into the Marker position.
+ */
+	{
+	if( iSize == 0 )
+		{
+		iPMark = iPrevP = iPoint = aSessP;
+		aSessP->iLinkP = aSessP;
+		iSize = 1;
+		return;
+		}
+
+	iPoint = iPMark->iLinkP;
+	iPMark->iLinkP = aSessP;
+	aSessP->iLinkP = iPoint;
+	iPMark = iPrevP = aSessP;
+	iSize++;
+	}
+
+
+void TMMCSessRing::Add(TMMCSessRing& aRing)
+/**
+ * Inserts aRing before Marker. Point is moved into the Marker position.
+ * aRing Marker becomes the fisrt inserted element.
+ * Erases aRing.
+ */
+	{
+	Point();
+
+	if( aRing.iSize == 0 )
+		return;
+
+	if( iSize == 0 )
+		{
+		iPrevP = iPMark = aRing.iPMark;
+		iPoint = iPrevP->iLinkP;
+		iSize = aRing.iSize;
+		}
+	else
+		{
+		iPrevP->iLinkP = aRing.iPMark->iLinkP;
+		iPMark = iPrevP = aRing.iPMark;
+		iPrevP->iLinkP = iPoint;
+		iSize += aRing.iSize;
+		}
+
+	aRing.Erase();
+	}
+
+DMMCSession* TMMCSessRing::Remove()
+/**
+ * Removes an element pointed to by Point.
+ * Point (and possibly Marker) move forward as in operator++
+ */
+	{
+	DMMCSession* remS = iPrevP;
+
+	if( iSize < 2 )
+		Erase();
+	else
+		{
+		remS = remS->iLinkP;
+		iPrevP->iLinkP = remS->iLinkP;
+		iSize--;
+
+		if( iPoint != NULL )
+			iPoint = iPrevP->iLinkP;
+
+		if( iPMark == remS )
+			{
+			iPMark = iPrevP;
+			iPoint = NULL;
+			}
+		}
+
+	return( remS );
+	}
+
+
+void TMMCSessRing::Remove(DMMCSession* aSessP)
+/**
+ * Removes a specified session from the ring
+ */
+	{
+	if( Point(aSessP) )
+		Remove();
+	else
+		DMMCSocket::Panic(DMMCSocket::EMMCSessRingNoSession);
+	}
+
+
+
+//	--------  class TMMCStateMachine  --------
+
+
+/**
+Removes all state from the state machine.
+
+It also resets the stack and the exit code.
+*/
+EXPORT_C void TMMCStateMachine::Reset()
+	{
+	iAbort = EFalse;
+	iSP = 0; iExitCode = 0;
+	iStack[0].iState = 0; iStack[0].iTrapMask = 0;
+	}
+
+
+
+
+/**
+The state machine dispatcher.
+
+@return The MultiMediaCard error code. 
+*/
+EXPORT_C TMMCErr TMMCStateMachine::Dispatch()
+	{
+
+	// If a state machine returns non-zero, i.e. a non-empty error set, then the second
+	// inner while loop is broken.  The errors are thrown like an exception where the
+	// stack is unravelled until it reaches a state machine which can handle at least
+	// one of the error codes, else this function returns with the exit code or'd with
+	// KMMCErrBypass.  If the state machine returns zero, then this function returns
+	// zero if iSuspend is set, i.e., if the stack is waiting on an asynchronous event.
+	// If suspend is not set, then the next state machine is called.  This may be the
+	// same as the current state machine, or its caller if the current state machine
+	// ended called Pop() before exiting, e.g., via SMF_END.
+
+	while( iSP >= 0 && !iAbort )
+		{
+		// If there is an un-trapped error, wind back down the stack, either
+		// to the end of the stack or until the error becomes trapped.
+		while( iSP >= 0 && (iExitCode & ~iStack[iSP].iTrapMask) != 0 )
+			iSP--;
+
+		iExitCode &= ~KMMCErrBypass;
+
+		if ( iExitCode )
+			{
+			__KTRACE_OPT(KPBUS1,Kern::Printf(">MMC:Err %x",iExitCode));
+			}
+
+		while( iSP >= 0 && !iAbort )
+			{
+			__KTRACE_OPT(KPBUS1,Kern::Printf("-msm:dsp:%02x:%08x.%02x",iSP, TUint32(iStack[iSP].iFunction), State()));
+
+			iSuspend = ETrue;
+			const TMMCErr signal = iStack[iSP].iFunction(iContextP);
+
+			if (signal)
+				{
+				iExitCode = signal;
+				break;
+				}
+
+			if( iSuspend )
+				{
+				__KTRACE_OPT(KPBUS1,Kern::Printf("<msm:dsp:exitslp"));
+				return(0);
+				}
+			}
+		}
+
+	__KTRACE_OPT(KPBUS1,Kern::Printf("<msm:dsp:exit%08x", iExitCode));
+	return( KMMCErrBypass | iExitCode );
+	}
+
+
+
+
+/**
+Pushes another state machine entry onto the stack.
+
+Typically, this is invoked using one of the macros:
+SMF_CALL, SMF_CALLWAIT, SMF_INVOKES, SMF_INVOKEWAITS 
+
+@param anEntry  The state machine function to be run; this will start at
+                the initial state (EStBegin), with no exception handling defined.
+@param aSuspend Indicates whether the state machine is to block on return to the dispatcher;
+                Specify ETrue to block; EFalse not to block.
+                EFalse is the default, if not explicitly stated.
+
+@return KMMCErrNone
+
+@panic PBUS-MMC 0 if the maximum depth of nested state machine entries is being exeeded.
+
+@see SMF_CALL
+@see SMF_CALLWAIT
+@see SMF_INVOKES
+@see SMF_INVOKEWAITS 
+*/
+EXPORT_C TMMCErr TMMCStateMachine::Push(TMMCErr (*anEntry)(TAny*), TBool aSuspend)
+	{
+	iSP++;
+	__ASSERT_ALWAYS(TUint(iSP)<KMaxMMCMachineStackDepth,
+		DMMCSocket::Panic(DMMCSocket::EMMCMachineStack));
+	iStack[iSP].iFunction = anEntry;
+	iStack[iSP].iState = 0;
+	iStack[iSP].iTrapMask = 0;
+	if( !aSuspend )
+		iSuspend = EFalse;
+	return( 0 );
+	}
+
+
+
+
+/**
+Jumps to the specified state machine function in the current state machine entry.
+
+@param anEntry  The state machine function to be run; this will start at
+                the initial state (EStBegin), with no exception handling defined.
+@param aSuspend Indicates whether the state machine is to block on return to the dispatcher;
+                Specify ETrue to block; EFalse not to block.
+                EFalse is the default, if not explicitly stated.
+
+@return KMMCErrNone
+*/
+EXPORT_C TMMCErr TMMCStateMachine::Jump(TMMCErr (*anEntry)(TAny*), TBool aSuspend)
+	{
+	iStack[iSP].iFunction = anEntry;
+	iStack[iSP].iState = 0;
+	iStack[iSP].iTrapMask = 0;
+	if( !aSuspend )
+		iSuspend = EFalse;
+	return( 0 );
+	}
+
+
+
+
+//	--------  class DMMCStack  --------
+
+#pragma warning( disable : 4355 )	// this used in initializer list
+EXPORT_C DMMCStack::DMMCStack(TInt /*aBus*/, DMMCSocket* aSocket)
+/**
+ * Constructs a DMMCStack object
+ * @param aBus Unused
+ * @param aSocket A pointer to the associated socket.
+ */
+	: iWorkSet(),
+	iReadyQueue(),
+	iEntryQueue(),
+	iStackDFC(DMMCStack::StackDFC, this, 1),
+	iSelectedCard(TUint16(~0)),
+	iSocket(aSocket),
+	iStackSession(NULL),
+	iAutoUnlockSession(TMMCCallBack(AutoUnlockCBST, this)),
+	iInitState(EISPending),
+	iInitialise(ETrue),
+	iCurrentDSR(),
+	iConfig(),
+	iRCAPool(),
+	iMasterConfig()
+	{
+//	iStackState(0),
+//	iLockingSessionP(NULL),
+//	iAttention(EFalse),
+//	iAbortReq(EFalse),
+//	iCompReq(EFalse),
+//	iDoorOpened(EFalse),
+//	iPoweredUp(EFalse),
+//	iDFCRunning(EFalse),
+//	iAbortAll(EFalse),
+//	iAllExitCode(0),
+//	iSessionP(NULL),
+//	iCurrentOpRange(0),
+//	iCardsPresent(0),
+//	iMaxCardsInStack(0)
+	}
+#pragma warning( default : 4355 )
+
+EXPORT_C TInt DMMCStack::Init()
+/**
+ * Initialises the generic MMC stack.
+ * @return KErrNone if successful, standard error code otherwise.
+ */
+	{
+	// allocate and initialize session object
+	if ((iStackSession = AllocSession(TMMCCallBack(StackSessionCBST, this))) == 0)
+		return(KErrNoMemory);
+
+	// create helper class
+	if ((iBody = new DBody(*this)) == NULL)
+		return(KErrNoMemory);
+
+	iStackSession->SetStack(this);
+
+	iStackDFC.SetDfcQ(&iSocket->iDfcQ);
+
+	// Get the maximal number of cards from ASSP layer
+	iMaxCardsInStack = iSocket->TotalSupportedCards();
+	if ( iMaxCardsInStack > KMaxMMCardsPerStack )
+		iMaxCardsInStack=KMaxMMCardsPerStack;
+
+	TInt r = iCardArray->AllocCards();
+
+	return(r);
+	}
+
+EXPORT_C void DMMCStack::PowerUpStack()
+/**
+ * Enforce stack power-up and initialisation.
+ * This is an asynchronous operation, which calls DMMCSocket::PowerUpSequenceComplete upon completion.
+ */
+	{
+	__KTRACE_OPT(KPBUS1,Kern::Printf("=mst:pus"));
+
+	if (iPSLBuf == NULL)
+		{
+		GetBufferInfo(&iPSLBuf, &iPSLBufLen);
+		iMinorBufLen = KMinMinorBufSize;
+		}
+
+	ReportPowerDown();							// ensure power will be switch on regardless
+
+	Scheduler( iInitialise );
+	}
+
+void DMMCStack::QSleepStack()
+/**
+ * Schedules a session to place media in Sleep State
+ */
+	{
+	__KTRACE_OPT(KPBUS1,Kern::Printf("=mst:qsleep"));
+
+	Scheduler( iSleep );
+	}
+
+EXPORT_C void DMMCStack::PowerDownStack()
+/**
+ * Enforce stack power down.
+ * Clients generally shouldn't need to concern themselves with powering down a stack 
+ * unless they specifically need to perform a power reset of a card.  If a driver fails to 
+ * open then normal practise is for that driver to leave the card powered so that any subsequent 
+ * driver which may attempt to open immediately after this failed attempt won't have to re-power the card. 
+ * If no driver successfully opens on the card then the Controllers inactivity/not in use 
+ * timeout system can be left to power it down.
+ */
+	{
+	__KTRACE_OPT(KPBUS1,Kern::Printf("=mst:pds"));
+
+	ReportPowerDown();
+	iInitState = EISPending;
+	DoPowerDown();
+
+	TBool cardRemoved = (iStackState & KMMCStackStateCardRemoved);
+	for (TUint i=0;i<iMaxCardsInStack;i++)
+		{
+		TMMCard& card = iCardArray->Card(i);
+		card.SetBusWidth(1);
+		card.SetHighSpeedClock(0);
+		if (cardRemoved)
+			{
+		    iCardArray->DeclareCardAsGone(i);
+			}
+		else
+			{
+			// set the locked bit if the card has a password - need to do this 
+			// now that RLocalDrive::Caps() no longer powers up the stack
+			if (card.HasPassword())
+				{
+				TMapping* pmp = iSocket->iPasswordStore->FindMappingInStore(card.CID());
+				if (!pmp || pmp->iState != TMapping::EStValid)
+					{
+					*((TUint32*) &card.iStatus) |= KMMCStatCardIsLocked;
+					}
+				}
+			
+			// Remove card state flags, after a power cycle all cards are in idle state
+			*((TUint32*) &card.iStatus) &= ~KMMCStatCurrentStateMask;
+			}
+		}
+	if (cardRemoved)
+	    iStackState &= ~KMMCStackStateCardRemoved;
+
+
+	iSocket->iVcc->SetState(EPsuOff);
+	if (iSocket->iVccCore)
+		iSocket->iVccCore->SetState(EPsuOff);
+
+	// Cancel timers, reset ASSP, cancel stack DFC & remove session from workset 
+	// to ensure stack doesn't wake up again & attempt to dereference iSessionP
+ 	if (iSessionP)
+		Abort(iSessionP);
+
+	iStackDFC.Cancel();
+
+	// The stack may have powered down while attempting to power up (e.g. because a card has not responded), 
+	// so ensure stack doesn't attempt to initialize itself again until next PowerUpStack()
+	iInitialise = EFalse;
+	iStackState &= ~(KMMCStackStateInitInProgress | KMMCStackStateInitPending | KMMCStackStateBusInconsistent | KMMCStackStateWaitingDFC);
+	iSessionP = NULL;
+	}
+
+//
+// DMMCStack:: --- Stack Scheduler and its supplementary functions ---
+//
+DMMCStack::TMMCStackSchedStateEnum DMMCStack::SchedGetOnDFC()
+/**
+ * Initiates stack DFC. Returns either Continue or Loop.
+ */
+	{
+	__KTRACE_OPT(KPBUS1,Kern::Printf("=mst:sgd"));
+
+	if( iDFCRunning )
+		return( ESchedContinue );
+
+	if( (iStackState & KMMCStackStateWaitingDFC) == 0 )
+		{
+		__KTRACE_OPT(KPBUS1,Kern::Printf("=mst:sgd:q"));
+		iStackState |= KMMCStackStateWaitingDFC;
+		if (NKern::CurrentContext()==NKern::EInterrupt)
+			iStackDFC.Add();
+		else
+			iStackDFC.Enque();
+		}
+
+	return( ESchedLoop );
+	}
+
+void DMMCStack::SchedSetContext(DMMCSession* aSessP)
+/**
+ * Sets up the specified session as the current session.
+ * Invoked by JobChooser and Initialiser.
+ * @param aSessP A pointer to the session.
+ */
+	{
+	__KTRACE_OPT(KPBUS1,Kern::Printf("=mst:ssc"));
+
+	if( (iStackState & (KMMCStackStateInitPending|KMMCStackStateBusInconsistent)) != 0 &&
+		aSessP->iSessionID != ECIMInitStack )
+		{
+		iInitialise = ETrue;
+		return;
+		}
+
+	if( iSessionP != aSessP )
+		{
+		iStackState |= KMMCStackStateReScheduled;
+		MergeConfig( aSessP );
+
+		if( aSessP->iSessionID == ECIMInitStack )
+			iInitialise = ETrue;
+		else
+			if( InitStackInProgress() )
+				MarkComplete( aSessP, KMMCErrStackNotReady );
+			else
+				if( aSessP->iBrokenLock )
+					MarkComplete( aSessP, KMMCErrBrokenLock );
+
+		iSessionP = aSessP;
+		}
+
+	iSessionP->iState &= ~KMMCSessStateDoReSchedule;
+	}
+
+void DMMCStack::SchedDoAbort(DMMCSession* aSessP)
+/**
+ * Aborts asynchronous activities of a session aSessP
+ * @param aSessP A pointer to the session to be aborted.
+ */
+	{
+	__KTRACE_OPT(KPBUS1,Kern::Printf("=mst:sda"));
+
+#ifdef __EPOC32__
+	if( aSessP->iBlockOn & KMMCBlockOnPollTimer )
+		aSessP->iPollTimer.Cancel();
+
+	if( aSessP->iBlockOn & KMMCBlockOnRetryTimer )
+		aSessP->iRetryTimer.Cancel();
+
+	if( aSessP->iBlockOn & KMMCBlockOnPgmTimer )
+		aSessP->iProgramTimer.Cancel();
+#endif	// #ifdef __EPOC32__
+
+	if( aSessP->iBlockOn & KMMCBlockOnWaitToLock )
+		iStackState &= ~KMMCStackStateWaitingToLock;
+
+	if( aSessP->iBlockOn & (KMMCBlockOnASSPFunction | KMMCBlockOnInterrupt | KMMCBlockOnDataTransfer) )
+		ASSPReset();
+
+	if( (aSessP->iState & (KMMCSessStateInProgress|KMMCSessStateCritical)) ==
+		 (KMMCSessStateInProgress|KMMCSessStateCritical) )
+		iStackState |= KMMCStackStateInitPending;
+	
+	
+	(void)__e32_atomic_and_ord32(&aSessP->iBlockOn, ~(KMMCBlockOnPollTimer | KMMCBlockOnRetryTimer |
+						  							  KMMCBlockOnWaitToLock | KMMCBlockOnASSPFunction | 
+						  							  KMMCBlockOnInterrupt | KMMCBlockOnDataTransfer) );
+	}
+
+DMMCStack::TMMCStackSchedStateEnum DMMCStack::SchedResolveStatBlocks(DMMCSession* aSessP)
+/**
+ * Checks static blocking conditions and removes them as necessary
+ * @param aSessP A pointer to the session.
+ * @return EschedContinue or ESchedLoop (if scheduler is to be restarted)
+ */
+	{
+
+	__KTRACE_OPT(KPBUS1,Kern::Printf("=mst:srsb"));
+
+	if( (aSessP->iBlockOn & KMMCBlockOnCardInUse) && aSessP->iCardP->iUsingSessionP == NULL )
+		aSessP->SynchUnBlock( KMMCBlockOnCardInUse );
+
+	if( (aSessP->iBlockOn & KMMCBlockOnWaitToLock) && iWorkSet.Size() == 1 )
+		{
+		// ECIMLockStack processed here
+		iLockingSessionP = aSessP;					// in this order
+		iStackState &= ~KMMCStackStateWaitingToLock;
+		aSessP->SynchUnBlock( KMMCBlockOnWaitToLock );
+		MarkComplete( aSessP, KMMCErrNone );
+		return( ESchedLoop );
+		}
+
+	return( ESchedContinue );
+	}
+
+DMMCStack::TMMCStackSchedStateEnum DMMCStack::SchedGroundDown(DMMCSession* aSessP, TMMCErr aReason)
+/**
+ * Aborts all asynchronous activities of session aSessP with
+ * iExitCode = aReason. This function conserns itself with asynchronous
+ * activities only; session static state (eg Critical) is not taken into
+ * account. Session dynamic state and action flags (i.e. SafeInGaps,
+ * DoReSchedule and DoDFC) are cleared.
+ * @param aSessP A pointer to the session.
+ * @param aReason The reason for aborting.
+ * @return EschedContinue if everything's done OK.
+ * @return ESchedLoop if the session can not be safely grounded (eg
+ * iStackSession) and should therefore be aborted and/or completed by a
+ * separate scheduler pass.
+ */
+	{
+	__KTRACE_OPT(KPBUS1,Kern::Printf("=mst:sgdn"));
+
+	if( (aSessP == iStackSession) || InitStackInProgress() )
+		return( ESchedLoop );
+	
+	if( aSessP->iState & KMMCSessStateInProgress )
+		{
+		SchedDoAbort( aSessP );
+		//coverity[check_return]
+		//return value is not saved or checked because there is no further uses.
+		aSessP->iMachine.SetExitCode( aReason );
+		aSessP->iState &= ~(KMMCSessStateSafeInGaps | KMMCSessStateDoReSchedule |
+							KMMCSessStateDoDFC);
+		}
+
+	return( ESchedContinue );
+	}
+
+DMMCStack::TMMCStackSchedStateEnum DMMCStack::SchedEnqueStackSession(TMMCSessionTypeEnum aSessID)
+/**
+ * Prepare internal session for InitStack and enque it into WorkSet.
+ * @return EschedContinue or ESchedLoop
+ */
+	{
+	__KTRACE_OPT(KPBUS1,Kern::Printf("=mst:sess"));
+
+		if( iStackSession->IsEngaged() )
+			{
+			MarkComplete( iStackSession, KMMCErrAbort );
+			return( ESchedLoop );
+			}
+
+		iStackSession->SetupCIMControl( aSessID );
+		iWorkSet.Add( iStackSession );
+		iStackSession->iState |= KMMCSessStateEngaged;
+		return( ESchedContinue );
+	}
+
+void DMMCStack::SchedGrabEntries()
+/**
+ * Merges Entry queue into Ready queue. Invoked at the scheduler entry and
+ * after the completion pass
+ */
+	{
+	__KTRACE_OPT(KPBUS1,Kern::Printf("=mst:sge"));
+
+	iAttention = EFalse;		// Strictly in this order
+	if( !iEntryQueue.IsEmpty() )
+		{
+		DISABLEPREEMPTION
+		iReadyQueue.Add( iEntryQueue );
+		RESTOREPREEMPTION
+		}
+	}
+
+void DMMCStack::SchedDisengage()
+/**
+ * This function is called by AbortPass() and CompletionPass() to remove the session
+ * at WorkSet Point, to abort its asynchronous activities (if any) and
+ * clear up the dependent resources
+ */
+	{
+	__KTRACE_OPT(KPBUS1,Kern::Printf("=mst:sd"));
+
+	DMMCSession* sessP = iWorkSet.Remove();
+
+	SchedDoAbort( sessP );
+
+	if( sessP == iSessionP )
+		iSessionP = NULL;
+
+	if( sessP->iCardP != NULL && sessP->iCardP->iUsingSessionP == sessP )
+		sessP->iCardP->iUsingSessionP = NULL;
+
+	// iAutoUnlockSession may attach to more than once card, so need to iterate 
+	// through all cards and clear their session pointers if they match sessP
+	if (sessP == &iAutoUnlockSession)
+		{
+		for (TUint i = 0; i < iMaxCardsInStack; i++)
+			{
+			TMMCard& cd = *(iCardArray->CardP(i));
+			if (cd.iUsingSessionP == sessP)
+				cd.iUsingSessionP = NULL;
+			}
+		}
+
+	if( sessP->iState & KMMCSessStateASSPEngaged )
+		ASSPDisengage();
+
+	sessP->iState = 0;
+	}
+
+inline DMMCStack::TMMCStackSchedStateEnum DMMCStack::SchedAbortPass()
+/**
+ * DMMCStack Scheduler private inline functions. These functions were separated as inline functions
+ * only for the sake of Scheduler() clarity.
+ */
+	{
+	__KTRACE_OPT(KPBUS1,Kern::Printf("=mst:sap"));
+
+	iAbortReq = EFalse;
+	SchedGrabEntries();
+	DMMCSession* sessP;
+
+	iWorkSet.Point();
+
+	while( (sessP = iWorkSet) != NULL )
+		if( iAbortAll || sessP->iDoAbort )
+			SchedDisengage();
+		else
+			iWorkSet++;
+
+	iReadyQueue.Point();
+
+	while( (sessP = iReadyQueue) != NULL )
+		if( iAbortAll || sessP->iDoAbort )
+			{
+			iReadyQueue.Remove();
+			sessP->iState = 0;
+			}
+		else
+			iReadyQueue++;
+
+	if( iAbortReq )
+		return( ESchedLoop );
+
+	// Clearing iAbortAll here is a bit dodgy. It wouldn't work if somebody interrupted us
+	// at this point, enqued a session and then immediately called Reset() - that session
+	// would not be discarded. However, the correct solution (enque Reset() requests
+	// and process them in the Scheduler main loop) seems to be too expensive just to avoid
+	// this particular effect.
+	iAbortAll = EFalse;
+	return( ESchedContinue );
+	}
+
+inline DMMCStack::TMMCStackSchedStateEnum DMMCStack::SchedCompletionPass()
+/**
+ * This function calls back all the sessions waiting to be completed
+ * Returns either Continue or Loop.
+ */
+	{
+	__KTRACE_OPT(KPBUS1,Kern::Printf("=mst:scp"));
+
+	iCompReq = EFalse;
+	DMMCSession* sessP;
+
+	if( iCompleteAllExitCode )
+		{
+		SchedGrabEntries();
+		iWorkSet.Add( iReadyQueue );
+		}
+
+	iWorkSet.Point();
+
+	while( (sessP = iWorkSet) != NULL )
+		if( iCompleteAllExitCode || sessP->iDoComplete )
+			{
+			if( (EffectiveModes(sessP->iConfig) & KMMCModeCompleteInStackDFC) != 0 &&
+				 SchedGetOnDFC() )
+				{
+				// DFC has been queued so return back to main loop.  Next time
+				// SchedGetOnDfc() will return EFalse, and the callback will be called.
+				iCompReq = ETrue;
+				return( ESchedLoop );
+				}
+
+			SchedDisengage();					// calls iWorkSet.Remove
+			sessP->iMMCExitCode |= iCompleteAllExitCode;
+			// Update the controller store if a password operation was in progress.
+			TBool doCallback = ETrue;
+			if (sessP->iSessionID == ECIMLockUnlock)
+				{
+				iSocket->PasswordControlEnd(sessP, sessP->EpocErrorCode());
+				
+				if(sessP->EpocErrorCode() == KErrNone)
+					{
+					sessP->SetupCIMInitStackAfterUnlock();
+					if(sessP->Engage() == KErrNone)
+						{
+						doCallback = EFalse;
+						}
+					}
+				}
+
+			if(sessP->iSessionID == ECIMInitStackAfterUnlock)
+				{
+				// After unlocking the stack, cards may have switched into HS mode
+				// (HS switch commands are only valid when the card is unlocked).
+				//
+				// Therefore, we need to re-negotiate the maximum bus clock again.
+				//
+				// The PSL will use this to set the master config (limiting the clock if
+				// appropriate).
+				//
+				// Note that the clock may change when a specific card is selected.
+				//
+				TUint maxClk;
+				iCardArray->UpdateAcquisitions(&maxClk);
+				SetBusConfigDefaults( iMasterConfig.iBusConfig, maxClk );
+				DoSetClock(maxClk);
+				}
+
+			if(doCallback)
+				{
+				// call media driver completion routine or StackSessionCBST().
+				sessP->iCallBack.CallBack();
+				}
+			}
+		else
+			iWorkSet++;
+
+	if( iCompReq )
+		return( ESchedLoop );
+
+	iCompleteAllExitCode = 0;
+
+	return( ESchedContinue );
+	}
+
+inline DMMCStack::TMMCStackSchedStateEnum DMMCStack::SchedInitStack()
+/**
+ * "Immediate" InitStack initiator. Returns either Continue or Loop.
+ */
+	{
+	__KTRACE_OPT(KPBUS1,Kern::Printf("=mst:sis"));
+
+	if( SchedGetOnDFC() )
+		return( ESchedLoop );
+
+	if( iSessionP != NULL && (iStackState & KMMCStackStateJobChooser) == 0 )
+		{
+		if( (iSessionP->iState & KMMCSessStateInProgress) )
+			{
+			if( SchedGroundDown(iSessionP, KMMCErrPowerDown) )
+				{
+				MarkComplete( iSessionP, KMMCErrPowerDown );
+				return( ESchedLoop );
+				}
+			}
+		else
+			iSessionP->iMachine.Reset();
+		}
+
+	// NB if the current session was InitStack InProgress, JobChooser can not be active;
+	// so we are not going to continue another InitStack as if nothing happened.
+
+	iStackState &= ~(KMMCStackStateInitInProgress|KMMCStackStateInitPending);
+
+	// If there is no current session (e.g. called from PowerUpStack()) or the current
+	// session isn't specifically ECIMInitStack (which it rarely will be) then we have to use
+	// the stack session to perform the stack init.
+	if( iSessionP == NULL || iSessionP->iSessionID != ECIMInitStack )
+		{
+		if( SchedEnqueStackSession(ECIMInitStack) )
+			return( ESchedLoop );
+
+		SchedSetContext( iStackSession );	// make the internal session to be current job
+		}
+
+	// Neither client nor internal session could be blocked here, not even on "BrokenLock"
+	__ASSERT_ALWAYS( (iSessionP->iBlockOn)==0,
+	DMMCSocket::Panic(DMMCSocket::EMMCInitStackBlocked) );
+
+	iStackState |= KMMCStackStateInitInProgress;
+	// nothing can stop this session now; it's safe to clear iInitialise here.
+	iInitialise = EFalse;
+	return( ESchedContinue );
+	}
+
+inline DMMCStack::TMMCStackSchedStateEnum DMMCStack::SchedSleepStack()
+/**
+ * "Immediate" Stack sleep mode. Returns either Continue or Loop.
+ */
+	{
+	__KTRACE_OPT(KPBUS1,Kern::Printf("=mst:SchdSlp!"));
+
+	// Make sure Stack DFC is Running!
+	if( SchedGetOnDFC() )
+		{
+		__KTRACE_OPT(KPBUS1, Kern::Printf("mst:SchdSlp - DFC not running"));
+		return( ESchedLoop );
+		}
+
+	if( iSessionP != NULL && (iStackState & KMMCStackStateJobChooser) == 0 )
+		{
+		if( (iSessionP->iState & KMMCSessStateInProgress) )
+			{
+			// A session has been queued before sleep, 
+			// cancel sleep and loop for next session
+			iSleep = EFalse;
+			return( ESchedLoop );
+			}
+		}
+	
+	// Use the stack session to perform the stack sleep.
+	if( SchedEnqueStackSession(ECIMSleep) )
+		{
+		__KTRACE_OPT(KPBUS1,Kern::Printf("SchdSlp: already Enqued"));
+		// Stack already busy cancel sleep
+		iSleep = EFalse;
+		return( ESchedLoop );
+		}
+
+	SchedSetContext( iStackSession );	// make the internal session to be current job
+
+	// Sleep has now been queued
+	iSleep = EFalse;
+	iStackState |= KMMCStackStateSleepinProgress;
+	__KTRACE_OPT(KPBUS1, Kern::Printf("<mst:SchdSlp"));
+	
+	return( ESchedLoop );
+	}
+
+
+inline TBool DMMCStack::SchedPreemptable()
+/**
+ * Checks if the current session can be preempted
+ */
+	{	// strictly in the following order
+	__KTRACE_OPT(KPBUS1,Kern::Printf("=mst:spe"));
+	
+	if( (iStackState & KMMCStackStateJobChooser) ||
+		(iSessionP->iState & KMMCSessStateDoReSchedule) )
+		return( ETrue );
+
+	if( (iSessionP->iBlockOn & KMMCBlockOnASSPFunction) )
+		return( EFalse );
+
+	TBool preemptDC = EFalse;	
+
+	if (iSessionP->iBlockOn & KMMCBlockOnYielding)
+		{
+		// Added to support yielding the stack for a specific command.
+		preemptDC = ETrue;		
+		}
+	else if( (iSessionP->iBlockOn & KMMCBlockOnDataTransfer) )
+		{
+		// Added for SDIO Read/Wait and SDC support.  This condition
+		// is set at the variant, and determines whether commands may be
+		// issued during the data transfer period.
+		if(!(iSessionP->iState & KMMCSessStateAllowDirectCommands))
+			return( EFalse );
+		
+		// We must consider the remaining blocking conditions
+		// before being sure that we can enable pre-emtion of this session
+		preemptDC = ETrue;
+		}
+
+	if( (iSessionP->iBlockOn & (KMMCBlockOnCardInUse | KMMCBlockOnNoRun)) )
+		return( ETrue );
+	
+	if( (iConfig.iModes & KMMCModeEnablePreemption) == 0 )
+		return( EFalse );
+
+	if( (iSessionP->iBlockOn & KMMCBlockOnGapTimersMask) &&
+		(iConfig.iModes & KMMCModePreemptInGaps) &&
+		(iSessionP->iState & KMMCSessStateSafeInGaps) )
+		return( ETrue );
+
+	if( iSessionP->iBlockOn & KMMCBlockOnInterrupt )
+		return( ETrue );
+
+	if(preemptDC)
+		return( ETrue );
+		
+	return( EFalse );
+	}
+
+inline DMMCStack::TMMCStackSchedStateEnum DMMCStack::SchedSession()
+/**
+ * Current context analyser. Returns Exit, Loop or ChooseJob.
+ */
+	{
+
+	__KTRACE_OPT(KPBUS1,Kern::Printf("=mst:ss"));
+
+	// If no current session selected then we need to choose one
+	if (iSessionP == NULL)
+		return(ESchedChooseJob);
+
+	// Check any static blocking conditions on the current session and remove if possible
+	if (SchedResolveStatBlocks(iSessionP)==ESchedLoop)
+		return(ESchedLoop);
+
+	// If current session is still blocked, see if we could pre-empt the session
+	if (iSessionP->iBlockOn)
+		{
+		if( SchedPreemptable() )
+			return(ESchedChooseJob);
+
+		return( ESchedExit );	// No preemption possible
+		}
+
+	// If the current session has been marked to be 'un-scheduled' then we
+	// need to choose another session if ones available
+	if ( (iSessionP->iState & KMMCSessStateDoReSchedule) )
+		return( ESchedChooseJob );
+
+	// Check if this session requires to be run in DFC context - loop if necessary
+	if ( (iSessionP->iState & KMMCSessStateDoDFC) )
+		{
+		iSessionP->iState &= ~KMMCSessStateDoDFC;
+		if( SchedGetOnDFC()==ESchedLoop )
+			return( ESchedLoop );
+		}
+
+	// Now we actually execute the current session
+	if( iLockingSessionP != NULL )
+		{
+		if( (iStackState & KMMCStackStateLocked) )
+			{
+			if( iSessionP != iLockingSessionP )
+				{
+				iLockingSessionP->iBrokenLock = ETrue;
+				iLockingSessionP = NULL;
+				DeselectsToIssue(KMMCIdleCommandsAtRestart); // use it for the number of deselects as well
+				}
+			}
+		else
+			if( iSessionP == iLockingSessionP )
+				iStackState |= KMMCStackStateLocked;
+		}
+
+	if( iSessionP->iInitContext != iInitContext )
+		{
+		// If the current session's init_stack pass number is set but isn't the same as the current
+		// pass number, it indicates this session is being resumed having tried to recover from
+		// a bus inconsitency by re-initialising the stack. Set the exit code to a special
+		// value so this session can un-wind from where the initial error occured, back to the start.
+		if( iSessionP->iInitContext != 0 )
+			//coverity[check_return]
+			//return value is not saved or checked because there is no further uses.
+			iSessionP->iMachine.SetExitCode(KMMCErrInitContext | iSessionP->iMachine.ExitCode());
+
+		iSessionP->iInitContext = iInitContext;
+		}
+
+	iStackState &= ~KMMCStackStateJobChooser;
+	iSessionP->iState &= ~KMMCSessStateSafeInGaps;
+
+	// Execute the session state machine until it completes, is blocked or is aborted.
+	TMMCErr exitCode = iSessionP->iMachine.Dispatch();
+
+	iStackState &= ~KMMCStackStateReScheduled;
+
+	if( exitCode )
+		MarkComplete( iSessionP, (exitCode & ~KMMCErrBypass) );
+
+	return(ESchedLoop);
+	}
+
+TBool DMMCStack::SchedYielding(DMMCSession* aSessP)
+/**
+ * Check whether the scheduler should yield to another command
+ */
+	{
+	// Test whether a full loop through the sessions has occurred during a yield
+	if ((aSessP->iBlockOn & KMMCBlockOnYielding) && (iStackState & KMMCStackStateYielding))
+		{
+		// We've looped, now stop yielding
+		aSessP->iBlockOn &= ~KMMCBlockOnYielding;
+		iStackState &= ~KMMCStackStateYielding;
+		}
+	return(iStackState & KMMCStackStateYielding) != 0;
+	}
+
+TBool DMMCStack::SchedAllowDirectCommands(DMMCSession* aSessP)
+/**
+ * Check whether direct only commands can be run.
+ */
+	{
+	TBool allowDirectCommands = EFalse;
+
+	// Test the remaining sessions to see if they have a DMA data transfer blockage which allow direct commands only
+	DMMCSession* testSessP = aSessP;
+	do
+		{
+		if ((testSessP->iBlockOn & KMMCBlockOnDataTransfer) && (testSessP->iState & KMMCSessStateAllowDirectCommands))
+			allowDirectCommands = ETrue;
+		testSessP = testSessP->iLinkP;
+		}			
+	while((aSessP != testSessP) && (testSessP != NULL));
+
+	return(allowDirectCommands);
+	}
+
+inline DMMCStack::TMMCStackSchedStateEnum DMMCStack::SchedChooseJob()
+/**
+ * Find an unblocked job to run. Returns Exit or Loop.
+ */
+	{
+	__KTRACE_OPT(KPBUS1,Kern::Printf("=mst:scj"));
+
+	iStackState |= KMMCStackStateJobChooser;
+	SchedGrabEntries();
+	DMMCSession* sessP = NULL;
+
+	if( iLockingSessionP != NULL )		// if stack is already locked we accept only locking session
+		{
+		if( iWorkSet.IsEmpty() && iReadyQueue.Point(iLockingSessionP) )
+			sessP = iReadyQueue.Remove();
+		}
+	else								// otherwise we might add a fresh session from reserve
+		{
+		iStackState &= ~KMMCStackStateLocked;
+		if( iWorkSet.Size() < KMMCMaxJobsInStackWorkSet &&		// if work set is not too big
+			!iReadyQueue.IsEmpty() &&							// and there are ready sessions
+			(iStackState & KMMCStackStateWaitingToLock) == 0 )	// and nobody waits to lock us
+			{
+			iReadyQueue.Point();								// at marker to preserve FIFO
+			sessP = iReadyQueue.Remove();
+			}
+		}
+
+	if( sessP != NULL )
+		{
+		iWorkSet.Add( sessP );
+
+		if( sessP->iSessionID == ECIMLockStack )
+			{
+			sessP->SynchBlock( KMMCBlockOnWaitToLock | KMMCBlockOnNoRun );
+			sessP->iBrokenLock = EFalse;
+			iStackState |= KMMCStackStateWaitingToLock;
+			}
+		}
+	
+	if( iSessionP  != NULL )
+		iWorkSet.AdvanceMarker();		// move current session to the end of the queue
+
+	iWorkSet.Point();
+	
+	while( (sessP = iWorkSet) != NULL )
+		{
+		// first, remove all static blocking conditions
+		if( SchedResolveStatBlocks(sessP) )
+			return( ESchedLoop );
+
+		TBool scheduleSession = ETrue;
+		// Test whether we are yielding 
+		if (SchedYielding(sessP) && (sessP->Command().iSpec.iCommandType != iYieldCommandType))
+			scheduleSession = EFalse;
+		// Test whether this session is blocked
+		else if (sessP->iBlockOn)
+			scheduleSession = EFalse;
+		// Test whether we can only handle direct commands
+		else if (SchedAllowDirectCommands(sessP) && (sessP->Command().iSpec.iCommandType != ECmdTypeADC))
+			scheduleSession = EFalse;
+		
+		if (scheduleSession)
+			{
+			iWorkSet.SetMarker();
+			SchedSetContext( sessP );
+			return( ESchedLoop );
+			}
+			
+		iWorkSet++;
+		}
+	
+	return( ESchedExit );		
+	}
+
+void DMMCStack::StackDFC(TAny* aStackP)
+/**
+ * This DFC is used to startup Stack Scheduler from the background.
+ */
+	{
+	__KTRACE_OPT(KPBUS1,Kern::Printf("=mst:sdf"));
+
+	DMMCStack* const stackP = static_cast<DMMCStack*>(aStackP);
+	stackP->Scheduler( stackP->iDFCRunning );
+	}
+
+void DMMCStack::Scheduler(volatile TBool& aFlag)
+/**
+ * This is the main function which controls, monitors and synchronises session execution.
+ * It's divided into the entry function Scheduler() and the scheduling mechanism itself,
+ * DoSchedule()
+ */
+	{
+	__KTRACE_OPT(KPBUS1,Kern::Printf("=mst:sch"));
+
+	DISABLEPREEMPTION
+	aFlag = ETrue;
+
+	if( iStackState & KMMCStackStateRunning )
+		{
+		RESTOREPREEMPTION
+		return;
+		}
+
+	iStackState |= KMMCStackStateRunning;
+	RESTOREPREEMPTION
+	DoSchedule();
+	}
+
+void DMMCStack::DoSchedule()
+	{
+	__KTRACE_OPT(KPBUS1,Kern::Printf(">mst:dos"));
+
+	for(;;)
+		{
+		for(;;)
+			{
+			if( iAbortReq && SchedAbortPass() )
+				continue;
+
+			if( iDFCRunning )
+				iStackState &= ~KMMCStackStateWaitingDFC;
+			else
+				if( iStackState & KMMCStackStateWaitingDFC )
+					break;
+
+			if( iCompReq && SchedCompletionPass() )
+				continue;
+
+			if( iInitialise && SchedInitStack() )
+				continue;
+						
+			if( iSleep && SchedSleepStack() )
+				continue;
+
+			iAttention = EFalse;
+
+			DMMCStack::TMMCStackSchedStateEnum toDo = SchedSession();
+
+			if( toDo == ESchedLoop )
+				continue;
+
+			if( toDo == ESchedExit )
+				break;
+
+			if( SchedChooseJob() == ESchedExit )
+				break;
+			}
+
+		DISABLEPREEMPTION
+
+		if( !iAbortReq &&
+			((iStackState & KMMCStackStateWaitingDFC) ||
+			 (iCompReq | iInitialise | iAttention)==0) ||
+			 ((iSessionP) && (iSessionP->iState & KMMCSessStateAllowDirectCommands)))
+			{
+			// Clear DFC flag here in case somebody was running scheduler in the background
+			// when DFC turned up. This should never really happen, but with EPOC who knows
+			iStackState &= ~KMMCStackStateRunning;
+			iDFCRunning = EFalse;
+			
+			RESTOREPREEMPTION
+			__KTRACE_OPT(KPBUS1,Kern::Printf("<mst:dos"));
+			return;
+			}
+
+		RESTOREPREEMPTION
+		}
+	}
+
+//
+// DMMCStack:: --- Session service ---
+//
+void DMMCStack::Add(DMMCSession* aSessP)
+/**
+ * Adds session aSessP to the EntryQueue (asynchronous function)
+ */
+	{
+	ASSERT_NOT_ISR_CONTEXT
+	__KTRACE_OPT(KPBUS1,Kern::Printf(">MMC:Add %d",TUint(aSessP->iSessionID)));
+
+	DISABLEPREEMPTION
+	iEntryQueue.Add( aSessP );
+	aSessP->iState |= KMMCSessStateEngaged;
+	RESTOREPREEMPTION
+	Scheduler( iAttention );
+	}
+
+void DMMCStack::Abort(DMMCSession* aSessP)
+/**
+ * Aborts a session
+ */
+	{
+	ASSERT_NOT_ISR_CONTEXT
+	__KTRACE_OPT(KPBUS1,Kern::Printf("=mst:abt"));
+
+	if( !aSessP->IsEngaged() )
+		return;
+
+	aSessP->iDoAbort = ETrue;
+	aSessP->iMachine.Abort();
+
+	Scheduler( iAbortReq );
+	}
+
+void DMMCStack::Stop(DMMCSession* aSessP)
+/**
+ * Signals session to stop
+ */
+	{
+	ASSERT_NOT_ISR_CONTEXT
+	__KTRACE_OPT(KPBUS1,Kern::Printf("=mst:stp"));
+
+	if( !aSessP->IsEngaged() )
+		return;
+
+	aSessP->iDoStop = ETrue;
+	}
+
+EXPORT_C void DMMCStack::Block(DMMCSession* aSessP, TUint32 aFlag)
+	{
+	ASSERT_NOT_ISR_CONTEXT
+	__KTRACE_OPT(KPBUS1,Kern::Printf("=mst:blk"));
+
+	if( !aSessP->IsEngaged() )
+		return;
+
+	__KTRACE_OPT(KPBUS1,Kern::Printf("=mst:blk:[aFlag=%08x, iBlockOn=%08x]", aFlag, aSessP->iBlockOn));
+
+	(void)__e32_atomic_ior_ord32(&aSessP->iBlockOn, aFlag);
+	}
+
+EXPORT_C void DMMCStack::UnBlock(DMMCSession* aSessP, TUint32 aFlag, TMMCErr anExitCode)
+/**
+ * aFlag is a bitset of KMMCBlockOnXXX events that have occured.  If the stack's
+ * session is waiting on all of these events, then it is scheduled.
+ */
+	{
+	__KTRACE_OPT(KPBUS1,Kern::Printf("=mst:ubl"));
+
+	if (aSessP != NULL)
+		{
+		__KTRACE_OPT(KPBUS1,Kern::Printf("=mst:ubl:[aFlag=%08x, iBlockOn=%08x", aFlag, aSessP->iBlockOn));
+
+		if( (aSessP->iBlockOn & aFlag) == 0 )
+			return;
+
+		// Must be either in a DFC or have the KMMCSessStateDoDFC flag set
+		__ASSERT_DEBUG( 
+			(aSessP->iState & KMMCSessStateDoDFC) != 0 || 
+			NKern::CurrentContext() != NKern::EInterrupt,
+			DMMCSocket::Panic(DMMCSocket::EMMCUnblockingInWrongContext));
+
+		(void)__e32_atomic_and_ord32(&aSessP->iBlockOn, ~aFlag);
+		aSessP->iMachine.SetExitCode( anExitCode );
+
+		if( aSessP->iBlockOn == 0 )
+			Scheduler( iAttention );
+		}
+	}
+
+void DMMCStack::UnlockStack(DMMCSession* aSessP)
+/**
+ * Removes stack lock. Asynchronous function.
+ */
+	{
+	ASSERT_NOT_ISR_CONTEXT
+	__KTRACE_OPT(KPBUS1,Kern::Printf("=mst:ust"));
+
+	aSessP->iBrokenLock = EFalse;
+
+	if( aSessP == iLockingSessionP )
+		{
+		iLockingSessionP = NULL;
+		Scheduler( iAttention );
+		}
+	}
+
+EXPORT_C TInt DMMCStack::Stop(TMMCard* aCardP)
+/**
+ * Completes all sessions operating with a specified card with KMMCErrAbort.
+ * Returns either KErrNone or KErrServerBusy.
+ */
+	{
+	ASSERT_NOT_ISR_CONTEXT
+	__KTRACE_OPT(KPBUS1,Kern::Printf("=mst:stp"));
+
+	DISABLEPREEMPTION
+
+	if( iStackState & KMMCStackStateRunning )
+		{
+		RESTOREPREEMPTION
+		return( KErrServerBusy );	// can not operate in foreground
+		}
+
+	iStackState |= KMMCStackStateRunning;
+	RESTOREPREEMPTION
+
+	DMMCSession* sessP;
+	SchedGrabEntries();
+
+	iWorkSet.Point();
+
+	while( (sessP = iWorkSet++) != NULL )
+		if( sessP->iCardP == aCardP )
+			MarkComplete( sessP, KMMCErrAbort );
+
+	iReadyQueue.Point();
+
+	while( (sessP = iReadyQueue) != NULL )
+		if( sessP->iCardP == aCardP )
+			{
+			MarkComplete( sessP, KMMCErrAbort );
+			iReadyQueue.Remove();
+			iWorkSet.Add( sessP );
+			}
+		else
+			iReadyQueue++;
+
+	SchedGetOnDFC();
+	DoSchedule();
+	return( KErrNone );
+	}
+
+void DMMCStack::MarkComplete(DMMCSession* aSessP, TMMCErr anExitCode)
+/**
+ * Marks session to be completed on the next scheduler pass.
+ */
+	{
+	ASSERT_NOT_ISR_CONTEXT
+	__KTRACE_OPT(KPBUS1,Kern::Printf("=mst:mcp"));
+
+	aSessP->SynchBlock( KMMCBlockOnNoRun );
+	aSessP->iMMCExitCode = anExitCode;
+	aSessP->iDoComplete = ETrue;
+	iCompReq = ETrue;
+	}
+
+//
+// DMMCStack:: --- Miscellaneous ---
+//
+EXPORT_C TUint32 DMMCStack::EffectiveModes(const TMMCStackConfig& aClientConfig)
+/**
+ * Calculates effective client modes as real client modes merged with iMasterConfig modes
+ */
+	{
+	__KTRACE_OPT(KPBUS1,Kern::Printf("=mst:em"));
+
+	const TUint32 masterMode = (iMasterConfig.iModes & iMasterConfig.iUpdateMask) |
+								(KMMCModeDefault & ~iMasterConfig.iUpdateMask);
+
+	const TUint32 c = aClientConfig.iClientMask;
+	const TUint32 u = aClientConfig.iUpdateMask;
+	const TUint32 m = aClientConfig.iModes;
+	const TUint32 userMode = (c & ((m & u) | ~u)) | (m & KMMCModeMask);
+	const TUint32 userMask = (u | KMMCModeClientMask) &
+							((masterMode & KMMCModeMasterOverrides) | ~KMMCModeMasterOverrides);
+
+	const TUint32 effectiveMode = (userMode & userMask) | (masterMode & ~userMask);
+
+	if( effectiveMode & KMMCModeEnableClientConfig )
+		return( effectiveMode );
+	else
+		return( (effectiveMode & KMMCModeClientOverrides) |
+				(masterMode & ~(KMMCModeClientOverrides | KMMCModeClientMask)) );
+	}
+
+void DMMCStack::MergeConfig(DMMCSession* aSessP)
+/**
+ * Merges client and master configuration into iConfig
+ */
+	{
+	__KTRACE_OPT(KPBUS1,Kern::Printf("=mst:mc"));
+
+	TMMCStackConfig& cC = aSessP->iConfig;
+	TMMCStackConfig& mC = iMasterConfig;
+	const TUint32 modes = EffectiveModes( cC );
+	const TUint32 mastM = mC.iClientMask;
+
+	iConfig.iModes = modes;
+
+	iConfig.iPollAttempts =
+			(modes & KMMCModeClientPollAttempts)
+		?	cC.iPollAttempts
+		:	((mastM & KMMCModeClientPollAttempts) ? mC.iPollAttempts : KMMCMaxPollAttempts);
+
+	iConfig.iTimeOutRetries =
+			(modes & KMMCModeClientTimeOutRetries)
+		?	cC.iTimeOutRetries
+		:	((mastM & KMMCModeClientTimeOutRetries) ? mC.iTimeOutRetries : KMMCMaxTimeOutRetries);
+
+	iConfig.iCRCRetries =
+			(modes & KMMCModeClientCRCRetries)
+		?	cC.iCRCRetries
+		:	((mastM & KMMCModeClientCRCRetries) ? mC.iCRCRetries : KMMCMaxCRCRetries);
+
+	iConfig.iUnlockRetries =
+			(modes & KMMCModeClientUnlockRetries)
+		?	cC.iUnlockRetries
+		:	((mastM & KMMCModeClientUnlockRetries) ? mC.iUnlockRetries : KMMCMaxUnlockRetries);
+		
+	iConfig.iOpCondBusyTimeout =
+			(modes & KMMCModeClientiOpCondBusyTimeout)
+		?	cC.iOpCondBusyTimeout
+		:	((mastM & KMMCModeClientiOpCondBusyTimeout) ? mC.iOpCondBusyTimeout : KMMCMaxOpCondBusyTimeout);	
+
+	// There are no default constants defining BusConfig parameters.
+	// iMasterConfig.iBusConfig must be initialised by ASSP layer
+
+	// _?_? The code below can be modified later for a card controlled session
+	// to include CSD analisys and calculate time-out and clock parameters on that basis.
+	// As it written now, the defaults for all cards will be the same.
+
+	if( modes & KMMCModeClientBusClock )
+		{
+		TUint clock = cC.iBusConfig.iBusClock;
+		if( clock > mC.iBusConfig.iBusClock )
+			clock = mC.iBusConfig.iBusClock;
+		if( clock < KMMCBusClockFOD )
+			clock = KMMCBusClockFOD;
+		DoSetClock(clock);
+		}
+	else if( modes & KMMCModeCardControlled && aSessP->CardP() )
+		{
+		TUint clock = MaxTranSpeedInKilohertz(*aSessP->CardP());
+		if( clock > mC.iBusConfig.iBusClock )
+			clock = mC.iBusConfig.iBusClock;
+		if( clock < KMMCBusClockFOD )
+			clock = KMMCBusClockFOD;
+		DoSetClock(clock);
+		}
+	else
+		DoSetClock(mC.iBusConfig.iBusClock);
+
+	iConfig.iBusConfig.iClockIn = (modes & KMMCModeClientClockIn)
+							? cC.iBusConfig.iClockIn
+							: mC.iBusConfig.iClockIn;
+
+	iConfig.iBusConfig.iClockOut = (modes & KMMCModeClientClockOut)
+							? cC.iBusConfig.iClockOut
+							: mC.iBusConfig.iClockOut;
+
+	iConfig.iBusConfig.iResponseTimeOut = (modes & KMMCModeClientResponseTimeOut)
+							? cC.iBusConfig.iResponseTimeOut
+							: mC.iBusConfig.iResponseTimeOut;
+
+	iConfig.iBusConfig.iDataTimeOut = (modes & KMMCModeClientDataTimeOut)
+							? cC.iBusConfig.iDataTimeOut
+							: mC.iBusConfig.iDataTimeOut;
+
+	iConfig.iBusConfig.iBusyTimeOut = (modes & KMMCModeClientBusyTimeOut)
+							? cC.iBusConfig.iBusyTimeOut
+							: mC.iBusConfig.iBusyTimeOut;
+	}
+
+TBool DMMCStack::StaticBlocks()
+/**
+ * This function realises the potential blocking conditions of the current session.
+ * Returns ETrue if the session has to be stopped right now
+ */
+	{
+	__KTRACE_OPT(KPBUS1,Kern::Printf("=mst:stb"));
+
+	if( iSessionP->iDoStop )
+		{
+		MarkComplete( iSessionP, KMMCErrAbort );
+		return( ETrue );
+		}
+
+	if( !iDFCRunning && (iSessionP->iState & KMMCSessStateDoDFC) )
+		return( ETrue );
+
+	return( (iSessionP->iState & KMMCSessStateDoReSchedule) != 0 );
+	}
+
+
+EXPORT_C TBool DMMCStack::CardDetect(TUint /*aCardNumber*/)
+/**
+ * Returns ETrue when a card is present in the card socket 'aCardNumber'.
+ * Default implementation when not provided by ASSP layer.
+ */
+	{
+	return(ETrue);
+	}
+
+EXPORT_C TBool DMMCStack::WriteProtected(TUint /*aCardNumber*/)
+/**
+ * Returns ETrue when the card in socket 'aCardNumber' is mechanically write
+ * protected.
+ * Default implementation when not provided by ASSP layer.
+ */
+	{
+	return(EFalse);
+	}
+
+//	--------  DMMCStack State Machine functions  --------
+//
+
+// Auxiliary SM function service
+
+void DMMCStack::StackSessionCBST(TAny* aStackP)
+/**
+ * Stack Session completion routine.
+ */
+	{
+
+	__KTRACE_OPT(KPBUS1,Kern::Printf("=mst:sscbs"));
+	static_cast<DMMCStack *>(aStackP)->StackSessionCB();
+	}
+
+
+TInt DMMCStack::StackSessionCB()
+	{
+	__KTRACE_OPT(KPBUS1,Kern::Printf("=mst:sscb "));
+
+	if (iStackState & KMMCStackStateSleepinProgress)
+		{
+		// Sleep completed update stack state
+		iStackState &= ~KMMCStackStateSleepinProgress;
+		return( 0 );
+		}
+	
+	TMMCErr mmcError = iStackSession->MMCExitCode();
+	iStackState &= ~KMMCStackStateInitInProgress;
+
+	TInt  errCode   = KErrNone;
+	TBool anyLocked = EFalse;
+
+	if (mmcError != KMMCErrNone)
+		{
+		//
+		// StackSessionCB is the completion callback for the internal initialisation/power-up
+		// session, so we never expect a callback while initialisation is still in progress.
+		// - unless a card has failed to respond and the controller has not detected the error (!)
+		//
+		errCode = KErrTimedOut;	// this error code is not sticky, so should allow stack to be powered up again
+		iInitialise = EFalse;
+		iStackState &= ~(KMMCStackStateInitInProgress | KMMCStackStateInitPending | KMMCStackStateBusInconsistent);
+		}
+	else
+		{
+		if (! iCardArray->CardsPresent())
+			{
+			errCode = KErrNotReady;
+			}
+		else
+			{
+			// Stack initialized ok, so complete request or start auto-unlock
+
+			iInitState = EISDone;
+
+			// remove bindings from password store for cards that do not have passwords
+			TUint i;
+			for (i = 0; i < iMaxCardsInStack; ++i)
+				{
+				TMMCard& cd = *(iCardArray->CardP(i));
+				if (cd.IsPresent())
+					{
+					if (cd.HasPassword())
+						anyLocked = ETrue;
+					else
+						{
+						TMapping* pmp = iSocket->iPasswordStore->FindMappingInStore(cd.CID());
+						if (pmp)
+							pmp->iState = TMapping::EStInvalid;
+						}
+					}	// if (cd.IsPresent())
+				}	// for (i = 0; i < iMaxCardsInStack; ++i)
+
+			// if any cards are locked then launch auto-unlock mechanism
+			if (anyLocked)
+				{
+				//
+				// During power up (stack session context), we use the iAutoUnlockSession
+				// to perform auto-unlock of the cards.  Upon completion of the AutoUnlock
+				// state machine, the local media subsystem is notified via ::PowerUpSequenceComplete
+				//
+				iAutoUnlockSession.SetStack(this);
+				iAutoUnlockSession.SetupCIMAutoUnlock();
+
+				errCode = iAutoUnlockSession.Engage();
+				if(errCode == KErrNone)
+					{
+					// don't complete power up request yet
+					//  - This will be done in DMMCStack::AutoUnlockCB()
+					return 0;
+					}
+				}
+			}	// else ( !iCardArray->CardsPresent() )
+		}
+
+	if(errCode == KErrNone)
+		{
+		//
+		// No cards are locked (otherwise we will have engaged iAutoUnlockSession) and we
+		// have encountered no error, so can now continue with the second-stage initialisation
+		// phase (InitStackAfterUnlock).  This performs initialisation that can only be
+		// performed when a card is unlocked (such as setting bus width, speed class etc..)
+		//
+		// iAutoUnlockSession::AutoUnlockCB will complete power-up by calling ::PowerUpSequenceComplete
+		//
+		iAutoUnlockSession.SetStack(this);
+		iAutoUnlockSession.iCardP = NULL;
+		iAutoUnlockSession.SetupCIMInitStackAfterUnlock();
+		errCode = iAutoUnlockSession.Engage();
+		}
+
+	if(errCode != KErrNone)
+		{
+		//
+		// We have encountered an error during power up initialisation
+		//  - Complete the request and notify the local media subsystem.
+		//
+
+		// Calling PowerUpSequenceComplete() with an error may result in the media driver being closed which will delete
+		// the media driver's session, so the stack must be made re-entrant here to allow all references to any engaged 
+		// sessions to be removed from the stack immediately to prevent the stack from referencing a deleted object
+		__ASSERT_ALWAYS(iStackState & KMMCStackStateRunning, DMMCSocket::Panic(DMMCSocket::EMMCNotInDfcContext));
+		iStackState &= ~KMMCStackStateRunning;
+		iSocket->PowerUpSequenceComplete(errCode);
+		iStackState |= KMMCStackStateRunning;
+
+		}
+
+	return( 0 );
+	}
+
+void DMMCStack::AutoUnlockCBST(TAny *aStackP)
+	{
+	__KTRACE_OPT(KPBUS1,Kern::Printf("=mst:aucbs"));
+
+	static_cast<DMMCStack *>(aStackP)->AutoUnlockCB();
+	}
+
+
+TInt DMMCStack::AutoUnlockCB()
+	{
+	__KTRACE_OPT(KPBUS1,Kern::Printf("=mst:aucb"));
+
+	// This is the session end callback for iAutoUnlockSession,
+	// called at the end of the power up and initialisation process.
+
+	TInt epocErr = iAutoUnlockSession.EpocErrorCode();
+
+	// Calling PowerUpSequenceComplete() with an error may result in the media driver being closed which will delete
+	// the media driver's session, so the stack must be made re-entrant here to allow all references to any engaged 
+	// sessions to be removed from the stack immediately to prevent the stack from referencing a deleted object
+	__ASSERT_ALWAYS(iStackState & KMMCStackStateRunning, DMMCSocket::Panic(DMMCSocket::EMMCNotInDfcContext));
+	if (epocErr != KErrNone)
+		iStackState &= ~KMMCStackStateRunning;
+	iSocket->PowerUpSequenceComplete(epocErr);
+	iStackState |= KMMCStackStateRunning;
+
+	return 0;
+	}
+
+
+inline TMMCErr DMMCStack::AttachCardSM()
+/**
+ * This SM function must be invoked by every session which is CardControlled.
+ *
+ * Some commands require that the data held by the stack for a given card is up to date. 
+ *
+ * These are card controlled commands. Before such commands are issued, this function should 
+ * first be invoked which performs the SEND_STATUS (CMD13) command. 
+ *
+ * @return MMC error code
+ */
+	{
+	__KTRACE_OPT(KPBUS1,Kern::Printf("=mst:ac"));
+
+		enum states
+			{
+			EStBegin=0,
+			EStAttStatus,
+			EStEnd
+			};
+
+		DMMCSession& s=Session();
+
+	SMF_BEGIN
+
+		if( s.iCardP == NULL )
+			return( KMMCErrNoCard );
+
+		if( s.iCardP->iUsingSessionP != NULL && s.iCardP->iUsingSessionP != &s )
+			{
+			s.SynchBlock( KMMCBlockOnCardInUse );
+			SMF_WAIT
+			}
+
+		if( s.iCardP->IsPresent() && s.iCardP->iCID == s.iCID )
+			s.iCardP->iUsingSessionP = &s;
+		else
+			return( KMMCErrNoCard );
+
+		s.iConfig.SetMode( KMMCModeCardControlled );	// for future context switching
+		iConfig.SetMode( KMMCModeCardControlled );		// for this context
+
+		// read card status if there are sticky bits in it
+		if( (TUint32(s.iCardP->iStatus) & KMMCStatClearByReadMask) == 0 ||
+			s.iCardP->iLastCommand == ECmdSendStatus ||
+			s.iSessionID == ECIMNakedSession )
+			SMF_EXIT
+
+		s.PushCommandStack();
+		s.FillCommandDesc( ECmdSendStatus, 0 );
+		m.SetTraps( KMMCErrBasic );		// to restore command stack position to its original level
+		SMF_INVOKES( ExecCommandSMST, EStAttStatus )
+
+	SMF_STATE(EStAttStatus)
+
+		s.PopCommandStack();
+		SMF_RETURN( err )
+
+	SMF_END
+	}
+
+inline TMMCErr DMMCStack::CIMInitStackSM()
+/**
+ * Performs the Perform the CIM_INIT_STACK macro.
+ * @return MMC error code
+ */
+	{
+		enum states
+			{
+			EStBegin=0,
+			EStInitDone,
+			EStEnd
+			};
+
+		__KTRACE_OPT(KPBUS1,Kern::Printf(">MMC:InitStackSM"));
+
+		DMMCSession& s=Session();
+
+	SMF_BEGIN
+
+		m.SetTraps( KMMCErrAll );	// to prevent this macro from infinite restarts via iInitialise
+
+		SMF_INVOKES( CIMUpdateAcqSMST, EStInitDone )
+
+	SMF_STATE(EStInitDone)
+
+		s.iState &= ~KMMCSessStateInProgress;	// now we won't be restarted
+		SchedGetOnDFC();						// StackSessionCB must be on DFC
+		SMF_RETURN( err )						// _?_ power cycles can be performed here if error
+
+	SMF_END
+	}
+
+TMMCErr DMMCStack::CIMUpdateAcqSM()
+/**
+ * Performs an identification of a card stack. New cards are always
+ * initialised but if KMMCStackStateInitInProgress is FALSE then existing
+ * cards keep their configuration.
+ * After successful execution of this function, all cards will be in standby
+ * state.
+ * If iPoweredUp is FALSE then the stack is powered up and a full INIT_STACK 
+ * is performed (i.e all cards set to idle and then initialized). 
+ * @return MMC error code
+ */
+	{
+		enum states
+			{
+			EStBegin=0,
+			EStPoweredUp,
+			EStClockOn,
+			EStStartInterrogation,
+			EStCheckStack,
+            EStCardCap,
+			EStIssueDSR,
+			EStFinishUp,
+			EStEnd
+			};
+
+		__KTRACE_OPT(KPBUS1,Kern::Printf(">MMC:UpdAcqSM"));
+
+		DMMCSession& s=Session();
+		DMMCPsu* psu=(DMMCPsu*)iSocket->iVcc;
+
+	SMF_BEGIN
+
+		// This macro works naked and must not be preempted
+		iConfig.RemoveMode( KMMCModeEnablePreemption | KMMCModeCardControlled );
+		// Ensure DFC is running before and after powering up
+		if( SchedGetOnDFC() )	// Such a direct synchronisation with Scheduler() can only
+			SMF_WAIT			// be used in this macro
+
+		s.iState |= (KMMCSessStateInProgress | KMMCSessStateCritical);
+
+		if (iPoweredUp)
+			SMF_GOTOS( EStPoweredUp )
+
+		// The bus is not powered so all cards need initialising - enforce INIT_STACK.
+		iStackState |= KMMCStackStateInitInProgress;
+
+		// Need to turn on the PSU at it's default voltage. Let the ASSP layer choose
+		// this voltage by calling SetVoltage() with the full range the ASSP supports. 
+		iCurrentOpRange=(psu->VoltageSupported() & ~KMMCAdjustableOpVoltage);
+		psu->SetVoltage(iCurrentOpRange);
+		SMF_INVOKES( DoPowerUpSMST, EStPoweredUp )
+
+	SMF_STATE(EStPoweredUp)
+
+		// Switch on the bus clock in identification mode
+		SetBusConfigDefaults(iMasterConfig.iBusConfig, KMMCBusClockFOD);
+		DoSetClock(KMMCBusClockFOD);
+
+		// Make sure controller is in 1-bit bus width mode
+		DoSetBusWidth(EBusWidth1);
+
+		MergeConfig(&s);	// This might take some time, but we are running in DFC here
+		// Reinstate config bits after the merge
+		iConfig.RemoveMode( KMMCModeEnablePreemption | KMMCModeCardControlled );
+		SMF_INVOKES( InitClockOnSMST, EStClockOn )	// Feed init clock to the bus
+
+	SMF_STATE(EStClockOn)
+
+		// Check if there are any cards present in the stack
+		if (!HasCardsPresent())
+			SMF_GOTOS( EStCheckStack )
+
+		if( !InitStackInProgress() )
+			SMF_GOTOS( EStStartInterrogation )
+
+		// Increment the stack's initialiser pass number. Set the current session's pass
+		// number to the new value. Pass number may be used later on to detect sessions
+		// which have been re-initialized due to problems on the bus. 
+		if ((++iInitContext) == 0)		
+			iInitContext++;				// Pass number must never be zero
+		s.iInitContext = iInitContext;	// this session is always in a proper context
+
+	SMF_STATE(EStStartInterrogation)
+
+		// NB: RCAs are not unlocked here. They will be unlocked one by one during the update of card info array.
+		SMF_INVOKES( AcquireStackSMST, EStCheckStack )
+
+	SMF_STATE(EStCheckStack)
+
+		// Check that all known cards are still present by issuing select/deselect
+		SMF_INVOKES( CheckStackSMST, EStCardCap )
+
+	SMF_STATE(EStCardCap)
+
+		// Call a licencee-specific state machine to allow card capabilities to be modified.
+		SMF_INVOKES( ModifyCardCapabilitySMST, EStIssueDSR )
+
+	SMF_STATE(EStIssueDSR)
+
+		// Now that we have updated the card entries, do any final initialisation
+		// of the card entries and determine the maximum bus clock that can be employed.
+		//
+		// If the bus is not multiplexed (ie - MMC stack), then the max clock is set to
+		// the lowest common denominator of all cards in the stack.  Otherwise (in the case
+		// of a multiplexed bus such as SD), the highest clock is returned and the clock
+		// rate is changed when a new card is selected.
+		//
+		TUint maxClk;
+		iCardArray->UpdateAcquisitions(&maxClk);
+		SetBusConfigDefaults( iMasterConfig.iBusConfig, maxClk );
+		DoSetClock(maxClk);
+
+		// merge clock from iMasterConfig.iBusConfig.iBusClock to 
+		// iConfig.iBusConfig.iBusClock - which the PSL should use to configure it's clock
+		MergeConfig(&s);	
+
+		// switch to normal iConfig clock mode
+		InitClockOff();
+		
+	SMF_STATE(EStFinishUp)
+
+		s.iState &= ~(KMMCSessStateInProgress | KMMCSessStateCritical);
+
+		// Update/Init stack has been completed. 
+
+	SMF_END
+	}
+
+
+#define MHZ_TO_KHZ(valInMhz) ((valInMhz) * 1000)
+
+EXPORT_C TMMCErr DMMCStack::InitStackAfterUnlockSM()
+/**
+ * Perform last-stage initialisation of the MMC card.
+ * This implements initialiation that must occur only when the card
+ * is unlocked (ie - immediately after unlocking, or during initialisation
+ * if the card is already unlocked)
+ */
+	{
+		enum states
+			{
+			EStBegin=0,
+			EStTestNextCard,
+			EStGetExtendedCSD,
+			EStGotExtendedCSD,
+			EStGotModifiedExtendedCSD,
+			EStEraseGroupDefSet,
+			EStDetermineBusWidthAndClock,
+			EStGotBusWidthAndClock,	
+			EStNoMoreCards,
+			EStExit,
+			EStEnd
+			};
+
+	DMMCSession& s = Session();
+	TBool initSingleCard = (s.CardP() == NULL) ? (TBool)EFalse : (TBool)ETrue;
+
+	SMF_BEGIN
+
+		if(initSingleCard)
+			{
+			iSelectedCardIndex = iCxCardCount;
+			TMMCard* cardP = iCardArray->CardP(iSelectedCardIndex);
+
+			// if card not present or is locked, exit
+			if ((!cardP->IsPresent()) || (cardP->IsLocked()))
+				SMF_GOTOS(EStExit);
+
+			s.SetCard(cardP);
+
+			// If a card is currently indexed for initialisation, then only configure this one.  
+			// We assume that this has been called from the SD stack, so only one MMC card will be present on the bus
+
+			SMF_GOTOS(EStGetExtendedCSD);
+			}
+
+		// Initialising the entire MMC stack - start with the first card
+		iSelectedCardIndex = -1;
+		
+		// ...fall through...
+
+	SMF_STATE(EStTestNextCard)
+
+		// any more cards ?
+		if (++iSelectedCardIndex >= iCxCardCount)
+			SMF_GOTOS(EStNoMoreCards);
+
+		// if no card in this slot, try next one
+		if (!iCardArray->CardP(iSelectedCardIndex)->IsPresent())
+			SMF_GOTOS(EStTestNextCard);
+
+		TMMCard* cardP = iCardArray->CardP(iSelectedCardIndex);
+		s.SetCard(cardP);
+
+		// if card is locked, try the next one
+		if(cardP->IsLocked())
+			SMF_GOTOS(EStTestNextCard);
+
+	SMF_STATE(EStGetExtendedCSD)
+
+		// Get the Extended CSD if this is an MMC version 4 card
+
+		__KTRACE_OPT(KPBUS1, Kern::Printf(">ConfigureHighSpeed(), SpecVers() %u", s.CardP()->CSD().SpecVers()));
+
+		// clear the Extended CSD contents in case this is a pre-version 4 card or the read fails.
+		memset(s.CardP()->iExtendedCSD.Ptr(), 0, KMMCExtendedCSDLength);
+
+		if (s.CardP()->CSD().SpecVers() < 4) 
+			SMF_GOTOS(EStTestNextCard);
+
+		m.SetTraps(KMMCErrResponseTimeOut | KMMCErrStatus | KMMCErrDataCRC | KMMCErrBypass);	// KMMCErrDataCRC will pick up if the card is not in 1-bit mode
+
+		s.FillCommandDesc(ECmdSendExtendedCSD);
+		s.FillCommandArgs(0, KMMCExtendedCSDLength, iPSLBuf, KMMCExtendedCSDLength);
+
+		__KTRACE_OPT(KPBUS1, Kern::Printf(">ConfigureHighSpeed(), Sending ECmdSendExtendedCSD"));
+		SMF_INVOKES(CIMReadWriteBlocksSMST, EStGotExtendedCSD)
+
+	SMF_STATE(EStGotExtendedCSD)
+				
+		if (err != KMMCErrNone)
+			{
+			SMF_GOTOS(EStExit);
+			}
+
+		memcpy(s.CardP()->iExtendedCSD.Ptr(), iPSLBuf, KMMCExtendedCSDLength);
+
+		// Call a licencee-specific state machine to allow the Extended CSD register to be modified.
+		SMF_INVOKES( ModifyCardCapabilitySMST, EStGotModifiedExtendedCSD )
+
+	SMF_STATE(EStGotModifiedExtendedCSD)
+
+		__KTRACE_OPT(KPBUS1, Kern::Printf("Extended CSD"));
+		__KTRACE_OPT(KPBUS1, Kern::Printf("CSDStructureVer:            %u", s.CardP()->ExtendedCSD().CSDStructureVer()));
+		__KTRACE_OPT(KPBUS1, Kern::Printf("ExtendedCSDRev:             %u", s.CardP()->ExtendedCSD().ExtendedCSDRev()));
+		__KTRACE_OPT(KPBUS1, Kern::Printf("-------------------------------"));
+		__KTRACE_OPT(KPBUS1, Kern::Printf("SupportedCmdSet:            %u", s.CardP()->ExtendedCSD().SupportedCmdSet()));
+		__KTRACE_OPT(KPBUS1, Kern::Printf("PowerClass26Mhz360V:        0x%02X", s.CardP()->ExtendedCSD().PowerClass26Mhz360V()));
+		__KTRACE_OPT(KPBUS1, Kern::Printf("PowerClass52Mhz360V:        0x%02X", s.CardP()->ExtendedCSD().PowerClass52Mhz360V()));
+		__KTRACE_OPT(KPBUS1, Kern::Printf("PowerClass26Mhz195V:        0x%02X", s.CardP()->ExtendedCSD().PowerClass26Mhz195V()));
+		__KTRACE_OPT(KPBUS1, Kern::Printf("PowerClass52Mhz195V:        0x%02X", s.CardP()->ExtendedCSD().PowerClass52Mhz195V()));
+		__KTRACE_OPT(KPBUS1, Kern::Printf("CardType:                   %u", s.CardP()->ExtendedCSD().CardType()));
+		__KTRACE_OPT(KPBUS1, Kern::Printf("CmdSet:                     %u", s.CardP()->ExtendedCSD().CmdSet()));
+		__KTRACE_OPT(KPBUS1, Kern::Printf("CmdSetRev:                  %u", s.CardP()->ExtendedCSD().CmdSetRev()));
+		__KTRACE_OPT(KPBUS1, Kern::Printf("PowerClass:                 %u", s.CardP()->ExtendedCSD().PowerClass()));
+		__KTRACE_OPT(KPBUS1, Kern::Printf("HighSpeedTiming:            %u", s.CardP()->ExtendedCSD().HighSpeedTiming()));
+		__KTRACE_OPT(KPBUS1, Kern::Printf("HighCapacityEraseGroupSize: %u", s.CardP()->ExtendedCSD().HighCapacityEraseGroupSize()));
+		__KTRACE_OPT(KPBUS1, Kern::Printf("AccessSize:                 %u", s.CardP()->ExtendedCSD().AccessSize()));
+		__KTRACE_OPT(KPBUS1, Kern::Printf("BootInfo:                   %u", s.CardP()->ExtendedCSD().BootInfo() ));
+		__KTRACE_OPT(KPBUS1, Kern::Printf("BootSizeMultiple:           %u", s.CardP()->ExtendedCSD().BootSizeMultiple() ));
+		__KTRACE_OPT(KPBUS1, Kern::Printf("EraseTimeoutMultiple:       %u", s.CardP()->ExtendedCSD().EraseTimeoutMultiple() ));
+		__KTRACE_OPT(KPBUS1, Kern::Printf("ReliableWriteSector:        %u", s.CardP()->ExtendedCSD().ReliableWriteSector() ));
+		__KTRACE_OPT(KPBUS1, Kern::Printf("HighCapWriteProtGroupSize:  %u", s.CardP()->ExtendedCSD().HighCapacityWriteProtectGroupSize() ));
+		__KTRACE_OPT(KPBUS1, Kern::Printf("SleepCurrentVcc:            %u", s.CardP()->ExtendedCSD().SleepCurrentVcc() ));
+		__KTRACE_OPT(KPBUS1, Kern::Printf("SleepCurrentVccQ:           %u", s.CardP()->ExtendedCSD().SleepCurrentVccQ()));
+		__KTRACE_OPT(KPBUS1, Kern::Printf("SleepAwakeTimeout:          %u", s.CardP()->ExtendedCSD().SleepAwakeTimeout()));
+		__KTRACE_OPT(KPBUS1, Kern::Printf("BootConfig:                 %u", s.CardP()->ExtendedCSD().BootConfig()));
+		__KTRACE_OPT(KPBUS1, Kern::Printf("BootBusWidth:               %u", s.CardP()->ExtendedCSD().BootBusWidth()));
+		__KTRACE_OPT(KPBUS1, Kern::Printf("EraseGroupDef:              %u", s.CardP()->ExtendedCSD().EraseGroupDef()));
+				
+		if (s.CardP()->ExtendedCSD().ExtendedCSDRev() >= 3)
+			{
+			if (!(s.CardP()->ExtendedCSD().EraseGroupDef()) && s.CardP()->ExtendedCSD().HighCapacityEraseGroupSize())
+				{
+				// Need to ensure that media is using correct erase group sizes.
+				TMMCArgument arg = TExtendedCSD::GetWriteArg(
+					TExtendedCSD::EWriteByte,
+					TExtendedCSD::EEraseGroupDefIndex,
+					TExtendedCSD::EEraseGrpDefEnableHighCapSizes,
+					0);
+	
+				__KTRACE_OPT(KPBUS1, Kern::Printf(">Writing to EXT_CSD (EEraseGroupDefIndex), arg %08X", (TUint32) arg));
+				s.FillCommandDesc(ECmdSwitch, arg);
+				
+				SMF_INVOKES(ExecSwitchCommandST, EStEraseGroupDefSet)
+				}
+			}
+		
+		SMF_GOTOS(EStDetermineBusWidthAndClock)
+		
+	SMF_STATE(EStEraseGroupDefSet)
+	
+		if (err == KMMCErrNone)
+			{
+			// EEraseGroupDef has been updated succussfully, 
+			// update the Extended CSD to reflect this			
+			memset( s.CardP()->iExtendedCSD.Ptr()+TExtendedCSD::EEraseGroupDefIndex, TExtendedCSD::EEraseGrpDefEnableHighCapSizes, 1);
+			}
+	
+	SMF_STATE(EStDetermineBusWidthAndClock)
+	
+		SMF_INVOKES( DetermineBusWidthAndClockSMST, EStGotBusWidthAndClock )
+
+    SMF_STATE(EStGotBusWidthAndClock)
+
+		SMF_NEXTS(initSingleCard ? EStExit : EStTestNextCard)
+
+		if(iMultiplexedBus || iCardArray->CardsPresent() == 1)
+			{
+			SMF_CALL( ConfigureHighSpeedSMST )
+			}
+		
+		SMF_GOTONEXTS
+
+	SMF_STATE(EStNoMoreCards)
+
+
+	SMF_STATE(EStExit)
+		m.ResetTraps();
+
+	SMF_END
+	}
+
+
+
+/**
+DetermineBusWidthAndClockSM()
+
+Reads the extended CSD register for all MMCV4 cards in the stack.
+If there is only one MMCV4 card, then an attempt is made to switch
+both the card and the controller to a higher clock rate (either 26MHz of 52MHz) 
+and to a wider bus width (4 or 8 bits). 
+The clock speed & bus width chosen depend on :
+
+- whether the card supports it
+- whether the controller supports it
+- whether the controller has the ability to supply enough current (the current used 
+  by the card can be read from the Extended CSD register)
+
+*/
+TMMCErr DMMCStack::DetermineBusWidthAndClockSM()
+	{
+		enum states
+			{
+			EStBegin=0,
+			EStWritePowerClass,
+			EStStartBusTest,
+			EStExit,
+			EStEnd
+			};
+
+	DMMCSession& s = Session();
+	TMMCard* cardP = iCardArray->CardP(iSelectedCardIndex);
+
+	SMF_BEGIN
+		// Trap Switch errors & no-response errors
+		m.SetTraps(KMMCErrResponseTimeOut | KMMCErrStatus);
+
+		__KTRACE_OPT(KPBUS1, Kern::Printf(">ConfigureHighSpeed(), iCxCardCount %u", iCxCardCount));
+
+
+	SMF_STATE(EStWritePowerClass)
+
+		// Check the card type is valid
+		// The only currently valid values for this field are 0x01 or 0x03
+		TUint cardType = cardP->iExtendedCSD.CardType();
+		if (cardType != (TExtendedCSD::EHighSpeedCard26Mhz) && 
+			cardType != (TExtendedCSD::EHighSpeedCard26Mhz | TExtendedCSD::EHighSpeedCard52Mhz))
+			{
+			__KTRACE_OPT(KPBUS1, Kern::Printf("Unsupported card type %u", cardType));
+			SMF_GOTOS(EStExit);
+			}
+
+		// determine the optimum bus width & clock speed which match the power constraints
+		TUint powerClass;
+		DetermineBusWidthAndClock(
+			*cardP, 
+			(iCurrentOpRange == KMMCOCRLowVoltage), 
+			powerClass, 
+			iBusWidthAndClock);
+
+		// If the power class for the chosen width is different from the default,
+		// send SWITCH cmd and write the POWER_CLASS byte of the EXT_CSD register
+		if (powerClass > 0)
+			{
+			TMMCArgument arg = TExtendedCSD::GetWriteArg(
+				TExtendedCSD::EWriteByte,
+				TExtendedCSD::EPowerClassIndex,
+				powerClass,
+				0);
+
+			__KTRACE_OPT(KPBUS1, Kern::Printf(">ConfigureHighSpeed(), Writing to EXT_CSD (EPowerClass), arg %08X", (TUint32) arg));
+			s.FillCommandDesc(ECmdSwitch, arg);
+			SMF_INVOKES(ExecSwitchCommandST, EStStartBusTest)
+			}
+
+	SMF_STATE(EStStartBusTest)
+		
+		if (err != KMMCErrNone)
+			{
+			SMF_GOTOS(EStExit);
+			}
+
+		// We have determined the capabilities of the host and card.
+		//  - Before switching to the required bus width, perform the BUSTEST sequence
+		SMF_INVOKES(ExecBusTestSMST, EStExit);
+
+	SMF_STATE(EStExit)
+		m.ResetTraps();
+
+	SMF_END
+	}
+
+
+/**
+ConfigureHighSpeedSM()
+
+Reads the extended CSD register for all MMCV4 cards in the stack.
+If there is only one MMCV4 card, then an attempt is made to switch
+both the card and the controller to a higher clock rate (either 26MHz of 52MHz) 
+and to a wider bus width (4 or 8 bits). 
+The clock speed & bus width chosen depend on :
+
+- whether the card supports it
+- whether the controller supports it
+- whether the controller has the ability to supply enough current (the current used 
+  by the card can be read from the Extended CSD register)
+
+*/
+TMMCErr DMMCStack::ConfigureHighSpeedSM()
+	{
+		enum states
+			{
+			EStBegin=0,
+			EStConfigureBusWidth,
+			EStWriteHsTiming,
+			EStConfigureClock,
+			EStExit,
+			EStEnd
+			};
+
+	DMMCSession& s = Session();
+	TMMCard* cardP = iCardArray->CardP(iSelectedCardIndex);
+
+	SMF_BEGIN
+
+		// Trap Switch errors & no-response errors
+		m.SetTraps(KMMCErrResponseTimeOut | KMMCErrStatus);
+
+		__KTRACE_OPT(KPBUS1, Kern::Printf(">ConfigureHighSpeed(), iCxCardCount %u", iCxCardCount));
+
+		cardP->SetHighSpeedClock(0);
+
+		// Check the card type is valid
+		// The only currently valid values for this field are 0x01 or 0x03
+		TUint cardType = cardP->iExtendedCSD.CardType();
+		if (cardType != (TExtendedCSD::EHighSpeedCard26Mhz) && 
+			cardType != (TExtendedCSD::EHighSpeedCard26Mhz | TExtendedCSD::EHighSpeedCard52Mhz))
+			{
+			__KTRACE_OPT(KPBUS1, Kern::Printf("Unsupported card type %u", cardType));
+			SMF_GOTOS(EStExit);
+			}
+
+		// If the bus width is 4 or 8, send SWITCH cmd and write the BUS_WIDTH byte of the EXT_CSD register
+
+		if (iBusWidthAndClock != E1Bit20Mhz)
+			{
+			TMMCArgument arg = TExtendedCSD::GetWriteArg(
+				TExtendedCSD::EWriteByte,
+				TExtendedCSD::EBusWidthModeIndex,
+				(iBusWidthAndClock & E4BitMask) ? TExtendedCSD::EExtCsdBusWidth4 : TExtendedCSD::EExtCsdBusWidth8,
+				0);
+
+			__KTRACE_OPT(KPBUS1, Kern::Printf(">ConfigureHighSpeed(), Writing to EXT_CSD (EBusWidthMode), arg %08X", (TUint32) arg));
+			s.FillCommandDesc(ECmdSwitch, arg);
+			SMF_INVOKES(ExecSwitchCommandST, EStConfigureBusWidth)
+			}
+
+	SMF_STATE(EStConfigureBusWidth)
+
+		if (err != KMMCErrNone)
+			{
+			SMF_GOTOS(EStExit);
+			}
+
+		// Ensure that the controller is configured for an 4 or 8 bit bus
+		//  - BUSTEST should have already done this
+		if (iBusWidthAndClock & E4BitMask)
+			{
+			DoSetBusWidth(EBusWidth4);
+			}
+		else if (iBusWidthAndClock & E8BitMask)
+			{
+			DoSetBusWidth(EBusWidth8);
+			}
+		// fall through to next state
+
+	SMF_STATE(EStWriteHsTiming)
+		if (iBusWidthAndClock == E1Bit20Mhz)
+			SMF_GOTOS(EStExit);
+
+		TMMCArgument arg = TExtendedCSD::GetWriteArg(
+			TExtendedCSD::EWriteByte,
+			TExtendedCSD::EHighSpeedInterfaceTimingIndex,
+			1,	// turn on high speed (26 or 52 Mhz, depending on the card type)
+			0);
+
+		__KTRACE_OPT(KPBUS1, Kern::Printf(">ConfigureHighSpeed(), Writing to EXT_CSD (EHighSpeedInterfaceTiming), arg %08X", (TUint32) arg));
+		s.FillCommandDesc(ECmdSwitch, arg);
+		SMF_INVOKES(ExecSwitchCommandST, EStConfigureClock)
+
+
+	SMF_STATE(EStConfigureClock)
+
+		if (err != KMMCErrNone)
+			{
+			DoSetBusWidth(EBusWidth1);
+			SMF_GOTOS(EStExit);
+			}
+
+		cardP->SetHighSpeedClock(
+			MHZ_TO_KHZ(((iBusWidthAndClock & E52MhzMask) ? 
+			            TMMCMachineInfoV4::EClockSpeed52Mhz:
+			            TMMCMachineInfoV4::EClockSpeed26Mhz)));
+
+	SMF_STATE(EStExit)
+		m.ResetTraps();
+
+	SMF_END
+	}
+
+// Issue a switch command and then wait while he card is in prg state
+//
+TMMCErr DMMCStack::ExecSwitchCommand()
+	{
+		enum states
+			{
+			EStBegin=0,
+			EStSendStatus,
+			EStGetStatus,
+			EStEnd
+			};
+
+		DMMCSession& s=Session();
+
+	SMF_BEGIN
+		SMF_INVOKES(ExecCommandSMST, EStSendStatus)
+
+	SMF_STATE(EStSendStatus)
+		s.FillCommandDesc(ECmdSendStatus, 0);
+		SMF_INVOKES(ExecCommandSMST, EStGetStatus)
+
+	SMF_STATE(EStGetStatus)
+		const TMMCStatus st(s.ResponseP());
+
+		const TMMCardStateEnum st1 = st.State();
+		if (st1 == ECardStatePrg)
+			{
+			SMF_INVOKES(ProgramTimerSMST, EStSendStatus);
+			}
+		
+	// Fall through if CURRENT_STATE is not PGM
+
+	SMF_END
+	}
+
+// Issue CMD5 to change device status to Sleep mode
+//
+TMMCErr DMMCStack::ExecSleepCommandSM()
+	{
+		enum states
+			{
+			EStBegin=0,
+			EStIndexNxtCard,
+			EStIssueSleepAwake,
+			EStSleepAwakeIssued,
+			EStUpdateStackState,
+			EStDone,
+			EStEnd
+			};
+
+		DMMCSession& s=Session();
+
+	SMF_BEGIN
+	
+		__KTRACE_OPT(KPBUS1, Kern::Printf(">ExecSleepCommandSM()"));
+		
+		iAutoUnlockIndex = -1;
+		// drop through....
+		
+	SMF_STATE(EStIndexNxtCard)
+	
+		__KTRACE_OPT(KPBUS1, Kern::Printf(">EStIndexNxtCard"));
+		// the cycle is finished when iAutoUnlockIndex == KMaxMultiMediaCardsPerStack
+		if(iAutoUnlockIndex >= TInt(KMaxMMCardsPerStack))
+			{
+			SMF_GOTOS(EStUpdateStackState);
+			}
+
+		// Potentionaly more than one eMMC device attached to Controller
+		// need to select each device and determine if Sleep can be issued
+		TBool useIndex = EFalse;
+		for (++iAutoUnlockIndex; iAutoUnlockIndex < TInt(KMaxMMCardsPerStack); ++iAutoUnlockIndex)
+			{
+			// card must be present and a valid 4.3 device
+			TMMCard* cardP = iCardArray->CardP(iAutoUnlockIndex);
+			useIndex = ( (cardP->IsPresent()) &&
+					     (cardP->ExtendedCSD().ExtendedCSDRev() >= 3) &&
+						 (cardP->iStatus != ECardStateSlp) );
+
+			// don't increment iAutoUnlockIndex in continuation loop
+			if (useIndex)
+				{
+				__KTRACE_OPT(KPBUS1, Kern::Printf(">Card[%d]: is v4.3 device",iAutoUnlockIndex));
+				break;
+				}
+			}
+		
+		if (!useIndex)
+			{
+			SMF_GOTOS(EStUpdateStackState);
+			}
+		
+		TMMCard &cd = *(iCardArray->CardP(iAutoUnlockIndex));
+		s.SetCard(&cd);
+		
+		s.PushCommandStack();		
+		s.FillCommandDesc(ECmdSleepAwake, KBit15);
+		
+		// CMD5 is an AC command, ExecCommandSMST will automatically issue a deselect
+		SMF_INVOKES(ExecCommandSMST, EStSleepAwakeIssued)
+		
+	SMF_STATE(EStSleepAwakeIssued)
+		 
+		__KTRACE_OPT(KPBUS1, Kern::Printf(">EStSleepAwakeIssued!"));
+		
+		const TMMCStatus status(s.ResponseP());
+		
+		s.PopCommandStack();
+
+		if(status.State() == ECardStateStby || status.State() == ECardStateSlp)
+			{			
+			// R1b is issued before Sleep state is achieved and 
+			// will therefore return the previous state which was Standby
+			__KTRACE_OPT(KPBUS1, Kern::Printf(">Card[%d]: SLEEP",iAutoUnlockIndex));
+			
+			// Ensure card status is ECardStateSlp
+			s.CardP()->iStatus.UpdateState(ECardStateSlp);
+			
+			// Update Stack state to indicate media is sleep mode
+			iStackState |= KMMCStackStateSleep;
+			}
+		else
+			{ 
+			__KTRACE_OPT(KPBUS1, Kern::Printf(">Card[%d]: UNKNOWN",iAutoUnlockIndex));
+			return (KMMCErrStatus);
+			}
+		
+		SMF_GOTOS(EStIndexNxtCard)
+
+	SMF_STATE(EStUpdateStackState)
+
+		if (iStackState & KMMCStackStateSleep)
+			{ 
+			// Media has been transitioned to sleep state
+			iStackState &= ~KMMCStackStateSleep;
+			
+			// VccCore may now be switched off
+			iSocket->iVccCore->SetState(EPsuOff);
+			}
+//		else
+			// No media transitioned to sleep state or media was already in sleep state,
+			// nothing to do...
+			
+	SMF_STATE(EStDone)
+		
+		__KTRACE_OPT(KPBUS1, Kern::Printf("<ExecSleepCommandSM()"));
+		
+	SMF_END
+	}
+
+
+//Issue CMD5 to change devices into STANDBY state
+TMMCErr DMMCStack::ExecAwakeCommandSM()
+	{
+		enum states
+			{
+			EStBegin=0,
+			EStPoweredUp,
+			EStAwakeIssued,
+			EStDone,
+			EStEnd
+			};
+
+		DMMCSession& s=Session();
+
+	SMF_BEGIN
+	
+		__KTRACE_OPT(KPBUS1, Kern::Printf(">ExecAwakeCommandSM()"));
+		
+		// Call PSL to ensure VccQ is powered up before continuing
+		SMF_INVOKES( DoWakeUpSMST, EStPoweredUp )
+		
+	SMF_STATE(EStPoweredUp)	
+
+		__KTRACE_OPT(KPBUS1, Kern::Printf("VccQ Powered Up"));
+		
+		//Issue CMD5 to awaken media
+		s.PushCommandStack();		
+		s.FillCommandDesc(ECmdSleepAwake);
+		s.Command().iArgument.SetRCA(s.CardP()->RCA());
+		
+		SMF_INVOKES(IssueCommandCheckResponseSMST, EStAwakeIssued)
+		
+	SMF_STATE(EStAwakeIssued)
+		 
+		__KTRACE_OPT(KPBUS1, Kern::Printf(">>EStAwakeIssued!"));
+		
+		TMMCStatus status(s.ResponseP());
+
+		if(status.State() == ECardStateStby || status.State() == ECardStateSlp)
+			{			
+			// R1b is issued before Standby state is achieved and 
+			// will therefore return the previous state which was Sleep
+			__KTRACE_OPT(KPBUS1, Kern::Printf(">Card[%d]: STANDBY",iAutoUnlockIndex));
+			
+			s.CardP()->iStatus.UpdateState(ECardStateStby);
+			}
+		else
+			{ 
+			__KTRACE_OPT(KPBUS1, Kern::Printf(">Card[%d]: UNKNOWN",iAutoUnlockIndex));
+			return (KMMCErrStatus);
+			}
+
+		s.PopCommandStack();	
+
+	SMF_STATE(EStDone)
+	
+		__KTRACE_OPT(KPBUS1, Kern::Printf("<ExecAwakeCommandSM()"));
+	
+	SMF_END
+	}
+
+
+// determine the maximum bus width and clock speed supported by both the controller
+// and the card which fits the power constraints.
+void DMMCStack::DetermineBusWidthAndClock(
+	const TMMCard& aCard, 
+	TBool aLowVoltage,
+	TUint& aPowerClass,
+	TBusWidthAndClock& aBusWidthAndClock)
+	{
+
+	// Set default return values - in case power constraints aren't matched
+	aPowerClass = 0;
+	aBusWidthAndClock = E1Bit20Mhz;
+
+	// Get the bus widths & clocks supported by the controller
+	// NB If the PSL doesn not support TMMCMachineInfoV4, return
+	TMMCMachineInfoV4 machineInfo;
+	TMMCMachineInfoV4Pckg machineInfoPckg(machineInfo);
+	MachineInfo(machineInfoPckg);
+	if (machineInfo.iVersion < TMMCMachineInfoV4::EVersion4)
+		return;
+
+	TBusWidth maxBusWidth = machineInfo.iMaxBusWidth;
+	TInt maxClockSpeedInMhz = machineInfo.iMaxClockSpeedInMhz;
+
+	TUint32 controllerWidthAndClock = E1Bit20Mhz;
+
+	if (maxClockSpeedInMhz >= TMMCMachineInfoV4::EClockSpeed26Mhz && maxBusWidth >= EBusWidth4)
+		controllerWidthAndClock|= E4Bits26Mhz;
+	if (maxClockSpeedInMhz >= TMMCMachineInfoV4::EClockSpeed52Mhz && maxBusWidth >= EBusWidth4)
+		controllerWidthAndClock|= E4Bits52Mhz;
+		
+	if (maxClockSpeedInMhz >= TMMCMachineInfoV4::EClockSpeed26Mhz && maxBusWidth >= EBusWidth8)
+		controllerWidthAndClock|= E8Bits26Mhz;
+	if (maxClockSpeedInMhz >= TMMCMachineInfoV4::EClockSpeed52Mhz && maxBusWidth >= EBusWidth8)
+		controllerWidthAndClock|= E8Bits52Mhz;
+		
+	// Get the bus widths & clocks supported by the card
+	TUint32 cardWidthAndClock = E1Bit20Mhz;
+
+	if (aCard.ExtendedCSD().CardType() & TExtendedCSD::EHighSpeedCard26Mhz)
+		cardWidthAndClock|= E4Bits26Mhz | E8Bits26Mhz;
+	if (aCard.ExtendedCSD().CardType() & TExtendedCSD::EHighSpeedCard52Mhz)
+		cardWidthAndClock|= E4Bits52Mhz | E8Bits52Mhz;
+
+
+	// Get the bus widths & clocks supported by both the controller & card
+	// by AND-ing them together,
+	TUint32 supportedWidthAndClock = controllerWidthAndClock & cardWidthAndClock;
+
+	// Iterate through all the modes (starting at the fastest) until we find one
+	// that is supported by both card & controller and fits the power constraints
+	TUint powerClass = 0;
+	for (TUint targetWidthAndClock = E8Bits52Mhz; targetWidthAndClock != 0; targetWidthAndClock>>= 1)
+		{
+		if ((supportedWidthAndClock & targetWidthAndClock) == 0)
+			continue;
+
+		powerClass = GetPowerClass(aCard, TBusWidthAndClock(targetWidthAndClock), aLowVoltage);
+
+		// can the controller support this power class ?
+		if (powerClass > (aLowVoltage ? machineInfo.iLowVoltagePowerClass : machineInfo.iHighVoltagePowerClass ))
+			continue;
+
+		aPowerClass = powerClass;
+		aBusWidthAndClock = TBusWidthAndClock(targetWidthAndClock);
+		break;
+		}
+
+	__KTRACE_OPT(KPBUS1, Kern::Printf("aPowerClass %u, targetWidthAndClock = %08X", aPowerClass, aBusWidthAndClock));
+	}
+
+TUint DMMCStack::GetPowerClass(const TMMCard& aCard, TBusWidthAndClock aWidthAndClock, TBool aLowVoltage)
+	{
+	// The power class for 4 bit bus configurations is in the low nibble,
+	// The power class for 8 bit bus configurations is in the high nibble,
+	#define LO_NIBBLE(val) (val & 0x0F)
+	#define HI_NIBBLE(val) ((val >> 4) & 0x0F)
+
+	const TExtendedCSD& extendedCSD = aCard.ExtendedCSD();
+
+	TUint powerClass = 0;
+
+	if (aLowVoltage)
+		{
+		switch(	aWidthAndClock)
+			{
+			case E4Bits26Mhz:
+				powerClass = LO_NIBBLE(extendedCSD.PowerClass26Mhz195V());
+				break;
+			case E4Bits52Mhz:
+				powerClass = LO_NIBBLE(extendedCSD.PowerClass52Mhz195V());
+				break;
+			case E8Bits26Mhz:
+				powerClass = HI_NIBBLE(extendedCSD.PowerClass26Mhz195V());
+				break;
+			case E8Bits52Mhz:
+				powerClass = HI_NIBBLE(extendedCSD.PowerClass52Mhz195V());
+				break;
+			case E1Bit20Mhz:
+				powerClass = 0;
+				break;
+			}
+		}
+	else
+		{
+		switch(	aWidthAndClock)
+			{
+			case E4Bits26Mhz:
+				powerClass = LO_NIBBLE(extendedCSD.PowerClass26Mhz360V());
+				break;
+			case E4Bits52Mhz:
+				powerClass = LO_NIBBLE(extendedCSD.PowerClass52Mhz360V());
+				break;
+			case E8Bits26Mhz:
+				powerClass = HI_NIBBLE(extendedCSD.PowerClass26Mhz360V());
+				break;
+			case E8Bits52Mhz:
+				powerClass = HI_NIBBLE(extendedCSD.PowerClass52Mhz360V());
+				break;
+			case E1Bit20Mhz:
+				powerClass = 0;
+				break;
+			}
+		}
+
+		return powerClass;
+	}
+
+//
+// Execute the BUSTEST procedure for a given bus width
+//
+TMMCErr DMMCStack::ExecBusTestSM()
+	{
+	enum states
+		{
+		EStBegin=0,
+		EstSendBusTest_W,
+		EstSendBusTest_R,
+		EstGotBusTest_R,
+		EStExit,
+		EStEnd
+		};
+
+	DMMCSession& s = Session();
+
+	SMF_BEGIN
+		//
+		// Start the BUSTEST sequence at the maximum supported by the PSL
+		//  - iSpare[0] keeps track of the current bus width
+		//
+		if (iBusWidthAndClock & E8BitMask)
+			{
+			iSpare[0] = EBusWidth8;
+			__KTRACE_OPT(KPBUS1, Kern::Printf("...Hardware supports 8-bit bus"));
+			}
+		else if(iBusWidthAndClock & E4BitMask)
+			{
+			iSpare[0] = EBusWidth4;
+			__KTRACE_OPT(KPBUS1, Kern::Printf("...Hardware supports 4-bit bus"));
+			}
+		else
+			{
+			__KTRACE_OPT(KPBUS1, Kern::Printf("...Hardware supports 1-bit bus"));
+			iSpare[0] = EBusWidth1;
+			}
+
+		// remove KMMCModeCardControlled so that IssueCommandCheckResponseSMST doesn't try to 
+		// override the bus width & clock rate using the card settings
+		iConfig.RemoveMode( KMMCModeCardControlled );
+
+	SMF_STATE(EstSendBusTest_W)
+		//
+		// Issue the BUSTEST_W command
+		//
+		TInt length = 2;
+		switch(iSpare[0])
+			{
+			case EBusWidth8:
+				// Set the host to 8-bit mode
+				__KTRACE_OPT(KPBUS1, Kern::Printf("BUSTEST : EBusWidth8"));
+				DoSetBusWidth(EBusWidth8);
+				iPSLBuf[0] = 0x55;
+				iPSLBuf[1] = 0xaa;
+				break;
+
+			case EBusWidth4:
+				// Set the host to 4-bit mode
+				__KTRACE_OPT(KPBUS1, Kern::Printf("BUSTEST : EBusWidth4"));
+				DoSetBusWidth(EBusWidth4);
+				iPSLBuf[0] = 0x5a;
+				iPSLBuf[1] = 0x00;
+				break;
+
+			case EBusWidth1:
+			default:
+				// Set the host to 1-bit mode
+				__KTRACE_OPT(KPBUS1, Kern::Printf("BUSTEST : EBusWidth1"));
+				DoSetBusWidth(EBusWidth1);
+				iPSLBuf[0] = 0x40;
+				iPSLBuf[1] = 0x00;
+				break;
+			}	
+
+		// Issue BUSTEST_W
+
+		__KTRACE_OPT(KPBUS1, Kern::Printf("...Issue BUSTEST_W [%02x:%02x]", iPSLBuf[1], iPSLBuf[0]));
+
+		m.SetTraps(KMMCErrDataCRC); // CRC check is optional for BUSTEST
+
+		s.FillCommandDesc(ECmdBustest_W);
+		s.FillCommandArgs(0, length, &iPSLBuf[0], length);
+		SMF_INVOKES(IssueCommandCheckResponseSMST, EstSendBusTest_R)
+
+	SMF_STATE(EstSendBusTest_R)
+		//
+		// Issue the BUSTEST_R command
+		//
+		__KTRACE_OPT(KPBUS1, Kern::Printf("...got BUSTEST_W response : %02x", err));
+
+		if(err == KMMCErrNone || err == KMMCErrDataCRC)
+			{
+			__KTRACE_OPT(KPBUS1, Kern::Printf("...sending BUSTEST_R"));
+
+			iPSLBuf[0] = 0;
+			iPSLBuf[1] = 0;
+
+			s.FillCommandDesc(ECmdBustest_R);
+			s.FillCommandArgs(0, 2, &iPSLBuf[0], 2);
+			SMF_INVOKES(IssueCommandCheckResponseSMST, EstGotBusTest_R)
+			}
+		else
+			{
+			SMF_RETURN(KMMCErrNotSupported);
+			}
+
+	SMF_STATE(EstGotBusTest_R)
+		//
+		// Validate the BUSTEST_R data with that issued by BUSTEST_W
+		//
+		__KTRACE_OPT(KPBUS1, Kern::Printf("...got BUSTEST_R response [%02x:%02x] : err(%02x)", iPSLBuf[1], iPSLBuf[0], err));
+
+		TBool retry = EFalse;
+		TBool is52MHzSupported = (iBusWidthAndClock & E52MhzMask) ? (TBool)ETrue : (TBool)EFalse;
+
+		switch(iSpare[0])
+			{
+			case EBusWidth8:
+				{
+				if(iPSLBuf[0] == 0xAA && iPSLBuf[1] == 0x55)
+					{
+					// 8-Bit bus supported
+					iBusWidthAndClock = is52MHzSupported ? E8Bits52Mhz : E8Bits26Mhz;
+					}
+				else
+					{
+					// 8-Bit bus not supported - retry with 4-Bit
+					retry = ETrue;
+					iSpare[0] = EBusWidth4;
+					}
+				break;
+				}
+
+			case EBusWidth4:
+				{
+				if(iPSLBuf[0] == 0xA5)
+					{
+					// 4-Bit Bus Supported
+					iBusWidthAndClock = is52MHzSupported ? E4Bits52Mhz : E4Bits26Mhz;
+					}
+				else
+					{
+					// 4-Bit bus not supported - retry with 1-Bit
+					retry = ETrue;
+					iSpare[0] = EBusWidth1;
+					}
+				break;
+				}
+
+			case EBusWidth1:
+				{
+				if((iPSLBuf[0] & 0xC0) == 0x80)
+					{
+					// 1-Bit Bus Supported
+					iBusWidthAndClock = E1Bit20Mhz;
+					}
+
+				// Failed to perform BUSTEST with 1-Bit bus.
+				//  - We can't recover from this, but let's continue using low-speed 1-Bit mode
+				iBusWidthAndClock = E1Bit20Mhz;
+				break;
+				}
+
+			default:
+				DMMCSocket::Panic(DMMCSocket::EMMCBadBusWidth);
+				break;
+			}	
+
+		if(retry)
+			{
+			__KTRACE_OPT(KPBUS1, Kern::Printf("...BUSTEST Failed : Retry"));
+			SMF_GOTOS(EstSendBusTest_W);
+			}
+		
+		switch(iBusWidthAndClock)
+			{
+			case E1Bit20Mhz:
+				iCardArray->CardP(iSelectedCardIndex)->SetBusWidth(1);
+				break;
+
+			case E4Bits26Mhz:
+			case E4Bits52Mhz:
+				iCardArray->CardP(iSelectedCardIndex)->SetBusWidth(4);
+				break;
+
+			case E8Bits26Mhz:
+			case E8Bits52Mhz:
+				iCardArray->CardP(iSelectedCardIndex)->SetBusWidth(8);
+				break;
+			}
+
+		__KTRACE_OPT(KPBUS1, Kern::Printf("...BUSTEST OK"));
+
+		DoSetBusWidth(EBusWidth1);
+
+	SMF_STATE(EStExit)
+		iConfig.SetMode( KMMCModeCardControlled );
+
+	SMF_END
+	}
+
+
+/**
+ * PSL-supplied method to retrieve an interface.
+ * The caller should set aInterfacePtr to NULL before calling
+ * the PSL should only modify aInterfacePtr if it supports the interface
+ * The default implementation here does nothing
+ * Replaces Dummy2()
+*/
+EXPORT_C void DMMCStack::GetInterface(TInterfaceId aInterfaceId, MInterface*& aInterfacePtr)
+	{
+	if (aInterfaceId == KInterfaceCancelSession)
+		{
+		DMMCSession* session = (DMMCSession*&) aInterfacePtr;
+		Abort(session);
+		UnlockStack(session);
+		}
+
+	}
+
+
+TMMCErr DMMCStack::GoIdleSM()
+/**
+ * Issues GO_IDLE_STATE twice with a RetryGap between them. 
+ * After that the bus context ought to be considered as "known".
+ * @return MMC error code
+ */
+	{
+		enum states
+			{
+			EStBegin=0,
+			EStIdleLoop,
+			EStIdleEndCheck,
+			EStEnd
+			};
+
+		DMMCSession& s=Session();
+
+	SMF_BEGIN
+
+		s.FillCommandDesc( ECmdGoIdleState, 0 );
+		iCxPollRetryCount = KMMCIdleCommandsAtRestart;
+
+	SMF_STATE(EStIdleLoop)
+		SMF_INVOKES( ExecCommandSMST, EStIdleEndCheck )
+
+	SMF_STATE(EStIdleEndCheck)
+
+		if( --iCxPollRetryCount > 0 )
+			SMF_INVOKES( RetryGapTimerSMST, EStIdleLoop )
+
+		iStackState &= ~(KMMCStackStateDoDeselect|KMMCStackStateBusInconsistent);
+		iSelectedCard = 0;
+
+		// According to the spec, the default bus width after power up or GO_IDLE is 1 bit bus width
+		DoSetBusWidth(EBusWidth1);
+
+	SMF_END
+	}
+
+EXPORT_C TMMCErr DMMCStack::AcquireStackSM()
+/**
+ * This macro acquires new cards in an MMC - bus topology stack.
+ * It starts with the Controller reading the operating conditions of the 
+ * cards in the stack (SEND_OP_COND - CMD1). Then, any new cards in the stack
+ * are identified (ALL_SEND_CID - CMD2) and each one is assigned a relative
+ * card address (SET_RCA - CMD3). This is done by systematically broadcasting
+ * CMD2 to all cards on the bus until all uninitialized cards have responded.
+ * Finally the card specific data (SEND_CSD - CMD9) is read from each card.
+ * @return MMC error code
+ */
+	{
+		enum states
+			{
+			EStBegin=0,
+			EStIdle,
+			EStFullRangeDone,
+			EStSetRangeLoop,
+			EStSetRangeBusyCheck,
+			EStCIDLoop,
+			EStSendCIDIssued,
+			EStCIDsDone,
+			EStCSDLoop,
+			EStSendCSDDone,
+			EStMergeCards,
+			EStReMergeCards,
+			EStEnd
+			};
+
+		DMMCSession& s=Session();
+		DMMCPsu* psu=(DMMCPsu*)iSocket->iVcc;
+
+	SMF_BEGIN
+
+		iRCAPool.ReleaseUnlocked();
+		iCxPollRetryCount = 0; // Reset max number of poll attempts on card busy
+
+		SMF_INVOKES( GoIdleSMST, EStIdle )
+
+	SMF_STATE(EStIdle)
+
+		// If this platform doesn't support an adjustable voltage PSU then there is
+		// no point in interogating the card(s) present for their supported range
+		if ( !(psu->VoltageSupported()&KMMCAdjustableOpVoltage) )
+			{
+			// if the PSU isn't adjustable then it can't support low voltage mode
+			iCurrentOpRange&= ~KMMCOCRLowVoltage;
+			s.FillCommandDesc(ECmdSendOpCond, (iCurrentOpRange | KMMCOCRAccessModeHCS | KMMCOCRBusy));	// Range supported + Busy bit (iArgument==KBit31)
+			SMF_GOTOS( EStSetRangeLoop )
+			}
+
+		// Interrogate card(s) present - issue CMD1 with omitted voltage range
+		s.FillCommandDesc( ECmdSendOpCond, KMMCOCRAccessModeHCS | KMMCOCRBusy);	// Full range + Sector Access + Busy bit (iArgument==KBit31)
+		m.SetTraps( KMMCErrResponseTimeOut );
+
+		SMF_INVOKES( ExecCommandSMST, EStFullRangeDone )
+
+	SMF_STATE(EStFullRangeDone)
+
+		if( err )												// If timeout
+			{
+			iConfig.RemoveMode( KMMCModeEnableTimeOutRetry );	// There is no point to do it second time
+			}
+		else
+			{
+			// Cards responded with Op range - evaluate the common subset with the current setting
+			// Dont worry aboout the busy bit for now, we'll check that when we repeat the command
+			TUint32 newrange = (TMMC::BigEndian32(s.ResponseP()) & ~KMMCOCRBusy);
+			newrange &= iCurrentOpRange;
+
+			if (newrange==0)
+				{
+				// One or more card is incompatible with our h/w
+				if (iMaxCardsInStack<=1)
+					return( KMMCErrNotSupported ); // There can only be one card - we don't support it.
+				else
+					// Force the default range
+					iCurrentOpRange=(psu->VoltageSupported() & ~KMMCAdjustableOpVoltage);  
+				}
+			else
+				iCurrentOpRange=newrange;		// OK, new cards are compatible
+			}
+
+		// If platform and the card both support low voltage mode (1.65 - 1.95v), switch
+		if (iCurrentOpRange & KMMCOCRLowVoltage)
+			{
+			iCurrentOpRange = KMMCOCRLowVoltage;
+			SMF_INVOKES( SwitchToLowVoltageSMST, EStSetRangeLoop )
+			}
+
+	SMF_STATE(EStSetRangeLoop)
+
+		// Repeat CMD1 this time setting Current Op Range
+		s.Command().iArgument = iCurrentOpRange | KMMCOCRAccessModeHCS | KMMCOCRBusy;
+
+		m.SetTraps( KMMCErrResponseTimeOut );
+
+		SMF_INVOKES( ExecCommandSMST, EStSetRangeBusyCheck )
+
+	SMF_STATE(EStSetRangeBusyCheck)
+
+		if( !err )
+			{
+			// Bit31 of the OCR response is low if the cards are still powering up.
+			const TUint32 ocrResponse = TMMC::BigEndian32(s.ResponseP());
+
+			const TBool isBusy = ((ocrResponse & KMMCOCRBusy) == 0);
+			__KTRACE_OPT(KPBUS1,Kern::Printf("-mmc:upd:bsy%d", isBusy));
+
+			if (isBusy)	
+				{
+				// Some cards are still busy powering up. Check if we should timeout
+				if ( ++iCxPollRetryCount > iConfig.OpCondBusyTimeout() )
+					return( KMMCErrBusTimeOut );
+				m.ResetTraps();
+				SMF_INVOKES( RetryGapTimerSMST, EStSetRangeLoop )
+				}
+
+			iSpare[0] = 0;
+
+			if((ocrResponse & KMMCOCRAccessModeMask) == KMMCOCRAccessModeHCS)
+				{
+				__KTRACE_OPT(KPBUS1, Kern::Printf("Found large MMC card."));
+				iSpare[0] = KMMCardIsHighCapacity;
+				}
+			}
+
+		iConfig.SetMode( EffectiveModes(s.iConfig) & KMMCModeEnableTimeOutRetry );	// Restore original setting
+
+		// All cards are now ready and notified of the voltage range - ask ASSP to set it up
+		psu->SetVoltage(iCurrentOpRange);
+		if (psu->SetState(EPsuOnFull) != KErrNone)
+			return(KMMCErrHardware);
+
+		iCardArray->InitNewCardScan(); // Collect new cards, one by one
+
+	SMF_STATE(EStCIDLoop)
+
+		if ( iCardArray->NewCardCount() >= iMaxCardsInStack )
+			SMF_GOTOS( EStCIDsDone )
+
+		s.FillCommandDesc( ECmdAllSendCID, 0 );
+		m.SetTraps( KMMCErrResponseTimeOut );
+
+		SMF_INVOKES( ExecCommandSMST, EStSendCIDIssued )
+
+	SMF_STATE(EStSendCIDIssued)
+
+		if( !err )
+			{
+			// A card responded with a CID. Create a new card entry in the card array 
+			// and initialise this entry with the CID. The card array allocates it an
+			// RCA, either the old RCA if we have seen this card before, or a new one.
+			TRCA rca;
+			iCardArray->AddNewCard(s.ResponseP(),&rca); // Response is CID
+
+			// Now assign the new RCA to the card
+			s.FillCommandDesc( ECmdSetRelativeAddr, TMMCArgument(rca) );
+			m.ResetTraps();
+			SMF_INVOKES( ExecCommandSMST, EStCIDLoop )
+			}
+
+	SMF_STATE(EStCIDsDone)
+
+		// All cards are initialised; get all their CSDs
+		m.ResetTraps();				// We are no longer processing any errors
+
+		if( iCardArray->NewCardCount()==0 )
+			SMF_EXIT						// No new cards acquired
+
+		iCxCardCount=0;							// New cards index
+		s.FillCommandDesc( ECmdSendCSD );
+
+	SMF_STATE(EStCSDLoop)
+
+		s.Command().iArgument = TMMCArgument(iCardArray->NewCardP(iCxCardCount)->iRCA);
+		SMF_INVOKES( ExecCommandSMST, EStSendCSDDone )
+
+	SMF_STATE(EStSendCSDDone)
+
+		// Store the CSD in the new card entry
+		TMMCard* cardP = iCardArray->NewCardP(iCxCardCount);
+		cardP->iCSD = s.ResponseP();
+
+		// Perform MMC Specific parsing of the CSD structure
+		TUint specVers = cardP->CSD().SpecVers();	// 1 => 1.4, 2 => 2.0 - 2.2, 3 => 3.1
+
+		if ((specVers >= 2) && (cardP->CSD().CCC() & KMMCCmdClassLockCard))
+			{
+			cardP->iFlags |= KMMCardIsLockable;
+			}
+
+		if(iSpare[0] == KMMCardIsHighCapacity)
+			{
+			cardP->iFlags |= KMMCardIsHighCapacity;
+			}
+
+		if( ++iCxCardCount < (TInt)iCardArray->NewCardCount() )
+			SMF_GOTOS( EStCSDLoop )
+
+		SMF_NEXTS(EStMergeCards)
+
+	SMF_STATE(EStMergeCards)
+
+		// Merging the old card info with newly acquired cards (we will ask each card for status
+		// to determine whether it's really present later).
+		if( SchedGetOnDFC() )
+			SMF_WAIT
+		if ( iCardArray->MergeCards(ETrue)==KErrNone )
+			SMF_EXIT						// Completed successfully
+
+		SMF_INVOKES( CheckStackSMST, EStReMergeCards ) // No space so check if any cards have gone
+
+	SMF_STATE(EStReMergeCards)
+
+		if( SchedGetOnDFC() )
+			SMF_WAIT
+		if ( iCardArray->MergeCards(EFalse)!=KErrNone ) // There are more cards in the stack than we can handle
+			return(KMMCErrTooManyCards);
+
+	SMF_END
+	}
+
+
+/**
+ * Power down the bus and power it up again in low-voltage mode
+ * 
+ * @return MMC error code.
+ */
+TMMCErr DMMCStack::SwitchToLowVoltageSM()
+	{
+	enum states
+		{
+		EStBegin=0,
+		EStPoweredUp,
+		EStClockOn,
+		EStEnd
+		};
+
+	__KTRACE_OPT(KPBUS1,Kern::Printf(">MMC:SwLowVolt"));
+
+	DMMCPsu* psu=(DMMCPsu*)iSocket->iVcc;
+
+	SMF_BEGIN
+		// turn power off
+		DoPowerDown();
+		psu->SetState(EPsuOff);
+
+		// turn power back on in low voltage mode
+		psu->SetVoltage(iCurrentOpRange);
+		if (psu->SetState(EPsuOnFull) != KErrNone)
+			return(KMMCErrHardware);
+
+		SMF_INVOKES( DoPowerUpSMST, EStPoweredUp )
+
+	SMF_STATE(EStPoweredUp)
+		// turn the clock back on
+		SMF_INVOKES( InitClockOnSMST, EStClockOn )	// Feed init clock to the bus
+
+	SMF_STATE(EStClockOn)
+		// wait for 1ms and then 74 clock cycles
+		// 74 clock cylces @ 400 Khz = 74 / 400,000 = 0.000185 secs = 0.185 ms
+		// so total wait = 1.185 ms
+		SMF_INVOKES(LowVoltagePowerupTimerSMST, EStEnd);
+
+	SMF_END
+
+	}
+
+inline TMMCErr DMMCStack::CIMCheckStackSM()
+/**
+ * Performs the CIM_CHECK_STACK macro (with pre-emption disabled).
+ * @return MMC error code
+ */
+	{
+		enum states
+			{
+			EStBegin=0,
+			EStFinish,
+			EStEnd
+			};
+
+		DMMCSession& s=Session();
+
+	SMF_BEGIN
+
+		// This macro works naked and must not be preempted
+		iConfig.RemoveMode( KMMCModeEnablePreemption | KMMCModeCardControlled );
+		s.iState |= KMMCSessStateInProgress;
+
+		SMF_INVOKES( CheckStackSMST, EStFinish )
+
+	SMF_STATE(EStFinish)
+
+		s.iState &= ~KMMCSessStateInProgress;
+
+	SMF_END
+	}
+
+inline TMMCErr DMMCStack::CheckStackSM()
+/**
+ * For each card in iCards[], sends CMD13 to see if still there.
+ * If not, calls DeclareCardAsGone().  Frees up space for new cards.
+ * @return MMC error code
+ */
+	{
+		enum states
+			{
+			EStBegin=0,
+			EStLoop,
+			EStCardSelectedGotStatus,
+			EStCardDeselected,
+			EStEnd
+			};
+
+		DMMCSession& s=Session();
+
+	SMF_BEGIN
+
+		iCxCardCount=-1;
+		m.SetTraps( KMMCErrResponseTimeOut );
+
+	SMF_STATE(EStLoop)
+
+		if ( ++iCxCardCount == (TInt)iMaxCardsInStack )
+			SMF_EXIT
+
+		if ( !iCardArray->CardP(iCxCardCount)->IsPresent() )
+			SMF_GOTOS( EStLoop )					// card's not present
+
+		TUint32 arg = TUint32(iCardArray->CardP(iCxCardCount)->RCA()) << 16;
+		s.FillCommandDesc(ECmdSelectCard, arg);
+		SMF_INVOKES(ExecCommandSMST, EStCardSelectedGotStatus)
+
+	SMF_STATE(EStCardSelectedGotStatus)
+
+		__KTRACE_OPT(KPBUS1, Kern::Printf("-mst:cssm:err%08x", err));
+
+		if(err)
+			{
+			// Timeout - the card is no longer present so remove from the card array
+			iCardArray->DeclareCardAsGone(iCxCardCount);
+			SMF_GOTOS( EStLoop )
+			}
+
+		TMMCard& card=*(iCardArray->CardP(iCxCardCount));
+		card.iStatus=s.ResponseP();
+
+		// This function is only called as part of the power up sequence, so
+		// take the opportunity to record if it has a password
+		if((card.iStatus & KMMCStatCardIsLocked) != 0)
+			{
+			card.iFlags|=KMMCardHasPassword;
+			}
+
+		s.FillCommandDesc(ECmdSelectCard, 0);
+		SMF_INVOKES(ExecCommandSMST, EStCardDeselected)
+			
+	SMF_STATE(EStCardDeselected)
+		SMF_GOTOS( EStLoop )
+
+	SMF_END
+	}
+
+inline TMMCErr DMMCStack::CheckLockStatusSM()
+/*
+ * Called as part of the power-up sequence, this determined if the card is locked or has a password.
+ *
+ * If the card reports itself as being unlocked and there is a mapping in the password store, 
+ * then the stored password is used to attempt to lock the card. The overall aim of this is
+ * to ensure that if a card always powers up in the locked state if it contains a known password.
+ *
+ * This ensures that cards that are still unlocked after a power down/power up sequence do not
+ * end up having their passwords removed from the store, which can happen in environments where
+ * the PSU voltage level is not monitored - in such systems, we cannot guarantee that a card will 
+ * be fully reset and power up locked, hence the need to attempt to lock the card.
+ *
+ * @return MMC error code
+ */
+	{
+		enum states
+			{
+			EStBegin=0,
+			EStLoop,
+			EStCardSelectedGotStatus,
+			EStCheckLockStatus,
+			EStCardDeselected,
+			EStEnd
+			};
+
+		DMMCSession& s=Session();
+
+	SMF_BEGIN
+
+		iCxCardCount=-1;
+		m.SetTraps( KMMCErrResponseTimeOut );
+		iMinorBufLen = KMinMinorBufSize;
+
+	SMF_STATE(EStLoop)
+
+		if ( ++iCxCardCount == (TInt)iMaxCardsInStack )
+			SMF_EXIT
+
+		if ( !iCardArray->CardP(iCxCardCount)->IsPresent() )
+			SMF_GOTOS( EStLoop )					// card's not present
+
+		TUint32 arg = TUint32(iCardArray->CardP(iCxCardCount)->RCA()) << 16;
+		s.FillCommandDesc(ECmdSelectCard, arg);
+		SMF_INVOKES(ExecCommandSMST, EStCardSelectedGotStatus)
+
+	SMF_STATE(EStCardSelectedGotStatus)
+
+		__KTRACE_OPT(KPBUS1, Kern::Printf("-mst:cssm:err%08x", err));
+		if ( !err )
+			{
+			TMMCard& card=*(iCardArray->CardP(iCxCardCount));
+			card.iStatus=s.ResponseP(); // Got the response
+
+			iMinorBufLen = Max(iMinorBufLen, 1 << card.MaxReadBlLen());
+
+			// this function is only called as part of the power up sequence, so
+			// take the opportunity to record if it has a password
+			if((card.iStatus & KMMCStatCardIsLocked) != 0)
+				{
+				card.iFlags |= KMMCardHasPassword;
+				}
+				
+			// If the status suggests that the card is unlocked, we test
+			// for the presence of a password by attempting to lock the card
+			// (if we have a password in the store).  This handles conditions
+			// where a card has not been fully powered down before reapplying power.
+			if(!(card.iFlags & KMMCardHasPassword))
+				{
+				TMapping* pmp = iSocket->iPasswordStore->FindMappingInStore(card.CID());
+				if(pmp)
+					{
+					if(pmp->iState == TMapping::EStValid)
+						{
+						const TInt kPWD_LEN = pmp->iPWD.Length();
+						iPSLBuf[0] = KMMCLockUnlockLockUnlock;	// LOCK_UNLOCK = 1, SET_PWD = 0, CLR_PWD = 0
+						iPSLBuf[1] = static_cast<TUint8>(kPWD_LEN);
+						TPtr8 pwd(&iPSLBuf[2], kPWD_LEN);
+						pwd.Copy(pmp->iPWD);
+
+						const TInt kBlockLen = 1 + 1 + kPWD_LEN;
+
+						// Need to use CIMReadWriteBlocksSMST to ensure that the
+						// card is connected and the block length is set correctly
+						s.SetCard(iCardArray->CardP(iCxCardCount));
+						m.SetTraps(KMMCErrStatus | KMMCErrUpdPswd);
+						s.FillCommandDesc(ECmdLockUnlock);
+						s.FillCommandArgs(0, kBlockLen, iPSLBuf, kBlockLen);
+
+						TMMCCommandDesc& cmd = s.Command();
+						cmd.iUnlockRetries = 0;
+						
+						SMF_INVOKES(CIMReadWriteBlocksSMST,EStCheckLockStatus)
+						}
+					}
+				}
+			}
+
+		SMF_GOTOS( EStLoop )
+
+	SMF_STATE(EStCheckLockStatus)
+
+		__KTRACE_OPT(KPBUS1, Kern::Printf("-mst:cssm:err%08x", err));
+
+		if ((err & KMMCErrUpdPswd) || 
+		   ((err & KMMCErrStatus) && (s.LastStatus().Error() == KMMCStatErrLockUnlock)))
+			{
+			// ECMDLockUnlock (with LockUnlockLockUnlock param) succeeded.
+			// (either locked successfully, or we have attempted to lock a locked card)
+			// Now determine if the card really is locked by checking the lock status.
+			TMMCard& card=*(iCardArray->CardP(iCxCardCount));
+			card.iStatus=s.LastStatus();
+			if((card.iStatus & KMMCStatCardIsLocked) != 0)
+				{
+				card.iFlags |= KMMCardHasPassword;
+				}
+			}
+
+		s.FillCommandDesc(ECmdSelectCard, 0);
+		SMF_INVOKES(ExecCommandSMST, EStCardDeselected)
+			
+	SMF_STATE(EStCardDeselected)
+		SMF_GOTOS( EStLoop )
+
+	SMF_END
+	}
+
+EXPORT_C TMMCErr DMMCStack::ModifyCardCapabilitySM()
+//
+// This function provides a chance to modify the capability of paticular cards.
+// Licensee may overide this function to modify certain card's capability as needed.
+// A state machine is needed in derived function and function of base class should be
+// called in order to act more generic behaviour.
+//
+    {
+		enum states
+			{
+			EStBegin=0,
+			EStEnd
+			};
+
+    SMF_BEGIN
+
+    SMF_END
+    }
+
+//
+// Timers
+//
+
+inline TMMCErr DMMCStack::PollGapTimerSM()
+/**
+ * Starts the poll timer.
+ *
+ * This may be used when executing CIM_UPDATE_ACQ when handling cards which are 
+ * slow to power-up/reset and return busy following the issuing of CMD1.
+ *
+ * @return MMC error code.
+ */
+	{
+		enum states
+			{
+			EStBegin=0,
+			EStEnd
+			};
+#ifdef __EPOC32__
+		DMMCSession& s=Session();
+#endif
+
+	SMF_BEGIN
+
+#ifdef __EPOC32__
+		s.SynchBlock( KMMCBlockOnPollTimer );
+		s.iPollTimer.OneShot(KMMCPollGapInMilliseconds,EFalse);
+
+		SMF_EXITWAIT
+#endif
+
+	SMF_END
+	}
+
+inline TMMCErr DMMCStack::RetryGapTimerSM()
+/**
+ * Starts the retry timer. 
+ *
+ * This may be used when executing CIM_UPDATE_ACQ. When initialising the stack,
+ * CMD0 is issued twice to get the bus in a known state and this timer is used 
+ * to time the gap between issuing the two CMD0 commands.
+ *
+ * @return MMC error code.
+ */
+	{
+		enum states
+			{
+			EStBegin=0,
+			EStEnd
+			};
+#ifdef __EPOC32__
+		DMMCSession& s=Session();
+#endif
+
+	SMF_BEGIN
+
+#ifdef __EPOC32__
+		s.SynchBlock( KMMCBlockOnRetryTimer );
+		s.iRetryTimer.OneShot(KMMCRetryGapInMilliseconds,EFalse);
+
+		SMF_EXITWAIT
+#endif
+
+	SMF_END
+	}
+
+inline TMMCErr DMMCStack::ProgramTimerSM()
+/**
+ * Starts the program timer.
+ *
+ * This is used during write operartions to a card to sleep for an PSL-dependent period 
+ * between issuing send status commands (CMD13). This is required in order to check when 
+ * the card has finished writing its data to payload memory.
+ *
+ * @return MMC error code.
+ */
+	{
+	enum states
+		{
+		EStBegin = 0,
+		EStEnd
+		};
+
+#ifdef __EPOC32__
+	DMMCSession &s = Session();
+#endif
+
+	SMF_BEGIN
+#ifdef __EPOC32__
+		s.SynchBlock(KMMCBlockOnPgmTimer);
+		s.iProgramTimer.Cancel();
+		s.iProgramTimer.OneShot(ProgramPeriodInMilliSeconds(),EFalse);
+
+		SMF_EXITWAIT
+#endif
+	SMF_END
+	}
+
+TMMCErr DMMCStack::LowVoltagePowerupTimerSM()
+/**
+ * Starts the low voltage power-up timer
+ * NB Re-uses the retry gap timer.
+ *
+ * This is used after powering the bus off and then on after discovering that 
+ * both the controller and card support low voltage operation.
+ *
+ * @return MMC error code.
+ */
+	{
+	enum states
+		{
+		EStBegin = 0,
+		EStEnd
+		};
+
+#ifdef __EPOC32__
+	DMMCSession &s = Session();
+#endif
+
+	SMF_BEGIN
+#ifdef __EPOC32__
+		s.SynchBlock(KMMCBlockOnRetryTimer);
+		s.iRetryTimer.OneShot(KMMCLowVoltagePowerUpTimeoutInMilliseconds,EFalse);
+
+		SMF_EXITWAIT
+#endif
+	SMF_END
+	}
+
+
+inline TMMCErr DMMCStack::ExecCommandSM()
+/**
+ * The main command executor. 
+ * Depending on the main command being issued, this macro may result in the issuing of whole sequence 
+ * of commands as it prepares the bus for the command in question. 
+ *
+ * In certain circumstances, this first issues one or more de-select commands (CMD7 + reserved RCA) 
+ * to get the bus in a known state. It then analyses the main command and if necessary, selects the 
+ * card in question (CMD7 + target RCA). 
+ *
+ * For block transfer commands, it will set the block length on the card concerned (CMD16) if this has 
+ * not been done already. Likewise, for SD Cards, if the bus width has not yet been set-up, it will issue 
+ * the appropriate bus width command (ACMD6). 
+ * 
+ * Finally it issues the main command requested before performing any error recovery that may be necessary 
+ * following this. 
+ *
+ * In all cases it calls the generic layer child function IssueCommandCheckResponseSM() to execute each command.
+ *
+ * @return MMC error code
+ */
+	{
+		enum states
+			{
+			EStBegin=0,
+			EStExecCmd,
+			EStRetry,
+			EStDeselectLoop,
+			EStDeselectEndCheck,
+			EStAnalyseCommand,
+			EStSelectDone,
+			EStBlockCountCmdIssued,
+			EStTestAppCommand,
+			EStIssueAppCommandDone,
+			EStIssueCommand,
+			EStCommandIssued,
+			EStErrRecover,
+			EStEnd
+			};
+
+		DMMCSession& s=Session();
+		
+	SMF_BEGIN
+
+		if ( ( s.CardRCA() != 0 ) && ( (s.CardP()->iStatus.State()) == ECardStateSlp) )
+			{
+			// Currently selected media is asleep, so it must be awoken
+			SMF_INVOKES(ExecAwakeCommandSMST,EStExecCmd)
+			}
+	
+	SMF_STATE(EStExecCmd)
+	
+		TMMCCommandDesc& cmd = s.Command();
+		// clearup some internally used flags
+		cmd.iFlags &= ~(KMMCCmdFlagExecTopBusy|KMMCCmdFlagExecSelBusy);
+		cmd.iPollAttempts = cmd.iTimeOutRetries = cmd.iCRCRetries = 0;
+
+	SMF_STATE(EStRetry)
+
+		TMMCCommandDesc& cmd = s.Command();
+		m.SetTraps( KMMCErrBasic & ~Command().iExecNotHandle);	// Processing all trappable errors
+
+		if (iMultiplexedBus)
+			{
+			if(cmd.iCommand == ECmdSelectCard)
+				{
+				DeselectsToIssue(1);
+				}
+
+			if (iConfig.iModes & KMMCModeCardControlled)
+				{
+				DoSetBusWidth(BusWidthEncoding(s.CardP()->BusWidth()));
+				DoSetClock(MaxTranSpeedInKilohertz(*s.CardP()));
+
+				// Check if this card is already in the appropriate selected/deselected
+				// state for the forthcoming command.
+				if (s.CardRCA() != iSelectedCard)
+					{
+					DeselectsToIssue(1);
+					}
+				}
+			}
+
+		// If bus context is unknown, issue DESELECT a few times with a RetryGap between them.
+		if ( (iStackState & KMMCStackStateDoDeselect) == 0 )
+			SMF_GOTOS( EStAnalyseCommand )
+
+		// Save the top-level command while we issue de-selects
+		s.PushCommandStack();
+		s.FillCommandDesc( ECmdSelectCard, 0 );		// Deselect - RCA=0
+		iCxDeselectCount=iDeselectsToIssue;
+
+	SMF_STATE(EStDeselectLoop)
+
+		SMF_INVOKES(IssueCommandCheckResponseSMST,EStDeselectEndCheck)	
+
+	SMF_STATE(EStDeselectEndCheck)
+
+		// If we got an error and this is the last de-select then give up
+		if (err && iCxDeselectCount == 1)
+			{
+			s.PopCommandStack();
+			SMF_RETURN(err)
+			}
+
+		if (--iCxDeselectCount > 0)
+			SMF_INVOKES(RetryGapTimerSMST,EStDeselectLoop)
+
+		s.PopCommandStack();
+		iStackState &= ~KMMCStackStateDoDeselect;
+
+	SMF_STATE(EStAnalyseCommand)
+
+		TMMCCommandDesc& cmd = s.Command();
+		// Check if its un-important whether the card is in transfer state (i.e
+		// selected) or not for the command we are about to execute. Such
+		// commands are CMD0, CMD7 and CMD13.
+		
+		// This state machine should never send CMD55
+		if (cmd.iCommand == ECmdAppCmd)
+			SMF_RETURN (KMMCErrNotSupported)
+
+		SMF_NEXTS( EStTestAppCommand )
+		if (cmd.iCommand == ECmdGoIdleState || cmd.iCommand == ECmdSelectCard || cmd.iCommand == ECmdSendStatus)
+			{
+			SMF_GOTONEXTS
+			}
+
+		// See if we need to select (or deselect) this card
+		TRCA targetRCA=0;
+		switch( cmd.iSpec.iCommandType )
+			{
+			case ECmdTypeBC: case ECmdTypeBCR: case ECmdTypeAC:
+			// Command which don't require the card to be selected
+				break;
+			case ECmdTypeACS: case ECmdTypeADTCS: case ECmdTypeADC:
+			// Commands which do require the card to be selected
+				{
+				if ( (iConfig.iModes & KMMCModeCardControlled) == 0 )
+					SMF_GOTONEXTS
+				// Get the RCA of the card
+				if ( (targetRCA = s.CardRCA()) == 0 )
+					SMF_RETURN( KMMCErrNoCard )
+				break;
+				}
+			default:
+				SMF_GOTONEXTS
+			}
+
+		// Check if this card is already in the appropriate selected/deselected
+		// state for the forthcoming command.
+		if (targetRCA==iSelectedCard)
+			SMF_GOTONEXTS
+
+		// Need to select (or deselect by using RCA(0)) the card so push the
+		// top-level command onto the command stack while we issue the select command.
+		s.PushCommandStack();
+		s.FillCommandDesc(ECmdSelectCard,targetRCA);
+		SMF_INVOKES(IssueCommandCheckResponseSMST,EStSelectDone)	
+
+	SMF_STATE(EStSelectDone)
+
+		TMMCCommandDesc& cmd = s.Command();
+		
+		if ( err )
+			{
+			cmd.iFlags &= ~(KMMCCmdFlagASSPFlags|KMMCCmdFlagExecSelBusy);
+
+			if (err == KMMCErrBusyTimeOut)
+				cmd.iFlags |= KMMCCmdFlagExecSelBusy;
+
+			s.PopCommandStack();
+			SMF_NEXTS(EStErrRecover)
+			return(err);		// re-enter the next state with that error
+			}
+
+		// Are we trying to recover from a top-level command returning busy (by de-selecting and re-selecting)
+		if ( cmd.iFlags & KMMCCmdFlagExecTopBusy )
+			{
+			cmd.iFlags &= ~KMMCCmdFlagExecTopBusy;
+
+			TUint32 blockLength = cmd.BlockLength();
+
+			if ( !(cmd.iSpec.iMultipleBlocks) || cmd.iTotalLength <= blockLength )
+				SMF_EXIT		// operation is completed
+
+			cmd.iTotalLength    -= blockLength;
+			cmd.iArgument        = TUint(cmd.iArgument) + blockLength;
+			cmd.iDataMemoryP    += blockLength;
+			s.iBytesTransferred += blockLength;
+			cmd.iPollAttempts = 0;
+			}
+
+		s.PopCommandStack();
+		
+		cmd = s.Command();
+		if (!cmd.iSpec.iUseStopTransmission && cmd.iSpec.iMultipleBlocks)
+			{
+			// Multi-block command using SET_BLOCK_COUNT
+			// This is a re-try of the data transfer, normally select (CMD7) is performed along with the issuing of CMD23, 
+			// therefore need to re-issue SET_BLOCK_COUNT.....
+	  		const TUint blocks = cmd.NumBlocks();
+	
+			s.PushCommandStack();
+			s.FillCommandDesc( ECmdSetBlockCount, blocks );
+			SMF_INVOKES( IssueCommandCheckResponseSMST, EStBlockCountCmdIssued )
+			}
+		else
+			{
+			SMF_GOTOS( EStTestAppCommand )
+			}
+
+	SMF_STATE(EStBlockCountCmdIssued)
+		
+		const TMMCStatus status(s.ResponseP());
+		s.PopCommandStack();
+		if (status.Error())
+			SMF_RETURN(KMMCErrStatus)
+
+		if(err & KMMCErrNotSupported)
+			{
+			// Not supported by the PSL, so use standard Stop Transmission mode
+			s.Command().iSpec.iUseStopTransmission = ETrue;
+			}
+		// Fall through...		
+
+	SMF_STATE(EStTestAppCommand)
+		TMMCCommandDesc& cmd = s.Command();
+		if (cmd.iSpec.iCommandClass != KMMCCmdClassApplication)
+			SMF_GOTOS( EStIssueCommand )
+
+		s.PushCommandStack();
+		s.FillCommandDesc(ECmdAppCmd, s.CardRCA()); // Send APP_CMD (CMD55)	
+		SMF_INVOKES(IssueCommandCheckResponseSMST,EStIssueAppCommandDone)
+		
+	SMF_STATE(EStIssueAppCommandDone)
+		s.PopCommandStack();
+		if ( err )
+			{
+			SMF_NEXTS(EStErrRecover)
+			return(err);		// re-enter the next state with that error
+			}
+
+
+	SMF_STATE(EStIssueCommand)
+
+		TMMCCommandDesc& cmd = s.Command();
+		// If its an addressed command (rather than a selected command), then
+		// setup the argument with the RCA. (Commands requiring card to be
+		// selected - ACS don't matter since selected cards don't need an RCA now).
+		if ((iConfig.iModes & KMMCModeCardControlled) && cmd.iSpec.iCommandType==ECmdTypeAC )
+			{
+			const TRCA targetRCA = s.CardRCA();
+			if ( targetRCA == 0 )
+				SMF_RETURN( KMMCErrNoCard )
+			cmd.iArgument.SetRCA(targetRCA);
+			}
+
+		// Issue the top-level command
+		SMF_INVOKES(IssueCommandCheckResponseSMST,EStCommandIssued)
+
+	SMF_STATE(EStCommandIssued)
+
+		// If command was succesful then we've finished.
+		if (!err)
+			SMF_EXIT
+
+	SMF_STATE(EStErrRecover)
+
+		TMMCCommandDesc& cmd = s.Command();
+		const TUint32 modes=iConfig.iModes;
+		SMF_NEXTS(EStRetry)
+
+		m.ResetTraps();							// no more re-entries via return()
+
+		if (cmd.iCommand == ECmdSelectCard)
+			DeselectsToIssue( 1 );	// in case stby/tran synch is lost
+
+		if (cmd.iSpec.iMultipleBlocks && (cmd.iFlags & KMMCCmdFlagBytesValid))
+			{
+			cmd.iTotalLength -= cmd.iBytesDone;
+			cmd.iArgument = TUint(cmd.iArgument) + cmd.iBytesDone;
+			cmd.iDataMemoryP += cmd.iBytesDone;
+			s.iBytesTransferred += cmd.iBytesDone;
+
+			if (cmd.iTotalLength < cmd.BlockLength())
+				{
+				DeselectsToIssue(1);
+				return(err);
+				}
+			}
+
+		if ((modes & KMMCModeEnableRetries) == 0)
+			return( err );
+
+		const TUint32 toMask = (KMMCErrResponseTimeOut|KMMCErrDataTimeOut);
+		const TUint32 crcMask = (KMMCErrResponseCRC|KMMCErrDataCRC|KMMCErrCommandCRC);
+
+		if( (err & ~(toMask|crcMask)) == KMMCErrNone ) // time-outs/CRC errors
+			{
+			if( cmd.iSpec.iCommandType == ECmdTypeADTCS )	// for data transfer commands
+				{
+				DeselectsToIssue( 1 );						// enforce deselect before any retries
+
+				if( (modes & KMMCModeCardControlled) == 0 )
+					return( err );		// we wouldn't know what to select - no retries
+				}
+
+			TUint32 gapEnabled = 0;
+
+			if( err & toMask )
+				{
+				cmd.iTimeOutRetries++;
+				gapEnabled |= KMMCModeTimeOutRetryGap;
+
+				if( (modes & KMMCModeEnableTimeOutRetry) == 0 ||
+					cmd.iTimeOutRetries > iConfig.iTimeOutRetries )
+					return( err );
+				}
+
+			if( err & crcMask )
+				{
+				cmd.iCRCRetries++;
+				gapEnabled |= KMMCModeCRCRetryGap;
+
+				if( (modes & KMMCModeEnableCRCRetry) == 0	||
+					cmd.iCRCRetries > iConfig.iCRCRetries		||
+					((err & KMMCErrDataCRC) != 0 && (modes & KMMCModeDataCRCRetry) == 0) )
+					return( err );
+				}
+
+			if( (modes & gapEnabled) == gapEnabled )
+				{
+				if( modes & KMMCModeCardControlled )
+					s.iState |= KMMCSessStateSafeInGaps;	// preemption allowed
+
+				SMF_CALL( RetryGapTimerSMST )
+				}
+
+			if( (modes & (KMMCModeEnablePreemption|KMMCModePreemptOnRetry|KMMCModeCardControlled)) ==
+				(KMMCModeEnablePreemption|KMMCModePreemptOnRetry|KMMCModeCardControlled) )
+				{
+				s.SwapMe();
+				SMF_WAIT		// let the others take over the bus before retry
+				}
+
+			// No preemption, just repeat the command
+			SMF_GOTONEXTS
+			}
+
+		if( err & KMMCErrBusInconsistent )
+			{
+			// ASSP layer reported that we must re-initialise the stack to recover.
+			// Here we'll allow stack initialiser to take over. The control will then be transferred
+			// to whoever processes KMMCErrInitContext (must be a top-level SM function)
+
+//			ReportInconsistentBusState();	// ASSP layer should have it done
+			s.iGlobalRetries++;
+
+			if( s.iGlobalRetries > KMMCMaxGlobalRetries )
+				return( err );
+
+			s.SwapMe();		// To prevent the initialiser from aborting this session
+			SMF_WAIT		// Initialiser will take over here
+			}
+
+		if( err == KMMCErrBusyTimeOut )
+			{
+			if ((cmd.iFlags & KMMCCmdFlagExecSelBusy) == 0)	// check if that was a response
+				cmd.iFlags |= KMMCCmdFlagExecTopBusy;		// to a top level command
+
+			DeselectsToIssue( 1 );	// force deselect as the next bus operation
+			cmd.iPollAttempts++;
+
+			if( (modes & KMMCModeEnableBusyPoll) == 0 ||
+				((modes & KMMCModeCardControlled) == 0 && cmd.iCommand != ECmdSelectCard) ||
+				cmd.iPollAttempts > iConfig.iPollAttempts )
+				return( err );
+
+			if( modes & KMMCModeBusyPollGap )
+				{
+				s.iState |= KMMCSessStateSafeInGaps;	// preemption allowed
+				SMF_CALL( PollGapTimerSMST )
+				}
+
+			if( (modes & (KMMCModeEnablePreemption|KMMCModePreemptOnBusy)) ==
+				(KMMCModeEnablePreemption|KMMCModePreemptOnBusy) )
+				{
+				s.SwapMe();
+				SMF_WAIT		// let the others take over the bus before retry
+				}
+
+			// No preemption, just repeat the Deselect/Select sequence
+			SMF_GOTONEXTS
+			}
+
+		return( err );
+
+	SMF_END
+	}
+
+TMMCErr DMMCStack::IssueCommandCheckResponseSM()
+/**
+ * Issue a single command to the card and check the response 
+ *
+ * This generic layer child function in turn calls IssueMMCCommandSM().
+ *
+ * @return MMC error code.
+ */
+	{
+	enum states
+		{
+		EStBegin=0,
+		EStIssueCommand,
+		EStCommandIssued,
+		EStEnd
+		};
+
+		DMMCSession& s=Session();
+		TMMCCommandDesc& cmd = Command();
+
+	SMF_BEGIN
+
+		__KTRACE_OPT(KPBUS1,Kern::Printf(">MMC:Issue %d %x",TUint(cmd.iCommand),TUint(cmd.iArgument)));
+
+		// Stop the Controller from powering down the card due to bus inactivity
+		iSocket->ResetInactivity(0);
+
+	SMF_STATE(EStIssueCommand)
+
+		// If command is directed at a specific card then save this command in card object
+		if (iConfig.iModes & KMMCModeCardControlled)
+			{
+			s.iCardP->iLastCommand = cmd.iCommand;
+
+			if(iMultiplexedBus)
+				{
+				DoSetBusWidth(BusWidthEncoding(s.CardP()->BusWidth()));
+				DoSetClock(MaxTranSpeedInKilohertz(*s.CardP()));
+				}
+			}
+
+		if (cmd.iCommand==ECmdSelectCard)
+			iSelectedCard = TUint16(~0);
+
+		// Pass the command to ASSP layer
+		cmd.iFlags &= ~(KMMCCmdFlagASSPFlags|KMMCCmdFlagExecSelBusy);
+		cmd.iBytesDone=0;
+		m.SetTraps(KMMCErrAll);
+		SMF_INVOKES(IssueMMCCommandSMST,EStCommandIssued)
+
+	SMF_STATE(EStCommandIssued)
+
+#ifdef ENABLE_DETAILED_SD_COMMAND_TRACE
+		cmd.Dump(s.ResponseP(), err);
+#endif
+
+		TMMCErr exitCode=err;
+		// If we have just de-selected all cards in the stack, RCA(0) then ignore response timeout.
+		if ( cmd.iCommand==ECmdSelectCard && TRCA(cmd.iArgument)==0 )
+			exitCode &= ~KMMCErrResponseTimeOut;
+		else
+			{
+			// If commands returns card status and there we no command errors
+			// (or the status contains errors) then save the status info.
+			if ( (cmd.iFlags & KMMCCmdFlagStatusReceived) ||
+				 ((exitCode==KMMCErrNone || (exitCode & KMMCErrStatus)) &&
+				  (cmd.iSpec.iResponseType==ERespTypeR1 || cmd.iSpec.iResponseType==ERespTypeR1B)) )
+				{
+				TMMCStatus status=s.ResponseP();
+				s.iLastStatus=status;
+				__KTRACE_OPT(KPBUS1, Kern::Printf("mmc:ec:st=%08x", TUint32(status)));
+
+				if (iConfig.iModes & KMMCModeCardControlled)
+					s.iCardP->iStatus=status;
+
+				// Update exit code if card status is reporting an error (in case not done already)
+				if (status.Error() != 0)
+					exitCode |= KMMCErrStatus;
+				}
+			}
+
+		// If we've just selected a card and the command was succesful then
+		// remember which one so we don't need to do it twice.
+		if (cmd.iCommand==ECmdSelectCard && exitCode==KMMCErrNone)
+			iSelectedCard=TRCA(cmd.iArgument);
+
+		SMF_RETURN(exitCode)
+
+	SMF_END
+	}
+
+//
+// General Client Service CIMs/Sessions; top level functions
+//
+inline TMMCErr DMMCStack::NakedSessionSM()
+/**
+ * Executes an individual MMC command (as opposed to a macro).
+ *
+ * If the command is 'card controlled' it first invokes AttachCardSM() 
+ * before calling ExecCommandSM() to excecute the requested command.
+ *
+ * @return MMC error code.
+ */
+	{
+		enum states
+			{
+			EStBegin=0,
+			EStAttached,
+			EStFinish,
+			EStEnd
+			};
+
+		DMMCSession& s=Session();
+
+	SMF_BEGIN
+		s.iState |= KMMCSessStateInProgress;
+
+		if( (iConfig.iModes & KMMCModeCardControlled) != 0 )
+			SMF_INVOKES( AttachCardSMST, EStAttached )
+
+	SMF_BPOINT(EStAttached)
+
+		SMF_INVOKES( ExecCommandSMST, EStFinish )
+
+	SMF_STATE(EStFinish)
+
+		s.iState &= ~KMMCSessStateInProgress;
+	SMF_END
+	}
+
+inline TMMCErr DMMCStack::CIMSetupCardSM()
+/**
+ * Executes the CIM_SETUP_CARD macro.
+ *
+ * @return MMC error code.
+ */
+	{
+		enum states
+			{
+			EStBegin=0,
+			EStAttached,
+			EStSelected,
+			EStGotCSD,
+			EStEnd
+			};
+
+		DMMCSession& s=Session();
+
+		__KTRACE_OPT(KPBUS1,Kern::Printf(">MMC:SetupCardSM %x",TUint(s.iLastStatus)));
+
+	SMF_BEGIN
+		s.iState |= KMMCSessStateInProgress;
+
+		SMF_INVOKES( AttachCardSMST, EStAttached )	// attachment is mandatory here
+
+	SMF_BPOINT(EStAttached)
+
+		s.FillCommandDesc( ECmdSelectCard, 0 );
+		SMF_INVOKES( ExecCommandSMST, EStSelected )
+
+	SMF_STATE(EStSelected)
+
+		if( !s.iCardP->IsReady() )
+			return( KMMCErrNoCard );
+
+		s.FillCommandDesc( ECmdSendCSD, Command().iArgument ); // NB: the card will be deselected to execute this command
+		SMF_INVOKES( ExecCommandSMST, EStGotCSD )
+
+	SMF_STATE(EStGotCSD)
+
+		s.iCardP->iCSD = s.ResponseP();
+
+		s.iState &= ~KMMCSessStateInProgress;
+	SMF_END
+	}
+
+EXPORT_C TMMCErr DMMCStack::CIMReadWriteBlocksSM()
+/**
+ * This macro performs single/multiple block reads and writes.
+ *
+ * For normal read/write block operations, this function determines the appropriate
+ * MMC command to send and fills the command descriptor accordingly based on 
+ * the value of the session ID set. However, it is necessary to have set the
+ * command arguments (with DMMCSession::FillCommandArgs()) before this function
+ * is called.
+ *
+ * For special block read/write operations, e.g. lock/unlock, it is required to
+ * have already filled the command descriptor (with DMMCSession::FillCommandDesc())
+ * for the special command required - in addition to have setup the command arguments.
+ *
+ * @return MMC error code.
+ */
+	{
+		enum states
+			{
+			EStBegin=0,
+			EStRestart,
+			EStAttached,
+			EStLength1,
+			EStLengthSet,
+			EStIssueBlockCount,
+			EStAppCmdIssued,
+			EStBlockCountCmdIssued,
+			EStBpoint1,
+			EStIssued,
+			EStWaitFinish,
+			EStWaitFinish1,
+			EStRWFinish,
+			EStEnd
+			};
+
+		DMMCSession& s=Session();
+
+		__KTRACE_OPT(KPBUS1,Kern::Printf(">MMC:RWBlocksSM %x",TUint(s.iLastStatus)));
+
+	SMF_BEGIN
+
+		if(s.iSessionID == ECIMWriteBlock || s.iSessionID == ECIMWriteMBlock)
+			{
+			// Check that the card supports class 4 (Write) commands
+			const TUint ccc = s.iCardP->CSD().CCC();
+			if(!(ccc & KMMCCmdClassBlockWrite))
+				return( KMMCErrNotSupported );
+			}
+
+		s.iState |= KMMCSessStateInProgress;
+		m.SetTraps(KMMCErrInitContext);
+
+	SMF_STATE(EStRestart)		// NB: ErrBypass is not processed here
+
+		SMF_CALLMEWR(EStRestart) // Create a recursive call entry to recover from the errors trapped
+		m.SetTraps(KMMCErrStatus);
+		if (s.Command().iSpec.iCommandClass!=KMMCCmdClassApplication || s.Command().iCommand==ECmdAppCmd)
+			{
+			s.ResetCommandStack();
+			SMF_INVOKES( AttachCardSMST, EStAttached )	// attachment is mandatory here
+			}
+
+	SMF_BPOINT(EStAttached)
+
+		TMMCCommandDesc& cmd = s.Command();
+
+		const TUint32 blockLength = cmd.BlockLength();
+		if(blockLength == 0)
+			return KMMCErrArgument;
+
+		if(s.iSessionID == ECIMReadBlock	||
+		   s.iSessionID == ECIMWriteBlock	||
+		   s.iSessionID == ECIMReadMBlock	||
+		   s.iSessionID == ECIMWriteMBlock)
+			{	
+			// read/write operation
+			if(!cmd.AdjustForBlockOrByteAccess(s))
+				{
+				// unable to convert command arguments to suit the underlying block/byte access mode
+				return KMMCErrArgument;
+				}
+			}
+
+		// Set the block length if it has changed. Always set for ECIMLockUnlock.
+		if ((blockLength == s.iCardP->iSetBlockLen) && (s.iSessionID != ECIMLockUnlock))
+			{
+			SMF_GOTOS( EStLengthSet )
+			}
+
+		s.iCardP->iSetBlockLen = 0;
+		s.PushCommandStack();
+		s.FillCommandDesc( ECmdSetBlockLen, blockLength );
+		SMF_INVOKES( ExecCommandSMST, EStLength1 )
+
+	SMF_STATE(EStLength1)
+
+		const TMMCStatus status(s.ResponseP());
+		s.PopCommandStack();
+		if (status.Error())
+			SMF_RETURN(KMMCErrStatus)
+		s.iCardP->iSetBlockLen = s.Command().BlockLength();
+
+	SMF_STATE(EStLengthSet)
+
+		TMMCCommandDesc& cmd = s.Command();
+		TUint opType = 0;
+		const TUint kTypeWrite =	KBit0;
+		const TUint kTypeMultiple =	KBit1;
+		const TUint kTypeSpecial =	KBit2;
+		static const TMMCCommandEnum cmdCodes[4] =
+			{ECmdReadSingleBlock, ECmdWriteBlock, ECmdReadMultipleBlock, ECmdWriteMultipleBlock};
+
+		switch( s.iSessionID )
+			{
+			case ECIMReadBlock:
+				break;
+			case ECIMWriteBlock:
+				opType=kTypeWrite;
+				break;
+			case ECIMReadMBlock:
+				opType=kTypeMultiple;
+				break;
+			case ECIMWriteMBlock:
+				opType=kTypeWrite|kTypeMultiple;
+				break;
+			case ECIMLockUnlock:
+			default:
+				opType=kTypeSpecial;
+				break;
+			}
+
+  		const TUint blocks = cmd.NumBlocks();
+
+		SMF_NEXTS(EStBpoint1)
+		if ( !(opType & kTypeSpecial) )	// A special session has already set its command descriptor
+			{
+			
+			if (cmd.iFlags & KMMCCmdFlagReliableWrite)
+				//ensure multiple block commands are used for reliable writing
+				opType |= kTypeMultiple;
+			
+			if ( (blocks==1) && !(cmd.iFlags & KMMCCmdFlagReliableWrite) )
+				{
+				// Reliable Write requires that Multi-Block command is used.
+				opType &= ~kTypeMultiple;
+				}
+
+			TUint32 oldFlags = cmd.iFlags;	// Maintain old flags which would be overwritten by FillCommandDesc
+			cmd.iCommand = cmdCodes[opType];
+			s.FillCommandDesc();
+			cmd.iFlags = oldFlags;			// ...and restore
+
+			if((opType & kTypeMultiple) == kTypeMultiple)
+				{
+				//
+				// This is a Multiple-Block DT command.  Check the version of the card, as
+				// MMC Version 3.1 onwards supports the SET_BLOCK_COUNT mode for DT.
+				//
+				// Note that if the PSL does not support pre-determined block count of
+				// data transmission, then it may return KMMCErrNotSupported to force
+				// the 'Stop Transmission' mode to be used instead.
+				//
+				if(s.iCardP->CSD().SpecVers() >= 3)
+					{
+					cmd.iSpec.iUseStopTransmission = EFalse;
+					SMF_NEXTS(EStIssueBlockCount)
+					}
+				else
+					{
+					cmd.iSpec.iUseStopTransmission = ETrue;
+					}
+				}
+			}
+
+		SMF_GOTONEXTS
+
+	SMF_STATE(EStIssueBlockCount)
+		//
+		// Issues SET_BLOCK_COUNT (CMD23) for MB R/W data transfers.
+		// This is only issued if MMC version >= 3.1 and the PSL
+		// supports this mode of operation.
+		//
+		TMMCCommandDesc& cmd = s.Command();
+  		TUint32 args = (TUint16)cmd.NumBlocks();
+	
+		m.SetTraps(KMMCErrStatus | KMMCErrNotSupported);
+
+		if (cmd.iFlags & KMMCCmdFlagReliableWrite)
+			{
+			// Command marked as Reliable Write 
+			// set Bit31 in CMD23 argument
+			args |= KMMCCmdReliableWrite;
+			}
+		
+		s.PushCommandStack();
+		s.FillCommandDesc( ECmdSetBlockCount, args );
+		SMF_INVOKES( ExecCommandSMST, EStBlockCountCmdIssued )
+
+	SMF_STATE(EStBlockCountCmdIssued)
+		
+		const TMMCStatus status(s.ResponseP());
+		s.PopCommandStack();
+		if (status.Error())
+			SMF_RETURN(KMMCErrStatus)
+
+		if(err & KMMCErrNotSupported)
+			{
+			// Not supported by the PSL, so use standard Stop Transmission mode
+			s.Command().iSpec.iUseStopTransmission = ETrue;
+			}
+
+		SMF_GOTOS(EStBpoint1)
+
+	SMF_STATE(EStAppCmdIssued)
+
+		const TMMCStatus status(s.ResponseP());
+		s.PopCommandStack();
+		if (status.Error())
+			SMF_RETURN(KMMCErrStatus)
+
+	SMF_BPOINT(EStBpoint1)
+
+		// NB We need to trap KMMCErrStatus errors, because if one occurs, 
+		// we still need to wait to exit PRG/RCV/DATA state 
+		m.SetTraps(KMMCErrStatus);
+
+		SMF_INVOKES( ExecCommandSMST, EStIssued )
+
+	SMF_STATE(EStIssued)
+
+		// check state of card after data transfer with CMD13.
+
+		if (s.Command().Direction() != 0)
+			{
+			SMF_GOTOS(EStWaitFinish)
+			}
+
+		SMF_GOTOS(EStRWFinish);
+
+	SMF_STATE(EStWaitFinish)
+		// Save the status and examine it after issuing CMD13...
+		// NB We don't know where in the command stack the last response is stored (e.g. there may 
+		// have bee a Deselect/Select issued), but we do know last response is stored in iLastStatus
+		TMMC::BigEndian4Bytes(s.ResponseP(), s.iLastStatus);
+
+		s.PushCommandStack();
+		s.FillCommandDesc(ECmdSendStatus, 0);
+		SMF_INVOKES(ExecCommandSMST, EStWaitFinish1)
+
+	SMF_STATE(EStWaitFinish1)
+		const TMMCStatus status(s.ResponseP());
+		s.PopCommandStack();
+
+#ifdef __WINS__
+		SMF_GOTOS(EStRWFinish);
+#else
+		const TMMCardStateEnum st1 = status.State();
+		if (st1 == ECardStatePrg || st1 == ECardStateRcv || st1 == ECardStateData)
+			{
+			SMF_INVOKES(ProgramTimerSMST, EStWaitFinish);
+			}
+		if (status.Error())
+			SMF_RETURN(KMMCErrStatus)
+#endif
+		
+		// Fall through if CURRENT_STATE is not PGM or DATA
+	SMF_STATE(EStRWFinish)
+
+		if (TMMCStatus(s.ResponseP()).Error() != 0)
+			SMF_RETURN(KMMCErrStatus);
+
+		s.iState &= ~KMMCSessStateInProgress;
+
+		// skip over recursive entry or throw error and catch in CIMLockUnlockSM()
+		return (s.Command().iCommand == ECmdLockUnlock) ? KMMCErrUpdPswd : KMMCErrBypass;
+
+	SMF_END
+	}
+
+inline TMMCErr DMMCStack::CIMEraseSM()
+/**
+ * This macro performs sector/group erase of a continuous area
+ *
+ * @return MMC error code.
+ */
+	{
+		enum states
+			{
+			EStBegin=0,
+			EStRestart,
+			EStAttached,
+			EStStartTagged,
+			EStEndTagged,
+			EStErased,
+			EStWaitFinish,
+			EStWaitFinish1,
+			EStEraseFinish,
+			EStEnd
+			};
+
+		DMMCSession& s=Session();
+
+		__KTRACE_OPT(KPBUS1,Kern::Printf(">MMC:EraseSM %x",TUint(s.iLastStatus)));
+
+	SMF_BEGIN
+
+		// Check that the card supports class 4 (Write) commands
+		const TUint ccc = s.iCardP->CSD().CCC();
+		if(!(ccc & KMMCCmdClassErase))
+			return( KMMCErrNotSupported );
+
+		s.iState |= KMMCSessStateInProgress;
+		m.SetTraps( KMMCErrInitContext );
+
+	SMF_STATE(EStRestart)
+
+		SMF_CALLMEWR(EStRestart) // Create a recursive call entry to recover from Init
+
+		s.ResetCommandStack();
+		SMF_INVOKES( AttachCardSMST, EStAttached )		// attachment is mandatory
+
+	SMF_BPOINT(EStAttached)
+
+		TMMCCommandDesc& cmd = s.Command();
+
+		if(cmd.iTotalLength == 0)
+			return( KMMCErrArgument );
+
+		switch( s.iSessionID )
+			{
+			case ECIMEraseSector:
+				TMMCEraseInfo eraseInfo;
+				s.iCardP->GetEraseInfo(eraseInfo);
+				cmd.iBlockLength = eraseInfo.iMinEraseSectorSize;
+				cmd.iCommand = ECmdTagSectorStart;
+				break;
+			case ECIMEraseGroup:
+				cmd.iBlockLength = s.iCardP->iCSD.EraseGroupSize();
+				if(cmd.iBlockLength == 0 || cmd.iTotalLength % cmd.iBlockLength != 0)
+					return KMMCErrArgument;
+				cmd.iCommand = ECmdTagEraseGroupStart;
+				break;
+			default:
+				DMMCSocket::Panic(DMMCSocket::EMMCEraseSessionID);
+			}
+
+		if(!cmd.AdjustForBlockOrByteAccess(s))
+			return KMMCErrArgument;
+
+		iConfig.RemoveMode( KMMCModeEnablePreemption );	// erase sequence must not be pre-empted
+
+		const TUint flags = cmd.iFlags;
+		s.FillCommandDesc();
+		cmd.iFlags = flags;
+		SMF_INVOKES( ExecCommandSMST, EStStartTagged )
+
+	SMF_STATE(EStStartTagged)
+
+		const TMMCCommandDesc& cmd = s.Command();
+
+		TMMCCommandEnum command;
+		TUint endAddr = cmd.iArgument;
+		if(s.iSessionID == ECIMEraseSector)
+			{
+			endAddr += cmd.IsBlockCmd() ? (cmd.iTotalLength / cmd.iBlockLength - 1) : (cmd.iTotalLength - cmd.iBlockLength);
+			command = ECmdTagSectorEnd;
+			}
+		else
+			{
+			if(cmd.IsBlockCmd())
+				{
+				endAddr += (cmd.iTotalLength - cmd.iBlockLength) >> KMMCardHighCapBlockSizeLog2;
+				}
+			else
+				{
+				endAddr += cmd.iTotalLength - cmd.iBlockLength;
+				}
+	
+			command = ECmdTagEraseGroupEnd;
+			}
+
+		s.PushCommandStack();
+		s.FillCommandDesc( command, endAddr );
+		SMF_INVOKES( ExecCommandSMST, EStEndTagged )
+
+	SMF_STATE(EStEndTagged)
+
+		// Increase the inactivity timeout as an erase operation can potentially take a long time
+		// At the moment this is a somewhat arbitrary 30 seconds. This could be calculated more accurately
+		// using TAAC,NSAC, R2W_FACTOR etc. but that seems to yield very large values (?)
+		const TInt KMaxEraseTimeoutInSeconds = 30;
+		iBody->SetInactivityTimeout(KMaxEraseTimeoutInSeconds);
+		m.SetTraps(KMMCErrAll);
+		s.FillCommandDesc( ECmdErase, 0 );
+		SMF_INVOKES( ExecCommandSMST, EStErased )
+
+	SMF_STATE(EStErased)
+		m.SetTraps( KMMCErrInitContext );
+		iBody->RestoreInactivityTimeout();
+		if (err != KMMCErrNone)
+			SMF_RETURN(err);
+
+
+	SMF_STATE(EStWaitFinish)
+
+		s.PushCommandStack();
+		s.FillCommandDesc(ECmdSendStatus, 0);
+		SMF_INVOKES(ExecCommandSMST, EStWaitFinish1)
+
+	SMF_STATE(EStWaitFinish1)
+
+		const TMMCStatus st(s.ResponseP());
+		s.PopCommandStack();
+
+#ifdef __WINS__
+		SMF_GOTOS(EStEraseFinish);
+#else
+		const TMMCardStateEnum st1 = st.State();
+		if (st1 == ECardStatePrg || st1 == ECardStateRcv || st1 == ECardStateData)
+			{
+			SMF_INVOKES(ProgramTimerSMST, EStWaitFinish);
+			}
+#endif
+		
+		// Fall through if CURRENT_STATE is not PGM or DATA
+	SMF_STATE(EStEraseFinish)
+
+		s.iState &= ~KMMCSessStateInProgress;
+		return( KMMCErrBypass );		// to skip over the recursive entry
+
+	SMF_END
+	}
+
+inline TMMCErr DMMCStack::CIMReadWriteIOSM()
+/**
+ * This macro reads/writes a stream of bytes from/to an I/O register. 
+ * This is a generalised form of FAST_IO (CMD39) MMC command.
+ *
+ * @return MMC error code.
+ */
+	{
+		enum states
+			{
+			EStBegin=0,
+			EStReadIO,
+			EStIOLoop,
+			EStEnd
+			};
+
+		DMMCSession& s=Session();
+		TMMCCommandDesc& cmd = s.Command();
+
+		__KTRACE_OPT(KPBUS1,Kern::Printf(">MMC:IOSM %x",TUint(s.iLastStatus)));
+
+	SMF_BEGIN
+		s.iState |= KMMCSessStateInProgress;
+		TUint argument = (TUint(cmd.iArgument)&0x7F) << 8; // shift reg addr into a proper position
+
+		switch( s.iSessionID )
+			{
+			case ECIMReadIO:
+				break;
+			case ECIMWriteIO:
+				argument |= 0x8000;
+				break;
+			default:
+				DMMCSocket::Panic(DMMCSocket::EMMCIOSessionID);
+			}
+
+		s.FillCommandDesc( ECmdFastIO, argument );
+		s.iBytesTransferred = ~0UL;
+
+		SMF_INVOKES( AttachCardSMST, EStIOLoop )	// attachment's mandatory
+
+	SMF_STATE(EStReadIO)
+
+		*(cmd.iDataMemoryP)++ = s.ResponseP()[3];
+		cmd.iTotalLength--;
+
+	SMF_BPOINT(EStIOLoop)
+
+		s.iBytesTransferred++;
+
+		if( cmd.iTotalLength == 0 )
+			{
+			s.iState &= ~KMMCSessStateInProgress;
+			SMF_EXIT
+			}
+
+		if( s.iSessionID == ECIMWriteIO )
+			{
+			TUint8 byte = *(cmd.iDataMemoryP)++;
+			cmd.iTotalLength--;
+			cmd.iArgument = (TUint(cmd.iArgument)&0xFF00) | byte;
+			}
+		else
+			SMF_NEXTS(EStReadIO)
+
+		iConfig.RemoveMode( KMMCModeEnableRetries );	// no retries on I/O registers!
+
+		SMF_CALL( ExecCommandSMST )
+
+	SMF_END
+	}
+
+inline TMMCErr DMMCStack::CIMLockUnlockSM()
+/**
+ * Locking and unlocking a card involves writing a data block to the card.
+ * CIMReadWriteBlocksSM() could be used directly, but, in practive, a card must
+ * sometimes be sent the data block twice.
+ *
+ * @return MMC error code.
+ */
+	{
+		enum states
+			{
+			EStBegin,
+			EStRetry,
+			EStTestR1,
+			EStEnd
+			};
+
+		DMMCSession& s=Session();
+		TMMCCommandDesc& cmd = Command();
+
+		__KTRACE_OPT(KPBUS1, Kern::Printf("mmc:clusm"));
+
+	SMF_BEGIN
+		m.SetTraps(KMMCErrStatus | KMMCErrUpdPswd);
+		cmd.iUnlockRetries = 0;					// attempt counter
+		iCMD42CmdByte = cmd.iDataMemoryP[0];
+
+	SMF_STATE(EStRetry)
+		__KTRACE_OPT(KPBUS1, Kern::Printf("mmc:clusm:%x/%x", cmd.iUnlockRetries, (iSessionP == &iAutoUnlockSession) ? KMMCMaxAutoUnlockRetries : iConfig.iUnlockRetries));
+
+	if (iCMD42CmdByte == KMMCLockUnlockErase)
+		{
+		// Section 4.6.2 of version 4.2 of the the MMC specification states that 
+		// the maximum time for a force erase operation should be 3 minutes 
+		const TInt KMaxForceEraseTimeoutInSeconds = 3 * 60;
+		iBody->SetInactivityTimeout(KMaxForceEraseTimeoutInSeconds);
+		m.SetTraps(KMMCErrAll);
+		}
+
+
+		SMF_INVOKES(CIMReadWriteBlocksSMST, EStTestR1);
+	
+	SMF_STATE(EStTestR1)
+		if (iCMD42CmdByte == KMMCLockUnlockErase)
+			{
+			m.SetTraps(KMMCErrStatus | KMMCErrUpdPswd);
+			iBody->RestoreInactivityTimeout();
+			}
+
+		if (err & KMMCErrStatus)
+			{
+			const TMMCStatus st = s.LastStatus();	// set in ExecCommandSM() / EStCommandIssued
+			TMMCCommandDesc& cmd0 = Command();
+			
+			__KTRACE_OPT(KPBUS1, Kern::Printf("mmc:clusm:EStTestR1 [err: %08x, st:%08x] : RETRY [%d]", 
+											  err, (TInt)s.LastStatus(), cmd0.iUnlockRetries));
+
+			const TInt KMaxRetries = (iSessionP == &iAutoUnlockSession) ? KMMCMaxAutoUnlockRetries : iConfig.iUnlockRetries;
+			
+			// retry if LOCK_UNLOCK_FAIL only error bit
+			if (!(	iCMD42CmdByte == 0	// LOCK_UNLOCK = 0; SET_PWD = 0
+				&&	err == KMMCErrStatus && st.Error() == KMMCStatErrLockUnlock
+				&&	(iConfig.iModes & KMMCModeEnableUnlockRetry) != 0
+				&&	++cmd0.iUnlockRetries < KMaxRetries ))
+				{
+				__KTRACE_OPT(KPBUS1, Kern::Printf("mmc:clusm:abt"));
+
+				SMF_RETURN(err);
+				}
+
+			__KTRACE_OPT(KPBUS1, Kern::Printf("mmc:clusm:retry"));
+
+#ifdef __EPOC32__
+			s.SynchBlock(KMMCBlockOnRetryTimer);
+			s.iRetryTimer.OneShot(KMMCUnlockRetryGapInMilliseconds,EFalse);
+			SMF_WAITS(EStRetry)
+#else
+			SMF_GOTOS(EStRetry);
+#endif
+			}
+		else if (err & KMMCErrUpdPswd)
+			{
+			// CMD42 executed successfully, so update 'Has Password' flag
+			if ((iCMD42CmdByte & (KMMCLockUnlockClrPwd | KMMCLockUnlockErase)) != 0)
+				{
+				s.CardP()->iFlags&=(~KMMCardHasPassword);
+				}
+			else if ((iCMD42CmdByte & KMMCLockUnlockSetPwd) != 0)
+				{
+				s.CardP()->iFlags|=KMMCardHasPassword;
+				}
+			
+			SMF_EXIT;
+			}
+		else if (err != KMMCErrNone)
+			{
+			SMF_RETURN(err);
+			}
+
+
+	SMF_END
+	}
+
+inline TMMCErr DMMCStack::CIMAutoUnlockSM()
+/**
+ * Performs auto-unlocking of the card stack
+ *
+ * @return MMC error code
+ */
+	{
+		enum states
+			{
+			EStBegin=0,
+			EStNextIndex,
+			EStInitStackAfterUnlock,
+			EStIssuedLockUnlock,
+			EStDone,
+			EStEnd
+			};
+
+		__KTRACE_OPT(KPBUS1,Kern::Printf(">MMC:CIMAutoUnlockSM"));
+
+		DMMCSession& s=Session();
+
+	SMF_BEGIN
+
+		iAutoUnlockIndex = -1;
+
+		m.SetTraps(KMMCErrAll);	// Trap (and ignore) all errors during auto-unlock
+
+	SMF_STATE(EStNextIndex)
+
+		if(err)
+			{
+			iSocket->PasswordControlEnd(&Session(), err);
+			}
+
+		// the cycle is finished when iAutoUnlockIndex == KMaxMultiMediaCardsPerStack
+		
+		if(iAutoUnlockIndex >= TInt(KMaxMMCardsPerStack))
+			{
+			SMF_GOTOS(EStInitStackAfterUnlock);
+			}
+
+		// find next available card with password in the controller store
+
+		const TMapping *mp = NULL;
+
+		TBool useIndex = EFalse;
+		for (++iAutoUnlockIndex; iAutoUnlockIndex < TInt(KMaxMMCardsPerStack); ++iAutoUnlockIndex)
+			{
+			useIndex =							// card must be present with valid mapping
+					iCardArray->CardP(iAutoUnlockIndex)->IsPresent()
+				&&	(mp = iSocket->iPasswordStore->FindMappingInStore(iCardArray->CardP(iAutoUnlockIndex)->CID())) != NULL
+				&&	mp->iState == TMapping::EStValid;
+
+			// don't increment iAutoUnlockIndex in continuation loop
+			if (useIndex)
+				break;
+			}
+
+		if (! useIndex)
+			{
+			// if no usable index, complete with no error code
+			SMF_GOTOS(EStInitStackAfterUnlock);
+			}
+
+		//
+		// We've found a locked card with a password in the password store,
+		// so attempt to unlock using the CIMLockUnlockSMST state machine.
+		//
+		// Upon completion, test the next card before performing further initialisation.
+		//
+
+		TMMCard &cd = *(iCardArray->CardP(iAutoUnlockIndex++));
+		s.SetCard(&cd);
+
+		const TInt kPWD_LEN = mp->iPWD.Length();
+		iPSLBuf[0] = 0;				// LOCK_UNLOCK = 0; unlock
+		iPSLBuf[1] = static_cast<TUint8>(kPWD_LEN);
+		TPtr8 pwd(&iPSLBuf[2], kPWD_LEN);
+		pwd.Copy(mp->iPWD);
+
+		const TInt kBlockLen = 1 + 1 + kPWD_LEN;
+
+		s.FillCommandDesc(ECmdLockUnlock);
+		s.FillCommandArgs(0, kBlockLen, iPSLBuf, kBlockLen);
+
+		SMF_INVOKES( CIMLockUnlockSMST, EStNextIndex )
+
+	SMF_STATE(EStInitStackAfterUnlock)
+
+		//
+		// We've attempted to unlock all cards (successfully or not)
+		//  - Now perform second-stage initialisation that can only be performed
+		//    on unlocked cards (such as setting bus width, high speed etc..)
+		//
+
+		m.ResetTraps();
+
+		SMF_INVOKES( InitStackAfterUnlockSMST, EStDone )
+
+	SMF_STATE(EStDone)
+
+	SMF_END
+	}
+
+inline TMMCErr DMMCStack::NoSessionSM()
+/**
+ * A null state machine function. Just returns KMMCErrNotSupported.
+ *
+ * @return KMMCErrNotSupported
+ */
+	{
+		enum states
+			{
+			EStBegin=0,
+			EStEnd
+			};
+
+	SMF_BEGIN
+
+		return( KMMCErrNotSupported );
+
+	SMF_END
+	}
+
+//
+// Static adapter functions to top-level state machines.
+//
+
+TMMCErr DMMCStack::NakedSessionSMST(TAny* aStackP)			// ECIMNakedSession
+	{ return( static_cast<DMMCStack *>(aStackP)->NakedSessionSM() ); }
+
+TMMCErr DMMCStack::CIMUpdateAcqSMST( TAny* aStackP )		// ECIMUpdateAcq
+	{ return( static_cast<DMMCStack *>(aStackP)->CIMUpdateAcqSM() ); }
+
+TMMCErr DMMCStack::CIMInitStackSMST( TAny* aStackP )		// ECIMInitStack
+	{ return( static_cast<DMMCStack *>(aStackP)->CIMInitStackSM() ); }
+
+TMMCErr DMMCStack::CIMCheckStackSMST( TAny* aStackP )		// ECIMCheckStack
+	{ return( static_cast<DMMCStack *>(aStackP)->CIMCheckStackSM() ); }
+
+TMMCErr DMMCStack::CIMSetupCardSMST(TAny* aStackP)			// ECIMSetupCard
+	{ return( static_cast<DMMCStack *>(aStackP)->CIMSetupCardSM() ); }
+
+EXPORT_C TMMCErr DMMCStack::CIMReadWriteBlocksSMST(TAny* aStackP)	// ECIMReadBlock, ECIMWriteBlock, ECIMReadMBlock, ECIMWriteMBlock
+	{ return( static_cast<DMMCStack *>(aStackP)->CIMReadWriteBlocksSM() ); }
+
+TMMCErr DMMCStack::CIMEraseSMST(TAny* aStackP)				// ECIMEraseSector, ECIMEraseGroup
+	{ return( static_cast<DMMCStack *>(aStackP)->CIMEraseSM() ); }
+
+TMMCErr DMMCStack::CIMReadWriteIOSMST(TAny* aStackP)		// ECIMReadIO, ECIMWriteIO
+	{ return( static_cast<DMMCStack *>(aStackP)->CIMReadWriteIOSM() ); }
+
+TMMCErr DMMCStack::CIMLockUnlockSMST(TAny* aStackP)			// ECIMLockUnlock
+	{ return( static_cast<DMMCStack *>(aStackP)->CIMLockUnlockSM() ); }
+
+TMMCErr DMMCStack::NoSessionSMST(TAny* aStackP)				// ECIMLockStack
+	{ return( static_cast<DMMCStack *>(aStackP)->NoSessionSM() ); }
+
+TMMCErr DMMCStack::InitStackAfterUnlockSMST( TAny* aStackP )		
+	{ return( static_cast<DMMCStack *>(aStackP)->InitStackAfterUnlockSM() ); }
+
+TMMCErr DMMCStack::AcquireStackSMST( TAny* aStackP )
+	{ return( static_cast<DMMCStack *>(aStackP)->AcquireStackSM() ); }
+
+TMMCErr DMMCStack::CheckStackSMST( TAny* aStackP )
+	{ return( static_cast<DMMCStack *>(aStackP)->CheckStackSM() ); }
+
+TMMCErr DMMCStack::ModifyCardCapabilitySMST( TAny* aStackP )
+	{ return( static_cast<DMMCStack *>(aStackP)->ModifyCardCapabilitySM() ); }
+
+TMMCErr DMMCStack::AttachCardSMST( TAny* aStackP )
+	{ return( static_cast<DMMCStack *>(aStackP)->AttachCardSM() ); }
+
+TMMCErr DMMCStack::ExecCommandSMST( TAny* aStackP )
+	{ return( static_cast<DMMCStack *>(aStackP)->ExecCommandSM() ); }
+
+TMMCErr DMMCStack::IssueCommandCheckResponseSMST(TAny* aStackP)
+	{ return( static_cast<DMMCStack *>(aStackP)->IssueCommandCheckResponseSM() ); }
+
+TMMCErr DMMCStack::PollGapTimerSMST( TAny* aStackP )
+	{ return( static_cast<DMMCStack *>(aStackP)->PollGapTimerSM() ); }
+
+TMMCErr DMMCStack::RetryGapTimerSMST( TAny* aStackP )
+	{ return( static_cast<DMMCStack *>(aStackP)->RetryGapTimerSM() ); }
+
+TMMCErr DMMCStack::ProgramTimerSMST(TAny *aStackP)
+	{ return static_cast<DMMCStack *>(aStackP)->ProgramTimerSM(); }
+
+TMMCErr DMMCStack::GoIdleSMST( TAny* aStackP )
+	{ return( static_cast<DMMCStack *>(aStackP)->GoIdleSM() ); }
+
+TMMCErr DMMCStack::CheckLockStatusSMST( TAny* aStackP )
+	{ return( static_cast<DMMCStack *>(aStackP)->CheckLockStatusSM() ); }
+
+TMMCErr DMMCStack::CIMAutoUnlockSMST( TAny* aStackP )
+	{ return( static_cast<DMMCStack *>(aStackP)->CIMAutoUnlockSM() ); }
+
+TMMCErr DMMCStack::ExecSleepCommandSMST( TAny* aStackP )
+	{ return( static_cast<DMMCStack *>(aStackP)->ExecSleepCommandSM() ); }
+
+TMMCErr DMMCStack::ExecAwakeCommandSMST( TAny* aStackP )
+	{ return( static_cast<DMMCStack *>(aStackP)->ExecAwakeCommandSM() ); }
+//
+// Static interfaces to ASSP layer SM functions
+//
+TMMCErr DMMCStack::DoPowerUpSMST( TAny* aStackP )
+	{ return( static_cast<DMMCStack *>(aStackP)->DoPowerUpSM() ); }
+
+TMMCErr DMMCStack::InitClockOnSMST( TAny* aStackP )
+	{ return( static_cast<DMMCStack *>(aStackP)->InitClockOnSM() ); }
+
+EXPORT_C TMMCErr DMMCStack::IssueMMCCommandSMST( TAny* aStackP )
+	{ return( static_cast<DMMCStack *>(aStackP)->IssueMMCCommandSM() ); }
+
+TMMCErr DMMCStack::DetermineBusWidthAndClockSMST( TAny* aStackP )
+	{ return( static_cast<DMMCStack *>(aStackP)->DetermineBusWidthAndClockSM() ); }
+
+TMMCErr DMMCStack::ConfigureHighSpeedSMST( TAny* aStackP )
+	{ return( static_cast<DMMCStack *>(aStackP)->ConfigureHighSpeedSM() ); }
+
+TMMCErr DMMCStack::ExecSwitchCommandST( TAny* aStackP )
+	{ return( static_cast<DMMCStack *>(aStackP)->ExecSwitchCommand() ); }
+
+TMMCErr DMMCStack::SwitchToLowVoltageSMST( TAny* aStackP )
+	{ return( static_cast<DMMCStack *>(aStackP)->SwitchToLowVoltageSM() ); }
+
+TMMCErr DMMCStack::LowVoltagePowerupTimerSMST(TAny *aStackP)
+	{ return static_cast<DMMCStack *>(aStackP)->LowVoltagePowerupTimerSM(); }
+
+TMMCErr DMMCStack::ExecBusTestSMST( TAny* aStackP )
+	{ return( static_cast<DMMCStack *>(aStackP)->ExecBusTestSM() ); }
+
+TMMCErr DMMCStack::DoWakeUpSMST( TAny* aStackP )
+	{ 	
+	MDoWakeUp* dowakeup = NULL;
+	static_cast<DMMCStack *>(aStackP)->GetInterface(KInterfaceDoWakeUpSM, (MInterface*&) dowakeup);
+	if (dowakeup)
+		{
+		return dowakeup->DoWakeUpSM();
+		}
+	else
+		{
+		// Interface not supported at PSL Level
+		return KMMCErrNotSupported;
+		}
+	}
+
+
+EXPORT_C DMMCSession* DMMCStack::AllocSession(const TMMCCallBack& aCallBack) const
+/**
+* Factory function to create DMMCSession derived object.  Non-generic MMC
+* controllers can override this to generate more specific objects.
+* @param aCallBack Callback function to notify the client that a session has completed
+* @return A pointer to the new session
+*/
+	{
+	return new DMMCSession(aCallBack);
+	}
+
+EXPORT_C void DMMCStack::Dummy1() {}
+
+/**
+ * Calls the PSL-implemented function SetBusWidth() if the bus width has changed
+ *
+ */
+void DMMCStack::DoSetBusWidth(TUint32 aBusWidth)
+	{
+	if (iBody->iCurrentSelectedBusWidth != aBusWidth)
+		{
+		iBody->iCurrentSelectedBusWidth = aBusWidth;
+		SetBusWidth(aBusWidth);
+		}
+	}
+
+/**
+ * Sets iConfig.iBusConfig.iBusClock - which the PSL SHOULD use to set the clock before every command.
+ *
+ * Some PSLs, however, may only change the clock when SetBusConfigDefaults() is called, 
+ * so if the clock has changed, SetBusConfigDefaults() is called
+ *
+ * @param aClock The requested clock frequency in Kilohertz
+ */
+void DMMCStack::DoSetClock(TUint32 aClock)
+	{
+	iConfig.iBusConfig.iBusClock = aClock;
+
+	if (iPoweredUp&&(iBody->iCurrentSelectedClock != aClock))
+		{
+		iBody->iCurrentSelectedClock = aClock;
+		SetBusConfigDefaults(iConfig.iBusConfig, aClock);
+		}
+	}
+
+
+TUint DMMCStack::MaxTranSpeedInKilohertz(const TMMCard& aCard) const
+	{
+	TUint32 highSpeedClock = aCard.HighSpeedClock();
+	return highSpeedClock ? highSpeedClock : aCard.MaxTranSpeedInKilohertz();
+	}
+
+
+
+EXPORT_C void DMMCStack::SetBusWidth(TUint32 /*aBusWidth*/)
+	{
+	}
+
+EXPORT_C void DMMCStack::MachineInfo(TDes8& /*aMachineInfo*/)
+	{
+	}
+
+TBusWidth DMMCStack::BusWidthEncoding(TInt aBusWidth) const
+/**
+ * Returns the bus width as a TBusWidth given a card's bus width 
+ * expressed as an integer (1,4 or 8)
+ * @return the bus width encoded as a TBusWidth
+ */
+	{
+	TBusWidth busWidth = EBusWidth1;
+
+	switch(aBusWidth)
+		{
+		case 8: 
+			busWidth =  EBusWidth8; 
+			break;
+		case 4: 
+			busWidth =  EBusWidth4; 
+			break;
+		case 1: 
+		case 0: 
+			busWidth =  EBusWidth1; 
+			break;
+		default:
+			DMMCSocket::Panic(DMMCSocket::EMMCBadBusWidth);
+
+		}
+	return busWidth;
+	}
+
+/**
+ * class DMMCSocket 
+ */
+EXPORT_C DMMCSocket::DMMCSocket(TInt aSocketNumber, TMMCPasswordStore* aPasswordStore)
+/**
+ * Constructs a DMMCSocket class
+ * @param aSocketNumber the socket ID
+ * @param aPasswordStore pointer to the password store
+ */
+	:DPBusSocket(aSocketNumber),
+	iPasswordStore(aPasswordStore)
+	{
+	}
+
+TInt DMMCSocket::TotalSupportedCards()
+/**
+ * Returns the total number of MMC slots supported by the socket.
+ * @return The number of MMC slots supported by the socket
+ */
+	{
+	return iMachineInfo.iTotalSockets;
+	}
+
+
+// -------- Password store management --------
+
+//
+// The persistent file is a contiguous sequence of entries.
+// An entry format is [CID@16 | PWD_LEN@4 | PWD@PWD_LEN].
+// CID and PWD_LEN are both stored in big endian format.
+//
+
+TInt DMMCSocket::PrepareStore(TInt aBus, TInt aFunc, TLocalDrivePasswordData &aData)
+/**
+ * Called from media driver before CMD42 session engaged, in kernel server context
+ * so that mappings can be allocated or deallocated.
+ * 
+ * Using zero-length passwords for MMC operations is disallowed by this function.
+ * Locking with and clearing a null password is failed with KErrAccessDenied.
+ * If the drive is already mounted, then TBusLocalDrive::Unlock() will fail with
+ * KErrAlreadyExists.  Otherwise, this function will fail with KErrLocked, which
+ * is translated to KErrAccessDenied in Unlock(), in the same way as unlocking
+ * a locked card with the wrong password
+ * 
+ * @param aBus The card to be unlocked.
+ * @param aFunc The operation to perform (EPasswordLock, EPasswordUnlock, EPasswordClear).
+ * @param aData TLocalDrivePasswordData reference containing the password
+ * @return KErrAccessDenied An attempt to lock or clear was made with a NULL password.
+ * @return KErrLocked An an attempt to unlock was made with a NULL password.
+ * @return KErrNone on success
+ */
+	{
+	TInt r = 0;
+
+	TMMCard *card=iStack->CardP(aBus);
+	__ASSERT_ALWAYS(card, Panic(EMMCSessionNoPswdCard));
+	const TCID &cid = card->CID();
+
+	switch (aFunc)
+		{
+	case DLocalDrive::EPasswordLock:
+		{
+		TMediaPassword newPwd = *aData.iNewPasswd;
+
+		if (newPwd.Length() == 0)
+			r = KErrAccessDenied;
+		else
+			r = PasswordControlStart(cid, aData.iStorePasswd ? &newPwd : NULL);
+		}
+		break;
+
+	case DLocalDrive::EPasswordUnlock:
+		{
+		TMediaPassword curPwd = *aData.iOldPasswd;
+
+		if (curPwd.Length() == 0)
+			r = KErrLocked;
+		else
+			r = PasswordControlStart(cid, aData.iStorePasswd ? &curPwd : NULL);
+		}
+		break;
+
+	case DLocalDrive::EPasswordClear:
+		{
+		TMediaPassword curPwd = *aData.iOldPasswd;
+
+		if (curPwd.Length() == 0)
+			r = KErrAccessDenied;
+		else
+			r = PasswordControlStart(cid, aData.iStorePasswd ? &curPwd : NULL);
+		}
+		break;
+
+	default:
+		Panic(EMMCSessionPswdCmd);
+		break;
+		}
+
+	return r;
+	}
+
+
+TInt DMMCSocket::PasswordControlStart(const TCID &aCID, const TMediaPassword *aPWD)
+/**
+ * Remove any non-validated mappings from the store, and allocate a binding for
+ * the card's CID if necessary.
+ * 
+ * s = source (current) password stored; t = target (new) password should be stored
+ * f = failure
+ * 
+ * t is equivalent to iMPTgt.Length() > 0, which is used by PasswordControlEnd().
+ * 
+ * The target password is not stored in the store at this point, but in the stack.
+ * This leaves any existing mapping which can be used for recovery if the operation
+ * fails.  This means the user does not have to re-enter the right password after
+ * trying to unlock a card with the wrong password.
+ * 
+ * See PasswordControlEnd() for recovery policy.
+ */
+	{
+	TInt r = KErrNone;							// error code
+
+	TBool changed = EFalse;						// compress store if changed
+
+	TBuf8<KMMCCIDLength> cid;					// convert to TBuf8<> for comparison
+	cid.SetLength(KMMCCIDLength);
+	aCID.Copy(&cid[0]);
+
+	TBool s = EFalse;							// source password (current mapping)
+	TBool t = aPWD != NULL;						// target pasword (new value for mapping)
+
+	// remove any bindings which were not validated.  This is all non EStValid
+	// bindings - the previous operation could have failed before CMD42 was sent,
+	// in which case its state would be EStPending, not EStInvalid.
+
+	// an inefficiency exists where an invalid binding for the target CID exists.
+	// This could be reused instead of being deallocated and reallocated.  This
+	// situation would occur if the user inserted a card whose password was not
+	// known to the machine, unlocked it with the wrong password and tried again.
+	// The case is rare and the cost is run-time speed, which is not noticeable,
+	// The run-time memory usage is equivalent, so it is probably not worth the
+	// extra rom bytes and logic.
+
+	for (TInt i = 0; i < iPasswordStore->iStore->Count(); )
+		{
+		if ((*iPasswordStore->iStore)[i].iState != TMapping::EStValid)
+			{
+			iPasswordStore->iStore->Remove(i);	// i becomes index for next item
+			changed = ETrue;
+			}
+		else
+			{
+			if ((*iPasswordStore->iStore)[i].iCID == cid)
+				s = ETrue;
+			++i;
+			}
+		}
+
+	if (! t)
+		iStack->iMPTgt.Zero();
+	else
+		{
+		iStack->iMPTgt = *aPWD;
+
+		if (!s)
+			{
+			TMediaPassword mp;					// empty, to indicate !s
+			if ((r = iPasswordStore->InsertMapping(aCID, mp, TMapping::EStPending)) != KErrNone)
+				return r;
+
+			changed = ETrue;
+			}
+		}
+
+	if (changed)
+		iPasswordStore->iStore->Compress();
+
+	return r;
+	}
+
+
+
+void DMMCSocket::PasswordControlEnd(DMMCSession *aSessP, TInt aResult)
+/**
+ * called by DMMCStack::SchedCompletionPass() after CMD42 has completed to
+ * update internal store.  This function does not run in ks context and so
+ * can only invalidate bindings for later removal in PasswordControlStart().
+ * 
+ * s = source (current) password stored; t = target (new) password should be stored
+ * f = failure
+ * 
+ * If the operation fails, then a recovery policy is used so the user does
+ * not lose the good current binding and have to re-enter the password.
+ * '
+ * f = 0					f = 1
+ * 				T						T
+ * 			0		1				0		1
+ * 	S	0	N		V		S	0	N		I
+ * 		1	W		V			1	R		R
+ * 
+ * 	N	nothing		V	validate	W	wipe
+ * 	I	invalidate	R	restore
+ * '
+ * See PasswordControlStart() for details of how store set up.
+ */
+	{
+	// autounlock is a special case because the PasswordControlStart() will
+	// not have been called (the CID is not known in ks context.)  The mapping
+	// for this specific card is removed on failure, because it is the current
+	// mapping that is definitely wrong.
+
+	TBuf8<KMMCCIDLength> cid;					// convert to TBuf8<> for comparison
+	cid.SetLength(KMMCCIDLength);
+	aSessP->CardP()->CID().Copy(&cid[0]);
+
+	if (aSessP == &iStack->iAutoUnlockSession)
+		{
+		TBool changed = EFalse;						// compress store if changed
+
+		for (TInt j = 0; j < iPasswordStore->iStore->Count(); )
+			{
+			TMapping &mp = (*iPasswordStore->iStore)[j];
+			if (mp.iCID == cid)
+				{
+				mp.iState = (aResult == KErrNone ? TMapping::EStValid : TMapping::EStInvalid);
+				if(mp.iState == TMapping::EStInvalid)
+					{
+					iPasswordStore->iStore->Remove(j);
+					changed = ETrue;
+					}
+				else
+					{
+					j++;
+					}
+				}
+			else
+				{
+				j++;
+				}
+			}
+
+		if (changed)
+			iPasswordStore->iStore->Compress();
+		}
+	else
+		{
+		const TMediaPassword &mpTgt = iStack->iMPTgt;
+		TBool s = EFalse;						// default value in case no mapping
+		TBool t = mpTgt.Length() > 0;
+		TBool f = (aResult != KErrNone);
+
+		TMapping mp, *pmp;						// get mapping to mutate
+		mp.iCID = cid;
+		TInt psn = iPasswordStore->iStore->Find(mp, iPasswordStore->iIdentityRelation);
+		if (psn == KErrNotFound)
+			{
+			return;
+			}
+		else
+			{
+			pmp = &(*iPasswordStore->iStore)[psn];
+			s = pmp->iPWD.Length() > 0;
+			}
+
+		if (f)
+			{
+			if (s)		// s & ~f
+				pmp->iState = TMapping::EStValid;		// restore
+			else
+				{
+				if (t)	// ~s & t & f 
+					pmp->iState = TMapping::EStInvalid;	// invalidate
+				}
+			}
+		else
+			{
+			if (t)		// t & ~f
+				{
+				pmp->iState = TMapping::EStValid;		// validate
+				pmp->iPWD = mpTgt;
+				}
+			else
+				{
+				if (s)	// s & ~t & ~f
+					pmp->iState = TMapping::EStInvalid;	// wipe
+				}
+			}	// else (f)
+		}	// else if (aSessP == &iStack->iAutoUnlockSession)
+	}
+
+
+TMMCPasswordStore::TMMCPasswordStore()
+/**
+ * Contructor
+ */
+	: iIdentityRelation(TMMCPasswordStore::CompareCID)
+	{
+	}
+
+TInt TMMCPasswordStore::Init()
+/**
+ * Initialises the password store and allocates resources.
+ * @return KErrNone if successful, standard error code otherwise.
+ */
+	{
+	// We don't have a destructor yet as this object lasts forever
+	iStore = new RArray<TMapping>(4, _FOFF(TMapping, iCID));
+	if(!iStore)
+		return KErrNoMemory;
+	return KErrNone;
+	}
+
+EXPORT_C TBool TMMCPasswordStore::IsMappingIncorrect(const TCID& aCID, const TMediaPassword& aPWD)
+/**
+ * Returns true if the password is definitely incorrect, i.e. if a valid entry with a
+ * different password exists.  Returns false if correct (because the mapping matches,)
+ * or if cannot tell (because no valid mapping.)
+ */
+	{
+	TMapping* pmp = FindMappingInStore(aCID);
+	return (pmp != 0 && pmp->iState == TMapping::EStValid && pmp->iPWD.Compare(aPWD) != 0);
+	}
+
+TMapping *TMMCPasswordStore::FindMappingInStore(const TCID &aCID)
+/**
+ * return pointer to aCID mapping in store or NULL if not found
+ */
+	{
+	TMapping *pmp = NULL;
+	TMapping mp;								// 8 + 16 + 8 + 16 + 4 bytes
+	mp.iCID.SetLength(KMMCCIDLength);
+	aCID.Copy(&mp.iCID[0]);
+
+	TInt psn=iStore->Find(mp, iIdentityRelation);
+	if(psn!=KErrNotFound)
+		{
+		pmp = &(*iStore)[psn];
+		}
+	return pmp;
+	}
+
+TInt TMMCPasswordStore::InsertMapping(const TCID &aCID, const TMediaPassword &aPWD, TMapping::TState aState)
+/**
+ * Ensures that a mapping from aCID to aPWD exists in the store.  If an
+ * existing entry does not exist to mutate, then insert a new one.  This
+ * may cause an allocation, depending on the granularity and count.
+ * 
+ * If the CID is already bound to something in the store, then this operation
+ * is a binary search, otherwise it may involve kernel heap allocation.
+ */
+	{
+	TInt r = KErrNone;
+	TMapping mpN;
+	mpN.iCID.SetLength(KMMCCIDLength);
+	aCID.Copy(&mpN.iCID[0]);					// copies from aCID into buffer.
+
+	TInt psn = iStore->Find(mpN, iIdentityRelation);
+	if(psn == KErrNotFound)
+		{
+		mpN.iPWD.Copy(aPWD);
+		mpN.iState = aState;
+		r=iStore->Insert(mpN, iStore->Count());
+		}
+	else
+		{
+		TMapping &mpE = (*iStore)[psn];
+		mpE.iPWD.Copy(aPWD);
+		mpE.iState = aState;
+		r = KErrNone;
+		}
+
+	return r;
+	}
+
+TInt TMMCPasswordStore::PasswordStoreLengthInBytes()
+/**
+ * virtual from DPeriphBusController, kern exec
+ * return number of bytes needed for persistent file representation
+ * of the password store
+ */
+	{
+	TInt sz = 0;
+
+	for (TInt i = 0; i < iStore->Count(); ++i)
+		{
+		const TMapping &mp = (*iStore)[i];
+		if (mp.iState == TMapping::EStValid)
+			sz += KMMCCIDLength + sizeof(TInt32) + mp.iPWD.Length();
+		}
+
+	return sz;
+	}
+
+TBool TMMCPasswordStore::ReadPasswordData(TDes8 &aBuf)
+/**
+ * virtual from DPeriphBusController, kern exec
+ * fills descriptor with persistent representation of password store
+ * data.  aBuf is resized to contain exactly the password data from
+ * the store.  If its maximum length is not enough then KErrOverflow
+ * is returned and aBuf is not mutated.
+ */
+	{
+	TInt r=KErrNone;										// error code
+
+	if (PasswordStoreLengthInBytes() > aBuf.MaxLength())
+		r = KErrOverflow;
+	else
+		{
+		aBuf.Zero();
+		for (TInt i = 0; i < iStore->Count(); ++i)
+			{
+			const TMapping &mp = (*iStore)[i];
+
+			if (mp.iState == TMapping::EStValid)
+				{
+				aBuf.Append(mp.iCID);
+
+				TUint8 lenBuf[sizeof(TInt32)];		// length, big-endian
+				TMMC::BigEndian4Bytes(lenBuf, TInt32(mp.iPWD.Length()));
+				aBuf.Append(&lenBuf[0], sizeof(TInt32));
+
+				aBuf.Append(mp.iPWD);
+				}
+			}
+
+		r = KErrNone;
+		}
+
+	return r;
+	}
+
+
+TInt TMMCPasswordStore::WritePasswordData(TDesC8 &aBuf)
+/**
+ * virtual from DPeriphBusController, kern server
+ * replace current store with data from persistent representation in aBuf.
+ */
+	{
+	// should only be called at boot up, but remove chance of duplicate entries
+	iStore->Reset();
+
+	TInt iBIdx;									// buffer index
+
+	// check buffer integrity
+	
+	TBool corrupt = EFalse;						// abort flag
+	for (iBIdx = 0; iBIdx < aBuf.Length(); )
+		{
+		// enough raw data for CID, PWD_LEN and 1 byte of PWD
+		corrupt = TUint(aBuf.Length() - iBIdx) < KMMCCIDLength + sizeof(TUint32) + 1;
+		if (corrupt)
+			break;
+		
+		// PWD_LEN is valid and enough raw data left for PWD
+		iBIdx += KMMCCIDLength;
+		const TInt32 pwd_len(TMMC::BigEndian32(&aBuf[iBIdx]));
+		corrupt = !(
+				(pwd_len <= TInt32(KMaxMediaPassword))
+			&&	aBuf.Length() - iBIdx >= TInt(sizeof(TUint32)) + pwd_len );
+		if (corrupt)
+			break;
+		
+		// skip over PWD_LEN and PWD to next entry
+		iBIdx += sizeof(TInt32) + pwd_len;
+		}
+
+	if (corrupt)
+		return KErrCorrupt;
+
+	// Build the store from the entries in the buffer.
+	TInt r = KErrNone;							// error code
+	for (iBIdx = 0; r == KErrNone && iBIdx < aBuf.Length(); )
+		{
+		TPtrC8 pCID(&aBuf[iBIdx], KMMCCIDLength);	// CID
+		const TCID cid(pCID.Ptr());
+
+		const TInt32 pwd_len(TMMC::BigEndian32(&aBuf[iBIdx + KMMCCIDLength]));
+		TMediaPassword pwd;
+		pwd.Copy(&aBuf[iBIdx + KMMCCIDLength + sizeof(TInt32)], pwd_len);
+
+		iBIdx += KMMCCIDLength + sizeof(TInt32) + pwd_len;
+		r = InsertMapping(cid, pwd, TMapping::EStValid);
+		}
+
+	// it may be acceptable to use a partially created store, providing the
+	// sections that do exist are valid.  Alternatively, the operation should
+	// atomic from the startup thread's point of view.
+
+	if (r != KErrNone)
+		iStore->Reset();
+
+	return r;
+	}
+
+TInt TMMCPasswordStore::CompareCID(const TMapping& aLeft, const TMapping& aRight)
+/**
+ * CID Comparason Functions for RArray::Find
+ */
+	{
+	return(aLeft.iCID == aRight.iCID);
+	}
+
+void DMMCSocket::InitiatePowerUpSequence()
+/**
+ * Initiates a power up sequence on the stack
+ */
+	{
+	iStack->PowerUpStack();
+	}
+
+TBool DMMCSocket::CardIsPresent()
+/**
+ * Indicates the presence of a card.
+ * @return ETrue if a card is present, EFalse otherwise
+ */
+	{
+	return(iStack->HasCardsPresent());
+	}
+
+void DMMCSocket::AdjustPartialRead(const TMMCard* aCard, TUint32 aStart, TUint32 aEnd, TUint32* aPhysStart, TUint32* aPhysEnd) const
+/**
+ * Calculates the minimum range that must be read off a card, an optimisation that takes advantage
+ * of the partial read feature found on some cards.  It takes the logical range that the media driver
+ * wants to read from the card, and increases it to take into account factors such as FIFO width and
+ * minimum DMA transfer size.
+ * @param aCard A pointer to the MMC Card
+ * @param aStart The required start position
+ * @param aEnd The required end position
+ * @param aPhysStart The adjusted start position
+ * @param aPhysEnd The adjusted end position
+ */
+	{
+	iStack->AdjustPartialRead(aCard, aStart, aEnd, aPhysStart, aPhysEnd);
+	}
+
+void DMMCSocket::GetBufferInfo(TUint8** aMDBuf, TInt* aMDBufLen)
+/**
+ * Returns the details of the buffer allocated by the socket for data transfer operations.  The buffer
+ * is allocated and configured at the variant layer to allow , for example, contiguous pages to be
+ * allocated for DMA transfers.
+ * @param aMDBuf A pointer to the allocated buffer
+ * @param aMDBufLen The length of the allocated buffer
+ */
+	{
+	iStack->GetBufferInfo(aMDBuf, aMDBufLen);
+	}
+
+void DMMCSocket::Reset1()
+/**
+ * Resets the socket by powering down the stack.
+ * If there are operations in progress (inCritical), this call will be deferred
+ * until the operation is complete.  In the case of an emergency power down,
+ * this will occur immediately.
+ */
+	{
+	if (iState == EPBusCardAbsent)
+	    {
+	    // Reset is result of card eject!
+	    iStack->iStackState |= KMMCStackStateCardRemoved;
+	    }
+	
+	
+	iStack->PowerDownStack();
+	}
+
+void DMMCSocket::Reset2()
+/**
+ * Resets the socket in response to a PSU fault or media change.
+ * Called after Reset1, gives the opportunity to free upp allocated resources
+ */
+	{
+	// No need to do anything here, as the only thing to do is power down the
+	// stack, which is performed in ::Reset1
+	}
+
+TInt DMMCSocket::Init()
+/**
+ * Allocates resources and initialises the MMC socket and associated stack object.
+ * @return KErrNotReady if no stack has been allocated, standard error code otherwise
+ */
+	{
+	__KTRACE_OPT(KPBUS1,Kern::Printf(">MMC:Init"));
+	
+	GetMachineInfo();
+
+	// We need to make sure the stack is initialised,
+	// as DPBusSocket::Init() will initiate a power up sequence
+	if(iStack == NULL)
+		return KErrNotReady;
+
+	TInt r = iStack->Init();
+	if (r!=KErrNone)
+		return r;
+
+	r = DPBusSocket::Init();
+	if (r!=KErrNone)
+		return r;
+	
+	return KErrNone;
+	}
+
+void DMMCSocket::GetMachineInfo()
+/**
+ * Gets the platform specific configuration information.
+ * @see TMMCMachineInfo
+ */
+	{
+	// Get machine info from the stack
+	iStack->MachineInfo(iMachineInfo);
+
+	__KTRACE_OPT(KPBUS1, Kern::Printf(">GetMI : iTotalSockets %u", iMachineInfo.iTotalSockets));
+	__KTRACE_OPT(KPBUS1, Kern::Printf(">GetMI : iTotalMediaChanges %u", iMachineInfo.iTotalMediaChanges));
+	__KTRACE_OPT(KPBUS1, Kern::Printf(">GetMI : iTotalPrimarySupplies %u", iMachineInfo.iTotalPrimarySupplies));
+	__KTRACE_OPT(KPBUS1, Kern::Printf(">GetMI : iSPIMode %u", iMachineInfo.iSPIMode));
+	__KTRACE_OPT(KPBUS1, Kern::Printf(">GetMI : iBaseBusNumber %u", iMachineInfo.iBaseBusNumber));
+
+	}
+
+
+// MMC specific functions
+
+EXPORT_C void DMMCSocket::Panic(TMMCPanic aPanic)
+/**
+ * Panic the MMC Controller
+ * @param aPanic The panic code
+ */
+	{
+
+	_LIT(KPncNm,"PBUS-MMC");
+	Kern::PanicCurrentThread(KPncNm,aPanic);
+	}
+
+EXPORT_C DMMCPsu::DMMCPsu(TInt aPsuNum, TInt aMediaChangedNum)
+/**
+ * Constructor for a DMMCPsu object
+ * @param aPsuNum The power supply number
+ * @param aMediaChangedNum The associated media change number
+ */
+	: DPBusPsuBase(aPsuNum, aMediaChangedNum)
+	{
+	
+	iVoltageSetting=0x00ffc000; // Default voltage range - 2.6V to 3.6V (OCR reg. format).
+	}
+
+EXPORT_C TInt DMMCPsu::DoCreate()
+/**
+ * Create a DMMCPsu object.
+ * This should be overridden at the variant layer to allow interrupts and
+ * other variant-specific parameters to be initialised.  The default 
+ * implementation does nothing.
+ * @return Standard Symbian OS error code.
+ */
+	{
+	return KErrNone;
+	}
+
+
+void DMMCPsu::SleepCheck(TAny* aPtr)
+/**
+ * Checks if media can be placed in Sleep state 
+ * and therefore if VccQ supply can be turned off.
+ * 
+ * @Param aPtr reference to DMMCPsu Object to be acted upon.
+ */
+	{	
+	DMMCPsu& self = *static_cast<DMMCPsu*>(aPtr);
+	
+	if (
+		(self.iNotLockedTimeout&&!self.IsLocked()&&++self.iNotLockedCount>self.iNotLockedTimeout) ||
+		(self.iInactivityTimeout&&++self.iInactivityCount>self.iInactivityTimeout)
+	   )
+		{
+		DMMCSocket* socket = static_cast<DMMCSocket*>(self.iSocket);
+		socket->iStack->QSleepStack();
+		}
+	}
+
+EXPORT_C DMMCMediaChange::DMMCMediaChange(TInt aMediaChangeNum)
+/**
+ * Constructor for a DMMCMediaChange object
+ * @param aMediaChangeNum The media change number
+ */
+	: DMediaChangeBase(aMediaChangeNum)
+	{}
+
+EXPORT_C TInt DMMCMediaChange::Create()
+/**
+ * Create a DMMCMediaChange object.
+ * This should be overridden at the variant layer to allow interrupts and
+ * other variant-specific parameters to be initialised.  The base class implementation
+ * should be called prior to any variant-specific initialisation.
+ * @return Standard Symbian OS error code.
+ */
+	{
+	return DMediaChangeBase::Create();
+	}
+