kerneltest/f32test/shostmassstorage/testclient/usbtestmsclient/drivemanager.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) 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:
// Class implementation of CDriveManager and CMassStorageDrive.
// 
//



/**
 @file
 @internalTechnology
*/

#include <f32fsys.h>
#include <e32property.h>


#include "usbmsshared.h"
#include "msctypes.h"

#include "drivepublisher.h"
#include "drivemanager.h"
#include "debug.h"
#include "msdebug.h"


void TMediaParams::Init(TLocalDriveCapsV4& aCaps)
    {
    iSize = aCaps.MediaSizeInBytes();
    TInt64 driveBlocks =  iSize / MAKE_TINT64(0, KDefaultBlockSize);
    iNumBlocks = I64LOW(driveBlocks);
    iMediaAtt = aCaps.iMediaAtt;
    }


void TLocalDriveRef::SetDriveState(TDriveState aState)
    {
    if (iDriveState != aState)
        {
        CMountCB* mount = iProxyDrive.Mount();
        __ASSERT_DEBUG(mount != NULL, User::Invariant());
        if (mount)
            {
            if (!IsActive(iDriveState) && IsActive(aState))
                {
                mount->IncLock();
                }
            else if (IsActive(iDriveState) && !IsActive(aState))
                {
                mount->DecLock();
                }
            __PRINT1(_L("SetDriveState: LockStatus=%d\n"), mount->LockStatus());
            }

        iDriveState = aState;
        iDriveStateChangedPublisher.DriveStateChanged();
        }
    }


TInt TLocalDriveRef::Read(const TInt64& aPos, TInt aLength, TDes8& aBuf, TBool aWholeMedia)
	{
    __MSFNLOG

	TInt err = KErrUnknown; // never return this

	if(aWholeMedia)
		{
		err = iProxyDrive.Read(aPos, aLength, &aBuf, KLocalMessageHandle, 0, RLocalDrive::ELocDrvWholeMedia);
		}
	else
		{
		err = iProxyDrive.Read(aPos, aLength, aBuf);
		}

	if (err == KErrLocked)
		{
		SetDriveState(TLocalDriveRef::ELocked);
		}

	return err;
	}


TInt TLocalDriveRef::Write(const TInt64& aPos, TDesC8& aBuf, TBool aWholeMedia)
    {
	TInt err = KErrNone;

	TDriveState oldState = iDriveState;
	if (oldState != EActive)
        {
		// SCSI hasn't called SetCritical
		SetDriveState(EActive);
		}

	if (aWholeMedia)
		{
		err = iProxyDrive.Write(aPos, aBuf.Length(), &aBuf, KLocalMessageHandle, 0, RLocalDrive::ELocDrvWholeMedia);
		}
	else
		{
		err = iProxyDrive.Write(aPos,aBuf);
		}

	if (err == KErrLocked)
		{
		SetDriveState(ELocked);
		}
	else if (oldState != EActive)
		{
		SetDriveState(oldState);
		}
    return err;
    }


/**
Checks the Media Changed flag, and optionally resets it.
@return The state of the Media Changed flag.
@param aReset If true, the Media Changed flag is reset to EFalse.
*/
TBool TLocalDriveRef::IsMediaChanged(TBool aReset)
	{
    __MSFNLOG
	TBool mediaChanged = iMediaChanged;
	if (aReset)
        {
	   	iMediaChanged = EFalse;
	   	}
	return mediaChanged;
	}


/**
Set the Drive State to Active or Idle.
@return KErrNone on success, KErrNotReady if media not present, KErrDisMounted if not mounted
@param aCritical ETrue for Active, EFalse for Idle
*/
TInt TLocalDriveRef::SetCritical(TBool aCritical)
	{
    __MSFNLOG
	TInt err = KErrNone;
    if (iDriveState == EMediaNotPresent)
		{
		err = KErrNotReady;
		}
	else
		{
        SetDriveState(aCritical ? EActive : EIdle);
		}
	return err;
	}


