kerneltest/e32utils/usbmsapp/usbmsapp.cpp
author Tom Cosgrove <tom.cosgrove@nokia.com>
Fri, 28 May 2010 16:29:07 +0100
changeset 30 8aab599e3476
parent 0 a41df078684a
permissions -rw-r--r--
Fix for bug 2283 (RVCT 4.0 support is missing from PDK 3.0.h) Have multiple extension sections in the bld.inf, one for each version of the compiler. The RVCT version building the tools will build the runtime libraries for its version, but make sure we extract all the other versions from zip archives. Also add the archive for RVCT4.

// Copyright (c) 2006-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:
// USB Mass Storage Application - also used as an improvised boot loader mechanism
// 
//

/**
 @file
*/

#include "usbmsapp.h"

#include <e32std.h>
#include <e32std_private.h>
#include <e32svr.h>
#include <e32cons.h>
#include <f32file.h>

#include <usbmsshared.h>
#include <massstorage.h>

TBool gSharedChunkLdd = EFalse;
#include <d32usbcsc.h>
#include <d32usbc.h>

#ifdef BUILD_OTG_USBMSAPP
#include <d32otgdi.h>
#endif

#include <nkern/nk_trace.h>
#include <hal.h>

#ifdef USB_BOOT_LOADER

#include "usbbootvar.h"
#include <rebootdrv.h>
#define KNANDLDRLDD_NAME _L("REBOOT.LDD")
static RReboot* RebootDrv;


/// Global number of seconds to delay before reboot
static TInt gRebootDelay = 0;

#endif

enum
	{
	EUsbDeviceStateUndefined  = EUsbcDeviceStateUndefined,
	EUsbDeviceStateConfigured = EUsbcDeviceStateConfigured,
	};

static CConsoleBase* console = NULL;
static RFs fs;
static TInt selectedDriveIndex = 0;
static TBuf<0x40> mountList;

static TFixedArray<TBool, KMaxDrives>                   msfsMountedList;  ///< 'true' entry corresponds to the drive with mounted MSFS.FSY
static TFixedArray<CFileSystemDescriptor*, KMaxDrives>  unmountedFsList;  ///< every non-NULL entry corresponds to the unmounted original FS for the drive


_LIT(KMsFsy, "MSFS.FSY");
_LIT(KMsFs, "MassStorageFileSystem");

_LIT(KOk,"OK");
_LIT(KError,"Error");
_LIT(KBytesTransferredFmt, "%c:%d/%d ");
_LIT(KErrFmt, "Error: %d\r");

#ifndef USB_BOOT_LOADER
_LIT(KTxtApp,"USBMSAPP");
_LIT(KDefPwd,"123");
#endif

//-- if defined, some useful information will be printed out via RDebug interface
//#define LOGGING_ENABLED

//-----------------------------------------------------------------------------
/** 
    prints a line to the console and copies it to the debug log if LOGGING_ENABLED
*/
void LogPrint(TRefByValue<const TDesC> aFmt,...)
    {
    VA_LIST list;
    VA_START(list, aFmt);
    
    TBuf<0x100> buf;
	// coverity[uninit_use_in_call]
    buf.FormatList(aFmt, list); //-- ignore overflows

    if(console)
        console->Write(buf);

#ifdef LOGGING_ENABLED
    //-- print out the line via RDebug::Print 
    const TInt bufLen = buf.Length();
    if(bufLen >0 && buf[bufLen-1] == '\n')
        {
        buf.Insert(bufLen-1, _L("\r"));
        }
    else
        {
        buf.Append(_L("\r\n"));    
        }

    RDebug::RawPrint(buf);
#endif
    }

//-----------------------------------------------------------------------------
/**
    prints a line to the debug log if LOGGING_ENABLED
*/
void Log(TRefByValue<const TDesC> aFmt,...)
    {
#ifdef LOGGING_ENABLED

    VA_LIST list;
    VA_START(list, aFmt);
    
    TBuf<0x100> buf;
    buf.FormatList(aFmt, list); //-- ignore overflows
    
    //-- print out the line via RDebug::Print 
    const TInt bufLen = buf.Length();
    if(bufLen >0 && buf[bufLen-1] == '\n')
        {
        buf.Insert(bufLen-1, _L("\r"));
        }

    RDebug::RawPrint(buf);
#else
    (void)aFmt;
#endif
    }


//-----------------------------------------------------------------------------

static void Clear(int row, int count=1)
	{
	_LIT(KBlank,"                                        ");
	for(TInt i=0; i<count; i++)
		{
		console->SetPos(0,row+i);
		console->Printf(KBlank);
		}
	console->SetPos(0,row);
	}


static void ShowDriveSelection()
	{
	console->SetPos(0,15);
	if(PropertyHandlers::allDrivesStatus.Length()/2 > selectedDriveIndex)
		{
		LogPrint(_L("Selected Drive: %c"), 'A' + PropertyHandlers::allDrivesStatus[selectedDriveIndex*2]);
		}
	else
		{
		LogPrint(_L("Selected Drive: (none)"));
		}
	}


#ifdef USB_BOOT_LOADER

static void rebootit()
	{
	TInt r=User::LoadLogicalDevice(KNANDLDRLDD_NAME);
	RebootDrv=new RReboot;
	if(!RebootDrv)
		{
		User::Panic(_L("Loading driver"),1);
		}
	r=RebootDrv->Open();
	if (r!=KErrNone)
		{
		User::Panic(_L("Opening driver"),r);
		}

	if (gRebootDelay>0)
		{
		_LIT(KMsgRebooting,"*** Reboot in %d secs ***\n");
		TInt delay=gRebootDelay;
		console->SetPos(0,20);
			do
			{
			LogPrint(KMsgRebooting, delay);
			User::After(1000000);
			} while(--delay);
		}
	r=RebootDrv->VariantCtrl(KVariantUsbmsVariantRebootReason, NULL);
	if (r!=KErrNone)
		{
		User::Panic(_L("Rebooting"),r);
		}
	}

