--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/kernel/eka/drivers/pbus/mmc/stack.cpp Thu Dec 17 09:24:54 2009 +0200
@@ -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();
+ }
+