// Copyright (c) 1999-2009 Nokia Corporation and/or its subsidiary(-ies).
// All rights reserved.
// This component and the accompanying materials are made available
// under the terms of the License "Eclipse Public License v1.0"
// which accompanies this distribution, and is available
// at the URL "http://www.eclipse.org/legal/epl-v10.html".
//
// Initial Contributors:
// Nokia Corporation - initial contribution.
//
// Contributors:
//
// Description:
// Media driver for MultiMediaCard Flash device
// 
//

#include "mmc.h" 
#include "pbusmedia.h"
#include <drivers/emmcptn.h>

#if defined(__DEMAND_PAGING__)
	// If in debug mode, enable paging stats and their retrieval using DLocalDrive::EControlIO
	#if defined( _DEBUG)
		#define __TEST_PAGING_MEDIA_DRIVER__
	#endif
	#include "mmcdp.h"
#endif

#ifndef BTRACE_PAGING_MEDIA
	#undef BTraceContext8
	#define BTraceContext8(aCategory,aSubCategory,a1,a2) 
#endif	// BTRACE_PAGING_MEDIA

// Enable this macro to debug cache: 
// NB The greater the number of blocks, the slower this is...
//#define _DEBUG_CACHE
#ifdef _DEBUG_CACHE
#define __ASSERT_CACHE(c,p) (void)((c)||(p,0))
#else
#define __ASSERT_CACHE(c,p)
#endif


GLREF_C TInt GetMediaDefaultPartitionInfo(TMBRPartitionEntry& aPartitionEntry, TUint16& aReservedSectors, const TMMCard* aCardP);
GLREF_C TBool MBRMandatory(const TMMCard* aCardP);
GLREF_C TBool CreateMBRAfterFormat(const TMMCard* aCardP);
GLREF_C TInt BlockSize(const TMMCard* aCardP);
GLREF_C TInt EraseBlockSize(const TMMCard* aCardP);
GLREF_C TInt GetCardFormatInfo(const TMMCard* aCardP, TLDFormatInfo& aFormatInfo);

const TInt KStackNumber = 0;

const TInt KDiskSectorSize=512;
const TInt KDiskSectorShift=9;

const TInt KIdleCurrentInMilliAmps = 1;

const TInt KMBRFirstPartitionEntry=0x1BE;

template <class T>
inline T UMin(T aLeft,T aRight)
	{return(aLeft<aRight ? aLeft : aRight);}


class DPhysicalDeviceMediaMmcFlash : public DPhysicalDevice
	{
public:
	DPhysicalDeviceMediaMmcFlash();

	virtual TInt Install();
	virtual void GetCaps(TDes8& aDes) const;
	virtual TInt Create(DBase*& aChannel, TInt aMediaId, const TDesC8* aInfo, const TVersion& aVer);
	virtual TInt Validate(TInt aDeviceType, const TDesC8* aInfo, const TVersion& aVer);
	virtual TInt Info(TInt aFunction, TAny* a1);
	};


// these should be static const members of DMmcMediaDriverFlash, but VC doesn't support this
const TInt64 KInvalidBlock = -1;
const TInt KNoCacheBlock = -1;

class DMmcMediaDriverFlash : public DMediaDriver
	{
public:
	DMmcMediaDriverFlash(TInt aMediaId);
	~DMmcMediaDriverFlash();
	// ...from DMediaDriver
	virtual void Close();
	// replacing pure virtual
	virtual void Disconnect(DLocalDrive* aLocalDrive, TThreadMessage*);
	virtual TInt Request(TLocDrvRequest& aRequest);
	virtual TInt PartitionInfo(TPartitionInfo& anInfo);
	virtual void NotifyPowerDown();
	virtual void NotifyEmergencyPowerDown();
	// For creation by DPhysicalDeviceMediaMmcFlash
	TInt DoCreate(TInt aMediaId);

private:
	enum TPanic
		{
		EDRInUse		= 0x0000,	EDRStart, EDRNotPositive, EDREnd,
		ELRRequest		= 0x0010,	ELRStart, ELRNotPositive, ELREnd, ELRCached,
		EDWInUse		= 0x0020,	EDWStart, EDWNotPositive, EDWEnd,
		EDFInUse		= 0x0030,	EDFStart, EDFNotPositive, EDFEnd, ENotMmcSocket,
		ELWRequest		= 0x0040,	ELWStart, ELWFmtStAlign, ELWNotPositive, ELWEnd, ELWFmtEndAlign, 
									ELWLength, ELFStart, ELFEnd, ELFNotPositive,
		ERPIInUse		= 0x0050,
		EPCInUse		= 0x0060,	EPCFunc,
		ESECBQueued		= 0x0070,
		EDSEDRequest	= 0x0080,	EDSEDNotErrComplete,
		ECRReqIdle		= 0x0090,	ECRRequest,
		ERRBStAlign		= 0x00a0,	ERRBStPos, ERRBNotPositive, ERRBEndAlign, ERRBEndPos,
									ERRBOverflow, ERRBCchInv, ERRBExist,
		ERWBStPos		= 0x00b0,	ERWBNotPositive, ERWBEndPos, ERWBOverflow, ERWBCchInv,
		EMBStPos		= 0x00c0,	EMBStAlign, EMBNotPositive, EMBEndPos, EMBEndAlign,
									EMBOverflow, EMBCchInvPre, EMBCchInvPost,
		EBGAStPos		= 0x00d0,	EBGAStAlign, EBGANotPositive, EBGAEndPos, EBGAEndAlign,
									EBGAOverflow, EBGACchInv,
		EICMNegative	= 0x00e0,	EICMOverflow, ECMIOverflow,
		EGCBAlign		= 0x00f0,	EGCBPos, EGCBCchInv,
		
		ECFSessPtrNull	= 0x0100,	// Code Fault - session pointer NULL

		EDBNotEven		= 0x0110,	// Not and even number of blocks in the buffer cache
		EDBCBQueued		= 0x0111,	// The data transfer callback is already queued
		EDBLength		= 0x0112,	// The length of data to transfer in data transfer callback is not positive
		EDBLengthTooBig	= 0x0113,	// The length of data to transfer in data transfer callback is too big
		EDBOffsetTooBig = 0x0114,	// The Offset into the user data buffer is too big
		EDBCacheInvalid	= 0x0115,	// The cache is invalid at the end of data transfer
		EDBNotOptimal	= 0x0116,	// Due to Cache size DB functionality will never be utilised
		ENoDBSupport	= 0x0120,	// DMA request arrived but PSL does not support double buffering
		ENotDMAAligned  = 0x0121,
		};
	static void Panic(TPanic aPnc);

	enum TMediaRequest
		{
		EMReqRead = 0,
		EMReqWrite = 1,
		EMReqFormat = 2,
		EMReqPtnInfo,
		EMReqPswdCtrl,
		EMReqForceErase,
		EMReqUpdatePtnInfo,
		EMReqWritePasswordData,
		EMReqIdle,
		EMReqEMMCPtnInfo,
		};
	enum TMediaReqType {EMReqTypeNormalRd,EMReqTypeNormalWr,EMReqTypeUnlockPswd,EMReqTypeChangePswd};

	enum {KWtRBMFst = 0x00000001, 	// iWtRBM - Read First Block only
		  KWtRBMLst = 0x00000002,	// iWtRBM - Read Last Block only
		  KWtMinFst = 0x00000004,	// iWtRBM - Write First Block only
		  KWtMinLst = 0x00000008,	// iWtRBM - Write Last Block only
		  KIPCSetup = 0x00000010,	// iRdROB - IPC Setup Next Iteration
		  KIPCWrite = 0x00000020};	// iRdROB - IPC Write Next Iteration

private:
	// MMC device specific stuff
	TInt DoRead();
	TInt DoWrite();
	TInt DoFormat();
	TInt Caps(TLocDrv& aDrive, TLocalDriveCapsV6& aInfo);

	inline DMMCStack& Stack() const;
	inline TInt CardNum() const;
	inline TMediaRequest CurrentRequest() const;

	TInt LaunchRead(TInt64 aStart, TUint32 aLength);
	TInt LaunchDBRead();
	TInt LaunchPhysRead(TInt64 aStart, TUint32 aLength);
	
	TInt LaunchWrite(TInt64 aStart, TUint32 aLength, TMediaRequest aMedReq);
	TInt LaunchFormat(TInt64 aStart, TUint32 aLength);

	TInt LaunchRPIUnlock(TLocalDrivePasswordData& aData);
	TInt LaunchRPIRead();
	TInt LaunchRPIErase();
	TInt DecodePartitionInfo();
	TInt WritePartitionInfo();
	TInt GetDefaultPartitionInfo(TMBRPartitionEntry& aPartitionEntry);
	TInt CreateDefaultPartition();


#if defined __TEST_PAGING_MEDIA_DRIVER__
	TInt HandleControlIORequest();
#endif

	static void SetPartitionEntry(TPartitionEntry* aEntry, TUint aFirstSector, TUint aNumSectors);

	TInt CheckDevice(TMediaReqType aReqType);

	static void SessionEndCallBack(TAny* aMediaDriver);
	static void SessionEndDfc(TAny* aMediaDriver);
	void DoSessionEndDfc();

	static void DataTransferCallBack(TAny* aMediaDriver);
	static void DataTransferCallBackDfc(TAny* aMediaDriver);

	void DoReadDataTransferCallBack();
	void DoWriteDataTransferCallBack();
	void DoPhysReadDataTransferCallBack();
	void DoPhysWriteDataTransferCallBack();
	
	TInt AdjustPhysicalFragment(TPhysAddr &physAddr, TInt &physLength);
	TInt PrepareFirstPhysicalFragment(TPhysAddr &aPhysAddr, TInt &aPhysLength, TUint32 aLength);
	void PrepareNextPhysicalFragment();

	TInt EngageAndSetReadRequest(TMediaRequest aRequest);
	TInt EngageAndSetWriteRequest(TMediaRequest aRequest);
	TInt EngageAndSetRequest(TMediaRequest aRequest, TInt aCurrent);
	void CompleteRequest(TInt aReason);

	TInt ReadDataUntilCacheExhausted(TBool* aAllDone);
	TInt WriteDataToUser(TUint8* aBufPtr);
	TInt ReadDataFromUser(TDes8& aDes, TInt aOffset);
	TUint8* ReserveReadBlocks(TInt64 aStart, TInt64 aEnd, TUint32* aLength);
	TUint8* ReserveWriteBlocks(TInt64 aMedStart, TInt64 aMedEnd, TUint* aRBM);
	void MarkBlocks(TInt64 aStart, TInt64 aEnd, TInt aStartIndex);
	void BuildGammaArray(TInt64 aStart, TInt64 aEnd);
	void InvalidateCache();
	void InvalidateCache(TInt64 aStart, TInt64 aEnd);
	TUint8* IdxToCchMem(TInt aIdx) const;
	TInt CchMemToIdx(TUint8* aMemP) const;

	TInt DoPasswordOp();
	void PasswordControl(TInt aFunc, TLocalDrivePasswordData& aData);
	void Reset();
	TInt AllocateSession();  

#ifdef _DEBUG_CACHE
	TBool CacheInvariant();
	TUint8* GetCachedBlock(TInt64 aAddr);
#endif
private:
	DMMCStack* iStack;			 				// controller objects
	TMMCard* iCard;
	DMMCSession* iSession;
	DMMCSocket* iSocket;

	TInt iCardNumber;

	TUint iBlkLenLog2;							// cached CSD data
	TUint32 iBlkLen;
	TInt64 iBlkMsk;
	TBool iReadBlPartial;
	TUint32 iPrWtGpLen;							// preferred write group size in bytes,
	TInt64 iPrWtGpMsk;

	TInt iReadCurrentInMilliAmps;				// power management
	TInt iWriteCurrentInMilliAmps;

	TUint8* iMinorBuf;							// MBR, CMD42, partial read
	TUint8* iCacheBuf;							// cached buffer
	TUint32 iMaxBufSize;
	TInt iBlocksInBuffer;
	TInt64* iCachedBlocks;
	TInt* iGamma;								// B lookup, ReserveReadBlocks()
	TUint8* iIntBuf;							// start of current buffer region
	TInt iLstUsdCchEnt;							// index of last used cache entry

	TLocDrvRequest* iCurrentReq;				// Current Request
	TMediaRequest iMedReq;
	
	TInt64 iReqStart;							// user-requested start region
	TInt64 iReqCur;								// Currently requested start region
	TInt64 iReqEnd;							    // user-requested end region
	TInt64 iPhysStart;							// physical region for one operation
	TInt64 iPhysEnd;						    // physical end point for one operation
	TInt64 iDbEnd;								// Double buffer end point for one operation

	TUint64 iEraseUnitMsk;

	TUint iWtRBM;								// Write - Read Before Modify Flags
	TUint iRdROB;								// Read  - Read Odd Blocks Flags
	
	TInt iFragOfset;			
	TUint32 iIPCLen;
	TUint32 iNxtIPCLen;
	TUint32 iBufOfset;
	
	TUint iHiddenSectors;						// bootup / password

	TMMCCallBack iSessionEndCallBack;
	TDfc iSessionEndDfc;

	TPartitionInfo* iPartitionInfo;
	TMMCMediaTypeEnum iMediaType;
	TMMCEraseInfo iEraseInfo;
	TBool iMbrMissing;
	TInt iMediaId;

	DMMCStack::TDemandPagingInfo iDemandPagingInfo;

#if defined(__TEST_PAGING_MEDIA_DRIVER__)
	SMmcStats iMmcStats;
#endif // __TEST_PAGING_MEDIA_DRIVER__

	TMMCCallBack iDataTransferCallBack;	// Callback registered with the MMC stack to perform double-buffering
	TDfc iDataTransferCallBackDfc;		// ...and the associated DFC queue.

	TBool iSecondBuffer;				// Specified the currently active buffer
	TBool iDoLastRMW;					// ETrue if the last double-buffer transfer requires RMW modification
	TBool iDoDoubleBuffer;				// ETrue if double-buffering is currently active
	TBool iDoPhysicalAddress;			// ETrue if Physical Addressing is currently active
	TBool iCreateMbr;
	TBool iReadToEndOfCard;				// {Read Only} ETrue if Reading to end of Card

	TBool iInternalSlot;

	DEMMCPartitionInfo* iMmcPartitionInfo;  // Responsible for decoding partitions for embedded devices
	};
	
// ======== DPhysicalDeviceMediaMmcFlash ========


DPhysicalDeviceMediaMmcFlash::DPhysicalDeviceMediaMmcFlash()
	{
	__KTRACE_OPT(KPBUSDRV, Kern::Printf("=mmd:ctr"));

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


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

	_LIT(KDrvNm, "Media.MmcF");
	return SetName(&KDrvNm);
	}


void DPhysicalDeviceMediaMmcFlash::GetCaps(TDes8& /* aDes */) const
	{
	__KTRACE_OPT(KPBUSDRV, Kern::Printf("=mmd:cap"));
	}
								 
									 
TInt DPhysicalDeviceMediaMmcFlash::Info(TInt aFunction, TAny* /*a1*/)
//
// Return the priority of this media driver
//
	{
	__KTRACE_OPT(KPBUSDRV, Kern::Printf("=mmd:info"));
	if (aFunction==EPriority)
		return KMediaDriverPriorityNormal;
	// Don't close media driver when peripheral bus powers down. This avoids the need for Caps() to power up the stack.
	if (aFunction==EMediaDriverPersistent)
		return KErrNone;
	return KErrNotSupported;
	}
								 
TInt DPhysicalDeviceMediaMmcFlash::Validate(TInt aDeviceType, const TDesC8* /*aInfo*/, const TVersion& aVer)
	{
	__KTRACE_OPT(KPBUSDRV, Kern::Printf("=mmd:validate aDeviceType %d", aDeviceType));
	if (!Kern::QueryVersionSupported(iVersion,aVer))
		return KErrNotSupported;
	if (aDeviceType!=MEDIA_DEVICE_MMC)
		return KErrNotSupported;
	return KErrNone;
	}
								 
TInt DPhysicalDeviceMediaMmcFlash::Create(DBase*& aChannel, TInt aMediaId, const TDesC8* /*aInfo*/, const TVersion& aVer)
//
// Create an MMC Card media driver.
//
	{
	__KTRACE_OPT(KPBUSDRV, Kern::Printf(">mmd:crt"));

	if (!Kern::QueryVersionSupported(iVersion,aVer))
		return KErrNotSupported;

	DMmcMediaDriverFlash* pD = new DMmcMediaDriverFlash(aMediaId);
	aChannel=pD;

	TInt r=KErrNoMemory;
	if (pD)
		r=pD->DoCreate(aMediaId);
	if (r==KErrNone)
		pD->OpenMediaDriverComplete(KErrNone);

	__KTRACE_OPT(KPBUSDRV, Kern::Printf("<mmd:mdf"));
	return r;
	}


// ======== DMmcMediaDriverFlash ========


void DMmcMediaDriverFlash::Panic(TPanic aPanic)
	{
	_LIT(KPncNm, "MEDMMC");
	Kern::PanicCurrentThread(KPncNm, aPanic);
	}


// ---- accessor functions -----

inline DMMCStack& DMmcMediaDriverFlash::Stack() const
	{ return *static_cast<DMMCStack*>(iStack); }


inline TInt DMmcMediaDriverFlash::CardNum() const
	{ return iCardNumber; }


inline DMmcMediaDriverFlash::TMediaRequest DMmcMediaDriverFlash::CurrentRequest() const
	{ return iMedReq; }


// Helper
template <class T>
inline T* KernAlloc(const TUint32 n)
	{ return static_cast<T*>(Kern::Alloc(n * sizeof(T))); }

// ---- ctor, open, close, dtor ----