/**
Provides an interface to CProxyDrive::Caps that hides the
package buffer.
@return KErrNone on success, otherwise system wide error code
@param aInfo
*/
TInt TLocalDriveRef::Caps(TLocalDriveCapsV4& aInfo)
	{
    __MSFNLOG
	TLocalDriveCapsV4Buf buf;
	buf.FillZ();

	__PRINT(_L("CMassStorageDrive::DoCaps calling Caps\n"));
	TInt err = iProxyDrive.Caps(buf);
	__PRINT1(_L("CMassStorageDrive::DoCaps: Caps returned %d\n"), err);

	if (err == KErrNone)
		{
		// Invoke function call operator to cast to TLocalDriveCapsV4&
		aInfo = buf();
		}

	return err;
	}


///////////////////////////////////////////////////////////////////////////////

/**
@param aCritSec A Critical Section object shared by all drives.
@param aDrives Reference to the list of CMassStorageDrive objects.
@param aDriveMap Reference to array mapping lun to drive number for supported
	   mass storage drives.
@post Object is fully constructed
 */
CMassStorageDrive* CMassStorageDrive::NewL(RCriticalSection& aCritSec,
                                           RDriveStateChangedPublisher& aDriveStateChangedPublisher)
    {
    __MSFNSLOG
	CMassStorageDrive* self = new (ELeave) CMassStorageDrive(aCritSec, aDriveStateChangedPublisher);
	CleanupStack::PushL(self);
	self->ConstructL();
	CleanupStack::Pop();
	return self;
    }


CMassStorageDrive::CMassStorageDrive(RCriticalSection& aCritSec,
									 RDriveStateChangedPublisher& aDriveStateChangedPublisher)
:   iCritSec(aCritSec),
	iMountState(EDisconnected),
	iDriveStateChangedPublisher(aDriveStateChangedPublisher)
	{
    __MSFNLOG
	}


void CMassStorageDrive::ConstructL()
    {
    __MSFNLOG
    iDriveMediaErrorPublisher = new (ELeave) RDriveMediaErrorPublisher();
    }


CMassStorageDrive::~CMassStorageDrive()
	{
    __MSFNLOG
    delete iDriveMediaErrorPublisher;
	delete iLocalDrive;
	}

/**
Read from the target drive unit.
@return KErrNone on success, otherwise system wide error code
*/
TInt CMassStorageDrive::Read(const TInt64& aPos, TInt aLength, TDes8& aBuf, TBool aWholeMedia)
	{
    __MSFNLOG

	TInt err = KErrUnknown; // never return this
	iCritSec.Wait();

	if(iMountState != EConnected)
		{
		err = KErrDisconnected;
		}
	else
		{
        err = iLocalDrive->Read(aPos, aLength, aBuf, aWholeMedia);
		}

	iCritSec.Signal();
	return err;
	}


/**
Write to the target drive unit.
@return KErrNone on success, otherwise system wide error code
*/
TInt CMassStorageDrive::Write(const TInt64& aPos, TDesC8& aBuf, TBool aWholeMedia)
	{
    __MSFNLOG

	TInt err = KErrNone;
	iCritSec.Wait();

	if (iMountState != EConnected)
		{
		err = KErrDisconnected;
		}
	else
		{
		__ASSERT_DEBUG(iLocalDrive, User::Invariant());
        err = iLocalDrive->Write(aPos, aBuf, aWholeMedia);
		}

	iCritSec.Signal();
	return err;
	}


/**
Provides an interface to CProxyDrive::Caps that hides the
package buffer.
@return KErrNone on success, otherwise system wide error code
@param aInfo
*/
TInt CMassStorageDrive::DoCaps(TLocalDriveCapsV4& aInfo)
	{
    __MSFNLOG
    TInt err = KErrDisMounted;

    if (iLocalDrive)
        {
        err = iLocalDrive->Caps(aInfo);
        }
	return err;
	}


/**
Publish media error, user should reinsert the memory card.
Similar to FAT32's TDriver::HandleCriticalError.
Note: User notification is not implemented, instead we abort and dismount.
*/
TInt CMassStorageDrive::HandleCriticalError()
	{
    __MSFNLOG
	TRAPD(err, iDriveMediaErrorPublisher->PublishErrorL(ETrue));
    // ignore leave
    err = err;
	return KErrAbort;
	}


TInt CMassStorageDrive::ClearCriticalError()
	{
    __MSFNLOG
	TRAPD(err, iDriveMediaErrorPublisher->PublishErrorL(EFalse));
    // ignore leave
    err = err;
	return KErrNone;
	}


