// Copyright (c) 1995-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:
// e32test\mediext\d_nfe.cpp
// 
//

#include <drivers/locmedia.h>
#include <platform.h>
#include <variantmediadef.h>
#include "nfe.h"

#if defined(_DEBUG)
	#define TRACE_ENABLED
#else
#endif

#if defined(TRACE_ENABLED)
#define __KTRACE_PRINT(p) {p;}
#else
#define __KTRACE_PRINT(p)
#endif




// Variant parameters for test Media Extension Driver


const TInt KNfeThreadPriority = 24;	        // same as file server
const TInt KNfeDiskOpReady = 100;       //100%
//const TInt KNfeDiskOpStart = 0;         //0%

_LIT(KPddName, "Media.NFE");
#define NFE_DRIVENAME "NFE"
#define NFE_NUMMEDIA 1

// Define the array of local drives which we're attaching to
__ASSERT_COMPILE(sizeof(TNfeDeviceInfo) <= 256);	// KMaxQueryDeviceLength

// Define the array of local code-paging drives which we're attaching to
#ifdef __DEMAND_PAGING__
	__ASSERT_COMPILE(NFE_PAGEDRIVECOUNT <= TNfeDeviceInfo::ENfeMaxPartitionEntries);
	__ASSERT_COMPILE(NFE_DRIVECOUNT >= NFE_PAGEDRIVECOUNT);
	#define	SECTOR_SHIFT 9
#endif	// #ifdef __DEMAND_PAGING__




class DPrimaryMediaExt : public DPrimaryMediaBase
	{
public:
	DPrimaryMediaExt(TInt aInstance);
public:
	TInt iInstance;
	TDfcQue iNfeDfcQ;
	};



// Get the number of drives in the drive array belonging to this instance 
TInt DriveCount(TInt aInstance)
	{
	static const TInt NfeInstanceDriveCounts[NFE_INSTANCE_COUNT]={NFE_INSTANCE_DRIVE_COUNTS};
	return NfeInstanceDriveCounts[aInstance];
	}

// Get a pointer to the first drive in the drive array belonging to this instance 
const TInt* DriveList(TInt aInstance)
	{
	static const TInt NfeDriveNumbers[NFE_DRIVECOUNT]={NFE_DRIVELIST};
	TInt driveListOffset = 0;
	for (TInt n=0; n<aInstance; n++)
		driveListOffset+= DriveCount(n);
	return  NfeDriveNumbers + driveListOffset;
	}

const TInt* DriveLetterList(TInt aInstance)
	{
	static const TInt NfeDriveLetters[NFE_DRIVECOUNT]={NFE_DRIVELETTERLIST};
	TInt driveListOffset = 0;
	for (TInt n=0; n<aInstance; n++)
		driveListOffset+= DriveCount(n);
	return  NfeDriveLetters + driveListOffset;
	}

TInt DriveLetter(TInt aIndex)
	{
	static const TInt NfeDriveLetters[NFE_DRIVECOUNT]={NFE_DRIVELETTERLIST};
	return NfeDriveLetters[aIndex];
	}

TChar DriveLetterToAscii(TInt aDriveLetter)
	{
	return aDriveLetter >= 0 && aDriveLetter <= 25 ? aDriveLetter +'A' : '?';
	}

#ifdef __DEMAND_PAGING__
	// Get the number of drives in the paged drive array belonging to this instance 
	TInt PageDriveCount(TInt aInstance)
		{
	#if NFE_PAGEDRIVECOUNT > 0
		static const TInt NfeInstancePageDriveCounts[NFE_INSTANCE_COUNT]={NFE_INSTANCE_PAGEDRIVE_COUNTS};
		return NfeInstancePageDriveCounts[aInstance];
	#else
		return 0;
	#endif
		}

	// Get a pointer to the first drive in the paged drive array belonging to this instance 
	const TInt* PageDriveList(TInt aInstance)
		{
	#if NFE_PAGEDRIVECOUNT > 0
		static const TInt NfePageDriveNumbers[NFE_PAGEDRIVECOUNT]={NFE_PAGEDRIVELIST};
		TInt driveListOffset = 0;
		for (TInt n=0; n<aInstance; n++)
			driveListOffset+= PageDriveCount(n);
		return  NfePageDriveNumbers + driveListOffset;
	#else
		return NULL;
	#endif
		}

	// Get the number of paging type belonging to this instance 
	TInt PagingType(TInt aInstance)
		{
	#if NFE_PAGEDRIVECOUNT > 0
		static const TInt NfeInstancePagingType[NFE_INSTANCE_COUNT]={NFE_INSTANCE_PAGING_TYPE};
		return NfeInstancePagingType[aInstance];
	#else
		return 0;
	#endif
		}

	// get the instance of the swap drive
	TInt SwapInstance()
		{
		for (TInt i=0; i<NFE_INSTANCE_COUNT; i++)
			if (PagingType(i) & DPagingDevice::EData)
				return i;
		return KErrNotFound;
		}
#endif	// #ifdef __DEMAND_PAGING__


const char* DriveStatus(TNfeDiskStatus aStatus)
	{
	const char* KNfeUnmounted = "Unmounted";
	const char* KNfeDecrypted = "Decrypted";
	const char* KNfeDecrypting = "Decrypting";
	const char* KNfeEncrypted = "Encrypted";
	const char* KNfeEncrypting = "Encrypting";
	const char* KNfeWiping = "Wiping";
	const char* KNfeCorrupted = "Corrupted";
	const char* KNfeUnrecognised = "Unrecognised";

	switch(aStatus)
		{
		case ENfeUnmounted:
			return KNfeUnmounted;
		case ENfeDecrypted:
			return KNfeDecrypted;
		case ENfeDecrypting:
			return KNfeDecrypting;
		case ENfeEncrypted:
			return KNfeEncrypted;
		case ENfeEncrypting:
			return KNfeEncrypting;
		case ENfeWiping:
			return KNfeWiping;
		case ENfeCorrupted:
			return KNfeCorrupted;
		default:
			return KNfeUnrecognised;

		}
	}


DPrimaryMediaExt::DPrimaryMediaExt(TInt aInstance) : iInstance(aInstance)
	{
	}


#define NFE_FAULT()	Kern::Fault("NFEMEDIA",__LINE__)

// disk encryption/decryption/wiping is only performed after the following period of inactivity
// NB USB Mass Storage tends to 'poll' the media driver by sending ECaps every second or so, so we need
// to ensure this timeout period is significantly less to ensure the timer DFC thread gets a chance to run...
const TInt KNotBusyInterval = 200;		// 200 mS



class DPhysicalDeviceMediaNFE : public DPhysicalDevice
	{
public:
	DPhysicalDeviceMediaNFE();
	virtual TInt Install();
	virtual void GetCaps(TDes8& aDes) const;
	virtual TInt Create(DBase*& aChannel, TInt aMediaId, const TDesC8* anInfo, const TVersion& aVer);
	virtual TInt Validate(TInt aDeviceType, const TDesC8* anInfo, const TVersion& aVer);
	virtual TInt Info(TInt aFunction, TAny* a1);
	};
								

