kernel/eka/drivers/medmmc/medmmc.cpp
author Tom Cosgrove <tom.cosgrove@nokia.com>
Fri, 28 May 2010 16:29:07 +0100
changeset 30 8aab599e3476
parent 14 5d2844f35677
child 28 5b5d147c7838
permissions -rw-r--r--
Fix for bug 2283 (RVCT 4.0 support is missing from PDK 3.0.h) Have multiple extension sections in the bld.inf, one for each version of the compiler. The RVCT version building the tools will build the runtime libraries for its version, but make sure we extract all the other versions from zip archives. Also add the archive for RVCT4.

// 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>

#include "OstTraceDefinitions.h"
#ifdef OST_TRACE_COMPILER_IN_USE
#include "locmedia_ost.h"
#ifdef __VC32__
#pragma warning(disable: 4127) // disabling warning "conditional expression is constant"
#endif
#include "medmmcTraces.h"
#endif

#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()
	{
	OstTraceFunctionEntry1( DPHYSICALDEVICEMEDIAMMCFLASH_DPHYSICALDEVICEMEDIAMMCFLASH_ENTRY, this );
	__KTRACE_OPT(KPBUSDRV, Kern::Printf("=mmd:ctr"));

	iUnitsMask = 0x01;
	iVersion = TVersion(KMediaDriverInterfaceMajorVersion,KMediaDriverInterfaceMinorVersion,KMediaDriverInterfaceBuildVersion);
	OstTraceFunctionExit1( DPHYSICALDEVICEMEDIAMMCFLASH_DPHYSICALDEVICEMEDIAMMCFLASH_EXIT, this );
	}


TInt DPhysicalDeviceMediaMmcFlash::Install()
	{
	OstTraceFunctionEntry1( DPHYSICALDEVICEMEDIAMMCFLASH_INSTALL_ENTRY, this );
	__KTRACE_OPT(KPBUSDRV, Kern::Printf("=mmd:ins"));

	_LIT(KDrvNm, "Media.MmcF");
	TInt r = SetName(&KDrvNm);
	OstTraceFunctionExitExt( DPHYSICALDEVICEMEDIAMMCFLASH_INSTALL_EXIT, this, r );
	return r;
	}


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
//
	{
	OstTraceExt2(TRACE_FLOW, DPHYSICALDEVICEMEDIAMMCFLASH_INFO_ENTRY ,"DPhysicalDeviceMediaMmcFlash::Info;aFunction=%d;this=%x", aFunction, (TUint) this);
	__KTRACE_OPT(KPBUSDRV, Kern::Printf("=mmd:info"));
	if (aFunction==EPriority)
	    {
		OstTraceFunctionExitExt( DPHYSICALDEVICEMEDIAMMCFLASH_INFO_EXIT1, this, KMediaDriverPriorityNormal );
		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)
	    {
		OstTraceFunctionExitExt( DPHYSICALDEVICEMEDIAMMCFLASH_INFO_EXIT2, this, KErrNone );
		return KErrNone;
	    }
	
	OstTraceFunctionExitExt( DPHYSICALDEVICEMEDIAMMCFLASH_INFO_EXIT3, this, KErrNotSupported );
	return KErrNotSupported;
	}
								 
TInt DPhysicalDeviceMediaMmcFlash::Validate(TInt aDeviceType, const TDesC8* /*aInfo*/, const TVersion& aVer)
	{
	OstTraceExt2(TRACE_FLOW, DPHYSICALDEVICEMEDIAMMCFLASH_VALIDATE_ENTRY ,"DPhysicalDeviceMediaMmcFlash::Validate;aDeviceType=%d;this=%x", aDeviceType, (TUint) this);
	__KTRACE_OPT(KPBUSDRV, Kern::Printf("=mmd:validate aDeviceType %d", aDeviceType));
	if (!Kern::QueryVersionSupported(iVersion,aVer))
	    {
		OstTraceFunctionExitExt( DPHYSICALDEVICEMEDIAMMCFLASH_VALIDATE_EXIT1, this, KErrNotSupported );
		return KErrNotSupported;
	    }
	if (aDeviceType!=MEDIA_DEVICE_MMC)
	    {
		OstTraceFunctionExitExt( DPHYSICALDEVICEMEDIAMMCFLASH_VALIDATE_EXIT2, this, KErrNotSupported );
		return KErrNotSupported;
	    }

	OstTraceFunctionExitExt( DPHYSICALDEVICEMEDIAMMCFLASH_VALIDATE_EXIT3, this, KErrNone );
	return KErrNone;
	}
								 
TInt DPhysicalDeviceMediaMmcFlash::Create(DBase*& aChannel, TInt aMediaId, const TDesC8* /*aInfo*/, const TVersion& aVer)
//
// Create an MMC Card media driver.
//
	{
	OstTraceExt2(TRACE_FLOW, DPHYSICALDEVICEMEDIAMMCFLASH_CREATE_ENTRY, "DPhysicalDeviceMediaMmcFlash::Create;aMediaId=%d;this=%x", aMediaId, (TUint) this);
	__KTRACE_OPT(KPBUSDRV, Kern::Printf(">mmd:crt"));

	if (!Kern::QueryVersionSupported(iVersion,aVer))
	    {
		OstTraceFunctionExitExt( DPHYSICALDEVICEMEDIAMMCFLASH_CREATE_EXIT1, this, KErrNotSupported );
		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"));

	OstTraceFunctionExitExt( DPHYSICALDEVICEMEDIAMMCFLASH_CREATE_EXIT2, this, r );
	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));
	OstTraceExt2( TRACE_FLOW, DMMCMEDIADRIVERFLASH_DMMCMEDIADRIVERFLASH, "> DMmcMediaDriverFlash::DMmcMediaDriverFlash;aMediaId=%d;iMediaId=%d", (TInt) aMediaId, (TInt) iMediaId );
	
	}