#endif

class CPeriodUpdate : public CActive
	{
public:
	static CPeriodUpdate* NewLC();
private:
	CPeriodUpdate();
	void ConstructL();
	~CPeriodUpdate();
	void RunL();
	void DoCancel();

	RTimer iTimer;
	TUint iUpTime;
	};

CPeriodUpdate* CPeriodUpdate::NewLC()
	{
	CPeriodUpdate* me=new(ELeave) CPeriodUpdate();
	CleanupStack::PushL(me);
	me->ConstructL();
	return me;
	}

CPeriodUpdate::CPeriodUpdate()
	: CActive(0), iUpTime(0)
	{}

void CPeriodUpdate::ConstructL()
	{
	CActiveScheduler::Add(this);
	iTimer.CreateLocal();
	RunL();
	}

CPeriodUpdate::~CPeriodUpdate()
	{
	Cancel();
	}

void CPeriodUpdate::DoCancel()
	{
	}

void CPeriodUpdate::RunL()
	{
	SetActive();
	// Print RAM usage & up time

	iUpTime++;
	TUint totmins=(iUpTime/60);
	TUint tothrs=(totmins/60);
	TInt mem=0;
	if (HAL::Get(HALData::EMemoryRAMFree, mem)==KErrNone)
		{
		console->SetPos(0,22);
		console->Printf(_L("mem (bytes) : %d\n"), mem);
		console->Printf(_L("up time     : %dh:%dm:%ds\n"),
					tothrs, totmins%60, iUpTime%60);
		}
	iTimer.After(iStatus, 1000000);
	}

//-----------------------------------------------------------------------------
/**
    Dismounts the originally mounted FS and optional primary extension from the drive and stores 
    this information in the FS descriptor

    @return on success returns a pointer to the instantinated FS descriptor
*/
static CFileSystemDescriptor* DoDismountOrginalFS(RFs& aFs, TInt aDrive)
    {
    TInt        nRes;
    TBuf<128>   fsName;
    TBuf<128>   primaryExtName;
    TBool       bDrvSync = EFalse;

    Log(_L("# DoDismountOrginalFS drv:%d\n"), aDrive);

    //-- 1. get file system name
    nRes = aFs.FileSystemName(fsName, aDrive);
    if(nRes != KErrNone)
        {//-- probably no file system installed at all
        return NULL;
        }

    //-- 2. find out if the drive sync/async
    TPckgBuf<TBool> drvSyncBuf;
    nRes = aFs.QueryVolumeInfoExt(aDrive, EIsDriveSync, drvSyncBuf);
    if(nRes == KErrNone)
        {
        bDrvSync = drvSyncBuf();
        }

    //-- 3. find out primary extension name if it is present; we will need to add it againt when mounting the FS
    //-- other extensions (non-primary) are not supported yet
    nRes = aFs.ExtensionName(primaryExtName, aDrive, 0);
    if(nRes != KErrNone)
        {   
        primaryExtName.SetLength(0);
        }

    //-- 3.1 check if the drive has non-primary extensions, fail in this case, because this FS can't be mounted back normally
    nRes = aFs.ExtensionName(primaryExtName, aDrive, 1);
    if(nRes == KErrNone)
        {   
        LogPrint(_L("Non-primary extensions are not supported!\n"));
        return NULL;
        }

    Log(_L("# DoDismountOrginalFS FS:%S, Prim ext:%S, synch:%d\n"), &fsName, &primaryExtName, bDrvSync);

    //-- create FS descriptor and dismount the FS
    CFileSystemDescriptor* pFsDesc = NULL; 
    
    TRAP(nRes, pFsDesc = CFileSystemDescriptor::NewL(fsName, primaryExtName, bDrvSync));
    if(nRes != KErrNone)
        return NULL; //-- OOM ?

    nRes = aFs.DismountFileSystem(fsName, aDrive);
    if(nRes != KErrNone)
        {
        delete pFsDesc;
        pFsDesc = NULL;
        Log(_L("# DoDismountOrginalFS Dismounting Err:%d\n"), nRes);
        }
    
    return pFsDesc;
}

//-----------------------------------------------------------------------------
/**
    Tries to restore the original FS on the drive using the FS descriptor provided
    @return standard error code.
*/
static TInt DoRestoreFS(RFs& aFs, TInt aDrive, CFileSystemDescriptor* apFsDesc)
    {
    TInt nRes;

    Log(_L("# DoRestoreFS drv:%d\n"), aDrive);

    //-- 1. check that there is no FS installed
        {
        TBuf<128>   fsName;
        nRes = aFs.FileSystemName(fsName, aDrive);
        if(nRes == KErrNone)
            {//-- probably no file system installed at all
            Log(_L("# This drive already has FS intalled:%S \n"), &fsName);
            return KErrAlreadyExists;
            }
        }

    TPtrC ptrN  (apFsDesc->FsName());
    TPtrC ptrExt(apFsDesc->PrimaryExtName());
    Log(_L("# Mounting FS:%S, Prim ext:%S, synch:%d\n"), &ptrN, &ptrExt, apFsDesc->DriveIsSynch());

    if(ptrExt.Length() >0)
        {//-- there is a primary extension to be mounted
        nRes = aFs.AddExtension(ptrExt);
        if(nRes != KErrNone && nRes != KErrAlreadyExists)
            {
            return nRes;
            }

        nRes = aFs.MountFileSystem(ptrN, ptrExt, aDrive, apFsDesc->DriveIsSynch());
        }
    else
        {
        nRes = aFs.MountFileSystem(ptrN, aDrive, apFsDesc->DriveIsSynch());
        }

    if(nRes != KErrNone)
        {
        Log(_L("# Mount failed! code:%d\n"),nRes);    
        }

    return nRes;
    }