class DMediaDriverNFE : public DMediaDriverExtension
	{
public:
	class TPropertyObserver
		{
	public:
		void Close();
		static void PropertySubsCompleteFn(TAny* aPtr, TInt aReason);
	public:
		TInt iDriveIndex;
		DMediaDriverNFE* iMediaExt;
		RPropertyRef iProperty;
		TPropertySubsRequest* iPropertySubsRequest;
		TDfc* iPropertyDfc;	// N.B. subscription call backs don't occur in our thread context, hence the need for this DFC
		TInt iValue;
		};

public:
	 DMediaDriverNFE(TInt aMediaId);
	~DMediaDriverNFE();

	// replacing pure virtual
	virtual TInt Request(TLocDrvRequest& aRequest);
	virtual TInt PartitionInfo(TPartitionInfo &anInfo);

	TInt DoCreate(TInt aMediaId);
	void Close();

	TNfeDriveInfo*  GetSwapDrive();

private:
	TInt HandleRead(TLocDrvRequest& aRequest);
	TInt HandleWrite(TLocDrvRequest& aRequest);
	TInt HandleFormat(TLocDrvRequest& aRequest);
	TInt HandleCaps(TLocDrvRequest& aReq);


	void EncryptBuffer(TDes8& aBuffer);
	void DecryptBuffer(TDes8& aBuffer);

	inline TUint8 EncryptByte(TUint8 aByte) {return (TUint8) (aByte ^ 0xDD);}
	inline TUint8 DecryptByte(TUint8 aByte) {return (TUint8) (aByte ^ 0xDD);}
	inline TInt DriveIndex(TInt aDriveNum) {return iDriveNumToIndex[aDriveNum];}

	static void IdleTimerCallBack(TAny* aMediaDriver);
	static void TimerDfcFunction(TAny* aMediaDriver);

	// Publish & Subscribe stuff - used to listen to requests from UI 
	static void FromUiPropertyDfcFunction(TAny* aObserver);
	void FromUiPropertyDfc(TPropertyObserver& aObserver);

	// Publish & Subscribe stuff - used to listen to status setting from other NFE drives
	static void StatusToUiPropertyDfcFunction(TAny* aObserver);
	void StatusToUiPropertyDfc(TPropertyObserver& aObserver);

	void StartEncrypting();
	void StartDecrypting();

	TInt HandleDiskContent();	// called from idle timer DFC

	TNfeDriveInfo* NextDrive();
	
	TBool AdjustRequest(TNfeDriveInfo*& aDriveInfo, TInt64& aCurrentPos, TInt64& aCurrentLen);

	void SetStatus(TNfeDriveInfo& aDi, TNfeDiskStatus aStatus);

	TBool ValidBootSector(TUint8* aBuffer);
	TUint32 VolumeId(TUint8* aBuffer);
	void CheckBootSector(TNfeDriveInfo &aDriveInfo);
	TInt WriteEncryptionStatusToBootSector(TNfeDriveInfo &aDi, TBool aFinalised = EFalse);

private:
	TInt iInstance;		// media drive instance

	// A local buffer use for encryting / decrypting
	// For paging requests we need this to be page aligned, so allocate enough to cater for 
	// the worst case of up to 4K being wasted at the start of the buffer and the end
	enum {KSectorSize = 512, KPageSize = 4096, KBufSize = 65536};
	TUint8 iNonPageAlignedBuffer[KBufSize + KPageSize*2];
	// a pointer to the start of the first page in iNonPageAlignedBuffer
	TUint8* iBuffer;

	
	// Idle timer & DFC for kicking an encryption pass
	NTimer iIdleTimer;
	TDfc iTimerDfc;

	TInt iDriveIndex;								// index of local drive number currently being encrypted
	TInt iDriveNumToIndex[KMaxPartitionEntries];	// maps drive numbers to index

	TBool iBusy;

	const TInt* iDriveList;	// pointer into the drives in NFE_DRIVELIST belonging to this media driver
	const TInt* iDriveLetterList;	// pointer into the drive letter in NFE_DRIVELETTERLIST belonging to this media driver

	// Publish & subscribe stuff which handles drive command notification events from the UI
	TPropertyObserver iFromUiPropertyObserver[NFE_DRIVECOUNT];

	// Publish & subscribe stuff which handles drive status notification events from the other NFE drives
	TPropertyObserver iStatusToUiPropertyObserver[NFE_DRIVECOUNT];

	TBool iDriveFinalised;

public:
	// Partition information etc for drives this driver is attached to
	TNfeDeviceInfo iInfo;
	};



class TBootSectorStatus
	{
public:
	TUint8 iFatBootSectorData[128];
	
	enum {ENfeBootSectorSignature = 0x2045464E};	// 'NFE '
	TUint32 iSignature;

	TNfeDiskStatus iStatus;
	TBool iFinalised;
	TInt64 iEncryptEndPos;	// position of the last encrypted byte +1. Only written when device is powered down
	};


DPhysicalDeviceMediaNFE::DPhysicalDeviceMediaNFE()
	{
	__KTRACE_PRINT(Kern::Printf(": DPhysicalDeviceMediaNFE::DPhysicalDeviceMediaNFE()"));
	iUnitsMask=0x1;
	iVersion=TVersion(KMediaDriverInterfaceMajorVersion,KMediaDriverInterfaceMinorVersion,KMediaDriverInterfaceBuildVersion);
	}

/**
Install the Internal NFE PDD.
*/
TInt DPhysicalDeviceMediaNFE::Install()
	{
	__KTRACE_PRINT(Kern::Printf(": TInt DPhysicalDeviceMediaNFE::Install()"));

	return SetName(&KPddName);
	}

void DPhysicalDeviceMediaNFE::GetCaps(TDes8& /*aDes*/) const
	{
	}

/**
Create an NFE media driver.
*/
TInt DPhysicalDeviceMediaNFE::Create(DBase*& aChannel, TInt aMediaId, const TDesC8* /* anInfo */,const TVersion &aVer)
	{
	__KTRACE_PRINT(Kern::Printf(": DPhysicalDeviceMediaNFE::Create()"));

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

	TInt r=KErrNoMemory;

	DMediaDriverNFE* pD = new DMediaDriverNFE(aMediaId);
	aChannel=pD;
	if (pD)
		r=pD->DoCreate(aMediaId);

	if (r == KErrNone)
		pD->OpenMediaDriverComplete(KErrNone);

	return r;
	}

TInt DPhysicalDeviceMediaNFE::Validate(TInt aDeviceType, const TDesC8* /*anInfo*/, const TVersion& aVer)
	{
	TInt r;
	if (!Kern::QueryVersionSupported(iVersion,aVer))
		r = KErrNotSupported;
	else if (aDeviceType == MEDIA_DEVICE_NFE)
		return r = KErrNone;
	else
		r = KErrNotSupported;

//	__KTRACE_PRINT(Kern::Printf("DPhysicalDeviceMediaNFE::Validate() aDeviceType %d NfeDeviceType %d r %d", aDeviceType, MEDIA_DEVICE_NFE, r));
	return r;
	}

TInt DPhysicalDeviceMediaNFE::Info(TInt aFunction, TAny*)
//
// Return the priority of this media driver
//
	{
//	__KTRACE_PRINT(Kern::Printf(": DPhysicalDeviceMediaNFE::Info()"));

	if (aFunction==EPriority)
		return KMediaDriverPriorityNormal;

	if (aFunction==EMediaDriverPersistent)
		return KErrNone;

	return KErrNotSupported;
	}

DMediaDriverNFE::DMediaDriverNFE(TInt aMediaId) :
	DMediaDriverExtension(aMediaId),
	iInstance(((DPrimaryMediaExt*) iPrimaryMedia)->iInstance),
	iIdleTimer(IdleTimerCallBack,this),
	iTimerDfc(TimerDfcFunction,this,2),
	iDriveList (DriveList(iInstance)),
	iDriveLetterList (DriveLetterList(iInstance))
	{
	__KTRACE_PRINT(Kern::Printf("NFE%d: DMediaDriverNFE::DMediaDriverNFE()", iInstance));
	iInfo.iDriveCount = DriveCount(iInstance);

	__ASSERT_ALWAYS(Kern::RoundToPageSize(1) == KPageSize, NFE_FAULT());

	// Align the buffer to a page boundary to improve efficiency for paging requests
	iBuffer = &iNonPageAlignedBuffer[0];
	iBuffer = (TUint8*) ((((TUint32) &iNonPageAlignedBuffer[0]) + KPageSize-1) & ~(KPageSize-1));
	}

DMediaDriverNFE::~DMediaDriverNFE()
//
// Destructor.
//
	{
	__KTRACE_PRINT(Kern::Printf("NFE%d: DMediaDriverNFE::~DMediaDriverNFE()", iInstance));

	TInt i;
	for (i=0; i<TNfeDeviceInfo::ENfeMaxPartitionEntries; i++)
		{
		RPropertyRef* property = (RPropertyRef*) iInfo.iDrives[i].iStatusToUiProperty;
		if (property)
			{
			property->Delete();
			delete property;
			}
		property = (RPropertyRef*) iInfo.iDrives[i].iToUiProperty;
		if (property)
			{
			property->Delete();
			delete property;
			}
		property = (RPropertyRef*) iInfo.iDrives[i].iProgressToUiProperty;
		if (property)
			{
			property->Delete();
			delete property;
			}
		}

	for (i=0; i<NFE_DRIVECOUNT; i++)
		{
		iFromUiPropertyObserver[i].Close();
		iStatusToUiPropertyObserver[i].Close();
		}
	}


TInt CreateKey(RPropertyRef*& aProperty, TUint aKey)
	{
	aProperty = new RPropertyRef;
	if (aProperty == NULL)
		return KErrNoMemory;
	TInt r = aProperty->Attach(KNfeUID, aKey);
	if (r != KErrNone)
		return r;

    static _LIT_SECURITY_POLICY_PASS(KPassPolicy);
	r = aProperty->Define( RProperty::EInt, KPassPolicy, KPassPolicy );
	if (r != KErrNone && r != KErrAlreadyExists)
		return r;
	return KErrNone;
	}