#pragma warning( default : 4355 )
TInt DMmcMediaDriverFlash::DoCreate(TInt /*aMediaId*/)
	{
	OstTraceFunctionEntry1( DMMCMEDIADRIVERFLASH_DOCREATE_ENTRY, this );
	__KTRACE_OPT(KPBUSDRV, Kern::Printf(">mmd:opn"));

	iSocket = ((DMMCSocket*)((DPBusPrimaryMedia*)iPrimaryMedia)->iSocket);
	if(iSocket == NULL)
	    {
		OstTraceFunctionExitExt( DMMCMEDIADRIVERFLASH_DOCREATE_EXIT1, this, KErrNoMemory );
		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));
	OstTraceExt2(TRACE_INTERNALS, DMMCMEDIADRIVERFLASH_DOCREATE_SLOT, "slotNumber=%d; iInternalSlot=%d", iCardNumber, iInternalSlot);

	iSessionEndDfc.SetDfcQ(&iSocket->iDfcQ);
	iDataTransferCallBackDfc.SetDfcQ(&iSocket->iDfcQ);

	// check right type of card
	if ((iMediaType=iCard->MediaType())==EMultiMediaNotSupported)	
	    {
		OstTraceFunctionExitExt( DMMCMEDIADRIVERFLASH_DOCREATE_EXIT2, this, KErrNotReady );
		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)
	    {
		OstTraceFunctionExitExt( DMMCMEDIADRIVERFLASH_DOCREATE_EXIT3, this, r );
		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));
	OstTraceExt2(TRACE_INTERNALS, DMMCMEDIADRIVERFLASH_DOCREATE_VARS1, "physicalCardSlots=%d; iCardNumber=%d", physicalCardSlots, iCardNumber);
	__KTRACE_OPT(KPBUSDRV, Kern::Printf("iMediaId %d numMedia %d, mediaIndex %d, totalNumMedia %d, bufIndex %d\n", 
			  iMediaId, numMedia, mediaIndex, totalNumMedia, bufIndex));
	OstTraceExt5(TRACE_INTERNALS, DMMCMEDIADRIVERFLASH_DOCREATE_VARS2, "iMediaId=%d; numMedia=%d; mediaIndex=%d; totalNumMedia=%d; bufIndex=%d", iMediaId, numMedia, mediaIndex, totalNumMedia, bufIndex);

	__KTRACE_OPT(KPBUSDRV, Kern::Printf("bufLen1 %08X iCacheBuf1 %08X", bufLen, iCacheBuf));
	OstTraceExt2(TRACE_INTERNALS, DMMCMEDIADRIVERFLASH_DOCREATE_CACHEBUF1, "bufLen1=0x%08x; iCacheBuf1=0x%08x", (TUint) bufLen, (TUint) iCacheBuf);
	bufLen/= totalNumMedia;
	iCacheBuf+= bufIndex * bufLen;
	__KTRACE_OPT(KPBUSDRV, Kern::Printf("bufLen2 %08X iCacheBuf2 %08X", bufLen, iCacheBuf));
	OstTraceExt2(TRACE_INTERNALS, DMMCMEDIADRIVERFLASH_DOCREATE_CACHEBUF2, "bufLen2=0x%08x; iCacheBuf2=0x%08x", (TUint) bufLen, (TUint) 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;
        if (maxDbBlocks)
            {
            __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)
	    {
		OstTraceFunctionExitExt( DMMCMEDIADRIVERFLASH_DOCREATE_EXIT4, this, KErrNotReady );
		return KErrNotReady;
	    }

	__KTRACE_OPT(KPBUSDRV, Kern::Printf("iMaxBufSize %d iPrWtGpLen %d\n", iMaxBufSize, iPrWtGpLen));
	OstTraceExt2(TRACE_INTERNALS, DMMCMEDIADRIVERFLASH_DOCREATE_IPRWTGPLEN1, "iMaxBufSize=%d; iPrWtGpLen1=%d", 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));
	OstTrace1(TRACE_INTERNALS, DMMCMEDIADRIVERFLASH_DOCREATE_IPRWTGPLEN2, "iPrWtGpLen2=%d", iPrWtGpLen);

	// allocate index for cached blocks
	iCachedBlocks =	KernAlloc<TInt64>(iBlocksInBuffer);
	if (iCachedBlocks == 0)
	    {
		OstTraceFunctionExitExt( DMMCMEDIADRIVERFLASH_DOCREATE_EXIT5, this, KErrNoMemory );
		return KErrNoMemory;
	    }

	InvalidateCache();
	iLstUsdCchEnt = iBlocksInBuffer - 1;		// use entry 0 first

	// allocate read lookup index
	iGamma = KernAlloc<TInt>(iBlocksInBuffer);
	if (iGamma == 0)
	    {
		OstTraceFunctionExitExt( DMMCMEDIADRIVERFLASH_DOCREATE_EXIT6, this, KErrNoMemory );
		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)
	    {
		OstTraceFunctionExitExt( DMMCMEDIADRIVERFLASH_DOCREATE_EXIT7, this, err );
		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)
		    {
			OstTraceFunctionExitExt( DMMCMEDIADRIVERFLASH_DOCREATE_EXIT8, this, KErrNoMemory );
			return KErrNoMemory;
		    }
		TInt err = iMmcPartitionInfo->Initialise(this);
		if(err != KErrNone)
		    {
			OstTraceFunctionExitExt( DMMCMEDIADRIVERFLASH_DOCREATE_EXIT9, this, err );
			return err;
		    }
		}

	__KTRACE_OPT(KPBUSDRV, Kern::Printf("<mmd:opn"));

	OstTraceFunctionExitExt( DMMCMEDIADRIVERFLASH_DOCREATE_EXIT10, this, KErrNone );
	return KErrNone;
	}

void DMmcMediaDriverFlash::Close()
//
// Close the media driver - also called on media change
//
	{
	OstTraceFunctionEntry0( DMMCMEDIADRIVERFLASH_CLOSE_ENTRY );
	__KTRACE_OPT(KPBUSDRV,Kern::Printf("=mmd:cls"));
	
	EndInCritical();
	iSessionEndDfc.Cancel();
	iDataTransferCallBackDfc.Cancel();
	CompleteRequest(KErrNotReady);
	DMediaDriver::Close();
	OstTraceFunctionExit0( DMMCMEDIADRIVERFLASH_CLOSE_EXIT );
	}


DMmcMediaDriverFlash::~DMmcMediaDriverFlash()
	{
	OstTraceFunctionEntry0( DMMCMEDIADRIVERFLASH_DMMCMEDIADRIVERFLASH_ENTRY );
	__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"));
	OstTraceFunctionExit0( DMMCMEDIADRIVERFLASH_DMMCMEDIADRIVERFLASH_EXIT );
	}