//-----------------------------------------------------------------------------
/**
    Dismount the original FS from the drive and mount MsFS instead
*/
static void MountMsFs(TInt driveNumber)
	{
	TInt x = console->WhereX();
	TInt y = console->WhereY();

    //-- 1. try dismounting the original FS
    CFileSystemDescriptor* fsDesc = DoDismountOrginalFS(fs, driveNumber);
    unmountedFsList[driveNumber] = fsDesc;
    
    console->SetPos(0, 10);

    if(fsDesc)
        {
        TPtrC ptrN(fsDesc->FsName());
        LogPrint(_L("drv:%d FS:%S Dismounted OK"),driveNumber, &ptrN);
        }
    else
        {
        LogPrint(_L("drv:%d Dismount FS Failed!"),driveNumber);
        }

    console->ClearToEndOfLine();
	
    //-- 2. try to mount the "MSFS"
    TInt error;
    error = fs.MountFileSystem(KMsFs, driveNumber);
	console->SetPos(0, 11);
	LogPrint(_L("MSFS Mount:   %S (%d)"), (error?&KError:&KOk), error);
	console->ClearToEndOfLine();

	if (!error)
		msfsMountedList[driveNumber] = ETrue;

	// restore console position
	console->SetPos(x,y);
	}

//-----------------------------------------------------------------------------
/**
    Dismount MsFS and mount the original FS 
*/
static TInt RestoreMount(TInt driveNumber)
	{
	TInt err = KErrNone;
	
	TInt x = console->WhereX();
	TInt y = console->WhereY();

    //-- 1. try dismounting the "MSFS"
	if (msfsMountedList[driveNumber])
		{
		err = fs.DismountFileSystem(KMsFs, driveNumber);
		console->SetPos(0, 11);
		LogPrint(_L("MSFS Dismount:%S (%d)"), (err?&KError:&KOk), err);
		console->ClearToEndOfLine();
		if (err)
			return err;

		msfsMountedList[driveNumber] = EFalse;
        }

    //-- 2. try to mount the original FS back
    CFileSystemDescriptor* fsDesc = unmountedFsList[driveNumber];
    if(fsDesc)
        {
        err = DoRestoreFS(fs, driveNumber, fsDesc);

        TPtrC ptrN(fsDesc->FsName());
        console->SetPos(0, 10);
        LogPrint(_L("%S Mount:    %S (%d)"), &ptrN, (err?&KError:&KOk), err);
        console->ClearToEndOfLine();
        
        delete fsDesc;
        unmountedFsList[driveNumber] = NULL;
        }

    
    // restore console position
	console->SetPos(x,y);
	return err;
	}

//////////////////////////////////////////////////////////////////////////////
//
// CPropertyWatch
// An active object that tracks changes to the KUsbMsDriveState properties
//
//////////////////////////////////////////////////////////////////////////////

CPropertyWatch* CPropertyWatch::NewLC(TUsbMsDriveState_Subkey aSubkey, PropertyHandlers::THandler aHandler)
	{
	CPropertyWatch* me=new(ELeave) CPropertyWatch(aHandler);
	CleanupStack::PushL(me);
	me->ConstructL(aSubkey);
	return me;
	}

CPropertyWatch::CPropertyWatch(PropertyHandlers::THandler aHandler)
	: CActive(0), iHandler(aHandler)
	{}

void CPropertyWatch::ConstructL(TUsbMsDriveState_Subkey aSubkey)
	{
	User::LeaveIfError(iProperty.Attach(KUsbMsDriveState_Category, aSubkey));
	CActiveScheduler::Add(this);
	// initial subscription and process current property value
	RunL();
	}

CPropertyWatch::~CPropertyWatch()
	{
	Cancel();
	iProperty.Close();
	}

void CPropertyWatch::DoCancel()
	{
	iProperty.Cancel();
	}

void CPropertyWatch::RunL()
	{
	// resubscribe before processing new value to prevent missing updates
	iProperty.Subscribe(iStatus);
	SetActive();

	iHandler(iProperty);
	}

//////////////////////////////////////////////////////////////////////////////
//
// CUsbWatch
//
//////////////////////////////////////////////////////////////////////////////

CUsbWatch* CUsbWatch::NewLC(TAny* aUsb)
	{
	CUsbWatch* me=new(ELeave) CUsbWatch(aUsb);
	CleanupStack::PushL(me);
	me->ConstructL();
	return me;
	}

CUsbWatch::CUsbWatch(TAny* aUsb)
	: 
	CActive(0), 
	iUsb(aUsb),
	iUsbDeviceState(EUsbDeviceStateUndefined),
	iWasConfigured(EFalse)
	{}

void CUsbWatch::ConstructL()
	{
	CActiveScheduler::Add(this);
	RunL();
	}

CUsbWatch::~CUsbWatch()
	{
	Cancel();
//	iUsb.DeviceStateNotificationCancel();
	if (gSharedChunkLdd)
		((RDevUsbcScClient*)iUsb)->AlternateDeviceStatusNotifyCancel();
	else
		((RDevUsbcClient*)iUsb)->AlternateDeviceStatusNotifyCancel();
	}

void CUsbWatch::DoCancel()
	{
//	iUsb.DeviceStateNotificationCancel();
	if (gSharedChunkLdd)
		((RDevUsbcScClient*)iUsb)->AlternateDeviceStatusNotifyCancel();
	else
		((RDevUsbcClient*)iUsb)->AlternateDeviceStatusNotifyCancel();
	}

static TBool IsDriveConnected(TInt driveStatusIndex)
	{
	TInt driveStatus = PropertyHandlers::allDrivesStatus[2*driveStatusIndex+1];
	return driveStatus >= EUsbMsDriveState_Connected ? ETrue : EFalse;
	}

static TChar DriveNumberToLetter(TInt driveNumber)
	{
	TChar driveLetter = '?';
	fs.DriveToChar(driveNumber, driveLetter);
	return driveLetter;
	}