TInt DMediaDriverNFE::DoCreate(TInt /*aMediaId*/)
//
// Create the media driver.
//
	{
	__KTRACE_PRINT(Kern::Printf("NFE%d: TInt DMediaDriverNFE::DoCreate()", iInstance));

	// Associate the idle timer DFC with our thread
	iTimerDfc.SetDfcQ(iPrimaryMedia->iDfcQ);

	// Publish & Subscribe stuff - used to initiate an encryption pass from the test app
	static _LIT_SECURITY_POLICY_PASS(KPassPolicy);
	TInt r;
	TInt i;

	TInt swapInstance = KErrNotFound;
#if defined (__DEMAND_PAGING__)
	swapInstance = SwapInstance();
#endif

	// **************************************************************************************
	// Set up P&S publishers so we can publish the status for our drives
	// **************************************************************************************
	__KTRACE_PRINT(Kern::Printf("NFE%d: Setting up StatusToUi, ToUi, ProgressToUi P&S publisher & FromUi P&S observer", iInstance));

	for (i = 0; i<DriveCount(iInstance); i++)
		{
		__KTRACE_PRINT(Kern::Printf("NFE%d:drive index %d", iInstance, i));
		TInt driveLetter = iDriveLetterList[i];
		__KTRACE_PRINT(Kern::Printf("NFE%d:drive letter %c", iInstance, (TInt) DriveLetterToAscii(driveLetter)));

		// no point setting up P&S for the swap drive
		if (driveLetter == -1)
			{
			__KTRACE_PRINT(Kern::Printf("NFE%d: i %d, Skipping P&S for swap partition", iInstance, i));
			continue;
			}

		r = CreateKey((RPropertyRef*&) iInfo.iDrives[i].iStatusToUiProperty, NFE_KEY(driveLetter, KNfeStatusToUiKey));
		if (r != KErrNone)
			return r;

		r = CreateKey((RPropertyRef*&) iInfo.iDrives[i].iToUiProperty, NFE_KEY(driveLetter, KNfeToUiKey));
		if (r != KErrNone)
			return r;

		r = CreateKey((RPropertyRef*&) iInfo.iDrives[i].iProgressToUiProperty, NFE_KEY(driveLetter, KNfeProgressToUiKey));
		if (r != KErrNone)
			return r;

		TPropertyObserver& observer = iFromUiPropertyObserver[i];
		observer.iDriveIndex = i;
		observer.iMediaExt = this;
		observer.iPropertySubsRequest = new TPropertySubsRequest(TPropertyObserver::PropertySubsCompleteFn, &observer);
		if (observer.iPropertySubsRequest == NULL)
			return KErrNoMemory;

		observer.iPropertyDfc = new TDfc(FromUiPropertyDfcFunction,&observer,iPrimaryMedia->iDfcQ,2);
		if (observer.iPropertyDfc == NULL)
			return KErrNoMemory;
		
		r = observer.iProperty.Attach(KNfeUID, NFE_KEY(driveLetter, KNfeToThreadKey));
		if (r != KErrNone)
			return r;
		r = observer.iProperty.Define(
			RProperty::EInt,
			KPassPolicy, 
			KPassPolicy);
		if (r != KErrNone && r != KErrAlreadyExists)
			return r;

		r = observer.iProperty.Subscribe(*observer.iPropertySubsRequest);
		if (r != KErrNone)
			return r;
		}

	// **************************************************************************************
	// If this instance owns the swap partition,
	// set up P&S listeners so we can get status notification events from the other drives
	// **************************************************************************************
	__KTRACE_PRINT(Kern::Printf("NFE%d: Setting up StatusToUi P&S observer", iInstance));

	for (i = 0; i < (iInstance == swapInstance ? NFE_DRIVECOUNT : -1); i++)
		{
		__KTRACE_PRINT(Kern::Printf("NFE%d:drive index %d", iInstance, i));
		__KTRACE_PRINT(Kern::Printf("NFE%d:drive letter %c", iInstance, (TInt) DriveLetterToAscii(DriveLetter(i))));

		// no point setting up P&S for the swap drive
		if (DriveLetter(i) == -1)
			{
			__KTRACE_PRINT(Kern::Printf("NFE%d: i %d, Skipping StatusToUi P&S observer for swap partition", iInstance, i));
			continue;
			}

		__KTRACE_PRINT(Kern::Printf("NFE%d: i %d, Setting up StatusToUi P&S observer for drive %c", iInstance, i, (TInt) DriveLetterToAscii(DriveLetter(i))));
		TPropertyObserver& observer = iStatusToUiPropertyObserver[i];
		observer.iDriveIndex = i;
		observer.iMediaExt = this;
		observer.iPropertySubsRequest = new TPropertySubsRequest(TPropertyObserver::PropertySubsCompleteFn, &observer);
		if (observer.iPropertySubsRequest == NULL)
			return KErrNoMemory;

		observer.iPropertyDfc = new TDfc(StatusToUiPropertyDfcFunction,&observer,iPrimaryMedia->iDfcQ,2);
		if (observer.iPropertyDfc == NULL)
			return KErrNoMemory;
		
		r = observer.iProperty.Attach(KNfeUID, NFE_KEY(DriveLetter(i), KNfeStatusToUiKey));
		if (r != KErrNone)
			return r;
		r = observer.iProperty.Define(
			RProperty::EInt,
			KPassPolicy, 
			KPassPolicy);
		if (r != KErrNone && r != KErrAlreadyExists)
			return r;

		r = observer.iProperty.Subscribe(*observer.iPropertySubsRequest);
		if (r != KErrNone)
			return r;
		}

	return(KErrNone);
	}

void DMediaDriverNFE::TPropertyObserver::Close()
	{
	iProperty.Close();
	delete iPropertyDfc;
	iPropertyDfc = NULL;
	delete iPropertySubsRequest;
	iPropertySubsRequest = NULL;
	}

void DMediaDriverNFE::TPropertyObserver::PropertySubsCompleteFn(TAny* aPtr, TInt /*aReason*/)
	{
	TPropertyObserver* self = (TPropertyObserver*) aPtr;
	// Queue a DFC to ensure we're running in the correct thread
	self->iPropertyDfc->Enque();
	}

void DMediaDriverNFE::FromUiPropertyDfcFunction(TAny* aObserver)
	{
	TPropertyObserver& observer = *(TPropertyObserver*) aObserver;
	observer.iMediaExt->FromUiPropertyDfc(observer);
	}

void DMediaDriverNFE::FromUiPropertyDfc(TPropertyObserver& aObserver)
	{
    // Get the value of request from the UI
    TInt err = aObserver.iProperty.Get(aObserver.iValue);

	TInt r = aObserver.iProperty.Subscribe(*aObserver.iPropertySubsRequest);
	__ASSERT_ALWAYS(r == KErrNone, NFE_FAULT());

	TInt driveLetter = iDriveLetterList[aObserver.iDriveIndex];

	__KTRACE_PRINT(Kern::Printf("NFE%d: DMediaDriverNFE::FromUiPropertyDfc() cmd %d driveLetter %c", 
		iInstance, aObserver.iValue, (TInt) DriveLetterToAscii(driveLetter)));

	// is this our drive letter ?
	TInt driveCount = DriveCount(iInstance);
	TNfeDriveInfo* driveInfo = NULL;

	for (TInt i=0; i<driveCount; i++)
		{
		TInt myDriveLetter = iDriveLetterList[i];

		__KTRACE_PRINT(Kern::Printf("NFE%d: Comparing drive %c with myDrive %c", iInstance, (TInt) DriveLetterToAscii(driveLetter), (TInt) DriveLetterToAscii(myDriveLetter)));

		if (myDriveLetter == driveLetter)
			{
			TInt driveNumber = iDriveList[i];
			driveInfo = &iInfo.iDrives[iDriveNumToIndex[driveNumber]];
			__KTRACE_PRINT(Kern::Printf("NFE%d: Drive Match found driveNumber %d", iInstance, driveInfo->iLocalDriveNum));

			__ASSERT_ALWAYS(driveInfo->iProgressToUiProperty, NFE_FAULT());
			((RPropertyRef*) (driveInfo->iProgressToUiProperty))->Set(0); 
			// Wake up the possibly waiting client, whether or not the request
			// was successfull.
			((RPropertyRef*) (driveInfo->iToUiProperty))->Set( err ); // Return value ignored
			break;
			}
		}


	__KTRACE_PRINT(Kern::Printf("NFE%d: err %d aObserver.iValue %d swap %x swap state %d", iInstance, err, aObserver.iValue, GetSwapDrive(), GetSwapDrive() ? GetSwapDrive()->Status() : -1));

	if (err == KErrNone && aObserver.iValue == ENfeEncryptDisk && driveInfo != NULL)
		{
		if (driveInfo->Status() == ENfeDecrypted)
			{
			SetStatus(*driveInfo, ENfeEncrypting);
			StartEncrypting();
			}
		}
	if (err == KErrNone && aObserver.iValue == ENfeDecryptDisk && driveInfo != NULL)
		{
		if (driveInfo->Status() == ENfeEncrypted)
			{
			SetStatus(*driveInfo, ENfeDecrypting);
			StartDecrypting();
			}
		}
	}


void DMediaDriverNFE::StatusToUiPropertyDfcFunction(TAny* aObserver)
	{
	TPropertyObserver& observer = *(TPropertyObserver*) aObserver;
	observer.iMediaExt->StatusToUiPropertyDfc(observer);
	}