TInt DMmcMediaDriverFlash::AllocateSession()
	{
OstTraceFunctionEntry1( DMMCMEDIADRIVERFLASH_ALLOCATESESSION_ENTRY, this );

	
	// already allocated ?
	if (iSession != NULL)
	    {
		OstTraceFunctionExitExt( DMMCMEDIADRIVERFLASH_ALLOCATESESSION_EXIT1, this, KErrNone );
		return KErrNone;
	    }

	iSession = iStack->AllocSession(iSessionEndCallBack);
	if (iSession == NULL)
	    {
		OstTraceFunctionExitExt( DMMCMEDIADRIVERFLASH_ALLOCATESESSION_EXIT2, this, KErrNoMemory );
		return KErrNoMemory;
	    }

	iSession->SetStack(iStack);
	iSession->SetCard(iCard);
	iSession->SetDataTransferCallback(iDataTransferCallBack);



	OstTraceFunctionExitExt( DMMCMEDIADRIVERFLASH_ALLOCATESESSION_EXIT3, this, KErrNone );
	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.
//
	{
	OstTraceFunctionEntry1( DMMCMEDIADRIVERFLASH_DOREAD_ENTRY, this );
	TInt r = CheckDevice(EMReqTypeNormalRd); 
	if (r != KErrNone)
	    {
	    OstTraceFunctionExitExt( DMMCMEDIADRIVERFLASH_DOREAD_EXIT1, this, r );
	    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));
	OstTraceExt2( TRACE_INTERNALS, DMMCMEDIADRIVERFLASH_DO_READ, "Position=0x%lx; Length=0x%x", (TUint) pos, (TUint) 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)
			    {
			    OstTraceFunctionExitExt( DMMCMEDIADRIVERFLASH_DOREAD_EXIT2, this, r );
			    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));
	
	OstTraceFunctionExitExt( DMMCMEDIADRIVERFLASH_DOREAD_EXIT3, this, 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.
//
	{
	OstTraceFunctionEntryExt( DMMCMEDIADRIVERFLASH_LAUNCHREAD_ENTRY, this );
	OstTraceExt2( TRACE_INTERNALS, DMMCMEDIADRIVERFLASH_LAUNCHREAD, "position=0x%lx; length=0x%x", (TUint) iCurrentReq->Pos(), (TUint) I64LOW(iCurrentReq->Length()));
	__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));
	OstTraceFunctionExitExt( DMMCMEDIADRIVERFLASH_LAUNCHREAD_EXIT, this, 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.
//
	{
	OstTraceFunctionEntry1( DMMCMEDIADRIVERFLASH_LAUNCHDBREAD_ENTRY, this );
	__KTRACE_OPT(KPBUSDRV, Kern::Printf(">mmd:ldbr:0x%lx,0x%x", iReqCur, I64LOW(iReqEnd - iReqCur)));
	OstTraceExt2( TRACE_INTERNALS, DMMCMEDIADRIVERFLASH_LAUNCHDBREAD, "position=0x%lx; length=0x%x", (TInt) iReqCur, (TInt) 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));

	OstTraceFunctionExitExt( DMMCMEDIADRIVERFLASH_LAUNCHDBREAD_EXIT, this, 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.
//
	{
	OstTraceFunctionEntryExt( DMMCMEDIADRIVERFLASH_LAUNCHPHYSREAD_ENTRY, this );
	OstTraceExt2( TRACE_INTERNALS, DMMCMEDIADRIVERFLASH_LAUNCHPHYSREAD, "position=0x%lx; length=0x%x", (TInt) iReqCur, (TInt) I64LOW(iReqEnd - iReqCur));
	__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"));
		OstTrace0( TRACE_INTERNALS, DMMCMEDIADRIVERFLASH_LAUNCH_PHYSREAD_FP, "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));

	OstTraceFunctionExitExt( DMMCMEDIADRIVERFLASH_LAUNCHPHYSREAD_EXIT, this, 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.
//
	{
	OstTraceFunctionEntry1( DMMCMEDIADRIVERFLASH_DOWRITE_ENTRY, this );
	const TInt64 pos = iCurrentReq->Pos();
	const TUint32 length = I64LOW(iCurrentReq->Length());

	__KTRACE_OPT(KPBUSDRV, Kern::Printf(">mmd:dw:0x%lx,0x%x", pos, length));
	OstTraceExt2( TRACE_INTERNALS, DMMCMEDIADRIVERFLASH_DOWRITE, "position=0x%lx; length=0x%x", (TUint) pos, (TUint) 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));

	OstTraceFunctionExitExt( DMMCMEDIADRIVERFLASH_DOWRITE_EXIT, this, r );
	return r;
	}


TInt DMmcMediaDriverFlash::DoFormat()
	{
	OstTraceFunctionEntry1( DMMCMEDIADRIVERFLASH_DOFORMAT_ENTRY, this );
	const TInt64 pos = iCurrentReq->Pos();
	const TUint32 length = I64LOW(iCurrentReq->Length());

	__KTRACE_OPT(KPBUSDRV, Kern::Printf(">mmd:df:0x%lx,0x%x", pos, length));
	OstTraceExt2( TRACE_INTERNALS, DMMCMEDIADRIVERFLASH_DOFORMAT, "position=0x%lx; length=0x%x", (TUint) pos, (TUint) 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));

	OstTraceFunctionExitExt( DMMCMEDIADRIVERFLASH_DOFORMAT_EXIT, this, 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.
//
	{
	OstTraceFunctionEntryExt( DMMCMEDIADRIVERFLASH_LAUNCHFORMAT_ENTRY, this );
	OstTraceExt2( TRACE_INTERNALS, DMMCMEDIADRIVERFLASH_LAUNCHFORMAT, "position=0x%lx; length=0x%x", (TInt) iReqCur, (TInt) I64LOW(iReqEnd - iReqCur));
	__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);
		}
		OstTraceFunctionExitExt( DMMCMEDIADRIVERFLASH_LAUNCHFORMAT_EXIT, this, r );
		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.
//
	{
	OstTraceExt4(TRACE_FLOW, DMMCMEDIADRIVERFLASH_LAUNCHWRITE_ENTRY, "DMmcMediaDriverFlash::LaunchWrite;aStart=%Ld;aLength=%x;aMedReq=%d;this=%x", aStart, (TUint) aLength, (TInt) aMedReq, (TUint) this);
	OstTraceExt2( TRACE_INTERNALS, DMMCMEDIADRIVERFLASH_LAUNCHWRITE, "position=0x%lx; length=0x%x", (TInt) iReqCur, (TInt) I64LOW(iReqEnd - iReqCur));
	__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();
								
				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
						{				
						if ( (iPhysEnd - iPhysStart) > iMaxBufSize)
						    {
		                    //
                            // 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;
						    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"));
			OstTrace0( TRACE_INTERNALS, DMMCMEDIADRIVERFLASH_LAUNCHWRITE_RBMF, "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);
			r = EngageAndSetReadRequest(aMedReq);
			OstTraceFunctionExitExt( DMMCMEDIADRIVERFLASH_LAUNCHWRITE_EXIT1, this, r );
			return r;
			}

		else if (iWtRBM & KWtRBMLst)
			{
			__KTRACE_OPT(KPBUSDRV, Kern::Printf("mmd:lw: read-before-modify required on last block"));			
			OstTrace0( TRACE_INTERNALS, DMMCMEDIADRIVERFLASH_LAUNCHWRITE_RBML, "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...
			r = EngageAndSetReadRequest(aMedReq);
			OstTraceFunctionExitExt( DMMCMEDIADRIVERFLASH_LAUNCHWRITE_EXIT2, this, r );
			return r;
			}
		
		if (iWtRBM & KWtMinFst)
			{
			__KTRACE_OPT(KPBUSDRV, Kern::Printf("mmd:lw:Phys write-first-block-only"));			
			OstTrace0( TRACE_INTERNALS, DMMCMEDIADRIVERFLASH_LAUNCHWRITE_FBO, "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)
			    {
				OstTraceFunctionExitExt( DMMCMEDIADRIVERFLASH_LAUNCHWRITE_EXIT3, this, r );
				return r;			
			    }
			}
		
		if (iWtRBM & KWtMinLst)
			{
			__KTRACE_OPT(KPBUSDRV, Kern::Printf("mmd:lw:Phys write-last-block-only"));
			OstTrace0( TRACE_INTERNALS, DMMCMEDIADRIVERFLASH_LAUNCHWRITE_LBO, "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)
			    {
				OstTraceFunctionExitExt( DMMCMEDIADRIVERFLASH_LAUNCHWRITE_EXIT4, this, r );
				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!	
           		OstTrace0( TRACE_INTERNALS, DMMCMEDIADRIVERFLASH_LAUNCHWRITE_PHYSICAL, "Physical write request" );
				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));
				
				OstTraceFunctionExitExt( DMMCMEDIADRIVERFLASH_LAUNCHWRITE_EXIT5, this, 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
					OstTrace0( TRACE_INTERNALS, DMMCMEDIADRIVERFLASH_LAUNCHWRITE_STANDARD, "Standard write request" );
					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.
					//
					OstTrace0( TRACE_INTERNALS, DMMCMEDIADRIVERFLASH_LAUNCHWRITE_DB, "Double-buffered write request" );
					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));

	OstTraceFunctionExitExt( DMMCMEDIADRIVERFLASH_LAUNCHWRITE_EXIT6, this, 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.
//
	{
	OstTraceFunctionEntry1( DMMCMEDIADRIVERFLASH_PARTITIONINFO_ENTRY, this );
	__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;
		
		OstTraceFunctionExitExt( DMMCMEDIADRIVERFLASH_PARTITIONINFO_EXIT1, this, r );
		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)
		    {
			OstTraceFunctionExitExt( DMMCMEDIADRIVERFLASH_PARTITIONINFO_EXIT2, this, r );
			return r;
		    }
		
		OstTraceFunctionExitExt( DMMCMEDIADRIVERFLASH_PARTITIONINFO_EXIT3, this, KErrLocked );
		return KErrLocked;
		}

	// KErrNone indicates asynchronous completion
	
	OstTraceFunctionExitExt( DMMCMEDIADRIVERFLASH_PARTITIONINFO_EXIT4, this, r );
	return r;
	}

TInt DMmcMediaDriverFlash::LaunchRPIUnlock(TLocalDrivePasswordData& aPasswordData)
	{
	OstTraceFunctionEntry1( DMMCMEDIADRIVERFLASH_LAUNCHRPIUNLOCK_ENTRY, this );
	__KTRACE_OPT(KPBUSDRV, Kern::Printf(">mmd:lru:%d,%d", iCard->IsReady(), iCard->IsLocked()));
	OstTraceExt2( TRACE_INTERNALS, DMMCMEDIADRIVERFLASH_LAUNCHRPIUNLOCK_ICARD, "iCard->IsReady=%d; iCard->IsLocked=%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));
	OstTraceFunctionExitExt( DMMCMEDIADRIVERFLASH_LAUNCHRPIUNLOCK_EXIT, this, r );
	return r;
	}


TInt DMmcMediaDriverFlash::LaunchRPIRead()
//
// launch read request on first KDiskSectorSize (512) bytes
//
	{
	OstTraceFunctionEntry1( DMMCMEDIADRIVERFLASH_LAUNCHRPIREAD_ENTRY, this );
	__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));
	OstTraceFunctionExitExt( DMMCMEDIADRIVERFLASH_LAUNCHRPIREAD_EXIT, this, r );
	return r;
	}


TInt DMmcMediaDriverFlash::LaunchRPIErase()
	{
	OstTraceFunctionEntry1( DMMCMEDIADRIVERFLASH_LAUNCHRPIERASE_ENTRY, this );
	__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"));
			OstTrace0( TRACE_INTERNALS, DMMCMEDIADRIVERFLASH_LAUNCHRPIERASE_FORCE_ERASE, "Force erase");
			iMinorBuf[0] = KMMCLockUnlockErase;
			iSession->SetupCIMLockUnlock(1, iMinorBuf);
			r = EngageAndSetWriteRequest(EMReqForceErase);
			}
		}

	__KTRACE_OPT(KPBUSDRV, Kern::Printf("<mmd:lru:%d", r));
	OstTraceFunctionExitExt( DMMCMEDIADRIVERFLASH_LAUNCHRPIERASE_EXIT, this, r );
	return r;
	}


TInt DMmcMediaDriverFlash::DecodePartitionInfo()
//
// decode partition info that was read into internal buffer 
//
	{
	OstTraceFunctionEntry1( DMMCMEDIADRIVERFLASH_DECODEPARTITIONINFO_ENTRY, this );
	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() || pe->IsValidExFATPartition())
			{
			SetPartitionEntry(&iPartitionInfo->iEntry[partitionCount],pe->iFirstSector,pe->iNumSectors);
			__KTRACE_OPT(KLOCDPAGING, Kern::Printf("Mmc: FAT partition found at sector #%u", pe->iFirstSector));
			OstTrace1(TRACE_INTERNALS, DMMCMEDIADRIVERFLASH_DECODEPARTITIONINFO_FS,"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"));
			OstTrace0( TRACE_INTERNALS, DMMCMEDIADRIVERFLASH_DECODEPARTITIONINFO_PARTCOUNT1, "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"));
				OstTrace0( TRACE_INTERNALS, DMMCMEDIADRIVERFLASH_DECODEPARTITIONINFO_PARTCOUNT2, "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"));
					OstTrace0( TRACE_INTERNALS, DMMCMEDIADRIVERFLASH_DECODEPARTITIONINFO_PARTCOUNT3, "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"));
						OstTrace0( TRACE_INTERNALS, DMMCMEDIADRIVERFLASH_DECODEPARTITIONINFO_PARTCOUNT4, "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"));
		OstTrace0( TRACE_INTERNALS, DMMCMEDIADRIVERFLASH_DECODEPARTITIONINFO_MBRDONE1, "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"));
			OstTrace0( TRACE_INTERNALS, DMMCMEDIADRIVERFLASH_DECODEPARTITIONINFO_MBRDONE2, "MBR mandatory, defining space for MBR + default partition" );
			iMbrMissing = ETrue;
			TInt r = CreateDefaultPartition();
			if (r != KErrNone)
			    {
				OstTraceFunctionExitExt( DMMCMEDIADRIVERFLASH_DECODEPARTITIONINFO_EXIT1, this, r );
				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)));
	OstTraceDefExt4(OST_TRACE_CATEGORY_RND, TRACE_MMCDEBUG, DMMCMEDIADRIVERFLASH_DECODEPARTITIONINFO_PARTINFO1, "Partition1 (B:0x%x L:0x%x); Partition2 (B:0x%x L:0x%x)", I64LOW(iPartitionInfo->iEntry[0].iPartitionBaseAddr),I64LOW(iPartitionInfo->iEntry[0].iPartitionLen),I64LOW(iPartitionInfo->iEntry[1].iPartitionBaseAddr),I64LOW(iPartitionInfo->iEntry[1].iPartitionLen));
	OstTraceDefExt4(OST_TRACE_CATEGORY_RND, TRACE_MMCDEBUG, DMMCMEDIADRIVERFLASH_DECODEPARTITIONINFO_PARTINFO2, "Partition3 (B:0x%x L:0x%x); Partition4 (B:0x%x L:0x%x)", I64LOW(iPartitionInfo->iEntry[2].iPartitionBaseAddr),I64LOW(iPartitionInfo->iEntry[2].iPartitionLen),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

	OstTraceFunctionExitExt( DMMCMEDIADRIVERFLASH_DECODEPARTITIONINFO_EXIT2, this, KErrNone );
	return KErrNone;
	}