static TBool IsDriveInMountList(TUint driveLetter)
	{
	TUint16 driveLetter16 = static_cast<TUint16>(driveLetter);
	return(!mountList.Length() || KErrNotFound != mountList.Find(&driveLetter16, 1));
	}

void CUsbWatch::RunL()
	{
//	RDebug::Print(_L(">> CUsbWatch[%d] %d"), iUsbDeviceState, iWasConfigured);

//	const TUint stateMask = 0xFF;
//	iUsb.DeviceStateNotification(stateMask, iUsbDeviceState, iStatus);
	if (gSharedChunkLdd)
		((RDevUsbcScClient*)iUsb)->AlternateDeviceStatusNotify(iStatus, iUsbDeviceState);
	else
		((RDevUsbcClient*)iUsb)->AlternateDeviceStatusNotify(iStatus, iUsbDeviceState);

	SetActive();

	//RDebug::Print(_L("CUsbWatch DeviceStateNotification: iUsbDeviceState=%d"), iUsbDeviceState);

	// If the cable is disconnected, unmount all the connected drives.
	if(iWasConfigured && iUsbDeviceState == EUsbDeviceStateUndefined)
		{
		for(TInt i=0; i<PropertyHandlers::allDrivesStatus.Length()/2; i++)
			{
			if(IsDriveConnected(i))
				{
				//RDebug::Print(_L("CUsbWatch calling RestoreMount"));
				RestoreMount(PropertyHandlers::allDrivesStatus[2*i]);
#ifdef USB_BOOT_LOADER
				// exit and reboot
				CActiveScheduler::Stop();
#endif
				}
			}

		iWasConfigured = EFalse;
		}

	// If cable is connected, mount all drives in the auto-mount list.
	// This is done for performance, since if this is not done here,
	// mounting will happen later after each drive enters the 
	// Connecting state.
	if(iUsbDeviceState == EUsbDeviceStateConfigured)
		{
		for(TInt i=0; i<PropertyHandlers::allDrivesStatus.Length()/2; i++)
			{
			TInt driveNumber = PropertyHandlers::allDrivesStatus[2*i];
			if(!IsDriveConnected(i) && IsDriveInMountList(DriveNumberToLetter(driveNumber)))
				{
				//RDebug::Print(_L("CUsbWatch calling MountMsFs"));
				MountMsFs(driveNumber);
				}
			}

		iWasConfigured = ETrue;
		}
	}

//////////////////////////////////////////////////////////////////////////////
//
// PropertyHandlers
//
//////////////////////////////////////////////////////////////////////////////

TBuf8<16> PropertyHandlers::allDrivesStatus;
TUsbMsBytesTransferred PropertyHandlers::iKBytesRead;
TUsbMsBytesTransferred PropertyHandlers::iKBytesWritten;
TInt PropertyHandlers::iMediaError;

void PropertyHandlers::Read(RProperty& aProperty)
	{
	Transferred(aProperty, iKBytesRead);
	}

void PropertyHandlers::Written(RProperty& aProperty)
	{
	Transferred(aProperty, iKBytesWritten);
	}

void PropertyHandlers::Transferred(RProperty& aProperty, TUsbMsBytesTransferred& aReadOrWritten)
	{
	console->SetPos(0,1);
	console->Printf(_L("KB R/W:  "));
	TInt err = aProperty.Get(aReadOrWritten);
	if(err == KErrNone)
		{
		for(TInt i = 0; i < allDrivesStatus.Length()/2; i++)
			{
			console->Printf(KBytesTransferredFmt, (char)DriveNumberToLetter(allDrivesStatus[2*i]), iKBytesRead[i], iKBytesWritten[i]);
			}
		console->ClearToEndOfLine();
		}
	else
		{
		console->Printf(KErrFmt, err);
		}
	}

void PropertyHandlers::DriveStatus(RProperty& aProperty)
	{
//	RDebug::Print(_L(">> PropertyHandlers::DriveStatus"));
	TInt err = aProperty.Get(allDrivesStatus);
	console->SetPos(0,0);
	if(err == KErrNone)
		{
		LogPrint(_L("Status:  "));
		for(TInt i = 0; i < allDrivesStatus.Length()/2; i++)
			{
			TInt driveNumber = allDrivesStatus[2*i];
			TInt driveStatus = allDrivesStatus[2*i+1];
			TChar driveLetter = DriveNumberToLetter(driveNumber);

//			RDebug::Print(_L("%c:%d   "), (char)driveLetter, driveStatus);

			switch(driveStatus)
				{
				case EUsbMsDriveState_Disconnected:
					{
					LogPrint(_L("%c:%d:Disconnected "), (char)driveLetter, driveStatus);
					break;
					}
				case EUsbMsDriveState_Connecting:
					{
					LogPrint(_L("%c:%d:Connecting   "), (char)driveLetter, driveStatus);
					break;
					}
				case EUsbMsDriveState_Connected:
					{
					LogPrint(_L("%c:%d:Connected    "), (char)driveLetter, driveStatus);
					break;
					}
				case EUsbMsDriveState_Disconnecting:
					{
					LogPrint(_L("%c:%d:Disconnecting"), (char)driveLetter, driveStatus);
					break;
					}
				case EUsbMsDriveState_Active:
					{
					LogPrint(_L("%c:%d:Active       "), (char)driveLetter, driveStatus);
					break;
					}
				case EUsbMsDriveState_Locked:
					{
					LogPrint(_L("%c:%d:Locked       "), (char)driveLetter, driveStatus);
					break;
					}
				case EUsbMsDriveState_MediaNotPresent:
					{
					LogPrint(_L("%c:%d:Not Present  "), (char)driveLetter, driveStatus);
					break;
					}
				case EUsbMsDriveState_Removed:
					{
					LogPrint(_L("%c:%d:Removed      "), (char)driveLetter, driveStatus);
					break;
					}
				case EUsbMsDriveState_Error:
					{
					LogPrint(_L("%c:%d:Error        "), (char)driveLetter, driveStatus);
					break;
					}
				default :
					{
					LogPrint(_L("%c:%d:Unknown      "), (char)driveLetter, driveStatus);
					break;
					}
				}

			if(IsDriveInMountList(driveLetter))
				{
#ifndef USB_BOOT_LOADER
				if (driveStatus == EUsbMsDriveState_Connecting)
					{
					MountMsFs(driveNumber);
					}
				else if (driveStatus == EUsbMsDriveState_Disconnected)
					{
					RestoreMount(driveNumber);
					}
#else
				if (driveStatus == EUsbMsDriveState_Disconnecting)
					{
					RestoreMount(driveNumber);
					}
				else if (driveStatus == EUsbMsDriveState_Disconnected)
					{
					static TBool firstTime = ETrue;

					if (!firstTime)
						{
						RDebug::Print(_L("Eject..."));
						// Exit and reboot the target upon receipt of an eject
						CActiveScheduler::Stop();
						rebootit();
						}
						
					firstTime = EFalse;
					}
#endif
				else
					{
					//RDebug::Print(_L("PropertyHandlers::DriveStatus: nothing to do"));
					}
				}
			else
				{
				//RDebug::Print(_L("PropertyHandlers::DriveStatus: %c: is not in mountList\n"), driveLetter);
				}
			}
		}
	else
		{
		LogPrint(KErrFmt, err);
		}

	//RDebug::Print(_L("<< PropertyHandlers::DriveStatus"));
	}