void DMediaDriverNFE::StatusToUiPropertyDfc(TPropertyObserver& aObserver)
	{
    // Get the value of request from the UI
    TInt err = aObserver.iProperty.Get(aObserver.iValue);

	TInt r = aObserver.iProperty.Subscribe(*aObserver.iPropertySubsRequest);
	__ASSERT_ALWAYS(r == KErrNone, NFE_FAULT());

	__KTRACE_PRINT(Kern::Printf("NFE%d: DMediaDriverNFE::StatusToUiPropertyDfc() status %d driveLetter %c", 
		iInstance, aObserver.iValue, DriveLetter(aObserver.iDriveIndex) >=0 ? DriveLetter(aObserver.iDriveIndex)+'A' : '?'));


	__KTRACE_PRINT(Kern::Printf("NFE%d: err %d aObserver.iValue %d swap %x swap state %d", iInstance, err, aObserver.iValue, GetSwapDrive(), GetSwapDrive() ? GetSwapDrive()->Status() : -1));

	if (err == KErrNone && (aObserver.iValue == ENfeEncrypted || aObserver.iValue == ENfeEncrypting))
		{
		// If any drive is being or is already encrypted then we have to encrypt the swap partition...
		TNfeDriveInfo* diSwap = GetSwapDrive();
		if (diSwap != NULL && diSwap->Status() == ENfeDecrypted)
			{
			SetStatus(*diSwap, ENfeEncrypting);
			StartEncrypting();
			}
		}
	}


void DMediaDriverNFE::Close()
	{
	__KTRACE_PRINT(Kern::Printf("NFE%d: DMediaDriverNFE::Close()", iInstance));
	DMediaDriverExtension::Close();
	}


void DMediaDriverNFE::SetStatus(TNfeDriveInfo& aDi, TNfeDiskStatus aStatus)
	{
	if (aStatus != aDi.Status())
		{
		aDi.SetStatus(aStatus);
		__KTRACE_PRINT(Kern::Printf("NFE%d: SetStatus = %s", iInstance, DriveStatus(aDi.Status())));
		}
	}

void TNfeDriveInfo::SetStatus(TNfeDiskStatus aStatus)
    {
	iStatus = aStatus;
	if (IsUDADrive())
		{
		// Update the status pub&sub variable for UI
		__ASSERT_ALWAYS(iStatusToUiProperty, NFE_FAULT());
		((RPropertyRef*) iStatusToUiProperty)->Set(aStatus);
		}
	}




TInt DMediaDriverNFE::Request(TLocDrvRequest& aReq)
	{
//	__KTRACE_PRINT(Kern::Printf("NFE%d: DMediaDriverNFE::DoRequest() : Req %d drv %d flags %x pos %lx len %lx", iInstance, reqId, aReq.Drive()->iDriveNumber, aReq.Flags(), aReq.Pos(), aReq.Length()));

	TInt r = KErrNotSupported;

	TInt reqId = aReq.Id();
    TNfeDriveInfo& di = iInfo.iDrives[DriveIndex(aReq.Drive()->iDriveNumber)];

	switch (reqId)
		{
#if defined(__DEMAND_PAGING__)
		case DMediaPagingDevice::ERomPageInRequest:
			BTraceContext8(BTrace::EPagingMedia,BTrace::EPagingMediaPagingMedDrvBegin,MEDIA_DEVICE_NFE,&aReq);
			r=HandleRead(aReq);
			break;

		case DMediaPagingDevice::ECodePageInRequest:
			BTraceContext8(BTrace::EPagingMedia,BTrace::EPagingMediaPagingMedDrvBegin,MEDIA_DEVICE_NFE,&aReq);
			r=HandleRead(aReq);
			break;

#endif	// __DEMAND_PAGING__

		case DLocalDrive::ERead:
			r=HandleRead(aReq);
			break;

		case DLocalDrive::EWrite:
			WriteEncryptionStatusToBootSector(di, EFalse);	// a write to the drive clears the finalised state
			r=HandleWrite(aReq);
			break;

		case DLocalDrive::ECaps:
			r = HandleCaps(aReq);
			break;

		case DLocalDrive::EFormat:
			r = HandleFormat(aReq);
			break;

		// API used by T_NFE to query state etc.
		case DLocalDrive::EQueryDevice:
			switch((TInt) aReq.iArg[0])
				{
				case EQueryNfeDeviceInfo:
					{
					TNfeDeviceInfo& deviceInfo = *(TNfeDeviceInfo*) aReq.RemoteDes();
					iInfo.iMediaSizeInBytes = iTotalSizeInBytes;
					deviceInfo = iInfo;

					r = KErrCompletion;
					break;
					}
				case RLocalDrive::EQueryFinaliseDrive:
					{
//					TLocalDriveFinaliseInfo& finaliseInfo = *(TLocalDriveFinaliseInfo*) aReq.RemoteDes();
//					__KTRACE_PRINT(Kern::Printf("NFE%d: EQueryFinaliseDrive iMode %d", iInstance, finaliseInfo.iMode));

					// write to boot sector to indicate that the drive has ben finalised
					WriteEncryptionStatusToBootSector(di, ETrue);
					}
				default:
					r = KErrNotSupported;
					break;
				}
			break;

		default:
			r = ForwardRequest(aReq);
			break;
		}

//	__KTRACE_PRINT(Kern::Printf("NFE%d: DMediaDriverNFE::DoRequest() : ret: %d", iInstance, r));

	if (!di.iDriveFinalised && iBusy)
		{
		// Restart the idle timer after processing a request 
		iIdleTimer.Cancel();
		iTimerDfc.Cancel();
		iIdleTimer.OneShot(NKern::TimerTicks(KNotBusyInterval));
		}

	return r;
	}

/**
PartitionInfo()

    Reads the partition information from the attached drive(s). 
    Note: this method is also called when a removable card is removed, so can  
    be used to detect a memory card insertions/removals. Assumes the swap 
    partition is encrypted if any encrypted FAT drives are found
*/
TInt DMediaDriverNFE::PartitionInfo(TPartitionInfo& aInfo)
	{
	__KTRACE_PRINT(Kern::Printf("NFE%d: DMediaDriverNFE::PartitionInfo()", iInstance));

	TInt r = DoDrivePartitionInfo(aInfo);
	__KTRACE_PRINT(Kern::Printf("NFE%d: DoDrivePartitionInfo() r %d", iInstance, r));
	if (r != KErrNone)
		return r;

	__KTRACE_PRINT(Kern::Printf("NFE%d: *** Slave drives partition info ***", iInstance));
	__KTRACE_PRINT(Kern::Printf("NFE%d: iMediaSizeInBytes %lx", iInstance, aInfo.iMediaSizeInBytes));
	__KTRACE_PRINT(Kern::Printf("NFE%d: iPartitionCount %d", iInstance, aInfo.iPartitionCount));
	__KTRACE_PRINT(Kern::Printf("NFE%d: ", iInstance));

	TInt i;

	__ASSERT_DEBUG(aInfo.iPartitionCount <= TNfeDeviceInfo::ENfeMaxPartitionEntries, NFE_FAULT());
	for (i=0; i<aInfo.iPartitionCount; i++)
		{
		TInt driveNum = iDriveList[i];
		iDriveNumToIndex[driveNum] = i;

		TNfeDriveInfo& di = iInfo.iDrives[i];

		di.iDriveFinalised = EFalse;	// a remount clears the finalised state

		// Make sure we haven't lost the swap partition
		__ASSERT_ALWAYS(!(di.iEntry.iPartitionType == KPartitionTypePagedData && aInfo.iEntry[i].iPartitionType != KPartitionTypePagedData), NFE_FAULT());

		// Make a copy of the TPartitionEntry
		di.iEntry = aInfo.iEntry[i];


		// save the local drive number
		di.iLocalDriveNum = driveNum;
		di.iDriveLetter = iDriveLetterList[i];

		__KTRACE_PRINT(Kern::Printf("NFE%d: DriveNum %d", iInstance, driveNum));
		__KTRACE_PRINT(Kern::Printf("NFE%d: DriveLetter %c", iInstance, (TInt) DriveLetterToAscii(di.iDriveLetter)));
		__KTRACE_PRINT(Kern::Printf("NFE%d: iPartitionBaseAddr %lX", iInstance, di.iEntry.iPartitionBaseAddr));
		__KTRACE_PRINT(Kern::Printf("NFE%d: iPartitionLen %lx", iInstance, di.iEntry.iPartitionLen));
		__KTRACE_PRINT(Kern::Printf("NFE%d: iPartitionType %x", iInstance, di.iEntry.iPartitionType));
		

		// If the drive was removed, reset it's state
		if (di.iEntry.iPartitionType == KPartitionTypeEmpty)
			{
			__KTRACE_PRINT(Kern::Printf("NFE%d: Empty Partition, setting state to ENfeUnmounted", iInstance));
			SetStatus(di, ENfeUnmounted);
			}

		// Is this an unencrypted FAT partition ?
		if (di.IsUDADrive())
			{
			r = Read(di.iLocalDriveNum, di.iEntry.iPartitionBaseAddr, (TLinAddr) iBuffer, KSectorSize);
			if (r != KErrNone)
				return r;
			CheckBootSector(di);
			}
		

		__KTRACE_PRINT(Kern::Printf("NFE%d: status = %s", iInstance, DriveStatus(di.Status())));

		__KTRACE_PRINT(Kern::Printf("NFE%d: iEncryptStartPos %lX", iInstance, di.iEncryptStartPos));
		__KTRACE_PRINT(Kern::Printf("NFE%d: iEncryptEndPos %lX", iInstance, di.iEncryptEndPos));
		__KTRACE_PRINT(Kern::Printf("NFE%d: ", iInstance));
		}



#ifdef COMPOSITE_DRIVES
	// Accumulate the sizes of consecutive FAT drives & report the accumulated size back in the first FAT partition
	for (i=0; i<aInfo.iPartitionCount; i++)
		{
		aInfo.iEntry[i] = iInfo.iDrives[i].iEntry;

		if (iInfo.iDrives[i].IsUDADrive())
			{
			aInfo.iEntry[i].iPartitionLen = 0;
			for (TInt j=i; j<aInfo.iPartitionCount; j++)
				{
				if (iInfo.iDrives[j].IsUDADrive())
					{
					aInfo.iEntry[i].iPartitionLen+= iInfo.iDrives[j].iEntry.iPartitionLen;
					}
				}
			iInfo.iDrives[i].iCompositeSize = aInfo.iEntry[i].iPartitionLen;
			i = j;
			}
		}
#endif


	SetTotalSizeInBytes(aInfo.iMediaSizeInBytes);


	return KErrCompletion;	// synchronous completion
	}