TInt DMmcMediaDriverFlash::WritePartitionInfo()
/**
	Write the default partition table to freshly formatted media
	@return Standard Symbian OS Error Code
 */
	{
	OstTraceFunctionEntry1( DMMCMEDIADRIVERFLASH_WRITEPARTITIONINFO_ENTRY, this );
	__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));
		OstTraceExt5(TRACE_MMCDEBUG, DMMCMEDIADRIVERFLASH_WRITEPARTITIONINFO_PARTINFO1, "Boot ID=0x%02x; Start Head=0x%02x; Start Sector=0x%02x; Start Cyclinder=0x%02x; System ID=0x%02x", (TUint) partitionEntry.iX86BootIndicator, (TUint) partitionEntry.iStartHead, (TUint) partitionEntry.iStartSector, (TUint) partitionEntry.iStartCylinder, (TUint) partitionEntry.iPartitionType);
		OstTraceExt5(TRACE_MMCDEBUG, DMMCMEDIADRIVERFLASH_WRITEPARTITIONINFO_PARTINFO2, "End Head=0x%02x; End Sector=0x%02x; End Cyclinder=0x%02x; Relative Sector=0x%08x; Number of Sectors=0x%08x", (TUint) partitionEntry.iEndHead, (TUint) partitionEntry.iEndSector, (TUint) partitionEntry.iEndCylinder, (TUint) partitionEntry.iFirstSector, (TUint) 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));

	OstTraceFunctionExitExt( DMMCMEDIADRIVERFLASH_WRITEPARTITIONINFO_EXIT, this, err );
	return err;
	}