void PropertyHandlers::MediaError(RProperty& aProperty)
	{
	TInt err = aProperty.Get(iMediaError);
	if(err != KErrNone)
		{
		// RDebug::Printf("RProperty::Get returned %d", err);
		return;
		}

	//RDebug::Printf("PropertyHandlers::MediaError %x", iMediaError);

	TInt x = console->WhereX();
	TInt y = console->WhereY();
	Clear(27,1);
	LogPrint(_L("Media Error %x"), iMediaError);
	// restore console position
	console->SetPos(x,y);
	}

//////////////////////////////////////////////////////////////////////////////
//
// CMessageKeyProcessor
//
//////////////////////////////////////////////////////////////////////////////
CMessageKeyProcessor::CMessageKeyProcessor(CConsoleBase* aConsole)
	: CActive(CActive::EPriorityUserInput), iConsole(aConsole)
	{
	} 

CMessageKeyProcessor* CMessageKeyProcessor::NewLC(CConsoleBase* aConsole
												 )
	{
	CMessageKeyProcessor* self=new (ELeave) CMessageKeyProcessor(aConsole);
	CleanupStack::PushL(self);
	self->ConstructL();
	return self;
	}

CMessageKeyProcessor* CMessageKeyProcessor::NewL(CConsoleBase* aConsole
												)
	{
	CMessageKeyProcessor* self = NewLC(aConsole);
	CleanupStack::Pop();
	return self;
	}

void CMessageKeyProcessor::ConstructL()
	{
	// Add to active scheduler
	CActiveScheduler::Add(this);
	RequestCharacter();
	}

#ifndef USB_BOOT_LOADER
void CMessageKeyProcessor::MakePassword(TMediaPassword &aPassword)
	{
	//  Create password with same format as eshell and S60
	TBuf<3> password(KDefPwd);

	// fill aPassword with contents of password, not converting to ASCII
	const TInt byteLen = password.Length() * 2;
	aPassword.Copy(reinterpret_cast<const TUint8 *>(password.Ptr()), byteLen);
	}
#endif


CMessageKeyProcessor::~CMessageKeyProcessor()
	{
	// Make sure we're cancelled
	Cancel();
	}

void  CMessageKeyProcessor::DoCancel()
	{
	iConsole->ReadCancel();
	}

void  CMessageKeyProcessor::RunL()
	{
	  // Handle completed request
	ProcessKeyPress(TChar(iConsole->KeyCode()));
	}

void CMessageKeyProcessor::RequestCharacter()
	{
	  // A request is issued to the CConsoleBase to accept a
	  // character from the keyboard.
	iConsole->Read(iStatus); 
	SetActive();
	}