/**
HandleCaps() - 

Return the Caps for a particular drive

Queries the caps from the attached drive, ORs in appropriate paging flags & returns
*/
TInt DMediaDriverNFE::HandleCaps(TLocDrvRequest& aReq)
	{
	// Get caps from slave drive
	// NB if we didn't want to alter anything then we could just call ForwardRequest(aReq);
	TBuf8<sizeof(TLocalDriveCapsV6)> slaveCapsBuf;
	TLocalDriveCapsV6& slaveCaps = *(TLocalDriveCapsV6*) slaveCapsBuf.Ptr();
	slaveCapsBuf.SetMax();
	slaveCapsBuf.FillZ();
	TInt r = Caps(aReq.Drive()->iDriveNumber, slaveCapsBuf);
	if (r != KErrNone)
		return r;

#ifdef COMPOSITE_DRIVES
	TInt driveNum = aReq.Drive()->iDriveNumber;
	TInt driveIndex = DriveIndex(driveNum);
	if (iInfo.iDrives[driveIndex].iCompositeSize)
		slaveCaps.iSize = iInfo.iDrives[driveIndex].iCompositeSize;
#endif

	// copy slave caps to returned caps
	TLocalDriveCapsV6& caps = *(TLocalDriveCapsV6*)aReq.RemoteDes();		
	caps = slaveCaps;

	// set the paging flags
#ifdef __DEMAND_PAGING__
	TLocDrv& drive = *aReq.Drive();
	if (drive.iPrimaryMedia->iPagingMedia)
		caps.iMediaAtt|=KMediaAttPageable;
	if (drive.iPagingDrv)
		caps.iDriveAtt|=KDriveAttPageable;
#endif // __DEMAND_PAGING__

	return KErrCompletion;
	}


/**
AdjustRequest() - 

Adjusts position & length if a request crosses these boundaries:
- the start of the partition (if RLocalDrive::ELocDrvWholeMedia set)
- the current encrytion point (iEncryptEndPos) N.B. this will point to the end of the partition 
  if the drive is fully encrypted

For composite drives, it also adjusts the position, length & drive number as appropriate to cater for 
crossing partition boundaries

returns ETrue if buffer needs encrypting/decrypting
*/

TBool DMediaDriverNFE::AdjustRequest(TNfeDriveInfo*& aDriveInfo, TInt64& aCurrentPos, TInt64& aCurrentLen)
	{
#ifdef COMPOSITE_DRIVES
	while (aCurrentPos >= aDriveInfo->iEntry.iPartitionLen)
		{
		aCurrentPos-= aDriveInfo->iEntry.iPartitionLen;
		aDriveInfo++;
		}
	if (aCurrentPos + aCurrentLen > aDriveInfo->iEntry.iPartitionLen)
		aCurrentLen = aDriveInfo->iEntry.iPartitionLen - aCurrentPos;
#endif

	// do we need to encrypt/decrypt this buffer ?
	TBool encodeBuffer = EFalse;
	
	if ((aDriveInfo->Status() == ENfeEncrypted) || aDriveInfo->Status() == ENfeEncrypting)
		{
//		__ASSERT_DEBUG(aDriveInfo->iEncryptEndPos <= aDriveInfo->iEntry.iPartitionBaseAddr + aDriveInfo->iEntry.iPartitionLen, NFE_FAULT());

		if (aCurrentPos < aDriveInfo->iEncryptStartPos)
			{
			aCurrentLen = Min(aCurrentLen, aDriveInfo->iEncryptStartPos - aCurrentPos);
			encodeBuffer = EFalse;
			}
		else if (aCurrentPos < aDriveInfo->iEncryptEndPos)
			{
			aCurrentLen = Min(aCurrentLen, aDriveInfo->iEncryptEndPos - aCurrentPos);
			encodeBuffer = ETrue;
			}
		else
			{
			encodeBuffer = EFalse;
			}
		}

	return encodeBuffer;
	}


TInt DMediaDriverNFE::HandleRead(TLocDrvRequest& aReq)
	{
	TInt r = KErrNone;
	TInt64 currentPos = aReq.Pos();
	TInt64 remainingLength = aReq.Length();
	TInt desPos = 0;
	TNfeDriveInfo* di = &iInfo.iDrives[DriveIndex(aReq.Drive()->iDriveNumber)];

//	__KTRACE_PRINT(Kern::Printf("NFE%d: HandleRead pos %lx len %lx status %d", iInstance, currentPos, remainingLength, di->Status()));


	di->iReadRequestCount++;

	if (aReq.Flags() & TLocDrvRequest::ECodePaging)
		di->iCodePagingRequesCount++;
	if (aReq.Flags() & TLocDrvRequest::EDataPaging)
		di->iDataPagingReadRequestCount++;

	
	// just forward the request if the drive is not encrypted
	if (di->Status() == ENfeDecrypted)
		return ForwardRequest(aReq);

	
	while(remainingLength)
		{
		TInt64 currentLength = (remainingLength <= KBufSize ? remainingLength : KBufSize);

		TBool decryptBuffer = AdjustRequest(di, currentPos, currentLength);

		// Read from attached drive
#ifdef __DEMAND_PAGING__
		if (DMediaPagingDevice::PagingRequest(aReq))
			r = ReadPaged(di->iLocalDriveNum, currentPos, (TLinAddr) iBuffer, I64LOW(currentLength));
		else
#endif
		r = Read(di->iLocalDriveNum, currentPos, (TLinAddr) iBuffer, I64LOW(currentLength));
		if(r != KErrNone)
			break;

		TPtr8 des(iBuffer, I64LOW(currentLength), I64LOW(currentLength));

		// decrypt buffer
		if (decryptBuffer)
			DecryptBuffer(des);

		//  write back to user
		r = aReq.WriteRemote(&des, desPos);
		if(r != KErrNone)
			break;

		remainingLength-= currentLength;
		currentPos+= currentLength;
		desPos+= I64LOW(currentLength);
		}

	return r == KErrNone ? KErrCompletion : r;
	}

