emulator/emulatorbsp/specific/lffsdev.cpp
changeset 0 cec860690d41
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/emulator/emulatorbsp/specific/lffsdev.cpp	Tue Feb 02 01:39:10 2010 +0200
@@ -0,0 +1,642 @@
+// Copyright (c) 1996-2009 Nokia Corporation and/or its subsidiary(-ies).
+// All rights reserved.
+// This component and the accompanying materials are made available
+// under the terms of "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:
+// wins\specific\lffsdev.cpp
+// 
+//
+
+#include <flash_media.h>
+#include <emulator.h>
+#include <property.h>
+
+#define FLASH_FAULT()	Kern::Fault("LFFSDEV",__LINE__)
+
+//#define M18_EMULATION // Test for Control Mode operation
+
+/********************************************
+ * Driver definitions
+ ********************************************/
+
+/** LFFS image name */
+const CHAR KLfsFileName[] = "LFSLDRV.BIN";
+
+
+//-- default values for LFFS related entries in "epoc.ini" file
+const TInt      KDefaultFlashSize = 0x400000;   //-- "FlashSize"        entry, default 4MB
+const TInt      KEraseBlockSize   = 0x20000;	//-- "FlashEraseSize"   entry, default 128KB
+const TUint32   KFlashEraseTime   =	1000000;	//-- "FlashEraseTime"   entry, default 1s
+const TInt      KWriteBlockSize   = 64;         //-- "FlashWriteSize"   entry
+const TInt      KFlashWriteTime   = 406;		//-- "FlashWriteTime"   entry, default 406us
+const TInt      KResumeTime       = 5000;		//-- "FlashResumeTime"  entry, default 5ms
+
+//-- other possible LFFS related entries in "epoc.ini" file:
+//-- "FlashHoldOffTime"   default value = iEraseTime/10
+
+//-- "FlashForceImgMount" default value = 0. If not 0 LFFS image will be mounted as it is, even if it doesn't have TLfsParams structure in the end.
+//-- Moreover, it won't be zero filled and TLfsParams structure won't be written at the end of the image file.
+//-- shall be useful for dealing with real images from the real devices
+
+
+/** 
+    This cunning structure is supposed to be at the very end of the LFFS image file. If it is not foung there, 
+    the image gets zero-filled (depends on "FlashForceImgMount" flag in epoc.ini). 
+*/
+
+struct TLfsParams
+	{
+	TInt iEraseSize;
+	};
+
+#ifdef _DEBUG
+/***************************************************
+ * ControlIO command types - for debug builds, only
+ ***************************************************/
+
+enum TCtrlIoTypes
+	{
+	//KCtrlIoRww=0,
+	KCtrlIoTimeout=1
+	};
+#endif
+
+
+const TInt KDataBufSize=1024;
+
+/********************************************
+ * Media driver class
+ ********************************************/
+class DMediaDriverFlashWin32 : public DMediaDriverFlash
+	{
+public:
+	enum TState
+		{
+		EIdle=0,
+		EWriting=1,
+		EEraseNoSuspend=2,
+		EErase=3,
+		ESuspendPending=4,
+		ESuspending=5,
+		ESuspended=6,
+		EErasePending=7
+		};
+public:
+	DMediaDriverFlashWin32(TInt aMediaId);
+public:
+	// replacing pure virtual - FLASH device specific stuff
+	virtual TInt Initialise();
+	virtual TUint32 EraseBlockSize();
+	virtual TUint32 TotalSize();
+	virtual TInt DoRead();
+	virtual TInt DoWrite();
+	virtual TInt DoErase();
+	virtual TInt Caps(TLocalDriveCapsV2& caps);
+public:
+	void HandleEvent();
+	void StartTimer(TInt aMicros);
+	void StartErase();
+	void SuspendErase();
+	void CompleteErase();
+	void CompleteWrite();
+	void CompleteSuspend();
+	void StartPendingRW();
+	void ReadFlashParameters();
+public:
+	static void TimerFn(TAny* aPtr);
+	static void EventDfc(TAny* aPtr);
+#ifdef _DEBUG
+public:
+    enum TCtrlIoState {/*EIdle=0,ECtrlIoWaitWr=1,ECtrlIoWaitRd=2,ECtrlIoWrActive=3,*/ECtrlIoTimeOutPrep=4};
+    TInt ControlIO(TInt aCommand,TAny* aParam1,TAny* /*aParam2*/);
+	TInt Request(TLocDrvRequest& m);
+#endif
+public:
+	TState iState;
+	
+	TInt iSize;
+	TInt iEraseBlockSize;
+	TInt iEraseTime;
+	TInt iSuspendHoldOffTime;
+	TInt iResumeTime;
+	TInt iWriteBlockSize;
+	TInt iWriteTime;
+    
+	HANDLE iFile;
+	HANDLE iMapping;
+	TUint8* iBase;
+	TUint8* iData;		// data being written
+	
+	NTimer iTimer;
+	TDfc iEventDfc;
+	TUint32 iTickPeriod;
+	TUint32 iEraseCounter;
+	TUint32 iErasePos;
+	TInt iTimerExtra;
+
+#ifdef _DEBUG
+public:
+    TUint8 iCtrlIoState;
+#endif
+	};
+
+DMediaDriverFlashWin32::DMediaDriverFlashWin32(TInt aMediaId)
+	:	DMediaDriverFlash(aMediaId),
+		iTimer(&TimerFn,this),
+		iEventDfc(&EventDfc,this,NULL,2),
+		iTickPeriod(NKern::TickPeriod())
+	{
+	// iState=EIdle;
+#ifdef _DEBUG
+    //iCtrlIoState=EIdle;
+#endif
+
+	}
+
+void DMediaDriverFlashWin32::ReadFlashParameters()
+//
+// Read the flash parameters from the ini file, or use defaults
+//
+	{
+   
+	iSize = Property::GetInt("FlashSize", KDefaultFlashSize);
+
+	TInt nblocks = Property::GetInt("FlashEraseBlocks", 0);
+	if (nblocks == 0)
+		nblocks = iSize/Property::GetInt("FlashEraseSize", KEraseBlockSize);
+
+	iEraseBlockSize = iSize / nblocks;
+	__ASSERT_ALWAYS(iEraseBlockSize * nblocks == iSize, FLASH_FAULT());
+	__ASSERT_ALWAYS((iEraseBlockSize & (iEraseBlockSize-1)) == 0, FLASH_FAULT());
+
+	iEraseTime = Property::GetInt("FlashEraseTime", KFlashEraseTime);
+	
+	iSuspendHoldOffTime = Property::GetInt("FlashHoldOffTime", iEraseTime/10);
+	iResumeTime = Property::GetInt("FlashResumeTime", KResumeTime);
+	iWriteBlockSize = Property::GetInt("FlashWriteSize", KWriteBlockSize);
+	__ASSERT_ALWAYS((iWriteBlockSize & (iWriteBlockSize-1)) == 0, FLASH_FAULT());
+
+	iWriteTime = Property::GetInt("FlashWriteTime", KFlashWriteTime);
+	
+	}
+
+TInt DMediaDriverFlashWin32::Initialise()
+	{
+	iEventDfc.SetDfcQ(iPrimaryMedia->iDfcQ);
+	iData=(TUint8*)Kern::Alloc(KDataBufSize);
+	if (!iData)
+		return KErrNoMemory;
+
+	ReadFlashParameters();
+
+	// locate/open the file that models the flash
+	CHAR filename[MAX_PATH];
+	strcpy(filename, Property::GetString("EmulatorMediaPath"));
+	if (!Emulator::CreateAllDirectories(filename))
+		return Emulator::LastError();
+	strcat(filename, KLfsFileName);
+	
+	iFile = CreateFileA(filename, GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_FLAG_RANDOM_ACCESS, NULL);
+	if (iFile == INVALID_HANDLE_VALUE)
+		return Emulator::LastError();
+
+	TLfsParams p;
+	p.iEraseSize = -1;
+	DWORD bytes;
+	TBool valid;
+	
+    //-- try to read TLfsParams structure from the end of the image file.
+	if (SetFilePointer(iFile, iSize, NULL, FILE_BEGIN) != -1
+		&& ReadFile(iFile, &p, sizeof(TLfsParams), &bytes, NULL)
+		&& p.iEraseSize == iEraseBlockSize)
+	{
+        valid = ETrue; //-- read it OK.
+	}
+	else
+	{//-- couldn't read TLfsParams structure from the end of the image file.
+     //-- if "FlashForceImgMount" parameter from epoc.ini is 0 or not present,
+     //-- zero-fill the lffs image file and write TLfsParams structure at the end of the file.
+
+		const TInt forceImgMount = Property::GetInt("FlashForceImgMount", 0);
+        if(!forceImgMount)
+		{
+		p.iEraseSize = iEraseBlockSize;
+		if (SetFilePointer(iFile,iSize, NULL, FILE_BEGIN) == -1
+			|| !WriteFile(iFile, &p, sizeof(p), &bytes, NULL)
+			|| !SetEndOfFile(iFile))
+			return Emulator::LastError();
+		    
+		valid = EFalse;
+		}
+        else
+        {//-- mount LFFS image forcingly.
+            valid = ETrue;
+        }
+	}
+
+	iMapping = CreateFileMappingA(iFile, NULL, PAGE_READWRITE, 0, iSize, NULL);
+	if (iMapping == NULL)
+		return Emulator::LastError();
+
+	iBase = (TUint8*)MapViewOfFile(iMapping, FILE_MAP_WRITE, 0, 0, iSize);
+	if (iBase == NULL)
+		return Emulator::LastError();
+
+	//-- zero-fill media image it doesn't contain TLfsParams data at the very end.
+	if (!valid)
+	    {
+		memclr(iBase, iSize);
+        }
+
+	return KErrNone;
+	}
+
+TUint32 DMediaDriverFlashWin32::EraseBlockSize()
+	{
+	return iEraseBlockSize;
+	}
+
+TUint32 DMediaDriverFlashWin32::TotalSize()
+	{
+	return iSize;
+	}
+
+TInt DMediaDriverFlashWin32::DoRead()
+	{
+	if (iWriteReq)
+		return 1;	// write in progress so defer read
+	
+	__KTRACE_OPT(KLOCDRV,Kern::Printf("Flash:DoRead"));
+
+	if (iState==EErasePending)
+		{
+		iTimer.Cancel();
+		iState = ESuspended;
+		}
+	if (iState==EIdle || iState==ESuspended)
+		{
+		// can do the read now
+		TInt pos=(TInt)iReadReq->Pos();
+		TInt len=(TInt)iReadReq->Length();
+
+		TPtrC8 des(iBase+pos,len);
+		TInt r=iReadReq->WriteRemote(&des,0);
+		Complete(EReqRead,r);
+		if (iState==ESuspended)
+			StartErase();
+		}
+	else if (iState==EErase)
+		{
+		// erase in progress - suspend it
+		SuspendErase();
+		}
+	else if (iState==EEraseNoSuspend)
+		iState=ESuspendPending;
+	// wait for suspend to complete
+	return KErrNone;
+	}
+
+TInt DMediaDriverFlashWin32::DoWrite()
+	{
+	if (iReadReq)
+		return 1;	// read in progress so defer write
+
+	if (iState==EErasePending)
+		{
+		iTimer.Cancel();
+		iState = ESuspended;
+		}
+	if (iState==EIdle || iState==ESuspended)
+		{
+		// can start the write now
+		__KTRACE_OPT(KLOCDRV,Kern::Printf("Flash:Write Pos=%08x Length=%08x RemDesOff=%08x",
+											(TInt)iWriteReq->Pos(),(TInt)iWriteReq->Length(),iWriteReq->RemoteDesOffset()));
+		if (iState==EIdle)
+			iState=EWriting;
+		TInt pos = (TInt)iWriteReq->Pos();
+		TInt end = pos + (TInt)iWriteReq->Length();
+		pos &= ~(iWriteBlockSize-1);
+		end = (end + iWriteBlockSize-1) & ~(iWriteBlockSize-1);
+		StartTimer(((end-pos)/iWriteBlockSize) * iWriteTime);
+		}
+	else if (iState==EErase)
+		{
+		// erase in progress - suspend it
+		SuspendErase();
+		}
+	else if (iState==EEraseNoSuspend)
+		iState=ESuspendPending;
+	// wait for suspend to complete
+	return KErrNone;
+	}
+
+void DMediaDriverFlashWin32::CompleteWrite()
+//
+// Do the actual write in the completion
+// Transfer data in blocks from the client and AND it to the 'flash'
+//
+	{
+	__KTRACE_OPT(KLOCDRV,Kern::Printf("Flash:WriteComplete"));
+
+	TInt r = KErrNone;
+	TUint8* flash = iBase + (TInt)iWriteReq->Pos();
+	TInt len = (TInt)iWriteReq->Length();
+	TInt offset = 0;
+	while (len > 0)
+		{
+		TInt size = Min(len, KDataBufSize);
+		TPtr8 des(iData,size);
+		r = iWriteReq->ReadRemote(&des, offset);
+		if (r!=KErrNone)
+			break;
+		len -= size;
+		offset += size;
+		const TUint8* ptr = iData;
+		do
+			{
+			*flash++ &= *ptr++;
+			} while (--size > 0);
+		}
+
+	if (iState == EWriting)
+		iState = EIdle;
+	Complete(EReqWrite,r);
+	if (iState == ESuspended)
+		StartErase();
+	}
+
+TInt DMediaDriverFlashWin32::DoErase()
+	{
+	if (iReadReq || iWriteReq)
+		return 1;		// read or write in progress so defer this request
+	TInt pos=(TUint32)iEraseReq->Pos();
+	TInt len=(TUint32)iEraseReq->Length();
+	__KTRACE_OPT(KLOCDRV,Kern::Printf("Flash:DoErase %d@%08x",len,pos));
+	if (len!=iEraseBlockSize)
+		return KErrArgument;	// only allow single-block erase
+	if (pos & (iEraseBlockSize-1))
+		return KErrArgument;	// start position must be on erase block boundary
+	__ASSERT_ALWAYS(iState==EIdle,FLASH_FAULT());
+	iErasePos=pos;
+	StartErase();
+	return KErrNone;
+	}
+
+void DMediaDriverFlashWin32::StartTimer(TInt aMicros)
+	{
+	aMicros += iTimerExtra - (iTickPeriod>>1);
+	if (aMicros < 0)
+		{
+		iTimerExtra = aMicros + (iTickPeriod>>1);
+		iEventDfc.Enque();		// go off 'immediately'
+		}
+	else
+		{
+		iTimerExtra = 0;
+		iTimer.OneShot(aMicros / iTickPeriod);
+		}
+	}
+
+void DMediaDriverFlashWin32::TimerFn(TAny* aPtr)
+	{
+	((DMediaDriverFlashWin32*)aPtr)->iEventDfc.Add();
+	}
+
+void DMediaDriverFlashWin32::EventDfc(TAny* aPtr)
+	{
+	((DMediaDriverFlashWin32*)aPtr)->HandleEvent();
+	}
+
+void DMediaDriverFlashWin32::HandleEvent()
+	{
+	__KTRACE_OPT(KLOCDRV,Kern::Printf("Flash:Event %d", iState));
+	switch (iState)
+		{
+	case ESuspended:
+	case EWriting:	// write completed
+		{
+#ifdef _DEBUG
+		if(iCtrlIoState==ECtrlIoTimeOutPrep)
+			{
+			iState=EIdle;
+			iCtrlIoState=EIdle;
+			Complete(EReqWrite,KErrNotReady);
+			}
+		else
+#endif
+			CompleteWrite();
+		break;
+		}
+	case EEraseNoSuspend:
+		{
+#ifdef _DEBUG
+		if(iCtrlIoState==ECtrlIoTimeOutPrep)
+			{
+			iState=EIdle;
+			iCtrlIoState=EIdle;
+			Complete(EReqErase,KErrNotReady);
+			}
+		else
+			{
+#endif
+		TInt remain = iEraseCounter - NKern::TickCount();
+		if (remain <= 0)
+			CompleteErase();
+		else
+			{
+			iState=EErase;
+			StartTimer(remain * iTickPeriod);
+			}
+#ifdef _DEBUG
+			}
+#endif
+		break;
+		}
+	case EErasePending:
+		StartErase();
+		break;
+	case EErase:	// erase completed
+		CompleteErase();
+		break;
+	case ESuspendPending:
+		if (TInt(iEraseCounter - NKern::TickCount()) <= 0)
+			CompleteErase();
+		else
+			SuspendErase();
+		break;
+	case ESuspending:
+		CompleteSuspend();
+		break;
+	default:
+		__KTRACE_OPT(KPANIC,Kern::Printf("iState=%d",iState));
+		FLASH_FAULT();
+		}
+	}
+
+void DMediaDriverFlashWin32::StartErase()
+//
+// Continue an erase - iEraseCounter has the remaining time for the erase
+//
+	{
+	__KTRACE_OPT(KLOCDRV,Kern::Printf("Flash:StartErase %08x",iBase+iErasePos));
+	switch (iState)
+		{
+	case ESuspended:
+		iState = EErasePending;
+		StartTimer(iResumeTime);
+		break;
+	case EIdle:	// starting to erase
+		iEraseCounter = iEraseTime;
+	case EErasePending:
+		{
+		iState = EEraseNoSuspend;
+		TUint32 remain = iEraseCounter;
+		iEraseCounter = NKern::TickCount() + remain/iTickPeriod;
+		StartTimer(Min(remain, iSuspendHoldOffTime));
+		}
+		break;
+	default:
+		__KTRACE_OPT(KPANIC,Kern::Printf("iState=%d",iState));
+		FLASH_FAULT();
+		}
+	}
+
+void DMediaDriverFlashWin32::SuspendErase()
+	{
+	__ASSERT_ALWAYS(iState==EErase || iState==ESuspendPending,FLASH_FAULT());
+	__KTRACE_OPT(KLOCDRV,Kern::Printf("Flash:SuspendErase %08x",iBase+iErasePos));
+	iTimer.Cancel();
+	TInt remain = Max(0, TInt(iEraseCounter - NKern::TickCount()));
+	iEraseCounter = remain * iTickPeriod;
+	iState = ESuspending;
+	StartTimer(0);
+	}
+
+void DMediaDriverFlashWin32::CompleteSuspend()
+	{
+	// erase suspend completion
+	__KTRACE_OPT(KLOCDRV,Kern::Printf("Flash:SuspendComplete"));
+
+	iState = ESuspended;
+	// start any pending read or write requests
+	StartPendingRW();
+	}
+
+void DMediaDriverFlashWin32::CompleteErase()
+//
+// Do the actual erase in the completion
+//
+	{
+	// erase completion
+	__KTRACE_OPT(KLOCDRV,Kern::Printf("Flash:EraseComplete"));
+
+	memset(iBase + iErasePos, 0xff, iEraseBlockSize);
+
+	// complete the erase request
+	iState = EIdle;
+	Complete(EReqErase,KErrNone);
+
+	// start any pending read or write requests
+	StartPendingRW();
+	}
+
+
+void DMediaDriverFlashWin32::StartPendingRW()
+	{
+	// start any pending read or write requests
+	if (iReadReq)
+		DoRead();
+	if (iWriteReq)
+		DoWrite();
+	}
+
+#ifdef _DEBUG
+// Override the base class version in order to provide access to ControlIO
+//
+TInt DMediaDriverFlashWin32::Request(TLocDrvRequest& m)
+	{
+	TInt r;
+	TInt id=m.Id();
+	__KTRACE_OPT(KLOCDRV,Kern::Printf(">DMediaDriverFlashWin32::Request %d",id));
+	if (id!=DLocalDrive::EControlIO)
+		{
+		r=DMediaDriverFlash::Request(m);
+		return r;
+		}
+	r=ControlIO((TInt)m.iArg[0],m.iArg[1],m.iArg[2]);
+	DMediaDriver::Complete(m,r);
+	return r;
+	}
+
+TInt DMediaDriverFlashWin32::ControlIO(TInt aCommand,TAny* /*aParam1*/,TAny* /*aParam2*/)
+	{
+	switch (aCommand)
+		{
+		case(KCtrlIoTimeout):
+			{
+			__KTRACE_OPT(KLOCDRV,Kern::Printf("Flash:DoControlIO invoked for KCtrlIoTimeout"));
+			// The aim of this test is simulate a flash timeout (and so exercise the consequent
+			// actions of the software that initiated the request)				
+			if(iCtrlIoState!=EIdle)
+				{
+				__KTRACE_OPT(KLOCDRV,Kern::Printf("Flash: ControlIO request before previous completed"));
+				return KErrServerBusy;
+				}
+			else
+				{
+				__KTRACE_OPT(KLOCDRV,Kern::Printf("Flash: ControlIO timeout initiated"));
+				iCtrlIoState=ECtrlIoTimeOutPrep;
+				}
+			break;
+			}
+		
+		default:
+			{
+			__KTRACE_OPT(KLOCDRV,Kern::Printf("Flash: ERROR - unrecognised ControlIO command %d",aCommand));
+			FLASH_FAULT();
+			break;
+			}
+		}
+	return KErrNone;
+	}
+	
+#endif
+
+TInt DMediaDriverFlashWin32::Caps(TLocalDriveCapsV2& aCaps)
+// On return, aCaps data contains capability information about the 
+// flash device, in the form of a class derived from TLocalDriveCapsV2. 
+// The size of the derived class should not exceed KMaxLocalDriveCapsLength 
+// which is defined and used in e32\drivers\locmedia\locmedia.cpp. If a 
+// larger sized capabilities class is used, and this code is modified to 
+// write to member data beyond KMaxLocalDriveCapsLength this will cause a 
+// fault.
+	{
+	// Invoke the base class method then update the sizes for
+	// Control Mode and Object Mode as required.
+	DMediaDriverFlash::Caps(aCaps);
+#if defined (M18_EMULATION)
+	TLocalDriveCapsV7* caps = &((TLocalDriveCapsV7&)(aCaps));
+	caps->iControlModeSize=16;
+	caps->iObjectModeSize=1024;
+    __KTRACE_OPT( KLOCDRV, Kern::Printf("MLFS: ) ControlModeSize UPDATED as=0x%x", caps->iControlModeSize) );
+    __KTRACE_OPT( KLOCDRV, Kern::Printf("MLFS: ) ObjectModeSize UPDATED as=0x%x", caps->iObjectModeSize) );
+#endif
+	return KErrCompletion;	// synchronous completion
+	}
+
+DMediaDriverFlash* DMediaDriverFlash::New(TInt aDevice)
+	{
+	return new DMediaDriverFlashWin32(aDevice);
+	}
+
+
+
+