/**
Checks the Media Changed flag, and optionally resets it.
@return The state of the Media Changed flag.
@param aReset If true, the Media Changed flag is reset to EFalse.
*/
TBool CMassStorageDrive::IsMediaChanged(TBool aReset)
	{
    __MSFNLOG

	iCritSec.Wait();

	TBool mediaChanged = EFalse;
	if (iLocalDrive)
		{
		mediaChanged = iLocalDrive->IsMediaChanged(aReset);
        }

	iCritSec.Signal();

	__PRINT1(_L("CMassStorageDrive::IsMediaChanged: returning %d\n"), mediaChanged);
	return mediaChanged;
	}

/**
Set the Drive State to Active or Idle.
@return KErrNone on success, KErrNotReady if media not present, KErrDisMounted if not mounted
@param aCritical ETrue for Active, EFalse for Idle
*/
TInt CMassStorageDrive::SetCritical(TBool aCritical)
	{
    __MSFNLOG

	TInt err = KErrDisMounted;
	iCritSec.Wait();
	if (iLocalDrive)
		{
        err = iLocalDrive->SetCritical(aCritical);
		}

	iCritSec.Signal();
	return err;
	}

/**
Set the mount state
*/
void CMassStorageDrive::SetMountConnectedL(CProxyDrive& aProxyDrive,
                                           TBool& aMediaChanged,
                                           RDriveStateChangedPublisher& aDriveStateChangedPublisher)
	{
    __MSFNLOG
	TLocalDriveRef* localDrive = NULL;

	__PRINT(_L("SetMountConnectedL entering critical section\n"));
	iCritSec.Wait(); // note: signalled in SetMountState

   	TRAPD(err, localDrive = new (ELeave) TLocalDriveRef(aProxyDrive,
                                                        aMediaChanged,
                                                        aDriveStateChangedPublisher));
   	if (err)
   		{
   		iCritSec.Signal();
   		User::Leave(err);
   		}
	iLocalDrive = localDrive;
	SetMountState(EConnected, ETrue);
	}

/**
@return KErrNone
@param aNewState
@param aLocalDrive Only provide this if aNewState is EConnected.
*/
void CMassStorageDrive::SetMountState(TMountState aNewState, TBool aCriticalSection/*=EFalse*/)
	{
    __MSFNLOG
	if(iMountState == aNewState)
		{
		__PRINT(_L("SetMountState: No change\n"));
		}
	else
		{
		// If called from SetMountConnected, already in critical section,
        // otherwise, must enter it here.
        if (!aCriticalSection)
            {
			iCritSec.Wait();
            }

		switch(aNewState)
			{
			case EDisconnected:
				delete iLocalDrive;
				iLocalDrive = NULL;
				break;

			case EConnected:
			case EDisconnecting:
			case EConnecting:
				// Do not change iLocalDrive for these state changes
				break;
			}

		iMountState = aNewState;
		__PRINT1(_L("SetMountState: state=%d\n"), iMountState);

		iDriveStateChangedPublisher.DriveStateChanged();
		iCritSec.Signal();
		__PRINT(_L("SetMountState has left the critical section\n"));
		}
	}

/**
@return Current drive media state
*/
TLocalDriveRef::TDriveState CMassStorageDrive::DriveState() const
	{
    __MSFNSLOG
	return iLocalDrive ? iLocalDrive->DriveState() : TLocalDriveRef::EErrDisMounted;
	}

/**
Check for media not present, and return the drive state.
@return Current drive media state
*/
TLocalDriveRef::TDriveState CMassStorageDrive::CheckDriveState()
	{
    __MSFNLOG
	TLocalDriveRef::TDriveState state = TLocalDriveRef::EErrDisMounted;
	iCritSec.Wait();

	if (iLocalDrive)
		{
		TInt err = KErrGeneral;
        TLocalDriveCapsV4 caps;

		FOREVER
			{
			// Initialise in case Caps() fails
			caps.iType = ::EMediaNotPresent;
			err = DoCaps(caps);

			__PRINTERR(_L("CheckDriveState: DoCaps err=%d\n"), err);
			if (err == KErrNotReady || (err == KErrNone && caps.iType == ::EMediaNotPresent))
				{
				__PRINT(_L("CheckDriveState: detected MediaNotPresent\n"));

				SetDriveState(TLocalDriveRef::EMediaNotPresent);

				if (HandleCriticalError() == KErrAbort)
					break;
				}
			else
				{
				ClearCriticalError();
				break;
				}
			}

		if (err == KErrNone && caps.iType != ::EMediaNotPresent)
			{
            iMediaParams.Init(caps);
            TLocalDriveRef::TDriveState driveState = TLocalDriveRef::EIdle;

			if (iLocalDrive->DriveState() == TLocalDriveRef::EMediaNotPresent)
				{
				__PRINT(_L("CheckDriveState: detected media inserted\n"));
				}
			else if (iLocalDrive->DriveState() == TLocalDriveRef::ELocked &&
					 !(caps.iMediaAtt & KMediaAttLocked))
				{
				__PRINT(_L("CheckDriveState: detected media unlocked\n"));
				}
			else if (caps.iMediaAtt & KMediaAttLocked)
				{
				__PRINT(_L("CheckDriveState: detected media locked\n"));
				driveState = TLocalDriveRef::ELocked;
				}
            SetDriveState(driveState);
			}

		// Get the current state
		state = iLocalDrive->DriveState();
		}

	iCritSec.Signal();

	return state;
	}