TInt DMediaDriverNFE::HandleWrite(TLocDrvRequest& aReq)
	{
	TInt r = KErrNone;
	TInt64 currentPos =  aReq.Pos();
	TInt64 remainingLength = aReq.Length();
	TInt desPos = 0;
	TNfeDriveInfo* di = &iInfo.iDrives[DriveIndex(aReq.Drive()->iDriveNumber)];

//	__KTRACE_PRINT(Kern::Printf("NFE%d: HandleWrite pos %lx len %lx status %d", iInstance, currentPos, remainingLength, di->Status()));


	di->iWriteRequestCount++;
	if (aReq.Flags() & TLocDrvRequest::EDataPaging)
		di->iDataPagingWriteRequestCount++;
	

	// just forward the request if the drive is not encrypted
	if (di->Status() == ENfeDecrypted)
		return ForwardRequest(aReq);

	while(remainingLength)
		{
		TInt64 currentLength = (remainingLength <= KBufSize ? remainingLength : KBufSize);

		TBool encryptBuffer = AdjustRequest(di, currentPos, currentLength);

		// read from user
		TPtr8 des(iBuffer,0,I64LOW(currentLength));
		r = aReq.ReadRemote(&des, desPos);
		if(r != KErrNone)
			break;
		
		// get the length of data read from the user in case user's
		// descriptor is shorter than advertised
		currentLength = des.Length();
		if (currentLength == 0)
			break;

		// writing to sector zero ?
		if (currentPos >= di->iEntry.iPartitionBaseAddr && 
			currentPos < di->iEntry.iPartitionBaseAddr + KSectorSize && 
			di->IsUDADrive())
			{
			__KTRACE_PRINT(Kern::Printf("NFE%d: Write to sector #0 detected", iInstance));


			TUint8* bootSector = iBuffer;
			TUint8 bootSectorBuffer[KSectorSize];
			// writing partial sector ?
			if (currentPos > di->iEntry.iPartitionBaseAddr || currentLength < KSectorSize)
				{
				bootSector = bootSectorBuffer;
				r = Read(di->iLocalDriveNum, di->iEntry.iPartitionBaseAddr, (TLinAddr) bootSector, KSectorSize);
				if(r != KErrNone)
					break;
				TInt64 readLen = KSectorSize;
				TBool encryptBuffer = AdjustRequest(di, di->iEntry.iPartitionBaseAddr, readLen);
				if (encryptBuffer)
					{
					TPtr8 des(bootSectorBuffer,KSectorSize,KSectorSize);
					DecryptBuffer(des);
					}
				TInt sectorOffset = (TInt) (currentPos - di->iEntry.iPartitionBaseAddr);
				TInt64 copyLen = currentLength;
				if (copyLen > KSectorSize-sectorOffset)
					copyLen = KSectorSize-sectorOffset;
				memcpy(bootSectorBuffer+sectorOffset, iBuffer, (TInt) copyLen);
				}

			if ((di->Status() == ENfeUnmounted || di->Status() == ENfeCorrupted) && 
				ValidBootSector(bootSector))
				{
				__KTRACE_PRINT(Kern::Printf("NFE%d: Setting status to ENfeDecrypted", iInstance ));
				di->SetStatus(ENfeDecrypted);
				}
			di->iUniqueID = VolumeId(bootSector);		// update the Volume ID
			__KTRACE_PRINT(Kern::Printf("NFE%d: Setting Volume ID to %08X", iInstance, di->iUniqueID ));
			TBootSectorStatus* bootSectorStatus = (TBootSectorStatus*) iBuffer;
			if (di->Status() == ENfeEncrypting || di->Status() == ENfeDecrypting)
				{
				__KTRACE_PRINT(Kern::Printf("NFE%d: Adding NFE status record to boot sector", iInstance ));
				bootSectorStatus->iSignature = TBootSectorStatus::ENfeBootSectorSignature;
				bootSectorStatus->iEncryptEndPos = di->iEncryptEndPos;
				bootSectorStatus->iStatus = di->Status();
				bootSectorStatus->iFinalised = EFalse;
				}
			}

		// encrypt the buffer
		if (encryptBuffer)
			EncryptBuffer(des);

		// write the data to the attached drive
#ifdef __DEMAND_PAGING__
		if (DMediaPagingDevice::PagingRequest(aReq))
			r = WritePaged(di->iLocalDriveNum, currentPos, (TLinAddr) iBuffer, I64LOW(currentLength));
		else
#endif
		r = Write(di->iLocalDriveNum, currentPos, (TLinAddr) iBuffer, I64LOW(currentLength));
		if(r != KErrNone)
			break;

		remainingLength-= currentLength;
		currentPos+= currentLength;
		desPos+= I64LOW(currentLength);
		}

	return r == KErrNone ? KErrCompletion : r;
	}

TInt DMediaDriverNFE::HandleFormat(TLocDrvRequest& aReq)
	{
	TInt r = KErrNone;
	TInt64 currentPos =  aReq.Pos();
	TInt64 remainingLength = aReq.Length();
	TNfeDriveInfo* di = &iInfo.iDrives[DriveIndex(aReq.Drive()->iDriveNumber)];

//	__KTRACE_PRINT(Kern::Printf("NFE%d: HandleFormat pos %lx len %lx status %d", iInstance, currentPos, remainingLength, di->Status()));


	// just forward the request if the drive is not encrypted
	if (di->Status() == ENfeDecrypted)
		return ForwardRequest(aReq);

	// otherwise create a buffer containing NULLs, encrypt it and write that to the attached drive
	while(remainingLength && r == KErrNone)
		{
		TInt64 currentLength = (remainingLength <= KBufSize ? remainingLength : KBufSize);

		TBool encryptBuffer = AdjustRequest(di, currentPos, currentLength);

		memclr(iBuffer, KBufSize);
		TPtr8 des(iBuffer,KBufSize,KBufSize);

		if (encryptBuffer)
			EncryptBuffer(des);
		
		r = Write(di->iLocalDriveNum, currentPos, (TLinAddr) iBuffer, I64LOW(currentLength));
		if(r != KErrNone)
			break;

		remainingLength-= currentLength;
		currentPos+= currentLength;
		}

	return r == KErrNone ? KErrCompletion : r;
	}


void DMediaDriverNFE::EncryptBuffer(TDes8& aBuffer)
	{
	TInt len = aBuffer.Length();
	for(TInt i=0; i<len; i++)
		aBuffer[i] = EncryptByte(aBuffer[i]);
	}

void DMediaDriverNFE::DecryptBuffer(TDes8& aBuffer)
	{
	TInt len = aBuffer.Length();
	for(TInt i=0; i<len; i++)
		aBuffer[i] = DecryptByte(aBuffer[i]);
	}


TNfeDriveInfo* DMediaDriverNFE::GetSwapDrive()
	{
	for (TInt i=0; i<iInfo.iDriveCount; i++)
		{
		TNfeDriveInfo& di = iInfo.iDrives[i];
		if (di.iEntry.iPartitionType == KPartitionTypePagedData)
			{
			return &di;
			}
		}
	return NULL;	// swap drive not found
	}

/**
Get the first/next drive to encrypt 
*/

TNfeDriveInfo* DMediaDriverNFE::NextDrive()
	{
	for (iDriveIndex = 0; iDriveIndex<iInfo.iDriveCount; iDriveIndex++)
		{
		TNfeDriveInfo& di = iInfo.iDrives[iDriveIndex];
		TNfeDiskStatus status = iInfo.iDrives[iDriveIndex].Status();
		if (status == ENfeEncrypting || status == ENfeDecrypting)
			{
			di.iEncryptStartPos = di.iEncryptEndPos = di.iEntry.iPartitionBaseAddr;

			// write to boot sector to indicate we are encrypting/decrypting this drive
			WriteEncryptionStatusToBootSector(di);

			return &di;
			}
		}
	__KTRACE_PRINT(Kern::Printf("NFE%d: Finished encrypting / decrypting", iInstance));
	iBusy = EFalse;
	return NULL;
	}


/** 
Finds the first unencrypted drive & kicks off the idle timer - 
when this expires the encryption of the drive will start
*/
void DMediaDriverNFE::StartEncrypting()
	{
	// start encrypting if not already doing so
	if (!iBusy)
		{
		iBusy = ETrue;
		TNfeDriveInfo* di = NextDrive();
		if (di)
			{
			__KTRACE_PRINT(Kern::Printf("NFE%d: Start encrypting drive %d...", iInstance, iInfo.iDrives[iDriveIndex].iLocalDriveNum));
			iIdleTimer.OneShot(NKern::TimerTicks(KNotBusyInterval));
			}
		}
	}

/** 
Finds the first unencrypted drive & kicks off the idle timer - 
when this expires the encryption of the drive will start
*/
void DMediaDriverNFE::StartDecrypting()
	{
	// start encrypting if not already doing so
	if (!iBusy)
		{
		iBusy = ETrue;
		TNfeDriveInfo* di = NextDrive();
		if (di)
			{
			__KTRACE_PRINT(Kern::Printf("NFE%d: Start decrypting drive %d...", iInstance, iInfo.iDrives[iDriveIndex].iLocalDriveNum));
			iIdleTimer.OneShot(NKern::TimerTicks(KNotBusyInterval));
			}
		}
	}

/**
Idle timer callback
Kicks off a DFC to ensure we are running in the correct thread
*/
void DMediaDriverNFE::IdleTimerCallBack(TAny* aMediaDriver)
	{
	((DMediaDriverNFE*)aMediaDriver)->iTimerDfc.Add();
	}

/**
Idle timer DFC
*/
void DMediaDriverNFE::TimerDfcFunction(TAny* aMediaDriver)
	{
	((DMediaDriverNFE*) aMediaDriver)->HandleDiskContent();
	}


TBool DMediaDriverNFE::ValidBootSector(TUint8* aBuffer)
	{
	if (aBuffer[0] == 0xEB || aBuffer[0] == 0xE9)
		return ETrue;
	else
		return EFalse;
	}


