diff -r 000000000000 -r cec860690d41 emulator/emulatorbsp/specific/lffsdev.cpp --- /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 +#include +#include + +#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); + } + + + +