#pragma warning( disable : 4355 )	// this used in initializer list
DMmcMediaDriverFlash::DMmcMediaDriverFlash(TInt aMediaId)
   :DMediaDriver(aMediaId),
	iMedReq(EMReqIdle),
    iSessionEndCallBack(DMmcMediaDriverFlash::SessionEndCallBack, this),
	iSessionEndDfc(DMmcMediaDriverFlash::SessionEndDfc, this, 1),
	iMediaId(iPrimaryMedia->iNextMediaId),
    iDataTransferCallBack(DMmcMediaDriverFlash::DataTransferCallBack, this),
	iDataTransferCallBackDfc(DMmcMediaDriverFlash::DataTransferCallBackDfc, this, 1)
	{
	__KTRACE_OPT(KPBUSDRV, Kern::Printf("=mmd:mmd"));
	// NB aMedia Id = the media ID of the primary media, iMediaId = the media ID of this media
	__KTRACE_OPT(KPBUSDRV, Kern::Printf("DMmcMediaDriverFlash(), iMediaId %d, aMediaId %d\n", iMediaId, aMediaId));
	}

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

	iSocket = ((DMMCSocket*)((DPBusPrimaryMedia*)iPrimaryMedia)->iSocket);
	if(iSocket == NULL)
		return(KErrNoMemory);

	iCardNumber = ((DPBusPrimaryMedia*)iPrimaryMedia)->iSlotNumber;

	iStack = iSocket->Stack(KStackNumber);
	iCard = iStack->CardP(CardNum());

	TMMCMachineInfo machineInfo;
	Stack().MachineInfo(machineInfo);
	TInt slotFlag = iCardNumber == 0 ? TMMCMachineInfo::ESlot1Internal : TMMCMachineInfo::ESlot2Internal;
	iInternalSlot = machineInfo.iFlags & slotFlag;

	__KTRACE_OPT(KPBUSDRV, Kern::Printf("DMmcMediaDriverFlash::DoCreate() slotNumber %d iInternalSlot %d", iCardNumber, iInternalSlot));


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

	// check right type of card
	if ((iMediaType=iCard->MediaType())==EMultiMediaNotSupported)	
		return(KErrNotReady);

	// get card characteristics
	const TCSD& csd = iCard->CSD();
	iBlkLenLog2 = iCard->MaxReadBlLen();
	iBlkLen = 1 << iBlkLenLog2;
	iBlkMsk = (TInt64)(iBlkLen - 1);

	SetTotalSizeInBytes(iCard->DeviceSize64());
	
	//
	// High capcity cards (block addressable, MMCV4.2, SD2.0) do not support partial reads
	// ...some cards incorrectly report that they do, so ensure that we don't
	//
	iReadBlPartial = iCard->IsHighCapacity() ? EFalse : csd.ReadBlPartial();

	// allocate and initialize session object
	TInt r = AllocateSession();
	if (r!= KErrNone)
		return r;

	// get buffer memory from EPBUS
	TUint8* buf;
	TInt bufLen;
	TInt minorBufLen;
	Stack().BufferInfo(buf, bufLen, minorBufLen);

	iMinorBuf = buf;
	
	// cache buffer can use rest of blocks in buffer.  Does not have to be power of 2.
	iCacheBuf = iMinorBuf + minorBufLen;

	// We need to devide up the buffer space between the media drivers.
	// The number of buffer sub-areas = number of physical card slots * number of media
	bufLen-= minorBufLen;
	DPBusPrimaryMedia* primaryMedia = (DPBusPrimaryMedia*) iPrimaryMedia;
	TInt physicalCardSlots = iStack->iMaxCardsInStack;
	TInt numMedia = primaryMedia->iLastMediaId - primaryMedia->iMediaId + 1;
	TInt totalNumMedia = numMedia * physicalCardSlots;

	TInt mediaIndex = iMediaId - primaryMedia->iMediaId;
	TInt bufIndex = (iCardNumber * numMedia)  + mediaIndex;
	
	__KTRACE_OPT(KPBUSDRV, Kern::Printf("physicalCardSlots %d, iCardNumber %d\n",  physicalCardSlots, iCardNumber));
	__KTRACE_OPT(KPBUSDRV, Kern::Printf("iMediaId %d numMedia %d, mediaIndex %d, totalNumMedia %d, bufIndex %d\n", 
			  iMediaId, numMedia, mediaIndex, totalNumMedia, bufIndex));

	__KTRACE_OPT(KPBUSDRV, Kern::Printf("bufLen1 %08X iCacheBuf1 %08X", bufLen, iCacheBuf));
	bufLen/= totalNumMedia;
	iCacheBuf+= bufIndex * bufLen;
	__KTRACE_OPT(KPBUSDRV, Kern::Printf("bufLen2 %08X iCacheBuf2 %08X", bufLen, iCacheBuf));

	iBlocksInBuffer = bufLen >> iBlkLenLog2;	// may lose partial block
	if(iSocket->SupportsDoubleBuffering())
		{
		// Ensure that there's always an even number of buffered blocks when double-buffering
		iBlocksInBuffer &= ~1;
		__ASSERT_DEBUG(iBlocksInBuffer >= 2, Panic(EDBNotEven));
#if defined(_DEBUG)		
		/** 
		 * If Double-Buffering is enabled then the cache should not be greater than the maximum addressable range of the DMA controller,
		 * otherwise Double buffering will never be utilised because all transfers will fit into the cache.
		 */
		const TUint32 maxDbBlocks = iSocket->MaxDataTransferLength() >> iBlkLenLog2;
		__ASSERT_DEBUG(iBlocksInBuffer <= (TInt)maxDbBlocks, Panic(EDBNotOptimal));
#endif		
		}

	iMaxBufSize = iBlocksInBuffer << iBlkLenLog2;

	iPrWtGpLen = iCard->PreferredWriteGroupLength();

	// check the preferred write group length is a power of two
	if(iPrWtGpLen == 0 || (iPrWtGpLen & (~iPrWtGpLen + 1)) != iPrWtGpLen)
		return(KErrNotReady);

	__KTRACE_OPT(KPBUSDRV, Kern::Printf("iMaxBufSize %d iPrWtGpLen %d\n", iMaxBufSize, iPrWtGpLen));
	// ensure the preferred write group length is as large as possible
	// so we can write to more than one write group at once
	while (iPrWtGpLen < (TUint32) iMaxBufSize)
		iPrWtGpLen <<= 1;

	// ensure preferred write group length is no greater than internal cache buffer
	while (iPrWtGpLen > (TUint32) iMaxBufSize)
		iPrWtGpLen >>= 1;
	iPrWtGpMsk = TInt64(iPrWtGpLen - 1);

	__KTRACE_OPT(KPBUSDRV, Kern::Printf("iPrWtGpLen #2 %d\n", iPrWtGpLen));

	// allocate index for cached blocks
	iCachedBlocks =	KernAlloc<TInt64>(iBlocksInBuffer);
	if (iCachedBlocks == 0)
		return(KErrNoMemory);

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

	// allocate read lookup index
	iGamma = KernAlloc<TInt>(iBlocksInBuffer);
	if (iGamma == 0)
		return(KErrNoMemory);

	// get current requirements
	iReadCurrentInMilliAmps = csd.MaxReadCurrentInMilliamps();
	iWriteCurrentInMilliAmps = csd.MaxWriteCurrentInMilliamps();

	// get preferred erase information for format operations
	const TInt err = iCard->GetEraseInfo(iEraseInfo);
	if(err != KErrNone)
		return(err);

	iEraseUnitMsk = TInt64(iEraseInfo.iPreferredEraseUnitSize) - 1;

	// Retrieve the demand paging info from the PSL of the stack
	Stack().DemandPagingInfo(iDemandPagingInfo);

	// if a password has been supplied then it is sent when the partition info is read

	//
	// If this is an internal slot, then use the eMMC partition function
	//
	if(iInternalSlot)
		{
		iMmcPartitionInfo = CreateEmmcPartitionInfo();
		if(iMmcPartitionInfo == NULL)
			return KErrNoMemory;

		TInt err = iMmcPartitionInfo->Initialise(this);
		if(err != KErrNone)
			return err;
		}

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

	return(KErrNone);
	}

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


DMmcMediaDriverFlash::~DMmcMediaDriverFlash()
	{
	__KTRACE_OPT(KPBUSDRV, Kern::Printf(">mmd:dtr"));

	iSessionEndDfc.Cancel();
	iDataTransferCallBackDfc.Cancel();

	delete iSession;
	Kern::Free(iCachedBlocks);
	Kern::Free(iGamma);

	delete iMmcPartitionInfo;

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


TInt DMmcMediaDriverFlash::AllocateSession()
	{
	// already allocated ?
	if (iSession != NULL)
		return KErrNone;

	iSession = iStack->AllocSession(iSessionEndCallBack);
	if (iSession == NULL)
		return KErrNoMemory;

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

	return KErrNone;
	}

// ---- media access ----

TInt DMmcMediaDriverFlash::DoRead()
//
// set up iReqStart, iReqEnd and iReqCur and launch first read.  Subsequent reads
// will be launched from the callback DFC.
//
	{
	TInt r = CheckDevice(EMReqTypeNormalRd); 
	if (r != KErrNone) return r;
	
	const TInt64 pos(iCurrentReq->Pos());
	TUint32 length(I64LOW(iCurrentReq->Length()));

	__KTRACE_OPT(KPBUSDRV, Kern::Printf(">mmd:dr:0x%lx,0x%x", pos, length));
	__ASSERT_DEBUG(CurrentRequest() == EMReqIdle, Panic(EDRInUse));
	__ASSERT_DEBUG(pos < TotalSizeInBytes(), Panic(EDRStart));
	__ASSERT_DEBUG(iCurrentReq->Length() >= 0, Panic(EDRNotPositive));
	__ASSERT_DEBUG(TotalSizeInBytes() >= pos + length, Panic(EDREnd));

	if(length > 0)
		{
		iReqCur = iReqStart = pos;
		iReqEnd = iReqStart + length;

		TBool allDone(EFalse);
		if ( ((r = ReadDataUntilCacheExhausted(&allDone)) == KErrNone) && !allDone)
			{
			iMedReq = EMReqRead;
			iPhysStart = iReqCur & ~iBlkMsk;
			__ASSERT_DEBUG(I64HIGH(iPhysStart >> KMMCardHighCapBlockSizeLog2) == 0, Panic(ELRStart));
			
			iReadToEndOfCard = ( iReqEnd >= TotalSizeInBytes() );
			// Re-calculate length as some data may have been recovered from cache
			length = I64LOW(iReqEnd - iReqCur);
			
			if (iCurrentReq->IsPhysicalAddress() && !iReadToEndOfCard && (length >= iBlkLen) )
				r = LaunchPhysRead(iReqCur, length);
			else if ( (iReqEnd - iPhysStart) > iMaxBufSize && iSocket->SupportsDoubleBuffering() && !iReadToEndOfCard)
				r = LaunchDBRead();
			else
				r = LaunchRead(iReqCur, length);

			if (r == KErrNone) return r;
			}
		}
	else
		{
#if defined(__DEMAND_PAGING__) && !defined(__WINS__)
		if (DMediaPagingDevice::PageInRequest(*iCurrentReq))
			{
			r = iCurrentReq->WriteToPageHandler(NULL, 0, 0);
			}
		else
#endif	// __DEMAND_PAGING__
			{
			TPtrC8 zeroDes(NULL, 0);
			r = iCurrentReq->WriteRemote(&zeroDes,0);
			}
		}

	// error occurred or read all from cache so complete immediately
	if(r == KErrNone)
		r = KErrCompletion;

	__KTRACE_OPT(KPBUSDRV, Kern::Printf("<mmd:dr:%d", r));
	
	return r;
	}


TInt DMmcMediaDriverFlash::LaunchRead(TInt64 aStart, TUint32 aLength)
//
// starts reads from DoRead() and the session end DFC.  This function does not maintain the
// iReq* instance variables.  It sets iPhysStart and iPhysEnd to the region that was actually
// read into iIntBuf. iIntBuf can be set to a cached entry or to the minor buffer.  It is
// assumed that before this function is called that ReadDataUntilCacheExhausted() has been used.
//
	{
	__KTRACE_OPT(KPBUSDRV, Kern::Printf(">mmd:lr:0x%lx,0x%x", aStart, aLength));
	__ASSERT_DEBUG(TotalSizeInBytes() > aStart, Panic(ELRStart));
	__ASSERT_DEBUG(aLength > 0, Panic(ELRNotPositive));
	__ASSERT_DEBUG(TotalSizeInBytes() >= aStart + aLength, Panic(ELREnd));
	__ASSERT_CACHE(GetCachedBlock(aStart & ~iBlkMsk) == 0, Panic(ELRCached));
	__ASSERT_DEBUG(iSession != NULL, Panic(ECFSessPtrNull));

	iDoPhysicalAddress = EFalse;
	iDoDoubleBuffer = EFalse;
	iSecondBuffer = EFalse;
	
	// 
	// if this read goes up to the end of the card then use only 
	// single sector reads to avoid CMD12 timing problems
	//
	const TUint32 bufSize(iReadToEndOfCard ? iBlkLen : iMaxBufSize);
	
	iPhysEnd = (UMin(iReqEnd, iPhysStart + bufSize) + iBlkMsk) & ~iBlkMsk;
	
	TUint32 physLen(I64LOW(iPhysEnd - iPhysStart));
	
	__ASSERT_DEBUG(I64HIGH(iPhysEnd - iPhysStart) == 0, Panic(ELREnd));

	// partial reads must be within a single physical block
	if (iReadBlPartial && physLen == iBlkLen && aLength <= (iBlkLen >> 1))
		{
		// 
		// Note : Partial reads are not supported for large block devices 
		//        (MMCV4.2 and SD2.0 high capacity cards)
		//
		__ASSERT_DEBUG(I64HIGH(aStart) == 0, Panic(ELRStart));
		__ASSERT_DEBUG(I64HIGH(aStart + aLength) == 0, Panic(ELREnd));

		iIntBuf = iMinorBuf;
		Stack().AdjustPartialRead(iCard, I64LOW(aStart), I64LOW(aStart + aLength), (TUint32*)&iPhysStart, (TUint32*)&iPhysEnd);
		iSession->SetupCIMReadBlock(I64LOW(iPhysStart >> KMMCardHighCapBlockSizeLog2), iIntBuf, physLen >> KMMCardHighCapBlockSizeLog2);
		}
	else
		{	
		iIntBuf = ReserveReadBlocks(iPhysStart, iPhysEnd, &physLen);
			
		// EPBUSM automatically uses CMD17 instead of CMD18 for single block reads
		iSession->SetupCIMReadBlock(I64LOW(iPhysStart >> KMMCardHighCapBlockSizeLog2), iIntBuf, physLen >> KMMCardHighCapBlockSizeLog2);
		
		// Update Physical end point as less may have been required due to additional blocks found in cache during ReserveReadBlocks
		iPhysEnd = iPhysStart + physLen;
		}
	
	TInt r = EngageAndSetReadRequest(EMReqRead);
	
	__KTRACE_OPT(KPBUSDRV, Kern::Printf("<mmd:lr:%d", r));
	return r;
	}

TInt DMmcMediaDriverFlash::LaunchDBRead()
//
// starts reads from DoRead() and the session end DFC.  This function does not maintain the
// iReq* instance variables.  It sets iPhysStart and iPhysEnd to the region that was actually
// read into iIntBuf. iIntBuf can be set to a cached entry or to the minor buffer.  It is
// assumed that before this function is called that ReadDataUntilCacheExhausted() has been used.
//
	{
	__KTRACE_OPT(KPBUSDRV, Kern::Printf(">mmd:ldbr:0x%lx,0x%x", iReqCur, I64LOW(iReqEnd - iReqCur)));
	__ASSERT_DEBUG(TotalSizeInBytes() > iReqCur, Panic(ELRStart));
	__ASSERT_DEBUG(I64LOW(iReqEnd - iReqCur) > 0, Panic(ELRNotPositive));
	__ASSERT_DEBUG(TotalSizeInBytes() >= iReqEnd, Panic(ELREnd));
	__ASSERT_CACHE(GetCachedBlock(iReqCur & ~iBlkMsk) == 0, Panic(ELRCached));
	__ASSERT_DEBUG(iSession != NULL, Panic(ECFSessPtrNull));

	iDoDoubleBuffer = ETrue;
	iDoPhysicalAddress = EFalse;
	
	iDbEnd = iReqEnd;
	const TUint32 maxDbLength = iSocket->MaxDataTransferLength();

	if(maxDbLength)
		{
		//
		// If the PSL specifies a limit on the maximum size of a data transfer, then truncate the request...
		//
		iDbEnd = UMin(iDbEnd, iPhysStart + maxDbLength);
		}

	iDbEnd = (iDbEnd + iBlkMsk) & ~iBlkMsk;

	const TUint32 doubleBufferSize = iMaxBufSize >> 1;
	iPhysEnd = (iReqCur + doubleBufferSize) & ~iBlkMsk;	// The end of the first double-buffered transfer
	
	//
	// If we're double-buffering, then the entire cache will be re-used
	// continuously.  Rather than continually reserve blocks during each 
	// transfer we calculate the blocks that will be present after all
	// transfers have completed. 
	// @see DoSessionEndDfc()
	//
	InvalidateCache();
	iIntBuf = iCacheBuf;
	
	iSession->SetupCIMReadBlock(I64LOW(iPhysStart >> KMMCardHighCapBlockSizeLog2), iIntBuf, I64LOW(iDbEnd - iPhysStart) >> KMMCardHighCapBlockSizeLog2);

	iSession->EnableDoubleBuffering(doubleBufferSize >> KDiskSectorShift);

	// ...and switch to the 'second' buffer, which will be populated in the
	// data transfer callback in parallel with hardware transfer of the first.
	iSecondBuffer = ETrue;

	TInt r = EngageAndSetReadRequest(EMReqRead);

	__KTRACE_OPT(KPBUSDRV, Kern::Printf("<mmd:ldbr:%d", r));

	return r;
	}


TInt DMmcMediaDriverFlash::LaunchPhysRead(TInt64 aStart, TUint32 aLength)
//
// This function does not maintain the iReq* instance variables.  
// It is assumed that before this function is called that 
// ReadDataUntilCacheExhausted() has been used.
//
	{
	__KTRACE_OPT(KPBUSDRV, Kern::Printf(">mmd:physr:0x%lx,0x%x", aStart, aLength));
	__ASSERT_DEBUG(TotalSizeInBytes() > aStart, Panic(ELRStart));
	__ASSERT_DEBUG(aLength > 0, Panic(ELRNotPositive));
	__ASSERT_DEBUG(TotalSizeInBytes() >= aStart + aLength, Panic(ELREnd));
	__ASSERT_CACHE(GetCachedBlock(aStart & ~iBlkMsk) == 0, Panic(ELRCached));
	__ASSERT_DEBUG(iSession != NULL, Panic(ECFSessPtrNull));

	TInt r(KErrNone);
	
	iDoPhysicalAddress = ETrue;  
	iDoDoubleBuffer = EFalse;
	
	// Local Media Subsystem ensures DMA Addressable range not exceeded.
	// @see LocDrv::RegisterDmaDevice()
	iPhysEnd = (iReqEnd + iBlkMsk) & ~iBlkMsk;
	
	iRdROB = 0;
	iFragOfset = iIPCLen = iNxtIPCLen = iBufOfset = 0; 
	
	// Determine if start/end are block aligned
	// physical memory can only read the exact amount, not more!
	const TBool firstPartial( (aStart & iBlkMsk) != 0);
	
	TPhysAddr physAddr(0);						
	TInt physLength(0);
	TUint32 physLen(I64LOW(iPhysEnd - iPhysStart));
	
	if (firstPartial)
		{
		__KTRACE_OPT(KPBUSDRV, Kern::Printf(">mmd:physr:FirstPartial"));
		// first index does not start on block boundary
		// iIntBuf linear address is used for IPC within DoReadDataTransferCallBack()
		iRdROB |= KIPCWrite;
		
		iIntBuf = ReserveReadBlocks(iPhysStart, iPhysStart+iBlkLen,(TUint32*)&physLength);
#if !defined(__WINS__)
		physAddr = Epoc::LinearToPhysical((TLinAddr)iIntBuf);
#else
		physAddr = (TPhysAddr)iIntBuf;
#endif
		// Set SecondBuffer flag to indicate IPC cannot be done on next callback
		iSecondBuffer = ETrue;
		iBufOfset = I64LOW(iReqStart - iPhysStart);
		//iReqCur already set in DoRead;
		iFragOfset = iNxtIPCLen = physLength - iBufOfset;
		}				
	else
		{
		// Determine offset from start due to data possibly recovered from local cache
		iFragOfset = I64LOW(aStart - iReqStart);	
		r = PrepareFirstPhysicalFragment(physAddr, physLength, aLength);
		
		// No use for secondBuffer yet...
		iSecondBuffer = EFalse;
		}
	
	if(r == KErrNone)
   		{         
   		iDbEnd = iPhysEnd;
   		iPhysEnd = iPhysStart + physLength;
   		
        if ((TUint32)physLength > physLen) physLength = physLen; // more memory in chunk than required
        
    	iSession->SetupCIMReadBlock(I64LOW(iPhysStart >> KMMCardHighCapBlockSizeLog2), (TUint8*)physAddr, physLen >> KMMCardHighCapBlockSizeLog2);				
    	
		iSession->Command().iFlags|= KMMCCmdFlagPhysAddr;
		iSession->EnableDoubleBuffering(physLength >> KDiskSectorShift);
		
		r = EngageAndSetReadRequest(EMReqRead);
   		} 
		
	__KTRACE_OPT(KPBUSDRV, Kern::Printf("<mmd:lphysr:%d", r));

	return r;
	}


TInt DMmcMediaDriverFlash::DoWrite()
//
// set up iReqStart, iReqEnd, and iReqCur, and launch first write.  Any subsequent
// writes are launched from the session end DFC.  LaunchWrite() handles pre-reading
// any sectors that are only partially modified.
//
	{
	const TInt64 pos = iCurrentReq->Pos();
	const TUint32 length = I64LOW(iCurrentReq->Length());

	__KTRACE_OPT(KPBUSDRV, Kern::Printf(">mmd:dw:0x%lx,0x%x", pos, length));
	__ASSERT_DEBUG(CurrentRequest() == EMReqIdle, Panic(EDWInUse));
	__ASSERT_DEBUG(pos < TotalSizeInBytes(), Panic(EDWStart));
	__ASSERT_DEBUG(length > 0, Panic(EDWNotPositive));
	__ASSERT_DEBUG(TotalSizeInBytes() >= pos + length, Panic(EDWEnd));

	iReqCur = iReqStart = pos;
	iReqEnd = iReqStart + length;

	// iWtRBM is zero on construction because CBase-derived, and cleared at end
	// of successful writes.  If a write does not complete successfully, it may
	// be left in non-zero state.
	iWtRBM = 0;
	
	iSecondBuffer  = EFalse;
	iDoLastRMW     = EFalse;
	iDoDoubleBuffer= EFalse;
	iDoPhysicalAddress = EFalse;
	
	const TInt r = LaunchWrite(iReqStart, length, EMReqWrite);

	__KTRACE_OPT(KPBUSDRV, Kern::Printf("<mmd:dw:%d", r));

	return r;
	}


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

	__KTRACE_OPT(KPBUSDRV, Kern::Printf(">mmd:df:0x%lx,0x%x", pos, length));
	__ASSERT_DEBUG(CurrentRequest() == EMReqIdle, Panic(EDFInUse));
	__ASSERT_DEBUG(pos < TotalSizeInBytes(), Panic(EDFStart));
	__ASSERT_DEBUG(length > 0, Panic(EDFNotPositive));
	__ASSERT_DEBUG(TotalSizeInBytes() >= pos + length, Panic(EDFEnd));

	iReqCur = iReqStart = pos & ~iBlkMsk;
	iReqEnd = (iReqStart + length + iBlkMsk) & ~iBlkMsk;

	// the cache isn't maintained during a format operation to avoid redundantly
	// writing 0xff to memory (the blocks won't be re-used.)
	InvalidateCache();

	// create an MBR after the first format step (or second if misaligned) 
 
	if (iInternalSlot)
		{
		iCreateMbr = EFalse;
		}
	else
		{
		if (iReqStart == (TInt64(iHiddenSectors) << KDiskSectorShift) && CreateMBRAfterFormat(iCard))
			iCreateMbr = ETrue;
		}

	const TInt r = LaunchFormat(iReqStart, I64LOW(iReqEnd - iReqStart));

	__KTRACE_OPT(KPBUSDRV, Kern::Printf("<mmd:df:%d", r));

	return r;
	}