TUint32 DMediaDriverNFE::VolumeId(TUint8* aBuffer)
	{
	TUint16 rootDirEntries;
	TUint32 uniqueID;   
    memcpy(&rootDirEntries,&aBuffer[17], 2);	// 17   TUint16 iRootDirEntries
	TBool fat32 = rootDirEntries == 0;
	TInt pos = fat32 ? 67 : 39;		// get position of VolumeID
	memcpy(&uniqueID,&aBuffer[pos],4);
	return uniqueID;
	}

void DMediaDriverNFE::CheckBootSector(TNfeDriveInfo &aDi)
	{
	TNfeDiskStatus  fatBootSectorStatus = ENfeDecrypted;

	// Try to determine whether the FAT boot sector is encypted
	if (ValidBootSector(iBuffer))
		{
		fatBootSectorStatus = ENfeDecrypted;
		__KTRACE_PRINT(Kern::Printf("NFE%d: FAT Boot sector is decrypted", iInstance));
		}
	else
		{
		TPtr8 des(iBuffer, KSectorSize, KSectorSize);
		DecryptBuffer(des);
		if (ValidBootSector(iBuffer))
			{
			__KTRACE_PRINT(Kern::Printf("NFE%d: FAT Boot sector is encrypted", iInstance));
			fatBootSectorStatus = ENfeEncrypted;
			}
		else
			{
			__KTRACE_PRINT(Kern::Printf("NFE%d: FAT Boot sector is corrupted", iInstance));
			fatBootSectorStatus = ENfeCorrupted;
			}
		}

	__KTRACE_PRINT(Kern::Printf("NFE%d: fatBootSectorStatus %d", iInstance, fatBootSectorStatus));

	// Find out whether the volume has changed
	TUint32 uniqueID = VolumeId(iBuffer);   
	TBool volumeChanged = uniqueID != aDi.iUniqueID;
	__KTRACE_PRINT(Kern::Printf("NFE%d: Old Volume ID %08X", iInstance, aDi.iUniqueID));
	__KTRACE_PRINT(Kern::Printf("NFE%d: New Volume ID %08X", iInstance, uniqueID));
	__KTRACE_PRINT(Kern::Printf("NFE%d: volumeChanged %d", iInstance, volumeChanged));
	aDi.iUniqueID = uniqueID;



	TBootSectorStatus* bootSectorStatus = (TBootSectorStatus*) iBuffer;

	__KTRACE_PRINT(Kern::Printf("NFE%d: CheckBootSector, iSignature %08X", iInstance, bootSectorStatus->iSignature));
	__KTRACE_PRINT(Kern::Printf("NFE%d: CheckBootSector, iStatus %d", iInstance, bootSectorStatus->iStatus));
	__KTRACE_PRINT(Kern::Printf("NFE%d: CheckBootSector, iEncryptEndPos %lx", iInstance, bootSectorStatus->iEncryptEndPos));


	/*
	If there IS NFE info in the boot sector, restore the encryption settings - 
	unless the 'finalised' flag is clear which indicates that the media was removed or power was lost
	while encrypting the device...

	If there is no NFE info in the boot sector and there has been a volume change, then we can decide  
	whether the drive is encrypted/decrypted/corrupt by examining the boot sector
	*/
	if (volumeChanged && 
		fatBootSectorStatus != ENfeCorrupted &&
		bootSectorStatus->iSignature == TBootSectorStatus::ENfeBootSectorSignature &&
		!bootSectorStatus->iFinalised)
		{
		SetStatus(aDi, ENfeCorrupted);
		}
	else if (volumeChanged && 
		fatBootSectorStatus != ENfeCorrupted &&
		bootSectorStatus->iFinalised &&
		bootSectorStatus->iSignature == TBootSectorStatus::ENfeBootSectorSignature &&
		(bootSectorStatus->iStatus == ENfeDecrypting || bootSectorStatus->iStatus == ENfeEncrypting))
		{
		SetStatus(aDi, bootSectorStatus->iStatus);
		aDi.iEncryptEndPos = bootSectorStatus->iEncryptEndPos;

		// write to boot sector to indicate we are no longer finalised
		WriteEncryptionStatusToBootSector(aDi, EFalse);	

		iBusy = ETrue;
		}
	else if (volumeChanged || aDi.Status() == ENfeUnmounted)
		{
		SetStatus(aDi, fatBootSectorStatus);
		if (aDi.Status() == ENfeEncrypted)
			{
			aDi.iEncryptStartPos = aDi.iEntry.iPartitionBaseAddr;
			aDi.iEncryptEndPos = aDi.iEntry.iPartitionBaseAddr + aDi.iEntry.iPartitionLen;
			}
		}
	}


TInt DMediaDriverNFE::WriteEncryptionStatusToBootSector(TNfeDriveInfo &aDi, TBool aFinalised)
	{
	if (!aDi.IsUDADrive())
		return KErrNone;

	if (aDi.iDriveFinalised == aFinalised)
		return KErrNone;

	TNfeDiskStatus status = aDi.Status();

	TInt64 currentPos = aDi.iEntry.iPartitionBaseAddr;
	TInt64 currentLen = KSectorSize;
	TNfeDriveInfo* di = &aDi;
	TBool encodeBuffer = EFalse;

	if (status == ENfeEncrypting || status == ENfeEncrypted || status == ENfeDecrypting)
		encodeBuffer = AdjustRequest(di, currentPos, currentLen);


	TInt r = Read(di->iLocalDriveNum, di->iEntry.iPartitionBaseAddr, (TLinAddr) iBuffer, KSectorSize);
	if (r != KErrNone)
		return r;
	TPtr8 des(iBuffer, I64LOW(currentLen), I64LOW(currentLen));

	if (encodeBuffer)
		DecryptBuffer(des);


	TBootSectorStatus* bootSectorStatus = (TBootSectorStatus*) iBuffer;

	if (status == ENfeEncrypting || status == ENfeDecrypting)
		{
		bootSectorStatus->iSignature = TBootSectorStatus::ENfeBootSectorSignature;
		bootSectorStatus->iEncryptEndPos = di->iEncryptEndPos;
		bootSectorStatus->iStatus = status;
		bootSectorStatus->iFinalised = aFinalised;
		}
	else
		{
		bootSectorStatus->iSignature = 0;
		bootSectorStatus->iEncryptEndPos = 0;
		bootSectorStatus->iStatus = ENfeUnmounted;
		bootSectorStatus->iFinalised = EFalse;
		}

	if (encodeBuffer)
		EncryptBuffer(des);


	r = Write(di->iLocalDriveNum, di->iEntry.iPartitionBaseAddr, (TLinAddr) iBuffer, KSectorSize);
	return r;
	}