TInt DMmcMediaDriverFlash::CreateDefaultPartition()
	{
	OstTraceFunctionEntry1( DMMCMEDIADRIVERFLASH_CREATEDEFAULTPARTITION_ENTRY, this );
	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();
		}
	OstTraceFunctionExitExt( DMMCMEDIADRIVERFLASH_CREATEDEFAULTPARTITION_EXIT, this, r );
	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
	TInt r = GetMediaDefaultPartitionInfo(aPartitionEntry, reservedSectors, iCard);
	return r;
	}


void DMmcMediaDriverFlash::SetPartitionEntry(TPartitionEntry* aEntry, TUint aFirstSector, TUint aNumSectors)
//
// auxiliary static function to record partition information in TPartitionEntry object
//
	{
	OstTraceFunctionEntry0( DMMCMEDIADRIVERFLASH_SETPARTITIONENTRY_ENTRY );
	aEntry->iPartitionBaseAddr=aFirstSector;
	aEntry->iPartitionBaseAddr<<=KDiskSectorShift;
	aEntry->iPartitionLen=aNumSectors;
	aEntry->iPartitionLen<<=KDiskSectorShift;
	aEntry->iPartitionType=KPartitionTypeFAT12;	
	OstTraceFunctionExit0( DMMCMEDIADRIVERFLASH_SETPARTITIONENTRY_EXIT );
	}

TInt DMmcMediaDriverFlash::DoPasswordOp()
	{
	OstTraceFunctionEntry1( DMMCMEDIADRIVERFLASH_DOPASSWORDOP_ENTRY, this );
	// 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);

	OstTraceFunctionExitExt( DMMCMEDIADRIVERFLASH_DOPASSWORDOP_EXIT, this, KErrNone );
	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().
//
	{
	OstTraceExt2(TRACE_FLOW, DMMCMEDIADRIVERFLASH_PASSWORDCONTROL_ENTRY ,"DMmcMediaDriverFlash::PasswordControl;aFunc=%d;this=%x", aFunc, (TUint) this);
	__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));
	OstTraceFunctionExit1( DMMCMEDIADRIVERFLASH_PASSWORDCONTROL_EXIT, this );
	}


// ---- device status, callback DFC ----

TInt DMmcMediaDriverFlash::CheckDevice(TMediaReqType aReqType)
//
// Check the device before initiating a command
//
	{
	OstTraceExt2(TRACE_FLOW, DMMCMEDIADRIVERFLASH_CHECKDEVICE_ENTRY, "DMmcMediaDriverFlash::CheckDevice;aReqType=%d;this=%x", (TInt) aReqType, (TUint) this);
	__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));
	OstTraceFunctionExitExt( DMMCMEDIADRIVERFLASH_CHECKDEVICE_EXIT, this, 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.
//
	{
	OstTraceFunctionEntry0( DMMCMEDIADRIVERFLASH_SESSIONENDCALLBACK_ENTRY );
	DMmcMediaDriverFlash& md = *static_cast<DMmcMediaDriverFlash*>(aMediaDriver);
	__ASSERT_DEBUG(! md.iSessionEndDfc.Queued(), Panic(ESECBQueued));
	md.iSessionEndDfc.Enque();
	OstTraceFunctionExit0( DMMCMEDIADRIVERFLASH_SESSIONENDCALLBACK_EXIT );
	}


void DMmcMediaDriverFlash::SessionEndDfc(TAny* aMediaDriver)
	{
	static_cast<DMmcMediaDriverFlash*>(aMediaDriver)->DoSessionEndDfc();
	}


void DMmcMediaDriverFlash::DoSessionEndDfc()
//
// launch next session or complete client request
//
	{
	OstTraceFunctionEntry1( DMMCMEDIADRIVERFLASH_DOSESSIONENDDFC_ENTRY, this );
	__KTRACE_OPT(KPBUSDRV, Kern::Printf(">mmd:dsed:%d", CurrentRequest()));
	OstTrace1( TRACE_INTERNALS, DMMCMEDIADRIVERFLASH_DOSESSIONENDDFC_REQUEST, "Current Request=%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));
		OstTrace1( TRACE_INTERNALS, DMMCMEDIADRIVERFLASH_DOSESSIONENDDFC_COMPLETE, "Complete request; retval=%d", r);
		CompleteRequest(r);
		}
	else
		{
		__KTRACE_OPT(KPBUSDRV, Kern::Printf("<mdf:dsed:ncmp"));
		OstTrace0( TRACE_INTERNALS, DMMCMEDIADRIVERFLASH_DOSESSIONENDDFC_NOT_COMPLETE, "Request not complete");
		}
	OstTraceFunctionExit1( DMMCMEDIADRIVERFLASH_DOSESSIONENDDFC_EXIT, this );
	}

void DMmcMediaDriverFlash::DataTransferCallBack(TAny* aMediaDriver)
	{
	OstTraceFunctionEntry0( DMMCMEDIADRIVERFLASH_DATATRANSFERCALLBACK_ENTRY );
	DMmcMediaDriverFlash& md = *static_cast<DMmcMediaDriverFlash*>(aMediaDriver);
	__ASSERT_DEBUG(! md.iDataTransferCallBackDfc.Queued(), Panic(EDBCBQueued));
	md.iDataTransferCallBackDfc.Enque();
	OstTraceFunctionExit0( DMMCMEDIADRIVERFLASH_DATATRANSFERCALLBACK_EXIT );
	}

void DMmcMediaDriverFlash::DataTransferCallBackDfc(TAny* aMediaDriver)
	{
	OstTraceFunctionEntry0( DMMCMEDIADRIVERFLASH_DATATRANSFERCALLBACKDFC_ENTRY );
	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();
			}
		}
	OstTraceFunctionExit0( DMMCMEDIADRIVERFLASH_DATATRANSFERCALLBACKDFC_EXIT );
	}

void DMmcMediaDriverFlash::DoPhysWriteDataTransferCallBack()
	{
	OstTraceFunctionEntry1( DMMCMEDIADRIVERFLASH_DOPHYSWRITEDATATRANSFERCALLBACK_ENTRY, this );
	__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)"));
		OstTraceFunctionExit1( DMMCMEDIADRIVERFLASH_DOPHYSWRITEDATATRANSFERCALLBACK_EXIT1, this );
		return;
		}
	
	PrepareNextPhysicalFragment();

	__KTRACE_OPT(KPBUSDRV, Kern::Printf("--DMmcMediaDriverFlash::DoPhysWriteDataTransferCallBack()"));
	OstTraceFunctionExit1( DMMCMEDIADRIVERFLASH_DOPHYSWRITEDATATRANSFERCALLBACK_EXIT2, this );
	}