/**
@param aNewState
*/
void CMassStorageDrive::SetDriveState(TLocalDriveRef::TDriveState aNewState)
	{
    __MSFNLOG
	__ASSERT_DEBUG(aNewState == TLocalDriveRef::EIdle ||
                   (iMountState == EConnected && NULL != iLocalDrive) ||
                   (iMountState == EDisconnecting && NULL != iLocalDrive),
        User::Invariant());

	if (!iLocalDrive)
		{
		__PRINT(_L("SetDriveState: Drive not mounted.\n"));
		}
	else
		{
        iLocalDrive->SetDriveState(aNewState);
		__PRINT2(_L("SetDriveState: %d->%d\n"), iLocalDrive->iDriveState, aNewState);
		}
	}


/////////////////////////////////////////////////////////////////

/**
Construct a CDriveManager object.
@param aDriveMap Reference to array mapping lun to drive number for supported
	   mass storage drives.
*/
CDriveManager* CDriveManager::NewL(const TLunToDriveMap& aDriveMap)
	{
    __MSFNSLOG
	__PRINT1(_L("CDriveManager::NewL - %d drives\n"), aDriveMap.Count());

	CDriveManager* self = new (ELeave) CDriveManager(aDriveMap.Count() -1);
	CleanupStack::PushL(self);
	self->ConstructL(aDriveMap);
	CleanupStack::Pop();
	return self;
	}

CDriveManager::CDriveManager(TLun aMaxLun)
:   iMaxLun(aMaxLun)
	{
    __MSFNLOG
    }

/**
Construct a CDriveManager object.
*/
void CDriveManager::ConstructL(const TLunToDriveMap& aDriveMap)
	{
    __MSFNLOG
	User::LeaveIfError(iDriveCritSec.CreateLocal());

    iDriveStateChangedPublisher = new (ELeave) RDriveStateChangedPublisher(iDrives, aDriveMap);

    iDrives.Reserve(iMaxLun + 1);

	for (TLun lun = 0; lun < iMaxLun + 1; lun++)
		{
		iDrives.Append(CMassStorageDrive::NewL(iDriveCritSec,
                                               *iDriveStateChangedPublisher));
		}

	// Publish initial drive state
	if (iDrives.Count() > 0)
		{
		iDriveStateChangedPublisher->DriveStateChanged();
		}
	}

/**
Destructor
*/
CDriveManager::~CDriveManager()
	{
    __MSFNLOG
	iDrives.ResetAndDestroy();
	delete iDriveStateChangedPublisher;
	iDriveCritSec.Close();
	}

/**
Set the mount state to Connected and specify the Proxy Drive.
@return KErrNone on success, otherwise system wide error code
@param aDrive The mounted Proxy Drive
@param aLun The Logical Drive Unit identifier (0..numDrives-1)
@pre If the Mount State is Connected, then aDrive must be the
     same as it was the last time this function was called.
@post The Mount State will be Connected.
*/
void CDriveManager::RegisterDriveL(CProxyDrive& aProxyDrive, TBool& aMediaChanged, TLun aLun)
	{
    __MSFNLOG
	__PRINT1(_L("Lun=%d \n"),aLun);
	CMassStorageDrive* drive = Drive(aLun);
	drive->SetMountConnectedL(aProxyDrive, aMediaChanged, *iDriveStateChangedPublisher);
	}