TInt DMmcMediaDriverFlash::LaunchFormat(TInt64 aStart, TUint32 aLength)
//
// starts writes from DoWrite(), DoFormat() and the session end DFC.  This function does not
// maintain the iReq* instance variables.  It sets iIntBuf, iPhysStart and iPhysEnd.
//
	{
	__KTRACE_OPT(KPBUSDRV, Kern::Printf(">mmd:lf:0x%lx,0x%x", aStart, aLength));
	__ASSERT_DEBUG(TotalSizeInBytes() > aStart, Panic(ELFStart));
	__ASSERT_DEBUG((aStart & iBlkMsk) == 0, Panic(ELWFmtStAlign));
	__ASSERT_DEBUG(aLength > 0, Panic(ELFNotPositive));
	__ASSERT_DEBUG(TotalSizeInBytes() >= aStart + aLength, Panic(ELFEnd));
	__ASSERT_DEBUG((aLength & iBlkMsk) == 0, Panic(ELWFmtEndAlign));
	__ASSERT_DEBUG(iSession != NULL, Panic(ECFSessPtrNull));

	TInt r;

	if ((r = CheckDevice(EMReqTypeNormalWr)) == KErrNone)
		{
		iPhysStart = aStart & ~iBlkMsk;

		// formats are always block-aligned, and the buffer is initialized to 0xff
		//  Check whether erase commands are supported by this card
		if (iCard->CSD().CCC() & KMMCCmdClassErase)
			{
			// Determine the erase end point for the next command. We don't erase past the preferred erase unit
			// size. Therefore, check which is lower, the preferred erase unit size or the end of the requested range.
			TInt64 prefEraseUnitEnd = (iPhysStart + iEraseInfo.iPreferredEraseUnitSize) & ~iEraseUnitMsk;
			iPhysEnd = UMin(prefEraseUnitEnd, aStart + aLength);

			const TUint32 minEraseSectorSize=iEraseInfo.iMinEraseSectorSize;
			const TInt64  minEraseSecMsk = TInt64(minEraseSectorSize-1);
			
			// If erase start point doesn't lie on a min. erase unit boundary, then truncate the erase endpoint to
			// the next min. erase unit boundary (assuming requested range allows this)
			if ((iPhysStart & minEraseSecMsk)!=0)
				{
				prefEraseUnitEnd=(iPhysStart+minEraseSectorSize) & ~minEraseSecMsk;
				iPhysEnd=UMin(prefEraseUnitEnd,iPhysEnd);
				}
				
			// Otherwise, if calculated erase end point doesn't lie on a min. erase unit boundary, but is at least one
			// min. erase unit beyond the erase start point then move erase endpoint back to last min. erase unit boundary
			else if ((iPhysEnd & minEraseSecMsk)!=0 && (iPhysEnd & ~minEraseSecMsk)>iPhysStart)
				{
				iPhysEnd&=(~minEraseSecMsk);
				}

			// Now, if the erase start/end points are aligned to a min. erase unit boundary, we can use an erase cmd.
			if ((iPhysStart & minEraseSecMsk) == 0 && (iPhysEnd & minEraseSecMsk) == 0)
				{
				// Aligned erase
				//  Check that erase commands are supported prior to issuing an erase command
				if(iEraseInfo.EraseGroupCmdsSupported())
					{
					iSession->SetupCIMEraseMGroup(I64LOW(iPhysStart >> KMMCardHighCapBlockSizeLog2),
												  I64LOW((iPhysEnd-iPhysStart) >> KMMCardHighCapBlockSizeLog2)); // Use ACMD35/36/38 (Erase Group)
					}
				else
					{
					iSession->SetupCIMEraseMSector(I64LOW(iPhysStart >> KMMCardHighCapBlockSizeLog2),
												   I64LOW((iPhysEnd - iPhysStart) >> KMMCardHighCapBlockSizeLog2)); // Use ACMD32/33/38 (Erase Sector)
					}
				}
			else
				{
				// Misaligned erase - use multi-block write. However, first - check write length doesn't exceed buffer size.
				if ((iPhysEnd-iPhysStart)>(TUint32)iMaxBufSize)
					{
					iPhysEnd=(iPhysStart+iMaxBufSize);
					}
					
				__ASSERT_DEBUG((iPhysEnd - iPhysStart) > 0, Panic(ELWLength));
				const TUint32 writeLen = I64LOW(iPhysEnd - iPhysStart);
				memset (iCacheBuf, 0x00, writeLen);
				iSession->SetupCIMWriteBlock(I64LOW(iPhysStart >> KMMCardHighCapBlockSizeLog2), iCacheBuf, writeLen >> KMMCardHighCapBlockSizeLog2);
				}
			}
		else
			{
			// Write to end of current write group, or end of request range, whichever is lower
			const TInt64 prefEraseUnitEnd = (iPhysStart + iPrWtGpLen) & ~iPrWtGpMsk;
			iPhysEnd = Min(prefEraseUnitEnd, aStart + aLength);				
			
			__ASSERT_DEBUG((iPhysEnd - iPhysStart) > 0, Panic(ELWLength));
			const TUint32 writeLen = I64LOW(iPhysEnd - iPhysStart);
			memset (iCacheBuf, 0x00, writeLen);
			iSession->SetupCIMWriteBlock(I64LOW(iPhysStart >> KMMCardHighCapBlockSizeLog2), iCacheBuf, writeLen >> KMMCardHighCapBlockSizeLog2);
			}
		
		r = EngageAndSetWriteRequest(EMReqFormat);
		}
		return r;
	}



TInt DMmcMediaDriverFlash::LaunchWrite(TInt64 aStart, TUint32 aLength, TMediaRequest aMedReq)
//
// starts writes from DoWrite(), DoFormat() and the session end DFC.  This function does not
// maintain the iReq* instance variables.  It sets iIntBuf, iPhysStart and iPhysEnd.
//
	{
	__KTRACE_OPT(KPBUSDRV, Kern::Printf("\n>mmd:lw:0x%lx,%d,%d", aStart, aLength, aMedReq));
	__ASSERT_DEBUG(aMedReq == EMReqWrite || aMedReq == EMReqFormat, Panic(ELWRequest));
	__ASSERT_DEBUG(TotalSizeInBytes() > aStart, Panic(ELWStart));
	__ASSERT_DEBUG(!(aMedReq == EMReqFormat) || (aStart & iBlkMsk) == 0, Panic(ELWFmtStAlign));
	__ASSERT_DEBUG(aLength > 0, Panic(ELWNotPositive));
	__ASSERT_DEBUG(TotalSizeInBytes() >= aStart + aLength, Panic(ELWEnd));
	__ASSERT_DEBUG(!(aMedReq == EMReqFormat) || (aLength & iBlkMsk) == 0, Panic(ELWFmtEndAlign));
	__ASSERT_DEBUG(iSession != NULL, Panic(ECFSessPtrNull));

	TInt r;

	if ((r = CheckDevice(EMReqTypeNormalWr)) == KErrNone)
		{
		iPhysStart = aStart & ~iBlkMsk;

		// PSL MUST support double-buffering for DMA requests
		// first write, or have just completed previous write
		if (iWtRBM == 0)
			{
			if(iDoDoubleBuffer == EFalse)
				{
				//
				// Can we use double-buffering for this request?
				//
				//  - Only if PSL supports double buffering and the request length 
				//	  is greater than the maximum PSL buffer size.
				//
				iDoPhysicalAddress = iCurrentReq->IsPhysicalAddress();
								
				const TInt64 medEnd = aStart + aLength;
		
				TInt64 maxPslEnd = medEnd;
				const TUint32 maxDbLength = iSocket->MaxDataTransferLength();
				
				if(maxDbLength)
					{
					//
					// If the PSL specifies a limit on the maximum size of a data transfer, then truncate the request...
					//
					maxPslEnd = UMin(medEnd, iPhysStart + maxDbLength);
					}

				iPhysEnd = (maxPslEnd + iBlkMsk) & ~iBlkMsk;
				
				if (iDoPhysicalAddress)
					{
					iDoDoubleBuffer = EFalse;
					iIntBuf = ReserveWriteBlocks(aStart, medEnd, &iWtRBM);
					iPhysEnd = (medEnd + iBlkMsk) & ~iBlkMsk;
					}

				if (!iDoPhysicalAddress)
					{
					iDoDoubleBuffer = iSocket->SupportsDoubleBuffering() && ((iPhysEnd - iPhysStart) > iMaxBufSize);
					if(iDoDoubleBuffer)
						{
						//
						// Conditions for double-buffering are met.  Set up the size of the first
						// transfer to half the size of the block cache.  
						//
						// Note that we don't bother to align to write groups here, as the entire
						// request will be processed under one multi-block command so there's no 
						// danger of forcing the card into RMW cycles as would be the case when
						// issuing multiple misaligned commands.
						//
						iDbEnd = maxPslEnd;												// The end of the complete double-buffered transfer
						iPhysEnd = (iPhysStart + (iMaxBufSize >> 1) + iBlkMsk) &~ iBlkMsk;	// The end of the first double-buffered transfer
						__ASSERT_DEBUG(iPhysEnd - iPhysStart <= (iMaxBufSize >> 1), Panic(ELWLength));
	
						//
						// Now reserve write blocks from the buffer cache. When double-buffering,
						// write blocks are only really reserved during the last transfer to avoid
						// continuously updating the cache indexes. Since the block cache is
						// continuously recycled, the following call shall invalidate the cache
						// and inform us as to whether we need to perform an RMW operation for
						// the first and last blocks prior to initiating data transfer.
						//
						iIntBuf = ReserveWriteBlocks(aStart, iDbEnd, &iWtRBM);
						}
					else
						{
						//
						// reserve buffers to end of first write group, or end of request range,
						// whichever is lower.  Note that if the range already exists in the buffer,
						// e.g. because of a previous RBM, the same range will be returned.  This
						// means that iWtRBM can be set to zero in the callback DFC, and this code
						// will retrieve the reserved range.
						//
						const TInt64 wtGpEnd = (iPhysStart + iPrWtGpLen) & ~iPrWtGpMsk;
						const TInt64 medEnd = UMin(wtGpEnd, aStart + aLength);
						iPhysEnd = (medEnd + iBlkMsk) & ~iBlkMsk;
						iIntBuf = ReserveWriteBlocks(aStart, medEnd, &iWtRBM);
						}
					} //if (!iDoPhysicalAddress)
				} //if(iDoDoubleBuffer == EFalse)
			} //if (iWtRBM == 0)

		if (iWtRBM & KWtRBMFst)
			{			
			__KTRACE_OPT(KPBUSDRV, Kern::Printf("mmd:lw: read-before-modify required on first block"));
			if (iDoPhysicalAddress)
				iSession->SetupCIMReadBlock(I64LOW(iPhysStart >> KMMCardHighCapBlockSizeLog2), iMinorBuf, iBlkLen >> KMMCardHighCapBlockSizeLog2);
			else
				iSession->SetupCIMReadBlock(I64LOW(iPhysStart >> KMMCardHighCapBlockSizeLog2), iIntBuf, iBlkLen >> KMMCardHighCapBlockSizeLog2);
			return EngageAndSetReadRequest(aMedReq);
			}

		else if (iWtRBM & KWtRBMLst)
			{
			__KTRACE_OPT(KPBUSDRV, Kern::Printf("mmd:lw: read-before-modify required on last block"));			
			if(iDoDoubleBuffer || iDoPhysicalAddress)
				{
				//
				// When double-buffering, the result of the RMW-read operation shall be stored
				// in the minor buffer, otherwise the data would be overwritten before the last
				// data transfer takes place.
				//
				const TInt64 lastBlock = (aStart + aLength) & ~iBlkMsk;  // start posn in media to read from (we know aStart + aLength isn't block aligned due to KWtRBMLst flag)
				if (iDoDoubleBuffer)
					iSession->SetupCIMReadBlock(I64LOW(lastBlock >> KMMCardHighCapBlockSizeLog2), iMinorBuf, iBlkLen >> KMMCardHighCapBlockSizeLog2);
				else
					iSession->SetupCIMReadBlock(I64LOW(lastBlock >> KMMCardHighCapBlockSizeLog2), iCacheBuf, iBlkLen >> KMMCardHighCapBlockSizeLog2);
				}
			else
				{
				//
				// If not double-buffering, we can read the RMW data of the last block directly
				// into the block cache as we know that the data transfer will fit entirely
				// within the cache..
				//
				const TInt64 lastBlock = iPhysEnd - iBlkLen;		// start posn in media to read from
				iSession->SetupCIMReadBlock(I64LOW(lastBlock >> KMMCardHighCapBlockSizeLog2), iIntBuf + (lastBlock - iPhysStart), iBlkLen >> KMMCardHighCapBlockSizeLog2);
				}

			// Kick off the RMW-read operation for the last block...
			return EngageAndSetReadRequest(aMedReq);
			}
		
		if (iWtRBM & KWtMinFst)
			{
			__KTRACE_OPT(KPBUSDRV, Kern::Printf("mmd:lw:Phys write-first-block-only"));			
			//Overwrite first block with the new data			
			TInt32 tlen = I64LOW(aStart & iBlkMsk);
			TInt32 wlen = UMin(I64LOW((iBlkMsk+1) - tlen), aLength);			
			
			const TInt64 usrOfst = (aStart - iReqStart);
			TPtr8 tgt(&iMinorBuf[tlen], I64LOW(wlen));

			if ( (r = iCurrentReq->ReadRemote(&tgt,I64LOW(usrOfst))) != KErrNone)
				return r;			
			}
		
		if (iWtRBM & KWtMinLst)
			{
			__KTRACE_OPT(KPBUSDRV, Kern::Printf("mmd:lw:Phys write-last-block-only"));
			iWtRBM &= ~KWtMinLst;
			//Overwrite last block with the new data
			const TInt64 medEnds = aStart + aLength;
			TInt64 tlen = medEnds & iBlkMsk;
			
			const TInt64 usrOfst = (aStart - iReqStart);					
			TPtr8 tgt(iCacheBuf, I64LOW(tlen));			
						
			if ( (r = iCurrentReq->ReadRemote(&tgt,I64LOW(usrOfst+aLength-tlen))) !=KErrNone)
				return r;			
			}
		
		// no reads required - read data from user buffer and launch write
		const TInt64 usrOfst = (aStart - iReqStart);
		const TInt64 bufOfst = aStart - iPhysStart;		// offset into first sector, not whole buffer
		const TInt64 len = UMin(aStart + aLength, iPhysEnd) - iReqCur;
		__ASSERT_DEBUG(len > 0, Panic(ELWLength));
		__ASSERT_DEBUG(I64HIGH(usrOfst) == 0, Panic(ELWLength));

		if (iDoPhysicalAddress)
			{
			TPhysAddr physAddr = 0;
			TInt physLength = 0;
			TUint32 physLen = I64LOW(iPhysEnd - iPhysStart);
			
			if (iWtRBM & KWtMinFst)
				{
#if !defined(__WINS__)
				physAddr = Epoc::LinearToPhysical((TLinAddr)iMinorBuf);
#else
				physAddr = (TPhysAddr)iMinorBuf;
#endif
				physLength = iBlkLen;
				iBufOfset = I64LOW(iReqStart - iPhysStart);
				//iReqCur already set in DoWrite
				iFragOfset = iIPCLen = iBlkLen - iBufOfset;
				iWtRBM &= ~KWtMinFst;
				}
			else
				{
				iFragOfset = I64LOW(usrOfst);
			
				r = PrepareFirstPhysicalFragment(physAddr, physLength, aLength);
				}
					           						
			if (r == KErrNone)
				{
				iDbEnd = iPhysEnd;
           		iPhysEnd = iPhysStart+physLength;
           		
           		if ((TUint32)physLength > physLen) physLength = physLen; // more memory in fragment than required!	
           		
				iSession->SetupCIMWriteBlock(I64LOW(iPhysStart >> KMMCardHighCapBlockSizeLog2), (TUint8*) physAddr, physLen >> KMMCardHighCapBlockSizeLog2);
				iSession->Command().iFlags|= KMMCCmdFlagPhysAddr;
				iSession->EnableDoubleBuffering(physLength >> KDiskSectorShift);
				}
			else 
				{
				__KTRACE_OPT(KPBUSDRV, Kern::Printf("<mmd:lw:Phys:%d", r));
				return r;					
				}
			} // if (iDoPhysicalAddress)
		else
			{
			TPtr8 tgt(&iIntBuf[bufOfst], I64LOW(len));
	
			r = ReadDataFromUser(tgt, I64LOW(usrOfst));
			if (r == KErrNone)
				{
				if(!iDoDoubleBuffer)
					{
					// EPBUSM automatically uses CMD24 instead of CMD25 for single block writes
					iSession->SetupCIMWriteBlock(I64LOW(iPhysStart >> KMMCardHighCapBlockSizeLog2), iIntBuf, I64LOW((iPhysEnd - iPhysStart) >> KMMCardHighCapBlockSizeLog2));
					}
				else
					{
					// 
					// When double-buffering, set up the data transfer command to the entire
					// request range. and flag the session to enable double-buffering (as well
					// as specifying the length of each double-buffered transfer as calculated
					// in 'len' above).  This is performed only once - the double-buffering 
					// is subsequently handled within the DoDataTransferCallback function.
					//
					iSession->SetupCIMWriteBlock(I64LOW(iPhysStart >> KMMCardHighCapBlockSizeLog2), iIntBuf, I64LOW((((iDbEnd + iBlkMsk) & ~iBlkMsk) - iPhysStart) >> KMMCardHighCapBlockSizeLog2));
					iSession->EnableDoubleBuffering(I64LOW((len + iBlkMsk) & ~iBlkMsk) >> KDiskSectorShift);
	
					// ...and switch to the 'second' buffer, which will be populated in the
					// data transfer callback in parallel with hardware transfer of the first.
					iSecondBuffer = ETrue;
					}
				}
			}
	
			//Reliable Write only supported by v4.3+ MMC media
			if (iCard->ExtendedCSD().ExtendedCSDRev() >= 3)
				{
				// One request, i.e. not end of previous DB request 
				// 512 Bytes long when sector aligned
				if ( ( I64LOW(iPhysEnd - iPhysStart) == iBlkLen) && ((iReqStart & ~iBlkMsk) == iPhysStart) )
					{
					__KTRACE_OPT(KPBUSDRV, Kern::Printf("mmd:lw:AtomicWrite"));
					iSession->Command().iFlags|= KMMCCmdFlagReliableWrite;
					}
				}
		
			// Engage the data transfer session...
			r = EngageAndSetWriteRequest(aMedReq);
		}	// if ((r = CheckDevice(EMReqTypeNormalWr)) == KErrNone)

	__KTRACE_OPT(KPBUSDRV, Kern::Printf("<mmd:lw:%d", r));

	return r;
	}

