diff -r 000000000000 -r a41df078684a kernel/eka/drivers/locmedia/locmedia.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/kernel/eka/drivers/locmedia/locmedia.cpp Mon Oct 19 15:55:17 2009 +0100 @@ -0,0 +1,5109 @@ +// Copyright (c) 1998-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: +// e32\drivers\locmedia\locmedia.cpp +// +// + +#include "locmedia.h" +#include +#include "dmasupport.h" +#include + +#if defined(_DEBUG) && defined(__DEMAND_PAGING__) +//#define __DEBUG_DEMAND_PAGING__ +#endif + + +#if 0 +#define CHECK_RET(r) if ((r)==KErrNotSupported && (KDebugNum(KSCRATCH))) {NKern::Lock(); *(TInt*)0xfaece5=0;} +//#define CHECK_RET(r) +#else +#define CHECK_RET(r) +#endif + +_LIT(KLddName,"LocDrv"); +_LIT(KLitMediaDriverName, "Media.*"); +_LIT(KLitLocMedia,"LocMedia"); + +#define LOCM_FAULT() Kern::Fault("LOCMEDIA",__LINE__) + +const TInt KMaxLocalDriveCapsLength=256; +const TInt KMaxQueryDeviceLength=256; + +// The maximum amount of user-data which will be pinned. If a request is longer +// than this value it will be split up into a number of requests +// This value is a bit arbitrary - it needs to be sufficiently large so that transfer +// rates don't suffer too much - but it can't be too big or we'd be "stealing" too much +// memory from the demand paging pool and starving other processes +const TInt KMaxPinData = 256*1024; + +// The number of locks available for pinning shared by all the drive threads in the system. +// If all locks are in use then a single pre-allocated lock is used. +const TInt KDynamicPagingLockCount = 8; + +TLocDrv* TheDrives[KMaxLocalDrives]; +DMedia* TheMedia[KMaxLocalDrives]; +HBuf* DriveNames[KMaxLocalDrives]; +TInt UsedMedia=0; +TPasswordStore* ThePasswordStore=NULL; + +class DPrimaryMediaBase::DBody : public DBase + { +public: + TInt iPhysDevIndex; + TInt iRequestCount; +#ifdef __DEMAND_PAGING__ + DMediaPagingDevice* iPagingDevice; + TInt iPageSizeMsk; // Mask of page size (e.g. 4096-1 -> 4095) + TInt iMediaChanges; +#endif + }; + +#ifdef __DEMAND_PAGING__ +DMediaPagingDevice* ThePagingDevices[KMaxLocalDrives]; +DPrimaryMediaBase* TheRomPagingMedia = NULL; +DPrimaryMediaBase* TheDataPagingMedia = NULL; +TBool DataPagingDeviceRegistered = EFalse; +class DPinObjectAllocator; +DPinObjectAllocator* ThePinObjectAllocator = NULL; + +// The paging media might share a DfcQ with other non-paging media (e.g. 2 MMC/SD cards sharing the same stack) +// In this case, we need to avoid taking page faults on the non-paging media too, hence the need for these checks: +inline TBool DataPagingDfcQ(DPrimaryMediaBase* aPrimaryMedia) + {return TheDataPagingMedia && TheDataPagingMedia->iDfcQ == aPrimaryMedia->iDfcQ;} +inline TBool RomPagingDfcQ(DPrimaryMediaBase* aPrimaryMedia) + {return TheRomPagingMedia && TheRomPagingMedia->iDfcQ == aPrimaryMedia->iDfcQ;} + + + +/* +DPinObjectAllocator + +Internal class which contains : + (1) a queue of pre-allocated TVirtualPinObject's; + (2) a single pre-allocated DFragmentationPagingLock object: + this may be used if there are no TVirtualPinObject's available or if Kern::PinVirtualMemory() fails +*/ +NONSHARABLE_CLASS(DPinObjectAllocator) : public DBase + { +public: + /* + SVirtualPinContainer + Internal class encapsulating a TVirtualPinObject. + Contains a SDblQueLink so that it may form part of a SDblQue + */ + typedef struct + { + TVirtualPinObject* iObject; + SDblQueLink iLink; + } SVirtualPinContainer; + +public: + inline DPinObjectAllocator() {}; + ~DPinObjectAllocator(); + TInt Construct(TInt aObjectCount, TUint aNumPages); + + SVirtualPinContainer* AcquirePinObject(); + void ReleasePinObject(SVirtualPinContainer* aVirtualPinObject); + + inline DFragmentationPagingLock& PreAllocatedDataLock() {return *iPreAllocatedDataLock;} + +private: + // array of SVirtualPinContainer's + SVirtualPinContainer* iVirtualPinContainers; + TInt iObjectCount; + + // queues containing SVirtualPinContainer's + SDblQue iFreeQ; + + // pre-allocated (small) buffers for locking client data should Kern::PinVirtualMemory() fail + DFragmentationPagingLock* iPreAllocatedDataLock; + + // A mutex to protect access to the pinning objects. + NFastMutex iLock; + +public: + TUint iFragmentGranularity; + }; + + +DPinObjectAllocator::~DPinObjectAllocator() + { + if (iPreAllocatedDataLock) + { + iPreAllocatedDataLock->Cleanup(); + delete iPreAllocatedDataLock; + } + + for (TInt n=0; nConstruct(aNumPages); + if (r != KErrNone) + return r; + + + SVirtualPinContainer* iVirtualPinContainers = new SVirtualPinContainer[aObjectCount]; + if (iVirtualPinContainers == NULL) + return KErrNoMemory; + memclr(iVirtualPinContainers, sizeof(SVirtualPinContainer) * aObjectCount); + iObjectCount = aObjectCount; + + // construct the queue of dynamic paging locks + for (TInt n=0; nDeque(); + } + + + NKern::FMSignal(&iLock); + return pinContainer; + } + +/** +returns a SVirtualPinContainer object to the pool +*/ +void DPinObjectAllocator::ReleasePinObject(SVirtualPinContainer* aPinContainer) + { + NKern::FMWait(&iLock); + + iFreeQ.Add(&aPinContainer->iLink); + + NKern::FMSignal(&iLock); + } + +#endif // __DEMAND_PAGING__ + + +/******************************************** + * Local drive device base class + ********************************************/ +DECLARE_EXTENSION_LDD() + { + return new DLocalDriveFactory; + } + +DLocalDriveFactory::DLocalDriveFactory() +// +// Constructor +// + { + iParseMask=KDeviceAllowUnit|KDeviceAllowInfo; + iUnitsMask=~(0xffffffff<DLocalDrive::DoClose D:%d, M:%08x",iDrive->iDriveNumber,iDrive->iMedia)); + iDrive->Disconnect(this); + __KTRACE_OPT(KLOCDRV,Kern::Printf("iDriveNumber,iDrive->iMedia)); + } + DThread* pC=NULL; + NKern::LockSystem(); + if (iCleanup.iThread) + { + pC=iCleanup.iThread; + iCleanup.Remove(); + iCleanup.iThread=NULL; + } + NKern::UnlockSystem(); + if (pC) // original client may already have terminated + { + if (iNotifyChangeRequest) + Kern::QueueRequestComplete(pC,iNotifyChangeRequest,KErrCancel); + pC->Close(NULL); // balances Open() in DoCreate + } + if (iNotifyChangeRequest) + Kern::DestroyClientRequest(iNotifyChangeRequest); + } + +TInt DLocalDrive::DoCreate(TInt aUnit, const TDesC8* anInfo, const TVersion& aVer) + { + if(!Kern::CurrentThreadHasCapability(ECapabilityTCB,__PLATSEC_DIAGNOSTIC_STRING("Checked by ELOCD.LDD (Local Media Driver)"))) + return KErrPermissionDenied; + if (!Kern::QueryVersionSupported(TVersion(KLocalDriveMajorVersion,KLocalDriveMinorVersion,KLocalDriveBuildVersion),aVer)) + return KErrNotSupported; + + NKern::ThreadEnterCS(); + TInt r = Kern::CreateClientDataRequest(iNotifyChangeRequest); + NKern::ThreadLeaveCS(); + if (r != KErrNone) + return r; + + DThread& t=Kern::CurrentThread(); + NKern::LockSystem(); + t.AddCleanup(&iCleanup); + NKern::UnlockSystem(); + t.Open(); + iNotifyChangeRequest->SetDestPtr((TBool*) anInfo); + + iDrive=TheDrives[aUnit]; + if (!iDrive) + return KErrNotSupported; + __KTRACE_OPT(KLOCDRV,Kern::Printf("DLocalDrive Create - connect to drive %d, M:%08x",iDrive->iDriveNumber,iDrive->iMedia)); + r=iDrive->Connect(this); + __KTRACE_OPT(KLOCDRV,Kern::Printf("iDriveNumber,iDrive->iMedia,r)); + if (r!=KErrNone) + iDrive=NULL; // didn't connect so don't disconnect + return r; + } + +#if defined(_DEBUG) +void DebugDumpDriveCaps(const TLocDrv* aDrive, const TAny* aCaps) + { + const TLocalDriveCapsV5& c=*(const TLocalDriveCapsV5*)aCaps; + Kern::Printf("Drive %d Caps:", aDrive->iDriveNumber); + Kern::Printf("Size: %lx", c.iSize); + Kern::Printf("Type: %08x", c.iType); + Kern::Printf("Batt: %08x", c.iBattery); + Kern::Printf("DAtt: %08x", c.iDriveAtt); + Kern::Printf("MAtt: %08x", c.iMediaAtt); + Kern::Printf("Base: %08x", c.iBaseAddress); + Kern::Printf("FSID: %04x", c.iFileSystemId); + Kern::Printf("PTYP: %04x", c.iPartitionType); + Kern::Printf("HIDN: %08x", c.iHiddenSectors); + Kern::Printf("EBSZ: %08x", c.iEraseBlockSize); + //---------------- V5 ------------------// + if (c.iSerialNumLength != 0) + { + Kern::Printf("SN: length is %d", c.iSerialNumLength); + TBuf8<2*KMaxSerialNumLength+20> snBuf; + snBuf.Append(_L8("SN: content is ")); + for (TUint i=0; iDLocalDrive::DoControl D:%d M:%08x F:%d A1:%08x A2:%08x", + iDrive->iDriveNumber, iDrive->iMedia, aFunction, a1, a2)); + TInt r=KErrNotSupported; + TLocDrvRequest& m=TLocDrvRequest::Get(); + m.Flags()=0; + m.Drive()=iDrive; + switch (aFunction) + { + case RLocalDrive::EControlRead: + { + m.Id()=ERead; + r=m.ProcessMessageData(a1); + __TRACE_TIMING(1); + if (r==KErrNone) + { + __TRACE_TIMING(2); + r=iDrive->Request(m); + __TRACE_TIMING(3); + } + m.CloseRemoteThread(); + break; + } + case RLocalDrive::EControlWrite: + { + m.Id()=EWrite; + r=m.ProcessMessageData(a1); + if (r==KErrNone) + r=iDrive->Request(m); + m.CloseRemoteThread(); + break; + } + case RLocalDrive::EControlCaps: + { + TBuf8 capsBuf; + capsBuf.SetMax(); + capsBuf.FillZ(); + m.Id()=ECaps; + m.RemoteDes()=(TAny*)capsBuf.Ptr(); // overload this + m.Length()=KMaxLocalDriveCapsLength; // for pinning + r=iDrive->Request(m); + + if(r == KErrNone && iDrive->iMedia != NULL && iDrive->iMedia->iDriver != NULL) + { + // Fill in default media size if not specified by the driver + // + // - This uses the members of TLocalDriveCapsV4 which was primarily used + // to report NAND flash characteristics, but are general enough to be + // used to report the size of any type of media without adding yet + // another extension to TLocalDriveCapsVx. + // + + TLocalDriveCapsV4& caps = *(TLocalDriveCapsV4*)capsBuf.Ptr(); + + if(caps.iSectorSizeInBytes == 0) + { + // Fill in a default value for the disk sector size + caps.iSectorSizeInBytes = 512; + + // Zero the number of sectors, as a sector count makes no sense without a sector size + // - Fault in debug mode if a sector count is provided to ensure that media driver creators + // set this value,but in release mode continue gracefully be recalculating the sector count. + __ASSERT_DEBUG(caps.iNumberOfSectors == 0, LOCM_FAULT()); + caps.iNumberOfSectors = 0; + caps.iNumPagesPerBlock = 1; // ...to ensure compatiility with NAND semantics + } + + if(caps.iNumberOfSectors == 0) + { + const Int64 totalSizeInSectors = iDrive->iMedia->iDriver->TotalSizeInBytes() / caps.iSectorSizeInBytes; + __ASSERT_DEBUG(I64HIGH(totalSizeInSectors) == 0, LOCM_FAULT()); + + if(I64HIGH(totalSizeInSectors) == 0) + { + caps.iNumberOfSectors = I64LOW(totalSizeInSectors); + } + } + } + +#if defined(_DEBUG) + __KTRACE_OPT(KLOCDRV,DebugDumpDriveCaps(iDrive,capsBuf.Ptr())); +#endif + Kern::InfoCopy(*(TDes8*)a1, capsBuf); + break; + } + case RLocalDrive::EControlFormat: + { + m.Id()=EFormat; + r=m.ProcessMessageData(a1); + if (r==KErrNone) + r=iDrive->Request(m); + break; + } + case RLocalDrive::EControlEnlarge: + if ((TInt)a1<0) + { + r=KErrArgument; + break; + } + m.Length()=(TInt)a1; + m.Id()=EEnlarge; + r=iDrive->Request(m); + break; + case RLocalDrive::EControlReduce: + { + if ((TInt)a1<0 || (TInt)a2<0) + { + r=KErrArgument; + break; + } + m.Pos()=(TInt)a1; + m.Length()=(TInt)a2; + m.Id()=EReduce; + r=iDrive->Request(m); + break; + } + case RLocalDrive::EControlForceMediaChange: + m.Pos()=(TInt)a1; + m.Id()=EForceMediaChange; + r = iDrive->Request(m); + break; + case RLocalDrive::EControlMediaDevice: + r=iDrive->iPrimaryMedia->iDevice; + break; + case RLocalDrive::EControlIsRemovable: + { + TInt sockNum; + r=iDrive->iPrimaryMedia->IsRemovableDevice(sockNum); + if (r) + kumemput32(a1,&sockNum,sizeof(TInt)); + break; + } + case RLocalDrive::EControlControlIO: + { + TLocalDriveControlIOData d; + kumemget32(&d,a1,sizeof(d)); + + m.Id() = EControlIO; + m.iArg[0] = (TAny*) d.iCommand; + m.iArg[1] = d.iParam1; + m.iArg[2] = d.iParam2; + + // if d.iHandle is == KLocalMessageHandle (-1), + // d.aParam1 and d.aParam2 are TAny* pointers + // + // if d.iHandle is == 0, + // d.aParam1 and d.aParam2 are TInts + // + // if d.iHandle is > 0, + // d.aParam1 is a data pointer (TUint8*) + // d.aParam2 is an optional extra paramater (TInt) + // d.iHandle is a data length (TInt) + m.iArg[3] = (TAny*) d.iHandle; + + //We're highjacking fields representing + //length and position in a normal message, so + //let's not have the dispatcher function attempt + //to adjust for partition size. + m.Flags() |= TLocDrvRequest::EAdjusted; + + r=iDrive->Request(m); + break; + } + case RLocalDrive::EControlSetMountInfo: + { + m.Id()=ERead; + r=m.ProcessMessageData(a1); + DPrimaryMediaBase* pM=iDrive->iPrimaryMedia; + if(!pM || r!=KErrNone) + break; + + if (pM->iMountInfo.iThread) + { + NKern::ThreadEnterCS(); + //Close original thread + Kern::SafeClose((DObject*&) pM->iMountInfo.iThread,NULL); + if (m.RemoteDes()!=NULL) + { + //Set new mount info and leave setting thread open +#ifdef __DEMAND_PAGING__ + // lock the mount info if this is a data paging media - and keep it locked + if ((DataPagingDfcQ(pM)) && ((r = LockMountInfo(*pM, m)) != KErrNone)) + break; +#endif + pM->iMountInfo.iInfo=(TDesC8*)m.RemoteDes(); + pM->iMountInfo.iThread=m.RemoteThread(); + } + else + { + //Clear existing mount info and close setting thread + +#ifdef __DEMAND_PAGING__ + // unlock the mount info if this is a data paging media + UnlockMountInfo(*pM); +#endif + + pM->iMountInfo.iInfo=NULL; + pM->iMountInfo.iThread=NULL; + m.CloseRemoteThread(); + } + NKern::ThreadLeaveCS(); + r=KErrNone; + } + else + { + //Setting mount info for the first time + if (m.RemoteDes()==NULL) + { + // if no mount info, close setting thread opened in ProcessMessageData() + m.CloseRemoteThread(); + break; + } + + NKern::ThreadEnterCS(); +#ifdef __DEMAND_PAGING__ + // lock the mount info if this is a data paging media - and keep it locked + if ((DataPagingDfcQ(pM)) && ((r = LockMountInfo(*pM, m)) != KErrNone)) + break; +#endif + + pM->iMountInfo.iInfo=(TDesC8*)m.RemoteDes(); + pM->iMountInfo.iThread=m.RemoteThread(); + NKern::ThreadLeaveCS(); + r=KErrNone; + } + break; + } + case RLocalDrive::EControlPasswordLock: + { + m.Id()=EPasswordLock; + TLocalDrivePasswordData* ppd = (TLocalDrivePasswordData*)a1; + m.RemoteDes()=(TAny*)ppd; + r=iDrive->Request(m); + break; + } + case RLocalDrive::EControlPasswordUnlock: + { + m.Id()=EPasswordUnlock; + TLocalDrivePasswordData* ppd = (TLocalDrivePasswordData*)a1; + m.RemoteDes()=(TAny*)ppd; + r=iDrive->Request(m); + if(r == KErrNone) + iDrive->iPrimaryMedia->iTotalPartitionsOpened = 0; + break; + } + case RLocalDrive::EControlPasswordClear: + { + m.Id()=EPasswordClear; + TLocalDrivePasswordData* ppd = (TLocalDrivePasswordData*)a1; + m.RemoteDes()=(TAny*)ppd; + r=iDrive->Request(m); + break; + } + case RLocalDrive::EControlPasswordErase: + { + m.Id()=EPasswordErase; + r=iDrive->Request(m); + if(r == KErrNone) + iDrive->iPrimaryMedia->iTotalPartitionsOpened = 0; + break; + } + case RLocalDrive::EControlNotifyChange: + if (iCleanup.iThread != &Kern::CurrentThread()) + Kern::PanicCurrentThread(KLitLocMedia,KErrAccessDenied); + r=KErrNone; + if (!iNotifyChangeRequest->StatusPtr()) + r = iNotifyChangeRequest->SetStatus((TRequestStatus*) a1); + break; + case RLocalDrive::EControlNotifyChangeCancel: + if (iCleanup.iThread != &Kern::CurrentThread()) + Kern::PanicCurrentThread(KLitLocMedia,KErrAccessDenied); + Kern::QueueRequestComplete(iCleanup.iThread,iNotifyChangeRequest,KErrCancel); + break; + case RLocalDrive::EControlReadPasswordStore: + { + m.Id()=EReadPasswordStore; + m.RemoteDes()=(TDes8*)a1; + r=iDrive->Request(m); + break; + } + case RLocalDrive::EControlWritePasswordStore: + { + m.Id()=EWritePasswordStore; + m.RemoteDes()=(TDes8*)a1; + r=iDrive->Request(m); + if(r == KErrNone) + iDrive->iPrimaryMedia->iTotalPartitionsOpened = 0; + break; + } + case RLocalDrive::EControlPasswordStoreLengthInBytes: + { + m.Id()=EPasswordStoreLengthInBytes; + m.RemoteDes()=a1; + r=iDrive->Request(m); + break; + } + case RLocalDrive::EControlGetLastErrorInfo: + { + m.Id()=EGetLastErrorInfo; + m.iArg[0]=this; + TErrorInfoBuf errorInfoBuf; + errorInfoBuf.SetMax(); + errorInfoBuf.FillZ(); + m.RemoteDes()=(TAny*) errorInfoBuf.Ptr(); // overload this + m.Length() = errorInfoBuf.MaxLength(); + r=iDrive->Request(m); + Kern::InfoCopy(*(TDes8*)a1, errorInfoBuf); + break; + } + case RLocalDrive::EControlDeleteNotify: + { + m.Id()=EDeleteNotify; + r=m.ProcessMessageData(a1); + if (r==KErrNone) + r=iDrive->Request(m); + break; + } + + case RLocalDrive::EControlQueryDevice: + { + TBuf8 queryBuf; + queryBuf.SetMax(); + queryBuf.FillZ(); + + m.Id() = EQueryDevice; + m.iArg[0] = a1; // RLocalDrive::TQueryDevice + m.RemoteDes() = (TAny*)queryBuf.Ptr(); // overload this + m.Length() = KMaxLocalDriveCapsLength; // for pinning + r=iDrive->Request(m); + + Kern::InfoCopy(*(TDes8*)a2, queryBuf); + break; + } + + } + __KTRACE_OPT(KLOCDRV,Kern::Printf("iDriveNumber, iDrive->iMedia, r)); + __TRACE_TIMING(4); + return r; + } + +#ifdef __DEMAND_PAGING__ +TInt DLocalDrive::LockMountInfo(DPrimaryMediaBase& aPrimaryMedia, TLocDrvRequest& aReq) + { + DMediaPagingDevice* pagingDevice = aPrimaryMedia.iBody->iPagingDevice; + if (pagingDevice == NULL) + return KErrNone; + + __ASSERT_DEBUG(pagingDevice->iMountInfoDataLock == NULL, LOCM_FAULT()); + __ASSERT_DEBUG(pagingDevice->iMountInfoDescHdrLock == NULL, LOCM_FAULT()); + __ASSERT_DEBUG(pagingDevice->iMountInfoDescLenLock == NULL, LOCM_FAULT()); + + DThread* pT = aReq.RemoteThread(); + if (!pT) + pT = &Kern::CurrentThread(); // e.g. when using TBusLocalDrive directly + + TInt length = 0; + TInt maxLength = 0; + TUint8* desAddress = NULL; + TInt r = Kern::ThreadGetDesInfo(pT,aReq.RemoteDes(),length,maxLength,desAddress,EFalse); // get descriptor length, maxlength and desAddress + if (r != KErrNone) + return r; + if (length == 0) + return KErrNone; + + + static const TUint8 LengthLookup[16]={4,8,12,8,12,0,0,0,0,0,0,0,0,0,0,0}; + TUint32 desHdr; + r = Kern::ThreadRawRead(pT, aReq.RemoteDes(), &desHdr, sizeof(desHdr)); + if(r!=KErrNone) + return r; + TInt desType = desHdr >>KShiftDesType8; + TInt desHdrLen = LengthLookup[desType]; + if(!desHdrLen) + return KErrBadDescriptor; + + + pagingDevice->iMountInfoDataLock = ThePinObjectAllocator->AcquirePinObject(); + pagingDevice->iMountInfoDescHdrLock = ThePinObjectAllocator->AcquirePinObject(); + pagingDevice->iMountInfoDescLenLock = ThePinObjectAllocator->AcquirePinObject(); + + if (pagingDevice->iMountInfoDataLock == NULL || + pagingDevice->iMountInfoDescHdrLock == NULL || + pagingDevice->iMountInfoDescLenLock == NULL) + { + UnlockMountInfo(aPrimaryMedia); // tidy up + return KErrNoMemory; + } + + + // First pin the descriptor header + DPinObjectAllocator::SVirtualPinContainer* lock; + lock = (DPinObjectAllocator::SVirtualPinContainer*) pagingDevice->iMountInfoDescHdrLock; + r = Kern::PinVirtualMemory(lock->iObject, (TLinAddr) (TUint8*) aReq.RemoteDes(), desHdrLen, pT); + if (r != KErrNone) + { + UnlockMountInfo(aPrimaryMedia); // tidy up + return KErrNoMemory; + } + + + + // For EBufCPtr-type descriptors, need to pin the extra length before the buffer (!) + lock = (DPinObjectAllocator::SVirtualPinContainer*) pagingDevice->iMountInfoDescLenLock; + if (desType == EBufCPtr) + { + TLinAddr extraLenAddr = TLinAddr(desAddress) - aReq.RemoteDesOffset() - sizeof(TUint32); + r = Kern::PinVirtualMemory(lock->iObject, (TLinAddr) (TUint8*) extraLenAddr, sizeof(TUint32), pT); + if (r != KErrNone) + { + UnlockMountInfo(aPrimaryMedia); // tidy up + return KErrNoMemory; + } + } + + + // Now pin the descriptor contents + lock = (DPinObjectAllocator::SVirtualPinContainer*) pagingDevice->iMountInfoDataLock; + r = Kern::PinVirtualMemory(lock->iObject, (TLinAddr) desAddress, length, pT); + if (r != KErrNone) + { + UnlockMountInfo(aPrimaryMedia); // tidy up + return KErrNoMemory; + } + + + return KErrNone; + } + + +void DLocalDrive::UnlockMountInfo(DPrimaryMediaBase& aPrimaryMedia) + { + DMediaPagingDevice* pagingDevice = aPrimaryMedia.iBody->iPagingDevice; + if (pagingDevice == NULL || pagingDevice->iMountInfoDataLock == NULL) + return; + + + if (pagingDevice->iMountInfoDataLock) + { + Kern::UnpinVirtualMemory(((DPinObjectAllocator::SVirtualPinContainer*) pagingDevice->iMountInfoDataLock)->iObject); + ThePinObjectAllocator->ReleasePinObject((DPinObjectAllocator::SVirtualPinContainer*) pagingDevice->iMountInfoDataLock); + pagingDevice->iMountInfoDataLock = NULL; + } + + if (pagingDevice->iMountInfoDescHdrLock) + { + Kern::UnpinVirtualMemory(((DPinObjectAllocator::SVirtualPinContainer*) pagingDevice->iMountInfoDescHdrLock)->iObject); + ThePinObjectAllocator->ReleasePinObject((DPinObjectAllocator::SVirtualPinContainer*) pagingDevice->iMountInfoDescHdrLock); + pagingDevice->iMountInfoDescHdrLock = NULL; + } + + if (pagingDevice->iMountInfoDescLenLock) + { + Kern::UnpinVirtualMemory(((DPinObjectAllocator::SVirtualPinContainer*) pagingDevice->iMountInfoDescLenLock)->iObject); + ThePinObjectAllocator->ReleasePinObject((DPinObjectAllocator::SVirtualPinContainer*) pagingDevice->iMountInfoDescLenLock); + pagingDevice->iMountInfoDescLenLock = NULL; + } + + } +#endif // __DEMAND_PAGING__ + +void DLocalDrive::NotifyChange(DPrimaryMediaBase& aPrimaryMedia, TBool aMediaChange) + { +#ifndef __DEMAND_PAGING__ + aPrimaryMedia; +#endif + + // Complete any notification request on media change or power down + if (aMediaChange) + { + DThread* pC=NULL; + NKern::LockSystem(); + if (iCleanup.iThread) + { + pC=iCleanup.iThread; + pC->Open(); + } + NKern::UnlockSystem(); + if (pC) + { + TBool b = ETrue; + // if change not yet queued, queue it now + if (iNotifyChangeRequest->IsReady()) + { + *((TBool*) iNotifyChangeRequest->Buffer()) = b; + Kern::QueueRequestComplete(pC,iNotifyChangeRequest,KErrNone); + } + // If change has not even been requested by the client, maintain the pre-wdp behaviour + // and write data immediately back to client (possibly taking a page fault) + // N.B. Must NOT do this on data paging media +#ifdef __DEMAND_PAGING__ + else if (!DataPagingDfcQ(&aPrimaryMedia)) +#else + else +#endif + { + Kern::ThreadRawWrite(pC, iNotifyChangeRequest->DestPtr(), &b, sizeof(b), NULL); + } + pC->AsyncClose(); + } + } + } + +TLocalDriveCleanup::TLocalDriveCleanup() + { + } + +// This will be called when the original client thread exits +// It is called in the context of the exiting thread with the system locked. +void TLocalDriveCleanup::Cleanup() + { + DLocalDrive& d=LocalDrive(); + d.iNotifyChangeRequest=NULL; + DThread* pC=iThread; + Remove(); + iThread=NULL; + NKern::UnlockSystem(); + pC->Close(NULL); // balances Open() in DoCreate + NKern::LockSystem(); + } + +/******************************************** + * Local drive request class + ********************************************/ + +/** +Reads data from the descriptor specified in the request, from the requesting +thread's process. + +This is used by the media driver to read data from a descriptor in the +requesting thread. The remote data is copied into the specified descriptor, +starting at the specified offset within that descriptor's data area. + +@param aDes The target descriptor into which data from the remote thread + is to be put. +@param anOffset The offset within the target descriptor data area, where data + from the remote thread is to be put. Note that this parameter + may be useful when write operations to the media must be broken + up into smaller chunks than the length requested. + +@return KErrNone,if successful, otherwise one of the other + system-wide error codes. + +@see Kern::ThreadDesRead() +*/ +EXPORT_C TInt TLocDrvRequest::ReadRemote(TDes8* aDes, TInt anOffset) + { + DThread* pT=RemoteThread(); + if (!pT) + pT=Client(); + +#ifdef __DEMAND_PAGING__ // only if driver has its own thread, we don't support paging in MD which run in the context of their clients + if (Flags() & ETClientBuffer) + return Kern::ThreadBufRead(pT, (TClientBuffer*) RemoteDes(),*aDes,anOffset+RemoteDesOffset(),KChunkShiftBy0); + + __ASSERT_ALWAYS((Flags() & ETClientBuffer) == 0, LOCM_FAULT()); +#endif + + return Kern::ThreadDesRead(pT,RemoteDes(),*aDes,anOffset+RemoteDesOffset(),KChunkShiftBy0); + } + + + + +/** +Reads data from an arbitrary descriptor in the requesting thread's process. + +This is used by the media driver to read data from a descriptor in the +requesting thread. + +NB This is NOT supported in a datapaging environment as there is no guarantee +that the remote descriptor won't be paged out. If this function is called and +data-paging is enabled the kernel will fault in debug mode and return +KErrNotSupported in release mode. + +@param aSrc A pointer to the source descriptor in the requesting thread's + address space. +@param aDes The target descriptor into which data from the remote thread + is to be put. + +@return KErrNone,if successful, + KErrNotSupported if data-paging is enabled + otherwise one of the other system-wide error codes. + +@see Kern::ThreadDesRead() +*/ +EXPORT_C TInt TLocDrvRequest::ReadRemote(const TAny* aSrc, TDes8* aDes) + { + DThread* pT=RemoteThread(); + if (!pT) + pT=Client(); + +#ifdef __DEMAND_PAGING__ + __ASSERT_DEBUG(!DataPagingDeviceRegistered, LOCM_FAULT()); + if (DataPagingDeviceRegistered) + return KErrNotSupported; +#endif + + return Kern::ThreadDesRead(pT,aSrc,*aDes,0,KChunkShiftBy0); + } + + + + +/** +Reads raw data from the requesting thread's process. + +This is used by the media driver to read raw data from a location in requesting +thread's address space. The remote data is copied into the specified +buffer. + +@param aDest A pointer to the buffer where the data is to be written. +@param aSize The number of bytes to read. + +@return KErrNone,if successful, otherwise one of the other + system-wide error codes. + +@see Kern::ThreadRawRead() +*/ +EXPORT_C TInt TLocDrvRequest::ReadRemoteRaw(TAny* aDest, TInt aSize) + { + DThread* pT=RemoteThread(); + if (!pT) + pT=Client(); + +#ifdef __DEMAND_PAGING__ + __ASSERT_ALWAYS((Flags() & ETClientBuffer) == 0, LOCM_FAULT()); +#endif + + return Kern::ThreadRawRead(pT,RemoteDes(),aDest,aSize); + } + + +/** +Writes data to a descriptor in the requesting thread's process. + +This is used by the media driver to write data to a descriptor in the requesting +thread. Data is copied from the specified descriptor, starting at the specified +offset within that descriptor's data area. + +@param aDes The source descriptor from which data is to be written to + the remote thread. + +@param anOffset The offset within the source descriptor data area, from where data + is to be written to the remote thread. Note that this parameter + may be useful when read operations from the media must be broken + up into smaller chunks than the length requested. + +@return KErrNone,if successful, otherwise one of the other + system-wide error codes. + +@see Kern::ThreadDesWrite() +*/ +EXPORT_C TInt TLocDrvRequest::WriteRemote(const TDesC8* aDes, TInt anOffset) + { + DThread* pC=Client(); + DThread* pT=RemoteThread(); + if (!pT) + pT=pC; + +#ifdef __DEMAND_PAGING__ + if (Flags() & ETClientBuffer) + return Kern::ThreadBufWrite(pT, (TClientBuffer*) RemoteDes(),*aDes,anOffset+RemoteDesOffset(),KChunkShiftBy0,pC); +#endif + + return Kern::ThreadDesWrite(pT,RemoteDes(),*aDes,anOffset+RemoteDesOffset(),KChunkShiftBy0,pC); + } + + +/** +Writes raw data to the requesting thread's process. + +This is used by the media driver to write raw data to a location in the +requesting thread's address space. + +@param aSrc The source addres from which data is to be written to + the remote thread. + +@param aSize The number of bytes to write. + +@return KErrNone,if successful, otherwise one of the other + system-wide error codes. + +@see Kern::ThreadRawWrite() +*/ +EXPORT_C TInt TLocDrvRequest::WriteRemoteRaw(const TAny* aSrc, TInt aSize) + { + DThread* pC=Client(); + DThread* pT=RemoteThread(); + if (!pT) + pT=pC; + +#ifdef __DEMAND_PAGING__ + __ASSERT_ALWAYS((Flags() & ETClientBuffer) == 0, LOCM_FAULT()); +#endif + + return Kern::ThreadRawWrite(pT,RemoteDes(),aSrc,aSize,pC); + } + + +TInt TLocDrvRequest::ProcessMessageData(TAny* aPtr) +// +// Get read/write parameters from client and open remote thread +// + { + RemoteThread()=NULL; + DThread& t=Kern::CurrentThread(); + TLocalDriveMessageData d; + kumemget32(&d,aPtr,sizeof(d)); + if (d.iHandle!=KLocalMessageHandle && Id()!=DLocalDrive::EFormat) + { + NKern::LockSystem(); + DThread* pT = RMessageK::MessageK(d.iHandle)->iClient; + if (!pT || pT->Open()!=KErrNone) + { + NKern::UnlockSystem(); + return KErrBadHandle; + } + t.iExtTempObj=pT; + RemoteThread()=pT; + NKern::UnlockSystem(); + } + Pos()=d.iPos; + Length()=d.iLength; + RemoteDes()=(TAny*)d.iPtr; + RemoteDesOffset()=d.iOffset; + DriverFlags()=d.iFlags; + if (Pos()<0 || Length()<0) + return KErrArgument; + return KErrNone; + } + +void TLocDrvRequest::CloseRemoteThread() + { + if (!RemoteThread()) + return; + NKern::ThreadEnterCS(); + DThread& t=Kern::CurrentThread(); + RemoteThread()=NULL; + Kern::SafeClose((DObject*&)t.iExtTempObj,NULL); + NKern::ThreadLeaveCS(); + } + +EXPORT_C TInt TLocDrvRequest::CheckAndAdjustForPartition() + { + TLocDrv& d=*Drive(); + __KTRACE_OPT(KLOCDRV,Kern::Printf("CheckAndAdjustForPartition drive %d partition len %lx",d.iDriveNumber,d.iPartitionLen)); + Flags() |= EAdjusted; + switch (Id()) + { + case DLocalDrive::ECaps: + case DLocalDrive::EForceMediaChange: + case DLocalDrive::EPasswordLock: + case DLocalDrive::EPasswordUnlock: + case DLocalDrive::EPasswordClear: + case DLocalDrive::EPasswordErase: + case DLocalDrive::EReadPasswordStore: + case DLocalDrive::EWritePasswordStore: + case DLocalDrive::EPasswordStoreLengthInBytes: + case DLocalDrive::EQueryDevice: + return KErrNone; + case DLocalDrive::EEnlarge: + __KTRACE_OPT(KLOCDRV,Kern::Printf("Enlarge request %lx",Length())); + if (Length()>KMaxTInt) + return KErrArgument; + return KErrNone; + case DLocalDrive::EReduce: + __KTRACE_OPT(KLOCDRV,Kern::Printf("Reduce request %lx@%lx",Length(),Pos())); + if (Pos()+Length()>d.iPartitionLen) + return KErrArgument; + return KErrNone; + case DLocalDrive::EFormat: + __KTRACE_OPT(KLOCDRV,Kern::Printf("Format request %lx@%lx",Length(),Pos())); + if (!(DriverFlags() & RLocalDrive::ELocDrvWholeMedia)) + { + if (Pos()>d.iPartitionLen) + { + Length()=0; + return KErrEof; + } + Int64 left=d.iPartitionLen-Pos(); + if (leftd.iPartitionLen) + return KErrArgument; + Pos()+=d.iPartitionBaseAddr; + return KErrNone; +#endif + + default: // read or write or fragment + __KTRACE_OPT(KLOCDRV,Kern::Printf("R/W request %lx@%lx",Length(),Pos())); + + if (DriverFlags() & RLocalDrive::ELocDrvWholeMedia) + { + if (d.iMedia && d.iMedia->iDriver && Pos()+Length() > d.iMedia->iDriver->iTotalSizeInBytes) + return KErrArgument; + } + else + { + if (Pos()+Length() > d.iPartitionLen) + return KErrArgument; + Pos()+=d.iPartitionBaseAddr; + } + return KErrNone; + } + } + +/******************************************** + * Local drive class + ********************************************/ +TLocDrv::TLocDrv(TInt aDriveNumber) + { + memclr(this, sizeof(TLocDrv)); + iDriveNumber=aDriveNumber; + iPartitionNumber=-1; + } + +/** +Initialises the DMedia entity with the media device number and ID. + +@param aDevice The unique ID for this device. This can take one of the + enumerated values defined in TMediaDevice enum. + +@param aMediaId The unique ID to associate with this media entity. + +@return KErrNone,if successful, otherwise one of the other + system-wide error codes. + +@see TMediaDevice +*/ +EXPORT_C TInt DMedia::Create(TMediaDevice aDevice, TInt aMediaId, TInt) + { + __KTRACE_OPT(KLOCDRV,Kern::Printf("DMedia::Create media %d device %d",aMediaId,aDevice)); + iMediaId=aMediaId; + iDevice=aDevice; + return KErrNone; + } + +/******************************************** + * Primary Media Class + ********************************************/ +void asyncDfc(TAny* aPtr) + { + DPrimaryMediaBase* pM=(DPrimaryMediaBase*)aPtr; + if (pM->iState==DMedia::EOpening) + pM->DoOpenMediaDriverComplete(pM->iAsyncErrorCode); + else if (pM->iState==DMedia::EReadPartitionInfo) + pM->DoPartitionInfoComplete(pM->iAsyncErrorCode); + } + +void handleMsg(TAny* aPtr) + { + DPrimaryMediaBase* primaryMedia=(DPrimaryMediaBase*)aPtr; + + for(TLocDrvRequest* m = (TLocDrvRequest*) primaryMedia->iMsgQ.iMessage; + m != NULL; + m = (TLocDrvRequest*) primaryMedia->iMsgQ.Poll()) + { +#if defined(_DEBUG) + if (!primaryMedia->iMsgQ.iQ.IsEmpty()) + __KTRACE_OPT(KLOCDRV, Kern::Printf("TRACE: handleMsg, queue not empty %08X", m)); +#endif + primaryMedia->HandleMsg(*m); + +#ifdef __DEMAND_PAGING__ + // don't empty the queue if this media is paging as there + // may be a (higher-priority) paging DFC waiting to run... + if (primaryMedia->iPagingMedia) + break; +#endif + } + + + primaryMedia->iMsgQ.Receive(); // allow reception of more messages + } + +EXPORT_C DPrimaryMediaBase::DPrimaryMediaBase() + : iMsgQ(handleMsg, this, NULL, 1), + iDeferred(NULL, NULL, NULL, 0), // callback never used + iWaitMedChg(NULL, NULL, NULL, 0), // callback never used + iAsyncDfc(asyncDfc, this, 1) +/** +Constructor of DPrimaryMediaBase class. +Initialises the media state as closed. +*/ + { + iState = EClosed; + } + + + +EXPORT_C TInt DPrimaryMediaBase::Create(TMediaDevice aDevice, TInt aMediaId, TInt aLastMediaId) +/** +Called from LocDrv::RegisterMediaDevice() function. +Calls DMedia::Create() + +@param aDevice Local media ID +@param aMediaId Media Id (unique for a media subsystem) +@param aLastMediaId This indicates number of used media ids+ number of DMedia objects to be associated with the media driver. + +@return KErrNone +@see TMediaDevice + +*/ + { + __KTRACE_OPT(KLOCDRV,Kern::Printf("DPrimaryMediaBase::Create media %d-%d device %d",aMediaId,aLastMediaId,aDevice)); + TInt r=DMedia::Create(aDevice,aMediaId,0); + if (r != KErrNone) + return r; + iBody = new DBody; + if (iBody == NULL) + return KErrNoMemory; + +#ifdef __DEMAND_PAGING__ + TInt pageSize = Kern::RoundToPageSize(1); + iBody->iPageSizeMsk = pageSize-1; +#endif + + iLastMediaId=aLastMediaId; + if (r==KErrNone && iDfcQ) + { + iMsgQ.SetDfcQ(iDfcQ); + iDeferred.SetDfcQ(iDfcQ); + iWaitMedChg.SetDfcQ(iDfcQ); + iAsyncDfc.SetDfcQ(iDfcQ); + } + return KErrNone; + } + + +EXPORT_C TInt DPrimaryMediaBase::Connect(DLocalDrive* aLocalDrive) +/** +Connects to a local drive + +@param aLocalDrive Local drive logical channel abstraction + +@pre Kernel must be unlocked +@pre Current thread in critical section + +@post Kernel must be unlocked + +@return KErrNone, if successful + KErrNotFound, If no PDD matches criteria while getting driver list + KErrNoMemory, If the array could not be expanded at some point while getting driver list or ran out of memory while opening media driver + KErrNotReady, If not ready when trying to open media driver + otherwise, one of the other system wide error codes. + +@see DLocalDrive +*/ + { + __KTRACE_OPT(KLOCDRV,Kern::Printf("DPrimaryMediaBase(%d)::Connect %O",iMediaId,aLocalDrive)); + if (iDfcQ) + { + TThreadMessage& m=Kern::Message(); + m.iValue=EConnect; + m.iArg[0]=aLocalDrive; + return m.SendReceive(&iMsgQ); + } + + // If no DFC queue, must be a fixed media device + // If this is the first connection, open media driver now + // Assume no non-primary media exist on this device + TInt r=KErrNone; + NKern::LockSystem(); + TBool first=iConnectionQ.IsEmpty(); + iConnectionQ.Add(&aLocalDrive->iLink); + NKern::UnlockSystem(); + if (first) + { + r=OpenMediaDriver(); + if (r!=KErrNone) + { + NKern::LockSystem(); + aLocalDrive->Deque(); + NKern::UnlockSystem(); + } + } + if (r==KErrNone) + aLocalDrive->iDrive->iMedia=this; + return r; + } + + + + +EXPORT_C void DPrimaryMediaBase::Disconnect(DLocalDrive* aLocalDrive) +/** +Disconnects from a local drive + +@param aLocalDrive Local drive logical channel abstraction + +@pre Kernel must be unlocked +@pre Current thread in critical section + +@post Kernel must be unlocked +@see DLocalDrive +*/ + + { + __KTRACE_OPT(KLOCDRV,Kern::Printf("DPrimaryMediaBase(%d)::Disconnect %O",iMediaId,aLocalDrive)); + if (iDfcQ) + { + TThreadMessage& m=Kern::Message(); + m.iValue=EDisconnect; + m.iArg[0]=aLocalDrive; + m.SendReceive(&iMsgQ); + return; + } + + // If no DFC queue, must be a fixed media device + // If this is the last connection, close media driver now + // Assume no non-primary media exist on this device + DMediaDriver* pD=NULL; + NKern::LockSystem(); + aLocalDrive->iDrive->iMedia=NULL; + aLocalDrive->Deque(); + if (iConnectionQ.IsEmpty()) + { + pD=iDriver; + iDriver=NULL; + } + NKern::UnlockSystem(); + if (pD) + pD->Close(); + } + +EXPORT_C TInt DPrimaryMediaBase::Request(TLocDrvRequest& aReq) +/** +Issues a local drive request. It is called from TLocDrv::Request() function . +Each local drive request is encapsulated as a TLocDrvRequest- a class derived from TThreadMessage, the kernel message class. +TLocDrvRequest contains information pertaining to the request, including the ID and any associated parameters such as drive position, length and source/destination location. +Passes the request through to the media driver. + +@param m Encapsulates the request information received from the client + +@pre Enter with kernel unlocked + +@post Leave with Kernel unlocked + +@return KErrNone,if successful + KErrBadDescriptor, if request encapsulates a bad descriptor + Otherwise, one of the other system wide error codes. + +@see TLocDrvRequest +*/ + { + + __KTRACE_OPT(KLOCDRV,Kern::Printf("DPrimaryMediaBase(%d)::Request(%08x)",iMediaId,&aReq)); + __KTRACE_OPT(KLOCDRV,Kern::Printf("this=%x, ReqId=%d, Pos=%lx, Len=%lx, remote thread %O",this,aReq.Id(),aReq.Pos(),aReq.Length(),aReq.RemoteThread())); + + TInt reqId = aReq.Id(); + + if (reqId == DLocalDrive::ECaps) + DefaultDriveCaps(*(TLocalDriveCapsV2*)aReq.RemoteDes()); // fill in stuff we know even if no media present + + TInt r = QuickCheckStatus(); + if (r != KErrNone && aReq.Id()!=DLocalDrive::EForceMediaChange && // EForceMediaChange, and + aReq.Id()!=DLocalDrive::EReadPasswordStore && // Password store operations + aReq.Id()!=DLocalDrive::EWritePasswordStore && // do not require the media + aReq.Id()!=DLocalDrive::EPasswordStoreLengthInBytes) // to be ready.) + { + return r; + } + + + // for ERead & EWrite requests, get the linear address for pinning & DMA + TUint8* linAddress = NULL; + TClientBuffer clientBuffer; + DThread* pT = NULL; + + if (reqId == DLocalDrive::ERead || reqId == DLocalDrive::EWrite) + { + pT = aReq.RemoteThread(); + if (!pT) + pT = &Kern::CurrentThread(); // e.g. when using TBusLocalDrive directly + + // for silly zero-length requests, return immediately, setting the client + // descriptor length to zero if it's a read request + if (aReq.Length() == 0) + { + DThread* pC = &Kern::CurrentThread(); + r = KErrNone; + if (reqId == DLocalDrive::ERead) + { + TPtrC8 ptr(NULL, 0); + r = Kern::ThreadDesWrite(pT, aReq.RemoteDes(), ptr, aReq.RemoteDesOffset(), KChunkShiftBy0,pC); + } + return r; + } + + clientBuffer.SetFromDescriptor(aReq.RemoteDes(), pT); + + TInt length = 0; + TInt maxLength = 0; + TInt r = Kern::ThreadGetDesInfo(pT,aReq.RemoteDes(),length,maxLength,linAddress,EFalse); // get descriptor length, maxlength and linAddress + if (r != KErrNone) + return r; + linAddress+= aReq.RemoteDesOffset(); + +#ifdef __DEMAND_PAGING__ + // NB change in behavior IF DATA PAGING IS ENABLED: TLocDrvRequest::RemoteDes() points + // to a TClientBuffer rather than the client's remote descriptor + if (DataPagingDeviceRegistered) + { + aReq.RemoteDes() = &clientBuffer; + aReq.Flags() |= TLocDrvRequest::ETClientBuffer; + } +#endif + } + + if (iDfcQ) + { + __TRACE_TIMING(0x10); + + +#ifdef __DEMAND_PAGING__ + // If this is a ROM/Code paging media, pin writes + // If there is a Data paging media registered, pin all requests with descriptors + if ( (DataPagingDeviceRegistered) || (reqId == DLocalDrive::EWrite && RomPagingDfcQ(this)) ) + r = PinSendReceive(aReq, (TLinAddr) linAddress); + else +#endif // __DEMAND_PAGING__ + + r = SendReceive(aReq, (TLinAddr) linAddress); + } + else + { + // If no DFC queue, must be a fixed media device + // Media driver must already have been opened + // Assume no non-primary media exist on this device + // Just pass request straight through to media driver + r = aReq.CheckAndAdjustForPartition(); + if (r == KErrNone) + r = iDriver->Request(aReq); + } + +#ifdef __DEMAND_PAGING__ + // NB change in behavior IF DATA PAGING IS ENABLED: TLocDrvRequest::RemoteDes() points + // to a TClientBuffer rather than the client's remote descriptor + if (reqId == DLocalDrive::ERead && DataPagingDeviceRegistered && r == KErrNone) + { + r = clientBuffer.UpdateDescriptorLength(pT); + } +#endif + + return r; + } + + +#ifdef __DEMAND_PAGING__ +TInt DPrimaryMediaBase::PinSendReceive(TLocDrvRequest& aReq, TLinAddr aLinAddress) + { + __ASSERT_DEBUG(ThePinObjectAllocator, LOCM_FAULT()); + + + TInt msgId = aReq.Id(); + + + switch(msgId) + { + case DLocalDrive::EControlIO: + { + TInt controlIoType = aReq.Int3(); + switch(controlIoType) + { + case KLocalMessageHandle: + // ControlIo is not supported if either of the two bare (TAny*) pointers are non-NULL + // as it's not possible to determine what the pointers are pointing at... + if (aReq.Int1() || aReq.Int2()) + { + __KTRACE_OPT(KDATAPAGEWARN, Kern::Printf("Data paging: Naked EControlIO not supported on paging device: fn=%x", aReq.Int0())); + return KErrNotSupported; + } + // fall into... + case 0: + return SendReceive(aReq); + + default: + // if Int3() is > 0, Int1() is a data pointer, and Int3() is a length + if (controlIoType > (TInt) ThePinObjectAllocator->iFragmentGranularity) + return KErrTooBig; + if (controlIoType < 0) + return KErrArgument; + return PinFragmentSendReceive(aReq, (TLinAddr) aReq.Ptr1(), controlIoType); + } + } + + case DLocalDrive::ECaps: + case DLocalDrive::EGetLastErrorInfo: + case DLocalDrive::EQueryDevice: + { + TInt len = aReq.Length(); + + if (len > (TInt) ThePinObjectAllocator->iFragmentGranularity) + return KErrTooBig; + + return PinFragmentSendReceive(aReq, (TLinAddr) aReq.RemoteDes(), len); + } + + case DLocalDrive::ERead: + case DLocalDrive::EWrite: + { + return PinFragmentSendReceive(aReq, aLinAddress, aReq.Length()); + } + + + + // For the time being, don't support any password requests to the data paging device. + // This shouldn't be a problem as the device should be flagged as non-removable... + // This would be difficult to do anyway as it would involve pinning up to 3 buffers - + // TLocalDrivePasswordData itself, iOldPasswd & iNewPasswd + case DLocalDrive::EPasswordLock: + case DLocalDrive::EPasswordUnlock: + case DLocalDrive::EPasswordClear: + case DLocalDrive::EReadPasswordStore: + case DLocalDrive::EWritePasswordStore: + case DLocalDrive::EPasswordStoreLengthInBytes: + case DLocalDrive::EPasswordErase: + return KErrNotSupported; + + default: + return SendReceive(aReq); + } + } + +TInt DPrimaryMediaBase::PinFragmentSendReceive(TLocDrvRequest& aReq, TLinAddr aLinAddress, TInt aLength) + { + TLocDrvRequest fragment = aReq; // create a request on the stack for use during fragmentation, pre-fill with the original req args, leave original Kernel message as repository (thread will block, message contents won't change) + TInt r = KErrNone; + +// Kern::Printf(">PFSR %02X aReq %08X aLinAddress %08X aLen %08X offset %08X", aReq.Id(), &aReq, aLinAddress, aLength, aReq.RemoteDesOffset()); + + DThread* pT = aReq.RemoteThread(); + if (!pT) + pT=&Kern::CurrentThread(); // e.g. when using TBusLocalDrive directly + + __KTRACE_OPT2(KLOCDPAGING,KLOCDRV,Kern::Printf("Fragmenting Read/Write Request(0x%08x) on drive(%d), remote des(0x%x), offset into des(0x%x), original req Length(0x%x)",&aReq,aReq.Drive()->iDriveNumber,(TInt)(aReq.RemoteDes()),aReq.RemoteDesOffset(),aLength)); + __KTRACE_OPT(KLOCDPAGING,Kern::Printf("Remote thread(0x%08x), current thread(0x%08x), start of data to write(0x%08x)",aReq.RemoteThread(),&Kern::CurrentThread(),(TInt)aLinAddress)); + + // don't want this thread to be terminated until last fragment is sent to MD and mem can be free'd up + NKern::ThreadEnterCS(); + + __ASSERT_DEBUG(ThePinObjectAllocator, LOCM_FAULT()); + + TUint fragmentGranularity = ThePinObjectAllocator->iFragmentGranularity; + TInt dataLockResult = 0; + // fragmentation only allowed for read/write requests + __ASSERT_DEBUG(aLength <= (TInt) fragmentGranularity || (aReq.Id() == DLocalDrive::EWrite || aReq.Id() == DLocalDrive::ERead), LOCM_FAULT()); + + // Pin the client buffer + TInt pinnedLen; + for (TInt pos = 0; pos < aLength; pos+= pinnedLen, aLinAddress+= pinnedLen) + { + pinnedLen = 0; + + // pin memory + TInt remainingLen = aLength - pos; // remaining length + + // first attempt to pin memory with no pre-allocated buffers (which may fail) + DPinObjectAllocator::SVirtualPinContainer* pinDataObject = ThePinObjectAllocator->AcquirePinObject(); + + if (pinDataObject) + { + TInt lenToPin = Min(KMaxPinData, remainingLen); + + TInt r = Kern::PinVirtualMemory(pinDataObject->iObject, aLinAddress, lenToPin, pT); + if (r == KErrNone) + { + pinnedLen = lenToPin; + } + else + { +#ifdef __DEBUG_DEMAND_PAGING__ + Kern::Printf("Kern::PinVirtualMemory() error %d", r); +#endif + // pin failed, so use preallocated buffer instead + ThePinObjectAllocator->ReleasePinObject(pinDataObject); + pinDataObject = NULL; + } + } + + if (!pinDataObject) + { + ThePinObjectAllocator->PreAllocatedDataLock().LockFragmentation(); + + TLinAddr start = aLinAddress; + do + { + TInt lenToPin = Min((TInt) fragmentGranularity, remainingLen - pinnedLen); + +#ifdef __DEBUG_DEMAND_PAGING__ + Kern::Printf(">SR PinS Id %d aLinAddress %08X lenToPin %08X offset %08X", aReq.Id(), aLinAddress, lenToPin); +#endif + + dataLockResult = ThePinObjectAllocator->PreAllocatedDataLock().Lock(pT, start, lenToPin); + +#ifdef __DEBUG_DEMAND_PAGING__ + Kern::Printf(" 0) release it after calling SendReceive() + if (dataLockResult <= 0) + ThePinObjectAllocator->PreAllocatedDataLock().UnlockFragmentation(); + +#ifdef __DEBUG_DEMAND_PAGING__ + if (dataLockResult < 0) + Kern::Printf("DFragmentationPagingLock::Lock() %d", dataLockResult); +#endif + + if (dataLockResult < 0) // if lock returned an error then give up + { + r = dataLockResult; + break; + } + } + + // fragment request Id defaults to same as original request + fragment.Id() = aReq.Id(); + fragment.Length() = Int64(pinnedLen); + fragment.RemoteDesOffset() = aReq.RemoteDesOffset() + pos; + fragment.Pos() = aReq.Pos() + pos; + fragment.Flags() = aReq.Flags(); + + __KTRACE_OPT2(KLOCDPAGING,KLOCDRV,Kern::Printf("Send fragment (0x%08x) type(%d), length(0x%x), offset within original req(0x%x), pos in media(0x%lx)",&fragment,fragment.Id(), pinnedLen, pos, fragment.Pos())); + +#ifdef BTRACE_PAGING_MEDIA + TInt buf[4]; + buf[0] = pinnedLen; // fragment length + buf[1] = pos; // offset within original request + buf[2] = fragment.Pos(); // offset in media + buf[3] = (TInt)&pT->iNThread; // thread that issued the original write req + BTraceContextN(BTrace::EPagingMedia,BTrace::EPagingMediaLocMedFragmentBegin,&fragment,fragment.Id(),buf,sizeof(buf)); +#endif + + r = SendReceive(fragment, aLinAddress); // only come back here when message (fragment) has been completed + + // unpin memory + if (pinDataObject) + { + Kern::UnpinVirtualMemory(pinDataObject->iObject); + ThePinObjectAllocator->ReleasePinObject(pinDataObject); + } + else if (dataLockResult > 0) // pinDataObject = NULL + { + __ASSERT_DEBUG(dataLockResult == 1, LOCM_FAULT()); + ThePinObjectAllocator->PreAllocatedDataLock().Unlock(); + ThePinObjectAllocator->PreAllocatedDataLock().UnlockFragmentation(); + } + +#ifdef BTRACE_PAGING_MEDIA + BTraceContext8(BTrace::EPagingMedia,BTrace::EPagingMediaLocMedFragmentEnd,&fragment,r); +#endif + + if (r != KErrNone) + break; + } + + NKern::ThreadLeaveCS(); + +// Kern::Printf("iDmaHelper; + +#ifdef __DEMAND_PAGING__ + RequestCountInc(); +#endif + + TInt r; + + if (dmaHelper) + r = dmaHelper->SendReceive(aReq, aLinAddress); + else + r = aReq.SendReceive(&iMsgQ); + +#ifdef __DEMAND_PAGING__ + RequestCountDec(); +#endif + + return r; + } + + + +EXPORT_C TInt DPrimaryMediaBase::ForceMediaChange(TInt) +/** +Forces a media change.The method can be overridden in the derived classes. +@param mode Media change mode + +@return KErrNotSupported, in the default implementation + KErrNone, if successful + Otherwise, one of the other system wide error codes. + +*/ + { + // default implementation + return KErrNotSupported; + } + +EXPORT_C TInt DPrimaryMediaBase::InitiatePowerUp() +/** +Initiates Power up sequence +@return KErrCompletion, operation is complete successfully or otherwise + KErrNone, if successful + Otherwise, one of the other system wide error codes. + +*/ + { + // default implementation, this is the default implementation. + return KErrCompletion; + } + +EXPORT_C TInt DPrimaryMediaBase::QuickCheckStatus() +/** +Checks the status of the media device, whether the device is present,absent,not ready,etc. +The function can be overridden in the derived classes + +@return KErrNone, if successful + Otherwise, one of the other system wide error codes. + +*/ + { + // default implementation + return KErrNone; + } + +EXPORT_C void DPrimaryMediaBase::DefaultDriveCaps(TLocalDriveCapsV2& aCaps) +/** +Fills in the default drive capabilities in TLocalDriveCapsV2 . +It initializes media type of drive as unknown and has to be overridden in the derived class. Called from the Request ( ) function of the same class. + +@param aCaps Media drive capability fields. Extension to Capabilities fields(i.e) in addition to TLocalDriveCaps mainly to support Nor flash +@see TLocalDriveCapsV2 +*/ + + { + // default implementation + // aCaps is zeroed beforehand + aCaps.iType = EMediaUnknown; + // aCaps.iBattery = EBatNotSupported; + } + +EXPORT_C TBool DPrimaryMediaBase::IsRemovableDevice(TInt& /*aSocketNum*/) +/** +Checks whether it is a removable device or not +@param aSocketNum Socket number +@return ETrue=Removable Device + EFalse=Non-Removable device, default implementation + +*/ + { + // default implementation + return(EFalse); + } + +EXPORT_C void DPrimaryMediaBase::HandleMsg(TLocDrvRequest& m) +/** +It handles the drive request encapsulated in TLocDrvRequest depending on the message id. + +@param aRequest Encapsulates the request information received from the client +@see TLocDrvRequest +*/ + { + switch (m.iValue) + { + case EConnect: + { + DLocalDrive* pD=(DLocalDrive*)m.Ptr0(); + iConnectionQ.Add(&pD->iLink); + m.Complete(KErrNone, EFalse); + return; + } + case EDisconnect: + { + DLocalDrive* pD=(DLocalDrive*)m.Ptr0(); + TLocDrv* pL=pD->iDrive; + DMedia* media=pL->iMedia; + if (iState==EReady && media && media->iDriver) + media->iDriver->Disconnect(pD,&m); + else + { + pD->Deque(); + m.Complete(KErrNone, EFalse); + } + return; + } + case DLocalDrive::EForceMediaChange: + { + TUint flags = (TUint) m.Pos(); + + // if KForceMediaChangeReOpenDriver specified wait for power up, + // and then re-open this drive's media driver + __KTRACE_OPT(KLOCDRV, Kern::Printf("EForceMediaChange, flags %08X\n", flags)); + if (flags == (TUint) KForceMediaChangeReOpenMediaDriver) + { + TInt sock; + if (!IsRemovableDevice(sock)) + { + CompleteRequest(m, KErrNotSupported); + return; + } + // wait for power up and then call DPrimaryMediaBase::DoRequest() + break; + } + + TInt r=ForceMediaChange(flags); + if (r==KErrNone) + { + // wait for media change notification to complete message + m.Forward(&iWaitMedChg,EFalse); + } + else + { + if (r==KErrNotSupported || r==KErrCompletion) + r=KErrNone; + CompleteRequest(m, r); + } + return; + } + case DLocalDrive::ECaps: + if (iState==EPoweredDown) + { + // The media is powered down, but the media driver still exists. + // - Issue the ECaps request without powering the media back up. + DoRequest(m); + __TRACE_TIMING(0x101); + return; + } + break; + + case DLocalDrive::ERead: + case DLocalDrive::EWrite: + case DLocalDrive::EFormat: + case DLocalDrive::EEnlarge: + case DLocalDrive::EReduce: + case DLocalDrive::EPasswordLock: + case DLocalDrive::EPasswordUnlock: + case DLocalDrive::EPasswordClear: + case DLocalDrive::EPasswordErase: + case DLocalDrive::EControlIO: + case DLocalDrive::EDeleteNotify: + case DLocalDrive::EQueryDevice: + +#ifdef __DEMAND_PAGING__ + case DMediaPagingDevice::ERomPageInRequest: + case DMediaPagingDevice::ECodePageInRequest: +#endif + break; + case DLocalDrive::EGetLastErrorInfo: + { + DLocalDrive* pD=(DLocalDrive*)m.Ptr0(); + TLocDrv* pL=pD->iDrive; + *((TErrorInfo*) m.RemoteDes()) = pL->iLastErrorInfo; + CompleteRequest(m, KErrNone); + return; + } + case DLocalDrive::EReadPasswordStore: + { + TUint8 passData[TPasswordStore::EMaxPasswordLength]; + TPtr8 pData(passData, TPasswordStore::EMaxPasswordLength); + TInt r = ThePasswordStore->ReadPasswordData(pData); + if (r==KErrNone) + r = m.WriteRemote(&pData,0); + CompleteRequest(m, r); + return; + } + case DLocalDrive::EWritePasswordStore: + { + TUint8 passData[TPasswordStore::EMaxPasswordLength]; + TPtr8 pData(passData, TPasswordStore::EMaxPasswordLength); + + DThread* pT=m.RemoteThread(); + if (!pT) + pT=m.Client(); + + TInt lengthOrError = Kern::ThreadGetDesLength(pT, m.RemoteDes() ); + if ( lengthOrError > pData.MaxLength() ) + { + CompleteRequest(m, KErrOverflow); + return; + } + else if ( lengthOrError < KErrNone) + { + CompleteRequest(m, lengthOrError); + return; + } + + TInt r = m.ReadRemote(&pData,0); + if (r==KErrNone) + r = ThePasswordStore->WritePasswordData(pData); + if(r != KErrNone) + { + CompleteRequest(m, r); + return; + } + + r = QuickCheckStatus(); + if(r != KErrNone) + { + // Don't try to power up the device if it's not ready. + // - Note that this isn't an error that needs to be returned to the client. + CompleteRequest(m, KErrNone); + return; + } + + break; + } + case DLocalDrive::EPasswordStoreLengthInBytes: + { + TInt length = ThePasswordStore->PasswordStoreLengthInBytes(); + TInt r = m.WriteRemoteRaw(&length,sizeof(TInt)); + CompleteRequest(m, r); + return; + } + default: + CHECK_RET(KErrNotSupported); + CompleteRequest(m, KErrNotSupported); + return; + } + + __KTRACE_OPT(KFAIL,Kern::Printf("mdrq %d",m.Id())); + __KTRACE_OPT(KLOCDRV,Kern::Printf("DPrimaryMediaBase(%d)::HandleMsg state %d req %d",iMediaId,iState,m.Id())); + + // if media driver already open, pass request through + if (iState==EReady) + { + DoRequest(m); + __TRACE_TIMING(0x101); + return; + } + + // if open or close in progress, defer this message + if (iState!=EClosed && iState!=EPoweredDown) + { +#ifdef __DEMAND_PAGING__ + if (DMediaPagingDevice::PagingRequest(m)) + { + __ASSERT_ALWAYS(iPagingMedia,LOCM_FAULT()); + __ASSERT_DEBUG(iBody->iPagingDevice,LOCM_FAULT()); + __ASSERT_ALWAYS( ((m.Flags() & TLocDrvRequest::ECodePaging) == 0) || (m.Drive()->iPagingDrv), LOCM_FAULT()); + + __KTRACE_OPT2(KLOCDPAGING,KLOCDRV,Kern::Printf("Deferring PageIn request 0x%08x because opening or closing",&m)); + iBody->iPagingDevice->SendToDeferredQ(&m); + } + else +#endif + m.Forward(&iDeferred,EFalse); + return; + } + + // nothing is open, so try to open something + __ASSERT_ALWAYS(!iCurrentReq,LOCM_FAULT()); + +#ifdef __DEMAND_PAGING__ + +#ifdef BTRACE_PAGING_MEDIA + if (DMediaPagingDevice::PagingRequest(m)) + BTraceContext12(BTrace::EPagingMedia,BTrace::EPagingMediaLocMedPageInQuietlyDeferred,&m,iState,m.iValue); +#endif // BTRACE_PAGING_MEDIA + +#ifdef _DEBUG + __ASSERT_ALWAYS( ((m.Flags() & TLocDrvRequest::ECodePaging) == 0) || (m.Drive()->iPagingDrv), LOCM_FAULT()); + + if (DMediaPagingDevice::PagingRequest(m)) + { + __ASSERT_DEBUG(iPagingMedia,LOCM_FAULT()); + __ASSERT_DEBUG(iBody->iPagingDevice,LOCM_FAULT()); + __KTRACE_OPT(KLOCDPAGING,Kern::Printf("Page request 0x%08x received -> opening MD",&m)); + } +#endif // _DEBUG + +#endif // __DEMAND_PAGING__ + + iCurrentReq=&m; + if(iState == EClosed) + { + iState=EPoweringUp1; + } + else if (iState == EPoweredDown) + { + iState=EPoweringUp2; + } + + TInt r=InitiatePowerUp(); + if (r==KErrNone || r==KErrServerBusy) + { + // wait for completion of power up request + return; + } + if (r==KErrCompletion) + r=KErrNone; // device already powered up + PowerUpComplete(r); + } + +EXPORT_C TInt DPrimaryMediaBase::DoRequest(TLocDrvRequest& m) +/** +If the media exists, it tries to get the partition information if not there. +It then passes on the request to the media driver by calling its Request( ) function. +Then it completes the kernel thread message and the reference count of the thread is closed asynchronously. + +@param aRequest Encapsulates the request information received from the client + +@return KErrNone, if successful + KErrNotReady, if missing partitions on removable media + KErrNotSupported, if missing partitions on fixed media + KErrArgument Out of range argument ,encapsulated in Local drive request , passed while checking and adjusting for partition + KErrEOF, Reached the end of file + KErrBadDescriptor, if request encapsulates a bad descriptor + Otherwise, one of the other system wide error codes. + +*/ + { + __KTRACE_OPT2(KLOCDRV,KLOCDPAGING,Kern::Printf("DPrimaryMediaBase::DoRequest %d",m.Id())); + TLocDrv* pL=m.Drive(); + DMedia* media=pL->iMedia; + TInt r=KErrNone; + + // re-open this drive's media driver ? + if (m.iValue == DLocalDrive::EForceMediaChange) + { + __ASSERT_DEBUG(((TUint) m.Pos()) == (TUint) KForceMediaChangeReOpenMediaDriver, LOCM_FAULT()); + + iCurrentReq=NULL; + + TLocDrv* pL = m.Drive(); + DMedia* media = pL->iMedia; + if (media && media->iDriver) + CloseMediaDrivers(media); + + iState=EOpening; + StartOpenMediaDrivers(); + + NotifyClients(ETrue,pL); + CompleteRequest(m, r); + return r; + } + + if (!media || !media->iDriver || iState == EClosed) + { + // Return KErrNotReady for missing partitions on removable media + // as opposed to KErrNotSupported for missing partitions on fixed media + // since the latter don't exist whereas the former might exist at some time. + TInt sock; + r=IsRemovableDevice(sock) ? KErrNotReady : KErrNotSupported; + } + + iCurrentReq=&m; + if (r==KErrNone) + { + if(iTotalPartitionsOpened == 0) + { + UpdatePartitionInfo(); + return KErrNone; + } + if (!(m.Flags() & TLocDrvRequest::EAdjusted)) + { + // If this isn't the only partition, don't allow access to the whole media + if (iTotalPartitionsOpened > 1) + m.DriverFlags() &= ~RLocalDrive::ELocDrvWholeMedia; + r=m.CheckAndAdjustForPartition(); + } + if (r==KErrNone) + { + r=media->iDriver->Request(m); + if (r>0) + { + // defer request +#ifdef __DEMAND_PAGING__ + if (DMediaPagingDevice::PagingRequest(m)) + { + __ASSERT_ALWAYS(iPagingMedia,LOCM_FAULT()); + __ASSERT_ALWAYS( ((m.Flags() & TLocDrvRequest::ECodePaging) == 0) || (m.Drive()->iPagingDrv), LOCM_FAULT()); + __ASSERT_DEBUG(iBody->iPagingDevice,LOCM_FAULT()); + __KTRACE_OPT2(KLOCDPAGING,KLOCDRV,Kern::Printf("Defer PageIn request 0x%08x",&m)); + DMediaPagingDevice* pagingdevice=iBody->iPagingDevice; + +#ifdef __CONCURRENT_PAGING_INSTRUMENTATION__ + TInt id=m.iValue; + if (id==DMediaPagingDevice::ERomPageInRequest) + { + NKern::FMWait(&pagingdevice->iInstrumentationLock); + if(pagingdevice->iEmptyingQ & DMediaPagingDevice::EDeferredQ) + pagingdevice->iROMStats.iTotalReDeferrals++; + else if(pagingdevice->iEmptyingQ & DMediaPagingDevice::EMainQ) + pagingdevice->iROMStats.iTotalSynchDeferredFromMainQ++; + NKern::FMSignal(&pagingdevice->iInstrumentationLock); + } + else if (m.Flags() & TLocDrvRequest::ECodePaging) + { + NKern::FMWait(&pagingdevice->iInstrumentationLock); + if(pagingdevice->iEmptyingQ & DMediaPagingDevice::EDeferredQ) + pagingdevice->iCodeStats.iTotalReDeferrals++; + else if(pagingdevice->iEmptyingQ & DMediaPagingDevice::EMainQ) + pagingdevice->iCodeStats.iTotalSynchDeferredFromMainQ++; + NKern::FMSignal(&pagingdevice->iInstrumentationLock); + } + else if (m.Flags() & TLocDrvRequest::EDataPaging) + { + NKern::FMWait(&pagingdevice->iInstrumentationLock); + if(pagingdevice->iEmptyingQ & DMediaPagingDevice::EDeferredQ) + pagingdevice->iDataStats.iTotalReDeferrals++; + else if(pagingdevice->iEmptyingQ & DMediaPagingDevice::EMainQ) + pagingdevice->iDataStats.iTotalSynchDeferredFromMainQ++; + NKern::FMSignal(&pagingdevice->iInstrumentationLock); + } +#endif + pagingdevice->SendToDeferredQ(&m); + } + else +#endif + m.Forward(&iDeferred,EFalse); + r=KErrNone; + } +#if defined(__DEMAND_PAGING__) && defined(_DEBUG) + else if (r == KErrNone && DMediaPagingDevice::PagingRequest(m)) + __KTRACE_OPT(KLOCDPAGING,Kern::Printf("PageIn req 0x%08x completing asynchronously",&m)); +#endif + } + } + + if (r!=KErrNone && iCurrentReq) + { + TInt s=(r==KErrCompletion)?KErrNone:r; + CHECK_RET(s); + +#ifdef __DEMAND_PAGING__ + // got here because media driver cannot service or defer this request or did service it synchronously + if (DMediaPagingDevice::PagingRequest(m)) + { + __ASSERT_ALWAYS(iPagingMedia,LOCM_FAULT()); + __ASSERT_ALWAYS( ((m.Flags() & TLocDrvRequest::ECodePaging) == 0) || (m.Drive()->iPagingDrv), LOCM_FAULT()); + __ASSERT_DEBUG(iBody->iPagingDevice,LOCM_FAULT()); + __KTRACE_OPT(KLOCDPAGING,Kern::Printf("media driver cannot service or defer PageIn request 0x%08x or serviced it synchronously (%d)",&m, s)); + iBody->iPagingDevice->CompleteRequest(&m, s); + } + else +#endif + + CompleteRequest(m, s); + + } + + iCurrentReq=NULL; + return r; + } + +EXPORT_C void DPrimaryMediaBase::PowerUpComplete(TInt anError) +/** +Called after the device is powered up or there is some error while powering up the device. +If there is an error powering up the devices then it just completes the current running requests with an error +and also completes the outstanding requests on the iDeferred message queue by calling SetClosed( ). +If the device is powered up OK then it either opens the media drivers +and if they are already open then it handles the current/pending requests. + +@param anError Error code to be passed on while completing outstanding requests. +*/ + { + __KTRACE_OPT(KLOCDRV,Kern::Printf(">DPrimaryMediaBase(%d)::PowerUpComplete err %d iState %d",iMediaId,anError,iState)); + if (anError!=KErrNone) + { + // error powering up device + if (iState==EPoweringUp1 || iState==EPoweringUp2) + SetClosed(anError); + return; + } + + // Powered up OK - now open media drivers + if (iState==EPoweringUp1) + { + iState=EOpening; + StartOpenMediaDrivers(); + } + else if (iState==EPoweringUp2) + { + // media is powered up and ready, so handle the current/pending requests + MediaReadyHandleRequest(); + } + } + +void DPrimaryMediaBase::CloseMediaDrivers(DMedia* aMedia) + { + __KTRACE_OPT(KLOCDRV,Kern::Printf(">DPrimaryMediaBase(%d)::CloseMediaDrivers",iMediaId)); + + // we mustn't ever close the media driver if it's responsible for data paging as re-opening the drive + // would involve memory allocation which might cause deadlock if the kernel heap were to grow +#ifdef __DEMAND_PAGING__ + if (DataPagingDfcQ(this)) + { + __KTRACE_OPT(KLOCDRV,Kern::Printf("CloseMediaDrivers aborting for data paging media %08X", this)); + return; + } +#endif + + TInt i; + for (i=0; iiPrimaryMedia==this) + { + __KTRACE_OPT(KLOCDRV,Kern::Printf("Drive %d",i)); + if (aMedia == NULL || pL->iMedia == aMedia) + { + pL->iMedia=NULL; + } + } + } + for (i=iLastMediaId; i>=iMediaId; i--) + { + DMedia* pM=TheMedia[i]; + if (aMedia == NULL || pM == aMedia) + { + DMediaDriver* pD=pM->iDriver; + pM->iDriver=NULL; + __KTRACE_OPT(KLOCDRV,Kern::Printf("DMedia[%d] @ %08x Driver @ %08x",i,pM,pD)); + if (pD) + pD->Close(); + } + } + __KTRACE_OPT(KLOCDRV,Kern::Printf("iPhysDevIndex=iPhysDevArray.Count()-1; + iTotalPartitionsOpened=0; + iMediaDriversOpened=0; + iNextMediaDriver=NULL; + OpenNextMediaDriver(); + } + +void DPrimaryMediaBase::OpenNextMediaDriver() + { + __KTRACE_OPT(KLOCDRV,Kern::Printf("DPrimaryMediaBase(%d)::OpenNextMediaDriver, this %x mediaId %d iBody->iPhysDevIndex %d",iNextMediaId, this, iMediaId, iBody->iPhysDevIndex)); + + TVersion ver(KMediaDriverInterfaceMajorVersion,KMediaDriverInterfaceMinorVersion,KMediaDriverInterfaceBuildVersion); + SPhysicalDeviceEntry& e=iPhysDevArray[iBody->iPhysDevIndex]; + DPhysicalDevice* pD=e.iPhysicalDevice; + + iState = EOpening; + + DMedia* pM=TheMedia[iNextMediaId]; + if (pM && pM->iDriver != NULL) + { + iNextMediaDriver = pM->iDriver; + DoOpenMediaDriverComplete(KErrNone); + return; + } + + // this may be asynchronous + TInt s=pD->Create( (DBase*&)iNextMediaDriver, iMediaId, (TDesC8*) &iMountInfo, ver); + + __KTRACE_OPT(KLOCDRV,Kern::Printf("Media:Open-Opening %o(PRI:%d)-%d",pD,e.iPriority,s)); + if (s!=KErrNone) + { + iAsyncErrorCode=s; + iAsyncDfc.Enque(); + } + } + +// Called when a media driver has responded to the Open request +void DPrimaryMediaBase::DoOpenMediaDriverComplete(TInt anError) + { + __KTRACE_OPT(KLOCDRV,Kern::Printf("DPrimaryMediaBase(%d)::DoOpenMediaDriverComplete error %d iNextMediaDriver %x",iNextMediaId,anError,iNextMediaDriver)); + + if (anError!=KErrNone) + { + DMediaDriver* md = iNextMediaDriver; + iNextMediaDriver = NULL; + if (md) + md->Close(); + } + if (anError==KErrNotReady || anError==KErrNoMemory) + { + // if it's not ready or we're out of memory, abort + CloseMediaDrivers(); + SetClosed(anError); + return; + } + if (anError==KErrNone) + { + DMedia* pM=TheMedia[iNextMediaId]; + pM->iDriver=iNextMediaDriver; + DPhysicalDevice*& pD=iPhysDevArray[iBody->iPhysDevIndex].iPhysicalDevice; + iNextMediaDriver->iPhysicalDevice=pD; + pD=NULL; // so it won't be closed when we tidy up + ++iMediaDriversOpened; + } + + // if no error, read partition info on media + iState = EReadPartitionInfo; + + if (anError == KErrNone) + { + DMedia* pM=TheMedia[iNextMediaId]; + TInt r = pM->iDriver->PartitionInfo(pM->iPartitionInfo); + if (r!=KErrNone) + { + if (r==KErrCompletion) + r=KErrNone; + DoPartitionInfoComplete(r); + } + } + else + { + DoPartitionInfoComplete(anError); + } + } + +void DPrimaryMediaBase::DoPartitionInfoComplete(TInt anError) + { + __KTRACE_OPT(KLOCDRV,Kern::Printf("DPrimaryMediaBase(%d)::DoPartitionInfoComplete error %d",iNextMediaId,anError)); + + DMedia* pM=TheMedia[iNextMediaId]; + if (anError==KErrNone || anError == KErrLocked) + { + // successfully read partition info + iTotalPartitionsOpened+=pM->PartitionCount(); + } + else + { + // couldn't read partition info or driver failed to open + if (pM->iDriver) + { +#ifdef __DEMAND_PAGING__ + if (DataPagingDfcQ(this)) + { + __KTRACE_OPT(KLOCDRV,Kern::Printf("DoPartitionInfoComplete(%d) Close Media Driver aborted for data paging media %08X", this)); + } + else +#endif + { + pM->iDriver->Close(); + pM->iDriver=NULL; + } + } + if (anError==KErrNotReady || anError==KErrNoMemory) + { + // if it's not ready or we're out of memory, or the drive is locked, abort + CloseMediaDrivers(); + SetClosed(anError); + return; + } + } + + // Open next media driver, if there is one + TBool complete = EFalse; + if (++iNextMediaId>iLastMediaId) + complete=ETrue; + if (iBody->iPhysDevIndex==0) + complete=ETrue; + else + iBody->iPhysDevIndex--; + if (!complete) + { + OpenNextMediaDriver(); + return; + } + + __KTRACE_OPT(KLOCDRV,Kern::Printf("DPrimaryMediaBase %d All media drivers open & partitions read",iMediaId)); + __KTRACE_OPT(KLOCDRV,Kern::Printf("%d media drivers opened",iMediaDriversOpened)); + if (iMediaDriversOpened==0) + { + SetClosed(KErrNotSupported); + return; + } + + // we are now finished with media driver list + iPhysDevArray.Close(); + + // Finished reading partition info + __KTRACE_OPT(KLOCDRV,Kern::Printf("DPrimaryMediaBase %d Read partition info complete",iMediaId)); + __KTRACE_OPT(KLOCDRV,Kern::Printf("%d total partitions",iTotalPartitionsOpened)); + if (iTotalPartitionsOpened==0) + { + SetClosed(KErrNotSupported); + return; + } + + // work out mapping of drives to partitions/media + TInt totalPartitions=iTotalPartitionsOpened; + TInt id=iMediaId; // start with primary media + TInt partitionsOnThisMedia=PartitionCount(); + TInt partition=0; + TInt j; + for (j=0; jiPrimaryMedia==this) + { + if (totalPartitions==0) + { + pD->iMedia=NULL; + continue; + } + if (partition==partitionsOnThisMedia) + { + id++; + partition=0; + partitionsOnThisMedia=TheMedia[id]->PartitionCount(); + } + __KTRACE_OPT(KLOCDRV,Kern::Printf("Drive %d = Media %d Partition %d",j,id,partition)); + pD->iMedia=TheMedia[id]; + pD->iPartitionNumber=partition; + memcpy(pD, pD->iMedia->iPartitionInfo.iEntry+partition, sizeof(TPartitionEntry)); + partition++; + totalPartitions--; + } + } + + // media is now ready - handle current or deferred requests + MediaReadyHandleRequest(); + } + +void DPrimaryMediaBase::MediaReadyHandleRequest() + { + __KTRACE_OPT(KLOCDRV,Kern::Printf("DPrimaryMediaBase::MediaReadyHandleRequest() this %x", this)); + iState = EReady; + + // now we can process the current request + // careful - thread may have exited while we were powering up + if (iCurrentReq) + { + DoRequest(*iCurrentReq); // this sets iCurrentReq=NULL + } + + // see if we can process any other requests concurrently + RunDeferred(); + } + +void DPrimaryMediaBase::UpdatePartitionInfo() + { + iState=EReadPartitionInfo; + iNextMediaId=iMediaId; + DMedia* pM=TheMedia[iNextMediaId]; + TInt r=pM->iDriver->PartitionInfo(pM->iPartitionInfo); + if (r!=KErrNone) + { + if (r==KErrCompletion) + r=KErrNone; + DoPartitionInfoComplete(r); + } + } + +void DPrimaryMediaBase::CompleteCurrent(TInt anError) + { + if (iCurrentReq) + { + CHECK_RET(anError); +#ifdef __DEMAND_PAGING__ + // got here because it was powered down when powering up, or failed powering up or failed opening MD or got media change + if (DMediaPagingDevice::PagingRequest(*iCurrentReq)) + { + __ASSERT_ALWAYS(iPagingMedia,LOCM_FAULT()); + __ASSERT_DEBUG(iBody->iPagingDevice,LOCM_FAULT()); + __ASSERT_ALWAYS( ((iCurrentReq->Flags() & TLocDrvRequest::ECodePaging) == 0) || (iCurrentReq->Drive()->iPagingDrv), LOCM_FAULT()); + + __KTRACE_OPT2(KLOCDPAGING,KFAIL,Kern::Printf("Got here because it was powered down when powering up, or failed powering up or failed opening MD or got media change")); + iBody->iPagingDevice->CompleteRequest(iCurrentReq, anError); + } + else +#endif + CompleteRequest(*iCurrentReq, anError); + iCurrentReq=NULL; + } + } + + +void DPrimaryMediaBase::CompleteRequest(TLocDrvRequest& aMsg, TInt aResult) + { + aMsg.Complete(aResult,EFalse); + } + +EXPORT_C void DPrimaryMediaBase::RunDeferred() +/** +Runs deferred Requests. Initiated from DPrimaryMediaBase::PowerUpComplete() function +to see if any other requests can be processed concurrently. +Can also be called from DPrimaryMediaBase::NotifyPowerDown +or DPrimaryMediaBase::NotifyEmergencyPowerDown() function or DMediaDriver::Complete() +*/ + { + // Do nothing if an open or close is in progress - this might be the case, for example, + // if a EForceMediaChange request (with the KForceMediaChangeReOpenMediaDriver flag) + // has recently been processed + if (iState!=EReady && iState!=EClosed && iState!=EPoweredDown) + return; + + // rerun deferred requests; +#ifdef __DEMAND_PAGING__ +#ifdef __CONCURRENT_PAGING_INSTRUMENTATION__ + TInt countROM=0; + TInt countCode=0; +#endif + + if(iPagingMedia) + { + __ASSERT_DEBUG(iBody->iPagingDevice,LOCM_FAULT()); + if(iBody->iPagingDevice->iEmptyingQ & DMediaPagingDevice::EDeferredQ) // if already emptying deferred page in queue, don't reenter + { + __KTRACE_OPT(KLOCDPAGING,Kern::Printf("Already emptying deferred queue")); + return; + } + + DMediaPagingDevice* pagingdevice=iBody->iPagingDevice; + TLocDrvRequest* pL = (TLocDrvRequest*) pagingdevice->iDeferredQ.Last(); + if(pL) + { + pagingdevice->iEmptyingQ|= DMediaPagingDevice::EDeferredQ; // prevent reentering when already emptying this queue + TLocDrvRequest* pM=NULL; + while (pM != pL && (pM = (TLocDrvRequest*) pagingdevice->iDeferredQ.Poll()) != NULL) // synchronously empty deferred queue but ignore re-deferrals + { + __ASSERT_ALWAYS( DMediaPagingDevice::PagingRequest(*pL), LOCM_FAULT() ); + +#ifdef __CONCURRENT_PAGING_INSTRUMENTATION__ + (pM->iValue==DMediaPagingDevice::ERomPageInRequest)?(countROM++):(countCode++); + if(pM==pL) + { + NKern::FMWait(&pagingdevice->iInstrumentationLock); + if(pM->iValue==DMediaPagingDevice::ERomPageInRequest && pagingdevice->iROMStats.iMaxReqsInDeferrediROMStats.iMaxReqsInDeferred=countROM; + else if ((pM->Flags() & TLocDrvRequest::ECodePaging) && pagingdevice->iCodeStats.iMaxReqsInDeferrediCodeStats.iMaxReqsInDeferred=countCode; + else if ((pM->Flags() & TLocDrvRequest::EDataPaging) && pagingdevice->iDataStats.iMaxReqsInDeferrediDataStats.iMaxReqsInDeferred=countCode; + NKern::FMSignal(&pagingdevice->iInstrumentationLock); + } +#endif + __KTRACE_OPT(KLOCDPAGING,Kern::Printf("RunDeferred: process req 0x%08x, last in deferred queue 0x%08x",pM,pL)); +#ifdef BTRACE_PAGING_MEDIA + BTraceContext8(BTrace::EPagingMedia,BTrace::EPagingMediaLocMedPageInDeferredReposted,pM,pM->iValue); +#endif + // if Page In requests are synchronous this services them all in sequence, + // if they're asynch it re-defers them + DoRequest(*(TLocDrvRequest*)pM); + } + pagingdevice->iEmptyingQ&= ~DMediaPagingDevice::EDeferredQ; + } + + // the reason we now try an empty the main Page In queue is there is at least one type of Page In request + // serviced synchronously in which case when we empty the deferred Page In queue as above, received Page In + // requests are left in the main queue (not deferred) and we don't want to start processing deferred normal + // requests before these Page In requests. If all deferred normal requests are synchronous, the received Page + // In requests will have to wait until all are serviced. NB: requests may be deferred even if the MD services + // all requests synchronously, but runs background tasks that cannot be interrupted. In this last case the + // normal deferred queue may have some very long latency requests. + if(pagingdevice->iEmptyingQ & DMediaPagingDevice::EMainQ) // already emptying main Page In queue, skip (any Page In requests will be deferred) + { + __KTRACE_OPT(KLOCDPAGING,Kern::Printf("Already emptying main queue")); + return; + } + + TLocDrvRequest* pM=NULL; + if (!pagingdevice->iMainQ.iReady) // if it's ready, then queue is empty + { + pM = (TLocDrvRequest*) pagingdevice->iMainQ.iMessage; + pagingdevice->iMainQ.iMessage = NULL; + if (pM == NULL) + pM = (TLocDrvRequest*) pagingdevice->iMainQ.Poll(); + } + +#ifdef __CONCURRENT_PAGING_INSTRUMENTATION__ + countROM = countCode=0; +#endif + if(pM) + { +#ifdef __CONCURRENT_PAGING_INSTRUMENTATION__ + __e32_atomic_add_ord32(&pagingdevice->iROMStats.iTotalSynchEmptiedMainQ, 1); +#endif + pagingdevice->iEmptyingQ|=DMediaPagingDevice::EMainQ; + for ( ; pM != NULL; pM = (TLocDrvRequest*) pagingdevice->iMainQ.Poll()) + { + __ASSERT_ALWAYS(DMediaPagingDevice::PagingRequest(*pM), LOCM_FAULT()); + +#ifdef __CONCURRENT_PAGING_INSTRUMENTATION__ + (pM->iValue==DMediaPagingDevice::ERomPageInRequest)?(countROM++):(countCode++); +#endif + + __KTRACE_OPT(KLOCDPAGING,Kern::Printf("RunDeferred: process req 0x%08x",pM)); + DoRequest(*(TLocDrvRequest*)pM); // if Page In requests are synchronous this services them all in sequence, if they're asynch it defers them + } + + pagingdevice->iEmptyingQ&= ~DMediaPagingDevice::EMainQ; + + +#ifdef __CONCURRENT_PAGING_INSTRUMENTATION__ + NKern::FMWait(&pagingdevice->iInstrumentationLock); + pagingdevice->iROMStats.iTotalSynchServicedFromMainQ+=countROM; + if(pagingdevice->iROMStats.iMaxReqsInPendingiROMStats.iMaxReqsInPending=countROM; + pagingdevice->iCodeStats.iTotalSynchServicedFromMainQ+=countCode; + if(pagingdevice->iCodeStats.iMaxReqsInPendingiCodeStats.iMaxReqsInPending=countCode; + NKern::FMSignal(&pagingdevice->iInstrumentationLock); +#endif + } // if (pM) + } // if(iPagingMedia) +#endif + if (iRunningDeferred) + return; + TMessageBase* pL = iDeferred.Last(); + if (!pL) + return; // no deferred requests + iRunningDeferred=1; + TMessageBase* pM=NULL; + + while( pM != pL && (pM=iDeferred.Poll()) != NULL) // stop after processing last one (requests may be re-deferred) + DoRequest(*(TLocDrvRequest*)pM); + iRunningDeferred=0; + } + +void DPrimaryMediaBase::SetClosed(TInt anError) + { + __KTRACE_OPT(KLOCDRV,Kern::Printf("DPrimaryMediaBase(%d)::SetClosed error %d",iMediaId,anError)); + CHECK_RET(anError); + + // cancel DMediaDriver::OpenMediaDriverComplete() / DMediaDriver::PartitionInfoComplete() DFC + iAsyncDfc.Cancel(); + + iDeferred.CompleteAll(anError); + +#ifdef __DEMAND_PAGING__ + if(iPagingMedia) + iBody->iPagingDevice->iDeferredQ.CompleteAll(anError); +#endif + + CompleteCurrent(anError); + + + + if (iState==EOpening) + iPhysDevArray.Close(); + + iState = EClosed; + + iWaitMedChg.CompleteAll(KErrNone); + } + +void DPrimaryMediaBase::NotifyClients(TBool aMediaChange,TLocDrv* aLocDrv) + +// +// Notify all clients of a media change or power-down event +// + { + SDblQueLink* pL=iConnectionQ.iA.iNext; + while (pL!=&iConnectionQ.iA) + { + DLocalDrive* pD=_LOFF(pL,DLocalDrive,iLink); + // Issue the notification if the caller wants to notify all drives (aLocDrv == NULL) or + // the specified drive matches this one + if (aLocDrv == NULL || aLocDrv == pD->iDrive) + pD->NotifyChange(*this, aMediaChange); + pL=pL->iNext; + } + } + +EXPORT_C void DPrimaryMediaBase::NotifyMediaChange() +/** +Closes all media drivers on this device and notifies all connections that media change has occurred +and completes any outstanding requests with KErrNotReady. +This also completes any force media change requests with KErrNone. +*/ + { + __KTRACE_OPT(KLOCDRV,Kern::Printf("DPrimaryMediaBase(%d)::NotifyMediaChange state %d",iMediaId,iState)); + + TInt state=iState; + + __ASSERT_DEBUG(iBody, LOCM_FAULT()); + +#ifdef __DEMAND_PAGING__ + iBody->iMediaChanges++; + + // As data paging media never close, need to ensure the media driver cancels + // any requests it owns as the stack may be powered down by DPBusPrimaryMedia::ForceMediaChange(). + // DMediaDriver::NotifyPowerDown() should do this + if(DataPagingDfcQ(this)) + NotifyPowerDown(); +#endif + + // complete any outstanding requests with KErrNotReady + // and any force media change requests with KErrNone + SetClosed(KErrNotReady); + + // close all media drivers on this device + if (state>=EOpening) + { + CloseMediaDrivers(); + } + + // notify all connections that media change has occurred + NotifyClients(ETrue); + + // complete any force media change requests + iWaitMedChg.CompleteAll(KErrNone); + } + + +EXPORT_C void DPrimaryMediaBase::NotifyPowerDown() +/** +Called on machine power-down. Notifies all media drivers on this device. +If device is not ready then it completes current requests but leaves other outstanding requests +If ready, media driver should complete current request. + +*/ + { + __KTRACE_OPT(KLOCDRV,Kern::Printf("DPrimaryMediaBase(%d)::NotifyPowerDown state %d",iMediaId,iState)); + + TInt id; + TBool allPersistent = ETrue; + TBool allOpen = ETrue; + + // notify all media drivers on this device + for (id=iMediaId; id<=iLastMediaId; id++) + { + DMedia* pM = TheMedia[id]; + DMediaDriver* pD = pM->iDriver; + + if ((pD) && (iState==EReady || iState==EReadPartitionInfo || iState==EOpening || iState==EPoweringUp2 || iState==ERecovering)) + pD->NotifyPowerDown(); + + if (pD == NULL || pD->iPhysicalDevice == NULL) + allOpen = EFalse; + else if (pD->iPhysicalDevice->Info(DPhysicalDevice::EMediaDriverPersistent, NULL) != KErrNone) + { + // We must NOT destroy the media driver if this media is responsible for data paging as + // re-opening the media driver would involve memory allocation which might cause a deadlock +#ifdef __DEMAND_PAGING__ + __ASSERT_ALWAYS(!DataPagingDfcQ(this), LOCM_FAULT()); +#endif + allPersistent = EFalse; + } + } + + __KTRACE_OPT(KLOCDRV,Kern::Printf("allPersistent(%d)::allOpen %d",allPersistent, allOpen)); + + if (allPersistent && allOpen && iState == EReady) + { + // + // The EPoweredDown state indicates that the media is powered down, but the media driver still exists. + // + // - This allows the media driver to still be accessed (ie - to determine driver capabilities) without + // the need to power up the device, which can be a lengthy operation. + // + // - NOTE : This will need re-visiting if we ever re-enable standby mode on a platform that is not capable + // of detecting door interrupts while in standby. In such a scenario, problems could occur as + // the device capabilities may change without the local media subsystem recognising. + // + iState=EPoweredDown; + } + else + { + CloseMediaDrivers(); + SetClosed(KErrNotReady); + } + + NotifyClients(EFalse); + } + + +EXPORT_C void DPrimaryMediaBase::NotifyPsuFault(TInt anError) +/** +Closes all media drivers on this device and completes any outstanding requests with error code. +@param anError Error code to be passed on while closing media drivers and completing outstanding requests. +*/ + + { + __KTRACE_OPT(KLOCDRV,Kern::Printf("DPrimaryMediaBase(%d)::NotifyPsuFault state %d, err %d",iMediaId,iState,anError)); + + if (iState>=EOpening) + { + CloseMediaDrivers(); + } + + // complete any outstanding requests with error + SetClosed(anError); + } + +EXPORT_C void DPrimaryMediaBase::NotifyEmergencyPowerDown() +/** +Called on emergency power down. Notifies all media drivers on this device. +If it is not in a ready state then it completes the current request but leaves other outstanding requests. +If it is ready then the media driver should complete the current request. +It closes all media drivers and notifies all clients of a power down event. +*/ + { + __KTRACE_OPT(KLOCDRV,Kern::Printf("DPrimaryMediaBase(%d)::NotifyEmergencyPowerDown state %d",iMediaId,iState)); + TBool recover=EFalse; + if (iState==EReady && iCritical!=0) + { + // check if emergency power recovery supported + ; + } + if (recover) + { + } + + // else just return KErrAbort + // notify all media drivers on this device + if (iState==EReady || iState==EOpening || iState==EPoweringUp2 || iState==ERecovering) + { + TInt id; + for (id=iMediaId; id<=iLastMediaId; id++) + { + DMedia* pM=TheMedia[id]; + DMediaDriver* pD=pM->iDriver; + if (pD) + pD->NotifyEmergencyPowerDown(); + } + } + + if (iState!=EReady) + { + // complete current request but leave other outstanding requests + // if ready, media driver should complete current request + CompleteCurrent(KErrNotReady); + } + CloseMediaDrivers(); + SetClosed(KErrNotReady); + NotifyClients(EFalse); + } + +EXPORT_C void DPrimaryMediaBase::NotifyMediaPresent() +/** +Notifies clients of a media change by calling NotifyClients ( ) function to indicate that media is present. +*/ + { + NotifyClients(ETrue); + } + +EXPORT_C TInt DPrimaryMediaBase::DoInCritical() +/** +Flags the media driver as entering a critical part of its processing. + +In this context, critical means that the driver must be allowed to complete +its current activity. +For example, a request to power down the device must be deferred until +the driver exits the critical part. + +@return KErrNone, if the driver has been successfully flagged as being in + a critical part; otherwise, one of the other system-wide error codes. + The default implementation just returns KErrNone and can be overridden in the derived class +@see DPrimaryMediaBase::DoEndInCritical() +*/ + + { + return KErrNone; + } + +EXPORT_C void DPrimaryMediaBase::DoEndInCritical() +/** +Flags the media driver as leaving a critical part of its processing. + +Default implementation does nothing +@see DPrimaryMediaBase::DoEndInCritical() +*/ + { + } + +TInt DPrimaryMediaBase::InCritical() + { + if (iCritical==0) + { + TInt r=DoInCritical(); + if (r!=KErrNone) + return r; + } + ++iCritical; + return KErrNone; + } + +void DPrimaryMediaBase::EndInCritical() + { + if (--iCritical==0) + DoEndInCritical(); + } + +EXPORT_C void DPrimaryMediaBase::DeltaCurrentConsumption(TInt /*aCurrent*/) +/** +Sets the incremental value of current consumption to aCurrent. +The default implementation does nothing . + +@param aCurrent Delta Current in Milliamps +*/ + { + // default implementation + } + +TInt DPrimaryMediaBase::OpenMediaDriver() +// +// Synchronous open for devices with no DFC queue (e.g. IRAM) +// + { + __KTRACE_OPT(KLOCDRV,Kern::Printf(">DPrimaryMediaBase:OpenMediaDriver-%d",iMediaId)); + TVersion ver(KMediaDriverInterfaceMajorVersion,KMediaDriverInterfaceMinorVersion,KMediaDriverInterfaceBuildVersion); + + // Get a list of all currently loaded media drivers + // Most media drivers do not make use of the pointer iMountInfo.iInfo when + // their Validate() procedures are called from RPhysicalDeviceArray::GetDriverList(). + // However, a group of media drivers sharing the same id (passed in iDevice) may use + // the additional information pointed to by iMountInfo.iInfo to distinguish + // group members. This information is passed when the media driver is registered + // using LocDrv::RegisterMediaDevice(). + TInt r=iPhysDevArray.GetDriverList(KLitMediaDriverName,iDevice,iMountInfo.iInfo,ver); + if (r!=KErrNone) + return r; + + // Go through them starting with highest priority + TInt totalPartitions=0; + TInt c=iPhysDevArray.Count(); // can't be zero + TInt i=c-1; + r=KErrNotSupported; + for (iNextMediaId=iMediaId; i>=0 && iNextMediaId<=iLastMediaId && r!=KErrNotReady; i--) + { + DPhysicalDevice* pD=iPhysDevArray[i].iPhysicalDevice; + DMediaDriver *pM=NULL; + + // try to open media driver + TInt s=pD->Create( (DBase*&)pM, iMediaId, NULL, ver); + + __KTRACE_OPT(KLOCDRV,Kern::Printf("Media:Open-Opening %o(PRI:%d)-%d",pD,iPhysDevArray[i].iPriority,s)); + if (s!=KErrNone && pM) + { + pM->Close(); + pM=NULL; + } + if (s==KErrNotReady) + { + r=KErrNotReady; // If it isn't ready - nothing will open. + break; + } + if (s==KErrNoMemory) + { + r=KErrNoMemory; // If we are out of memory, give up now + break; + } + if (s==KErrNone) + { + // Found a media driver for this device - check for valid partitions. + DMedia* media=TheMedia[iNextMediaId]; + s=pM->PartitionInfo(media->iPartitionInfo); + if (s==KErrNone) + { + r=KErrNone; + media->iDriver=pM; + pM->iPhysicalDevice=pD; + iPhysDevArray[i].iPhysicalDevice=NULL; // so it won't be closed when we tidy up + totalPartitions+=media->PartitionCount(); + } + else + pM->Close(); + } + } + + // we are now finished with media driver list + iPhysDevArray.Close(); + + // if driver opened OK, work out mapping of drives to partitions/media + if (r==KErrNone) + { + TInt id=iMediaId; // start with primary media + TInt partitionsOnThisMedia=PartitionCount(); + TInt partition=0; + TInt j; + for (j=0; jiPrimaryMedia==this) + { + if (totalPartitions==0) + { + pD->iMedia=NULL; + continue; + } + if (partition==partitionsOnThisMedia) + { + id++; + partition=0; + partitionsOnThisMedia=TheMedia[id]->PartitionCount(); + } + pD->iMedia=TheMedia[id]; + pD->iPartitionNumber=partition; + memcpy(pD, pD->iMedia->iPartitionInfo.iEntry+partition, sizeof(TPartitionEntry)); + partition++; + totalPartitions--; + } + } + } + + __KTRACE_OPT(KLOCDRV,Kern::Printf("iRequestCount, (TUint) 1); +//Kern::Printf("RCINC: this %x cnt %d, old %d", this, iBody->iRequestCount, oldVal); + if (oldVal == 0 && iBody->iPagingDevice) + { +//Kern::Printf("RCINC: NotifyBusy()"); + iBody->iPagingDevice->NotifyBusy(); + } + } + +// RequestCountDec() +// +// Counts the number of outstanding requests +// For data-paging media, calls DPagingDevice::NotifyIdle() when count reaches zero +// +void DPrimaryMediaBase::RequestCountDec() + { + __ASSERT_DEBUG(iBody, LOCM_FAULT()); + TInt oldVal = (TInt) __e32_atomic_add_ord32(&iBody->iRequestCount, (TUint) -1); +//Kern::Printf("RCDEC: this %x cnt %d, old %d", this, iBody->iRequestCount, oldVal); + if (oldVal == 1 && iBody->iPagingDevice) + { +//Kern::Printf("RCDEC: NotifyIdle()"); + iBody->iPagingDevice->NotifyIdle(); + } + __ASSERT_DEBUG(iBody->iRequestCount >= 0, LOCM_FAULT()); + } +#endif // __DEMAND_PAGING__ + + +TPartitionInfo::TPartitionInfo() +// +// Constructor +// + { + memclr(this, sizeof(TPartitionInfo)); + } + +#ifdef __DEMAND_PAGING__ + +void pageInDfc(TAny* aPtr) + { + __KTRACE_OPT2(KLOCDRV,KLOCDPAGING,Kern::Printf("pageInDfc")); + DPrimaryMediaBase* primaryMedia=(DPrimaryMediaBase*)aPtr; + __ASSERT_ALWAYS(primaryMedia && primaryMedia->iPagingMedia && primaryMedia->iBody->iPagingDevice,LOCM_FAULT()); + DMediaPagingDevice* pagingdevice=primaryMedia->iBody->iPagingDevice; + + TLocDrvRequest* m = (TLocDrvRequest*) pagingdevice->iMainQ.iMessage; + pagingdevice->iMainQ.iMessage = NULL; + +#ifdef __CONCURRENT_PAGING_INSTRUMENTATION__ + if (!m) + __e32_atomic_add_ord8(&pagingdevice->iROMStats.iTotalRunDry, 1); +#endif + +#ifdef __CONCURRENT_PAGING_INSTRUMENTATION__ + TInt countROM=0; + TInt countCode=0; +#endif + + for ( ; m != NULL; m = (TLocDrvRequest*) pagingdevice->iMainQ.Poll()) + { + __ASSERT_ALWAYS(DMediaPagingDevice::PagingRequest(*m), LOCM_FAULT()); + +#ifdef __CONCURRENT_PAGING_INSTRUMENTATION__ + (m->iValue == DMediaPagingDevice::ERomPageInRequest)?(countROM++):(countCode++); +#endif + __KTRACE_OPT(KLOCDPAGING, Kern::Printf("pageInDfc: process request 0x%08x, last in queue 0x%08x",m, pagingdevice->iMainQ.Last()) ); + + primaryMedia->HandleMsg(*m); + } + +#ifdef __CONCURRENT_PAGING_INSTRUMENTATION__ + NKern::FMWait(&pagingdevice->iInstrumentationLock); + if (pagingdevice->iROMStats.iMaxReqsInPendingiROMStats.iMaxReqsInPending=countROM; + if (pagingdevice->iCodeStats.iMaxReqsInPendingiCodeStats.iMaxReqsInPending=countCode; + NKern::FMSignal(&pagingdevice->iInstrumentationLock); +#endif + + pagingdevice->iMainQ.Receive(); // allow reception of more messages + } + +DMediaPagingDevice::DMediaPagingDevice(DPrimaryMediaBase* aPtr) + : iMainQ(pageInDfc, aPtr, NULL, KMaxDfcPriority), + iDeferredQ(NULL, NULL, NULL, 0), // callback never used + iEmptyingQ(NULL), + iInstrumentationLock() +#ifdef __CONCURRENT_PAGING_INSTRUMENTATION__ + ,iServicingROM(NULL), iServicingCode(NULL) +#endif + { + iPrimaryMedia = aPtr; + if (iPrimaryMedia->iDfcQ) // media driver has its own thread + { + iMainQ.SetDfcQ(iPrimaryMedia->iDfcQ); + } +#ifdef __CONCURRENT_PAGING_INSTRUMENTATION__ + memclr((TAny*)&iROMStats,sizeof(SMediaROMPagingConcurrencyInfo)+sizeof(SMediaCodePagingConcurrencyInfo)); +#endif +#ifdef __DEMAND_PAGING_BENCHMARKS__ + iROMBenchmarkData.iCount=iROMBenchmarkData.iTotalTime=iROMBenchmarkData.iMaxTime=0; + iROMBenchmarkData.iMinTime = KMaxTInt; + iCodeBenchmarkData.iCount=iCodeBenchmarkData.iTotalTime=iCodeBenchmarkData.iMaxTime=0; + iCodeBenchmarkData.iMinTime = KMaxTInt; + iDataInBenchmarkData.iCount=iDataInBenchmarkData.iTotalTime=iDataInBenchmarkData.iMaxTime=0; + iDataInBenchmarkData.iMinTime = KMaxTInt; + iDataOutBenchmarkData.iCount=iDataOutBenchmarkData.iTotalTime=iDataOutBenchmarkData.iMaxTime=0; + iDataOutBenchmarkData.iMinTime = KMaxTInt; +#endif + + iMainQ.Receive(); + } + +DMediaPagingDevice::~DMediaPagingDevice() + { + + if (iMountInfoDataLock) + ThePinObjectAllocator->ReleasePinObject((DPinObjectAllocator::SVirtualPinContainer*) iMountInfoDataLock); + + if (iMountInfoDescHdrLock) + ThePinObjectAllocator->ReleasePinObject((DPinObjectAllocator::SVirtualPinContainer*) iMountInfoDescHdrLock); + + if (iMountInfoDescLenLock) + ThePinObjectAllocator->ReleasePinObject((DPinObjectAllocator::SVirtualPinContainer*) iMountInfoDescLenLock); + } + + +void DMediaPagingDevice::SendToMainQueueDfcAndBlock(TThreadMessage* aMsg) + { + __KTRACE_OPT2(KLOCDRV,KLOCDPAGING,Kern::Printf("Send request 0x%08x to main queue",aMsg)); + __ASSERT_ALWAYS(aMsg->iState==TMessageBase::EFree,LOCM_FAULT()); // check that message was previously completed or never queued + + // if drive supports DMA, turn on Physical memory flag & sync memory + TLocDrvRequest& m=*(TLocDrvRequest*)(aMsg); + + TLinAddr addr = (TLinAddr) m.RemoteDes(); + TInt len = I64LOW(m.Length()); + + TBool needSyncAfterRead = EFalse; + if (m.Drive()->iDmaHelper) + { + m.Flags() |= TLocDrvRequest::EPhysAddr; + if (m.Id() == DLocalDrive::EWrite) + { + Cache::SyncMemoryBeforeDmaWrite(addr, len); + } + else + { + Cache::SyncMemoryBeforeDmaRead(addr, len); + needSyncAfterRead = ETrue; + } + } + + // Count the number of outstanding requests if this is the data-paging media, so that + // we can call DPagingDevice::NotifyBusy() / DPagingDevice::NotifyIdle() + if ((m.Flags() & TLocDrvRequest::EBackgroundPaging) == 0) + iPrimaryMedia->RequestCountInc(); + + aMsg->SendReceive(&iMainQ); + +#ifdef __DEMAND_PAGING__ + if ((m.Flags() & TLocDrvRequest::EBackgroundPaging) == 0) + iPrimaryMedia->RequestCountDec(); +#endif + + if (needSyncAfterRead) + { + Cache::SyncMemoryAfterDmaRead(addr, len); + } + + + // come back here when request is completed + __ASSERT_DEBUG(aMsg->iState==TMessageBase::EFree,LOCM_FAULT()); // check message has been completed + } + +void DMediaPagingDevice::SendToDeferredQ(TThreadMessage* aMsg) + { + // This queue is only accessed from MD thread + __ASSERT_ALWAYS(aMsg->iState==TMessageBase::EAccepted,LOCM_FAULT()); // check that message was previously dequeued +#ifdef BTRACE_PAGING_MEDIA + if(iEmptyingQ&DMediaPagingDevice::EDeferredQ) // already deferring + BTraceContext8(BTrace::EPagingMedia,BTrace::EPagingMediaLocMedPageInReDeferred,aMsg,aMsg->iValue); + else + BTraceContext8(BTrace::EPagingMedia,BTrace::EPagingMediaLocMedPageInDeferred,aMsg,aMsg->iValue); +#endif + + aMsg->Forward(&iDeferredQ, EFalse); + } + + +void DMediaPagingDevice::CompleteRequest(TThreadMessage* aMsg, TInt aResult) + { + __KTRACE_OPT2(KLOCDRV,KLOCDPAGING,Kern::Printf("DMediaPagingDevice::CompleteRequest, request 0x%08x result %d", aMsg, aResult)); + __ASSERT_DEBUG(aMsg->iState==TMessageBase::EAccepted,LOCM_FAULT()); + +#ifdef BTRACE_PAGING_MEDIA + BTraceContext12(BTrace::EPagingMedia,BTrace::EPagingMediaLocMedPageInPagedIn,aMsg,aResult,aMsg->iValue); +#endif + + iPrimaryMedia->CompleteRequest(*((TLocDrvRequest*) aMsg), aResult); + } + +TInt DMediaPagingDevice::Read(TThreadMessage* aReq,TLinAddr aBuffer,TUint aOffset,TUint aSize,TInt aDrvNumber) + { + __ASSERT_ALWAYS(NKern::CurrentThread()!=iPrimaryMedia->iDfcQ->iThread,LOCM_FAULT()); // that would lock up the system, thus better die now + __ASSERT_ALWAYS(aReq,LOCM_FAULT()); + __ASSERT_CRITICAL + +#ifdef __DEMAND_PAGING_BENCHMARKS__ + TUint32 bmStart = NKern::FastCounter(); +#endif + +#ifdef __CONCURRENT_PAGING_INSTRUMENTATION__ + TUint8* servicingCount; + NKern::FMWait(&iInstrumentationLock); + if(aDrvNumber == EDriveRomPaging) // ROM paging + { + servicingCount = &iServicingROM; + if(iServicingROM) + iROMStats.iTotalConcurrentReqs++; + if(!(++iServicingROM)) + { + iServicingROM=1; // overflow... + iROMStats.iTotalConcurrentReqs=0; // ...reset this + } + TBool empty = iMainQ.iReady && iDeferredQ.iQ.IsEmpty(); + if(!empty) + iROMStats.iTotalReqIssuedNonEmptyQ++; + } + else if (aDrvNumber == EDriveDataPaging) // Data paging + { + servicingCount = &iServicingDataIn; + if(iServicingDataIn) + iDataStats.iTotalConcurrentReqs++; + if(!(++iServicingDataIn)) + { + iServicingDataIn=1; // overflow... + iDataStats.iTotalConcurrentReqs=0; // ...reset this + } + TBool empty = iMainQ.iReady && iDeferredQ.iQ.IsEmpty(); + if(!empty) + iDataStats.iTotalReqIssuedNonEmptyQ++; + } + else + { + servicingCount = &iServicingCode; + if(iServicingCode) + iCodeStats.iTotalConcurrentReqs++; + if(!(++iServicingCode)) + { + iServicingCode=1; // overflow... + iCodeStats.iTotalConcurrentReqs=0; // ...reset this + } + TBool empty = iMainQ.iReady && iDeferredQ.iQ.IsEmpty(); + if(!empty) + iCodeStats.iTotalReqIssuedNonEmptyQ++; + } + NKern::FMSignal(&iInstrumentationLock); +#endif + + TUint offset=aOffset<iDfcQ) + { + LOCM_FAULT(); // don't allow paging + return KErrNone; // keep compiler happy + } + + + TLocDrvRequest& m=*(TLocDrvRequest*)(aReq); + +#ifdef __DEMAND_PAGING_BENCHMARKS__ + SPagingBenchmarkInfo* info = NULL; +#endif + + + // Read from the media and allow for retries in the unlikely event of an error. + const TInt KPageInRetries = 5; + TInt retVal = KErrGeneral; + for (TInt i=0; retVal != KErrNone && i < KPageInRetries; i++) + { + m.Flags() = TLocDrvRequest::EPaging; + TLocDrv* pL=NULL; + if(aDrvNumber == EDriveRomPaging) // ROM paging + { + m.Id() = DMediaPagingDevice::ERomPageInRequest; + if (iRomPagingDriveNumber == KErrNotFound) + { + // ROM partition has not been reported by the media driver + // it is assumed that the media driver will adjust the request accordingly + m.Flags() |= TLocDrvRequest::EAdjusted; + // Use a media drive number so the request reaches the correct media... + m.Drive() = TheDrives[iFirstLocalDriveNumber]; + } + else + { + //ROM partition has been reported + //Set drive for use with CheckAndAdjustForPartition + m.Drive() = TheDrives[iRomPagingDriveNumber]; + } +#ifdef __DEMAND_PAGING_BENCHMARKS__ + __e32_atomic_add_ord32(&iMediaPagingInfo.iRomPageInCount, (TUint) 1); + info = &iROMBenchmarkData; +#endif + } + else if(aDrvNumber == EDriveDataPaging) // Data paging + { + m.Id() = DLocalDrive::ERead; + m.Flags() |= TLocDrvRequest::EDataPaging; + m.Drive() = TheDrives[iDataPagingDriveNumber]; +#ifdef __DEMAND_PAGING_BENCHMARKS__ + __e32_atomic_add_ord32(&iMediaPagingInfo.iDataPageInCount, (TUint) 1); + info = &iDataInBenchmarkData; +#endif + } + else if ((aDrvNumber >=0) && (aDrvNumberiPrimaryMedia==iPrimaryMedia),LOCM_FAULT()); // valid drive number? + m.Drive()=pL; +#ifdef __DEMAND_PAGING_BENCHMARKS__ + __e32_atomic_add_ord32(&iMediaPagingInfo.iCodePageInCount, (TUint) 1); + info = &iCodeBenchmarkData; +#endif + } + else + LOCM_FAULT(); // invalid drive number + + m.RemoteThread()=NULL; + m.Pos()=offset; + m.Length()=Int64(size); + m.RemoteDes()=(TAny*)aBuffer; + m.RemoteDesOffset()=0; // pre-aligned + m.DriverFlags()=0; + __KTRACE_OPT2(KLOCDRV,KLOCDPAGING,Kern::Printf("ReqId=%d, Pos=0x%lx, Len=0x%lx, remote Des 0x%x",m.Id(),m.Pos(),m.Length(),m.RemoteDes())); + + __ASSERT_DEBUG(iPrimaryMedia->iBody, LOCM_FAULT()); + TInt mediaChanges = iPrimaryMedia->iBody->iMediaChanges; + + SendToMainQueueDfcAndBlock(&m); // queues request, sets and opens client thread, queues dfc and blocks thread until request is completed + retVal = m.iValue; + +#ifdef __DEBUG_DEMAND_PAGING__ + if (retVal != KErrNone) + Kern::Printf("Pagin Failure %d, retry %d", retVal, i); +#endif + + // reset retry count if there's ben a media change + if (retVal != KErrNone && mediaChanges != iPrimaryMedia->iBody->iMediaChanges) + i = 0; + } // for () + +#ifdef __CONCURRENT_PAGING_INSTRUMENTATION__ + NKern::FMWait(&iInstrumentationLock); + if (*servicingCount) + (*servicingCount)--; + NKern::FMSignal(&iInstrumentationLock); +#endif + +#ifdef __DEMAND_PAGING_BENCHMARKS__ + TUint32 bmEnd = NKern::FastCounter(); + ++info->iCount; +#if !defined(HIGHIGH_RES_TIMER) || defined(HIGH_RES_TIMER_COUNTS_UP) + TInt64 elapsed=bmEnd-bmStart; +#else + TInt64 elapsed=bmStart-bmEnd; +#endif + info->iTotalTime += elapsed; + if (elapsed > info->iMaxTime) + info->iMaxTime = elapsed; + if (elapsed < info->iMinTime) + info->iMinTime = elapsed; +#endif // __DEMAND_PAGING_BENCHMARKS__ + + return retVal; + } + +TInt DMediaPagingDevice::Write(TThreadMessage* aReq,TLinAddr aBuffer,TUint aOffset,TUint aSize,TBool aBackground) + { + __ASSERT_ALWAYS(NKern::CurrentThread()!=iPrimaryMedia->iDfcQ->iThread,LOCM_FAULT()); // that would lock up the system, thus better die now + __ASSERT_ALWAYS(aReq,LOCM_FAULT()); + __ASSERT_CRITICAL + +#ifdef __DEMAND_PAGING_BENCHMARKS__ + TUint32 bmStart = NKern::FastCounter(); +#endif + +#ifdef __CONCURRENT_PAGING_INSTRUMENTATION__ + NKern::FMWait(&iInstrumentationLock); + if(iServicingDataOut) + iDataStats.iTotalConcurrentReqs++; + if(!(++iServicingDataOut)) + { + iServicingDataOut=1; // overflow... + iDataStats.iTotalConcurrentReqs=0; // ...reset this + } + TBool empty = iMainQ.iReady && iDeferredQ.iQ.IsEmpty(); + if(!empty) + iDataStats.iTotalReqIssuedNonEmptyQ++; + NKern::FMSignal(&iInstrumentationLock); +#endif + + TUint offset=aOffset<iDfcQ) + { + LOCM_FAULT(); // don't allow paging + return KErrNone; // keep compiler happy + } + + + TLocDrvRequest& m=*(TLocDrvRequest*)(aReq); + +#ifdef __DEMAND_PAGING_BENCHMARKS__ + __e32_atomic_add_ord32(&iMediaPagingInfo.iDataPageOutCount, (TUint) 1); + if (aBackground) + __e32_atomic_add_ord32(&iMediaPagingInfo.iDataPageOutBackgroundCount, (TUint) 1); +#endif + + // Write to the media and allow for retries in the unlikely event of an error. + const TInt KPageOutRetries = 5; + TInt retVal = KErrGeneral; + for (TInt i=0; retVal != KErrNone && i < KPageOutRetries; i++) + { + m.Flags() = TLocDrvRequest::EPaging | TLocDrvRequest::EDataPaging | (aBackground ? TLocDrvRequest::EBackgroundPaging : 0); + + m.Id() = DLocalDrive::EWrite; + m.Drive() = TheDrives[iDataPagingDriveNumber]; + + m.RemoteThread()=NULL; + m.Pos()=offset; + m.Length()=Int64(size); + m.RemoteDes()=(TAny*)aBuffer; + m.RemoteDesOffset()=0; // pre-aligned + m.DriverFlags()=0; + __KTRACE_OPT2(KLOCDRV,KLOCDPAGING,Kern::Printf("ReqId=%d, Pos=0x%lx, Len=0x%lx, remote Des 0x%x",m.Id(),m.Pos(),m.Length(),m.RemoteDes())); + + __ASSERT_DEBUG(iPrimaryMedia->iBody, LOCM_FAULT()); + TInt mediaChanges = iPrimaryMedia->iBody->iMediaChanges; + + SendToMainQueueDfcAndBlock(&m); // queues request, sets and opens client thread, queues dfc and blocks thread until request is completed + retVal = m.iValue; + +#ifdef __DEBUG_DEMAND_PAGING__ + if (retVal != KErrNone) + Kern::Printf("Pagout Failure %d, retry %d", retVal, i); +#endif + // reset retry count if there's ben a media change + if (retVal != KErrNone && mediaChanges != iPrimaryMedia->iBody->iMediaChanges) + i = 0; + } // for () + +#ifdef __CONCURRENT_PAGING_INSTRUMENTATION__ + NKern::FMWait(&iInstrumentationLock); + if (iServicingDataOut) + iServicingDataOut--; + NKern::FMSignal(&iInstrumentationLock); +#endif + +#ifdef __DEMAND_PAGING_BENCHMARKS__ + SPagingBenchmarkInfo& info = iDataOutBenchmarkData; + TUint32 bmEnd = NKern::FastCounter(); + ++info.iCount; +#if !defined(HIGHIGH_RES_TIMER) || defined(HIGH_RES_TIMER_COUNTS_UP) + TInt64 elapsed=bmEnd-bmStart; +#else + TInt64 elapsed=bmStart-bmEnd; +#endif + info.iTotalTime += elapsed; + if (elapsed > info.iMaxTime) + info.iMaxTime = elapsed; + if (elapsed < info.iMinTime) + info.iMinTime = elapsed; +#endif // __DEMAND_PAGING_BENCHMARKS__ + + return retVal; + } + + +TInt DMediaPagingDevice::DeleteNotify(TThreadMessage* aReq,TUint aOffset,TUint aSize) + { + if (iDeleteNotifyNotSupported) + return KErrNotSupported; + + __ASSERT_ALWAYS(NKern::CurrentThread()!=iPrimaryMedia->iDfcQ->iThread,LOCM_FAULT()); // that would lock up the system, thus better die now + __ASSERT_ALWAYS(aReq,LOCM_FAULT()); + __ASSERT_ALWAYS(DataPagingDfcQ(iPrimaryMedia),LOCM_FAULT()); + __ASSERT_CRITICAL + + TUint offset = aOffset<iDfcQ) + { + LOCM_FAULT(); // don't allow paging + return KErrNone; // keep compiler happy + } + + TLocDrvRequest& m=*(TLocDrvRequest*)(aReq); + + + m.Flags() = TLocDrvRequest::EPaging | TLocDrvRequest::EDataPaging; + m.Id() = DLocalDrive::EDeleteNotify; + m.Drive() = TheDrives[iDataPagingDriveNumber]; + + m.RemoteThread() = NULL; + m.Pos() = offset; + m.Length() = Int64(size); + m.RemoteDes() = NULL; + m.RemoteDesOffset() = 0; // pre-aligned + m.DriverFlags()=0; + __KTRACE_OPT2(KLOCDRV,KLOCDPAGING,Kern::Printf("ReqId=%d, Pos=0x%lx, Len=0x%lx, remote Des 0x%x",m.Id(),m.Pos(),m.Length(),m.RemoteDes())); + + + // send request aynchronously as we don't particularly care about the result + // and waiting would slow down the thread taking the page fault + iPrimaryMedia->RequestCountInc(); + + m.SendReceive(&iMainQ); // send request synchronously + +#ifdef __DEMAND_PAGING__ + iPrimaryMedia->RequestCountDec(); +#endif + + TInt retVal = m.iValue; + + if (retVal == KErrNotSupported) + iDeleteNotifyNotSupported = ETrue; + + return retVal; + } + + + +EXPORT_C TInt TLocDrvRequest::WriteToPageHandler(const TAny* aSrc, TInt aSize, TInt anOffset) + { +#ifdef BTRACE_PAGING_MEDIA + TMediaDevice medDev=Drive()->iMedia->iDevice; + TInt buf[3]; + buf[0]=(TUint32)RemoteDes(); + buf[1]=anOffset; + buf[2]=aSize; + BTraceContextN(BTrace::EPagingMedia,BTrace::EPagingMediaMedDrvWriteBack,medDev,this,buf,sizeof(buf)); +#endif + __KTRACE_OPT2(KLOCDRV,KLOCDPAGING,Kern::Printf("TLocDrvRequest::WriteToPageHandler, memcpy((aTrg)%08x, (aSrc)%08x, (aLength)%08x)",(TUint32)RemoteDes()+anOffset,aSrc,aSize)); + (void)memcpy((TAny*)((TUint32)RemoteDes()+anOffset), aSrc, aSize); // maybe in later versions this could be something else + return KErrNone; + } + +EXPORT_C TInt TLocDrvRequest::ReadFromPageHandler(TAny* aDst, TInt aSize, TInt anOffset) + { +#ifdef BTRACE_PAGING_MEDIA + TMediaDevice medDev=Drive()->iMedia->iDevice; + TInt buf[3]; + buf[0]=(TUint32)RemoteDes(); + buf[1]=anOffset; + buf[2]=aSize; + BTraceContextN(BTrace::EPagingMedia,BTrace::EPagingMediaMedDrvRead,medDev,this,buf,sizeof(buf)); +#endif + __KTRACE_OPT2(KLOCDRV,KLOCDPAGING,Kern::Printf("TLocDrvRequest::ReadFromPageHandler, memcpy((aDst)%08x, (aTrg)%08x, (aLength)%08x)",aDst,(TUint32)RemoteDes()+anOffset,aSize)); + (void)memcpy(aDst, (TAny*)((TUint32)RemoteDes()+anOffset), aSize); // maybe in later versions this could be something else + return KErrNone; + } + +_LIT(KLitFragmentationMutexName, "FRAGMENTATION_MUTEX"); + +TInt DFragmentationPagingLock::Construct(TUint aNumPages) + { + TInt r=KErrNone; + __KTRACE_OPT2(KBOOT,KLOCDPAGING,Kern::Printf("Fragmentation Lock: creating Mutex")); + r=Kern::MutexCreate(this->iFragmentationMutex, KLitFragmentationMutexName, KMutexOrdNone); + if (r!=KErrNone) + return r; + __KTRACE_OPT2(KBOOT,KLOCDPAGING,Kern::Printf("Fragmentation Lock: Mutex created OK")); + + iFragmentGranularity = 0; + if (aNumPages == 0) + return KErrNone; + + // in CS + TInt pageSize=Kern::RoundToPageSize(1); + LockFragmentation(); + r=Alloc(pageSize*aNumPages); // alloc pages + UnlockFragmentation(); + + if(r==KErrNone) + { + iFragmentGranularity = pageSize * aNumPages; + __KTRACE_OPT2(KBOOT,KLOCDPAGING,Kern::Printf("Fragmentation granularity set to 0x%x", iFragmentGranularity)); + } + + return r; + } + +void DFragmentationPagingLock::Cleanup() + { + // in CS + if (iFragmentationMutex) + { + LockFragmentation(); + Free(); // at last! + UnlockFragmentation(); + Kern::SafeClose((DObject*&)iFragmentationMutex,NULL); + } + } + +#else +#if !defined(__WINS__) +EXPORT_C TInt TLocDrvRequest::WriteToPageHandler(const TAny* , TInt , TInt) + { + return KErrNone; // stub for def file + } +#endif // __WINS__ +#endif //__DEMAND_PAGING__ +/******************************************** + * Media driver base class + ********************************************/ + + + + +/** +Constructor. + +This is called, typically, by a derived class constructor in its ctor list. + +@param aMediaId The value of the unique media ID assigned when the media + driver is registered. + +@see LocDrv::RegisterMediaDevice() +*/ +EXPORT_C DMediaDriver::DMediaDriver(TInt aMediaId) + { +// iPhysicalDevice=NULL; +// iTotalSizeInBytes=0; +// iCurrentConsumption=0; +// iPrimaryMedia=NULL; +// iCritical=EFalse; + iPrimaryMedia=(DPrimaryMediaBase*)TheMedia[aMediaId]; + } + + + + +/** +Destructor. + +Sets the device's current consumption to zero, and calls Close() on +the PDD factory object. + +@see DObject::Close() +*/ +EXPORT_C DMediaDriver::~DMediaDriver() + { + SetCurrentConsumption(0); + Kern::SafeClose((DObject*&)iPhysicalDevice,NULL); + } + + + + +/** +Closes the media driver. + +This default implementation simply deletes this DMediaDriver object. + +Media drivers can provide their own implementation, which gives them +the opportunity to clean up resources before closure; for example, +cancelling a DFC. +Any replacement function must call this base class function as +the last instruction. +*/ +EXPORT_C void DMediaDriver::Close() + { + delete this; + } + + + + +/** +Sets the total size of the media device. + +The function must be called by the media driver's implementation of PartitionInfo(). + +@param aTotalSizeInBytes The total size of the media, in bytes. +@param aLocDrv This is not used by media drivers; the class + definition provides a default value. + +@see DMediaDriver::PartitionInfo() +*/ +EXPORT_C void DMediaDriver::SetTotalSizeInBytes(Int64 aTotalSizeInBytes, TLocDrv* aLocDrv) + { + iTotalSizeInBytes=aTotalSizeInBytes; + if (aLocDrv) + aLocDrv->iPartitionLen=aTotalSizeInBytes; + } + + + + +/** +Gets the total size of the media. + +@return The total size of the media, in bytes. + +@see DMediaDriver::SetTotalSizeInBytes() +*/ +EXPORT_C Int64 DMediaDriver::TotalSizeInBytes() + { + return iTotalSizeInBytes; + } + + + + +/** +Flags the media driver as entering a critical part of its processing. + +In this context, critical means that the driver must be allowed to complete +its current activity. +For example, a request to power down the device must be deferred until +the driver exits the critical part. + +@return KErrNone, if the driver has been successfully flagged as being in + a critical part; otherwise, one of the other system-wide error codes. + +@see DMediaDriver::EndInCritical() +*/ +EXPORT_C TInt DMediaDriver::InCritical() + { + if (!iCritical) + { + TInt r=iPrimaryMedia->InCritical(); + if (r!=KErrNone) + return r; + iCritical=ETrue; + } + return KErrNone; + } + + + + +/** +Flags the media driver as leaving a critical part of its processing. + +@see DMediaDriver::InCritical() +*/ +EXPORT_C void DMediaDriver::EndInCritical() + { + if (iCritical) + { + iCritical=EFalse; + iPrimaryMedia->EndInCritical(); + } + } + + + + +/** +@internalComponent +*/ +EXPORT_C void DMediaDriver::SetCurrentConsumption(TInt aValue) + { + TInt old = (TInt)__e32_atomic_swp_ord32(&iCurrentConsumption, aValue); + TInt delta = aValue - old; + iPrimaryMedia->DeltaCurrentConsumption(delta); + } + + + + +/** +Informs the media driver subsystem that an asynchronous request is complete. + +@param m The request that this call is completing. +@param aResult The return code for the asynchronous request. Typically, this + is KErrNone to report success, or one of the other system-wide + error codes to report failure or other problems. +*/ +EXPORT_C void DMediaDriver::Complete(TLocDrvRequest& m, TInt aResult) + { + CHECK_RET(aResult); +#ifdef __DEMAND_PAGING__ + if (DMediaPagingDevice::PagingRequest(m)) + { + __ASSERT_ALWAYS(iPrimaryMedia && iPrimaryMedia->iPagingMedia && iPrimaryMedia->iBody->iPagingDevice,LOCM_FAULT()); + __ASSERT_ALWAYS( ((m.Flags() & TLocDrvRequest::ECodePaging) == 0) || (m.Drive()->iPagingDrv), LOCM_FAULT()); + DMediaPagingDevice* pagingdevice = iPrimaryMedia->iBody->iPagingDevice; + pagingdevice->CompleteRequest(&m, aResult); + } + else +#endif + iPrimaryMedia->CompleteRequest(m, aResult); + + if (&m == iPrimaryMedia->iCurrentReq) // Complete() called on request serviced synchronously + iPrimaryMedia->iCurrentReq = NULL; + + iPrimaryMedia->RunDeferred(); + } + + + + +/** +Informs the media driver subsystem that the media driver is open +and has been initialised. + +This can be called from the PDD factory function Create(), if opening and +initialising the media driver is synchronous, otherwise it should be called by +the asynchronous media driver function that is responsible for opening and +initialising the driver. + +@param anError KErrNone if successful, otherwise one of the other system wide + error codes. +*/ +EXPORT_C void DMediaDriver::OpenMediaDriverComplete(TInt anError) + { + __KTRACE_OPT(KLOCDRV,Kern::Printf("DMediaDriver::OpenMediaDriverComplete(%d) this %x iPrimaryMedia %x", anError, this, iPrimaryMedia)); + DPrimaryMediaBase* pM=iPrimaryMedia; + pM->iAsyncErrorCode=anError; + pM->iAsyncDfc.Enque(); + } + + + + +/** +Informs the media driver subsystem that the media driver has completed +the provision of partition information. + +The media driver provides partition information in its implementation +of PartitionInfo(). + +@param anError KErrNone if successful, otherwise one of the other system wide + error codes. + +@see DMediaDriver::PartitionInfo() +*/ +EXPORT_C void DMediaDriver::PartitionInfoComplete(TInt anError) + { + __KTRACE_OPT(KLOCDRV,Kern::Printf("DMediaDriver::PartitionInfoComplete(%d) anError %d this %x iPrimaryMedia %x", anError, this, iPrimaryMedia)); + DPrimaryMediaBase* pM=iPrimaryMedia; + pM->iAsyncErrorCode=anError; + pM->iAsyncDfc.Enque(); + } + + + + +/** +@internalComponent +*/ +// Default implementation +EXPORT_C void DMediaDriver::Disconnect(DLocalDrive* aLocalDrive, TThreadMessage* aMsg) + { + // don't need to worry about DLocalDrive going away + aLocalDrive->Deque(); + + aMsg->Complete(KErrNone, EFalse); + } + + + + +/** +Registers a media driver with the Local Media Subsystem, and provides +information about the number of supported drives, partitions, +names and drive numbers. + +@param aDevice The unique Media ID for this device. + This can take one of the enumerated values defined + by the TMediaDevice enum. +@param aDriveCount Specifies the number of local drive objects to be assigned + to the media driver. Drives that support more than one + partition must specify a number greater than 1. +@param aDriveList A pointer to an array of TInt values, which define + the drive numbers that are to be allocated to each partition. + 0 signifies Drive C, 1 signifies drive D, etc. For example, + to allocate drive letters J and K, specify an array + containing the values [7,8]. + Note that the size of this array must be the same as the value + specified by aDriveCount. +@param aPrimaryMedia A pointer to the primary DPrimaryMedia object to be + associated with the media. This object is responsible for + the overall state of the media, i.e. powering up, reading + partition information etc. It also has overall control over + all partitions as represented by the additional (aNumMedia-1) + DMedia objects. +@param aNumMedia Specifies the total number of DMedia objects to be + associated with the media driver. This number includes the + primary DPrimaryMedia object referred to by aPrimaryMedia, + plus all of the DMedia objects that are created for each + additional drive, and which hold basic information about + partitions. +@param aName The name of the media driver, for example: PCCard + +@return KErrNone, if successful; + KErrInUse, if a drive is already in use; + KErrNoMemory, if there is insufficient memory; + or one of the other system-wide error codes. +*/ +EXPORT_C TInt LocDrv::RegisterMediaDevice(TMediaDevice aDevice, TInt aDriveCount, const TInt* aDriveList, DPrimaryMediaBase* aPrimaryMedia, TInt aNumMedia, const TDesC& aName) + { + // Create TLocDrv / DMedia objects to handle a media device + __KTRACE_OPT(KBOOT,Kern::Printf("RegisterMediaDevice %lS dev=%1d #drives=%d 1st=%d PM=%08x #media=%d",&aName,aDevice,aDriveCount,*aDriveList,aPrimaryMedia,aNumMedia)); + const TInt* p=aDriveList; + TInt i; + TInt r=0; + if (UsedMedia+aNumMedia>KMaxLocalDrives) + return KErrInUse; + for (i=0; iCreate(aDevice,i,lastMedia); + __KTRACE_OPT(KBOOT,Kern::Printf("Media %d Create() returns %d",i,r)); + if (r!=KErrNone) + return r; + } + + __KTRACE_OPT(KBOOT,Kern::Printf("FirstMedia %d LastMedia %d",UsedMedia,lastMedia)); + UsedMedia+=aNumMedia; + p=aDriveList; + for (i=0; iiPrimaryMedia=aPrimaryMedia; + __KTRACE_OPT(KBOOT,Kern::Printf("Drive %d: TLocDrv @ %08x",drv,pL)); + } + return KErrNone; + } + + + + +/** +A utility function that is used internally to register the specified +password store. + +The password store is used to save passwords for local media. + +@param aStore A pointer to the password store to be registered. + +@return KErrNone, if successful; + KErrAlreadyExists, if a password store has already been registered. +*/ +EXPORT_C TInt LocDrv::RegisterPasswordStore(TPasswordStore* aStore) + { + // Create TLocDrv / DMedia objects to handle a media device + __KTRACE_OPT(KBOOT,Kern::Printf("RegisterPasswordStore")); + + TInt r = KErrNone; + + if(ThePasswordStore == NULL) + ThePasswordStore = aStore; + else + r = KErrAlreadyExists; + + return r; + } + +/** +Returns a pointer to the registered password store. + +The password store is used to save passwords for local media. + +@return A pointer to the registered password store. +*/ +EXPORT_C TPasswordStore* LocDrv::PasswordStore() + { + return ThePasswordStore; + } + + +#ifdef __DEMAND_PAGING__ +/** +Registers a paging device with the Local Media Subsystem, and provides +information about drive numbers used in Code Paging. + +@param aPrimaryMedia A pointer to the primary DPrimaryMedia object associated + with the media. +@param aPagingDriveList A pointer to an array of TInt values, which define + the drive numbers used as Code backup in Code Paging, which + are the target of Page In requests. For NAND these will + will be usually associated with ROFS and/or User Data drives. + In ROM pagigng systems no drive is specified, it is assumed + a fixed media for which no non-primary media exists, will be + used. +@param aDriveCount Specifies the number of local drives associated with this + media device which can be used for code paging. +@param aPagingType Identifies the type of Paging this media device is capable + of servicing. +@param aReadShift Log2 of the read unit size. A read unit is the number of bytes + which the device can optimally read from the underlying media. + E.g. for small block NAND, a read unit would be equal to the + page size, 512 bytes, therefore iReadShift would be set to 9. +@param aNumPages The number of pages to alloc for each drive associated with this + media driver. The pages are used in request fragmentation. + +@return KErrNone, if successful; + KErrNotFound, if at least one of the drive numbers + specified has not yet been mapped. + KErrArgument, if the passed in an invalid argument. + KErrNotSupported, if at least one of the drive numbers + specifed is not associated with this Primary Media. + KErrNoMemory, if there is insufficient memory; + or one of the other system-wide error codes. +*/ +EXPORT_C TInt LocDrv::RegisterPagingDevice(DPrimaryMediaBase* aPrimaryMedia, const TInt* aPagingDriveList, TInt aDriveCount, TUint aPagingType, TInt aReadShift, TUint aNumPages) + { + __KTRACE_OPT2(KBOOT,KLOCDPAGING,Kern::Printf(">RegisterPagingDevice: paging type=%d PM=0x%x read shift=%d",aPagingType,aPrimaryMedia,aReadShift)); + TInt i; + + if(!aPagingType || (aPagingType&~(DPagingDevice::ERom | DPagingDevice::ECode | DPagingDevice::EData))) + { + __KTRACE_OPT2(KBOOT,KLOCDPAGING,Kern::Printf("Unsupported paging type, exiting")); + return KErrArgument; + } + + + + for(i=0; iiType&DPagingDevice::ERom) && (aPagingType & DPagingDevice::ERom)) + { + aPagingType&=~DPagingDevice::ERom; // already have a ROM paging device + __KTRACE_OPT2(KBOOT,KLOCDPAGING,Kern::Printf("Already has ROM pager on locdrv no %d",i)); + } + if ((ThePagingDevices[i]->iType&DPagingDevice::EData) && (aPagingType & DPagingDevice::EData)) + { + aPagingType&=~DPagingDevice::EData; // already have a Data paging device + __KTRACE_OPT2(KBOOT,KLOCDPAGING,Kern::Printf("Already has Data pager on locdrv no %d",i)); + } + } + + + if (aPagingType == 0) + { + // there's already a ROM or Data paging device & this doesn't support code paging so quietly exit without further addo + __KTRACE_OPT2(KBOOT,KLOCDPAGING,Kern::Printf("Nothing left to register on locdrv no %d, exiting",i)); + return KErrNone; + } + + const TInt* p=aPagingDriveList; + if(aPagingType&DPagingDevice::ECode) // supports code paging, do argument check + { + if(!aDriveCount || (aDriveCount>=KMaxLocalDrives)) + { + __KTRACE_OPT2(KBOOT,KLOCDPAGING,Kern::Printf("Invalid code paging drive count: %d", aDriveCount)); + return KErrArgument; + } + + TInt drvCount=0; + for(i=0; iiPrimaryMedia==aPrimaryMedia) + drvCount++; + if(aDriveCount>drvCount) // can't exceed number of drives registered by this device + { + __KTRACE_OPT2(KBOOT,KLOCDPAGING,Kern::Printf("Invalid code paging drive count: %d total %d", aDriveCount, drvCount)); + return KErrArgument; + } + + for (i=0; i=KMaxLocalDrives) + { + __KTRACE_OPT2(KBOOT,KLOCDPAGING,Kern::Printf("Invalid code paging drive number: %d", drv)); + return KErrArgument; + } + TLocDrv* pD=TheDrives[drv]; + if (!pD) + return KErrNotFound; + if (pD->iPrimaryMedia!=aPrimaryMedia) + return KErrNotSupported; + } + } + + + TInt firstLocalDriveNumber = KErrNotFound; + TInt romPagingDriveNumber = KErrNotFound; + + TInt dataPagingDriveNumber = KErrNotFound; + TInt swapSize = 0; + + // find the local drive assocated with the primary media + for (i=0; iiPrimaryMedia == aPrimaryMedia) + { + firstLocalDriveNumber = i; + break; + } + } + __ASSERT_ALWAYS(i < KMaxLocalDrives, LOCM_FAULT()); + __KTRACE_OPT2(KBOOT,KLOCDPAGING,Kern::Printf("DMediaPagingDevice(), firstLocalDriveNumber %d", firstLocalDriveNumber)); + + + // Send an ECaps message to wake up the media driver & ensure all partitions are + // reported, then search for paged-data or paged-ROM partitions + if ((aPagingType & DPagingDevice::EData) || + (aPagingType & DPagingDevice::ERom && aPrimaryMedia->iDfcQ && aPrimaryMedia->iMsgQ.iReady)) + { + // the message queue must have been started already (by the media driver calling iMsgQ.Receive()) + // otherwise we can't send the DLocalDrive::EQueryDevice request + if (aPrimaryMedia->iDfcQ && !aPrimaryMedia->iMsgQ.iReady) + { + __KTRACE_OPT2(KBOOT,KLOCDPAGING,Kern::Printf("RegisterPagingDevice: Message queue not started")); + return KErrNotReady; + } + + + TLocDrvRequest m; + memclr(&m, sizeof(m)); + + + // Get the Caps from the device. NB for MMC/SD we may need to retry as some PSLs start up + // in "door open" or "media not present" state which can result in the cancellation of requests + TInt i; + const TInt KRetries = 5; + TInt r = KErrNotReady; + for (i=0; r == KErrNotReady && i < KRetries; i++) + { + TBuf8 capsBuf; + capsBuf.SetMax(); + capsBuf.FillZ(); + m.Drive() = TheDrives[firstLocalDriveNumber]; + m.Id() = DLocalDrive::ECaps; + m.RemoteDes() = (TAny*)capsBuf.Ptr(); // overload this + m.Length() = KMaxLocalDriveCapsLength; // for pinning + r = aPrimaryMedia->Request(m); + +//Kern::Printf("EQueryPageDeviceInfo: i %d: r %d ", i, r); + __KTRACE_OPT2(KBOOT,KLOCDPAGING, Kern::Printf("Paging device ECaps: i %d: r %d ", i, r)); + } + + if (r != KErrNone) + return r; + + TLocDrv* drive; + for (i=0; iiPrimaryMedia == aPrimaryMedia) + { + __KTRACE_OPT2(KBOOT,KLOCDPAGING, Kern::Printf("RegisterPagingDevice: local drive %d, partition type %x size %x", i, drive->iPartitionType, I64LOW(drive->iPartitionLen))); + // ROM partition ? + if ((romPagingDriveNumber == KErrNotFound) && (drive->iPartitionType == KPartitionTypeROM)) + { + __KTRACE_OPT2(KBOOT,KLOCDPAGING, Kern::Printf("Found ROM partition on local drive %d, size %x", i, I64LOW(drive->iPartitionLen))); + romPagingDriveNumber = i; + } + // swap partition ? + else if ((dataPagingDriveNumber == KErrNotFound) && (drive->iPartitionType == KPartitionTypePagedData)) + { + __KTRACE_OPT2(KBOOT,KLOCDPAGING, Kern::Printf("Found swap partition on local drive %d, size %x", i, I64LOW(drive->iPartitionLen))); + dataPagingDriveNumber = i; + swapSize = drive->iPartitionLen >> aReadShift; + } + } + } + + if (swapSize == 0) + { + __KTRACE_OPT2(KBOOT,KLOCDPAGING, Kern::Printf("Disabling data paging")); + aPagingType &= ~DPagingDevice::EData; + } + + } + + + // create and set up a DPagingDevice to allow PageIn request servicing + DMediaPagingDevice* pagingDevice = new DMediaPagingDevice(aPrimaryMedia); + if(!pagingDevice) + { + __KTRACE_OPT2(KBOOT,KLOCDPAGING,Kern::Printf("RegisterPagingDevice: could not create paging device")); + return KErrNoMemory; + } + + pagingDevice->iType = aPagingType; + pagingDevice->iReadUnitShift = aReadShift; + + pagingDevice->iFirstLocalDriveNumber = firstLocalDriveNumber; + pagingDevice->iRomPagingDriveNumber = romPagingDriveNumber; + + pagingDevice->iDataPagingDriveNumber = dataPagingDriveNumber; + pagingDevice->iSwapSize = swapSize; + +#ifdef __DEBUG_DEMAND_PAGING__ + Kern::Printf("PagingDevice :"); + Kern::Printf("iType 0x%x\n", pagingDevice->iType); + Kern::Printf("iReadUnitShift 0x%x\n", pagingDevice->iReadUnitShift); + Kern::Printf("iFirstLocalDriveNumber 0x%x\n", pagingDevice->iFirstLocalDriveNumber); + Kern::Printf("iRomPagingDriveNumber 0x%x\n", pagingDevice->iRomPagingDriveNumber); + Kern::Printf("iDataPagingDriveNumber 0x%x\n", pagingDevice->iDataPagingDriveNumber); + Kern::Printf("iSwapSize 0x%x\n", pagingDevice->iSwapSize); +#endif + + + // This table is indexed by DPagingDevice::TType + const char* DeviceName[] = + { + "Error", + "RomPagingDevice", + "CodePagingDevice", + "RomAndCodePagingDevice", + "DataPagingDevice", + "RomAndDataPagingDevice", + "CodeAndDataPagingDevice", + "RomAndCodeAndDataPagingDevice" + }; + + + if(aPagingType & DPagingDevice::ECode) + { + for (i=0; iiDrivesSupported|=(0x1<iName = DeviceName[aPagingType]; + + if (ThePinObjectAllocator == NULL) + ThePinObjectAllocator = new DPinObjectAllocator(); + if(!ThePinObjectAllocator) + { + __KTRACE_OPT2(KBOOT,KLOCDPAGING,Kern::Printf("RegisterPagingDevice: could not create ThePinObjectAllocator")); + return KErrNoMemory; + } + TInt r = ThePinObjectAllocator->Construct(KDynamicPagingLockCount, aNumPages); + if (r != KErrNone) + { + __KTRACE_OPT2(KBOOT,KLOCDPAGING,Kern::Printf("RegisterPagingDevice: could not construct ThePinObjectAllocator")); + return r; + } + + + // Register our DPagingDevice with the Kernel + r=Kern::InstallPagingDevice(pagingDevice); + +#ifdef __DEBUG_DEMAND_PAGING__ + Kern::Printf("Kern::InstallPagingDevice() r %d", r); +#endif + + if (r!=KErrNone) + { + __KTRACE_OPT2(KBOOT,KLOCDPAGING,Kern::Printf("RegisterPagingDevice: could not install paging device")); + delete pagingDevice; + return r; + } + + // all hunky dory, save paging device and mark our media as pageable + ThePagingDevices[aPrimaryMedia->iMediaId] = pagingDevice; // association created between PrimaryMedia and PagingDevice via iMediaId + aPrimaryMedia->iPagingMedia = 1; + + // mark our drives as pageable + p=aPagingDriveList; + if (aPagingType&DPagingDevice::ECode) + { + for (i=0; iiPagingDrv=1; + } + } + + // Flags to indicate that a paging device is registered and pinning of user requests may be required + aPrimaryMedia->iPagingMedia = 1; + + // point the primary media to the paging device + aPrimaryMedia->iBody->iPagingDevice = pagingDevice; + + if (aPagingType & DPagingDevice::ERom) + { + aPrimaryMedia->iRomPagingMedia = 1; + TheRomPagingMedia = aPrimaryMedia; + } + + // Is data paging enabled in this ROM ? + TInt memModelAttributes = Kern::HalFunction(EHalGroupKernel, EKernelHalMemModelInfo, NULL, NULL); + TBool dataPagingSupported = memModelAttributes & EMemModelAttrDataPaging; +#ifdef __DEBUG_DEMAND_PAGING__ + Kern::Printf("memModelAttributes %08X", memModelAttributes); + Kern::Printf("DataPagingSupported %d", dataPagingSupported); +#endif + if (!dataPagingSupported) + { +#ifdef __DEBUG_DEMAND_PAGING__ + if (aPagingType & DPagingDevice::EData) + Kern::Printf("Disabling data paging, not supported in this ROM"); +#endif + aPagingType&= ~DPagingDevice::EData; + } + + + if (aPagingType & DPagingDevice::EData) + { + DataPagingDeviceRegistered = ETrue; + aPrimaryMedia->iDataPagingMedia = 1; + TheDataPagingMedia = aPrimaryMedia; + } + + __KTRACE_OPT2(KBOOT,KLOCDPAGING,Kern::Printf("iPrimaryMedia == aPrimaryMedia && pL->iDmaHelper == NULL) + { + pL->iDmaHelper = new DDmaHelper; + __ASSERT_ALWAYS(pL != NULL, LOCM_FAULT()); + + // if no limit stated on addressing range use 1MB + TInt MaxAddress = aDmaMaxAddressable ? (1024*1024) : aDmaMaxAddressable; + + TInt r = pL->iDmaHelper->Construct(MaxAddress, aMediaBlockSize, aDmaAlignment); + __ASSERT_ALWAYS(r == KErrNone, LOCM_FAULT()); + } + } + + return KErrNone; + } + +void GetDriveInfo(TDriveInfoV1& info) + { + TInt i; + TInt drives=0; + TUint32 sock_mask=0; + TInt sockets=0; + + info.iRegisteredDriveBitmask = 0; + + for (i=0; iiPrimaryMedia; + if (pM->IsRemovableDevice(sockNum)) + { + if (!(sock_mask & (1<iDevice, DriveNames[i])); + if ( (sockNum + 1) > sockets ) + sockets = sockNum + 1; + } + sock_mask |= (1<iDevice,DriveNames[i])); + + info.iRegisteredDriveBitmask |= (0x01 << i); + } + } + info.iTotalSupportedDrives=drives; + info.iTotalSockets=sockets; + info.iRuggedFileSystem=ETrue; + __KTRACE_OPT(KLOCDRV,Kern::Printf("Total drives=%d, sockets=%d",drives,sockets)); + } + +#if defined(__DEMAND_PAGING__) && defined(__CONCURRENT_PAGING_INSTRUMENTATION__) +void ResetConcurrencyStats(DMediaPagingDevice* aDevice, TMediaPagingStats aStats) + { + NKern::FMWait(&aDevice->iInstrumentationLock); + switch(aStats) + { + case EMediaPagingStatsRom: + aDevice->iServicingROM=0; + memclr(&aDevice->iROMStats,sizeof(SMediaROMPagingConcurrencyInfo)); + break; + case EMediaPagingStatsCode: + aDevice->iServicingCode=0; + memclr(&aDevice->iCodeStats,sizeof(SMediaCodePagingConcurrencyInfo)); + break; + case EMediaPagingStatsDataIn: + aDevice->iServicingDataIn=0; + memclr(&aDevice->iDataStats,sizeof(SMediaDataPagingConcurrencyInfo)); + break; + case EMediaPagingStatsDataOut: + aDevice->iServicingDataOut=0; + memclr(&aDevice->iDataStats,sizeof(SMediaDataPagingConcurrencyInfo)); + break; + case EMediaPagingStatsAll: + aDevice->iServicingROM=0; + aDevice->iServicingCode=0; + aDevice->iServicingDataIn=0; + aDevice->iServicingDataOut=0; + memclr(&aDevice->iROMStats,sizeof(SMediaROMPagingConcurrencyInfo)); + memclr(&aDevice->iCodeStats,sizeof(SMediaCodePagingConcurrencyInfo)); + memclr(&aDevice->iDataStats,sizeof(SMediaDataPagingConcurrencyInfo)); + break; + } + NKern::FMSignal(&aDevice->iInstrumentationLock); + } +#endif +#if defined(__DEMAND_PAGING__) && defined(__DEMAND_PAGING_BENCHMARKS__) +void ResetBenchmarkStats(DMediaPagingDevice* aDevice, TMediaPagingStats aStats) + { + NKern::FMWait(&aDevice->iInstrumentationLock); + switch(aStats) + { + case EMediaPagingStatsRom: + aDevice->iROMBenchmarkData.iCount = 0; + aDevice->iROMBenchmarkData.iTotalTime = 0; + aDevice->iROMBenchmarkData.iMaxTime = 0; + aDevice->iROMBenchmarkData.iMinTime = KMaxTInt; + break; + case EMediaPagingStatsCode: + aDevice->iCodeBenchmarkData.iCount = 0; + aDevice->iCodeBenchmarkData.iTotalTime = 0; + aDevice->iCodeBenchmarkData.iMaxTime = 0; + aDevice->iCodeBenchmarkData.iMinTime = KMaxTInt; + break; + case EMediaPagingStatsDataIn: + aDevice->iDataInBenchmarkData.iCount = 0; + aDevice->iDataInBenchmarkData.iTotalTime = 0; + aDevice->iDataInBenchmarkData.iMaxTime = 0; + aDevice->iDataInBenchmarkData.iMinTime = KMaxTInt; + break; + case EMediaPagingStatsDataOut: + aDevice->iDataOutBenchmarkData.iCount = 0; + aDevice->iDataOutBenchmarkData.iTotalTime = 0; + aDevice->iDataOutBenchmarkData.iMaxTime = 0; + aDevice->iDataOutBenchmarkData.iMinTime = KMaxTInt; + break; + case EMediaPagingStatsAll: + aDevice->iDataInBenchmarkData.iCount = 0; + aDevice->iDataInBenchmarkData.iTotalTime = 0; + aDevice->iDataInBenchmarkData.iMaxTime = 0; + aDevice->iDataInBenchmarkData.iMinTime = KMaxTInt; + + aDevice->iDataOutBenchmarkData.iCount = 0; + aDevice->iDataOutBenchmarkData.iTotalTime = 0; + aDevice->iDataOutBenchmarkData.iMaxTime = 0; + aDevice->iDataOutBenchmarkData.iMinTime = KMaxTInt; + + aDevice->iROMBenchmarkData.iCount = 0; + aDevice->iROMBenchmarkData.iTotalTime = 0; + aDevice->iROMBenchmarkData.iMaxTime = 0; + aDevice->iROMBenchmarkData.iMinTime = KMaxTInt; + + aDevice->iCodeBenchmarkData.iCount = 0; + aDevice->iCodeBenchmarkData.iTotalTime = 0; + aDevice->iCodeBenchmarkData.iMaxTime = 0; + aDevice->iCodeBenchmarkData.iMinTime = KMaxTInt; + break; + } + NKern::FMSignal(&aDevice->iInstrumentationLock); + } +#endif + +TInt MediaHalFunction(TAny*, TInt aFunction, TAny* a1, TAny* a2) + { + TInt r=KErrNotSupported; + switch (aFunction) + { + case EMediaHalDriveInfo: + { + (void) a2; + TDriveInfoV1Buf infoBuf; + TDriveInfoV1& info=infoBuf(); + GetDriveInfo(info); + Kern::InfoCopy(*(TDes8*)a1,infoBuf); + r=KErrNone; + break; + } +#if defined(__DEMAND_PAGING__) && defined(__CONCURRENT_PAGING_INSTRUMENTATION__) + case EMediaHalGetROMConcurrencyInfo: + { + TInt drvNo=(TInt)a1; + TLocDrv* drv=TheDrives[drvNo]; + if(!drv) + break; + DMediaPagingDevice* device = drv->iPrimaryMedia->iBody->iPagingDevice; + if(!device) + break; + NKern::FMWait(&device->iInstrumentationLock); + SMediaROMPagingConcurrencyInfo info=device->iROMStats; + NKern::FMSignal(&device->iInstrumentationLock); + kumemput32(a2,&info,sizeof(info)); + r=KErrNone; + break; + } + case EMediaHalGetCodeConcurrencyInfo: + { + TInt drvNo=(TInt)a1; + TLocDrv* drv=TheDrives[drvNo]; + if(!drv) + break; + DMediaPagingDevice* device=drv->iPrimaryMedia->iBody->iPagingDevice; + if(!device) + break; + NKern::FMWait(&device->iInstrumentationLock); + SMediaCodePagingConcurrencyInfo info=device->iCodeStats; + NKern::FMSignal(&device->iInstrumentationLock); + kumemput32(a2,&info,sizeof(info)); + r=KErrNone; + break; + } + case EMediaHalGetDataConcurrencyInfo: + { + TInt drvNo=(TInt)a1; + TLocDrv* drv=TheDrives[drvNo]; + if(!drv) + break; + DMediaPagingDevice* device = drv->iPrimaryMedia->iBody->iPagingDevice; + if(!device) + break; + NKern::FMWait(&device->iInstrumentationLock); + SMediaDataPagingConcurrencyInfo info=device->iDataStats; + NKern::FMSignal(&device->iInstrumentationLock); + kumemput32(a2,&info,sizeof(info)); + r=KErrNone; + break; + } + case EMediaHalResetConcurrencyInfo: + { + TInt drvNo=(TInt)a1; + TLocDrv* drv=TheDrives[drvNo]; + if(!drv) + break; + DMediaPagingDevice* device=drv->iPrimaryMedia->iBody->iPagingDevice; + if(!device) + break; + TUint index=(TInt)a2; + if(index>EMediaPagingStatsCode) + break; + ResetConcurrencyStats(device, (TMediaPagingStats)index); + r=KErrNone; + break; + } +#endif +#if defined(__DEMAND_PAGING__) && defined(__DEMAND_PAGING_BENCHMARKS__) + case EMediaHalGetROMPagingBenchmark: + { + TInt drvNo=(TInt)a1; + TLocDrv* drv=TheDrives[drvNo]; + if(!drv) + break; + DMediaPagingDevice* device=drv->iPrimaryMedia->iBody->iPagingDevice; + if(!device) + break; + NKern::FMWait(&device->iInstrumentationLock); + SPagingBenchmarkInfo info = device->iROMBenchmarkData; + NKern::FMSignal(&device->iInstrumentationLock); + kumemput32(a2,&info,sizeof(info)); + r=KErrNone; + break; + } + case EMediaHalGetCodePagingBenchmark: + { + TInt drvNo=(TInt)a1; + TLocDrv* drv=TheDrives[drvNo]; + if(!drv) + break; + DMediaPagingDevice* device=drv->iPrimaryMedia->iBody->iPagingDevice; + if(!device) + break; + NKern::FMWait(&device->iInstrumentationLock); + SPagingBenchmarkInfo info = device->iCodeBenchmarkData; + NKern::FMSignal(&device->iInstrumentationLock); + kumemput32(a2,&info,sizeof(info)); + r=KErrNone; + break; + } + case EMediaHalGetDataInPagingBenchmark: + { + TInt drvNo=(TInt)a1; + TLocDrv* drv=TheDrives[drvNo]; + if(!drv) + break; + DMediaPagingDevice* device=drv->iPrimaryMedia->iBody->iPagingDevice; + if(!device) + break; + NKern::FMWait(&device->iInstrumentationLock); + SPagingBenchmarkInfo info = device->iDataInBenchmarkData; + NKern::FMSignal(&device->iInstrumentationLock); + kumemput32(a2,&info,sizeof(info)); + r=KErrNone; + break; + } + case EMediaHalGetDataOutPagingBenchmark: + { + TInt drvNo=(TInt)a1; + TLocDrv* drv=TheDrives[drvNo]; + if(!drv) + break; + DMediaPagingDevice* device=drv->iPrimaryMedia->iBody->iPagingDevice; + if(!device) + break; + NKern::FMWait(&device->iInstrumentationLock); + SPagingBenchmarkInfo info = device->iDataOutBenchmarkData; + NKern::FMSignal(&device->iInstrumentationLock); + kumemput32(a2,&info,sizeof(info)); + r=KErrNone; + break; + } + case EMediaHalResetPagingBenchmark: + { + TInt drvNo=(TInt)a1; + TLocDrv* drv=TheDrives[drvNo]; + if(!drv) + break; + DMediaPagingDevice* device=drv->iPrimaryMedia->iBody->iPagingDevice; + if(!device) + break; + TUint index=(TInt)a2; + if(index>EMediaPagingStatsCode) + break; + ResetBenchmarkStats(device, (TMediaPagingStats)index); + r=KErrNone; + break; + } + case EMediaHalGetPagingInfo: + { + TInt drvNo=(TInt)a1; + TLocDrv* drv=TheDrives[drvNo]; + if(!drv) + break; + DMediaPagingDevice* device=drv->iPrimaryMedia->iBody->iPagingDevice; + if(!device) + break; + NKern::FMWait(&device->iInstrumentationLock); + SMediaPagingInfo info = device->iMediaPagingInfo; + NKern::FMSignal(&device->iInstrumentationLock); + kumemput32(a2,&info,sizeof(info)); + r=KErrNone; + break; + } +#endif + default: + break; + } + return r; + } + + +/****************************************************************************** + Partition table scanner + ******************************************************************************/ + +#ifdef _DEBUG +#define DMEMDUMP(base,size) DbgMemDump((TLinAddr)base,size) +void DbgMemDump(TLinAddr aBase, TInt aSize) + { + TInt off; + const TUint8* p=(const TUint8*)aBase; + NKern::Lock(); + for (off=0; off0, LOCM_FAULT()); + memclr(this, sizeof(TPartitionTableScanner)); + iLBA = -1; + iSectorBuffer = aSectorBuffer; + iFirstEntry = aEntry; + iNextEntry = aEntry; + iLimit = aEntry + aMaxPartitions; + iMediaSize = aMediaSize; + } + +EXPORT_C TInt TPartitionTableScanner::NumberOfPartitionsFound() const + { + TInt n = iNextEntry - iFirstEntry; + __KTRACE_OPT(KLOCDRV, Kern::Printf("TPartitionTableScanner N=%d", n)); + return n; + } + +TPartitionTableScanner::SPart::SPart(const TUint8* a) + { + iBootInd = a[0]; + iType = a[4]; + iRSS = a[8]|(a[9]<<8)|(a[10]<<16)|(a[11]<<24); + iSectors = a[12]|(a[13]<<8)|(a[14]<<16)|(a[15]<<24); + __KTRACE_OPT(KLOCDRV, Kern::Printf("SPart: BI=%02x TYPE=%02x RSS=%08x SIZE=%08x", iBootInd, iType, iRSS, iSectors)); + } + +TInt TPartitionTableScanner::MakeEntry(const SPart& a) + { + if (iNextEntry == iLimit) + return KErrOverflow; + if (a.iRSS<=0 || a.iSectors<=0 || a.iRSS>=iMediaSize) + return KErrCorrupt; + if (TUint64(a.iRSS) + TUint64(a.iSectors) > TUint64(iMediaSize)) + return KErrCorrupt; + iNextEntry->iBootIndicator = a.iBootInd; + iNextEntry->iPartitionType = a.iType; + iNextEntry->iPartitionBaseAddr = TInt64(a.iRSS)<iPartitionLen = TInt64(a.iSectors)<TPartitionTableScanner iLBA=%08x %08x", I64HIGH(iLBA), I64LOW(iLBA))); + TInt r; + TUint8* b = iSectorBuffer; + TUint8* pS = b + 0x1be; + TUint8* pE = pS + 64; + TUint8* p = pS; + TInt orig_sp = iStackPointer; + TInt sp; + if (iLBA < 0) + { + iLBA = 0; + goto end; + } + __KTRACE_OPT(KLOCDRV,DMEMDUMP(b, ESectorSize)); + if (b[ESectorSize-2]!=0x55 || b[ESectorSize-1]!=0xaa) + { + __KTRACE_OPT(KLOCDRV, Kern::Printf("Bad signature")); + iLBA = KErrCorrupt; + goto end; + } + if (iLBA==0 && iNextEntry==iFirstEntry) + { + // Look for bootable partition first + for (; p0) + { + p[4] = 0; + r = MakeEntry(pt); + if (r!=KErrNone) + { + iLBA = r; + goto end; + } + } + } + } + // Look for extended partitions + for (p=pE-16; p>=pS; p-=16) + { + SPart pt(p); + if ((pt.iType==0x05 || pt.iType==0x0f) && pt.iSectors>0) + { + // This one is an EBR + p[4] = 0; + if (iStackPointer == EMaxNest) + { + if (iStackPointer == orig_sp) + continue; + --iStackPointer; + for(sp = orig_sp; sp0) + { + pt.iRSS += TUint32(iLBA); // data partitions are specified relative to the EBR they appear in + r = MakeEntry(pt); + if (r!=KErrNone) + { + iLBA = r; + goto end; + } + } + } + // If any EBRs on stack, pop off the first and process it + if (iStackPointer) + { + --iStackPointer; + iLBA = iFirstEBR + iStack[iStackPointer].iRSS; // LBA of second and subsequent EBR is specified relative to first EBR + if (!iFirstEBR) + iFirstEBR = iLBA; + } + else + iLBA = KErrEof; // finished + +end: + __KTRACE_OPT(KLOCDRV, Kern::Printf("iDmaHelper->GetPhysicalAddress(aAddr, aLen); + } + else + { + return KErrNotSupported; + } + } + + +/****************************************************************************** + Entry point + ******************************************************************************/ +DECLARE_STANDARD_EXTENSION() + { + __KTRACE_OPT(KBOOT,Kern::Printf("Starting LOCMEDIA extension")); + + // install the HAL function + TInt r=Kern::AddHalEntry(EHalGroupMedia,MediaHalFunction,NULL); +#ifdef __DEMAND_PAGING__ + if (r==KErrNone) + { + __KTRACE_OPT(KBOOT,Kern::Printf("Creating LocDrv device")); + DLocalDriveFactory* device = new DLocalDriveFactory; + if (device==NULL) + r=KErrNoMemory; + else + r=Kern::InstallLogicalDevice(device); + __KTRACE_OPT(KBOOT,Kern::Printf("Installing LocDrv device in kernel returned %d",r)); + } +#endif // __DEMAND_PAGING__ + return r; + } + +