void CMessageKeyProcessor::ProcessKeyPress(TChar aChar)
	{
#ifndef USB_BOOT_LOADER
	TInt error = KErrNone;
#endif
#if defined(_DEBUG)
	static TBool tracetoggle=EFalse;
#endif

	switch(aChar)
		{
		case 'q':
		case 'Q':
		case EKeyEscape:
			{
			TInt err = KErrNone;
			for(TInt j=0; j<KMaxDrives; j++)
				{
				err = RestoreMount(j);
				
				if (err)
					{
					// Mount is busy/locked and can not be restored.
					break;
					}

				}

			if (err == KErrNone)
				{
#ifdef USB_BOOT_LOADER
					gRebootDelay=0;	// Force reboot to occur immediately
#endif				
				CActiveScheduler::Stop();
				return;
				}

			}
			break;

#if defined(_DEBUG)
		case 't':
		case 'T':
			tracetoggle=!tracetoggle;
			if (tracetoggle)	// 0x44008401
				User::SetDebugMask(KHARDWARE|KDLL|KSCRATCH|KPOWER|KMEMTRACE);
			else
				User::SetDebugMask(0);
			break;
#endif

#ifndef USB_BOOT_LOADER
		case 'd':
		case 'D':
			if(++selectedDriveIndex >= PropertyHandlers::allDrivesStatus.Length()/2)
				{
				selectedDriveIndex = 0;
				}
			ShowDriveSelection();
			break;

		case 'm':
		case 'M':
			if(PropertyHandlers::allDrivesStatus.Length())
				{
				MountMsFs(PropertyHandlers::allDrivesStatus[selectedDriveIndex*2]);
				}
			break;

		case 'u':
		case 'U':
			if(PropertyHandlers::allDrivesStatus.Length())
				{
				RestoreMount(PropertyHandlers::allDrivesStatus[selectedDriveIndex*2]);
				}
			break;

		case 'l':
			{
			// lock unprotected drive
			TMediaPassword password;
			MakePassword(password);

			_LIT(KEmpty, "");
			TMediaPassword nul;
			nul.Copy(KEmpty);
			error = fs.LockDrive(PropertyHandlers::allDrivesStatus[selectedDriveIndex*2],
                                 nul, password, ETrue);
			console->SetPos(0,9);
			LogPrint(_L("LockDrive %S (%d)"), (error?&KError:&KOk), error);
			break;
			}

			case 'L':
            {
            // lock password protected drive
            TMediaPassword password;
            MakePassword(password);
            error = fs.LockDrive(PropertyHandlers::allDrivesStatus[selectedDriveIndex*2],
                                 password, password, ETrue);
            console->SetPos(0,9);
            LogPrint(_L("LockDrive %S (%d)"), (error?&KError:&KOk), error);
            break;
            }

		case 'n':
        case 'N':
            {
            TMediaPassword password;
            MakePassword(password);
            error = fs.UnlockDrive(PropertyHandlers::allDrivesStatus[selectedDriveIndex*2],
                                   password, ETrue);
            Clear(9);
            LogPrint(_L("UnlockDrive %S (%d)"), (error?&KError:&KOk), error);
            }
			break;
			
		case 'c':
        case 'C':
            {
            TMediaPassword password;
            MakePassword(password);
            error = fs.ClearPassword(PropertyHandlers::allDrivesStatus[selectedDriveIndex*2],
                                     password);
            Clear(9);
            LogPrint(_L("ClearPassword %S (%d)"), (error?&KError:&KOk), error);
            }
			break;
#endif

		default:
			break;
		}
	RequestCharacter();
	}

#ifdef USB_BOOT_LOADER

static void RunMode()
	{
	RFs fs;

	TInt r=fs.Connect();
	if (r!=KErrNone)
		{
		RDebug::Print(_L("Help\n"));
		return;
		}

	TFileName sessionpath = _L("?:\\");

	TDriveList drivelist;
	fs.DriveList(drivelist);
	for (TInt driveno=EDriveC; driveno<=EDriveZ; driveno++)
		{
		if (!drivelist[driveno])
			continue;

		sessionpath[0]='A'+driveno;

		/*
		 If a filename with the format EJECTDELAY.nnn is found, delay any reboot
		 action by "nnn" seconds
		 */
		CDir* dir;
		TFindFile finder(fs);
		r=finder.FindWildByPath(_L("EJECTDELAY.*"),&sessionpath,dir);
		if (r == KErrNone)
			{ // Found one or more files
			TEntry entry;
			entry=(*dir)[0];

			TParse parser;
			parser.Set(entry.iName, NULL, NULL);
			TPtrC tok = parser.Ext();
			TLex lex(tok);
			lex.SkipAndMark(1);
			tok.Set(lex.NextToken());
			lex.Assign(tok);

			r=lex.Val(gRebootDelay);
			if (r!=KErrNone)
				continue;
			}
		}
	}

#endif

