--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/kernel/eka/drivers/medmmc/medmmc.cpp Mon Oct 19 15:55:17 2009 +0100
@@ -0,0 +1,3648 @@
+// Copyright (c) 1999-2009 Nokia Corporation and/or its subsidiary(-ies).
+// All rights reserved.
+// This component and the accompanying materials are made available
+// under the terms of the License "Eclipse Public License v1.0"
+// which accompanies this distribution, and is available
+// at the URL "http://www.eclipse.org/legal/epl-v10.html".
+//
+// Initial Contributors:
+// Nokia Corporation - initial contribution.
+//
+// Contributors:
+//
+// Description:
+// Media driver for MultiMediaCard Flash device
+//
+//
+
+#include "mmc.h"
+#include "pbusmedia.h"
+#include <drivers/emmcptn.h>
+
+#if defined(__DEMAND_PAGING__)
+ // If in debug mode, enable paging stats and their retrieval using DLocalDrive::EControlIO
+ #if defined( _DEBUG)
+ #define __TEST_PAGING_MEDIA_DRIVER__
+ #endif
+ #include "mmcdp.h"
+#endif
+
+#ifndef BTRACE_PAGING_MEDIA
+ #undef BTraceContext8
+ #define BTraceContext8(aCategory,aSubCategory,a1,a2)
+#endif // BTRACE_PAGING_MEDIA
+
+// Enable this macro to debug cache:
+// NB The greater the number of blocks, the slower this is...
+//#define _DEBUG_CACHE
+#ifdef _DEBUG_CACHE
+#define __ASSERT_CACHE(c,p) (void)((c)||(p,0))
+#else
+#define __ASSERT_CACHE(c,p)
+#endif
+
+
+GLREF_C TInt GetMediaDefaultPartitionInfo(TMBRPartitionEntry& aPartitionEntry, TUint16& aReservedSectors, const TMMCard* aCardP);
+GLREF_C TBool MBRMandatory(const TMMCard* aCardP);
+GLREF_C TBool CreateMBRAfterFormat(const TMMCard* aCardP);
+GLREF_C TInt BlockSize(const TMMCard* aCardP);
+GLREF_C TInt EraseBlockSize(const TMMCard* aCardP);
+GLREF_C TInt GetCardFormatInfo(const TMMCard* aCardP, TLDFormatInfo& aFormatInfo);
+
+const TInt KStackNumber = 0;
+
+const TInt KDiskSectorSize=512;
+const TInt KDiskSectorShift=9;
+
+const TInt KIdleCurrentInMilliAmps = 1;
+
+const TInt KMBRFirstPartitionEntry=0x1BE;
+
+template <class T>
+inline T UMin(T aLeft,T aRight)
+ {return(aLeft<aRight ? aLeft : aRight);}
+
+
+class DPhysicalDeviceMediaMmcFlash : public DPhysicalDevice
+ {
+public:
+ DPhysicalDeviceMediaMmcFlash();
+
+ virtual TInt Install();
+ virtual void GetCaps(TDes8& aDes) const;
+ virtual TInt Create(DBase*& aChannel, TInt aMediaId, const TDesC8* aInfo, const TVersion& aVer);
+ virtual TInt Validate(TInt aDeviceType, const TDesC8* aInfo, const TVersion& aVer);
+ virtual TInt Info(TInt aFunction, TAny* a1);
+ };
+
+
+// these should be static const members of DMmcMediaDriverFlash, but VC doesn't support this
+const TInt64 KInvalidBlock = -1;
+const TInt KNoCacheBlock = -1;
+
+class DMmcMediaDriverFlash : public DMediaDriver
+ {
+public:
+ DMmcMediaDriverFlash(TInt aMediaId);
+ ~DMmcMediaDriverFlash();
+ // ...from DMediaDriver
+ virtual void Close();
+ // replacing pure virtual
+ virtual void Disconnect(DLocalDrive* aLocalDrive, TThreadMessage*);
+ virtual TInt Request(TLocDrvRequest& aRequest);
+ virtual TInt PartitionInfo(TPartitionInfo& anInfo);
+ virtual void NotifyPowerDown();
+ virtual void NotifyEmergencyPowerDown();
+ // For creation by DPhysicalDeviceMediaMmcFlash
+ TInt DoCreate(TInt aMediaId);
+
+private:
+ enum TPanic
+ {
+ EDRInUse = 0x0000, EDRStart, EDRNotPositive, EDREnd,
+ ELRRequest = 0x0010, ELRStart, ELRNotPositive, ELREnd, ELRCached,
+ EDWInUse = 0x0020, EDWStart, EDWNotPositive, EDWEnd,
+ EDFInUse = 0x0030, EDFStart, EDFNotPositive, EDFEnd, ENotMmcSocket,
+ ELWRequest = 0x0040, ELWStart, ELWFmtStAlign, ELWNotPositive, ELWEnd, ELWFmtEndAlign,
+ ELWLength, ELFStart, ELFEnd, ELFNotPositive,
+ ERPIInUse = 0x0050,
+ EPCInUse = 0x0060, EPCFunc,
+ ESECBQueued = 0x0070,
+ EDSEDRequest = 0x0080, EDSEDNotErrComplete,
+ ECRReqIdle = 0x0090, ECRRequest,
+ ERRBStAlign = 0x00a0, ERRBStPos, ERRBNotPositive, ERRBEndAlign, ERRBEndPos,
+ ERRBOverflow, ERRBCchInv, ERRBExist,
+ ERWBStPos = 0x00b0, ERWBNotPositive, ERWBEndPos, ERWBOverflow, ERWBCchInv,
+ EMBStPos = 0x00c0, EMBStAlign, EMBNotPositive, EMBEndPos, EMBEndAlign,
+ EMBOverflow, EMBCchInvPre, EMBCchInvPost,
+ EBGAStPos = 0x00d0, EBGAStAlign, EBGANotPositive, EBGAEndPos, EBGAEndAlign,
+ EBGAOverflow, EBGACchInv,
+ EICMNegative = 0x00e0, EICMOverflow, ECMIOverflow,
+ EGCBAlign = 0x00f0, EGCBPos, EGCBCchInv,
+
+ ECFSessPtrNull = 0x0100, // Code Fault - session pointer NULL
+
+ EDBNotEven = 0x0110, // Not and even number of blocks in the buffer cache
+ EDBCBQueued = 0x0111, // The data transfer callback is already queued
+ EDBLength = 0x0112, // The length of data to transfer in data transfer callback is not positive
+ EDBLengthTooBig = 0x0113, // The length of data to transfer in data transfer callback is too big
+ EDBOffsetTooBig = 0x0114, // The Offset into the user data buffer is too big
+ EDBCacheInvalid = 0x0115, // The cache is invalid at the end of data transfer
+ EDBNotOptimal = 0x0116, // Due to Cache size DB functionality will never be utilised
+ ENoDBSupport = 0x0120, // DMA request arrived but PSL does not support double buffering
+ ENotDMAAligned = 0x0121,
+ };
+ static void Panic(TPanic aPnc);
+
+ enum TMediaRequest
+ {
+ EMReqRead = 0,
+ EMReqWrite = 1,
+ EMReqFormat = 2,
+ EMReqPtnInfo,
+ EMReqPswdCtrl,
+ EMReqForceErase,
+ EMReqUpdatePtnInfo,
+ EMReqWritePasswordData,
+ EMReqIdle,
+ EMReqEMMCPtnInfo,
+ };
+ enum TMediaReqType {EMReqTypeNormalRd,EMReqTypeNormalWr,EMReqTypeUnlockPswd,EMReqTypeChangePswd};
+
+ enum {KWtRBMFst = 0x00000001, // iWtRBM - Read First Block only
+ KWtRBMLst = 0x00000002, // iWtRBM - Read Last Block only
+ KWtMinFst = 0x00000004, // iWtRBM - Write First Block only
+ KWtMinLst = 0x00000008, // iWtRBM - Write Last Block only
+ KIPCSetup = 0x00000010, // iRdROB - IPC Setup Next Iteration
+ KIPCWrite = 0x00000020}; // iRdROB - IPC Write Next Iteration
+
+private:
+ // MMC device specific stuff
+ TInt DoRead();
+ TInt DoWrite();
+ TInt DoFormat();
+ TInt Caps(TLocDrv& aDrive, TLocalDriveCapsV6& aInfo);
+
+ inline DMMCStack& Stack() const;
+ inline TInt CardNum() const;
+ inline TMediaRequest CurrentRequest() const;
+
+ TInt LaunchRead(TInt64 aStart, TUint32 aLength);
+ TInt LaunchDBRead();
+ TInt LaunchPhysRead(TInt64 aStart, TUint32 aLength);
+
+ TInt LaunchWrite(TInt64 aStart, TUint32 aLength, TMediaRequest aMedReq);
+ TInt LaunchFormat(TInt64 aStart, TUint32 aLength);
+
+ TInt LaunchRPIUnlock(TLocalDrivePasswordData& aData);
+ TInt LaunchRPIRead();
+ TInt LaunchRPIErase();
+ TInt DecodePartitionInfo();
+ TInt WritePartitionInfo();
+ TInt GetDefaultPartitionInfo(TMBRPartitionEntry& aPartitionEntry);
+ TInt CreateDefaultPartition();
+
+
+#if defined __TEST_PAGING_MEDIA_DRIVER__
+ TInt HandleControlIORequest();
+#endif
+
+ static void SetPartitionEntry(TPartitionEntry* aEntry, TUint aFirstSector, TUint aNumSectors);
+
+ TInt CheckDevice(TMediaReqType aReqType);
+
+ static void SessionEndCallBack(TAny* aMediaDriver);
+ static void SessionEndDfc(TAny* aMediaDriver);
+ void DoSessionEndDfc();
+
+ static void DataTransferCallBack(TAny* aMediaDriver);
+ static void DataTransferCallBackDfc(TAny* aMediaDriver);
+
+ void DoReadDataTransferCallBack();
+ void DoWriteDataTransferCallBack();
+ void DoPhysReadDataTransferCallBack();
+ void DoPhysWriteDataTransferCallBack();
+
+ TInt AdjustPhysicalFragment(TPhysAddr &physAddr, TInt &physLength);
+ TInt PrepareFirstPhysicalFragment(TPhysAddr &aPhysAddr, TInt &aPhysLength, TUint32 aLength);
+ void PrepareNextPhysicalFragment();
+
+ TInt EngageAndSetReadRequest(TMediaRequest aRequest);
+ TInt EngageAndSetWriteRequest(TMediaRequest aRequest);
+ TInt EngageAndSetRequest(TMediaRequest aRequest, TInt aCurrent);
+ void CompleteRequest(TInt aReason);
+
+ TInt ReadDataUntilCacheExhausted(TBool* aAllDone);
+ TInt WriteDataToUser(TUint8* aBufPtr);
+ TInt ReadDataFromUser(TDes8& aDes, TInt aOffset);
+ TUint8* ReserveReadBlocks(TInt64 aStart, TInt64 aEnd, TUint32* aLength);
+ TUint8* ReserveWriteBlocks(TInt64 aMedStart, TInt64 aMedEnd, TUint* aRBM);
+ void MarkBlocks(TInt64 aStart, TInt64 aEnd, TInt aStartIndex);
+ void BuildGammaArray(TInt64 aStart, TInt64 aEnd);
+ void InvalidateCache();
+ void InvalidateCache(TInt64 aStart, TInt64 aEnd);
+ TUint8* IdxToCchMem(TInt aIdx) const;
+ TInt CchMemToIdx(TUint8* aMemP) const;
+
+ TInt DoPasswordOp();
+ void PasswordControl(TInt aFunc, TLocalDrivePasswordData& aData);
+ void Reset();
+ TInt AllocateSession();
+
+#ifdef _DEBUG_CACHE
+ TBool CacheInvariant();
+ TUint8* GetCachedBlock(TInt64 aAddr);
+#endif
+private:
+ DMMCStack* iStack; // controller objects
+ TMMCard* iCard;
+ DMMCSession* iSession;
+ DMMCSocket* iSocket;
+
+ TInt iCardNumber;
+
+ TUint iBlkLenLog2; // cached CSD data
+ TUint32 iBlkLen;
+ TInt64 iBlkMsk;
+ TBool iReadBlPartial;
+ TUint32 iPrWtGpLen; // preferred write group size in bytes,
+ TInt64 iPrWtGpMsk;
+
+ TInt iReadCurrentInMilliAmps; // power management
+ TInt iWriteCurrentInMilliAmps;
+
+ TUint8* iMinorBuf; // MBR, CMD42, partial read
+ TUint8* iCacheBuf; // cached buffer
+ TUint32 iMaxBufSize;
+ TInt iBlocksInBuffer;
+ TInt64* iCachedBlocks;
+ TInt* iGamma; // B lookup, ReserveReadBlocks()
+ TUint8* iIntBuf; // start of current buffer region
+ TInt iLstUsdCchEnt; // index of last used cache entry
+
+ TLocDrvRequest* iCurrentReq; // Current Request
+ TMediaRequest iMedReq;
+
+ TInt64 iReqStart; // user-requested start region
+ TInt64 iReqCur; // Currently requested start region
+ TInt64 iReqEnd; // user-requested end region
+ TInt64 iPhysStart; // physical region for one operation
+ TInt64 iPhysEnd; // physical end point for one operation
+ TInt64 iDbEnd; // Double buffer end point for one operation
+
+ TUint64 iEraseUnitMsk;
+
+ TUint iWtRBM; // Write - Read Before Modify Flags
+ TUint iRdROB; // Read - Read Odd Blocks Flags
+
+ TInt iFragOfset;
+ TUint32 iIPCLen;
+ TUint32 iNxtIPCLen;
+ TUint32 iBufOfset;
+
+ TUint iHiddenSectors; // bootup / password
+
+ TMMCCallBack iSessionEndCallBack;
+ TDfc iSessionEndDfc;
+
+ TPartitionInfo* iPartitionInfo;
+ TMMCMediaTypeEnum iMediaType;
+ TMMCEraseInfo iEraseInfo;
+ TBool iMbrMissing;
+ TInt iMediaId;
+
+ DMMCStack::TDemandPagingInfo iDemandPagingInfo;
+
+#if defined(__TEST_PAGING_MEDIA_DRIVER__)
+ SMmcStats iMmcStats;
+#endif // __TEST_PAGING_MEDIA_DRIVER__
+
+ TMMCCallBack iDataTransferCallBack; // Callback registered with the MMC stack to perform double-buffering
+ TDfc iDataTransferCallBackDfc; // ...and the associated DFC queue.
+
+ TBool iSecondBuffer; // Specified the currently active buffer
+ TBool iDoLastRMW; // ETrue if the last double-buffer transfer requires RMW modification
+ TBool iDoDoubleBuffer; // ETrue if double-buffering is currently active
+ TBool iDoPhysicalAddress; // ETrue if Physical Addressing is currently active
+ TBool iCreateMbr;
+ TBool iReadToEndOfCard; // {Read Only} ETrue if Reading to end of Card
+
+ TBool iInternalSlot;
+
+ DEMMCPartitionInfo* iMmcPartitionInfo; // Responsible for decoding partitions for embedded devices
+ };
+
+// ======== DPhysicalDeviceMediaMmcFlash ========
+
+
+DPhysicalDeviceMediaMmcFlash::DPhysicalDeviceMediaMmcFlash()
+ {
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("=mmd:ctr"));
+
+ iUnitsMask = 0x01;
+ iVersion = TVersion(KMediaDriverInterfaceMajorVersion,KMediaDriverInterfaceMinorVersion,KMediaDriverInterfaceBuildVersion);
+ }
+
+
+TInt DPhysicalDeviceMediaMmcFlash::Install()
+ {
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("=mmd:ins"));
+
+ _LIT(KDrvNm, "Media.MmcF");
+ return SetName(&KDrvNm);
+ }
+
+
+void DPhysicalDeviceMediaMmcFlash::GetCaps(TDes8& /* aDes */) const
+ {
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("=mmd:cap"));
+ }
+
+
+TInt DPhysicalDeviceMediaMmcFlash::Info(TInt aFunction, TAny* /*a1*/)
+//
+// Return the priority of this media driver
+//
+ {
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("=mmd:info"));
+ if (aFunction==EPriority)
+ return KMediaDriverPriorityNormal;
+ // Don't close media driver when peripheral bus powers down. This avoids the need for Caps() to power up the stack.
+ if (aFunction==EMediaDriverPersistent)
+ return KErrNone;
+ return KErrNotSupported;
+ }
+
+TInt DPhysicalDeviceMediaMmcFlash::Validate(TInt aDeviceType, const TDesC8* /*aInfo*/, const TVersion& aVer)
+ {
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("=mmd:validate aDeviceType %d", aDeviceType));
+ if (!Kern::QueryVersionSupported(iVersion,aVer))
+ return KErrNotSupported;
+ if (aDeviceType!=MEDIA_DEVICE_MMC)
+ return KErrNotSupported;
+ return KErrNone;
+ }
+
+TInt DPhysicalDeviceMediaMmcFlash::Create(DBase*& aChannel, TInt aMediaId, const TDesC8* /*aInfo*/, const TVersion& aVer)
+//
+// Create an MMC Card media driver.
+//
+ {
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf(">mmd:crt"));
+
+ if (!Kern::QueryVersionSupported(iVersion,aVer))
+ return KErrNotSupported;
+
+ DMmcMediaDriverFlash* pD = new DMmcMediaDriverFlash(aMediaId);
+ aChannel=pD;
+
+ TInt r=KErrNoMemory;
+ if (pD)
+ r=pD->DoCreate(aMediaId);
+ if (r==KErrNone)
+ pD->OpenMediaDriverComplete(KErrNone);
+
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("<mmd:mdf"));
+ return r;
+ }
+
+
+// ======== DMmcMediaDriverFlash ========
+
+
+void DMmcMediaDriverFlash::Panic(TPanic aPanic)
+ {
+ _LIT(KPncNm, "MEDMMC");
+ Kern::PanicCurrentThread(KPncNm, aPanic);
+ }
+
+
+// ---- accessor functions -----
+
+inline DMMCStack& DMmcMediaDriverFlash::Stack() const
+ { return *static_cast<DMMCStack*>(iStack); }
+
+
+inline TInt DMmcMediaDriverFlash::CardNum() const
+ { return iCardNumber; }
+
+
+inline DMmcMediaDriverFlash::TMediaRequest DMmcMediaDriverFlash::CurrentRequest() const
+ { return iMedReq; }
+
+
+// Helper
+template <class T>
+inline T* KernAlloc(const TUint32 n)
+ { return static_cast<T*>(Kern::Alloc(n * sizeof(T))); }
+
+// ---- ctor, open, close, dtor ----
+
+#pragma warning( disable : 4355 ) // this used in initializer list
+DMmcMediaDriverFlash::DMmcMediaDriverFlash(TInt aMediaId)
+ :DMediaDriver(aMediaId),
+ iMedReq(EMReqIdle),
+ iSessionEndCallBack(DMmcMediaDriverFlash::SessionEndCallBack, this),
+ iSessionEndDfc(DMmcMediaDriverFlash::SessionEndDfc, this, 1),
+ iMediaId(iPrimaryMedia->iNextMediaId),
+ iDataTransferCallBack(DMmcMediaDriverFlash::DataTransferCallBack, this),
+ iDataTransferCallBackDfc(DMmcMediaDriverFlash::DataTransferCallBackDfc, this, 1)
+ {
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("=mmd:mmd"));
+ // NB aMedia Id = the media ID of the primary media, iMediaId = the media ID of this media
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("DMmcMediaDriverFlash(), iMediaId %d, aMediaId %d\n", iMediaId, aMediaId));
+ }
+
+#pragma warning( default : 4355 )
+TInt DMmcMediaDriverFlash::DoCreate(TInt /*aMediaId*/)
+ {
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf(">mmd:opn"));
+
+ iSocket = ((DMMCSocket*)((DPBusPrimaryMedia*)iPrimaryMedia)->iSocket);
+ if(iSocket == NULL)
+ return(KErrNoMemory);
+
+ iCardNumber = ((DPBusPrimaryMedia*)iPrimaryMedia)->iSlotNumber;
+
+ iStack = iSocket->Stack(KStackNumber);
+ iCard = iStack->CardP(CardNum());
+
+ TMMCMachineInfo machineInfo;
+ Stack().MachineInfo(machineInfo);
+ TInt slotFlag = iCardNumber == 0 ? TMMCMachineInfo::ESlot1Internal : TMMCMachineInfo::ESlot2Internal;
+ iInternalSlot = machineInfo.iFlags & slotFlag;
+
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("DMmcMediaDriverFlash::DoCreate() slotNumber %d iInternalSlot %d", iCardNumber, iInternalSlot));
+
+
+ iSessionEndDfc.SetDfcQ(&iSocket->iDfcQ);
+ iDataTransferCallBackDfc.SetDfcQ(&iSocket->iDfcQ);
+
+ // check right type of card
+ if ((iMediaType=iCard->MediaType())==EMultiMediaNotSupported)
+ return(KErrNotReady);
+
+ // get card characteristics
+ const TCSD& csd = iCard->CSD();
+ iBlkLenLog2 = iCard->MaxReadBlLen();
+ iBlkLen = 1 << iBlkLenLog2;
+ iBlkMsk = (TInt64)(iBlkLen - 1);
+
+ SetTotalSizeInBytes(iCard->DeviceSize64());
+
+ //
+ // High capcity cards (block addressable, MMCV4.2, SD2.0) do not support partial reads
+ // ...some cards incorrectly report that they do, so ensure that we don't
+ //
+ iReadBlPartial = iCard->IsHighCapacity() ? EFalse : csd.ReadBlPartial();
+
+ // allocate and initialize session object
+ TInt r = AllocateSession();
+ if (r!= KErrNone)
+ return r;
+
+ // get buffer memory from EPBUS
+ TUint8* buf;
+ TInt bufLen;
+ TInt minorBufLen;
+ Stack().BufferInfo(buf, bufLen, minorBufLen);
+
+ iMinorBuf = buf;
+
+ // cache buffer can use rest of blocks in buffer. Does not have to be power of 2.
+ iCacheBuf = iMinorBuf + minorBufLen;
+
+ // We need to devide up the buffer space between the media drivers.
+ // The number of buffer sub-areas = number of physical card slots * number of media
+ bufLen-= minorBufLen;
+ DPBusPrimaryMedia* primaryMedia = (DPBusPrimaryMedia*) iPrimaryMedia;
+ TInt physicalCardSlots = iStack->iMaxCardsInStack;
+ TInt numMedia = primaryMedia->iLastMediaId - primaryMedia->iMediaId + 1;
+ TInt totalNumMedia = numMedia * physicalCardSlots;
+
+ TInt mediaIndex = iMediaId - primaryMedia->iMediaId;
+ TInt bufIndex = (iCardNumber * numMedia) + mediaIndex;
+
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("physicalCardSlots %d, iCardNumber %d\n", physicalCardSlots, iCardNumber));
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("iMediaId %d numMedia %d, mediaIndex %d, totalNumMedia %d, bufIndex %d\n",
+ iMediaId, numMedia, mediaIndex, totalNumMedia, bufIndex));
+
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("bufLen1 %08X iCacheBuf1 %08X", bufLen, iCacheBuf));
+ bufLen/= totalNumMedia;
+ iCacheBuf+= bufIndex * bufLen;
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("bufLen2 %08X iCacheBuf2 %08X", bufLen, iCacheBuf));
+
+ iBlocksInBuffer = bufLen >> iBlkLenLog2; // may lose partial block
+ if(iSocket->SupportsDoubleBuffering())
+ {
+ // Ensure that there's always an even number of buffered blocks when double-buffering
+ iBlocksInBuffer &= ~1;
+ __ASSERT_DEBUG(iBlocksInBuffer >= 2, Panic(EDBNotEven));
+#if defined(_DEBUG)
+ /**
+ * If Double-Buffering is enabled then the cache should not be greater than the maximum addressable range of the DMA controller,
+ * otherwise Double buffering will never be utilised because all transfers will fit into the cache.
+ */
+ const TUint32 maxDbBlocks = iSocket->MaxDataTransferLength() >> iBlkLenLog2;
+ __ASSERT_DEBUG(iBlocksInBuffer <= (TInt)maxDbBlocks, Panic(EDBNotOptimal));
+#endif
+ }
+
+ iMaxBufSize = iBlocksInBuffer << iBlkLenLog2;
+
+ iPrWtGpLen = iCard->PreferredWriteGroupLength();
+
+ // check the preferred write group length is a power of two
+ if(iPrWtGpLen == 0 || (iPrWtGpLen & (~iPrWtGpLen + 1)) != iPrWtGpLen)
+ return(KErrNotReady);
+
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("iMaxBufSize %d iPrWtGpLen %d\n", iMaxBufSize, iPrWtGpLen));
+ // ensure the preferred write group length is as large as possible
+ // so we can write to more than one write group at once
+ while (iPrWtGpLen < (TUint32) iMaxBufSize)
+ iPrWtGpLen <<= 1;
+
+ // ensure preferred write group length is no greater than internal cache buffer
+ while (iPrWtGpLen > (TUint32) iMaxBufSize)
+ iPrWtGpLen >>= 1;
+ iPrWtGpMsk = TInt64(iPrWtGpLen - 1);
+
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("iPrWtGpLen #2 %d\n", iPrWtGpLen));
+
+ // allocate index for cached blocks
+ iCachedBlocks = KernAlloc<TInt64>(iBlocksInBuffer);
+ if (iCachedBlocks == 0)
+ return(KErrNoMemory);
+
+ InvalidateCache();
+ iLstUsdCchEnt = iBlocksInBuffer - 1; // use entry 0 first
+
+ // allocate read lookup index
+ iGamma = KernAlloc<TInt>(iBlocksInBuffer);
+ if (iGamma == 0)
+ return(KErrNoMemory);
+
+ // get current requirements
+ iReadCurrentInMilliAmps = csd.MaxReadCurrentInMilliamps();
+ iWriteCurrentInMilliAmps = csd.MaxWriteCurrentInMilliamps();
+
+ // get preferred erase information for format operations
+ const TInt err = iCard->GetEraseInfo(iEraseInfo);
+ if(err != KErrNone)
+ return(err);
+
+ iEraseUnitMsk = TInt64(iEraseInfo.iPreferredEraseUnitSize) - 1;
+
+ // Retrieve the demand paging info from the PSL of the stack
+ Stack().DemandPagingInfo(iDemandPagingInfo);
+
+ // if a password has been supplied then it is sent when the partition info is read
+
+ //
+ // If this is an internal slot, then use the eMMC partition function
+ //
+ if(iInternalSlot)
+ {
+ iMmcPartitionInfo = CreateEmmcPartitionInfo();
+ if(iMmcPartitionInfo == NULL)
+ return KErrNoMemory;
+
+ TInt err = iMmcPartitionInfo->Initialise(this);
+ if(err != KErrNone)
+ return err;
+ }
+
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("<mmd:opn"));
+
+ return(KErrNone);
+ }
+
+void DMmcMediaDriverFlash::Close()
+//
+// Close the media driver - also called on media change
+//
+ {
+ __KTRACE_OPT(KPBUSDRV,Kern::Printf("=mmd:cls"));
+
+ EndInCritical();
+ iSessionEndDfc.Cancel();
+ iDataTransferCallBackDfc.Cancel();
+ CompleteRequest(KErrNotReady);
+ DMediaDriver::Close();
+ }
+
+
+DMmcMediaDriverFlash::~DMmcMediaDriverFlash()
+ {
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf(">mmd:dtr"));
+
+ iSessionEndDfc.Cancel();
+ iDataTransferCallBackDfc.Cancel();
+
+ delete iSession;
+ Kern::Free(iCachedBlocks);
+ Kern::Free(iGamma);
+
+ delete iMmcPartitionInfo;
+
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("<mmd:dtr"));
+ }
+
+
+TInt DMmcMediaDriverFlash::AllocateSession()
+ {
+ // already allocated ?
+ if (iSession != NULL)
+ return KErrNone;
+
+ iSession = iStack->AllocSession(iSessionEndCallBack);
+ if (iSession == NULL)
+ return KErrNoMemory;
+
+ iSession->SetStack(iStack);
+ iSession->SetCard(iCard);
+ iSession->SetDataTransferCallback(iDataTransferCallBack);
+
+ return KErrNone;
+ }
+
+// ---- media access ----
+
+TInt DMmcMediaDriverFlash::DoRead()
+//
+// set up iReqStart, iReqEnd and iReqCur and launch first read. Subsequent reads
+// will be launched from the callback DFC.
+//
+ {
+ TInt r = CheckDevice(EMReqTypeNormalRd);
+ if (r != KErrNone) return r;
+
+ const TInt64 pos(iCurrentReq->Pos());
+ TUint32 length(I64LOW(iCurrentReq->Length()));
+
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf(">mmd:dr:0x%lx,0x%x", pos, length));
+ __ASSERT_DEBUG(CurrentRequest() == EMReqIdle, Panic(EDRInUse));
+ __ASSERT_DEBUG(pos < TotalSizeInBytes(), Panic(EDRStart));
+ __ASSERT_DEBUG(iCurrentReq->Length() >= 0, Panic(EDRNotPositive));
+ __ASSERT_DEBUG(TotalSizeInBytes() >= pos + length, Panic(EDREnd));
+
+ if(length > 0)
+ {
+ iReqCur = iReqStart = pos;
+ iReqEnd = iReqStart + length;
+
+ TBool allDone(EFalse);
+ if ( ((r = ReadDataUntilCacheExhausted(&allDone)) == KErrNone) && !allDone)
+ {
+ iMedReq = EMReqRead;
+ iPhysStart = iReqCur & ~iBlkMsk;
+ __ASSERT_DEBUG(I64HIGH(iPhysStart >> KMMCardHighCapBlockSizeLog2) == 0, Panic(ELRStart));
+
+ iReadToEndOfCard = ( iReqEnd >= TotalSizeInBytes() );
+ // Re-calculate length as some data may have been recovered from cache
+ length = I64LOW(iReqEnd - iReqCur);
+
+ if (iCurrentReq->IsPhysicalAddress() && !iReadToEndOfCard && (length >= iBlkLen) )
+ r = LaunchPhysRead(iReqCur, length);
+ else if ( (iReqEnd - iPhysStart) > iMaxBufSize && iSocket->SupportsDoubleBuffering() && !iReadToEndOfCard)
+ r = LaunchDBRead();
+ else
+ r = LaunchRead(iReqCur, length);
+
+ if (r == KErrNone) return r;
+ }
+ }
+ else
+ {
+#if defined(__DEMAND_PAGING__) && !defined(__WINS__)
+ if (DMediaPagingDevice::PageInRequest(*iCurrentReq))
+ {
+ r = iCurrentReq->WriteToPageHandler(NULL, 0, 0);
+ }
+ else
+#endif // __DEMAND_PAGING__
+ {
+ TPtrC8 zeroDes(NULL, 0);
+ r = iCurrentReq->WriteRemote(&zeroDes,0);
+ }
+ }
+
+ // error occurred or read all from cache so complete immediately
+ if(r == KErrNone)
+ r = KErrCompletion;
+
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("<mmd:dr:%d", r));
+
+ return r;
+ }
+
+
+TInt DMmcMediaDriverFlash::LaunchRead(TInt64 aStart, TUint32 aLength)
+//
+// starts reads from DoRead() and the session end DFC. This function does not maintain the
+// iReq* instance variables. It sets iPhysStart and iPhysEnd to the region that was actually
+// read into iIntBuf. iIntBuf can be set to a cached entry or to the minor buffer. It is
+// assumed that before this function is called that ReadDataUntilCacheExhausted() has been used.
+//
+ {
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf(">mmd:lr:0x%lx,0x%x", aStart, aLength));
+ __ASSERT_DEBUG(TotalSizeInBytes() > aStart, Panic(ELRStart));
+ __ASSERT_DEBUG(aLength > 0, Panic(ELRNotPositive));
+ __ASSERT_DEBUG(TotalSizeInBytes() >= aStart + aLength, Panic(ELREnd));
+ __ASSERT_CACHE(GetCachedBlock(aStart & ~iBlkMsk) == 0, Panic(ELRCached));
+ __ASSERT_DEBUG(iSession != NULL, Panic(ECFSessPtrNull));
+
+ iDoPhysicalAddress = EFalse;
+ iDoDoubleBuffer = EFalse;
+ iSecondBuffer = EFalse;
+
+ //
+ // if this read goes up to the end of the card then use only
+ // single sector reads to avoid CMD12 timing problems
+ //
+ const TUint32 bufSize(iReadToEndOfCard ? iBlkLen : iMaxBufSize);
+
+ iPhysEnd = (UMin(iReqEnd, iPhysStart + bufSize) + iBlkMsk) & ~iBlkMsk;
+
+ TUint32 physLen(I64LOW(iPhysEnd - iPhysStart));
+
+ __ASSERT_DEBUG(I64HIGH(iPhysEnd - iPhysStart) == 0, Panic(ELREnd));
+
+ // partial reads must be within a single physical block
+ if (iReadBlPartial && physLen == iBlkLen && aLength <= (iBlkLen >> 1))
+ {
+ //
+ // Note : Partial reads are not supported for large block devices
+ // (MMCV4.2 and SD2.0 high capacity cards)
+ //
+ __ASSERT_DEBUG(I64HIGH(aStart) == 0, Panic(ELRStart));
+ __ASSERT_DEBUG(I64HIGH(aStart + aLength) == 0, Panic(ELREnd));
+
+ iIntBuf = iMinorBuf;
+ Stack().AdjustPartialRead(iCard, I64LOW(aStart), I64LOW(aStart + aLength), (TUint32*)&iPhysStart, (TUint32*)&iPhysEnd);
+ iSession->SetupCIMReadBlock(I64LOW(iPhysStart >> KMMCardHighCapBlockSizeLog2), iIntBuf, physLen >> KMMCardHighCapBlockSizeLog2);
+ }
+ else
+ {
+ iIntBuf = ReserveReadBlocks(iPhysStart, iPhysEnd, &physLen);
+
+ // EPBUSM automatically uses CMD17 instead of CMD18 for single block reads
+ iSession->SetupCIMReadBlock(I64LOW(iPhysStart >> KMMCardHighCapBlockSizeLog2), iIntBuf, physLen >> KMMCardHighCapBlockSizeLog2);
+
+ // Update Physical end point as less may have been required due to additional blocks found in cache during ReserveReadBlocks
+ iPhysEnd = iPhysStart + physLen;
+ }
+
+ TInt r = EngageAndSetReadRequest(EMReqRead);
+
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("<mmd:lr:%d", r));
+ return r;
+ }
+
+TInt DMmcMediaDriverFlash::LaunchDBRead()
+//
+// starts reads from DoRead() and the session end DFC. This function does not maintain the
+// iReq* instance variables. It sets iPhysStart and iPhysEnd to the region that was actually
+// read into iIntBuf. iIntBuf can be set to a cached entry or to the minor buffer. It is
+// assumed that before this function is called that ReadDataUntilCacheExhausted() has been used.
+//
+ {
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf(">mmd:ldbr:0x%lx,0x%x", iReqCur, I64LOW(iReqEnd - iReqCur)));
+ __ASSERT_DEBUG(TotalSizeInBytes() > iReqCur, Panic(ELRStart));
+ __ASSERT_DEBUG(I64LOW(iReqEnd - iReqCur) > 0, Panic(ELRNotPositive));
+ __ASSERT_DEBUG(TotalSizeInBytes() >= iReqEnd, Panic(ELREnd));
+ __ASSERT_CACHE(GetCachedBlock(iReqCur & ~iBlkMsk) == 0, Panic(ELRCached));
+ __ASSERT_DEBUG(iSession != NULL, Panic(ECFSessPtrNull));
+
+ iDoDoubleBuffer = ETrue;
+ iDoPhysicalAddress = EFalse;
+
+ iDbEnd = iReqEnd;
+ const TUint32 maxDbLength = iSocket->MaxDataTransferLength();
+
+ if(maxDbLength)
+ {
+ //
+ // If the PSL specifies a limit on the maximum size of a data transfer, then truncate the request...
+ //
+ iDbEnd = UMin(iDbEnd, iPhysStart + maxDbLength);
+ }
+
+ iDbEnd = (iDbEnd + iBlkMsk) & ~iBlkMsk;
+
+ const TUint32 doubleBufferSize = iMaxBufSize >> 1;
+ iPhysEnd = (iReqCur + doubleBufferSize) & ~iBlkMsk; // The end of the first double-buffered transfer
+
+ //
+ // If we're double-buffering, then the entire cache will be re-used
+ // continuously. Rather than continually reserve blocks during each
+ // transfer we calculate the blocks that will be present after all
+ // transfers have completed.
+ // @see DoSessionEndDfc()
+ //
+ InvalidateCache();
+ iIntBuf = iCacheBuf;
+
+ iSession->SetupCIMReadBlock(I64LOW(iPhysStart >> KMMCardHighCapBlockSizeLog2), iIntBuf, I64LOW(iDbEnd - iPhysStart) >> KMMCardHighCapBlockSizeLog2);
+
+ iSession->EnableDoubleBuffering(doubleBufferSize >> KDiskSectorShift);
+
+ // ...and switch to the 'second' buffer, which will be populated in the
+ // data transfer callback in parallel with hardware transfer of the first.
+ iSecondBuffer = ETrue;
+
+ TInt r = EngageAndSetReadRequest(EMReqRead);
+
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("<mmd:ldbr:%d", r));
+
+ return r;
+ }
+
+
+TInt DMmcMediaDriverFlash::LaunchPhysRead(TInt64 aStart, TUint32 aLength)
+//
+// This function does not maintain the iReq* instance variables.
+// It is assumed that before this function is called that
+// ReadDataUntilCacheExhausted() has been used.
+//
+ {
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf(">mmd:physr:0x%lx,0x%x", aStart, aLength));
+ __ASSERT_DEBUG(TotalSizeInBytes() > aStart, Panic(ELRStart));
+ __ASSERT_DEBUG(aLength > 0, Panic(ELRNotPositive));
+ __ASSERT_DEBUG(TotalSizeInBytes() >= aStart + aLength, Panic(ELREnd));
+ __ASSERT_CACHE(GetCachedBlock(aStart & ~iBlkMsk) == 0, Panic(ELRCached));
+ __ASSERT_DEBUG(iSession != NULL, Panic(ECFSessPtrNull));
+
+ TInt r(KErrNone);
+
+ iDoPhysicalAddress = ETrue;
+ iDoDoubleBuffer = EFalse;
+
+ // Local Media Subsystem ensures DMA Addressable range not exceeded.
+ // @see LocDrv::RegisterDmaDevice()
+ iPhysEnd = (iReqEnd + iBlkMsk) & ~iBlkMsk;
+
+ iRdROB = 0;
+ iFragOfset = iIPCLen = iNxtIPCLen = iBufOfset = 0;
+
+ // Determine if start/end are block aligned
+ // physical memory can only read the exact amount, not more!
+ const TBool firstPartial( (aStart & iBlkMsk) != 0);
+
+ TPhysAddr physAddr(0);
+ TInt physLength(0);
+ TUint32 physLen(I64LOW(iPhysEnd - iPhysStart));
+
+ if (firstPartial)
+ {
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf(">mmd:physr:FirstPartial"));
+ // first index does not start on block boundary
+ // iIntBuf linear address is used for IPC within DoReadDataTransferCallBack()
+ iRdROB |= KIPCWrite;
+
+ iIntBuf = ReserveReadBlocks(iPhysStart, iPhysStart+iBlkLen,(TUint32*)&physLength);
+#if !defined(__WINS__)
+ physAddr = Epoc::LinearToPhysical((TLinAddr)iIntBuf);
+#else
+ physAddr = (TPhysAddr)iIntBuf;
+#endif
+ // Set SecondBuffer flag to indicate IPC cannot be done on next callback
+ iSecondBuffer = ETrue;
+ iBufOfset = I64LOW(iReqStart - iPhysStart);
+ //iReqCur already set in DoRead;
+ iFragOfset = iNxtIPCLen = physLength - iBufOfset;
+ }
+ else
+ {
+ // Determine offset from start due to data possibly recovered from local cache
+ iFragOfset = I64LOW(aStart - iReqStart);
+ r = PrepareFirstPhysicalFragment(physAddr, physLength, aLength);
+
+ // No use for secondBuffer yet...
+ iSecondBuffer = EFalse;
+ }
+
+ if(r == KErrNone)
+ {
+ iDbEnd = iPhysEnd;
+ iPhysEnd = iPhysStart + physLength;
+
+ if ((TUint32)physLength > physLen) physLength = physLen; // more memory in chunk than required
+
+ iSession->SetupCIMReadBlock(I64LOW(iPhysStart >> KMMCardHighCapBlockSizeLog2), (TUint8*)physAddr, physLen >> KMMCardHighCapBlockSizeLog2);
+
+ iSession->Command().iFlags|= KMMCCmdFlagPhysAddr;
+ iSession->EnableDoubleBuffering(physLength >> KDiskSectorShift);
+
+ r = EngageAndSetReadRequest(EMReqRead);
+ }
+
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("<mmd:lphysr:%d", r));
+
+ return r;
+ }
+
+
+TInt DMmcMediaDriverFlash::DoWrite()
+//
+// set up iReqStart, iReqEnd, and iReqCur, and launch first write. Any subsequent
+// writes are launched from the session end DFC. LaunchWrite() handles pre-reading
+// any sectors that are only partially modified.
+//
+ {
+ const TInt64 pos = iCurrentReq->Pos();
+ const TUint32 length = I64LOW(iCurrentReq->Length());
+
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf(">mmd:dw:0x%lx,0x%x", pos, length));
+ __ASSERT_DEBUG(CurrentRequest() == EMReqIdle, Panic(EDWInUse));
+ __ASSERT_DEBUG(pos < TotalSizeInBytes(), Panic(EDWStart));
+ __ASSERT_DEBUG(length > 0, Panic(EDWNotPositive));
+ __ASSERT_DEBUG(TotalSizeInBytes() >= pos + length, Panic(EDWEnd));
+
+ iReqCur = iReqStart = pos;
+ iReqEnd = iReqStart + length;
+
+ // iWtRBM is zero on construction because CBase-derived, and cleared at end
+ // of successful writes. If a write does not complete successfully, it may
+ // be left in non-zero state.
+ iWtRBM = 0;
+
+ iSecondBuffer = EFalse;
+ iDoLastRMW = EFalse;
+ iDoDoubleBuffer= EFalse;
+ iDoPhysicalAddress = EFalse;
+
+ const TInt r = LaunchWrite(iReqStart, length, EMReqWrite);
+
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("<mmd:dw:%d", r));
+
+ return r;
+ }
+
+
+TInt DMmcMediaDriverFlash::DoFormat()
+ {
+ const TInt64 pos = iCurrentReq->Pos();
+ const TUint32 length = I64LOW(iCurrentReq->Length());
+
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf(">mmd:df:0x%lx,0x%x", pos, length));
+ __ASSERT_DEBUG(CurrentRequest() == EMReqIdle, Panic(EDFInUse));
+ __ASSERT_DEBUG(pos < TotalSizeInBytes(), Panic(EDFStart));
+ __ASSERT_DEBUG(length > 0, Panic(EDFNotPositive));
+ __ASSERT_DEBUG(TotalSizeInBytes() >= pos + length, Panic(EDFEnd));
+
+ iReqCur = iReqStart = pos & ~iBlkMsk;
+ iReqEnd = (iReqStart + length + iBlkMsk) & ~iBlkMsk;
+
+ // the cache isn't maintained during a format operation to avoid redundantly
+ // writing 0xff to memory (the blocks won't be re-used.)
+ InvalidateCache();
+
+ // create an MBR after the first format step (or second if misaligned)
+
+ if (iInternalSlot)
+ {
+ iCreateMbr = EFalse;
+ }
+ else
+ {
+ if (iReqStart == (TInt64(iHiddenSectors) << KDiskSectorShift) && CreateMBRAfterFormat(iCard))
+ iCreateMbr = ETrue;
+ }
+
+ const TInt r = LaunchFormat(iReqStart, I64LOW(iReqEnd - iReqStart));
+
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("<mmd:df:%d", r));
+
+ return r;
+ }
+
+
+TInt DMmcMediaDriverFlash::LaunchFormat(TInt64 aStart, TUint32 aLength)
+//
+// starts writes from DoWrite(), DoFormat() and the session end DFC. This function does not
+// maintain the iReq* instance variables. It sets iIntBuf, iPhysStart and iPhysEnd.
+//
+ {
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf(">mmd:lf:0x%lx,0x%x", aStart, aLength));
+ __ASSERT_DEBUG(TotalSizeInBytes() > aStart, Panic(ELFStart));
+ __ASSERT_DEBUG((aStart & iBlkMsk) == 0, Panic(ELWFmtStAlign));
+ __ASSERT_DEBUG(aLength > 0, Panic(ELFNotPositive));
+ __ASSERT_DEBUG(TotalSizeInBytes() >= aStart + aLength, Panic(ELFEnd));
+ __ASSERT_DEBUG((aLength & iBlkMsk) == 0, Panic(ELWFmtEndAlign));
+ __ASSERT_DEBUG(iSession != NULL, Panic(ECFSessPtrNull));
+
+ TInt r;
+
+ if ((r = CheckDevice(EMReqTypeNormalWr)) == KErrNone)
+ {
+ iPhysStart = aStart & ~iBlkMsk;
+
+ // formats are always block-aligned, and the buffer is initialized to 0xff
+ // Check whether erase commands are supported by this card
+ if (iCard->CSD().CCC() & KMMCCmdClassErase)
+ {
+ // Determine the erase end point for the next command. We don't erase past the preferred erase unit
+ // size. Therefore, check which is lower, the preferred erase unit size or the end of the requested range.
+ TInt64 prefEraseUnitEnd = (iPhysStart + iEraseInfo.iPreferredEraseUnitSize) & ~iEraseUnitMsk;
+ iPhysEnd = UMin(prefEraseUnitEnd, aStart + aLength);
+
+ const TUint32 minEraseSectorSize=iEraseInfo.iMinEraseSectorSize;
+ const TInt64 minEraseSecMsk = TInt64(minEraseSectorSize-1);
+
+ // If erase start point doesn't lie on a min. erase unit boundary, then truncate the erase endpoint to
+ // the next min. erase unit boundary (assuming requested range allows this)
+ if ((iPhysStart & minEraseSecMsk)!=0)
+ {
+ prefEraseUnitEnd=(iPhysStart+minEraseSectorSize) & ~minEraseSecMsk;
+ iPhysEnd=UMin(prefEraseUnitEnd,iPhysEnd);
+ }
+
+ // Otherwise, if calculated erase end point doesn't lie on a min. erase unit boundary, but is at least one
+ // min. erase unit beyond the erase start point then move erase endpoint back to last min. erase unit boundary
+ else if ((iPhysEnd & minEraseSecMsk)!=0 && (iPhysEnd & ~minEraseSecMsk)>iPhysStart)
+ {
+ iPhysEnd&=(~minEraseSecMsk);
+ }
+
+ // Now, if the erase start/end points are aligned to a min. erase unit boundary, we can use an erase cmd.
+ if ((iPhysStart & minEraseSecMsk) == 0 && (iPhysEnd & minEraseSecMsk) == 0)
+ {
+ // Aligned erase
+ // Check that erase commands are supported prior to issuing an erase command
+ if(iEraseInfo.EraseGroupCmdsSupported())
+ {
+ iSession->SetupCIMEraseMGroup(I64LOW(iPhysStart >> KMMCardHighCapBlockSizeLog2),
+ I64LOW((iPhysEnd-iPhysStart) >> KMMCardHighCapBlockSizeLog2)); // Use ACMD35/36/38 (Erase Group)
+ }
+ else
+ {
+ iSession->SetupCIMEraseMSector(I64LOW(iPhysStart >> KMMCardHighCapBlockSizeLog2),
+ I64LOW((iPhysEnd - iPhysStart) >> KMMCardHighCapBlockSizeLog2)); // Use ACMD32/33/38 (Erase Sector)
+ }
+ }
+ else
+ {
+ // Misaligned erase - use multi-block write. However, first - check write length doesn't exceed buffer size.
+ if ((iPhysEnd-iPhysStart)>(TUint32)iMaxBufSize)
+ {
+ iPhysEnd=(iPhysStart+iMaxBufSize);
+ }
+
+ __ASSERT_DEBUG((iPhysEnd - iPhysStart) > 0, Panic(ELWLength));
+ const TUint32 writeLen = I64LOW(iPhysEnd - iPhysStart);
+ memset (iCacheBuf, 0x00, writeLen);
+ iSession->SetupCIMWriteBlock(I64LOW(iPhysStart >> KMMCardHighCapBlockSizeLog2), iCacheBuf, writeLen >> KMMCardHighCapBlockSizeLog2);
+ }
+ }
+ else
+ {
+ // Write to end of current write group, or end of request range, whichever is lower
+ const TInt64 prefEraseUnitEnd = (iPhysStart + iPrWtGpLen) & ~iPrWtGpMsk;
+ iPhysEnd = Min(prefEraseUnitEnd, aStart + aLength);
+
+ __ASSERT_DEBUG((iPhysEnd - iPhysStart) > 0, Panic(ELWLength));
+ const TUint32 writeLen = I64LOW(iPhysEnd - iPhysStart);
+ memset (iCacheBuf, 0x00, writeLen);
+ iSession->SetupCIMWriteBlock(I64LOW(iPhysStart >> KMMCardHighCapBlockSizeLog2), iCacheBuf, writeLen >> KMMCardHighCapBlockSizeLog2);
+ }
+
+ r = EngageAndSetWriteRequest(EMReqFormat);
+ }
+ return r;
+ }
+
+
+
+TInt DMmcMediaDriverFlash::LaunchWrite(TInt64 aStart, TUint32 aLength, TMediaRequest aMedReq)
+//
+// starts writes from DoWrite(), DoFormat() and the session end DFC. This function does not
+// maintain the iReq* instance variables. It sets iIntBuf, iPhysStart and iPhysEnd.
+//
+ {
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("\n>mmd:lw:0x%lx,%d,%d", aStart, aLength, aMedReq));
+ __ASSERT_DEBUG(aMedReq == EMReqWrite || aMedReq == EMReqFormat, Panic(ELWRequest));
+ __ASSERT_DEBUG(TotalSizeInBytes() > aStart, Panic(ELWStart));
+ __ASSERT_DEBUG(!(aMedReq == EMReqFormat) || (aStart & iBlkMsk) == 0, Panic(ELWFmtStAlign));
+ __ASSERT_DEBUG(aLength > 0, Panic(ELWNotPositive));
+ __ASSERT_DEBUG(TotalSizeInBytes() >= aStart + aLength, Panic(ELWEnd));
+ __ASSERT_DEBUG(!(aMedReq == EMReqFormat) || (aLength & iBlkMsk) == 0, Panic(ELWFmtEndAlign));
+ __ASSERT_DEBUG(iSession != NULL, Panic(ECFSessPtrNull));
+
+ TInt r;
+
+ if ((r = CheckDevice(EMReqTypeNormalWr)) == KErrNone)
+ {
+ iPhysStart = aStart & ~iBlkMsk;
+
+ // PSL MUST support double-buffering for DMA requests
+ // first write, or have just completed previous write
+ if (iWtRBM == 0)
+ {
+ if(iDoDoubleBuffer == EFalse)
+ {
+ //
+ // Can we use double-buffering for this request?
+ //
+ // - Only if PSL supports double buffering and the request length
+ // is greater than the maximum PSL buffer size.
+ //
+ iDoPhysicalAddress = iCurrentReq->IsPhysicalAddress();
+
+ const TInt64 medEnd = aStart + aLength;
+
+ TInt64 maxPslEnd = medEnd;
+ const TUint32 maxDbLength = iSocket->MaxDataTransferLength();
+
+ if(maxDbLength)
+ {
+ //
+ // If the PSL specifies a limit on the maximum size of a data transfer, then truncate the request...
+ //
+ maxPslEnd = UMin(medEnd, iPhysStart + maxDbLength);
+ }
+
+ iPhysEnd = (maxPslEnd + iBlkMsk) & ~iBlkMsk;
+
+ if (iDoPhysicalAddress)
+ {
+ iDoDoubleBuffer = EFalse;
+ iIntBuf = ReserveWriteBlocks(aStart, medEnd, &iWtRBM);
+ iPhysEnd = (medEnd + iBlkMsk) & ~iBlkMsk;
+ }
+
+ if (!iDoPhysicalAddress)
+ {
+ iDoDoubleBuffer = iSocket->SupportsDoubleBuffering() && ((iPhysEnd - iPhysStart) > iMaxBufSize);
+ if(iDoDoubleBuffer)
+ {
+ //
+ // Conditions for double-buffering are met. Set up the size of the first
+ // transfer to half the size of the block cache.
+ //
+ // Note that we don't bother to align to write groups here, as the entire
+ // request will be processed under one multi-block command so there's no
+ // danger of forcing the card into RMW cycles as would be the case when
+ // issuing multiple misaligned commands.
+ //
+ iDbEnd = maxPslEnd; // The end of the complete double-buffered transfer
+ iPhysEnd = (iPhysStart + (iMaxBufSize >> 1) + iBlkMsk) &~ iBlkMsk; // The end of the first double-buffered transfer
+ __ASSERT_DEBUG(iPhysEnd - iPhysStart <= (iMaxBufSize >> 1), Panic(ELWLength));
+
+ //
+ // Now reserve write blocks from the buffer cache. When double-buffering,
+ // write blocks are only really reserved during the last transfer to avoid
+ // continuously updating the cache indexes. Since the block cache is
+ // continuously recycled, the following call shall invalidate the cache
+ // and inform us as to whether we need to perform an RMW operation for
+ // the first and last blocks prior to initiating data transfer.
+ //
+ iIntBuf = ReserveWriteBlocks(aStart, iDbEnd, &iWtRBM);
+ }
+ else
+ {
+ //
+ // reserve buffers to end of first write group, or end of request range,
+ // whichever is lower. Note that if the range already exists in the buffer,
+ // e.g. because of a previous RBM, the same range will be returned. This
+ // means that iWtRBM can be set to zero in the callback DFC, and this code
+ // will retrieve the reserved range.
+ //
+ const TInt64 wtGpEnd = (iPhysStart + iPrWtGpLen) & ~iPrWtGpMsk;
+ const TInt64 medEnd = UMin(wtGpEnd, aStart + aLength);
+ iPhysEnd = (medEnd + iBlkMsk) & ~iBlkMsk;
+ iIntBuf = ReserveWriteBlocks(aStart, medEnd, &iWtRBM);
+ }
+ } //if (!iDoPhysicalAddress)
+ } //if(iDoDoubleBuffer == EFalse)
+ } //if (iWtRBM == 0)
+
+ if (iWtRBM & KWtRBMFst)
+ {
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("mmd:lw: read-before-modify required on first block"));
+ if (iDoPhysicalAddress)
+ iSession->SetupCIMReadBlock(I64LOW(iPhysStart >> KMMCardHighCapBlockSizeLog2), iMinorBuf, iBlkLen >> KMMCardHighCapBlockSizeLog2);
+ else
+ iSession->SetupCIMReadBlock(I64LOW(iPhysStart >> KMMCardHighCapBlockSizeLog2), iIntBuf, iBlkLen >> KMMCardHighCapBlockSizeLog2);
+ return EngageAndSetReadRequest(aMedReq);
+ }
+
+ else if (iWtRBM & KWtRBMLst)
+ {
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("mmd:lw: read-before-modify required on last block"));
+ if(iDoDoubleBuffer || iDoPhysicalAddress)
+ {
+ //
+ // When double-buffering, the result of the RMW-read operation shall be stored
+ // in the minor buffer, otherwise the data would be overwritten before the last
+ // data transfer takes place.
+ //
+ const TInt64 lastBlock = (aStart + aLength) & ~iBlkMsk; // start posn in media to read from (we know aStart + aLength isn't block aligned due to KWtRBMLst flag)
+ if (iDoDoubleBuffer)
+ iSession->SetupCIMReadBlock(I64LOW(lastBlock >> KMMCardHighCapBlockSizeLog2), iMinorBuf, iBlkLen >> KMMCardHighCapBlockSizeLog2);
+ else
+ iSession->SetupCIMReadBlock(I64LOW(lastBlock >> KMMCardHighCapBlockSizeLog2), iCacheBuf, iBlkLen >> KMMCardHighCapBlockSizeLog2);
+ }
+ else
+ {
+ //
+ // If not double-buffering, we can read the RMW data of the last block directly
+ // into the block cache as we know that the data transfer will fit entirely
+ // within the cache..
+ //
+ const TInt64 lastBlock = iPhysEnd - iBlkLen; // start posn in media to read from
+ iSession->SetupCIMReadBlock(I64LOW(lastBlock >> KMMCardHighCapBlockSizeLog2), iIntBuf + (lastBlock - iPhysStart), iBlkLen >> KMMCardHighCapBlockSizeLog2);
+ }
+
+ // Kick off the RMW-read operation for the last block...
+ return EngageAndSetReadRequest(aMedReq);
+ }
+
+ if (iWtRBM & KWtMinFst)
+ {
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("mmd:lw:Phys write-first-block-only"));
+ //Overwrite first block with the new data
+ TInt32 tlen = I64LOW(aStart & iBlkMsk);
+ TInt32 wlen = UMin(I64LOW((iBlkMsk+1) - tlen), aLength);
+
+ const TInt64 usrOfst = (aStart - iReqStart);
+ TPtr8 tgt(&iMinorBuf[tlen], I64LOW(wlen));
+
+ if ( (r = iCurrentReq->ReadRemote(&tgt,I64LOW(usrOfst))) != KErrNone)
+ return r;
+ }
+
+ if (iWtRBM & KWtMinLst)
+ {
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("mmd:lw:Phys write-last-block-only"));
+ iWtRBM &= ~KWtMinLst;
+ //Overwrite last block with the new data
+ const TInt64 medEnds = aStart + aLength;
+ TInt64 tlen = medEnds & iBlkMsk;
+
+ const TInt64 usrOfst = (aStart - iReqStart);
+ TPtr8 tgt(iCacheBuf, I64LOW(tlen));
+
+ if ( (r = iCurrentReq->ReadRemote(&tgt,I64LOW(usrOfst+aLength-tlen))) !=KErrNone)
+ return r;
+ }
+
+ // no reads required - read data from user buffer and launch write
+ const TInt64 usrOfst = (aStart - iReqStart);
+ const TInt64 bufOfst = aStart - iPhysStart; // offset into first sector, not whole buffer
+ const TInt64 len = UMin(aStart + aLength, iPhysEnd) - iReqCur;
+ __ASSERT_DEBUG(len > 0, Panic(ELWLength));
+ __ASSERT_DEBUG(I64HIGH(usrOfst) == 0, Panic(ELWLength));
+
+ if (iDoPhysicalAddress)
+ {
+ TPhysAddr physAddr = 0;
+ TInt physLength = 0;
+ TUint32 physLen = I64LOW(iPhysEnd - iPhysStart);
+
+ if (iWtRBM & KWtMinFst)
+ {
+#if !defined(__WINS__)
+ physAddr = Epoc::LinearToPhysical((TLinAddr)iMinorBuf);
+#else
+ physAddr = (TPhysAddr)iMinorBuf;
+#endif
+ physLength = iBlkLen;
+ iBufOfset = I64LOW(iReqStart - iPhysStart);
+ //iReqCur already set in DoWrite
+ iFragOfset = iIPCLen = iBlkLen - iBufOfset;
+ iWtRBM &= ~KWtMinFst;
+ }
+ else
+ {
+ iFragOfset = I64LOW(usrOfst);
+
+ r = PrepareFirstPhysicalFragment(physAddr, physLength, aLength);
+ }
+
+ if (r == KErrNone)
+ {
+ iDbEnd = iPhysEnd;
+ iPhysEnd = iPhysStart+physLength;
+
+ if ((TUint32)physLength > physLen) physLength = physLen; // more memory in fragment than required!
+
+ iSession->SetupCIMWriteBlock(I64LOW(iPhysStart >> KMMCardHighCapBlockSizeLog2), (TUint8*) physAddr, physLen >> KMMCardHighCapBlockSizeLog2);
+ iSession->Command().iFlags|= KMMCCmdFlagPhysAddr;
+ iSession->EnableDoubleBuffering(physLength >> KDiskSectorShift);
+ }
+ else
+ {
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("<mmd:lw:Phys:%d", r));
+ return r;
+ }
+ } // if (iDoPhysicalAddress)
+ else
+ {
+ TPtr8 tgt(&iIntBuf[bufOfst], I64LOW(len));
+
+ r = ReadDataFromUser(tgt, I64LOW(usrOfst));
+ if (r == KErrNone)
+ {
+ if(!iDoDoubleBuffer)
+ {
+ // EPBUSM automatically uses CMD24 instead of CMD25 for single block writes
+ iSession->SetupCIMWriteBlock(I64LOW(iPhysStart >> KMMCardHighCapBlockSizeLog2), iIntBuf, I64LOW((iPhysEnd - iPhysStart) >> KMMCardHighCapBlockSizeLog2));
+ }
+ else
+ {
+ //
+ // When double-buffering, set up the data transfer command to the entire
+ // request range. and flag the session to enable double-buffering (as well
+ // as specifying the length of each double-buffered transfer as calculated
+ // in 'len' above). This is performed only once - the double-buffering
+ // is subsequently handled within the DoDataTransferCallback function.
+ //
+ iSession->SetupCIMWriteBlock(I64LOW(iPhysStart >> KMMCardHighCapBlockSizeLog2), iIntBuf, I64LOW((((iDbEnd + iBlkMsk) & ~iBlkMsk) - iPhysStart) >> KMMCardHighCapBlockSizeLog2));
+ iSession->EnableDoubleBuffering(I64LOW((len + iBlkMsk) & ~iBlkMsk) >> KDiskSectorShift);
+
+ // ...and switch to the 'second' buffer, which will be populated in the
+ // data transfer callback in parallel with hardware transfer of the first.
+ iSecondBuffer = ETrue;
+ }
+ }
+ }
+
+ //Reliable Write only supported by v4.3+ MMC media
+ if (iCard->ExtendedCSD().ExtendedCSDRev() >= 3)
+ {
+ // One request, i.e. not end of previous DB request
+ // 512 Bytes long when sector aligned
+ if ( ( I64LOW(iPhysEnd - iPhysStart) == iBlkLen) && ((iReqStart & ~iBlkMsk) == iPhysStart) )
+ {
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("mmd:lw:AtomicWrite"));
+ iSession->Command().iFlags|= KMMCCmdFlagReliableWrite;
+ }
+ }
+
+ // Engage the data transfer session...
+ r = EngageAndSetWriteRequest(aMedReq);
+ } // if ((r = CheckDevice(EMReqTypeNormalWr)) == KErrNone)
+
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("<mmd:lw:%d", r));
+
+ return r;
+ }
+
+TInt DMmcMediaDriverFlash::PartitionInfo(TPartitionInfo& anInfo)
+//
+// Read the partition information for the media. If the user supplied a password,
+// then unlock the card before trying to read the first sector.
+//
+ {
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf(">mmd:rpi"));
+ __ASSERT_DEBUG(CurrentRequest() == EMReqIdle, Panic(ERPIInUse));
+
+ iPartitionInfo = &anInfo;
+
+ if(iMmcPartitionInfo)
+ {
+ // If this is an embedded device, use the custom formatting function:
+ TInt r = iMmcPartitionInfo->PartitionInfo(*iPartitionInfo, iSessionEndCallBack);
+
+ iHiddenSectors = 0; // Not used for internal media
+
+ if (KErrNone == r)
+ iMedReq = EMReqEMMCPtnInfo;
+
+ return(r);
+ }
+
+ // Assume MBR will be present or is not required
+ iMbrMissing = EFalse;
+
+ // If media driver is persistent (see EMediaDriverPersistent),
+ // the card may have changed since last power down, so reset CID
+ iSession->SetCard(iCard);
+
+ TInt r = LaunchRPIRead();
+
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("<mmd:rpi:%d", r));
+
+ if(r == KErrLocked)
+ {
+ // If the media is locked, we present a default partition entry to the local
+ // media subsystem, which will be updated when the media is finally unlocked.
+ r = CreateDefaultPartition();
+ if (r != KErrNone)
+ return r;
+ return KErrLocked;
+ }
+
+ // KErrNone indicates asynchronous completion
+ return r;
+ }
+
+TInt DMmcMediaDriverFlash::LaunchRPIUnlock(TLocalDrivePasswordData& aPasswordData)
+ {
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf(">mmd:lru:%d,%d", iCard->IsReady(), iCard->IsLocked()));
+ __ASSERT_DEBUG(iSession != NULL, Panic(ECFSessPtrNull));
+
+ TInt r = KErrNone;
+
+ // CMD42 is an adtc, so check state in same way as for write
+ if ((r = CheckDevice(EMReqTypeUnlockPswd)) == KErrNone)
+ {
+ r = Stack().MMCSocket()->PrepareStore(CardNum(), DLocalDrive::EPasswordUnlock, aPasswordData);
+
+ if (r == KErrNone)
+ {
+ TMediaPassword curPwd;
+
+ curPwd = *aPasswordData.iOldPasswd;
+
+ TInt curPwdLen = curPwd.Length();
+ TInt blockLen = 2 + curPwdLen;
+
+ TPtr8 pbuf(&iMinorBuf[0], 2, blockLen);
+ pbuf[0] = 0; // LOCK_UNLOCK = 0; SET_PWD = 0
+ pbuf[1] = static_cast<TUint8>(curPwdLen);
+ pbuf.Append(curPwd);
+ iSession->SetupCIMLockUnlock(blockLen, iMinorBuf);
+
+ r = EngageAndSetWriteRequest(EMReqUpdatePtnInfo);
+ }
+ }
+
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("<mmd:lru:%d", r));
+ return r;
+ }
+
+
+TInt DMmcMediaDriverFlash::LaunchRPIRead()
+//
+// launch read request on first KDiskSectorSize (512) bytes
+//
+ {
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf((">mmd:lrr")));
+ __ASSERT_DEBUG(iSession != NULL, Panic(ECFSessPtrNull));
+
+ // the partition information is read before any other area is read from /
+ // written to, and so does not need to maintain cache coherence. Therefore
+ // it can safely use the minor buffer.
+
+ TInt r;
+ if ((r = CheckDevice(EMReqTypeNormalRd)) == KErrNone)
+ {
+ iIntBuf = iMinorBuf;
+ iSession->SetupCIMReadBlock(0, iIntBuf); // aBlocks = 1
+ r = EngageAndSetReadRequest(EMReqPtnInfo);
+ }
+
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("<mmd:lrr:%d", r));
+ return r;
+ }
+
+
+TInt DMmcMediaDriverFlash::LaunchRPIErase()
+ {
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf(">mmd:lre:%d,%d", iCard->IsReady(), iCard->IsLocked()));
+ __ASSERT_DEBUG(iSession != NULL, Panic(ECFSessPtrNull));
+
+ TInt r = KErrNone;
+
+ // CMD42 is an adtc, so check state in same way as for write
+ if ((r = CheckDevice(EMReqTypeUnlockPswd)) == KErrNone)
+ {
+ if(iCard->IsWriteProtected())
+ {
+ r = KErrAccessDenied;
+ }
+ else
+ {
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("mmd:df:EMReqForceErase"));
+ iMinorBuf[0] = KMMCLockUnlockErase;
+ iSession->SetupCIMLockUnlock(1, iMinorBuf);
+ r = EngageAndSetWriteRequest(EMReqForceErase);
+ }
+ }
+
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("<mmd:lru:%d", r));
+ return r;
+ }
+
+
+TInt DMmcMediaDriverFlash::DecodePartitionInfo()
+//
+// decode partition info that was read into internal buffer
+//
+ {
+ TInt partitionCount=iPartitionInfo->iPartitionCount=0;
+ TInt defaultPartitionNumber=-1;
+ TMBRPartitionEntry* pe;
+ const TUint KMBRFirstPartitionOffsetAligned = KMBRFirstPartitionOffset & ~3;
+ TInt i;
+
+ // Read of the first sector successful so check for a Master Boot Record
+ if (*(TUint16*)(&iIntBuf[KMBRSignatureOffset])!=0xAA55)
+ goto mbr_done;
+
+ __ASSERT_COMPILE(KMBRFirstPartitionOffsetAligned + KMBRMaxPrimaryPartitions * sizeof(TMBRPartitionEntry) <= KMBRSignatureOffset);
+
+ memmove(&iIntBuf[0], &iIntBuf[2],
+ KMBRFirstPartitionOffsetAligned + KMBRMaxPrimaryPartitions * sizeof(TMBRPartitionEntry));
+
+
+ for (i=0, pe = (TMBRPartitionEntry*)(&iIntBuf[KMBRFirstPartitionOffsetAligned]);
+ pe->iPartitionType != 0 && i < KMBRMaxPrimaryPartitions;i++,pe++)
+ {
+ if (pe->IsDefaultBootPartition())
+ {
+ SetPartitionEntry(&iPartitionInfo->iEntry[0],pe->iFirstSector,pe->iNumSectors);
+ defaultPartitionNumber=i;
+ partitionCount++;
+ break;
+ }
+ }
+
+ // Now add any other partitions
+ for (i=0, pe = (TMBRPartitionEntry*)(&iIntBuf[KMBRFirstPartitionOffsetAligned]);
+ pe->iPartitionType != 0 && i < KMBRMaxPrimaryPartitions;i++,pe++)
+ {
+ TBool validPartition = ETrue; // assume partition valid
+
+ if (defaultPartitionNumber==i)
+ {
+ // Already sorted
+ }
+
+ // FAT partition ?
+ else if (pe->IsValidDosPartition() || pe->IsValidFAT32Partition())
+ {
+ SetPartitionEntry(&iPartitionInfo->iEntry[partitionCount],pe->iFirstSector,pe->iNumSectors);
+ __KTRACE_OPT(KLOCDPAGING, Kern::Printf("Mmc: FAT partition found at sector #%u", pe->iFirstSector));
+ partitionCount++;
+ }
+ else
+ {
+ validPartition = EFalse;
+ }
+
+ if (validPartition && partitionCount == 1)
+ iHiddenSectors = pe->iFirstSector;
+
+ }
+
+ // Check the validity of the partition address boundaries
+ // If there is any
+ if(partitionCount > 0)
+ {
+ const TInt64 deviceSize = iCard->DeviceSize64();
+ TPartitionEntry& part = iPartitionInfo->iEntry[partitionCount - 1];
+ // Check that the card address space boundary is not exceeded by the last partition
+ // In case of only 1 partition in the media check also it
+ if(part.iPartitionBaseAddr + part.iPartitionLen > deviceSize)
+ {
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("Mmc: MBR partition exceeds card memory space"));
+ // Adjust the partition length to card address boundary
+ part.iPartitionLen = (deviceSize - part.iPartitionBaseAddr);
+
+ // Check that the base address contained valid information
+ if(part.iPartitionLen <= 0)
+ {
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("Mmc: Invalid base address"));
+ // Invalid MBR - assume the boot sector is in the first sector
+ defaultPartitionNumber =-1;
+ partitionCount=0;
+ }
+ }
+ // More than one partition. Go through all of them
+ if (partitionCount > 0)
+ {
+ for(i=partitionCount-1; i>0; i--)
+ {
+ const TPartitionEntry& curr = iPartitionInfo->iEntry[i];
+ TPartitionEntry& prev = iPartitionInfo->iEntry[i-1];
+ // Check if partitions overlap
+ if(curr.iPartitionBaseAddr < (prev.iPartitionBaseAddr + prev.iPartitionLen))
+ {
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("Mmc: Overlapping partitions"));
+ // Adjust the partition length to not overlap the next partition
+ prev.iPartitionLen = (curr.iPartitionBaseAddr - prev.iPartitionBaseAddr);
+
+ // Check that the base address contained valid information
+ if(prev.iPartitionLen <= 0)
+ {
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("Mmc: Invalid base address"));
+ // Invalid MBR - assume the boot sector is in the first sector
+ defaultPartitionNumber=(-1);
+ partitionCount=0;
+ }
+ }
+ }
+ }
+ }
+
+mbr_done:
+ if (defaultPartitionNumber==(-1) && partitionCount==0)
+ {
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("Mmc:PartitionInfo no MBR"));
+ if (MBRMandatory(iCard))
+ {
+ // If the MBR is missing AND is required, we present a default partition entry to the local
+ // media subsystem, which will be updated when the media is finally formatted
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("MBR mandatory, defining space for MBR + default partition"));
+ iMbrMissing = ETrue;
+ TInt r = CreateDefaultPartition();
+ if (r != KErrNone)
+ return r;
+ }
+ else
+ {
+ // Assume it has no MBR, and the Boot Sector is in the 1st sector
+ SetPartitionEntry(&iPartitionInfo->iEntry[0],0,I64LOW(iCard->DeviceSize64()>>KDiskSectorShift));
+ iHiddenSectors=0;
+ }
+ partitionCount=1;
+ }
+
+ iPartitionInfo->iPartitionCount=partitionCount;
+ iPartitionInfo->iMediaSizeInBytes=TotalSizeInBytes();
+
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("<Mmc:PartitionInfo (C:%d)",iPartitionInfo->iPartitionCount));
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf(" Partition1 (B:%xH L:%xH)",I64LOW(iPartitionInfo->iEntry[0].iPartitionBaseAddr),I64LOW(iPartitionInfo->iEntry[0].iPartitionLen)));
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf(" Partition2 (B:%xH L:%xH)",I64LOW(iPartitionInfo->iEntry[1].iPartitionBaseAddr),I64LOW(iPartitionInfo->iEntry[1].iPartitionLen)));
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf(" Partition3 (B:%xH L:%xH)",I64LOW(iPartitionInfo->iEntry[2].iPartitionBaseAddr),I64LOW(iPartitionInfo->iEntry[2].iPartitionLen)));
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf(" Partition4 (B:%xH L:%xH)",I64LOW(iPartitionInfo->iEntry[3].iPartitionBaseAddr),I64LOW(iPartitionInfo->iEntry[3].iPartitionLen)));
+
+#ifdef _DEBUG
+ TMBRPartitionEntry cPe;
+ if(GetDefaultPartitionInfo(cPe) == KErrNone)
+ {
+ pe = (TMBRPartitionEntry*)(&iIntBuf[0]);
+
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("-------------------------------------------"));
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("-- Partition Entry Validation/Comparison --"));
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("-------------------------------------------"));
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("-- iX86BootIndicator [%02x:%02x] %c -", pe->iX86BootIndicator, cPe.iX86BootIndicator, pe->iX86BootIndicator == cPe.iX86BootIndicator ? ' ' : 'X'));
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("-- iStartHead [%02x:%02x] %c -", pe->iStartHead, cPe.iStartHead, pe->iStartHead == cPe.iStartHead ? ' ' : 'X'));
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("-- iStartSector [%02x:%02x] %c -", pe->iStartSector, cPe.iStartSector, pe->iStartSector == cPe.iStartSector ? ' ' : 'X'));
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("-- iStartCylinder [%02x:%02x] %c -", pe->iStartCylinder, cPe.iStartCylinder, pe->iStartCylinder == cPe.iStartCylinder ? ' ' : 'X'));
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("-- iPartitionType [%02x:%02x] %c -", pe->iPartitionType, cPe.iPartitionType, pe->iPartitionType == cPe.iPartitionType ? ' ' : 'X'));
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("-- iEndHead [%02x:%02x] %c -", pe->iEndHead, cPe.iEndHead, pe->iEndHead == cPe.iEndHead ? ' ' : 'X'));
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("-- iEndSector [%02x:%02x] %c -", pe->iEndSector, cPe.iEndSector, pe->iEndSector == cPe.iEndSector ? ' ' : 'X'));
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("-- iEndCylinder [%02x:%02x] %c -", pe->iEndCylinder, cPe.iEndCylinder, pe->iEndCylinder == cPe.iEndCylinder ? ' ' : 'X'));
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("-- iFirstSector [%08x:%08x] %c -", pe->iFirstSector, cPe.iFirstSector, pe->iFirstSector == cPe.iFirstSector ? ' ' : 'X'));
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("-- iNumSectors [%08x:%08x] %c -", pe->iNumSectors, cPe.iNumSectors, pe->iNumSectors == cPe.iNumSectors ? ' ' : 'X'));
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("-------------------------------------------"));
+ }
+#endif
+
+ return(KErrNone);
+ }
+
+
+TInt DMmcMediaDriverFlash::WritePartitionInfo()
+/**
+ Write the default partition table to freshly formatted media
+ @return Standard Symbian OS Error Code
+ */
+ {
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf(">mmd:wpi"));
+ __ASSERT_DEBUG(iSession != NULL, Panic(ECFSessPtrNull));
+
+ TMBRPartitionEntry partitionEntry;
+ TInt err = GetDefaultPartitionInfo(partitionEntry);
+ if(err == KErrNone)
+ {
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("mmd:MBR/Partition Table"));
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf(" Boot ID : %02xh", partitionEntry.iX86BootIndicator));
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf(" Start Head : %02xh", partitionEntry.iStartHead));
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf(" Start Sector : %02xh", partitionEntry.iStartSector));
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf(" Start Cyclinder : %02xh", partitionEntry.iStartCylinder));
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf(" System ID : %02xh", partitionEntry.iPartitionType));
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf(" End Head : %02xh", partitionEntry.iEndHead));
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf(" End Sector : %02xh", partitionEntry.iEndSector));
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf(" End Cyclinder : %02xh", partitionEntry.iEndCylinder));
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf(" Relative Sector : %08xh", partitionEntry.iFirstSector));
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf(" Number of Sectors: %08xh", partitionEntry.iNumSectors));
+
+ //
+ // Clear all other partition entries and align the partition info into the minor buffer for writing...
+ //
+ memclr(iMinorBuf, KDiskSectorSize);
+ memcpy(&iMinorBuf[KMBRFirstPartitionEntry], &partitionEntry, sizeof(TMBRPartitionEntry));
+
+ *(TUint16*)(&iMinorBuf[KMBRSignatureOffset]) = 0xAA55;
+
+ iSession->SetupCIMWriteBlock(0, iMinorBuf);
+
+ //
+ // Write the partition table and engage the read to validate and complete the mount process
+ //
+ iMbrMissing = EFalse;
+ iCreateMbr = EFalse;
+ err = EngageAndSetWriteRequest(EMReqUpdatePtnInfo);
+ }
+
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("<mmd:wpi:%d", err));
+
+ return(err);
+ }
+
+
+TInt DMmcMediaDriverFlash::CreateDefaultPartition()
+ {
+ TMBRPartitionEntry defPartition;
+ TInt r = GetDefaultPartitionInfo(defPartition);
+ if (r == KErrNone)
+ {
+ SetPartitionEntry(&iPartitionInfo->iEntry[0], defPartition.iFirstSector, defPartition.iNumSectors);
+ iHiddenSectors = defPartition.iFirstSector;
+ iPartitionInfo->iPartitionCount = 1;
+ iPartitionInfo->iMediaSizeInBytes = TotalSizeInBytes();
+ }
+ return r;
+ }
+
+TInt DMmcMediaDriverFlash::GetDefaultPartitionInfo(TMBRPartitionEntry& aPartitionEntry)
+/**
+ Calculates the default patition information for an specific card.
+ @param aPartitionEntry The TMBRPartitionEntry to be filled in with the format parameters
+ @return Standard Symbian OS Error Code
+ */
+ {
+ memclr(&aPartitionEntry, sizeof(TMBRPartitionEntry));
+ TUint16 reservedSectors; // Not used
+ return GetMediaDefaultPartitionInfo(aPartitionEntry, reservedSectors, iCard);
+ }
+
+
+void DMmcMediaDriverFlash::SetPartitionEntry(TPartitionEntry* aEntry, TUint aFirstSector, TUint aNumSectors)
+//
+// auxiliary static function to record partition information in TPartitionEntry object
+//
+ {
+ aEntry->iPartitionBaseAddr=aFirstSector;
+ aEntry->iPartitionBaseAddr<<=KDiskSectorShift;
+ aEntry->iPartitionLen=aNumSectors;
+ aEntry->iPartitionLen<<=KDiskSectorShift;
+ aEntry->iPartitionType=KPartitionTypeFAT12;
+ }
+
+TInt DMmcMediaDriverFlash::DoPasswordOp()
+ {
+ // Reconstruct password data structure in our address space
+ TLocalDrivePasswordData clientData;
+ TInt r = iCurrentReq->ReadRemoteRaw(&clientData, sizeof(TLocalDrivePasswordData));
+
+ TMediaPassword oldPassword;
+ if (r == KErrNone)
+ r = iCurrentReq->ReadRemote(clientData.iOldPasswd, &oldPassword);
+
+ TMediaPassword newPassword;
+ if (r == KErrNone)
+ r = iCurrentReq->ReadRemote(clientData.iNewPasswd, &newPassword);
+
+ TLocalDrivePasswordData passData(oldPassword, newPassword, clientData.iStorePasswd);
+
+ if (r == KErrNone)
+ {
+ TInt id=iCurrentReq->Id();
+ switch (id)
+ {
+ case DLocalDrive::EPasswordUnlock:
+ r = LaunchRPIUnlock(passData);
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("<mmd:rpi:%d", r));
+ break;
+ case DLocalDrive::EPasswordLock:
+ case DLocalDrive::EPasswordClear:
+ PasswordControl(id, passData);
+ break;
+ }
+ }
+
+ // This will complete the request in the event of an error
+ if(r != KErrNone)
+ PartitionInfoComplete(r);
+
+ return KErrNone; // ensures to indicate asynchronoous completion
+ }
+
+void DMmcMediaDriverFlash::PasswordControl(TInt aFunc, TLocalDrivePasswordData& aData)
+//
+// Change a card's password, or clear the pasword from a locked card. The card
+// must be unlocked for this function. A locked card is unlocked when it is mounted,
+// to read the partition information. This is done from ReadPartitionInfo() and
+// LaunchRPIUnlock().
+//
+ {
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf(">mmd:pc:%d", (TInt) aFunc));
+ __ASSERT_DEBUG(CurrentRequest() == EMReqIdle, Panic(EPCInUse));
+ __ASSERT_DEBUG(aFunc == DLocalDrive::EPasswordLock || aFunc == DLocalDrive::EPasswordClear, Panic(EPCFunc));
+ __ASSERT_DEBUG(iSession != NULL, Panic(ECFSessPtrNull));
+
+ TInt r;
+
+ if ((r = CheckDevice(EMReqTypeChangePswd)) == KErrNone)
+ {
+ // check if the current password is correct here. (This makes the
+ // clear operation redundant only if the password is stored and it
+ // is wrong.) Complete with same value as DoSessionEndDfc() would.
+
+ TMediaPassword curPwd;
+
+ curPwd = *aData.iOldPasswd;
+ TInt curPwdLen = curPwd.Length();
+ TInt blockLen;
+
+ if (!(iCard->iFlags & KMMCardIsLockable))
+ r = KErrNotSupported;
+ else if (Stack().PasswordStore()->IsMappingIncorrect(iCard->CID(), curPwd))
+ r = KErrAccessDenied;
+ else
+ {
+ if ((r = Stack().MMCSocket()->PrepareStore(CardNum(), aFunc, aData/*, aThread*/)) == KErrNone)
+ {
+ switch (aFunc)
+ {
+ case DLocalDrive::EPasswordLock:
+ {
+ TMediaPassword newPwd;
+ newPwd = *aData.iNewPasswd;
+ TInt newPwdLen = newPwd.Length();
+ blockLen = 1 + 1 + curPwdLen + newPwdLen;
+
+ #ifndef __EPOC32__
+ TUint16 env_Var[]=L"_EPOC_PWD_LEN";
+ TUint16 env_Val[2];
+ env_Val[0]=(TUint16)(curPwdLen+1);
+ env_Val[1]=0;//make a null terminated string
+ r=SetEnvironmentVariable(env_Var,&env_Val[0]);
+ __ASSERT_DEBUG(r!=0, Panic(EPCFunc));
+
+ #endif
+
+ TPtr8 pbuf(&iMinorBuf[0], 2, blockLen);
+ pbuf[0] = KMMCLockUnlockSetPwd; // LOCK_UNLOCK = 0, SET_PWD = 1
+ pbuf[1] = static_cast<TUint8>(curPwdLen + newPwdLen);
+ pbuf.Append(curPwd);
+ pbuf.Append(newPwd);
+ }
+ break;
+
+ case DLocalDrive::EPasswordClear:
+ {
+ blockLen = 1 + 1 + curPwdLen;
+
+ TPtr8 pbuf(&iMinorBuf[0], 2, blockLen);
+ pbuf[0] = KMMCLockUnlockClrPwd; // LOCK_UNLOCK = dc, CLR_PWD = 1
+ pbuf[1] = static_cast<TUint8>(curPwdLen);
+ pbuf.Append(curPwd);
+ }
+ break;
+
+ default:
+ // DLocalDrive::EPasswordUnlock is not handled. This avoids warnings for unused
+ // case, and uninitialized variable.
+ blockLen = 0;
+ break;
+ } // switch (aFunc)
+
+ iSession->SetupCIMLockUnlock(blockLen, iMinorBuf);
+ r = EngageAndSetWriteRequest(EMReqPswdCtrl);
+ } // if ((r = Stack().PrepareStore(CardNum(), aFunc, aData, aThread)) == KErrNone)
+ } // else (Stack().IsMappingIncorrect(iCard->CID(), curPwd))
+ } // (r = CheckDevice(EMReqTypeChangePswd)) == KErrNone
+
+ // complete immediately if error occured
+ if (r != KErrNone)
+ CompleteRequest(r);
+
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("<mmd:pc:%d", r));
+ }
+
+
+// ---- device status, callback DFC ----
+
+TInt DMmcMediaDriverFlash::CheckDevice(TMediaReqType aReqType)
+//
+// Check the device before initiating a command
+//
+ {
+
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf(">mmd:cd:%d",aReqType));
+
+ TInt r=KErrNone;
+
+ if (!iCard->IsReady())
+ r=KErrNotReady;
+
+ // The card must be locked if attempting to unlock during RPI, and
+ // unlocked at all other times.
+ else if (aReqType!=EMReqTypeUnlockPswd && iCard->IsLocked())
+ r=KErrLocked;
+ // Don't perform Password setting for WriteProtected cards,
+ // unable to recover (ForcedErase) if password lost.
+ else if (aReqType==EMReqTypeChangePswd)
+ {
+ if (iCard->MediaType()==EMultiMediaROM)
+ {
+ r=KErrAccessDenied;
+ }
+ }
+ else if (iMbrMissing && aReqType==EMReqTypeNormalRd)
+ r=KErrCorrupt;
+
+#if !defined(__WINS__)
+ // Don't perform write/password operations when the battery is low
+// else if (aReqType!=EMReqTypeNormalRd && Hal::MainBatteryStatus()<ELow && !Hal::ExternalPowerPresent())
+// r=KErrBadPower;
+#endif
+ // Don't perform write operations when the mechanical write protect switch is set
+ else if (aReqType==EMReqTypeNormalWr && iCard->IsWriteProtected())
+ r=KErrAccessDenied;
+ // Don't perform write/format operations on MMC ROM cards
+ else if (iMediaType==EMultiMediaROM && aReqType == EMReqTypeNormalWr)
+ r=KErrAccessDenied;
+
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("<mmd:cd:%d", r));
+ return(r);
+ }
+
+void DMmcMediaDriverFlash::SessionEndCallBack(TAny* aMediaDriver)
+//
+// called by EPBUS when a single session has finished. Queues DFC to launch
+// next session or to complete client request.
+//
+ {
+ DMmcMediaDriverFlash& md = *static_cast<DMmcMediaDriverFlash*>(aMediaDriver);
+ __ASSERT_DEBUG(! md.iSessionEndDfc.Queued(), Panic(ESECBQueued));
+ md.iSessionEndDfc.Enque();
+ }
+
+
+void DMmcMediaDriverFlash::SessionEndDfc(TAny* aMediaDriver)
+ {
+ static_cast<DMmcMediaDriverFlash*>(aMediaDriver)->DoSessionEndDfc();
+ }
+
+
+void DMmcMediaDriverFlash::DoSessionEndDfc()
+//
+// launch next session or complete client request
+//
+ {
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf(">mmd:dsed:%d", CurrentRequest()));
+
+ TInt r=KErrNone;
+
+ EndInCritical();
+
+ // Abort if writing or formatting and power has gone down
+ if (!Kern::PowerGood() && CurrentRequest()!=EMReqRead)
+ r=KErrAbort;
+ // Return KErrNotReady if we have has a deferred media change
+ if (!iCard->IsReady())
+ r=KErrNotReady;
+ // if stack has powered down session pointer will be NULL
+ if (iSession == NULL)
+ r = KErrNotReady;
+
+ TBool complete = ETrue;
+
+ if (r==KErrNone)
+ {
+ r = iSession->EpocErrorCode();
+
+ switch (CurrentRequest())
+ {
+ case EMReqRead:
+ {
+ if (r != KErrNone) // abort if MMC error
+ break;
+
+ if(iDoDoubleBuffer)
+ {
+ //
+ // This is the end of a double-buffered transfer.
+ // - Now we have two buffers to copy back to the user...
+ //
+ TUint8* bufPtr = iIntBuf + (iSecondBuffer ? (iMaxBufSize >> 1) : 0);
+ if((r = WriteDataToUser(bufPtr)) == KErrNone)
+ {
+ MarkBlocks(iReqCur, iPhysEnd, CchMemToIdx(bufPtr));
+
+ iReqCur = iPhysEnd;
+ iPhysEnd = iDbEnd;
+
+ bufPtr = iIntBuf + (iSecondBuffer ? 0 : (iMaxBufSize >> 1));
+ if((r = WriteDataToUser(bufPtr)) == KErrNone)
+ {
+ MarkBlocks(iReqCur, (iPhysEnd + iBlkMsk) & ~iBlkMsk, CchMemToIdx(bufPtr));
+ }
+ }
+ iDoDoubleBuffer = EFalse;
+ }
+ else if (iDoPhysicalAddress)
+ {
+ if (iRdROB & KIPCWrite)
+ {
+ // partial end point
+ TInt len = I64LOW(iReqEnd & iBlkMsk);
+ const TInt ofset = I64LOW(iPhysEnd - iBlkLen - iReqStart);
+
+ TPtrC8 extrView(iIntBuf, len);
+ r = iCurrentReq->WriteRemote(&extrView,ofset);
+ }
+ // Reset attributes
+ iRdROB = 0;
+ iFragOfset = iIPCLen = iBufOfset = 0;
+ iReqCur = iPhysEnd = iReqEnd;
+ iDoPhysicalAddress = EFalse;
+ }
+ else
+ {
+ r = WriteDataToUser(&iIntBuf[I64LOW(iReqCur - iPhysStart)]);
+ }
+
+ if (r != KErrNone)
+ break;
+
+ // if there is more information to read for the user then engage another session
+ if ((iReqCur = iPhysEnd) < iReqEnd)
+ {
+ TBool allDone = EFalse;
+ if ( ((r = ReadDataUntilCacheExhausted(&allDone)) == KErrNone) && !allDone)
+ {
+ iPhysStart = iReqCur & ~iBlkMsk;
+ TUint32 length = I64LOW(iReqEnd - iReqCur);
+
+ if ( (iReqEnd - iPhysStart) > iMaxBufSize && iSocket->SupportsDoubleBuffering() && !iReadToEndOfCard)
+ r = LaunchDBRead();
+ else
+ r = LaunchRead(iReqCur, length);
+
+ if ( r == KErrNone)
+ complete = EFalse;
+ }
+ }
+ }
+ break;
+
+ case EMReqWrite:
+ {
+ if (r != KErrNone) // abort if MMC error
+ {
+ break;
+ }
+
+ if (iWtRBM == 0)
+ {
+ iReqCur = iPhysEnd;
+ iDoDoubleBuffer = EFalse;
+ iDoPhysicalAddress = EFalse;
+ iRdROB = 0;
+ iFragOfset = iIPCLen = iBufOfset = 0;
+ }
+ // clear current RBM flag
+ else
+ {
+ if (iWtRBM & KWtRBMFst)
+ {
+ iWtRBM &= ~KWtRBMFst;
+ }
+ else if (iWtRBM & KWtRBMLst)
+ {
+ iWtRBM &= ~KWtRBMLst;
+ }
+ }
+
+ // advance media position if just finished write, as opposed to read-before-modify
+ if (iReqCur < iReqEnd)
+ {
+ if ((r = LaunchWrite(iReqCur, I64LOW(iReqEnd - iReqCur), EMReqWrite)) == KErrNone)
+ {
+ complete = EFalse;
+ }
+
+ complete = (r != KErrNone) ? (TBool)ETrue : (TBool)EFalse;
+ }
+ }
+ break;
+
+ case EMReqFormat:
+ {
+ if (r != KErrNone) // abort if MMC error
+ break;
+
+ if ((iEraseUnitMsk == KMaxTUint64) || // no erase unit defined (Erase Class Commands not supported) ?
+ (iPhysEnd == iReqEnd) || // finshed already ?
+ ((iPhysStart & iEraseUnitMsk) == 0 && (iPhysEnd & iEraseUnitMsk) == 0))
+ {
+ iReqCur = iPhysEnd;
+ }
+ else
+ {
+ // Formating to a mis-aligned boundary, so we can't make best use of
+ // multiple erase blocks. We shall simply erase up to the next block
+ // boundary, and return the adjustment info to the file system
+ r = I64LOW(iPhysEnd - iPhysStart);
+ iReqCur = iReqEnd;
+ }
+
+ if(r == KErrNone)
+ {
+ // advance media position if just finished write, as opposed to read-before-modify
+ if (iReqCur < iReqEnd)
+ {
+ if ((r = LaunchFormat(iReqCur, I64LOW(iReqEnd - iReqCur))) == KErrNone)
+ {
+ complete = EFalse;
+ }
+ }
+ // if format finished, write an MBR if required
+ // Always write an MBR if it's an SD card
+ else if (iCreateMbr)
+ {
+ // Finished Format, so write the MBR/default partition table if required
+ r = WritePartitionInfo();
+ complete = (r != KErrNone) ? (TBool)ETrue : (TBool)EFalse;
+ }
+ }
+ }
+ break;
+
+ case EMReqPtnInfo:
+ if (r == KErrNone)
+ r = DecodePartitionInfo(); // set up iPartitionInfo
+
+ PartitionInfoComplete(r == KErrNone?KErrNone:KErrNotReady);
+ break;
+
+ case EMReqEMMCPtnInfo:
+ iMedReq = EMReqIdle;
+ // For now do nothing..
+ break;
+
+ case EMReqUpdatePtnInfo:
+ break;
+
+ case EMReqPswdCtrl:
+ if (r == KErrLocked)
+ r = KErrAccessDenied;
+ break;
+
+ case EMReqForceErase:
+
+ if (r == KErrNone)
+ {
+ // Finished Forced Erase , so write the default partition table...
+ r = WritePartitionInfo();
+ }
+
+ complete = (r != KErrNone) ? (TBool)ETrue : (TBool)EFalse;
+ break;
+
+ case EMReqWritePasswordData:
+ //
+ // WritePasswordData also kicks off an auto-unlock session to ensure that
+ // any locked cards that have passwords in the password store are immediately
+ // available. We can safely ignore any errors returned at this stage, as the
+ // password store will have been successfully updated (in locmedia.cpp), even
+ // if the card is unable to accept the password.
+ //
+ r = KErrNone;
+ break;
+
+ case EMReqIdle:
+ // request has been completed already (e.g. due to a power down)
+ break;
+
+
+ default:
+ __ASSERT_DEBUG(EFalse, Panic(EDSEDRequest));
+ break;
+ }
+ }
+
+ // r != KErrNone => complete
+ __ASSERT_DEBUG(!(r != KErrNone) || complete, Panic(EDSEDNotErrComplete));
+
+ if (complete)
+ {
+ if (r != KErrNone)
+ InvalidateCache();
+
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("<mdf:dsed:cmp:%d", r));
+ CompleteRequest(r);
+ }
+ else
+ {
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("<mdf:dsed:ncmp"));
+ }
+ }
+
+void DMmcMediaDriverFlash::DataTransferCallBack(TAny* aMediaDriver)
+ {
+ DMmcMediaDriverFlash& md = *static_cast<DMmcMediaDriverFlash*>(aMediaDriver);
+ __ASSERT_DEBUG(! md.iDataTransferCallBackDfc.Queued(), Panic(EDBCBQueued));
+ md.iDataTransferCallBackDfc.Enque();
+ }
+
+void DMmcMediaDriverFlash::DataTransferCallBackDfc(TAny* aMediaDriver)
+ {
+ DMmcMediaDriverFlash& md = *static_cast<DMmcMediaDriverFlash*>(aMediaDriver);
+
+ if (md.iDoPhysicalAddress)
+ {
+ if(md.CurrentRequest() == EMReqWrite)
+ {
+ md.DoPhysWriteDataTransferCallBack();
+ }
+ else
+ {
+ md.DoPhysReadDataTransferCallBack();
+ }
+ }
+ else
+ {
+ if(md.CurrentRequest() == EMReqWrite)
+ {
+ md.DoWriteDataTransferCallBack();
+ }
+ else
+ {
+ md.DoReadDataTransferCallBack();
+ }
+ }
+ }
+
+void DMmcMediaDriverFlash::DoPhysWriteDataTransferCallBack()
+ {
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("++DMmcMediaDriverFlash::DoPhysWriteDataTransferCallBack()"));
+
+ TInt err = KErrNone;
+
+ if ( (iRdROB & KIPCSetup) || ((iReqEnd - iPhysEnd) < iBlkLen) )
+ {
+ //IPC to be setup, or partial end block read
+ iRdROB &= ~KIPCSetup;
+
+ if ((iReqEnd - iPhysEnd) < iBlkLen)
+ {
+ iIntBuf = iCacheBuf;
+ }
+ else
+ {
+ TPtr8 tgt(iMinorBuf, iBlkLen);
+ err = ReadDataFromUser(tgt, I64LOW(iPhysEnd-iReqStart));
+ iIntBuf = iMinorBuf;
+ }
+
+ iReqCur = iPhysEnd;
+ iPhysEnd += iBlkLen;
+ iBufOfset = 0;
+ iIPCLen = iBlkLen;
+
+#if !defined(__WINS__)
+ iSession->MoreDataAvailable( (TInt)(iBlkLen >> KDiskSectorShift), (TUint8*)Epoc::LinearToPhysical((TLinAddr) iIntBuf), err);
+#else
+ iSession->MoreDataAvailable( (TInt)(iBlkLen >> KDiskSectorShift), iIntBuf, err);
+#endif
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("--iDoPhysicalAddress(KIPCSetup)"));
+ return;
+ }
+
+ PrepareNextPhysicalFragment();
+
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("--DMmcMediaDriverFlash::DoPhysWriteDataTransferCallBack()"));
+ }
+
+
+void DMmcMediaDriverFlash::DoPhysReadDataTransferCallBack()
+ {
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("++DMmcMediaDriverFlash::DoPhysReadTransferCallBack()"));
+
+ TInt err = KErrNone;
+
+ if ((iRdROB & KIPCWrite) && !iSecondBuffer)
+ {
+ // an IPC transfer completed
+ iRdROB &= ~KIPCWrite;
+ if(iNxtIPCLen)
+ {
+ // First transfer is an IPC,
+ // Corner-case - transfer is most likely IPC-DMA-IPC,
+ // because write cannot occur until after the first 2 iterations it is possible to arrive here with both IPCSetup & IPCWrite Set.
+ // need to use iIPCNxtLen instead
+ TPtrC8 extrView(&iIntBuf[iBufOfset], iNxtIPCLen);
+ err = iCurrentReq->WriteRemote(&extrView,I64LOW(iReqCur - iReqStart));
+ iNxtIPCLen = iBufOfset = 0;
+ }
+ else
+ {
+ TPtrC8 extrView(&iIntBuf[iBufOfset], iIPCLen);
+ err = iCurrentReq->WriteRemote(&extrView,I64LOW(iReqCur - iReqStart));
+ iIPCLen = iBufOfset = 0;
+ }
+ }
+
+ if ( (iRdROB & KIPCSetup) || ((iReqEnd - iPhysEnd) < iBlkLen) )
+ {
+ // IPC to be setup, or partial end block read.
+ iRdROB &= ~KIPCSetup;
+ iRdROB |= KIPCWrite;
+
+ iIntBuf = ReserveReadBlocks(iPhysEnd,(iPhysEnd+iBlkLen), &iIPCLen);
+
+ iReqCur = iPhysEnd;
+ iPhysEnd += iIPCLen;
+ iBufOfset = 0;
+#if !defined(__WINS__)
+ iSession->MoreDataAvailable( (TInt)(iIPCLen >> KDiskSectorShift), (TUint8*)Epoc::LinearToPhysical((TLinAddr) iIntBuf), err);
+#else
+ iSession->MoreDataAvailable( (TInt)(iIPCLen >> KDiskSectorShift), iIntBuf, err);
+#endif
+ iSecondBuffer = ETrue;
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("--iDoPhysicalAddress(KIPCWrite)"));
+ return;
+ }
+
+ PrepareNextPhysicalFragment();
+
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("--DMmcMediaDriverFlash::DoPhysReadTransferCallBack()"));
+ }
+
+void DMmcMediaDriverFlash::DoWriteDataTransferCallBack()
+ {
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("++DMmcMediaDriverFlash::DoWriteDataTransferCallBack()"));
+
+ TInt err = KErrNone;
+
+ // Advance current request progress...
+ iReqCur = iPhysEnd;
+
+ const TUint32 doubleBufferSize = iMaxBufSize >> 1;
+
+ TInt64 length = iDbEnd - iReqCur;
+ TInt64 medEnd = UMin(iReqCur + doubleBufferSize, iReqCur + length);
+
+ iPhysEnd = (medEnd + iBlkMsk) & ~iBlkMsk;
+ TInt64 len = UMin(iDbEnd, iPhysEnd) - iReqCur;
+
+ if(len > doubleBufferSize)
+ {
+ // Adjust for maximum size of double-buffering
+ len = doubleBufferSize;
+ }
+
+ __ASSERT_DEBUG(len > 0, Panic(EDBLength));
+ __ASSERT_DEBUG(I64HIGH((len + (KDiskSectorSize-1)) >> KDiskSectorShift) == 0, Panic(EDBLengthTooBig));
+
+ TUint32 numBlocks = I64LOW((len + (KDiskSectorSize-1)) >> KDiskSectorShift);
+
+ const TInt64 usrOfst = (iReqCur - iReqStart);
+
+ __ASSERT_DEBUG(I64HIGH(usrOfst) == 0, Panic(EDBOffsetTooBig));
+
+ // Setup the next buffer pointer and switch buffers...
+ TUint8* bufPtr = iIntBuf + (iSecondBuffer ? doubleBufferSize : 0);
+ TPtr8 tgt(bufPtr, I64LOW(len));
+ iSecondBuffer = iSecondBuffer ? (TBool)EFalse : (TBool)ETrue;
+
+ if(iDoLastRMW && length < doubleBufferSize)
+ {
+ //
+ // This is the last transfer, and RMW is required. The result of the read exists
+ // in iMinorBuf, so copy the non-modified section of the block to the active buffer.
+ //
+ memcpy(&bufPtr[(numBlocks-1) << KDiskSectorShift], iMinorBuf, KDiskSectorSize);
+ }
+
+ if(I64LOW(iDbEnd - iReqCur) <= iMaxBufSize)
+ {
+ //
+ // This is the last transfer (with or without RMW)
+ // - Mark the last blocks as active in the buffer cache.
+ //
+ MarkBlocks(iReqCur, iPhysEnd, CchMemToIdx(bufPtr));
+ }
+
+ //
+ // Read the requested data from the remote thread...
+ //
+ err = ReadDataFromUser(tgt, I64LOW(usrOfst));
+
+ //
+ // ...and signal that data is available to the PSL.
+ //
+ iSession->MoreDataAvailable(numBlocks, bufPtr, err);
+
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("--DMmcMediaDriverFlash::DoWriteDataTransferCallBack()"));
+ }
+
+
+void DMmcMediaDriverFlash::DoReadDataTransferCallBack()
+ {
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("++DMmcMediaDriverFlash::DoReadTransferCallBack()"));
+
+ TInt err = KErrNone;
+
+ const TUint32 doubleBufferSize = iMaxBufSize >> 1;
+
+ TUint32 bufOfst = 0;
+
+ if((iReqCur & ~iBlkMsk) == iPhysStart)
+ {
+ if(iSecondBuffer)
+ {
+ //
+ // If this is the first callback, don't copy data as it's not available yet
+ // - just drop through to set up the next buffer.
+ //
+ TUint32 numBlocks = I64LOW((doubleBufferSize + (KDiskSectorSize-1)) >> KDiskSectorShift);
+ TUint8* bufPtr = iIntBuf + doubleBufferSize;
+
+ iSecondBuffer = EFalse;
+
+ iSession->MoreDataAvailable(numBlocks, bufPtr, KErrNone);
+ return;
+ }
+ else
+ {
+ //
+ // If this is the second callback we're ready to copy
+ // back to the client - data may be mis-aligned in the first
+ // instance, but all subsequent data will be aligned...
+ //
+ bufOfst = I64LOW(iReqCur - iPhysStart);
+ }
+ }
+
+ // ...otherwise, write the previous buffer contents to the user
+ TUint8* bufPtr = iIntBuf + (iSecondBuffer ? doubleBufferSize : 0);
+
+ err = WriteDataToUser(bufPtr + bufOfst);
+
+ // Advance current request progress...
+ iReqCur = iPhysEnd;
+
+ TInt64 medEnd = UMin(iReqCur + doubleBufferSize, iDbEnd);
+
+ iPhysEnd = (medEnd + iBlkMsk) & ~iBlkMsk;
+
+ // Current buffer is one step ahead of the current request progress...
+ TInt64 len = UMin((iDbEnd - iPhysEnd + iBlkMsk) & ~iBlkMsk, TInt64(doubleBufferSize));
+
+ __ASSERT_DEBUG(len == 0 || (I64HIGH((len + (KDiskSectorSize-1)) >> KDiskSectorShift) == 0), Panic(EDBLengthTooBig));
+
+ TUint32 numBlocks = I64LOW((len + (KDiskSectorSize-1)) >> KDiskSectorShift);
+
+ //
+ // ...switch buffers and signal that data is available to the PSL.
+ //
+ iSecondBuffer = iSecondBuffer ? (TBool)EFalse : (TBool)ETrue;
+
+ iSession->MoreDataAvailable(numBlocks, bufPtr, err);
+
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("--DMmcMediaDriverFlash::DoDataTransferCallBack()"));
+ }
+
+
+// ---- request management ----
+
+
+TInt DMmcMediaDriverFlash::EngageAndSetReadRequest(DMmcMediaDriverFlash::TMediaRequest aRequest)
+ {
+ return EngageAndSetRequest(aRequest, iReadCurrentInMilliAmps);
+ }
+
+
+TInt DMmcMediaDriverFlash::EngageAndSetWriteRequest(DMmcMediaDriverFlash::TMediaRequest aRequest)
+ {
+ return EngageAndSetRequest(aRequest, iWriteCurrentInMilliAmps);
+ }
+
+
+TInt DMmcMediaDriverFlash::EngageAndSetRequest(DMmcMediaDriverFlash::TMediaRequest aRequest, TInt aCurrent)
+//
+// In WINS, all of the processing, including the callbacks, is done when Engage() is called,
+// so the request value must be set up in advanced. Both the request and the current are
+// cleared in the corresponding call to CompleteRequest().
+//
+ {
+ __ASSERT_DEBUG(iSession != NULL, Panic(ECFSessPtrNull));
+
+ iMedReq = aRequest;
+ SetCurrentConsumption(aCurrent);
+
+ TInt r = InCritical();
+ if (r == KErrNone)
+ {
+ r = iSession->Engage();
+ }
+
+ if(r != KErrNone)
+ {
+ if (!Kern::PowerGood())
+ r=KErrAbort; // If emergency power down - return abort rather than anything else.
+ if (!iCard->IsReady())
+ r=KErrNotReady; // If media change - return not ready rather than anything else.
+ EndInCritical();
+ }
+
+ return r;
+ }
+
+
+void DMmcMediaDriverFlash::CompleteRequest(TInt aReason)
+//
+// completes the specified request
+//
+ {
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("=mmd:cr0x%08x,%d", iCurrentReq, aReason));
+
+ iMedReq = EMReqIdle;
+ SetCurrentConsumption(KIdleCurrentInMilliAmps);
+
+ TLocDrvRequest* pR=iCurrentReq;
+ if (pR)
+ {
+#ifdef __DEMAND_PAGING__
+#if defined(__TEST_PAGING_MEDIA_DRIVER__)
+ __KTRACE_OPT(KLOCDPAGING,Kern::Printf("DMediaDriverFlash::Complete req Id(%d) with(%d)", pR->Id(), aReason));
+#endif // __TEST_PAGING_MEDIA_DRIVER__
+#endif // __DEMAND_PAGING__
+ iCurrentReq=NULL;
+ DMediaDriver::Complete(*pR,aReason);
+ }
+ }
+
+TInt DMmcMediaDriverFlash::Caps(TLocDrv& aDrive, TLocalDriveCapsV6& aInfo)
+ {
+ // Fill buffer with current media caps.
+ aInfo.iType = EMediaHardDisk;
+ aInfo.iBattery = EBatNotSupported;
+ aInfo.iDriveAtt = KDriveAttLocal;
+ aInfo.iMediaAtt = KMediaAttFormattable;
+
+ if(iCard->iFlags & KMMCardIsLockable)
+ aInfo.iMediaAtt |= KMediaAttLockable;
+
+ if (iCard->HasPassword())
+ aInfo.iMediaAtt |= KMediaAttHasPassword;
+ if (iCard->IsWriteProtected())
+ aInfo.iMediaAtt |= KMediaAttWriteProtected;
+ if (iCard->IsLocked())
+ aInfo.iMediaAtt |= KMediaAttLocked;
+
+ aInfo.iFileSystemId = KDriveFileSysFAT;
+
+ // Format is performed in multiples of the erase sector (or multiple block) size
+ aInfo.iMaxBytesPerFormat = iEraseInfo.iPreferredEraseUnitSize;
+
+ if ((!iInternalSlot) && (GetCardFormatInfo(iCard,aInfo.iFormatInfo) == KErrNone))
+ {
+ TUint16 reservedSectors;
+ TMBRPartitionEntry dummy; // Not used here
+ const TInt r = GetMediaDefaultPartitionInfo(dummy, reservedSectors, iCard);
+ if(r != KErrNone)
+ return r;
+
+ aInfo.iFormatInfo.iReservedSectors = reservedSectors;
+ aInfo.iExtraInfo = ETrue;
+ }
+
+ // Set serial number to CID
+ __ASSERT_DEBUG(KMMCCIDLength<=KMaxSerialNumLength, Kern::PanicCurrentThread(_L("Mmc"), KErrOverflow));
+ aInfo.iSerialNumLength = KMMCCIDLength;
+ for (TUint i=0; i<KMMCCIDLength; i++)
+ aInfo.iSerialNum[i] = iCard->CID().At(i);
+
+ // Get block size & erase block size to allow the file system to align first usable cluster correctly
+ aInfo.iBlockSize = BlockSize(iCard);
+ aInfo.iEraseBlockSize = EraseBlockSize(iCard);
+
+#if defined(__DEMAND_PAGING__)
+ // If the stack has flagged this as a demand-paging device, then it is assumed that it is internal
+ // and (optionally) write protected.
+ if(aDrive.iPrimaryMedia->iPagingMedia)
+ {
+ aInfo.iMediaAtt|= KMediaAttPageable;
+ if (iDemandPagingInfo.iWriteProtected)
+ {
+ aInfo.iMediaAtt|= KMediaAttWriteProtected;
+ aInfo.iMediaAtt&= ~KMediaAttFormattable;
+ }
+ }
+
+ // code paging enabled on this drive ?
+ if(aDrive.iPagingDrv)
+ {
+ aInfo.iDriveAtt|= KDriveAttPageable;
+ }
+
+#endif
+
+ if (iInternalSlot)
+ {
+ aInfo.iDriveAtt|= KDriveAttInternal;
+ }
+ else
+ {
+ aInfo.iDriveAtt|= KDriveAttRemovable;
+ }
+
+
+ if (iMmcPartitionInfo)
+ {
+ TLocalDriveCapsV6Buf CapsInfo = aInfo;
+ iMmcPartitionInfo->PartitionCaps(aDrive,CapsInfo);
+ aInfo = CapsInfo();
+ }
+
+
+ if (iMediaType==EMultiMediaROM)
+ {
+ aInfo.iMediaAtt|= KMediaAttWriteProtected;
+ aInfo.iMediaAtt&= ~KMediaAttFormattable;
+ }
+
+ // Must return KErrCompletion to indicate that this
+ // is a synchronous version of the function
+ return KErrCompletion;
+ }
+
+
+// ---- cache ----
+
+TInt DMmcMediaDriverFlash::ReadDataUntilCacheExhausted(TBool* aAllDone)
+//
+// scans the cache for blocks corresponding to the range iReqCur to iReqEnd and
+// writes them to user memory. Starts at iReqCur & ~iBlkMsk and looks for blocks
+// at sequential media positions. Completes when a block is not available, even
+// if a following block is available in the cache. *aAllDone is undefined if the
+// return value is not KErrNone.
+//
+// This function is linear in the number of blocks in the cache.
+//
+ {
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf(">mmd:rdc:%x,%x", iReqCur, iReqEnd));
+
+#if defined(__DEMAND_PAGING__) && !defined(__WINS__)
+ if (DMediaPagingDevice::PageInRequest(*iCurrentReq))
+ {
+ *aAllDone = EFalse;
+ return KErrNone;
+ }
+#endif //DEMAND_PAGING
+
+ TInt64 physStart = iReqCur & ~iBlkMsk;
+ TInt64 physEnd = Min(physStart + iMaxBufSize, (iReqEnd + iBlkMsk) & ~iBlkMsk);
+ BuildGammaArray(physStart, physEnd);
+
+ TInt r = KErrNone;
+ TInt curBlk = 0;
+ TInt cchBlk;
+ while (
+ r == KErrNone
+ && physStart + (curBlk << iBlkLenLog2) < physEnd
+ && (cchBlk = iGamma[curBlk]) != KNoCacheBlock )
+ {
+ // set up instance variables for WriteDataToUser()
+ iPhysStart = physStart + (curBlk << iBlkLenLog2);
+ iPhysEnd = iPhysStart + iBlkLen;
+ iIntBuf = IdxToCchMem(cchBlk);
+
+ if ((r = WriteDataToUser(&iIntBuf[I64LOW(iReqCur - iPhysStart)])) == KErrNone)
+ {
+ iReqCur = iPhysEnd;
+ iLstUsdCchEnt = iGamma[curBlk];
+ ++curBlk;
+ }
+ }
+
+ *aAllDone = (iReqCur >= iReqEnd);
+
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("<mmd:rdc:%d,%d", *aAllDone, r));
+ return r;
+ }
+
+
+TInt DMmcMediaDriverFlash::WriteDataToUser(TUint8* aBufPtr)
+//
+// write the data from the most recent read operation to the user descriptor
+//
+ {
+ TInt r = KErrNotSupported;
+
+ // get range of data to read out of internal buffer
+
+ TInt len = I64LOW(UMin(iPhysEnd, iReqEnd) - iReqCur);
+ TPtrC8 extrView(aBufPtr, len);
+
+ // write data from internal buffer
+ TUint usrOfst = I64LOW(iReqCur - iReqStart);
+
+#if defined(__DEMAND_PAGING__) && !defined(__WINS__)
+ if (DMediaPagingDevice::PageInRequest(*iCurrentReq))
+ r=iCurrentReq->WriteToPageHandler((TUint8 *)(&extrView[0]), len, usrOfst);
+ else
+#endif // __DEMAND_PAGING__
+ r = iCurrentReq->WriteRemote(&extrView,usrOfst);
+
+ return r;
+ }
+
+TInt DMmcMediaDriverFlash::ReadDataFromUser(TDes8& aDes, TInt aOffset)
+ {
+#ifndef __WINS__
+ if (DMediaPagingDevice::PageOutRequest(*iCurrentReq))
+ return iCurrentReq->ReadFromPageHandler((TAny*) aDes.Ptr(), aDes.MaxLength(), aOffset);
+ else
+#endif // #ifndef __WINS__
+ return iCurrentReq->ReadRemote(&aDes, aOffset);
+ }
+
+TInt DMmcMediaDriverFlash::AdjustPhysicalFragment(TPhysAddr &aPhysAddr, TInt &aPhysLength)
+//
+// Retrieve next Physical memory fragment and adjust the start pointer and length with
+// respect to the set offset {iFragOfset}.
+// Note the offset may encompass multiple memory fragments.
+//
+ {
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf(">mmd:APF"));
+
+ TInt err = KErrNone;
+ TInt offset = iFragOfset;
+
+ do
+ {
+ err = iCurrentReq->GetNextPhysicalAddress(aPhysAddr, aPhysLength);
+
+ if (err != KErrNone)
+ return err;
+
+ if (offset >= aPhysLength) // more offset than in this physical chunk
+ {
+ offset -= aPhysLength;
+ }
+ else
+ {
+ // offset < physLength
+ // offset lies within the memory chunk
+ // Adjust length and address for first transfer
+ aPhysLength -= offset;
+ aPhysAddr += offset;
+ offset = -1;
+ }
+
+ } while (offset >= 0);
+
+ iFragOfset = 0; // reset offset now complete
+
+ if (aPhysAddr == 0)
+ {
+ return KErrNoMemory;
+ }
+
+#ifdef _DEBUG
+ // DMAHelper ensures memory is dma aligned
+ if ( (aPhysAddr & (iSocket->DmaAlignment()-1) ) )
+ {
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("mmd:lr:Memory Fragment Not Word Aligned!"));
+ Panic(ENotDMAAligned);
+ }
+#endif //_DEBUG
+
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("<mmd:APF physAddr(0x%x), physLength(%d)",aPhysAddr, aPhysLength));
+ return err;
+ }
+
+TInt DMmcMediaDriverFlash::PrepareFirstPhysicalFragment(TPhysAddr &aPhysAddr, TInt &aPhysLength, TUint32 aLength)
+//
+// Retrieves the first Physical memory fragment and determines the type of the next transfer
+// Next transfer may either be the last block (end not block aligned) or a block may straddle
+// memory fragments.
+//
+ {
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf(">mmd:PFPF"));
+ TInt r = KErrNone;
+
+ r = AdjustPhysicalFragment(aPhysAddr, aPhysLength);
+
+ if (r == KErrNone)
+ {
+ TUint len = I64LOW(iReqEnd & iBlkMsk);
+ if ( ((TUint32)aPhysLength >= aLength) && len )
+ {
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf(">mmd:PFPF-end block"));
+ //next iteration will be an IPC for the end block
+ //There is enough space in physical memory to fit
+ //the extended read, but exceeds boundary for this request.
+ iIPCLen = len;
+ iRdROB |= KIPCSetup; // IPC setup for next iteration
+ aPhysLength -= len;
+ }
+
+ if (aPhysLength & iBlkMsk)
+ {
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf(">mmd:PFPF-straddles boundary"));
+ // block must be straddling a fragment boundary
+ // Next iteration must be an IPC
+ iRdROB |= KIPCSetup;
+
+ // Calculate the offset into the next memory block
+ iFragOfset = I64LOW(iBlkLen - (aPhysLength & iBlkMsk));
+ aPhysLength &= ~iBlkMsk;
+ }
+ }
+
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("<mmd:PFPF err(%d), physAddr(0x%x), physLength(%d)",r, aPhysAddr, aPhysLength));
+ return r;
+ }
+
+
+void DMmcMediaDriverFlash::PrepareNextPhysicalFragment()
+//
+// Retrieves next Physical memory fragment and determines the type of the next transfer
+// Next transfer may either be the last block (end not block aligned) or a block may straddle
+// memory fragments.
+//
+ {
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf(">mmd:PNPF"));
+ TInt err = KErrNone;
+ TPhysAddr physAddr = 0;
+ TInt physLength = 0;
+
+ err = AdjustPhysicalFragment(physAddr, physLength);
+
+ if (err == KErrNone)
+ {
+ if (iPhysEnd+physLength >= iReqEnd)
+ {
+ //Last physical transfer ...
+ TUint len = I64LOW(iReqEnd & iBlkMsk);
+ if (len)
+ {
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf(">mmd:PNPF-end block"));
+ // end point not block aligned!
+ // next iteration must be an IPC call
+ iRdROB |= KIPCSetup;
+ iIPCLen = len;
+ physLength -= len;
+ }
+ else{
+ physLength = I64LOW(iDbEnd - iPhysEnd);
+ }
+ }
+
+ if (physLength & iBlkMsk)
+ {
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf(">mmd:PNPF-straddles boundary"));
+ // block must be straddling a fragment boundary
+ // Next iteration must be an IPC
+ iRdROB |= KIPCSetup;
+
+ // Calculate the offset into the next memory block
+ iFragOfset = I64LOW(iBlkLen - (physLength & iBlkMsk));
+ physLength &= ~iBlkMsk;
+ }
+
+ iPhysEnd += physLength;
+ }
+
+ iSession->MoreDataAvailable( (physLength >> KDiskSectorShift), (TUint8*) physAddr, err);
+ iSecondBuffer = EFalse;
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("<mmd:PNPF"));
+ }
+
+TUint8* DMmcMediaDriverFlash::ReserveReadBlocks(TInt64 aStart, TInt64 aEnd, TUint32* aLength)
+//
+// Assume the cache has been drained before this function is called and so
+// the first block is not in the cache. The length of the allocated range is
+// either aEnd - aStart, or enough blocks such that the next block to read
+// is already available in the cache, and so will be read when
+// ReadDataUntilCacheExhausted() is called from the callback DFC.
+//
+ {
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf(">mmd:rrb:%lx,%lx", aStart, aEnd));
+
+ __ASSERT_DEBUG((aStart & iBlkMsk) == 0, Panic(ERRBStAlign));
+ __ASSERT_DEBUG(TotalSizeInBytes() > aStart, Panic(ERRBStPos));
+ __ASSERT_DEBUG(aEnd > aStart, Panic(ERRBNotPositive));
+ __ASSERT_DEBUG((aEnd & iBlkMsk) == 0, Panic(ERRBEndAlign));
+ __ASSERT_DEBUG(TotalSizeInBytes() >= aEnd, Panic(ERRBEndPos));
+ __ASSERT_DEBUG(!iDoDoubleBuffer, Panic(ENoDBSupport));
+ __ASSERT_CACHE(CacheInvariant(), Panic(ERRBCchInv));
+ __ASSERT_CACHE(GetCachedBlock(aStart & ~iBlkMsk) == 0, Panic(ERRBExist));
+
+ TUint8* raby;
+
+ BuildGammaArray(aStart, aEnd);
+
+ // reposition start index at 0 if the full range would run off the end of the
+ // buffer. This is heuristic - enabling a longer multi-block may cost some
+ // cached reads. However, assume long reads do not generally re-read the same
+ // data, and are used for streaming large amounts of data into memory.
+
+ const TInt blocksInRange = I64LOW((aEnd - aStart) >> iBlkLenLog2);
+ TInt startIndex = (iLstUsdCchEnt + 1) % iBlocksInBuffer;
+ if (startIndex + blocksInRange > iBlocksInBuffer)
+ startIndex = 0;
+
+ // starting at startIndex, increase the range until it covers aEnd - aStart,
+ // or until the next block to read is available in the cache.
+
+ TInt blkCnt = 0;
+ TBool finished;
+ do
+ {
+ finished = (
+ // range allocated for entire read
+ blkCnt == blocksInRange
+ // next block already exists in buffer and has not been overwritten
+ // by existing multi-block read
+ || ( iGamma[blkCnt] != KNoCacheBlock
+ && ( iGamma[blkCnt] < startIndex
+ || iGamma[blkCnt] >= startIndex + blkCnt ) ) );
+
+ if (! finished)
+ ++blkCnt;
+ } while (! finished);
+
+ iLstUsdCchEnt = startIndex + blkCnt - 1;
+
+ if (blkCnt < 1) blkCnt = 1; //RBW required < 1 block to be read
+
+ TUint32 lengthInBytes = blkCnt << iBlkLenLog2;
+ *aLength = lengthInBytes;
+ MarkBlocks(aStart, aStart + lengthInBytes, startIndex);
+
+ raby = IdxToCchMem(startIndex);
+
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("<mmd:rrb:%x", (TUint32) raby));
+
+ return raby;
+ }
+
+
+TUint8* DMmcMediaDriverFlash::ReserveWriteBlocks(TInt64 aStart, TInt64 aEnd, TUint* aRBM)
+//
+// reserve a range of blocks in the buffer. If the block containing aStart or aEnd
+// are already in the buffer, attempts to position on them. This can save one or two
+// RBMs for writes.
+//
+// This function is linear in the number of blocks - it runs through the array
+// exactly twice.
+//
+// aStart and aEnd are not necessarily block aligned - the function uses alignment
+// information to minimize RBMs.
+//
+ {
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf(">mmd:rwb:%lx,%lx", aStart, aEnd));
+
+ TInt64 physStart = aStart & ~iBlkMsk;
+ TInt64 physEnd = (aEnd + iBlkMsk) & ~iBlkMsk;
+
+ __ASSERT_DEBUG(TotalSizeInBytes() > physStart, Panic(ERWBStPos));
+ __ASSERT_DEBUG(aEnd > aStart, Panic(ERWBNotPositive));
+ __ASSERT_DEBUG(TotalSizeInBytes() >= physEnd, Panic(ERWBEndPos));
+ __ASSERT_DEBUG(iDoPhysicalAddress || iDoDoubleBuffer || (!iDoDoubleBuffer && !iDoPhysicalAddress && physEnd - physStart <= (TInt64)iMaxBufSize), Panic(ERWBOverflow));
+ __ASSERT_CACHE(CacheInvariant(), Panic(ERWBCchInv));
+
+ const TBool firstPartial = (aStart & iBlkMsk) != 0;
+ const TBool lastPartial = (aEnd & iBlkMsk) != 0;
+
+ const TInt blkCnt = I64LOW((physEnd - physStart) >> iBlkLenLog2);
+
+ TBool startUsed = EFalse;
+ TBool endUsed = EFalse;
+
+ TUint8* raby = NULL;
+
+ if(iDoDoubleBuffer)
+ {
+ //
+ // If we're double-buffering, then the entire cache will be re-used
+ // continuously. Rather than continually reserve blocks during each
+ // transfer we calculate the blocks that will be present after all
+ // transfers have completed.
+ //
+ InvalidateCache();
+ raby = iCacheBuf;
+ }
+ else
+ {
+ TInt idx;
+
+ // check if the first or last blocks are already in the buffer.
+ TInt fst = -1, lst = -1;
+ const TInt64 lstBlk = physEnd - iBlkLen;
+ TInt i;
+ for (i = 0; i < iBlocksInBuffer; ++i)
+ {
+ if (iCachedBlocks[i] == physStart)
+ fst = i;
+
+ if (iCachedBlocks[i] == lstBlk)
+ lst = i;
+ }
+
+ const TBool firstUsable = (fst != -1) && (iBlocksInBuffer - fst) >= blkCnt;
+ const TBool lastUsable = (lst != -1) && lst >= (blkCnt - 1);
+
+ if (iDoPhysicalAddress)
+ {
+ if ( (firstPartial || lastPartial) && blkCnt <= 2)
+ {
+ //Physical addressing not to be used.
+ //more efficent to use local Cache copying
+ iDoPhysicalAddress = EFalse;
+ return raby;
+ }
+ else
+ {
+ raby = iMinorBuf;
+
+ const TBool firstPres = (fst != -1);
+ const TBool lastPres = (lst != -1);
+
+ if (firstPartial && firstPres)
+ {
+ // move to minor buffer
+ memcpy(iMinorBuf, IdxToCchMem(fst), iBlkLen);
+ }
+ if (lastPartial && lastPres)
+ {
+ // move to beginning of cache
+ memcpy(iCacheBuf, IdxToCchMem(lst), iBlkLen);
+ }
+
+ InvalidateCache(physStart,physEnd);
+
+ if (lastPartial)
+ {
+ //re-mark beginning of cache
+ MarkBlocks((physEnd-iBlkLen), physEnd, 0);
+ }
+
+ if (aRBM)
+ {
+ *aRBM = 0;
+
+ if (firstPartial)
+ *aRBM |= KWtMinFst;
+
+ if (firstPartial && !firstPres)
+ *aRBM |= KWtRBMFst;
+
+ if (lastPartial)
+ *aRBM |= KWtMinLst;
+
+ if (lastPartial && !lastPres)
+ *aRBM |= KWtRBMLst;
+ }
+
+ return raby;
+ }
+ } // if (iDoPhysicalAddress)
+
+ if (!firstUsable && !lastUsable)
+ {
+ if(iDoDoubleBuffer)
+ {
+ idx = iSecondBuffer ? iBlocksInBuffer >> 1 : 0;
+ }
+ else
+ {
+ idx = (iLstUsdCchEnt + 1) % iBlocksInBuffer;
+ if (idx + blkCnt > iBlocksInBuffer)
+ idx = 0;
+ }
+ }
+ else if (firstUsable && ! lastUsable)
+ {
+ idx = fst;
+ }
+ else if (! firstUsable && lastUsable)
+ {
+ idx = lst - (blkCnt - 1);
+ }
+ else // (lastUsable && firstUsable)
+ {
+ if (firstPartial || ! lastPartial)
+ idx = fst;
+ else
+ idx = lst - (blkCnt - 1);
+ }
+
+ MarkBlocks(physStart, physEnd, idx);
+
+ // if the range started or ended on a partial block, but could not
+ // be allocated on that existing block, and the existing block is
+ // somewhere in the cache, then memcpy() that block to the end of the
+ // range. used is not the same as usable - both the start and end
+ // blocks may be usable, through not in the same range, or any range.
+
+ const TInt startExtent = I64LOW(aStart & iBlkMsk);
+ TBool firstInTemp = EFalse;
+ startUsed = (idx == fst);
+ if (! startUsed && firstPartial && fst != -1)
+ {
+ // if the range has started at index occupied by the last block then
+ // temporarily copy to minor buffer. This is unnecessary when the
+ // last block is not partial because the last block does not need to
+ // be preserved.
+
+ if (idx == lst && lastPartial)
+ {
+ firstInTemp = ETrue;
+ memcpy(iMinorBuf, IdxToCchMem(fst), startExtent);
+ }
+ else
+ {
+ memcpy(IdxToCchMem(idx), IdxToCchMem(fst), startExtent);
+ }
+
+ startUsed = ETrue;
+ }
+
+ endUsed = (idx + blkCnt - 1 == lst);
+ if (! endUsed && lastPartial && lst != -1)
+ {
+ const TInt endOffset = I64LOW(aEnd & iBlkMsk);
+ const TInt endExtent = iBlkLen - endOffset;
+ memcpy(IdxToCchMem(idx + blkCnt - 1) + endOffset, IdxToCchMem(lst) + endOffset, endExtent);
+ endUsed = ETrue;
+ }
+
+ if (firstInTemp)
+ memcpy(IdxToCchMem(idx), iMinorBuf, startExtent);
+
+ // start reclaiming at block following this range
+ iLstUsdCchEnt = idx + blkCnt - 1;
+ raby = IdxToCchMem(idx);
+ }
+
+ // work out if read-before-write required
+ if (aRBM)
+ {
+ *aRBM = 0;
+ // first index was not already in range, and does not start on block boundary
+ if (firstPartial && ! startUsed)
+ *aRBM |= KWtRBMFst;
+
+ // last index was not already in range, and does not end on block boundary
+ if (lastPartial && ! endUsed)
+ *aRBM |= KWtRBMLst;
+
+ // only use one pre-read if contained in single block
+ if (blkCnt == 1 && *aRBM == (KWtRBMFst | KWtRBMLst))
+ *aRBM = KWtRBMFst;
+
+ //
+ // When double-buffering, RMW for the last block is stored in the
+ // minor buffer and writen during the last transfer, so flag this
+ // seperately (as aRBM is used for the initial RMW Read then subsequently cleared).
+ //
+ if(iDoDoubleBuffer && (*aRBM & KWtRBMLst))
+ iDoLastRMW = ETrue;
+ }
+
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("<mmd:rwb:%x", (TUint32) raby));
+
+ return raby;
+ }
+
+void DMmcMediaDriverFlash::MarkBlocks(TInt64 aStart, TInt64 aEnd, TInt aStartIndex)
+//
+// mark range of blocks for media range. If existing cache entries for any part of
+// the cache are already in the buffer they are invalidated.
+//
+ {
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("=mmd:mb:%lx,%lx,%d", aStart, aEnd, aStartIndex));
+
+ __ASSERT_DEBUG(TotalSizeInBytes() > aStart, Panic(EMBStPos));
+ __ASSERT_DEBUG((aStart & iBlkMsk) == 0, Panic(EMBStAlign));
+ __ASSERT_DEBUG(aEnd > aStart, Panic(EMBNotPositive));
+ __ASSERT_DEBUG(TotalSizeInBytes() >= aEnd, Panic(EMBEndPos));
+ __ASSERT_DEBUG((aEnd & iBlkMsk) == 0, Panic(EMBEndAlign));
+ __ASSERT_DEBUG(aStartIndex + (TInt)((aEnd - aStart) >> iBlkLenLog2) <= iBlocksInBuffer, Panic(EMBOverflow));
+ __ASSERT_CACHE(CacheInvariant(), Panic(EMBCchInvPre));
+
+ TInt i;
+
+ for (i = 0; i < aStartIndex; ++i)
+ {
+ if (iCachedBlocks[i] >= aStart && iCachedBlocks[i] < aEnd)
+ iCachedBlocks[i] = KInvalidBlock;
+ }
+
+ TInt blkCnt = I64LOW((aEnd - aStart) >> iBlkLenLog2);
+ for (i = aStartIndex; i < aStartIndex + blkCnt; ++i)
+ iCachedBlocks[i] = aStart + (static_cast<TUint32>(i - aStartIndex) << iBlkLenLog2);
+
+ for (i = aStartIndex + blkCnt; i < iBlocksInBuffer; ++i)
+ {
+ if (iCachedBlocks[i] >= aStart && iCachedBlocks[i] < aEnd)
+ iCachedBlocks[i] = KInvalidBlock;
+ }
+
+ __ASSERT_CACHE(CacheInvariant(), Panic(EMBCchInvPost));
+ }
+
+
+void DMmcMediaDriverFlash::BuildGammaArray(TInt64 aStart, TInt64 aEnd)
+//
+// iGamma is an array of indexes that correspond to cached blocks starting
+// from aStart. iGamma[0] is the index of aStart, iGamma[1] is the index of
+// aStart + iBlkLen, and so on. Building an array here means that all of
+// the available cached entries can be found in linear time instead of
+// quadratically searching through the array for each block.
+//
+ {
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("=mmd:bga:%lx,%lx", aStart, aEnd));
+
+ __ASSERT_DEBUG(TotalSizeInBytes() > aStart, Panic(EBGAStPos));
+ __ASSERT_DEBUG((aStart & iBlkMsk) == 0, Panic(EBGAStAlign));
+ __ASSERT_DEBUG(aEnd > aStart, Panic(EBGANotPositive));
+ __ASSERT_DEBUG(TotalSizeInBytes() >= aEnd, Panic(EBGAEndPos));
+ __ASSERT_DEBUG((aEnd & iBlkMsk) == 0, Panic(EBGAEndAlign));
+ __ASSERT_DEBUG(aEnd - aStart <= (TInt64) iMaxBufSize, Panic(EBGAOverflow));
+ __ASSERT_CACHE(CacheInvariant(), Panic(EBGACchInv));
+
+ // KNoCacheBlock = (0xff) x 4
+ TUint blocksInRange = I64LOW((aEnd - aStart) >> iBlkLenLog2);
+ memset(iGamma, 0xff, sizeof(*iGamma) * blocksInRange);
+
+ TInt64 blkAddr = 0;
+ for (TInt i = 0; ( (blocksInRange > 0 ) && (i < iBlocksInBuffer) ); ++i)
+ {
+ blkAddr = iCachedBlocks[i];
+ if (blkAddr >= aStart && blkAddr < aEnd)
+ {
+ iGamma[I64LOW((blkAddr - aStart) >> iBlkLenLog2)] = i;
+ blocksInRange--;
+ }
+ }
+ }
+
+void DMmcMediaDriverFlash::InvalidateCache()
+ {
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("=mmd:ich"));
+
+ // KInvalidBlock = (0xff) x 4
+ memset(iCachedBlocks, 0xff, sizeof(*iCachedBlocks) * iBlocksInBuffer);
+ }
+
+// Invalidate any cache entries from aStart to aEnd
+// This is for DMA writes and is to prevent the cache becoming inconsistent with the media.
+void DMmcMediaDriverFlash::InvalidateCache(TInt64 aStart, TInt64 aEnd)
+ {
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("=mmd:ich:%lx,%lx", aStart, aEnd));
+
+ __ASSERT_DEBUG(TotalSizeInBytes() > aStart, Panic(EBGAStPos));
+ __ASSERT_DEBUG(aEnd > aStart, Panic(EBGANotPositive));
+ __ASSERT_DEBUG(TotalSizeInBytes() >= aEnd, Panic(EBGAEndPos));
+
+ const TInt blkCnt = I64LOW((aStart - aEnd) >> iBlkLenLog2);
+
+ __ASSERT_CACHE(CacheInvariant(), Panic(EBGACchInv));
+
+ TInt64 endBlk = (blkCnt == 0) ? (aStart+iBlkLen) : aEnd;
+
+ for (TInt i = 0; i < iBlocksInBuffer; ++i)
+ {
+ const TInt64 blkAddr = iCachedBlocks[i];
+ if (blkAddr >= aStart && blkAddr < endBlk)
+ iCachedBlocks[i] = KInvalidBlock;
+ }
+ }
+
+TUint8* DMmcMediaDriverFlash::IdxToCchMem(TInt aIdx) const
+ {
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("=mmd:icm:%d", aIdx));
+
+ __ASSERT_DEBUG(aIdx >= 0, Panic(EICMNegative));
+ __ASSERT_DEBUG(aIdx < iBlocksInBuffer, Panic(EICMOverflow));
+
+ return &iCacheBuf[aIdx << iBlkLenLog2];
+ }
+
+TInt DMmcMediaDriverFlash::CchMemToIdx(TUint8* aMemP) const
+ {
+ __ASSERT_DEBUG((aMemP >= iCacheBuf) && (aMemP < iCacheBuf + (iBlocksInBuffer << iBlkLenLog2)), Panic(ECMIOverflow));
+
+ return((aMemP - iCacheBuf) >> iBlkLenLog2);
+ }
+
+#ifdef _DEBUG_CACHE
+TBool DMmcMediaDriverFlash::CacheInvariant()
+//
+// check each cache entry refers to a valid block and that no two
+// entries cover the same block. This algorithm is quadratic in
+// the cache length.
+//
+ {
+ for (TInt i = 0; i < iBlocksInBuffer; ++i)
+ {
+ if (iCachedBlocks[i] == KInvalidBlock)
+ continue;
+
+ if ((iCachedBlocks[i] & iBlkMsk) != 0)
+ return EFalse;
+
+ if (iCachedBlocks[i] >= TotalSizeInBytes())
+ return EFalse;
+
+ for (TInt j = i + 1; j < iBlocksInBuffer; ++j)
+ {
+ if (iCachedBlocks[i] == iCachedBlocks[j])
+ return EFalse;
+ }
+ }
+
+ return ETrue;
+ }
+#endif
+
+void DMmcMediaDriverFlash::NotifyPowerDown()
+ {
+ __KTRACE_OPT(KPBUSDRV,Kern::Printf(">Mmc:NotifyPowerDown"));
+
+ iSessionEndDfc.Cancel();
+ iDataTransferCallBackDfc.Cancel();
+
+ EndInCritical();
+
+ // need to cancel the session as the stack doesn't take too kindly to having the same session engaged more than once.
+ if (iSession)
+ iStack->CancelSession(iSession);
+
+ CompleteRequest(KErrNotReady);
+ iMedReq = EMReqIdle;
+ }
+
+void DMmcMediaDriverFlash::NotifyEmergencyPowerDown()
+ {
+ __KTRACE_OPT(KPBUSDRV,Kern::Printf(">Ata:NotifyEmergencyPowerDown"));
+
+ iSessionEndDfc.Cancel();
+ iDataTransferCallBackDfc.Cancel();
+
+ TInt r=KErrNotReady;
+ if (iCritical)
+ r=KErrAbort;
+ EndInCritical();
+
+ // need to cancel the session as the stack doesn't take too kindly to having the same session engaged more than once.
+ if (iSession)
+ iStack->CancelSession(iSession);
+
+ CompleteRequest(r);
+ iMedReq = EMReqIdle;
+ }
+
+TInt DMmcMediaDriverFlash::Request(TLocDrvRequest& aRequest)
+ {
+ __KTRACE_OPT(KLOCDRV,Kern::Printf("MmcMd:Req %08x id %d",&aRequest,aRequest.Id()));
+ TInt r=KErrNotSupported;
+ TInt id=aRequest.Id();
+
+#if defined (__TEST_PAGING_MEDIA_DRIVER__)
+ DThread* client=aRequest.Client();
+ __KTRACE_OPT(KLOCDPAGING,Kern::Printf("Client:0x%08x",client));
+#endif // __TEST_PAGING_MEDIA_DRIVER__
+
+ // First handle requests that can be handled without deferring
+ if(id==DLocalDrive::ECaps)
+ {
+ TLocalDriveCapsV6& c = *(TLocalDriveCapsV6*)aRequest.RemoteDes();
+ TLocDrv& drive = *aRequest.Drive();
+ r = Caps(drive, c);
+ c.iSize = drive.iPartitionLen;
+ c.iPartitionType = drive.iPartitionType;
+ c.iHiddenSectors = (TUint) (drive.iPartitionBaseAddr >> KDiskSectorShift);
+ return r;
+ }
+
+ // All other requests must be deferred if a request is currently in progress
+ if (iCurrentReq)
+ {
+
+#if defined(__TEST_PAGING_MEDIA_DRIVER__)
+ if (DMediaPagingDevice::PageInRequest(*iCurrentReq))
+ iMmcStats.iReqPage++;
+ else
+ iMmcStats.iReqNormal++;
+#endif // __TEST_PAGING_MEDIA_DRIVER__
+
+ // a request is already in progress, so hold on to this one
+ __KTRACE_OPT(KLOCDRV,Kern::Printf("MmcMd:Req %08x ret 1",&aRequest));
+ return KMediaDriverDeferRequest;
+ }
+ else
+ {
+ iCurrentReq=&aRequest;
+ TUint partitionType = iCurrentReq->Drive()->iPartitionType;
+ TBool readOnly = (partitionType == KPartitionTypeRofs || partitionType == KPartitionTypeROM);
+
+ switch (id)
+ {
+
+
+#if defined(__DEMAND_PAGING__)
+ case DMediaPagingDevice::ERomPageInRequest:
+ __KTRACE_OPT(KLOCDPAGING,Kern::Printf("DMediaDriverFlash::Request(ERomPageInRequest)"));
+ BTraceContext8(BTrace::EPagingMedia,BTrace::EPagingMediaPagingMedDrvBegin,MEDIA_DEVICE_MMC,iCurrentReq);
+ r=DoRead();
+ break;
+ case DMediaPagingDevice::ECodePageInRequest:
+ __KTRACE_OPT(KLOCDPAGING,Kern::Printf("DMediaDriverFlash::Request(ECodePageInRequest)"));
+ BTraceContext8(BTrace::EPagingMedia,BTrace::EPagingMediaPagingMedDrvBegin,MEDIA_DEVICE_MMC,iCurrentReq);
+ r=DoRead();
+ break;
+#endif // __DEMAND_PAGING__
+
+ case DLocalDrive::EQueryDevice:
+ r = KErrNotSupported;
+ break;
+
+ case DLocalDrive::ERead:
+ r=DoRead();
+ break;
+ case DLocalDrive::EWrite:
+ if (readOnly)
+ return KErrNotSupported;
+ r=DoWrite();
+ break;
+ case DLocalDrive::EFormat:
+ if (readOnly)
+ return KErrNotSupported;
+ r=DoFormat();
+ break;
+
+#if defined __TEST_PAGING_MEDIA_DRIVER__
+ case DLocalDrive::EControlIO:
+ {
+ r = HandleControlIORequest();
+ break;
+ }
+#endif
+
+ case DLocalDrive::EPasswordUnlock:
+ case DLocalDrive::EPasswordLock:
+ case DLocalDrive::EPasswordClear:
+ // Don't allow passords on internal MMC; one reason is that this may be used for paging and
+ // we can't really stop paging just because the password hasn't been supplied
+ if (iInternalSlot)
+ r = KErrNotSupported;
+ else
+ r = DoPasswordOp();
+ break;
+ case DLocalDrive::EPasswordErase:
+ {
+ r = LaunchRPIErase();
+ // This will complete the request in the event of an error
+ if(r != KErrNone)
+ PartitionInfoComplete(r);
+
+ r = KErrNone; // ensures to indicate asynchronoous completion
+ break;
+ }
+ case DLocalDrive::EWritePasswordStore:
+ {
+ //
+ // If the card is ready and locked, request the stack to perform the
+ // auto-unlock sequence. This is required, as the stack only performs
+ // automatic unlocking during power-up, and the stack may already be powered.
+ //
+ r = KErrNone; // asynchronous completion
+
+ if(iCard->IsReady() && iCard->IsLocked())
+ {
+ iSession->SetupCIMAutoUnlock();
+ if(EngageAndSetRequest(EMReqWritePasswordData, 0) != KErrNone)
+ {
+ // If error, complete with KErrNone anyway
+ // - The password store has been set, any errors
+ // will be reported on the next access.
+ CompleteRequest(KErrNone);
+ }
+ }
+ else
+ {
+ CompleteRequest(KErrNone);
+ }
+ break;
+ }
+ case DLocalDrive::EEnlarge:
+ case DLocalDrive::EReduce:
+ default:
+ r=KErrNotSupported;
+ break;
+ }
+ }
+
+ __KTRACE_OPT(KLOCDRV,Kern::Printf("MmcMd:Req %08x cmp %d",&aRequest,r));
+
+ if (r != KErrNone)
+ {
+ iMedReq = EMReqIdle;
+ iCurrentReq=NULL;
+ SetCurrentConsumption(KIdleCurrentInMilliAmps);
+ }
+
+ return r;
+ }
+
+void DMmcMediaDriverFlash::Disconnect(DLocalDrive* aLocalDrive, TThreadMessage* aMsg)
+ {
+ // Complete using the default implementation
+ DMediaDriver::Disconnect(aLocalDrive, aMsg);
+ }
+
+#ifdef _DEBUG_CACHE
+TUint8* DMmcMediaDriverFlash::GetCachedBlock(TInt64 aMdAddr)
+//
+// return cache block for media at aMdAddr, 0 if not found.
+// This is a debug function to determine whether or not a block is in
+// the cache. It should not be used for general block retrieval.
+// If there are m blocks in the cache, and n in the requested range,
+// this function is o(mn), whereas BuildGammaArray() is theta(m).
+//
+ {
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf(">mmd:gcb:%lx", aMdAddr));
+
+ __ASSERT_DEBUG((aMdAddr & iBlkMsk) == 0, Panic(EGCBAlign));
+ __ASSERT_DEBUG(TotalSizeInBytes() > aMdAddr, Panic(EGCBPos));
+ __ASSERT_CACHE(CacheInvariant(), Panic(EGCBCchInv));
+
+ for (TInt i = 0; i < iBlocksInBuffer; ++i)
+ {
+ if (iCachedBlocks[i] == aMdAddr)
+ {
+ TUint8* raby = IdxToCchMem(i);
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("<mmd:gcb:%x", (TUint32) raby));
+ return raby;
+ }
+ }
+
+ __KTRACE_OPT(KPBUSDRV, Kern::Printf("<mmd:gcb:0"));
+ return 0;
+ }
+#endif // _DEBUG_CACHE
+
+
+#if defined(__TEST_PAGING_MEDIA_DRIVER__)
+/**
+Handles a ControlIO request to the MMC media driver.
+made by one of the MMC paging tests
+
+@internalTechnology
+
+@return Corresponding Symbian OS error code
+*/
+TInt DMmcMediaDriverFlash::HandleControlIORequest()
+ {
+ const TInt command = iCurrentReq->Int0();
+ TAny* aParam1 = iCurrentReq->Ptr1();
+// TAny* aParam2 = iCurrentReq->Ptr2();
+
+ TInt r = KErrCompletion;
+
+ __KTRACE_OPT(KLOCDPAGING,Kern::Printf("[MD : ] HandleControlIORequest aCommand: 0x%x", command));
+
+
+ switch (command)
+ {
+ case KMmcGetStats:
+ {
+ DThread* pC = iCurrentReq->Client();
+ DThread* pT = iCurrentReq->RemoteThread();
+ if (!pT)
+ pT = pC;
+ Kern::ThreadRawWrite(pT, aParam1, &iMmcStats, sizeof(iMmcStats), pC);
+
+ iMmcStats.iReqNormal=0;
+ iMmcStats.iNormalFragmenting=0;
+ iMmcStats.iClashFragmenting=0;
+
+ break;
+ }
+ default:
+ r=KErrNotSupported;
+ break;
+ }
+
+ return r;
+ }
+#endif // __TEST_PAGING_MEDIA_DRIVER__
+
+
+
+
+DECLARE_EXTENSION_PDD()
+ {
+ // NB if the media driver has been defined as a kernel extension in the .OBY/.IBY file
+ // i.e the "extension" keyword has been used rather than "device", then an instance of
+ // DPhysicalDeviceMediaMmcFlash will already have been created by InitExtension(). In this
+ // case the kernel will see that an object of the same name already exists and delete the
+ // new one.
+ return new DPhysicalDeviceMediaMmcFlash;
+ }
+DECLARE_STANDARD_EXTENSION()
+ {
+ __KTRACE_OPT(KBOOT,Kern::Printf("Creating MMCDrv PDD"));
+
+ DPhysicalDeviceMediaMmcFlash* device = new DPhysicalDeviceMediaMmcFlash;
+
+ TInt r;
+ if (device==NULL)
+ r=KErrNoMemory;
+ else
+ r=Kern::InstallPhysicalDevice(device);
+ __KTRACE_OPT(KBOOT,Kern::Printf("Installing MMCDrv PDD in kernel returned %d",r));
+
+ __KTRACE_OPT(KBOOT,Kern::Printf("Mmc extension entry point drive returns %d",r));
+ return r;
+ }
+
+