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