//////////////////////////////////////////////////////////////////////////////
//
// Application entry point
//
//////////////////////////////////////////////////////////////////////////////
static void RunAppL()
	{
#ifdef USB_BOOT_LOADER
	RunMode();
#endif

    TInt error = KErrUnknown;

	//RDebug::Print(_L("USBMSAPP: Creating console\n"));

#ifdef USB_BOOT_LOADER
	console = Console::NewL(KVariantUsbmsTitle,TSize(KConsFullScreen,KConsFullScreen));
#else
	console = Console::NewL(KTxtApp,TSize(KConsFullScreen,KConsFullScreen));
#endif

	CleanupStack::PushL(console);

	console->SetPos(0,2);
	console->Printf(_L("========================================"));

	CActiveScheduler* sched = new(ELeave) CActiveScheduler;
	CleanupStack::PushL(sched);
	CActiveScheduler::Install(sched);

	TBuf<20> KDriverFileName;
	// Load the logical device
	RDevUsbcClient usb;
	RDevUsbcScClient usbsc;
	RChunk gChunk;

	fs.Connect();
	CleanupClosePushL(fs);

	_LIT(KMountAllDefault,"(all)");
	console->SetPos(0,3);
	LogPrint(_L("Drives to auto-mount: %S"), (mountList.Length() ? &mountList : &KMountAllDefault));

	// Add MS file system
	error = fs.AddFileSystem(KMsFsy);

	if(error != KErrNone && error != KErrAlreadyExists)
		{
		User::Leave(error);
		}
	console->SetPos(0,4);
	LogPrint(_L("MSFS file system:\tAdded OK\n"));

	if (gSharedChunkLdd)
		{
		KDriverFileName = _L("EUSBCSC.LDD");
		error = User::LoadLogicalDevice(KDriverFileName);
		}
	else
		{
		KDriverFileName = _L("EUSBC.LDD");
		error = User::LoadLogicalDevice(KDriverFileName);
		}

	if (error != KErrAlreadyExists)
		{
		User::LeaveIfError(error);
		}


	if (gSharedChunkLdd)
		{
		error = usbsc.Open(0);
		}
	else
		{
		error = usb.Open(0);
		}

	User::LeaveIfError(error);

#ifdef BUILD_OTG_USBMSAPP

	_LIT(KOtgdiLddFilename, "otgdi");
	// Check for OTG support
	TBuf8<KUsbDescSize_Otg> otg_desc;
		if (gSharedChunkLdd)
		{
		error = usbsc.GetOtgDescriptor(otg_desc);
		}
	else
		{
		error = usb.GetOtgDescriptor(otg_desc);
		}

	if (!(error == KErrNotSupported || error == KErrNone))
		{
		LogPrint(_L("Error %d while fetching OTG descriptor"), error);
		User::Leave(-1);
		return;
		}

	// On an OTG device we have to start the OTG driver, otherwise the Client
	// stack will remain disabled forever.
	if (error == KErrNotSupported)
	{
	if (gSharedChunkLdd)
		{
		CleanupClosePushL(usbsc);
		}
	else
		{
		CleanupClosePushL(usb);
		}

		User::Leave(-1);
	}

	error = User::LoadLogicalDevice(KOtgdiLddFilename);

	if (error != KErrNone)
		{
		LogPrint(_L("Error %d on loading OTG LDD"), error);
		User::Leave(-1);
		return;
		}

	RUsbOtgDriver iOtgPort;

	error = iOtgPort.Open();

	if (error != KErrNone)
		{
		LogPrint(_L("Error %d on opening OTG port"), error);
		User::Leave(-1);
		return;
		}
	error = iOtgPort.StartStacks();

	if (error != KErrNone)
		{
		LogPrint(_L("Error %d on starting USB stack"), error);
		User::Leave(-1);
		return;
		}

#endif

	if (gSharedChunkLdd)
		{
		CleanupClosePushL(usbsc);
		RChunk *tChunk = &gChunk;
		usbsc.FinalizeInterface(tChunk);
		}
	else
		{
		CleanupClosePushL(usb);
		}


//		RDebug::Print(_L("USBMSAPP: Create active objects\n"));
	CMessageKeyProcessor::NewLC(console);
	CPropertyWatch::NewLC(EUsbMsDriveState_KBytesRead, PropertyHandlers::Read);
	CPropertyWatch::NewLC(EUsbMsDriveState_KBytesWritten, PropertyHandlers::Written);
	CPropertyWatch::NewLC(EUsbMsDriveState_DriveStatus, PropertyHandlers::DriveStatus);
	CPropertyWatch::NewLC(EUsbMsDriveState_MediaError, PropertyHandlers::MediaError);
	if (gSharedChunkLdd)
		{
		CUsbWatch::NewLC(&usbsc);
		}
	else
		{
		CUsbWatch::NewLC(&usb);
		}

	CPeriodUpdate::NewLC();

	RUsbMassStorage UsbMs;
	TBuf<8>  t_vendorId(_L("vendor"));
	TBuf<16> t_productId(_L("product"));
	TBuf<4>  t_productRev(_L("1.00"));

	TMassStorageConfig msConfig;
	msConfig.iVendorId.Copy(t_vendorId);
	msConfig.iProductId.Copy(t_productId);
	msConfig.iProductRev.Copy(t_productRev);

//   	console->Printf(_L("Connect to Mass Storage"));
	error = UsbMs.Connect();
	User::LeaveIfError(error);

//   	console->Printf(_L("Start Mass Storage"));
	error = UsbMs.Start(msConfig);
	User::LeaveIfError(error);

	TBuf8<KUsbDescSize_Device> deviceDescriptor;
	if (gSharedChunkLdd)
		{
		error = usbsc.GetDeviceDescriptor(deviceDescriptor);
		}
	else
		{
		error = usb.GetDeviceDescriptor(deviceDescriptor);
		}

	User::LeaveIfError(error);

	const TInt KUsbSpecOffset = 2;
	const TInt KUsbDeviceClassOffset = 4;
	const TInt KUsbVendorIdOffset = 8;
	const TInt KUsbProductIdOffset = 10;
	const TInt KUsbDevReleaseOffset = 12;
	//Change the USB spec number to 2.00
	deviceDescriptor[KUsbSpecOffset]   = 0x00;
	deviceDescriptor[KUsbSpecOffset+1] = 0x02;
	//Change the Device Class, Device SubClass and Device Protocol 
	deviceDescriptor[KUsbDeviceClassOffset] = 0x00;
	deviceDescriptor[KUsbDeviceClassOffset+1] = 0x00;
	deviceDescriptor[KUsbDeviceClassOffset+2] = 0x00;
	//Change the device vendor ID (VID) to 0x0E22 (Symbian)
	deviceDescriptor[KUsbVendorIdOffset]   = 0x22;   // little endian
	deviceDescriptor[KUsbVendorIdOffset+1] = 0x0E;
	//Change the device product ID (PID) to 0x1111
	deviceDescriptor[KUsbProductIdOffset]   = 0x12;
	deviceDescriptor[KUsbProductIdOffset+1] = 0x11;
	//Change the device release number to 3.05
	deviceDescriptor[KUsbDevReleaseOffset]   = 0x05;
	deviceDescriptor[KUsbDevReleaseOffset+1] = 0x03;
	if (gSharedChunkLdd)
		{
		error = usbsc.SetDeviceDescriptor(deviceDescriptor);
		}
	else
		{
		error = usb.SetDeviceDescriptor(deviceDescriptor);
		}

	User::LeaveIfError(error);

	// Remove possible Remote-Wakup support in Configuration descriptor,
	// so that we can use the MSC device also easily for Chapter9 testing.
	TBuf8<KUsbDescSize_Config> configDescriptor;
	if (gSharedChunkLdd)
		{
		error = usbsc.GetConfigurationDescriptor(configDescriptor);
		}
	else
		{
		error = usb.GetConfigurationDescriptor(configDescriptor);
		}

	User::LeaveIfError(error);
	const TInt KConfDesc_AttribOffset = 7;
	configDescriptor[KConfDesc_AttribOffset] &= ~KUsbDevAttr_RemoteWakeup;
	if (gSharedChunkLdd)
		{
		error = usbsc.SetConfigurationDescriptor(configDescriptor);
		}
	else
		{
		error = usb.SetConfigurationDescriptor(configDescriptor);
		}

	User::LeaveIfError(error);

	_LIT16(productID_L, "Symbian USB Mass Storage Device (Base)");
	TBuf16<KUsbStringDescStringMaxSize / 2> productID(productID_L);
		if (gSharedChunkLdd)
		{
		error = usbsc.SetProductStringDescriptor(productID);
		}
	else
		{
		error = usb.SetProductStringDescriptor(productID);
		}

	User::LeaveIfError(error); 

	TRequestStatus enum_status;
	console->SetPos(0,5);
	LogPrint(_L("Re-enumerating...\n"));

#ifdef BUILD_OTG_USBMSAPP
    // For OTG: The USB stack may not yet in the peripheral role. If it is not,
    // then ReEnumerate() will not work here as the stack will ignore the call
    // since the stack is not active. Therefore we simulate device connection to
    // force the stack into peripheral role by calling DeviceConnectToHost().
	if (gSharedChunkLdd)
		{
        usbsc.DeviceConnectToHost();
        usbsc.ReEnumerate(enum_status);
		
		}
	else
		{
        usb.DeviceConnectToHost();
        usb.ReEnumerate(enum_status);		
		}    
#else
	if (gSharedChunkLdd)
		{
		usbsc.ReEnumerate(enum_status);
		}
	else
		{
		usb.ReEnumerate(enum_status);
		}
#endif

	User::LeaveIfError(error);
	console->SetPos(0,5);
	User::WaitForRequest(enum_status);
	if(enum_status.Int() == KErrNone)
		LogPrint(_L("Re-enumeration Done\n"));
	else
		LogPrint(_L("Re-enumeration not successfully done\n"));

#ifndef USB_BOOT_LOADER
    console->SetPos(0,14);
    TBuf<3>password(KDefPwd);
    LogPrint(_L("Password: %S"), &password);
#endif
	ShowDriveSelection();

	console->SetPos(0,17);
#ifdef USB_BOOT_LOADER
	_LIT(KMsgTitleB,"Menu:\n[Esc,Q]=RESET (boot image)\n");
#else
	_LIT(KMsgTitleB,"Menu: q=quit  d=chg drv\n      m=mount u=unmount\n       l=lock n=unlock\n      c=clr pwd");
#endif

	//RDebug::Print(_L("USBMSAPP: Start CActiveScheduler\n"));

	console->Printf(KMsgTitleB);

#ifdef USB_BOOT_LOADER
	// Mount the mass storage on variant specific drive
	MountMsFs(KVariantUsbmsRemoveableDrive);
#endif

	CActiveScheduler::Start();

	error = UsbMs.Stop();
	User::LeaveIfError(error);
	UsbMs.Close();
	error = fs.RemoveFileSystem(KMsFs);
	User::LeaveIfError(error);

	//The console object is left on the Cleanup Stack,
	//which is used in the delay processing logic of rebootit(). 
	CleanupStack::PopAndDestroy(10);

#ifdef BUILD_OTG_USBMSAPP
	iOtgPort.StopStacks();
	iOtgPort.Close();
	error = User::FreeLogicalDevice(RUsbOtgDriver::Name());
	User::LeaveIfError(error);
#endif

	// UnLoad the logical device
	TBuf<20> KDriverName;
	if (gSharedChunkLdd)
		{
		KDriverName = _L("USBCSC");
		gChunk.Close();
		usbsc.Close();
		User::After(100000);
		error = User::FreeLogicalDevice(KDriverName);
		}
	else
		{
		KDriverName = _L("USBC");
		error = User::FreeLogicalDevice(KDriverName);
		}

	User::LeaveIfError(error); 

#ifdef USB_BOOT_LOADER
	rebootit();
#endif
	CleanupStack::PopAndDestroy(1);
	}