void DMmcMediaDriverFlash::DoPhysReadDataTransferCallBack()
	{
	OstTraceFunctionEntry1( DMMCMEDIADRIVERFLASH_DOPHYSREADDATATRANSFERCALLBACK_ENTRY, this );
	__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)"));
		OstTraceFunctionExit1( DMMCMEDIADRIVERFLASH_DOPHYSREADDATATRANSFERCALLBACK_EXIT1, this );
		return;
		}

	PrepareNextPhysicalFragment();

	__KTRACE_OPT(KPBUSDRV, 	Kern::Printf("--DMmcMediaDriverFlash::DoPhysReadTransferCallBack()"));
	OstTraceFunctionExit1( DMMCMEDIADRIVERFLASH_DOPHYSREADDATATRANSFERCALLBACK_EXIT2, this );
	}

void DMmcMediaDriverFlash::DoWriteDataTransferCallBack()
	{
	OstTraceFunctionEntry1( DMMCMEDIADRIVERFLASH_DOWRITEDATATRANSFERCALLBACK_ENTRY, this );
	__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()"));
	OstTraceFunctionExit1( DMMCMEDIADRIVERFLASH_DOWRITEDATATRANSFERCALLBACK_EXIT, this );
	}


void DMmcMediaDriverFlash::DoReadDataTransferCallBack()
	{
	OstTraceFunctionEntry1( DMMCMEDIADRIVERFLASH_DOREADDATATRANSFERCALLBACK_ENTRY, this );
	__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);
			OstTraceFunctionExit1( DMMCMEDIADRIVERFLASH_DOREADDATATRANSFERCALLBACK_EXIT1, this );
			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()"));
	OstTraceFunctionExit1( DMMCMEDIADRIVERFLASH_DOREADDATATRANSFERCALLBACK_EXIT2, this );
	}


// ---- request management ----


TInt DMmcMediaDriverFlash::EngageAndSetReadRequest(DMmcMediaDriverFlash::TMediaRequest aRequest)
	{
	OstTraceExt2(TRACE_FLOW, DMMCMEDIADRIVERFLASH_ENGAGEANDSETREADREQUEST_ENTRY, "DMmcMediaDriverFlash::EngageAndSetReadRequest;aRequest=%d;this=%x", (TInt) aRequest, (TUint) this);
	TInt r = EngageAndSetRequest(aRequest, iReadCurrentInMilliAmps);
	OstTraceFunctionExitExt( DMMCMEDIADRIVERFLASH_ENGAGEANDSETREADREQUEST_EXIT, this, r );
	return r;
	}


TInt DMmcMediaDriverFlash::EngageAndSetWriteRequest(DMmcMediaDriverFlash::TMediaRequest aRequest)
	{
	OstTraceExt2(TRACE_FLOW, DMMCMEDIADRIVERFLASH_ENGAGEANDSETWRITEREQUEST_ENTRY, "DMmcMediaDriverFlash::EngageAndSetReadRequest;aRequest=%d;this=%x", (TInt) aRequest, (TUint) this);
	TInt r = EngageAndSetRequest(aRequest, iWriteCurrentInMilliAmps);
	OstTraceFunctionExitExt( DMMCMEDIADRIVERFLASH_ENGAGEANDSETWRITEREQUEST_EXIT, this, r );
	return r;
	}


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().
//
	{
	OstTraceExt3(TRACE_FLOW, DMMCMEDIADRIVERFLASH_ENGAGEANDSETREQUEST_ENTRY, "DMmcMediaDriverFlash::EngageAndSetRequest;aRequest=%d;aCurrent=%d;this=%x", (TInt) aRequest, aCurrent, (TUint) this);
	__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();
		}

	OstTraceFunctionExitExt( DMMCMEDIADRIVERFLASH_ENGAGEANDSETREQUEST_EXIT, this, r );
	return r;
	}


void DMmcMediaDriverFlash::CompleteRequest(TInt aReason)
//
// completes the specified request
//
	{
	OstTraceFunctionEntryExt( DMMCMEDIADRIVERFLASH_COMPLETEREQUEST_ENTRY, this );
	__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);
		}
	OstTraceFunctionExit1( DMMCMEDIADRIVERFLASH_COMPLETEREQUEST_EXIT, this );
	}

TInt DMmcMediaDriverFlash::Caps(TLocDrv& aDrive, TLocalDriveCapsV6& aInfo)
	{
	OstTraceFunctionEntry1( DMMCMEDIADRIVERFLASH_CAPS_ENTRY, this );
	// Fill buffer with current media caps.
	aInfo.iType = EMediaHardDisk;
	aInfo.iConnectionBusType = EConnectionBusInternal;
	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)
		    {
			OstTraceFunctionExitExt( DMMCMEDIADRIVERFLASH_CAPS_EXIT1, this, r );
			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
	OstTraceFunctionExitExt( DMMCMEDIADRIVERFLASH_CAPS_EXIT2, this, KErrCompletion );
	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.
//
	{
	OstTraceFunctionEntryExt( DMMCMEDIADRIVERFLASH_READDATAUNTILCACHEEXHAUSTED_ENTRY, this );
	__KTRACE_OPT(KPBUSDRV, Kern::Printf(">mmd:rdc:%x,%x", iReqCur, iReqEnd));
	OstTraceExt2( TRACE_INTERNALS, DMMCMEDIADRIVERFLASH_READDATAUNTILCACHEEXHAUSTED, "iReqCur=0x%x; iReqEnd=0x%x", (TUint) iReqCur, (TUint) iReqEnd );
	
	if ( iCurrentReq->IsPhysicalAddress()
#if defined(__DEMAND_PAGING__) && !defined(__WINS__)
	     || DMediaPagingDevice::PageInRequest(*iCurrentReq)
#endif //DEMAND_PAGING 
        )
		{
		*aAllDone = EFalse;
		OstTraceFunctionExitExt( DMMCMEDIADRIVERFLASH_READDATAUNTILCACHEEXHAUSTED_EXIT1, this, KErrNone );
		return KErrNone;
		}
	
	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));
	OstTraceFunctionExitExt( DMMCMEDIADRIVERFLASH_READDATAUNTILCACHEEXHAUSTED_EXIT2, this, r );
	return r;
	}


TInt DMmcMediaDriverFlash::WriteDataToUser(TUint8* aBufPtr)
//
// write the data from the most recent read operation to the user descriptor
//
	{
	OstTraceFunctionEntryExt( DMMCMEDIADRIVERFLASH_WRITEDATATOUSER_ENTRY, this );
	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);

	OstTrace0( TRACE_INTERNALS, DMMCMEDIADRIVERFLASH_WRITEDATATOUSER_LATENCY1, "Begin writing user data" );
#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);
	
	OstTrace0( TRACE_INTERNALS, DMMCMEDIADRIVERFLASH_WRITEDATATOUSER_LATENCY2, "End writing user data" );

	OstTraceFunctionExitExt( DMMCMEDIADRIVERFLASH_WRITEDATATOUSER_EXIT, this, r );
	return r;
	}

