// 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" fileconst TInt KDefaultFlashSize = 0x400000; //-- "FlashSize" entry, default 4MBconst TInt KEraseBlockSize = 0x20000; //-- "FlashEraseSize" entry, default 128KBconst TUint32 KFlashEraseTime = 1000000; //-- "FlashEraseTime" entry, default 1sconst TInt KWriteBlockSize = 64; //-- "FlashWriteSize" entryconst TInt KFlashWriteTime = 406; //-- "FlashWriteTime" entry, default 406usconst 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 };#endifconst 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 _DEBUGpublic: enum TCtrlIoState {/*EIdle=0,ECtrlIoWaitWr=1,ECtrlIoWaitRd=2,ECtrlIoWrActive=3,*/ECtrlIoTimeOutPrep=4}; TInt ControlIO(TInt aCommand,TAny* aParam1,TAny* /*aParam2*/); TInt Request(TLocDrvRequest& m);#endifpublic: 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 _DEBUGpublic: 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; }#endifTInt 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); }