/**
Set the mount state to Disconnected.
@return KErrNone on success, otherwise system wide error code
@param aLun The Logical Drive Unit identifier (0..numDrives-1)
@post The Mount State will be Disconnected.
*/
void CDriveManager::DeregisterDrive(TLun aLun)
	{
    __MSFNLOG
    CMassStorageDrive* drive = Drive(aLun);
	drive->SetMountDisconnected();
	}

/**
Return a pointer to the drive specified aLun, or NULL if aLun is invalid.

@return Pointer to the specified drive, or NULL.
@param aLun The Logical Drive Unit identifier (0..numDrives-1)
@param aError KErrNone on success, KErrArgument if NULL is returned.
*/
CMassStorageDrive* CDriveManager::Drive(TLun aLun) const
	{
    __MSFNSLOG
	__ASSERT_DEBUG(aLun < iDrives.Count(), User::Invariant());
    return iDrives[aLun];
	}

/**
Checks the Media Changed flag, and optionally resets it.
@return The state of the Media Changed flag.
@param aLun The Logical Drive Unit identifier (0..numDrives-1)
@param aReset If true, the Media Changed flag is reset to EFalse.
*/
TBool CDriveManager::IsMediaChanged(TLun aLun, TBool aReset)
	{
    __MSFNLOG
	CMassStorageDrive* drive = Drive(aLun);
	return drive->IsMediaChanged(aReset);
	}

/**
Set the Drive State to Active or Idle.
Ref: 3.6.3.2 - PREVENT_MEDIUM_REMOVAL
@return KErrNone on success, otherwise system wide error code
@param aLun The Logical Drive Unit identifier (0..numDrives-1)
@param aCritical ETrue for Active, EFalse for Idle
*/
TInt CDriveManager::SetCritical(TLun aLun, TBool aCritical)
	{
    __MSFNLOG
	TInt err = KErrUnknown; // never return this

	TLun i = aLun;
	TLun cnt = aLun + 1;

	if (aLun == KAllLuns)
		{
		i = 0;
		cnt = iMaxLun + 1;
		}

	for(; i < cnt; i++)
		{
		CMassStorageDrive* drive = Drive(i);
		err = drive->SetCritical(aCritical);
		}
	return err;
	}

void CDriveManager::Connect()
	{
	__FNLOG("CDriveManager::Connect");
    TLun lun = iMaxLun;
    do
        {
        Connect(lun);
        }
    while(--lun >= 0);
    }

/**
Inititiate transition to Connected.
@return KErrNone on success, otherwise system wide error code
@param aLun The Logical Drive Unit identifier (0..numDrives-1)
@post The Mount State will be Connected or Connecting.
*/
void CDriveManager::Connect(TLun aLun)
	{
    __MSFNLOG
	CMassStorageDrive* drive = Drive(aLun);

	__PRINT2(_L("CDriveManager::Connect lun=%d, mountState=%d\n"), aLun, drive->MountState());

   	switch(drive->MountState())
   		{
 	case CMassStorageDrive::EDisconnected:
 		drive->SetMountConnecting();
 		break;
 	case CMassStorageDrive::EDisconnecting:
 		drive->SetMountConnected();
 		break;
 	case CMassStorageDrive::EConnected:
 	case CMassStorageDrive::EConnecting:
    default:
 		// do nothing
 		break;
		}
	}

void CDriveManager::Disconnect()
	{
	__FNLOG("CDriveManager::Disconnect");
    TLun lun = iMaxLun;
    do
        {
        Disconnect(lun);
        }
    while(--lun >= 0);
    }

/**
Inititiate transition to Disconnected.
@return KErrNone on success, otherwise system wide error code
@param aLun The Logical Drive Unit identifier (0..numDrives-1)
@post The Mount State will be Disconnected or Disconnecting.
*/
void CDriveManager::Disconnect(TLun aLun)
	{
    __MSFNLOG
	CMassStorageDrive* drive = Drive(aLun);
   	switch (drive->MountState())
   		{
   	case CMassStorageDrive::EConnected:
   		drive->SetMountDisconnecting();
   		break;
   	case CMassStorageDrive::EConnecting:
   		drive->SetMountDisconnected();
   		break;
   	case CMassStorageDrive::EDisconnected:
   	case CMassStorageDrive::EDisconnecting:
   		// do nothing
   		break;
   		}
	}