TInt DMmcMediaDriverFlash::ReadDataFromUser(TDes8& aDes, TInt aOffset)
	{
	OstTraceExt2(TRACE_FLOW, DMMCMEDIADRIVERFLASH_READDATAFROMUSER_ENTRY ,"DMmcMediaDriverFlash::ReadDataFromUser;aOffset=%d;this=%x", aOffset, (TUint) this);
	TInt r = KErrNotSupported;
#ifndef __WINS__
	if (DMediaPagingDevice::PageOutRequest(*iCurrentReq))
	    {
		r = iCurrentReq->ReadFromPageHandler((TAny*) aDes.Ptr(), aDes.MaxLength(), aOffset);
		OstTraceFunctionExitExt( DMMCMEDIADRIVERFLASH_READDATAFROMUSER_EXIT1, this, r );
		return r;
	    }
	else
#endif // #ifndef __WINS__
		r = iCurrentReq->ReadRemote(&aDes, aOffset);
	
	OstTraceFunctionExitExt( DMMCMEDIADRIVERFLASH_READDATAFROMUSER_EXIT2, this, r );
	return r;
	}

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.
//
	{
	OstTraceExt3(TRACE_FLOW, DMMCMEDIADRIVERFLASH_ADJUSTPHYSICALFRAGMENT_ENTRY, "DMmcMediaDriverFlash::AdjustPhysicalFragment;aPhysAddr=%x;aPhysLength=%d;this=%x", (TUint) aPhysAddr, aPhysLength, (TUint) this);
	__KTRACE_OPT(KPBUSDRV, Kern::Printf(">mmd:APF"));
	
	TInt err = KErrNone;
	TInt offset = iFragOfset;

	do 
		{				
		err = iCurrentReq->GetNextPhysicalAddress(aPhysAddr, aPhysLength);

		if (err != KErrNone)
		    {
			OstTraceFunctionExitExt( DMMCMEDIADRIVERFLASH_ADJUSTPHYSICALFRAGMENT_EXIT1, this, err );
			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)
		{
		OstTraceFunctionExitExt( DMMCMEDIADRIVERFLASH_ADJUSTPHYSICALFRAGMENT_EXIT2, this, KErrNoMemory );
		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!"));
		OstTrace0( TRACE_INTERNALS, DMMCMEDIADRIVERFLASH_ADJUSTPHYSICALFRAGMENT_DMA, "Memory fragment not word aligned");
		Panic(ENotDMAAligned);
		}
#endif	//_DEBUG
	
	__KTRACE_OPT(KPBUSDRV, Kern::Printf("<mmd:APF physAddr(0x%x), physLength(%d)",aPhysAddr, aPhysLength));
	OstTraceFunctionExitExt( DMMCMEDIADRIVERFLASH_ADJUSTPHYSICALFRAGMENT_EXIT3, this, err );
	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.
//
	{
	OstTraceExt4(TRACE_FLOW, DMMCMEDIADRIVERFLASH_PREPAREFIRSTPHYSICALFRAGMENT_ENTRY, "DMmcMediaDriverFlash::PrepareFirstPhysicalFragment;aPhysAddr=%x;aPhysLength=%d;aLength=%x;this=%x", (TUint) aPhysAddr, aPhysLength, (TUint) aLength, (TUint) this);
	__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"));
			OstTrace0( TRACE_INTERNALS, DMMCMEDIADRIVERFLASH_PREPAREFIRSTPHYSICALFRAGMENT_EB, "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"));
			OstTrace0( TRACE_INTERNALS, DMMCMEDIADRIVERFLASH_PREPAREFIRSTPHYSICALFRAGMENT_SB, "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));	
	OstTraceFunctionExitExt( DMMCMEDIADRIVERFLASH_PREPAREFIRSTPHYSICALFRAGMENT_EXIT, this, r );
	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.
//
	{
	OstTraceFunctionEntry0( DMMCMEDIADRIVERFLASH_PREPARENEXTPHYSICALFRAGMENT_ENTRY );
	__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"));
				OstTrace0( TRACE_INTERNALS, DMMCMEDIADRIVERFLASH_PREPARENEXTPHYSICALFRAGMENT_EB, "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"));
			OstTrace0( TRACE_INTERNALS, DMMCMEDIADRIVERFLASH_PREPARENEXTPHYSICALFRAGMENT_SB, "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"));
	OstTraceFunctionExit0( DMMCMEDIADRIVERFLASH_PREPARENEXTPHYSICALFRAGMENT_EXIT );
	}

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.
//
	{
	OstTraceFunctionEntryExt( DMMCMEDIADRIVERFLASH_RESERVEREADBLOCKS_ENTRY, this );
	__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
	
	OstTraceExt2( TRACE_INTERNALS, DMMCMEDIADRIVERFLASH_RESERVEREADBLOCKS_RANGE, "blocksInRange=%d; blkCnt=%d", blocksInRange, blkCnt );
	
	TUint32 lengthInBytes = blkCnt << iBlkLenLog2;
	*aLength = lengthInBytes;
	MarkBlocks(aStart, aStart + lengthInBytes, startIndex);

	raby = IdxToCchMem(startIndex);

	__KTRACE_OPT(KPBUSDRV, Kern::Printf("<mmd:rrb:%x", (TUint32) raby));

	OstTraceFunctionExitExt( DMMCMEDIADRIVERFLASH_RESERVEREADBLOCKS_EXIT, this, ( TUint )( 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.
//
	{
	OstTraceFunctionEntryExt( DMMCMEDIADRIVERFLASH_RESERVEWRITEBLOCKS_ENTRY, this );
	__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);
	OstTrace1( TRACE_INTERNALS, DMMCMEDIADRIVERFLASH_RESERVEWRITEBLOCKS_RANGE, "blkCnt=%d", blkCnt );
	
	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;
				OstTraceFunctionExitExt( DMMCMEDIADRIVERFLASH_RESERVEWRITEBLOCKS_EXIT1, this, ( TUint )( raby ) );
				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;
					}
				
				OstTraceFunctionExitExt( DMMCMEDIADRIVERFLASH_RESERVEWRITEBLOCKS_EXIT2, this, ( TUint )( raby ) );
				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));

	OstTraceFunctionExitExt( DMMCMEDIADRIVERFLASH_RESERVEWRITEBLOCKS_EXIT3, this, ( TUint )( 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.
//
	{
	OstTraceFunctionEntryExt( DMMCMEDIADRIVERFLASH_MARKBLOCKS_ENTRY, this );
	__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);
	OstTrace1( TRACE_INTERNALS, DMMCMEDIADRIVERFLASH_MARKBLOCKS_RANGE, "blkCnt=%d", blkCnt );
	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));
	OstTraceFunctionExit1( DMMCMEDIADRIVERFLASH_MARKBLOCKS_EXIT, this );
	}


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.
//
	{
	OstTraceFunctionEntryExt( DMMCMEDIADRIVERFLASH_BUILDGAMMAARRAY_ENTRY, this );
	__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);
	OstTrace1( TRACE_INTERNALS, DMMCMEDIADRIVERFLASH_BUILDGAMMAARRAY_RANGE, "blocksInRange=%d", blocksInRange );
	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--;
			}
		}
	OstTraceFunctionExit1( DMMCMEDIADRIVERFLASH_BUILDGAMMAARRAY_EXIT, this );
	}

void DMmcMediaDriverFlash::InvalidateCache()
	{
	OstTraceFunctionEntry0( DMMCMEDIADRIVERFLASH_INVALIDATECACHE1_ENTRY );
	__KTRACE_OPT(KPBUSDRV, Kern::Printf("=mmd:ich"));

	// KInvalidBlock = (0xff) x 4
	memset(iCachedBlocks, 0xff, sizeof(*iCachedBlocks) * iBlocksInBuffer);
	OstTraceFunctionExit0( DMMCMEDIADRIVERFLASH_INVALIDATECACHE1_EXIT );
	}