TInt DMmcMediaDriverFlash::PartitionInfo(TPartitionInfo& anInfo)
//
// Read the partition information for the media.  If the user supplied a password,
// then unlock the card before trying to read the first sector.
//
	{
	__KTRACE_OPT(KPBUSDRV, Kern::Printf(">mmd:rpi"));
	__ASSERT_DEBUG(CurrentRequest() == EMReqIdle, Panic(ERPIInUse));

	iPartitionInfo = &anInfo;

	if(iMmcPartitionInfo)
		{
		// If this is an embedded device, use the custom formatting function:
		TInt r = iMmcPartitionInfo->PartitionInfo(*iPartitionInfo, iSessionEndCallBack);
		
		iHiddenSectors = 0; // Not used for internal media
		
		if (KErrNone == r)
			iMedReq = EMReqEMMCPtnInfo;
		
		return(r);
		}
	
	// Assume MBR will be present or is not required
	iMbrMissing = EFalse;

	// If media driver is persistent (see EMediaDriverPersistent), 
	// the card may have changed since last power down, so reset CID
	iSession->SetCard(iCard);

	TInt r = LaunchRPIRead();

	__KTRACE_OPT(KPBUSDRV, Kern::Printf("<mmd:rpi:%d", r));

	if(r == KErrLocked)
		{
		// If the media is locked, we present a default partition entry to the local
		// media subsystem, which will be updated when the media is finally unlocked.
		r = CreateDefaultPartition();
		if (r != KErrNone)
			return r;
		return KErrLocked;
		}

	// KErrNone indicates asynchronous completion
	return r;
	}