/**
HandleDiskContent - 

Called from Idle timer DFC

Starts encrypting the current drive (iDrives[iDriveIndex]) from the current encryption position (iEncryptEndPos)
*/
TInt DMediaDriverNFE::HandleDiskContent()
	{
	TNfeDriveInfo* di = &iInfo.iDrives[iDriveIndex];

	__KTRACE_PRINT(Kern::Printf("NFE%d: Starting to encrypt Drive %d at pos %lx", iInstance, di->iLocalDriveNum, di->iEncryptEndPos));

	if (di->iDriveFinalised)
		{
	    __KTRACE_PRINT(Kern::Printf("HandleDiskContent aborting as drive has been finalised", iInstance));
		return KErrNone;
		}

//	TInt KBackgroundPriority = 7;						//*test*
//	Kern::SetThreadPriority(KBackgroundPriority);		//*test*

	TInt r = KErrNone;
	for (;;)
		{
		// If we've finished encryting this drive, change the state and move on to the next drive
		if (r != KErrNone || di->iEncryptEndPos >= di->iEntry.iPartitionBaseAddr + di->iEntry.iPartitionLen)
			{
			if (di->Status() == ENfeEncrypting)
				{
				__KTRACE_PRINT(Kern::Printf("NFE%d: Finished encrypting Drive %d r %d", iInstance, di->iLocalDriveNum, r));
				SetStatus(*di,  r == KErrNone ? ENfeEncrypted : ENfeCorrupted);
				}
			if (di->Status() == ENfeDecrypting)
				{
				__KTRACE_PRINT(Kern::Printf("NFE%d: Finished decrypting Drive %d r %d", iInstance, di->iLocalDriveNum, r));
				SetStatus(*di,  r == KErrNone ? ENfeDecrypted : ENfeCorrupted);
				}
			
			// write to boot sector to indicate we have finished encrypting/decrypting this drive
			r = WriteEncryptionStatusToBootSector(*di);

			di = NextDrive();
			if (di == NULL)
				{
				r = KErrCompletion;
				break;
				}
			__KTRACE_PRINT(Kern::Printf("NFE%d: Starting to encrypt Drive %d", iInstance, iInfo.iDrives[iDriveIndex].iLocalDriveNum));
			}

		// If this media or any of the attached media are busy, stop encrypting & wait for the next idle timeout
		if (MediaBusy(di->iLocalDriveNum))
			{
			__KTRACE_PRINT(Kern::Printf("NFE%d: Media is busy !!!", iInstance));
			r = KErrNone;	// goto sleep & wait for another timer event
			break;
			}

		TInt64& pos = di->iEncryptEndPos;
		TInt64 partitionEnd = di->iEntry.iPartitionBaseAddr + di->iEntry.iPartitionLen;
		TInt len = (TInt) Min (partitionEnd - pos, KBufSize);

#if defined(TRACE_ENABLED)
		// print position every 1/16 of the partition size
		TInt64 printPos = Max((di->iEntry.iPartitionLen >> 4) & ~(KBufSize-1), KBufSize);
		if (((di->iEncryptEndPos - di->iEncryptStartPos)% printPos) == 0) 
			__KTRACE_PRINT(Kern::Printf("NFE%d: Encrypting drive %d from %lx to %lx end %lx", iInstance, di->iLocalDriveNum, pos, pos + len, partitionEnd));
#endif
//		__KTRACE_PRINT(Kern::Printf("NFE%d: Encrypting drive %d from %lx to %lx end %lx", iInstance, di->iLocalDriveNum, pos, pos + len, partitionEnd));


		// Read a buffer, encrypt it, and then write it back
		// retry in case of media change
		const TInt KRetries = 5;
		r = KErrNotReady;
		for (TInt i=0; r == KErrNotReady && i < KRetries; i++)
			{
			r = Read(di->iLocalDriveNum, pos, (TLinAddr) iBuffer, len);
			if (r != KErrNone)
				continue;

			TPtr8 des(iBuffer,len,len);
			if (di->Status() == ENfeEncrypting)
				EncryptBuffer(des);
			else
				DecryptBuffer(des);
			
			r = Write(di->iLocalDriveNum, pos, (TLinAddr) iBuffer, len);
			}

		if (r == KErrNone)
			pos+= len;

		if (di->iProgressToUiProperty)	// no iProgressToUiProperty for swap drive
			{
			TInt progress = (TInt) (KNfeDiskOpReady * (pos - di->iEntry.iPartitionBaseAddr) / di->iEntry.iPartitionLen);
//			__KTRACE_PRINT(Kern::Printf("NFE%d: Progess %d ", progress));
			((RPropertyRef*) (di->iProgressToUiProperty))->Set( progress ); // Return value ignored
			}
		}
	
	__KTRACE_PRINT(Kern::Printf("NFE%d: HandleDiskContent returned %d", iInstance, r));

	// If not completed, start the idle timer & try again later
	if (r != KErrCompletion)
		iIdleTimer.OneShot(NKern::TimerTicks(KNotBusyInterval));

//	Kern::SetThreadPriority(KNfeThreadPriority);	//*test*
	
	return r;
	}



DECLARE_EXTENSION_PDD()
	{
	__KTRACE_PRINT(Kern::Printf("DECLARE_EXTENSION_PDD()"));
	return new DPhysicalDeviceMediaNFE;
	}

DECLARE_STANDARD_EXTENSION()
	{
	__KTRACE_PRINT(Kern::Printf("DECLARE_STANDARD_EXTENSION()"));


	// Create the media driver factory object and register this with the kernel
	__KTRACE_PRINT(Kern::Printf("Creating NFE PDD"));
	DPhysicalDeviceMediaNFE* device = new DPhysicalDeviceMediaNFE;
	if (device == NULL)
		return KErrNoMemory;
	TInt r = Kern::InstallPhysicalDevice(device);
	__KTRACE_PRINT(Kern::Printf("Installing NFE PDD in extension init - name %s r:%d", NFE_DRIVENAME, r));
	if (r != KErrNone)
		return r;

	TInt swapInstance = KErrNotFound;
#if defined (__DEMAND_PAGING__)
	swapInstance = SwapInstance();
#endif

	DPrimaryMediaExt* primaryMedia[NFE_INSTANCE_COUNT];
	TInt instance;

	for (instance=0; instance<NFE_INSTANCE_COUNT; instance++)
		{
		// Register this media device & define which drives we want to attach to.
		// These drives must already be registered with the local media subsystem
		// i.e. this media's kernel extension must be defined AFTER any attached
		// media's kernel extension in the appropriate .IBY file
		__KTRACE_PRINT(Kern::Printf("NFE%d: Creating NFE primary media", instance));
		DPrimaryMediaExt* pM = new DPrimaryMediaExt(instance);
		if (pM == NULL)
			return KErrNoMemory;
		primaryMedia[instance] = pM;

		_LIT(KMediaThreadName,"NfeThread?");
		HBuf* pMediaThreadName = HBuf::New(KMediaThreadName);
		(*pMediaThreadName)[9] = (TUint8) ('0' + (TUint8) instance);

		TInt r = Kern::DfcQInit(&pM->iNfeDfcQ,KNfeThreadPriority,pMediaThreadName);
		if (r != KErrNone)
			return r;

#ifdef CPU_AFFINITY_ANY
		NKern::ThreadSetCpuAffinity((NThread*)(pM->iNfeDfcQ.iThread), KCpuAffinityAny);
#endif
		

		pM->iDfcQ = &pM->iNfeDfcQ;
		pM->iMsgQ.Receive();


		const TInt* driveList = DriveList(instance);
		TInt driveCount = DriveCount(instance);

		TBuf<4> driveName(_L("NFE?"));
		driveName[3] = (TUint8) ('0' + (TUint8) instance);

		
		r = LocDrv::RegisterMediaDevice(
			MEDIA_DEVICE_NFE, 
			driveCount, driveList,
			pM, NFE_NUMMEDIA, driveName);
		if (r != KErrNone)
			return r;


#if defined (__DEMAND_PAGING__)
		if (PagingType(instance))
			{
			// Define which of the drives we have already attached to have code or data paging enabled 
			const TInt* pageDriveList = PageDriveList(instance);
			TInt pageDriveCount = PageDriveCount(instance);

			r = LocDrv::RegisterPagingDevice(pM,pageDriveList,pageDriveCount,PagingType(instance),SECTOR_SHIFT,NFE_NUM_PAGES);
			__KTRACE_PRINT(Kern::Printf("NFE%d: Installing NFE PagingDevice in extension init - r:%d", pM->iInstance, r));
			// Ignore error if demand paging not supported by kernel
			if (r == KErrNotSupported)
				r = KErrNone;
			if (r != KErrNone)
				return r;
			}


#endif	// __NAND_DEMAND_PAGING__

		/*
		If there is a swap partition we need to make sure all instances have their PartitionInfo() called
		so that we can flag the swap partition as 'encrypted' if there are any encrypted drives at all
		*/
		if (swapInstance != KErrNotFound)
			{
			TBuf8<sizeof(TLocalDriveCapsV6)> capsBuf;
			capsBuf.SetMax();
			capsBuf.FillZ();
			DLocalDrive::Caps(driveList[0], capsBuf);
			}
		}
		

	// If we encounter an encrypted drive belonging to ANY NFE instance, then assume the swap partition is 
	// encrypted too. We need to do this because the swap partition has no equivalent of the boot sector
	if (swapInstance != KErrNotFound)
		{
		__KTRACE_PRINT(Kern::Printf("NFE: Searching for encrypted drives to determine whether swap partition should be encrypted..."));
		TBool encryptedDriveFound = EFalse;
		TNfeDriveInfo* swapDriveInfo = NULL;
		for (instance=0; instance<NFE_INSTANCE_COUNT; instance++)
			{
			DPrimaryMediaExt* pM = primaryMedia[instance];
			DMediaDriverNFE* mediaDriver = (DMediaDriverNFE*) pM->iDriver;
			__ASSERT_ALWAYS(mediaDriver, NFE_FAULT());

			if (swapDriveInfo == NULL)
				swapDriveInfo = mediaDriver->GetSwapDrive();

			for (TInt i=0; i<mediaDriver->iInfo.iDriveCount; i++)
				{
				TNfeDriveInfo& di = mediaDriver->iInfo.iDrives[i];
				__KTRACE_PRINT(Kern::Printf("NFE%d: Testing drive %d DriveLetter %c status %s", 
					instance, di.iLocalDriveNum, (TInt) DriveLetterToAscii(di.iDriveLetter), DriveStatus(di.Status()) ));
				if (di.Status() == ENfeEncrypted || di.Status() == ENfeEncrypting)
					encryptedDriveFound = ETrue;
				}
			}
		if (swapDriveInfo)
			{
			swapDriveInfo->SetStatus(encryptedDriveFound ? ENfeEncrypted : ENfeDecrypted);
			swapDriveInfo->iEncryptEndPos = swapDriveInfo->iEntry.iPartitionBaseAddr + swapDriveInfo->iEntry.iPartitionLen;

			__KTRACE_PRINT(Kern::Printf("NFE: Setting swap partition state to %s...", DriveStatus(swapDriveInfo->Status())));
			}
		}


	return r;
	}