TInt ParseCommandLine()
	{
	TBuf<32> args;
	User::CommandLine(args);
	TLex lex(args);
	TInt err=KErrNone;
	FOREVER
		{
		TPtrC token=lex.NextToken();

		if(token.Length()!=0)
			{
			if ((token.MatchF(_L("-sc")) == KErrNone))
				{
				gSharedChunkLdd = ETrue;
				err = KErrNone;
				}
			else
				{
				// Command line: list of drive letters to auto-mount (all if not specified)
				mountList.Append(token);
				mountList.UpperCase();
				err = KErrNone;
				} // endif token 
			}
		else
			break;
		}
	return err;
	}



GLDEF_C TInt E32Main()
	{
	__UHEAP_MARK;
	CTrapCleanup* cleanup=CTrapCleanup::New();

	if (ParseCommandLine())
		return KErrNone;

    msfsMountedList.Reset();  
    unmountedFsList.Reset();  

	
    TRAPD(error,RunAppL());
#ifdef USB_BOOT_LOADER
	__ASSERT_ALWAYS(!error, User::Panic(KVariantUsbmsTitle, error));
#else
	__ASSERT_ALWAYS(!error, User::Panic(KTxtApp, error));
#endif

	delete cleanup;
	__UHEAP_MARKEND;
	return 0;
	}




//-----------------------------------------------------------------------------

CFileSystemDescriptor::~CFileSystemDescriptor()
    {
    iFsName.Close();
    iPrimaryExtName.Close();
    }

//-----------------------------------------------------------------------------
CFileSystemDescriptor* CFileSystemDescriptor::NewL(const TDesC& aFsName, const TDesC& aPrimaryExtName, TBool aDrvSynch)
    {
    CFileSystemDescriptor* pSelf = new (ELeave) CFileSystemDescriptor;

    CleanupStack::PushL(pSelf);
    
    pSelf->iFsName.CreateMaxL(aFsName.Length());
    pSelf->iFsName.Copy(aFsName);
    
    pSelf->iPrimaryExtName.CreateMaxL(aPrimaryExtName.Length());
    pSelf->iPrimaryExtName.Copy(aPrimaryExtName);

    pSelf->iDriveSynch = aDrvSynch;

    CleanupStack::Pop();

    return pSelf;
    }