// 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)
	{
	OstTraceFunctionEntryExt( DMMCMEDIADRIVERFLASH_INVALIDATECACHE2_ENTRY, this );
	__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);
	OstTrace1( TRACE_INTERNALS, DMMCMEDIADRIVERFLASH_INVALIDATECACHE_RANGE, "blocksInRange=%d", blkCnt );

	__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;
		}
	OstTraceFunctionExit1( DMMCMEDIADRIVERFLASH_INVALIDATECACHE2_EXIT, this );
	}

TUint8* DMmcMediaDriverFlash::IdxToCchMem(TInt aIdx) const
	{
	OstTraceFunctionEntryExt( DMMCMEDIADRIVERFLASH_IDXTOCCHMEM_ENTRY, this );
	__KTRACE_OPT(KPBUSDRV, Kern::Printf("=mmd:icm:%d", aIdx));

	__ASSERT_DEBUG(aIdx >= 0, Panic(EICMNegative));
	__ASSERT_DEBUG(aIdx < iBlocksInBuffer, Panic(EICMOverflow));
	
	OstTraceFunctionExit1( DMMCMEDIADRIVERFLASH_IDXTOCCHMEM_EXIT, this );
	return &iCacheBuf[aIdx << iBlkLenLog2];
	}

TInt DMmcMediaDriverFlash::CchMemToIdx(TUint8* aMemP) const
	{
	OstTraceFunctionEntry1( DMMCMEDIADRIVERFLASH_CCHMEMTOIDX_ENTRY, this );
	__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.
//
	{
	OstTraceFunctionEntry1( DMMCMEDIADRIVERFLASH_CACHEINVARIANT_ENTRY, this );
	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;
			}
		}

	OstTraceFunctionExit1( DMMCMEDIADRIVERFLASH_CACHEINVARIANT_EXIT, this );
	return ETrue;
	}
#endif

void DMmcMediaDriverFlash::NotifyPowerDown()
	{
	OstTraceFunctionEntry0( DMMCMEDIADRIVERFLASH_NOTIFYPOWERDOWN_ENTRY );
	__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;
	OstTraceFunctionExit0( DMMCMEDIADRIVERFLASH_NOTIFYPOWERDOWN_EXIT );
	}

void DMmcMediaDriverFlash::NotifyEmergencyPowerDown()
	{
	OstTraceFunctionEntry0( DMMCMEDIADRIVERFLASH_NOTIFYEMERGENCYPOWERDOWN_ENTRY );
	__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;
	OstTraceFunctionExit0( DMMCMEDIADRIVERFLASH_NOTIFYEMERGENCYPOWERDOWN_EXIT );
	}

TInt DMmcMediaDriverFlash::Request(TLocDrvRequest& aRequest)
	{
	OstTraceFunctionEntry1( DMMCMEDIADRIVERFLASH_REQUEST_ENTRY, this );
	__KTRACE_OPT(KLOCDRV,Kern::Printf("MmcMd:Req %08x id %d",&aRequest,aRequest.Id()));
	TInt r=KErrNotSupported;
	TInt id=aRequest.Id();
	OstTraceDefExt2( OST_TRACE_CATEGORY_RND, TRACE_REQUEST, DMMCMEDIADRIVERFLASH_REQUEST_ID, "Request=0x%08x; Request ID=%d", (TUint) &aRequest, id);

#if defined (__TEST_PAGING_MEDIA_DRIVER__)
	DThread* client=aRequest.Client();
	__KTRACE_OPT(KLOCDPAGING,Kern::Printf("Client:0x%08x",client));
	OstTraceDef1( OST_TRACE_CATEGORY_RND, TRACE_REQUEST, DMMCMEDIADRIVERFLASH_REQUEST_CLIENT, "Request client=0x%08x", (TUint) 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);
		OstTraceFunctionExitExt( DMMCMEDIADRIVERFLASH_REQUEST_EXIT1, this, r );
		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));
		OstTraceDef1( OST_TRACE_CATEGORY_RND, TRACE_REQUEST, DMMCMEDIADRIVERFLASH_REQUEST_IN_PROGRESS, "Request in progress=0x%08x", (TUint) &aRequest);
		OstTraceFunctionExitExt( DMMCMEDIADRIVERFLASH_REQUEST_EXIT2, this, KMediaDriverDeferRequest );
		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);
				OstTraceDef0( OST_TRACE_CATEGORY_RND, TRACE_REQUEST, DMMCMEDIADRIVERFLASH_REQUEST_ROM_PAGE_IN, "ROM page-in request");
				r=DoRead();
				break;
			case DMediaPagingDevice::ECodePageInRequest:
				__KTRACE_OPT(KLOCDPAGING,Kern::Printf("DMediaDriverFlash::Request(ECodePageInRequest)"));
				BTraceContext8(BTrace::EPagingMedia,BTrace::EPagingMediaPagingMedDrvBegin,MEDIA_DEVICE_MMC,iCurrentReq);
				OstTraceDef0( OST_TRACE_CATEGORY_RND, TRACE_REQUEST, DMMCMEDIADRIVERFLASH_REQUEST_CODE_PAGE_IN, "Code page-in request");
				r=DoRead();
				break;
#endif	// __DEMAND_PAGING__

			case DLocalDrive::EQueryDevice:
				r = KErrNotSupported;
				break;

			case DLocalDrive::ERead:
				r=DoRead();
				break;
			case DLocalDrive::EWrite:
				if (readOnly)
				    {
					OstTraceFunctionExitExt( DMMCMEDIADRIVERFLASH_REQUEST_EXIT3, this, KErrNotSupported );
					return KErrNotSupported;
				    }
				r=DoWrite();
				break;
			case DLocalDrive::EFormat:
				if (readOnly)
				    {
					OstTraceFunctionExitExt( DMMCMEDIADRIVERFLASH_REQUEST_EXIT4, this, KErrNotSupported );
					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);
		}
	
	OstTraceFunctionExitExt( DMMCMEDIADRIVERFLASH_REQUEST_EXIT5, this, r );
	return r;
	}

void DMmcMediaDriverFlash::Disconnect(DLocalDrive* aLocalDrive, TThreadMessage* aMsg)
	{
	OstTraceFunctionEntry1( DMMCMEDIADRIVERFLASH_DISCONNECT_ENTRY, this );
	// Complete using the default implementation
	DMediaDriver::Disconnect(aLocalDrive, aMsg);
	OstTraceFunctionExit1( DMMCMEDIADRIVERFLASH_DISCONNECT_EXIT, this );
	}

#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).
//
	{
	OstTraceFunctionEntryExt( DMMCMEDIADRIVERFLASH_GETCACHEDBLOCK_ENTRY, this );
	__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));
			OstTraceFunctionExitExt( DMMCMEDIADRIVERFLASH_GETCACHEDBLOCK_EXIT1, this, ( TUint )( raby ) );
			return raby;
			}
		}

	__KTRACE_OPT(KPBUSDRV, Kern::Printf("<mmd:gcb:0"));
	OstTraceFunctionExit1( DMMCMEDIADRIVERFLASH_GETCACHEDBLOCK_EXIT2, this );
	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;
	}