TInt DMmcMediaDriverFlash::LaunchRPIUnlock(TLocalDrivePasswordData& aPasswordData)
	{
	__KTRACE_OPT(KPBUSDRV, Kern::Printf(">mmd:lru:%d,%d", iCard->IsReady(), iCard->IsLocked()));
	__ASSERT_DEBUG(iSession != NULL, Panic(ECFSessPtrNull));

	TInt r = KErrNone;

	// CMD42 is an adtc, so check state in same way as for write
	if ((r = CheckDevice(EMReqTypeUnlockPswd)) == KErrNone)
		{
		r = Stack().MMCSocket()->PrepareStore(CardNum(), DLocalDrive::EPasswordUnlock, aPasswordData);

		if (r == KErrNone)
			{
			TMediaPassword curPwd;

			curPwd = *aPasswordData.iOldPasswd;

			TInt curPwdLen = curPwd.Length();
			TInt blockLen = 2 + curPwdLen;

			TPtr8 pbuf(&iMinorBuf[0], 2, blockLen);
			pbuf[0] = 0;				// LOCK_UNLOCK = 0; SET_PWD = 0
			pbuf[1] = static_cast<TUint8>(curPwdLen);
			pbuf.Append(curPwd);
			iSession->SetupCIMLockUnlock(blockLen, iMinorBuf);

			r = EngageAndSetWriteRequest(EMReqUpdatePtnInfo);
			}
		}

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


TInt DMmcMediaDriverFlash::LaunchRPIRead()
//
// launch read request on first KDiskSectorSize (512) bytes
//
	{
	__KTRACE_OPT(KPBUSDRV, Kern::Printf((">mmd:lrr")));
	__ASSERT_DEBUG(iSession != NULL, Panic(ECFSessPtrNull));

	// the partition information is read before any other area is read from /
	// written to, and so does not need to maintain cache coherence.  Therefore
	// it can safely use the minor buffer.

	TInt r;
	if ((r = CheckDevice(EMReqTypeNormalRd)) == KErrNone)
		{
		iIntBuf = iMinorBuf;
		iSession->SetupCIMReadBlock(0, iIntBuf);	// aBlocks = 1
		r = EngageAndSetReadRequest(EMReqPtnInfo);
		}

	__KTRACE_OPT(KPBUSDRV, Kern::Printf("<mmd:lrr:%d", r));
	return r;
	}


TInt DMmcMediaDriverFlash::LaunchRPIErase()
	{
	__KTRACE_OPT(KPBUSDRV, Kern::Printf(">mmd:lre:%d,%d", iCard->IsReady(), iCard->IsLocked()));
	__ASSERT_DEBUG(iSession != NULL, Panic(ECFSessPtrNull));

	TInt r = KErrNone;

	// CMD42 is an adtc, so check state in same way as for write
	if ((r = CheckDevice(EMReqTypeUnlockPswd)) == KErrNone)
		{
		if(iCard->IsWriteProtected())
			{
			r = KErrAccessDenied;
			}
		else
			{
			__KTRACE_OPT(KPBUSDRV, Kern::Printf("mmd:df:EMReqForceErase"));
			iMinorBuf[0] = KMMCLockUnlockErase;
			iSession->SetupCIMLockUnlock(1, iMinorBuf);
			r = EngageAndSetWriteRequest(EMReqForceErase);
			}
		}

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


TInt DMmcMediaDriverFlash::DecodePartitionInfo()
//
// decode partition info that was read into internal buffer 
//
	{
	TInt partitionCount=iPartitionInfo->iPartitionCount=0;
	TInt defaultPartitionNumber=-1;
	TMBRPartitionEntry* pe;
	const TUint KMBRFirstPartitionOffsetAligned = KMBRFirstPartitionOffset & ~3;
	TInt i;

	// Read of the first sector successful so check for a Master Boot Record
	if (*(TUint16*)(&iIntBuf[KMBRSignatureOffset])!=0xAA55)
		goto mbr_done;

	__ASSERT_COMPILE(KMBRFirstPartitionOffsetAligned + KMBRMaxPrimaryPartitions * sizeof(TMBRPartitionEntry) <= KMBRSignatureOffset);

	memmove(&iIntBuf[0], &iIntBuf[2],
		KMBRFirstPartitionOffsetAligned + KMBRMaxPrimaryPartitions * sizeof(TMBRPartitionEntry)); 


	for (i=0, pe = (TMBRPartitionEntry*)(&iIntBuf[KMBRFirstPartitionOffsetAligned]);
		pe->iPartitionType != 0 && i < KMBRMaxPrimaryPartitions;i++,pe++)
		{
		if (pe->IsDefaultBootPartition())
			{
			SetPartitionEntry(&iPartitionInfo->iEntry[0],pe->iFirstSector,pe->iNumSectors);
			defaultPartitionNumber=i;
			partitionCount++;
			break;
			}
		}

	// Now add any other partitions
	for (i=0, pe = (TMBRPartitionEntry*)(&iIntBuf[KMBRFirstPartitionOffsetAligned]);
		pe->iPartitionType != 0 && i < KMBRMaxPrimaryPartitions;i++,pe++)
		{
		TBool validPartition = ETrue;	// assume partition valid

		if (defaultPartitionNumber==i)
			{
			// Already sorted
			}

		// FAT partition ?
		else if (pe->IsValidDosPartition() || pe->IsValidFAT32Partition())
			{
			SetPartitionEntry(&iPartitionInfo->iEntry[partitionCount],pe->iFirstSector,pe->iNumSectors);
			__KTRACE_OPT(KLOCDPAGING, Kern::Printf("Mmc: FAT partition found at sector #%u", pe->iFirstSector));
			partitionCount++;
			}
		else
			{
			validPartition = EFalse;
			}
		
		if (validPartition && partitionCount == 1)
			iHiddenSectors = pe->iFirstSector;

		}

	// Check the validity of the partition address boundaries
	// If there is any
	if(partitionCount > 0)
		{
		const TInt64 deviceSize = iCard->DeviceSize64();
		TPartitionEntry& part = iPartitionInfo->iEntry[partitionCount - 1];
		// Check that the card address space boundary is not exceeded by the last partition
		// In case of only 1 partition in the media check also it
		if(part.iPartitionBaseAddr + part.iPartitionLen > deviceSize)
			{
			__KTRACE_OPT(KPBUSDRV, Kern::Printf("Mmc: MBR partition exceeds card memory space"));
			// Adjust the partition length to card address boundary
			part.iPartitionLen = (deviceSize - part.iPartitionBaseAddr);

			// Check that the base address contained valid information
			if(part.iPartitionLen <= 0)
				{
				__KTRACE_OPT(KPBUSDRV, Kern::Printf("Mmc: Invalid base address"));
				// Invalid MBR - assume the boot sector is in the first sector
				defaultPartitionNumber =-1; 
				partitionCount=0;
				}
			}
		// More than one partition. Go through all of them
		if (partitionCount > 0)
			{
			for(i=partitionCount-1; i>0; i--)
				{
				const TPartitionEntry& curr = iPartitionInfo->iEntry[i];
				TPartitionEntry& prev = iPartitionInfo->iEntry[i-1];
				// Check if partitions overlap
				if(curr.iPartitionBaseAddr < (prev.iPartitionBaseAddr + prev.iPartitionLen))
					{
					__KTRACE_OPT(KPBUSDRV, Kern::Printf("Mmc: Overlapping partitions"));
					// Adjust the partition length to not overlap the next partition
					prev.iPartitionLen = (curr.iPartitionBaseAddr - prev.iPartitionBaseAddr);

					// Check that the base address contained valid information
					if(prev.iPartitionLen <= 0)
						{
						__KTRACE_OPT(KPBUSDRV, Kern::Printf("Mmc: Invalid base address"));
						// Invalid MBR - assume the boot sector is in the first sector
						defaultPartitionNumber=(-1); 
						partitionCount=0;
						}
					}
				}
			}
		}

mbr_done:
	if (defaultPartitionNumber==(-1) && partitionCount==0)
		{
		__KTRACE_OPT(KPBUSDRV, Kern::Printf("Mmc:PartitionInfo no MBR"));
		if (MBRMandatory(iCard))
			{
			// If the MBR is missing AND is required, we present a default partition entry to the local
			// media subsystem, which will be updated when the media is finally formatted
			__KTRACE_OPT(KPBUSDRV, Kern::Printf("MBR mandatory, defining space for MBR + default partition"));
			iMbrMissing = ETrue;
			TInt r = CreateDefaultPartition();
			if (r != KErrNone)
				return r;
			}
		else
			{
			// Assume it has no MBR, and the Boot Sector is in the 1st sector
			SetPartitionEntry(&iPartitionInfo->iEntry[0],0,I64LOW(iCard->DeviceSize64()>>KDiskSectorShift));
			iHiddenSectors=0;
			}
		partitionCount=1;
		}

	iPartitionInfo->iPartitionCount=partitionCount;
	iPartitionInfo->iMediaSizeInBytes=TotalSizeInBytes();

	__KTRACE_OPT(KPBUSDRV, Kern::Printf("<Mmc:PartitionInfo (C:%d)",iPartitionInfo->iPartitionCount));
	__KTRACE_OPT(KPBUSDRV, Kern::Printf("     Partition1 (B:%xH L:%xH)",I64LOW(iPartitionInfo->iEntry[0].iPartitionBaseAddr),I64LOW(iPartitionInfo->iEntry[0].iPartitionLen)));
	__KTRACE_OPT(KPBUSDRV, Kern::Printf("     Partition2 (B:%xH L:%xH)",I64LOW(iPartitionInfo->iEntry[1].iPartitionBaseAddr),I64LOW(iPartitionInfo->iEntry[1].iPartitionLen)));
	__KTRACE_OPT(KPBUSDRV, Kern::Printf("     Partition3 (B:%xH L:%xH)",I64LOW(iPartitionInfo->iEntry[2].iPartitionBaseAddr),I64LOW(iPartitionInfo->iEntry[2].iPartitionLen)));
	__KTRACE_OPT(KPBUSDRV, Kern::Printf("     Partition4 (B:%xH L:%xH)",I64LOW(iPartitionInfo->iEntry[3].iPartitionBaseAddr),I64LOW(iPartitionInfo->iEntry[3].iPartitionLen)));

#ifdef _DEBUG
	TMBRPartitionEntry cPe;
	if(GetDefaultPartitionInfo(cPe) == KErrNone)
		{
		pe = (TMBRPartitionEntry*)(&iIntBuf[0]);

		__KTRACE_OPT(KPBUSDRV, Kern::Printf("-------------------------------------------"));
		__KTRACE_OPT(KPBUSDRV, Kern::Printf("-- Partition Entry Validation/Comparison --"));
		__KTRACE_OPT(KPBUSDRV, Kern::Printf("-------------------------------------------"));
		__KTRACE_OPT(KPBUSDRV, Kern::Printf("-- iX86BootIndicator [%02x:%02x] %c       -", pe->iX86BootIndicator, cPe.iX86BootIndicator, pe->iX86BootIndicator == cPe.iX86BootIndicator ? ' ' : 'X'));
		__KTRACE_OPT(KPBUSDRV, Kern::Printf("--        iStartHead [%02x:%02x] %c       -", pe->iStartHead,        cPe.iStartHead,        pe->iStartHead        == cPe.iStartHead        ? ' ' : 'X'));
		__KTRACE_OPT(KPBUSDRV, Kern::Printf("--      iStartSector [%02x:%02x] %c       -", pe->iStartSector,      cPe.iStartSector,      pe->iStartSector      == cPe.iStartSector      ? ' ' : 'X'));
		__KTRACE_OPT(KPBUSDRV, Kern::Printf("--    iStartCylinder [%02x:%02x] %c       -", pe->iStartCylinder,    cPe.iStartCylinder,    pe->iStartCylinder    == cPe.iStartCylinder    ? ' ' : 'X'));
		__KTRACE_OPT(KPBUSDRV, Kern::Printf("--    iPartitionType [%02x:%02x] %c       -", pe->iPartitionType,    cPe.iPartitionType,    pe->iPartitionType    == cPe.iPartitionType    ? ' ' : 'X'));
		__KTRACE_OPT(KPBUSDRV, Kern::Printf("--          iEndHead [%02x:%02x] %c       -", pe->iEndHead,          cPe.iEndHead,          pe->iEndHead          == cPe.iEndHead          ? ' ' : 'X'));
		__KTRACE_OPT(KPBUSDRV, Kern::Printf("--        iEndSector [%02x:%02x] %c       -", pe->iEndSector,        cPe.iEndSector,        pe->iEndSector        == cPe.iEndSector        ? ' ' : 'X'));
		__KTRACE_OPT(KPBUSDRV, Kern::Printf("--      iEndCylinder [%02x:%02x] %c       -", pe->iEndCylinder,      cPe.iEndCylinder,      pe->iEndCylinder      == cPe.iEndCylinder      ? ' ' : 'X'));
		__KTRACE_OPT(KPBUSDRV, Kern::Printf("--      iFirstSector [%08x:%08x] %c       -", pe->iFirstSector,      cPe.iFirstSector,      pe->iFirstSector      == cPe.iFirstSector      ? ' ' : 'X'));
		__KTRACE_OPT(KPBUSDRV, Kern::Printf("--       iNumSectors [%08x:%08x] %c       -", pe->iNumSectors,       cPe.iNumSectors,       pe->iNumSectors       == cPe.iNumSectors       ? ' ' : 'X'));
		__KTRACE_OPT(KPBUSDRV, Kern::Printf("-------------------------------------------"));
		}
#endif

	return(KErrNone);
	}


TInt DMmcMediaDriverFlash::WritePartitionInfo()
/**
	Write the default partition table to freshly formatted media
	@return Standard Symbian OS Error Code
 */
	{
	__KTRACE_OPT(KPBUSDRV, Kern::Printf(">mmd:wpi"));
	__ASSERT_DEBUG(iSession != NULL, Panic(ECFSessPtrNull));

	TMBRPartitionEntry partitionEntry;
	TInt err = GetDefaultPartitionInfo(partitionEntry);
	if(err == KErrNone)
		{
		__KTRACE_OPT(KPBUSDRV, Kern::Printf("mmd:MBR/Partition Table"));
		__KTRACE_OPT(KPBUSDRV, Kern::Printf("    Boot ID          : %02xh", partitionEntry.iX86BootIndicator));
		__KTRACE_OPT(KPBUSDRV, Kern::Printf("    Start Head       : %02xh", partitionEntry.iStartHead));
		__KTRACE_OPT(KPBUSDRV, Kern::Printf("    Start Sector     : %02xh", partitionEntry.iStartSector));
		__KTRACE_OPT(KPBUSDRV, Kern::Printf("    Start Cyclinder  : %02xh", partitionEntry.iStartCylinder));
		__KTRACE_OPT(KPBUSDRV, Kern::Printf("    System ID        : %02xh", partitionEntry.iPartitionType));
		__KTRACE_OPT(KPBUSDRV, Kern::Printf("    End Head         : %02xh", partitionEntry.iEndHead));
		__KTRACE_OPT(KPBUSDRV, Kern::Printf("    End Sector       : %02xh", partitionEntry.iEndSector));
		__KTRACE_OPT(KPBUSDRV, Kern::Printf("    End Cyclinder    : %02xh", partitionEntry.iEndCylinder));
		__KTRACE_OPT(KPBUSDRV, Kern::Printf("    Relative Sector  : %08xh", partitionEntry.iFirstSector));
		__KTRACE_OPT(KPBUSDRV, Kern::Printf("    Number of Sectors: %08xh", partitionEntry.iNumSectors));

		//
		// Clear all other partition entries and align the partition info into the minor buffer for writing...
		//
		memclr(iMinorBuf, KDiskSectorSize);
		memcpy(&iMinorBuf[KMBRFirstPartitionEntry], &partitionEntry, sizeof(TMBRPartitionEntry));

		*(TUint16*)(&iMinorBuf[KMBRSignatureOffset]) = 0xAA55;

		iSession->SetupCIMWriteBlock(0, iMinorBuf);
		
		//
		// Write the partition table and engage the read to validate and complete the mount process
		//
		iMbrMissing = EFalse;
		iCreateMbr = EFalse;
		err = EngageAndSetWriteRequest(EMReqUpdatePtnInfo);
		}

	__KTRACE_OPT(KPBUSDRV, Kern::Printf("<mmd:wpi:%d", err));

	return(err);
	}


TInt DMmcMediaDriverFlash::CreateDefaultPartition()
	{
	TMBRPartitionEntry defPartition;
	TInt r = GetDefaultPartitionInfo(defPartition);
	if (r == KErrNone)
		{
		SetPartitionEntry(&iPartitionInfo->iEntry[0], defPartition.iFirstSector, defPartition.iNumSectors);
		iHiddenSectors = defPartition.iFirstSector;
		iPartitionInfo->iPartitionCount   = 1;
		iPartitionInfo->iMediaSizeInBytes = TotalSizeInBytes();
		}
	return r;
	}

TInt DMmcMediaDriverFlash::GetDefaultPartitionInfo(TMBRPartitionEntry& aPartitionEntry)
/**
	Calculates the default patition information for an specific card.
	@param aPartitionEntry The TMBRPartitionEntry to be filled in with the format parameters
	@return Standard Symbian OS Error Code
 */
	{
	memclr(&aPartitionEntry, sizeof(TMBRPartitionEntry));
	TUint16 reservedSectors; // Not used
	return GetMediaDefaultPartitionInfo(aPartitionEntry, reservedSectors, iCard);
	}


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

TInt DMmcMediaDriverFlash::DoPasswordOp()
	{
	// Reconstruct password data structure in our address space
	TLocalDrivePasswordData clientData;
	TInt r = iCurrentReq->ReadRemoteRaw(&clientData, sizeof(TLocalDrivePasswordData));

	TMediaPassword oldPassword;
	if (r == KErrNone)
		r = iCurrentReq->ReadRemote(clientData.iOldPasswd, &oldPassword);

	TMediaPassword newPassword;
	if (r == KErrNone)
		r = iCurrentReq->ReadRemote(clientData.iNewPasswd, &newPassword);

	TLocalDrivePasswordData passData(oldPassword, newPassword, clientData.iStorePasswd);

	if (r == KErrNone)
		{
		TInt id=iCurrentReq->Id();
		switch (id)
			{
			case DLocalDrive::EPasswordUnlock:
				r = LaunchRPIUnlock(passData);
				__KTRACE_OPT(KPBUSDRV, Kern::Printf("<mmd:rpi:%d", r));
				break;
			case DLocalDrive::EPasswordLock:
			case DLocalDrive::EPasswordClear:
				PasswordControl(id, passData);	
				break;
			}
		}
		
	// This will complete the request in the event of an error
	if(r != KErrNone)
		PartitionInfoComplete(r);

	return KErrNone; // ensures to indicate asynchronoous completion
	}

void DMmcMediaDriverFlash::PasswordControl(TInt aFunc, TLocalDrivePasswordData& aData)
//
// Change a card's password, or clear the pasword from a locked card.  The card
// must be unlocked for this function.  A locked card is unlocked when it is mounted,
// to read the partition information.  This is done from ReadPartitionInfo() and
// LaunchRPIUnlock().
//
	{
	__KTRACE_OPT(KPBUSDRV, Kern::Printf(">mmd:pc:%d", (TInt) aFunc));
	__ASSERT_DEBUG(CurrentRequest() == EMReqIdle, Panic(EPCInUse));
	__ASSERT_DEBUG(aFunc == DLocalDrive::EPasswordLock || aFunc == DLocalDrive::EPasswordClear, Panic(EPCFunc));
	__ASSERT_DEBUG(iSession != NULL, Panic(ECFSessPtrNull));

	TInt r;

	if ((r = CheckDevice(EMReqTypeChangePswd)) == KErrNone)
		{
		// check if the current password is correct here.  (This makes the
		// clear operation redundant only if the password is stored and it
		// is wrong.)  Complete with same value as DoSessionEndDfc() would.

		TMediaPassword curPwd;
		
		curPwd = *aData.iOldPasswd;
		TInt curPwdLen = curPwd.Length();
		TInt blockLen;

		if (!(iCard->iFlags & KMMCardIsLockable))
			r = KErrNotSupported;
		else if (Stack().PasswordStore()->IsMappingIncorrect(iCard->CID(), curPwd))
			r = KErrAccessDenied;
		else
			{
			if ((r = Stack().MMCSocket()->PrepareStore(CardNum(), aFunc, aData/*, aThread*/)) == KErrNone)
				{
				switch (aFunc)
					{
				case DLocalDrive::EPasswordLock:
					{
					TMediaPassword newPwd;
					newPwd = *aData.iNewPasswd;
					TInt newPwdLen = newPwd.Length();
					blockLen = 1 + 1 + curPwdLen + newPwdLen;
					
					#ifndef __EPOC32__
					TUint16 env_Var[]=L"_EPOC_PWD_LEN";
					TUint16 env_Val[2];
					env_Val[0]=(TUint16)(curPwdLen+1);
					env_Val[1]=0;//make a null terminated string
					r=SetEnvironmentVariable(env_Var,&env_Val[0]);
					__ASSERT_DEBUG(r!=0, Panic(EPCFunc));
					
					#endif
					
					TPtr8 pbuf(&iMinorBuf[0], 2, blockLen);
					pbuf[0] = KMMCLockUnlockSetPwd; 	// LOCK_UNLOCK = 0, SET_PWD = 1
					pbuf[1] = static_cast<TUint8>(curPwdLen + newPwdLen);
					pbuf.Append(curPwd);
					pbuf.Append(newPwd);
					}
					break;

				case DLocalDrive::EPasswordClear:
					{
					blockLen = 1 + 1 + curPwdLen;

					TPtr8 pbuf(&iMinorBuf[0], 2, blockLen);
					pbuf[0] = KMMCLockUnlockClrPwd; 	// LOCK_UNLOCK = dc, CLR_PWD = 1
					pbuf[1] = static_cast<TUint8>(curPwdLen);
					pbuf.Append(curPwd);
					}
					break;

				default:
					// DLocalDrive::EPasswordUnlock is not handled.  This avoids warnings for unused
					// case, and uninitialized variable.
					blockLen = 0;
					break;
					}	// switch (aFunc)

				iSession->SetupCIMLockUnlock(blockLen, iMinorBuf);
				r = EngageAndSetWriteRequest(EMReqPswdCtrl);
				}	// if ((r = Stack().PrepareStore(CardNum(), aFunc, aData, aThread)) == KErrNone)
			}	// else (Stack().IsMappingIncorrect(iCard->CID(), curPwd))
		}	// (r = CheckDevice(EMReqTypeChangePswd)) == KErrNone

	// complete immediately if error occured
	if (r != KErrNone)
		CompleteRequest(r);

	__KTRACE_OPT(KPBUSDRV, Kern::Printf("<mmd:pc:%d", r));
	}


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

TInt DMmcMediaDriverFlash::CheckDevice(TMediaReqType aReqType)
//
// Check the device before initiating a command
//
	{
	
	__KTRACE_OPT(KPBUSDRV, Kern::Printf(">mmd:cd:%d",aReqType));

	TInt r=KErrNone;

	if (!iCard->IsReady())
		r=KErrNotReady;

	// The card must be locked if attempting to unlock during RPI, and
	// unlocked at all other times.
	else if (aReqType!=EMReqTypeUnlockPswd && iCard->IsLocked())
		r=KErrLocked;
	// Don't perform Password setting for WriteProtected cards, 
	// unable to recover (ForcedErase) if password lost.
	else if (aReqType==EMReqTypeChangePswd)
		{
		if (iCard->MediaType()==EMultiMediaROM)
			{
			r=KErrAccessDenied;
			}
		}
	else if (iMbrMissing && aReqType==EMReqTypeNormalRd)
		r=KErrCorrupt;

#if !defined(__WINS__)
	// Don't perform write/password operations when the battery is low
//	else if (aReqType!=EMReqTypeNormalRd && Hal::MainBatteryStatus()<ELow && !Hal::ExternalPowerPresent())
//		r=KErrBadPower;
#endif
	// Don't perform write operations when the mechanical write protect switch is set
	else if (aReqType==EMReqTypeNormalWr && iCard->IsWriteProtected())
		r=KErrAccessDenied;
	// Don't perform write/format operations on MMC ROM cards
	else if (iMediaType==EMultiMediaROM && aReqType == EMReqTypeNormalWr)
		r=KErrAccessDenied;

	__KTRACE_OPT(KPBUSDRV, Kern::Printf("<mmd:cd:%d", r));
	return(r);
	}

void DMmcMediaDriverFlash::SessionEndCallBack(TAny* aMediaDriver)
//
// called by EPBUS when a single session has finished.  Queues DFC to launch
// next session or to complete client request.
//
	{
	DMmcMediaDriverFlash& md = *static_cast<DMmcMediaDriverFlash*>(aMediaDriver);
	__ASSERT_DEBUG(! md.iSessionEndDfc.Queued(), Panic(ESECBQueued));
	md.iSessionEndDfc.Enque();
	}


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


void DMmcMediaDriverFlash::DoSessionEndDfc()
//
// launch next session or complete client request
//
	{
	__KTRACE_OPT(KPBUSDRV, Kern::Printf(">mmd:dsed:%d", CurrentRequest()));

	TInt r=KErrNone;

	EndInCritical();

	// Abort if writing or formatting and power has gone down
	if (!Kern::PowerGood() && CurrentRequest()!=EMReqRead)
		r=KErrAbort;
	// Return KErrNotReady if we have has a deferred media change
	if (!iCard->IsReady())
		r=KErrNotReady;
	// if stack has powered down session pointer will be NULL 
	if (iSession == NULL)
		r = KErrNotReady;

	TBool complete = ETrue;

	if (r==KErrNone)
		{
		r = iSession->EpocErrorCode();

		switch (CurrentRequest())
			{
			case EMReqRead:
				{
				if (r != KErrNone)						// abort if MMC error
					break;
				
				if(iDoDoubleBuffer)
					{
					//
					// This is the end of a double-buffered transfer.
					//  - Now we have two buffers to copy back to the user...
					//
					TUint8* bufPtr = iIntBuf + (iSecondBuffer ? (iMaxBufSize >> 1) : 0);
					if((r = WriteDataToUser(bufPtr)) == KErrNone)
						{
						MarkBlocks(iReqCur, iPhysEnd, CchMemToIdx(bufPtr));

						iReqCur  = iPhysEnd;
						iPhysEnd = iDbEnd;

						bufPtr = iIntBuf + (iSecondBuffer ? 0 : (iMaxBufSize >> 1));
						if((r = WriteDataToUser(bufPtr)) == KErrNone)
							{
							MarkBlocks(iReqCur, (iPhysEnd + iBlkMsk) & ~iBlkMsk, CchMemToIdx(bufPtr));
							}
						}
					iDoDoubleBuffer = EFalse;
					}
				else if (iDoPhysicalAddress)
					{
					if (iRdROB & KIPCWrite)
						{
						// partial end point
						TInt len = I64LOW(iReqEnd & iBlkMsk);
						const TInt ofset = I64LOW(iPhysEnd - iBlkLen - iReqStart);								
				
						TPtrC8 extrView(iIntBuf, len);
						r = iCurrentReq->WriteRemote(&extrView,ofset);
						}
					// Reset attributes
					iRdROB = 0;
					iFragOfset = iIPCLen = iBufOfset = 0;
					iReqCur = iPhysEnd = iReqEnd;					
					iDoPhysicalAddress = EFalse;
					}
				else
					{
					r = WriteDataToUser(&iIntBuf[I64LOW(iReqCur - iPhysStart)]);
					}

				if (r != KErrNone)
					break;

				// if there is more information to read for the user then engage another session
				if ((iReqCur = iPhysEnd) < iReqEnd)
					{
					TBool allDone = EFalse;
					if ( ((r = ReadDataUntilCacheExhausted(&allDone)) == KErrNone) && !allDone)
						{
						iPhysStart = iReqCur & ~iBlkMsk;
						TUint32 length = I64LOW(iReqEnd - iReqCur);
		
						if ( (iReqEnd - iPhysStart) > iMaxBufSize && iSocket->SupportsDoubleBuffering() && !iReadToEndOfCard)
							r = LaunchDBRead();				
						else
							r = LaunchRead(iReqCur, length);
						
						if ( r == KErrNone)
							complete = EFalse;
						}
					}
				}
				break;

			case EMReqWrite:
				{				
				if (r != KErrNone)						// abort if MMC error
					{
					break;
					}

				if (iWtRBM == 0)
					{
					iReqCur = iPhysEnd;
					iDoDoubleBuffer = EFalse;
					iDoPhysicalAddress = EFalse;
					iRdROB = 0;
					iFragOfset = iIPCLen = iBufOfset = 0;
					}
				// clear current RBM flag
				else
					{
					if (iWtRBM & KWtRBMFst)
						{
						iWtRBM &= ~KWtRBMFst;
						}
					else if (iWtRBM & KWtRBMLst)
						{
						iWtRBM &= ~KWtRBMLst;
						}
					}

				// advance media position if just finished write, as opposed to read-before-modify
				if (iReqCur < iReqEnd)
					{
					if ((r = LaunchWrite(iReqCur, I64LOW(iReqEnd - iReqCur), EMReqWrite)) == KErrNone)
						{
						complete = EFalse;
						}

					complete = (r != KErrNone) ? (TBool)ETrue : (TBool)EFalse;
					}
				}
				break;

			case EMReqFormat:
				{
				if (r != KErrNone)						// abort if MMC error
					break;

				if ((iEraseUnitMsk == KMaxTUint64) ||	// no erase unit defined (Erase Class Commands not supported) ?
					(iPhysEnd == iReqEnd) ||			// finshed already ?
					((iPhysStart & iEraseUnitMsk) == 0 && (iPhysEnd & iEraseUnitMsk) == 0))
					{
					iReqCur = iPhysEnd;
					}
				else
					{
					// Formating to a mis-aligned boundary, so we can't make best use of
					// multiple erase blocks.  We shall simply erase up to the next block
					// boundary, and return the adjustment info to the file system
					r = I64LOW(iPhysEnd - iPhysStart);
					iReqCur = iReqEnd;
					}

				if(r == KErrNone)
					{
					// advance media position if just finished write, as opposed to read-before-modify
					if (iReqCur < iReqEnd)
						{
						if ((r = LaunchFormat(iReqCur, I64LOW(iReqEnd - iReqCur))) == KErrNone)
							{
							complete = EFalse;
							}
						}
					// if format finished, write an MBR if required
					// Always write an MBR if it's an SD card
					else if (iCreateMbr)
						{
						// Finished Format, so write the MBR/default partition table if required
						r = WritePartitionInfo();
						complete = (r != KErrNone) ? (TBool)ETrue : (TBool)EFalse;
						}
					}
				}
				break;

			case EMReqPtnInfo:
				if (r == KErrNone)
					r = DecodePartitionInfo();		// set up iPartitionInfo

				PartitionInfoComplete(r == KErrNone?KErrNone:KErrNotReady);
				break;
				
			case EMReqEMMCPtnInfo:
				iMedReq = EMReqIdle;
				// For now do nothing..
				break;				
				
			case EMReqUpdatePtnInfo:
				break;

			case EMReqPswdCtrl:
				if (r == KErrLocked)
					r = KErrAccessDenied;
				break;

			case EMReqForceErase:
				
				if (r == KErrNone)
					{
					// Finished Forced Erase , so write the default partition table...
					r = WritePartitionInfo();
					}

				complete = (r != KErrNone) ? (TBool)ETrue : (TBool)EFalse;
				break;

			case EMReqWritePasswordData:
				// 
				// WritePasswordData also kicks off an auto-unlock session to ensure that
				// any locked cards that have passwords in the password store are immediately
				// available.  We can safely ignore any errors returned at this stage, as the
				// password store will have been successfully updated (in locmedia.cpp), even
				// if the card is unable to accept the password.
				//
				r = KErrNone;
				break;
				
			case EMReqIdle:
				// request has been completed already (e.g. due to a power down)
				break;


			default:
				__ASSERT_DEBUG(EFalse, Panic(EDSEDRequest));
				break;
			}
		}

	// r != KErrNone => complete
	__ASSERT_DEBUG(!(r != KErrNone) || complete, Panic(EDSEDNotErrComplete));

	if (complete)
		{
		if (r != KErrNone)
			InvalidateCache();

		__KTRACE_OPT(KPBUSDRV, Kern::Printf("<mdf:dsed:cmp:%d", r));
		CompleteRequest(r);
		}
	else
		{
		__KTRACE_OPT(KPBUSDRV, Kern::Printf("<mdf:dsed:ncmp"));
		}
	}

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

void DMmcMediaDriverFlash::DataTransferCallBackDfc(TAny* aMediaDriver)
	{
	DMmcMediaDriverFlash& md = *static_cast<DMmcMediaDriverFlash*>(aMediaDriver);

	if (md.iDoPhysicalAddress)
		{
		if(md.CurrentRequest() == EMReqWrite)
			{
			md.DoPhysWriteDataTransferCallBack();
			}
		else
			{
			md.DoPhysReadDataTransferCallBack();
			}
		}
	else
		{
		if(md.CurrentRequest() == EMReqWrite)
			{
			md.DoWriteDataTransferCallBack();
			}
		else
			{
			md.DoReadDataTransferCallBack();
			}
		}
	}

void DMmcMediaDriverFlash::DoPhysWriteDataTransferCallBack()
	{
	__KTRACE_OPT(KPBUSDRV, Kern::Printf("++DMmcMediaDriverFlash::DoPhysWriteDataTransferCallBack()"));

	TInt err = KErrNone;
		
	if ( (iRdROB & KIPCSetup) || ((iReqEnd - iPhysEnd) < iBlkLen) )
		{
		//IPC to be setup, or partial end block read
		iRdROB &= ~KIPCSetup;

		if ((iReqEnd - iPhysEnd) < iBlkLen)
			{
			iIntBuf = iCacheBuf;
			}
		else
			{	
			TPtr8 tgt(iMinorBuf, iBlkLen);				
			err = ReadDataFromUser(tgt, I64LOW(iPhysEnd-iReqStart));
			iIntBuf = iMinorBuf;
			}			

		iReqCur = iPhysEnd;
		iPhysEnd += iBlkLen;
		iBufOfset = 0;
		iIPCLen = iBlkLen;

#if !defined(__WINS__)
		iSession->MoreDataAvailable( (TInt)(iBlkLen >> KDiskSectorShift), (TUint8*)Epoc::LinearToPhysical((TLinAddr) iIntBuf), err);			
#else
		iSession->MoreDataAvailable( (TInt)(iBlkLen >> KDiskSectorShift), iIntBuf, err);
#endif
		__KTRACE_OPT(KPBUSDRV, 	Kern::Printf("--iDoPhysicalAddress(KIPCSetup)"));
		return;
		}
	
	PrepareNextPhysicalFragment();

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


void DMmcMediaDriverFlash::DoPhysReadDataTransferCallBack()
	{
	__KTRACE_OPT(KPBUSDRV, 	Kern::Printf("++DMmcMediaDriverFlash::DoPhysReadTransferCallBack()"));

	TInt err = KErrNone;
	
	if ((iRdROB & KIPCWrite) && !iSecondBuffer)
		{
		// an IPC transfer completed
		iRdROB &= ~KIPCWrite;
		if(iNxtIPCLen)
			{
			// First transfer is an IPC, 
			// Corner-case - transfer is most likely IPC-DMA-IPC, 
			// because write cannot occur until after the first 2 iterations it is possible to arrive here with both IPCSetup & IPCWrite Set. 
			// need to use iIPCNxtLen instead
			TPtrC8 extrView(&iIntBuf[iBufOfset], iNxtIPCLen);
			err = iCurrentReq->WriteRemote(&extrView,I64LOW(iReqCur - iReqStart));
			iNxtIPCLen = iBufOfset = 0;  
			}
		else
			{
			TPtrC8 extrView(&iIntBuf[iBufOfset], iIPCLen);
			err = iCurrentReq->WriteRemote(&extrView,I64LOW(iReqCur - iReqStart));
			iIPCLen = iBufOfset = 0;     
			}                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 
		}
	
	if ( (iRdROB & KIPCSetup) || ((iReqEnd - iPhysEnd) < iBlkLen) )
		{
		// IPC to be setup, or partial end block read.
		iRdROB &= ~KIPCSetup;
		iRdROB |= KIPCWrite;
		
		iIntBuf = ReserveReadBlocks(iPhysEnd,(iPhysEnd+iBlkLen), &iIPCLen);

		iReqCur = iPhysEnd;
		iPhysEnd += iIPCLen;
		iBufOfset = 0;
#if !defined(__WINS__)
		iSession->MoreDataAvailable( (TInt)(iIPCLen  >> KDiskSectorShift), (TUint8*)Epoc::LinearToPhysical((TLinAddr) iIntBuf), err);			
#else
		iSession->MoreDataAvailable( (TInt)(iIPCLen  >> KDiskSectorShift), iIntBuf, err);			
#endif
		iSecondBuffer = ETrue;
		__KTRACE_OPT(KPBUSDRV, 	Kern::Printf("--iDoPhysicalAddress(KIPCWrite)"));
		return;
		}

	PrepareNextPhysicalFragment();

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

void DMmcMediaDriverFlash::DoWriteDataTransferCallBack()
	{
	__KTRACE_OPT(KPBUSDRV, Kern::Printf("++DMmcMediaDriverFlash::DoWriteDataTransferCallBack()"));

	TInt err = KErrNone;
	
	// Advance current request progress...
	iReqCur = iPhysEnd;

	const TUint32 doubleBufferSize = iMaxBufSize >> 1;

	TInt64 length = iDbEnd - iReqCur;
	TInt64 medEnd = UMin(iReqCur + doubleBufferSize, iReqCur + length);

	iPhysEnd = (medEnd + iBlkMsk) & ~iBlkMsk;
	TInt64 len = UMin(iDbEnd, iPhysEnd) - iReqCur;
	
	if(len > doubleBufferSize)
		{
		// Adjust for maximum size of double-buffering
		len = doubleBufferSize;
		}

	__ASSERT_DEBUG(len > 0, Panic(EDBLength));
	__ASSERT_DEBUG(I64HIGH((len + (KDiskSectorSize-1)) >> KDiskSectorShift) == 0, Panic(EDBLengthTooBig));

	TUint32 numBlocks = I64LOW((len + (KDiskSectorSize-1)) >> KDiskSectorShift);

	const TInt64 usrOfst = (iReqCur - iReqStart);
	
	__ASSERT_DEBUG(I64HIGH(usrOfst) == 0, Panic(EDBOffsetTooBig));

	// Setup the next buffer pointer and switch buffers...
	TUint8* bufPtr = iIntBuf + (iSecondBuffer ? doubleBufferSize : 0);
	TPtr8 tgt(bufPtr, I64LOW(len));
	iSecondBuffer = iSecondBuffer ? (TBool)EFalse : (TBool)ETrue;

	if(iDoLastRMW && length < doubleBufferSize)
		{
		//
		// This is the last transfer, and RMW is required.  The result of the read exists
		// in iMinorBuf, so copy the non-modified section of the block to the active buffer.
		//
		memcpy(&bufPtr[(numBlocks-1) << KDiskSectorShift], iMinorBuf, KDiskSectorSize);
		}

	if(I64LOW(iDbEnd - iReqCur) <= iMaxBufSize)
		{
		//
		// This is the last transfer (with or without RMW)
		//  - Mark the last blocks as active in the buffer cache.
		//
		MarkBlocks(iReqCur, iPhysEnd, CchMemToIdx(bufPtr));
		}

	//
	// Read the requested data from the remote thread...
	//
	err = ReadDataFromUser(tgt, I64LOW(usrOfst));

	//
	// ...and signal that data is available to the PSL.
	//
	iSession->MoreDataAvailable(numBlocks, bufPtr, err);

	__KTRACE_OPT(KPBUSDRV, Kern::Printf("--DMmcMediaDriverFlash::DoWriteDataTransferCallBack()"));
	}


void DMmcMediaDriverFlash::DoReadDataTransferCallBack()
	{
	__KTRACE_OPT(KPBUSDRV, 	Kern::Printf("++DMmcMediaDriverFlash::DoReadTransferCallBack()"));

	TInt err = KErrNone;
	
	const TUint32 doubleBufferSize = iMaxBufSize >> 1;

	TUint32 bufOfst = 0;

	if((iReqCur & ~iBlkMsk) == iPhysStart)
		{
		if(iSecondBuffer)
			{
			//
			// If this is the first callback, don't copy data as it's not available yet
			//  - just drop through to set up the next buffer.
			//
			TUint32 numBlocks = I64LOW((doubleBufferSize + (KDiskSectorSize-1)) >> KDiskSectorShift);
			TUint8* bufPtr = iIntBuf + doubleBufferSize;

			iSecondBuffer = EFalse;

			iSession->MoreDataAvailable(numBlocks, bufPtr, KErrNone);
			return;
			}
		else
			{
			//
			// If this is the second callback we're ready to copy
			// back to the client - data may be mis-aligned in the first
			// instance, but all subsequent data will be aligned...
			//
			bufOfst = I64LOW(iReqCur - iPhysStart);
			}
		}

	// ...otherwise, write the previous buffer contents to the user
	TUint8* bufPtr = iIntBuf + (iSecondBuffer ? doubleBufferSize : 0);

	err = WriteDataToUser(bufPtr + bufOfst);

	// Advance current request progress...
	iReqCur = iPhysEnd;

	TInt64 medEnd = UMin(iReqCur + doubleBufferSize, iDbEnd);

	iPhysEnd = (medEnd + iBlkMsk) & ~iBlkMsk;

	// Current buffer is one step ahead of the current request progress...
	TInt64 len = UMin((iDbEnd - iPhysEnd + iBlkMsk) & ~iBlkMsk, TInt64(doubleBufferSize));

	__ASSERT_DEBUG(len == 0 || (I64HIGH((len + (KDiskSectorSize-1)) >> KDiskSectorShift) == 0), Panic(EDBLengthTooBig));

	TUint32 numBlocks = I64LOW((len + (KDiskSectorSize-1)) >> KDiskSectorShift);

	//
	// ...switch buffers and signal that data is available to the PSL.
	//
	iSecondBuffer = iSecondBuffer ? (TBool)EFalse : (TBool)ETrue;

	iSession->MoreDataAvailable(numBlocks, bufPtr, err);

	__KTRACE_OPT(KPBUSDRV, Kern::Printf("--DMmcMediaDriverFlash::DoDataTransferCallBack()"));
	}


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


TInt DMmcMediaDriverFlash::EngageAndSetReadRequest(DMmcMediaDriverFlash::TMediaRequest aRequest)
	{
	return EngageAndSetRequest(aRequest, iReadCurrentInMilliAmps);
	}


TInt DMmcMediaDriverFlash::EngageAndSetWriteRequest(DMmcMediaDriverFlash::TMediaRequest aRequest)
	{
	return EngageAndSetRequest(aRequest, iWriteCurrentInMilliAmps);
	}


TInt DMmcMediaDriverFlash::EngageAndSetRequest(DMmcMediaDriverFlash::TMediaRequest aRequest, TInt aCurrent)
//
// In WINS, all of the processing, including the callbacks, is done when Engage() is called,
// so the request value must be set up in advanced.  Both the request and the current are
// cleared in the corresponding call to CompleteRequest().
//
	{
	__ASSERT_DEBUG(iSession != NULL, Panic(ECFSessPtrNull));

	iMedReq = aRequest;
	SetCurrentConsumption(aCurrent);

	TInt r = InCritical();
	if (r == KErrNone)
		{
		r = iSession->Engage();
		}

	if(r != KErrNone)
		{
		if (!Kern::PowerGood())
			r=KErrAbort; // If emergency power down - return abort rather than anything else.
		if (!iCard->IsReady())
			r=KErrNotReady; // If media change - return not ready rather than anything else.
		EndInCritical();
		}

	return r;
	}


void DMmcMediaDriverFlash::CompleteRequest(TInt aReason)
//
// completes the specified request
//
	{
	__KTRACE_OPT(KPBUSDRV, Kern::Printf("=mmd:cr0x%08x,%d", iCurrentReq, aReason));
	
	iMedReq = EMReqIdle;
	SetCurrentConsumption(KIdleCurrentInMilliAmps);

	TLocDrvRequest* pR=iCurrentReq;
	if (pR)
		{
#ifdef __DEMAND_PAGING__
#if defined(__TEST_PAGING_MEDIA_DRIVER__)
		__KTRACE_OPT(KLOCDPAGING,Kern::Printf("DMediaDriverFlash::Complete req Id(%d) with(%d)", pR->Id(), aReason));
#endif		// __TEST_PAGING_MEDIA_DRIVER__
#endif		// __DEMAND_PAGING__
		iCurrentReq=NULL;
		DMediaDriver::Complete(*pR,aReason);
		}
	}

TInt DMmcMediaDriverFlash::Caps(TLocDrv& aDrive, TLocalDriveCapsV6& aInfo)
	{
	// Fill buffer with current media caps.
	aInfo.iType = EMediaHardDisk;
	aInfo.iBattery = EBatNotSupported;
	aInfo.iDriveAtt = KDriveAttLocal;
	aInfo.iMediaAtt	= KMediaAttFormattable;

	if(iCard->iFlags & KMMCardIsLockable)
		aInfo.iMediaAtt |= KMediaAttLockable;

	if (iCard->HasPassword())
		aInfo.iMediaAtt |= KMediaAttHasPassword;
	if (iCard->IsWriteProtected())
		aInfo.iMediaAtt |= KMediaAttWriteProtected;
	if (iCard->IsLocked())
		aInfo.iMediaAtt |= KMediaAttLocked;

	aInfo.iFileSystemId = KDriveFileSysFAT;

	// Format is performed in multiples of the erase sector (or multiple block) size
	aInfo.iMaxBytesPerFormat = iEraseInfo.iPreferredEraseUnitSize;

	if ((!iInternalSlot) && (GetCardFormatInfo(iCard,aInfo.iFormatInfo) == KErrNone))
		{
		TUint16 reservedSectors;
		TMBRPartitionEntry dummy;	// Not used here
		const TInt r = GetMediaDefaultPartitionInfo(dummy, reservedSectors, iCard);
		if(r != KErrNone)
			return r;

		aInfo.iFormatInfo.iReservedSectors = reservedSectors;
		aInfo.iExtraInfo = ETrue;
		}

    // Set serial number to CID
    __ASSERT_DEBUG(KMMCCIDLength<=KMaxSerialNumLength, Kern::PanicCurrentThread(_L("Mmc"), KErrOverflow));
    aInfo.iSerialNumLength = KMMCCIDLength;
    for (TUint i=0; i<KMMCCIDLength; i++)
        aInfo.iSerialNum[i] = iCard->CID().At(i);
    
	// Get block size & erase block size to allow the file system to align first usable cluster correctly
	aInfo.iBlockSize = BlockSize(iCard);
	aInfo.iEraseBlockSize = EraseBlockSize(iCard);

#if defined(__DEMAND_PAGING__)
	// If the stack has flagged this as a demand-paging device, then it is assumed that it is internal
	// and (optionally) write protected.
	if(aDrive.iPrimaryMedia->iPagingMedia)
		{
		aInfo.iMediaAtt|= KMediaAttPageable;
		if (iDemandPagingInfo.iWriteProtected)
			{
			aInfo.iMediaAtt|= KMediaAttWriteProtected;
			aInfo.iMediaAtt&= ~KMediaAttFormattable;
			}		
		}

	// code paging enabled on this drive ?
	if(aDrive.iPagingDrv)
		{
		aInfo.iDriveAtt|= KDriveAttPageable;
		}

#endif

	if (iInternalSlot)
		{
		aInfo.iDriveAtt|= KDriveAttInternal;
		}
	else
		{
		aInfo.iDriveAtt|= KDriveAttRemovable;
		}


	if (iMmcPartitionInfo)
		{
		TLocalDriveCapsV6Buf CapsInfo = aInfo;
		iMmcPartitionInfo->PartitionCaps(aDrive,CapsInfo);
		aInfo = CapsInfo();
		}
	
	
	if (iMediaType==EMultiMediaROM)
		{
		aInfo.iMediaAtt|= KMediaAttWriteProtected;
		aInfo.iMediaAtt&= ~KMediaAttFormattable;
		}
	
	// Must return KErrCompletion to indicate that this 
	// is a synchronous version of the function
	return KErrCompletion;
	}


// ---- cache ----

TInt DMmcMediaDriverFlash::ReadDataUntilCacheExhausted(TBool* aAllDone)
//
// scans the cache for blocks corresponding to the range iReqCur to iReqEnd and
// writes them to user memory.  Starts at iReqCur & ~iBlkMsk and looks for blocks
// at sequential media positions.  Completes when a block is not available, even
// if a following block is available in the cache.  *aAllDone is undefined if the
// return value is not KErrNone.
//
// This function is linear in the number of blocks in the cache.
//
	{
	__KTRACE_OPT(KPBUSDRV, Kern::Printf(">mmd:rdc:%x,%x", iReqCur, iReqEnd));
	
#if defined(__DEMAND_PAGING__) && !defined(__WINS__)
	if (DMediaPagingDevice::PageInRequest(*iCurrentReq))
		{
		*aAllDone = EFalse;
		return KErrNone;
		}
#endif //DEMAND_PAGING	
	
	TInt64 physStart = iReqCur & ~iBlkMsk;
	TInt64 physEnd = Min(physStart + iMaxBufSize, (iReqEnd + iBlkMsk) & ~iBlkMsk);
	BuildGammaArray(physStart, physEnd);

	TInt r = KErrNone;
	TInt curBlk = 0;
	TInt cchBlk;
	while (
			r == KErrNone
		&&	physStart + (curBlk << iBlkLenLog2) < physEnd
		&&	(cchBlk = iGamma[curBlk]) != KNoCacheBlock )
		{
		// set up instance variables for WriteDataToUser()
		iPhysStart = physStart + (curBlk << iBlkLenLog2);
		iPhysEnd = iPhysStart + iBlkLen;
		iIntBuf = IdxToCchMem(cchBlk);

		if ((r = WriteDataToUser(&iIntBuf[I64LOW(iReqCur - iPhysStart)])) == KErrNone)
			{
			iReqCur = iPhysEnd;
			iLstUsdCchEnt = iGamma[curBlk];
			++curBlk;
			}
		}

	*aAllDone = (iReqCur >= iReqEnd);

	__KTRACE_OPT(KPBUSDRV, Kern::Printf("<mmd:rdc:%d,%d", *aAllDone, r));
	return r;
	}


TInt DMmcMediaDriverFlash::WriteDataToUser(TUint8* aBufPtr)
//
// write the data from the most recent read operation to the user descriptor
//
	{
	TInt r = KErrNotSupported;

	// get range of data to read out of internal buffer

	TInt len = I64LOW(UMin(iPhysEnd, iReqEnd) - iReqCur);
	TPtrC8 extrView(aBufPtr, len);

	// write data from internal buffer
	TUint usrOfst = I64LOW(iReqCur - iReqStart);

#if defined(__DEMAND_PAGING__) && !defined(__WINS__)
	if (DMediaPagingDevice::PageInRequest(*iCurrentReq))
		r=iCurrentReq->WriteToPageHandler((TUint8 *)(&extrView[0]), len, usrOfst);
	else
#endif	// __DEMAND_PAGING__
		r = iCurrentReq->WriteRemote(&extrView,usrOfst);

	return r;
	}

TInt DMmcMediaDriverFlash::ReadDataFromUser(TDes8& aDes, TInt aOffset)
	{
#ifndef __WINS__
	if (DMediaPagingDevice::PageOutRequest(*iCurrentReq))
		return iCurrentReq->ReadFromPageHandler((TAny*) aDes.Ptr(), aDes.MaxLength(), aOffset);
	else
#endif // #ifndef __WINS__
		return iCurrentReq->ReadRemote(&aDes, aOffset);
	}

TInt DMmcMediaDriverFlash::AdjustPhysicalFragment(TPhysAddr &aPhysAddr, TInt &aPhysLength)
//
// Retrieve next Physical memory fragment and adjust the start pointer and length with
// respect to the set offset {iFragOfset}.
// Note the offset may encompass multiple memory fragments.
//
	{
	__KTRACE_OPT(KPBUSDRV, Kern::Printf(">mmd:APF"));
	
	TInt err = KErrNone;
	TInt offset = iFragOfset;

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

		if (err != KErrNone)
			return err;
		
		if (offset >= aPhysLength) // more offset than in this physical chunk
			{
			offset -= aPhysLength;
			}
		else
			{
			// offset < physLength
			// offset lies within the memory chunk
			// Adjust length and address for first transfer
			aPhysLength -= offset;
			aPhysAddr += offset;
			offset = -1;
			}
			
		} while (offset >= 0);
	
	iFragOfset = 0; // reset offset now complete
	
	if (aPhysAddr == 0)
		{
		return KErrNoMemory;
		}

#ifdef _DEBUG
	// DMAHelper ensures memory is dma aligned
	if ( (aPhysAddr & (iSocket->DmaAlignment()-1) ) )
		{
		__KTRACE_OPT(KPBUSDRV, Kern::Printf("mmd:lr:Memory Fragment Not Word Aligned!"));
		Panic(ENotDMAAligned);
		}
#endif	//_DEBUG
	
	__KTRACE_OPT(KPBUSDRV, Kern::Printf("<mmd:APF physAddr(0x%x), physLength(%d)",aPhysAddr, aPhysLength));
	return err;
	}

TInt DMmcMediaDriverFlash::PrepareFirstPhysicalFragment(TPhysAddr &aPhysAddr, TInt &aPhysLength, TUint32 aLength)
//
// Retrieves the first Physical memory fragment and determines the type of the next transfer
// Next transfer may either be the last block (end not block aligned) or a block may straddle
// memory fragments.
//
	{
	__KTRACE_OPT(KPBUSDRV, Kern::Printf(">mmd:PFPF"));
	TInt r = KErrNone;
	
	r = AdjustPhysicalFragment(aPhysAddr, aPhysLength);
	
	if (r == KErrNone)
		{
		TUint len = I64LOW(iReqEnd & iBlkMsk);
		if ( ((TUint32)aPhysLength >= aLength) && len )
			{
			__KTRACE_OPT(KPBUSDRV, Kern::Printf(">mmd:PFPF-end block"));
			//next iteration will be an IPC for the end block
			//There is enough space in physical memory to fit
			//the extended read, but exceeds boundary for this request.
			iIPCLen = len;
			iRdROB |= KIPCSetup; // IPC setup for next iteration
			aPhysLength -= len;
			}
		
		if (aPhysLength & iBlkMsk)
			{
			__KTRACE_OPT(KPBUSDRV, Kern::Printf(">mmd:PFPF-straddles boundary"));
			// block must be straddling a fragment boundary
			// Next iteration must be an IPC 
			iRdROB |= KIPCSetup;
			
			// Calculate the offset into the next memory block
			iFragOfset = I64LOW(iBlkLen - (aPhysLength & iBlkMsk));
			aPhysLength &= ~iBlkMsk;
			}
		}
	
	__KTRACE_OPT(KPBUSDRV, Kern::Printf("<mmd:PFPF err(%d), physAddr(0x%x), physLength(%d)",r, aPhysAddr, aPhysLength));	
	return r;
	}
	

void DMmcMediaDriverFlash::PrepareNextPhysicalFragment()
//
// Retrieves next Physical memory fragment and determines the type of the next transfer
// Next transfer may either be the last block (end not block aligned) or a block may straddle
// memory fragments.
//
	{
	__KTRACE_OPT(KPBUSDRV, Kern::Printf(">mmd:PNPF"));
	TInt err = KErrNone;
	TPhysAddr physAddr = 0;
	TInt physLength = 0;
	
	err = AdjustPhysicalFragment(physAddr, physLength);
	
	if (err == KErrNone)
		{
		if (iPhysEnd+physLength >= iReqEnd)
			{
			//Last physical transfer ...
			TUint len = I64LOW(iReqEnd & iBlkMsk);
			if (len)
				{
				__KTRACE_OPT(KPBUSDRV, Kern::Printf(">mmd:PNPF-end block"));
				// end point not block aligned!
				// next iteration must be an IPC call
				iRdROB |= KIPCSetup;
				iIPCLen = len;
				physLength -= len;
				}
			else{
				physLength = I64LOW(iDbEnd  - iPhysEnd);
				}
			}
			
		if (physLength & iBlkMsk)
			{
			__KTRACE_OPT(KPBUSDRV, Kern::Printf(">mmd:PNPF-straddles boundary"));
			// block must be straddling a fragment boundary
			// Next iteration must be an IPC 
			iRdROB |= KIPCSetup;
			
			// Calculate the offset into the next memory block
			iFragOfset = I64LOW(iBlkLen - (physLength & iBlkMsk));
			physLength &= ~iBlkMsk;
			}			
		
		iPhysEnd += physLength;
		}
		
	iSession->MoreDataAvailable( (physLength  >> KDiskSectorShift), (TUint8*) physAddr, err);		
	iSecondBuffer = EFalse;
	__KTRACE_OPT(KPBUSDRV, Kern::Printf("<mmd:PNPF"));
	}

TUint8* DMmcMediaDriverFlash::ReserveReadBlocks(TInt64 aStart, TInt64 aEnd, TUint32* aLength)
//
// Assume the cache has been drained before this function is called and so
// the first block is not in the cache.  The length of the allocated range is
// either aEnd - aStart, or enough blocks such that the next block to read
// is already available in the cache, and so will be read when
// ReadDataUntilCacheExhausted() is called from the callback DFC.
//
	{
	__KTRACE_OPT(KPBUSDRV, Kern::Printf(">mmd:rrb:%lx,%lx", aStart, aEnd));

	__ASSERT_DEBUG((aStart & iBlkMsk) == 0, Panic(ERRBStAlign));
	__ASSERT_DEBUG(TotalSizeInBytes() > aStart, Panic(ERRBStPos));
	__ASSERT_DEBUG(aEnd > aStart, Panic(ERRBNotPositive));
	__ASSERT_DEBUG((aEnd & iBlkMsk) == 0, Panic(ERRBEndAlign));
	__ASSERT_DEBUG(TotalSizeInBytes() >= aEnd, Panic(ERRBEndPos));
	__ASSERT_DEBUG(!iDoDoubleBuffer, Panic(ENoDBSupport));
	__ASSERT_CACHE(CacheInvariant(), Panic(ERRBCchInv));
	__ASSERT_CACHE(GetCachedBlock(aStart & ~iBlkMsk) == 0, Panic(ERRBExist));

	TUint8* raby;

	BuildGammaArray(aStart, aEnd);

	// reposition start index at 0 if the full range would run off the end of the
	// buffer.  This is heuristic - enabling a longer multi-block may cost some
	// cached reads.  However, assume long reads do not generally re-read the same
	// data, and are used for streaming large amounts of data into memory.

	const TInt blocksInRange = I64LOW((aEnd - aStart) >> iBlkLenLog2);
	TInt startIndex = (iLstUsdCchEnt + 1) % iBlocksInBuffer;
	if (startIndex + blocksInRange > iBlocksInBuffer)
		startIndex = 0;

	// starting at startIndex, increase the range until it covers aEnd - aStart,
	// or until the next block to read is available in the cache.

	TInt blkCnt = 0;
	TBool finished;
	do
		{
		finished = (
			// range allocated for entire read
				blkCnt == blocksInRange
			// next block already exists in buffer and has not been overwritten
			// by existing multi-block read
			|| (	iGamma[blkCnt] != KNoCacheBlock
				&&	(	iGamma[blkCnt] < startIndex
					||	iGamma[blkCnt] >= startIndex + blkCnt ) ) );

		if (! finished)
			++blkCnt;
		} while (! finished);

	iLstUsdCchEnt = startIndex + blkCnt - 1;

	if (blkCnt < 1) blkCnt = 1; //RBW required < 1 block to be read
	
	TUint32 lengthInBytes = blkCnt << iBlkLenLog2;
	*aLength = lengthInBytes;
	MarkBlocks(aStart, aStart + lengthInBytes, startIndex);

	raby = IdxToCchMem(startIndex);

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

	return raby;
	}


TUint8* DMmcMediaDriverFlash::ReserveWriteBlocks(TInt64 aStart, TInt64 aEnd, TUint* aRBM)
//
// reserve a range of blocks in the buffer.  If the block containing aStart or aEnd
// are already in the buffer, attempts to position on them.  This can save one or two
// RBMs for writes.
//
// This function is linear in the number of blocks - it runs through the array
// exactly twice.
//
// aStart and aEnd are not necessarily block aligned - the function uses alignment
// information to minimize RBMs.
//
	{
	__KTRACE_OPT(KPBUSDRV, Kern::Printf(">mmd:rwb:%lx,%lx", aStart, aEnd));

	TInt64 physStart = aStart & ~iBlkMsk;
	TInt64 physEnd = (aEnd + iBlkMsk) & ~iBlkMsk;

	__ASSERT_DEBUG(TotalSizeInBytes() > physStart, Panic(ERWBStPos));
	__ASSERT_DEBUG(aEnd > aStart, Panic(ERWBNotPositive));
	__ASSERT_DEBUG(TotalSizeInBytes() >= physEnd, Panic(ERWBEndPos));
	__ASSERT_DEBUG(iDoPhysicalAddress || iDoDoubleBuffer || (!iDoDoubleBuffer && !iDoPhysicalAddress && physEnd - physStart <= (TInt64)iMaxBufSize), Panic(ERWBOverflow));
	__ASSERT_CACHE(CacheInvariant(), Panic(ERWBCchInv));
	
	const TBool firstPartial = (aStart & iBlkMsk) != 0;
	const TBool lastPartial  = (aEnd & iBlkMsk)   != 0;
	
	const TInt blkCnt = I64LOW((physEnd - physStart) >> iBlkLenLog2);
	
	TBool startUsed = EFalse;
	TBool endUsed   = EFalse;
	
	TUint8* raby = NULL;

	if(iDoDoubleBuffer)
		{
		//
		// If we're double-buffering, then the entire cache will be re-used
		// continuously.  Rather than continually reserve blocks during each 
		// transfer we calculate the blocks that will be present after all
		// transfers have completed.
		//
		InvalidateCache();
		raby = iCacheBuf;
		}
	else
		{
		TInt idx;

		// check if the first or last blocks are already in the buffer.
		TInt fst = -1, lst = -1;
		const TInt64 lstBlk = physEnd - iBlkLen;
		TInt i;
		for (i = 0; i < iBlocksInBuffer; ++i)
			{
			if (iCachedBlocks[i] == physStart)
				fst = i;

			if (iCachedBlocks[i] == lstBlk)
				lst = i;
			}

		const TBool firstUsable = (fst != -1) && (iBlocksInBuffer - fst) >= blkCnt;
		const TBool lastUsable = (lst != -1) && lst >= (blkCnt - 1);

		if (iDoPhysicalAddress)
			{				
			if ( (firstPartial || lastPartial) && blkCnt <= 2)
				{
				//Physical addressing not to be used.
				//more efficent to use local Cache copying
				iDoPhysicalAddress = EFalse;
				return raby;
				}
			else
				{						
				raby = iMinorBuf;
				
				const TBool firstPres = (fst != -1);
				const TBool lastPres = (lst != -1);
				
				if (firstPartial && firstPres)
					{
					// move to minor buffer
					memcpy(iMinorBuf, IdxToCchMem(fst), iBlkLen);
					}
				if (lastPartial && lastPres)
					{
					// move to beginning of cache
					memcpy(iCacheBuf, IdxToCchMem(lst), iBlkLen);
					}					
								
				InvalidateCache(physStart,physEnd);
				
				if (lastPartial)
					{
					//re-mark beginning of cache
					MarkBlocks((physEnd-iBlkLen), physEnd, 0);
					}
				
				if (aRBM)
					{
					*aRBM = 0;
										
					if (firstPartial) 
						*aRBM |= KWtMinFst; 
					
					if (firstPartial && !firstPres) 
						*aRBM |= KWtRBMFst;
					
					if (lastPartial) 
						*aRBM |= KWtMinLst;					
					
					if (lastPartial && !lastPres) 
						*aRBM |= KWtRBMLst;
					}
				
				return raby;
				}
			} // if (iDoPhysicalAddress)			
		
		if (!firstUsable && !lastUsable)
			{
			if(iDoDoubleBuffer)
				{
				idx = iSecondBuffer ? iBlocksInBuffer >> 1 : 0;
				}
			else
				{
				idx = (iLstUsdCchEnt + 1) % iBlocksInBuffer;
				if (idx + blkCnt > iBlocksInBuffer)
					idx = 0;
				}
			}
		else if (firstUsable && ! lastUsable)
			{
			idx = fst;
			}
		else if (! firstUsable && lastUsable)
			{
			idx = lst - (blkCnt - 1);
			}
		else	// (lastUsable && firstUsable)
			{
			if (firstPartial || ! lastPartial)
				idx = fst;
			else
				idx = lst - (blkCnt - 1);
			}

		MarkBlocks(physStart, physEnd, idx);

		// if the range started or ended on a partial block, but could not
		// be allocated on that existing block, and the existing block is
		// somewhere in the cache, then memcpy() that block to the end of the
		// range.  used is not the same as usable - both the start and end
		// blocks may be usable, through not in the same range, or any range.

		const TInt startExtent = I64LOW(aStart & iBlkMsk);
		TBool firstInTemp = EFalse;
		startUsed = (idx == fst);
		if (! startUsed && firstPartial && fst != -1)
			{
			// if the range has started at index occupied by the last block then
			// temporarily copy to minor buffer.  This is unnecessary when the
			// last block is not partial because the last block does not need to
			// be preserved.

			if (idx == lst && lastPartial)
				{
				firstInTemp = ETrue;
				memcpy(iMinorBuf, IdxToCchMem(fst), startExtent);
				}
			else
				{
				memcpy(IdxToCchMem(idx), IdxToCchMem(fst), startExtent);
				}

			startUsed = ETrue;
			}

		endUsed = (idx + blkCnt - 1 == lst);
		if (! endUsed && lastPartial && lst != -1)
			{
			const TInt endOffset = I64LOW(aEnd & iBlkMsk);
			const TInt endExtent = iBlkLen - endOffset;
			memcpy(IdxToCchMem(idx + blkCnt - 1) + endOffset, IdxToCchMem(lst) + endOffset, endExtent);
			endUsed = ETrue;
			}

		if (firstInTemp)
			memcpy(IdxToCchMem(idx), iMinorBuf, startExtent);

		// start reclaiming at block following this range
		iLstUsdCchEnt = idx + blkCnt - 1;
		raby = IdxToCchMem(idx);
		}

	// work out if read-before-write required
	if (aRBM)
		{
		*aRBM = 0;
		// first index was not already in range, and does not start on block boundary
		if (firstPartial && ! startUsed)
			*aRBM |= KWtRBMFst;

		// last index was not already in range, and does not end on block boundary
		if (lastPartial && ! endUsed)
			*aRBM |= KWtRBMLst;

		// only use one pre-read if contained in single block
		if (blkCnt == 1 && *aRBM == (KWtRBMFst | KWtRBMLst))
			*aRBM = KWtRBMFst;

		//
		// When double-buffering, RMW for the last block is stored in the
		// minor buffer and writen during the last transfer, so flag this
		// seperately (as aRBM is used for the initial RMW Read then subsequently cleared).
		//
		if(iDoDoubleBuffer && (*aRBM & KWtRBMLst))
			iDoLastRMW = ETrue;
		}

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

	return raby;
	}

void DMmcMediaDriverFlash::MarkBlocks(TInt64 aStart, TInt64 aEnd, TInt aStartIndex)
//
// mark range of blocks for media range.  If existing cache entries for any part of
// the cache are already in the buffer they are invalidated.
//
	{
	__KTRACE_OPT(KPBUSDRV, Kern::Printf("=mmd:mb:%lx,%lx,%d", aStart, aEnd, aStartIndex));

	__ASSERT_DEBUG(TotalSizeInBytes() > aStart, Panic(EMBStPos));
	__ASSERT_DEBUG((aStart & iBlkMsk) == 0, Panic(EMBStAlign));
	__ASSERT_DEBUG(aEnd > aStart, Panic(EMBNotPositive));
	__ASSERT_DEBUG(TotalSizeInBytes() >= aEnd, Panic(EMBEndPos));
	__ASSERT_DEBUG((aEnd & iBlkMsk) == 0, Panic(EMBEndAlign));
	__ASSERT_DEBUG(aStartIndex + (TInt)((aEnd - aStart) >> iBlkLenLog2) <= iBlocksInBuffer, Panic(EMBOverflow));
	__ASSERT_CACHE(CacheInvariant(), Panic(EMBCchInvPre));

	TInt i;

	for (i = 0; i < aStartIndex; ++i)
		{
		if (iCachedBlocks[i] >= aStart && iCachedBlocks[i] < aEnd)
			iCachedBlocks[i] = KInvalidBlock;
		}

	TInt blkCnt = I64LOW((aEnd - aStart) >> iBlkLenLog2);
	for (i = aStartIndex; i < aStartIndex + blkCnt; ++i)
		iCachedBlocks[i] = aStart + (static_cast<TUint32>(i - aStartIndex) << iBlkLenLog2);

	for (i = aStartIndex + blkCnt; i < iBlocksInBuffer; ++i)
		{
		if (iCachedBlocks[i] >= aStart && iCachedBlocks[i] < aEnd)
			iCachedBlocks[i] = KInvalidBlock;
		}

	__ASSERT_CACHE(CacheInvariant(), Panic(EMBCchInvPost));
	}


void DMmcMediaDriverFlash::BuildGammaArray(TInt64 aStart, TInt64 aEnd)
//
// iGamma is an array of indexes that correspond to cached blocks starting
// from aStart.  iGamma[0] is the index of aStart, iGamma[1] is the index of
// aStart + iBlkLen, and so on.  Building an array here means that all of
// the available cached entries can be found in linear time instead of
// quadratically searching through the array for each block.
//
	{
	__KTRACE_OPT(KPBUSDRV, Kern::Printf("=mmd:bga:%lx,%lx", aStart, aEnd));

	__ASSERT_DEBUG(TotalSizeInBytes() > aStart, Panic(EBGAStPos));
	__ASSERT_DEBUG((aStart & iBlkMsk) == 0, Panic(EBGAStAlign));
	__ASSERT_DEBUG(aEnd > aStart, Panic(EBGANotPositive));
	__ASSERT_DEBUG(TotalSizeInBytes() >= aEnd, Panic(EBGAEndPos));
	__ASSERT_DEBUG((aEnd & iBlkMsk) == 0, Panic(EBGAEndAlign));
	__ASSERT_DEBUG(aEnd - aStart <= (TInt64) iMaxBufSize, Panic(EBGAOverflow));
	__ASSERT_CACHE(CacheInvariant(), Panic(EBGACchInv));

	// KNoCacheBlock = (0xff) x 4
	TUint blocksInRange = I64LOW((aEnd - aStart) >> iBlkLenLog2);
	memset(iGamma, 0xff, sizeof(*iGamma) * blocksInRange);

	TInt64 blkAddr = 0;
	for (TInt i = 0; ( (blocksInRange > 0 ) && (i < iBlocksInBuffer) ); ++i)
		{
		blkAddr = iCachedBlocks[i];
		if (blkAddr >= aStart && blkAddr < aEnd)
			{
			iGamma[I64LOW((blkAddr - aStart) >> iBlkLenLog2)] = i;
			blocksInRange--;
			}
		}
	}

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

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

// Invalidate any cache entries from aStart to aEnd
// This is for DMA writes and is to prevent the cache becoming inconsistent with the media.
void DMmcMediaDriverFlash::InvalidateCache(TInt64 aStart, TInt64 aEnd)
	{
	__KTRACE_OPT(KPBUSDRV, Kern::Printf("=mmd:ich:%lx,%lx", aStart, aEnd));

	__ASSERT_DEBUG(TotalSizeInBytes() > aStart, Panic(EBGAStPos));
	__ASSERT_DEBUG(aEnd > aStart, Panic(EBGANotPositive));
	__ASSERT_DEBUG(TotalSizeInBytes() >= aEnd, Panic(EBGAEndPos));

	const TInt blkCnt = I64LOW((aStart - aEnd) >> iBlkLenLog2);

	__ASSERT_CACHE(CacheInvariant(), Panic(EBGACchInv));
	
	TInt64 endBlk = (blkCnt == 0) ? (aStart+iBlkLen) : aEnd;			
	
	for (TInt i = 0; i < iBlocksInBuffer; ++i)
		{
		const TInt64 blkAddr = iCachedBlocks[i];
		if (blkAddr >= aStart && blkAddr < endBlk)
			iCachedBlocks[i] = KInvalidBlock;
		}
	}

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

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

TInt DMmcMediaDriverFlash::CchMemToIdx(TUint8* aMemP) const
	{
	__ASSERT_DEBUG((aMemP >= iCacheBuf) && (aMemP < iCacheBuf + (iBlocksInBuffer << iBlkLenLog2)), Panic(ECMIOverflow));

	return((aMemP - iCacheBuf) >> iBlkLenLog2);
	}

#ifdef _DEBUG_CACHE
TBool DMmcMediaDriverFlash::CacheInvariant()
//
// check each cache entry refers to a valid block and that no two
// entries cover the same block.  This algorithm is quadratic in
// the cache length.
//
	{
	for (TInt i = 0; i < iBlocksInBuffer; ++i)
		{
		if (iCachedBlocks[i] == KInvalidBlock)
			continue;

		if ((iCachedBlocks[i] & iBlkMsk) != 0)
			return EFalse;

		if (iCachedBlocks[i] >= TotalSizeInBytes())
			return EFalse;

		for (TInt j = i + 1; j < iBlocksInBuffer; ++j)
			{
			if (iCachedBlocks[i] == iCachedBlocks[j])
				return EFalse;
			}
		}

	return ETrue;
	}
#endif

void DMmcMediaDriverFlash::NotifyPowerDown()
	{
	__KTRACE_OPT(KPBUSDRV,Kern::Printf(">Mmc:NotifyPowerDown"));

	iSessionEndDfc.Cancel();
	iDataTransferCallBackDfc.Cancel();

	EndInCritical();

	// need to cancel the session as the stack doesn't take too kindly to having the same session engaged more than once.
	if (iSession)
		iStack->CancelSession(iSession);

	CompleteRequest(KErrNotReady);
	iMedReq = EMReqIdle;
	}

void DMmcMediaDriverFlash::NotifyEmergencyPowerDown()
	{
	__KTRACE_OPT(KPBUSDRV,Kern::Printf(">Ata:NotifyEmergencyPowerDown"));

	iSessionEndDfc.Cancel();
	iDataTransferCallBackDfc.Cancel();

	TInt r=KErrNotReady;
	if (iCritical)
		r=KErrAbort;
	EndInCritical();

	// need to cancel the session as the stack doesn't take too kindly to having the same session engaged more than once.
	if (iSession)
		iStack->CancelSession(iSession);

	CompleteRequest(r);
	iMedReq = EMReqIdle;
	}

TInt DMmcMediaDriverFlash::Request(TLocDrvRequest& aRequest)
	{
	__KTRACE_OPT(KLOCDRV,Kern::Printf("MmcMd:Req %08x id %d",&aRequest,aRequest.Id()));
	TInt r=KErrNotSupported;
	TInt id=aRequest.Id();

#if defined (__TEST_PAGING_MEDIA_DRIVER__)
	DThread* client=aRequest.Client();
	__KTRACE_OPT(KLOCDPAGING,Kern::Printf("Client:0x%08x",client));
#endif // __TEST_PAGING_MEDIA_DRIVER__

	// First handle requests that can be handled without deferring
	if(id==DLocalDrive::ECaps)
		{
		TLocalDriveCapsV6& c = *(TLocalDriveCapsV6*)aRequest.RemoteDes();
		TLocDrv& drive = *aRequest.Drive();
		r = Caps(drive, c);
		c.iSize = drive.iPartitionLen;
		c.iPartitionType = drive.iPartitionType;	
		c.iHiddenSectors = (TUint) (drive.iPartitionBaseAddr >> KDiskSectorShift);
		return r;
		}

	// All other requests must be deferred if a request is currently in progress
	if (iCurrentReq)
		{

#if defined(__TEST_PAGING_MEDIA_DRIVER__)
		if (DMediaPagingDevice::PageInRequest(*iCurrentReq))
			iMmcStats.iReqPage++;
		else
			iMmcStats.iReqNormal++;
#endif // __TEST_PAGING_MEDIA_DRIVER__

		// a request is already in progress, so hold on to this one
		__KTRACE_OPT(KLOCDRV,Kern::Printf("MmcMd:Req %08x ret 1",&aRequest));
		return KMediaDriverDeferRequest;
		}
	else
		{
		iCurrentReq=&aRequest;
		TUint partitionType = iCurrentReq->Drive()->iPartitionType;
		TBool readOnly = (partitionType == KPartitionTypeRofs || partitionType == KPartitionTypeROM);

		switch (id)
			{


#if defined(__DEMAND_PAGING__)
			case DMediaPagingDevice::ERomPageInRequest:
				__KTRACE_OPT(KLOCDPAGING,Kern::Printf("DMediaDriverFlash::Request(ERomPageInRequest)"));
				BTraceContext8(BTrace::EPagingMedia,BTrace::EPagingMediaPagingMedDrvBegin,MEDIA_DEVICE_MMC,iCurrentReq);
				r=DoRead();
				break;
			case DMediaPagingDevice::ECodePageInRequest:
				__KTRACE_OPT(KLOCDPAGING,Kern::Printf("DMediaDriverFlash::Request(ECodePageInRequest)"));
				BTraceContext8(BTrace::EPagingMedia,BTrace::EPagingMediaPagingMedDrvBegin,MEDIA_DEVICE_MMC,iCurrentReq);
				r=DoRead();
				break;
#endif	// __DEMAND_PAGING__

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

			case DLocalDrive::ERead:
				r=DoRead();
				break;
			case DLocalDrive::EWrite:
				if (readOnly)
					return KErrNotSupported;
				r=DoWrite();
				break;
			case DLocalDrive::EFormat:
				if (readOnly)
					return KErrNotSupported;
				r=DoFormat();
				break;

#if defined __TEST_PAGING_MEDIA_DRIVER__
			case DLocalDrive::EControlIO:
				{
				r = HandleControlIORequest();
				break;
				}
#endif

			case DLocalDrive::EPasswordUnlock:
			case DLocalDrive::EPasswordLock:
			case DLocalDrive::EPasswordClear:
				// Don't allow passords on internal MMC; one reason is that this may be used for paging and 
				// we can't really stop paging just because the password hasn't been supplied
				if (iInternalSlot)
					r = KErrNotSupported;
				else
					r = DoPasswordOp();
				break;
			case DLocalDrive::EPasswordErase:
				{
				r = LaunchRPIErase();
				// This will complete the request in the event of an error
				if(r != KErrNone)
					PartitionInfoComplete(r);

				r = KErrNone; // ensures to indicate asynchronoous completion
				break;
				}
			case DLocalDrive::EWritePasswordStore:
				{
				//
				// If the card is ready and locked, request the stack to perform the 
				// auto-unlock sequence.  This is required, as the stack only performs
				// automatic unlocking during power-up, and the stack may already be powered.
				//
				r = KErrNone; // asynchronous completion

				if(iCard->IsReady() && iCard->IsLocked())
					{
					iSession->SetupCIMAutoUnlock();
					if(EngageAndSetRequest(EMReqWritePasswordData, 0) != KErrNone)
						{
						// If error, complete with KErrNone anyway
						//  - The password store has been set, any errors
						//    will be reported on the next access.
						CompleteRequest(KErrNone);
						}
					}
				else
					{
					CompleteRequest(KErrNone);
					}
				break;
				}
			case DLocalDrive::EEnlarge:
			case DLocalDrive::EReduce:
			default:
				r=KErrNotSupported;
				break;
			}
		}

	__KTRACE_OPT(KLOCDRV,Kern::Printf("MmcMd:Req %08x cmp %d",&aRequest,r));

	if (r != KErrNone)
		{
		iMedReq = EMReqIdle;
		iCurrentReq=NULL;
		SetCurrentConsumption(KIdleCurrentInMilliAmps);
		}
	
	return r;
	}

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

#ifdef _DEBUG_CACHE
TUint8* DMmcMediaDriverFlash::GetCachedBlock(TInt64 aMdAddr)
// 
// return cache block for media at aMdAddr, 0 if not found.
// This is a debug function to determine whether or not a block is in
// the cache.  It should not be used for general block retrieval.
// If there are m blocks in the cache, and n in the requested range,
// this function is o(mn), whereas BuildGammaArray() is theta(m).
//
	{
	__KTRACE_OPT(KPBUSDRV, Kern::Printf(">mmd:gcb:%lx", aMdAddr));

	__ASSERT_DEBUG((aMdAddr & iBlkMsk) == 0, Panic(EGCBAlign));
	__ASSERT_DEBUG(TotalSizeInBytes() > aMdAddr, Panic(EGCBPos));
	__ASSERT_CACHE(CacheInvariant(), Panic(EGCBCchInv));

	for (TInt i = 0; i < iBlocksInBuffer; ++i)
		{
		if (iCachedBlocks[i] == aMdAddr)
			{
			TUint8* raby = IdxToCchMem(i);
			__KTRACE_OPT(KPBUSDRV, Kern::Printf("<mmd:gcb:%x", (TUint32) raby));
			return raby;
			}
		}

	__KTRACE_OPT(KPBUSDRV, Kern::Printf("<mmd:gcb:0"));
	return 0;
	}
#endif // _DEBUG_CACHE


#if defined(__TEST_PAGING_MEDIA_DRIVER__)
/**
Handles a ControlIO request to the MMC media driver.
made by one of the MMC paging tests

@internalTechnology

@return Corresponding Symbian OS error code
*/
TInt DMmcMediaDriverFlash::HandleControlIORequest()
	{
	const TInt command = iCurrentReq->Int0();
	TAny* aParam1 = iCurrentReq->Ptr1();
//	TAny* aParam2 = iCurrentReq->Ptr2();

	TInt r = KErrCompletion;

	__KTRACE_OPT(KLOCDPAGING,Kern::Printf("[MD :   ] HandleControlIORequest aCommand: 0x%x", command));


	switch (command)
		{
		case KMmcGetStats:
			{	
			DThread* pC = iCurrentReq->Client();
			DThread* pT = iCurrentReq->RemoteThread();
			if (!pT)
				pT = pC;
			Kern::ThreadRawWrite(pT, aParam1, &iMmcStats, sizeof(iMmcStats), pC);
		
			iMmcStats.iReqNormal=0;
			iMmcStats.iNormalFragmenting=0;
			iMmcStats.iClashFragmenting=0;
				
			break; 
			}
		default:
			r=KErrNotSupported;
			break;
		}

	return r;
	}
#endif	// __TEST_PAGING_MEDIA_DRIVER__




DECLARE_EXTENSION_PDD()
	{
	// NB if the media driver has been defined as a kernel extension in the .OBY/.IBY file 
	// i.e the "extension" keyword has been used rather than "device", then an instance of 
	// DPhysicalDeviceMediaMmcFlash will already have been created by InitExtension(). In this 
	// case the kernel will see that an object of the same name already exists and delete the 
	// new one.
	return new DPhysicalDeviceMediaMmcFlash;
	}
DECLARE_STANDARD_EXTENSION()
	{	
	__KTRACE_OPT(KBOOT,Kern::Printf("Creating MMCDrv PDD"));

	DPhysicalDeviceMediaMmcFlash* device = new DPhysicalDeviceMediaMmcFlash;

	TInt r;
	if (device==NULL)
		r=KErrNoMemory;
	else
		r=Kern::InstallPhysicalDevice(device);
	__KTRACE_OPT(KBOOT,Kern::Printf("Installing MMCDrv PDD in kernel returned %d",r));

	__KTRACE_OPT(KBOOT,Kern::Printf("Mmc extension entry point drive returns %d",r));
	return r;
	}
	

