usbclasses/usbphoneasmodem/classcontroller/src/usbmscfileclasscontroller.cpp
author Pat Downey <patd@symbian.org>
Wed, 01 Sep 2010 12:20:49 +0100
branchRCL_3
changeset 80 e02eb84a14d2
parent 79 25fce757be94
permissions -rw-r--r--
Revert incorrect RCL_3 drop: Revision: 201033 Kit: 201035

// 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 "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: Adheres to the UsbMan USB Class Controller API and talks to mass storage file server
// 
// 

#include <barsc.h> 
#include <barsread.h>
#include <usb_std.h>
#include <cusbclasscontrollerplugin.h>
#include <centralrepository.h>
#include <usbms.rsg>
#include <data_caging_path_literals.hrh> 
#include "usbmscfileclasscontroller.h"
#include "usbmscfile.h"
#include "usbmscfileclasscontroller_crkeys.h"
#include "debug.h"
 
// Panic category 
#ifdef _DEBUG
_LIT( KMsCcPanicCategory, "UsbMscFileCc" );
#endif

_LIT( KUsbMsResource, "\\private\\101fe1db\\usbms.rsc" );

/**
 Panic codes for the USB MSC File Class Controller.
 */
enum TMscFileCcPanic
	{
	//Class called while in an illegal state
	EBadApiCall = 0,
    EUnusedFunction = 1,
	};


static TInt CreateServerProcess()
    {
    TRACE_FUNC

    RProcess server;
    TInt result = server.Create( KMscFileServerFileName, KNullDesC );
    if ( result != KErrNone )
        {
        return  result;
        }

    TRequestStatus stat;

    server.Rendezvous( stat );
    if ( stat != KRequestPending )
        {
        server.Kill( 0 ); // abort startup
        }    
    else
        {
        server.Resume(); // logon OK - start the server
        server.SetPriority( EPriorityForeground );
        }
    User::WaitForRequest( stat ); // wait for start or death
  
    // we can't use the 'exit reason' if the server panicked as this
    // is the panic 'reason' and may be '0' which cannot be distinguished
    // from KErrNone
    result = ( server.ExitType() == EExitPanic ) ? KErrGeneral : stat.Int();    
    
    server.Close();

    return result;
    }

/**
 Start MSC File Server
 */
static TInt StartServer()
    {
    TInt result;

    TFindServer findMscFileServer( KMscFileServerName );
    TFullName name;

    result = findMscFileServer.Next( name );
    if ( result != KErrNone )
        {       
        result = CreateServerProcess();     
        }

    TRACE_INFO(( _L( "Server process created: %d" ), result ))
    return result;
    }

/**
 Constructs a CUsbMscFileClassController object
 
 @param	aOwner	USB Device that owns and manages the class
 @return	A new CUsbMscFileClassController object
 */
CUsbMscFileClassController* CUsbMscFileClassController::NewL(
	MUsbClassControllerNotify& aOwner)
	{
	TRACE_FUNC

	CUsbMscFileClassController* r = new (ELeave) CUsbMscFileClassController(aOwner);
	CleanupStack::PushL(r);
	r->ConstructL();
	CleanupStack::Pop();
	return r;
	}

/**
 Destructor
 */
CUsbMscFileClassController::~CUsbMscFileClassController()
	{
	Cancel();
	}

/**
 Constructor.
 
 @param	aOwner	USB Device that owns and manages the class
 */
CUsbMscFileClassController::CUsbMscFileClassController(
	MUsbClassControllerNotify& aOwner )
	: CUsbClassControllerPlugIn( aOwner, KMsStartupPriority )	
	{
	// Intentionally left blank
	}

/**
 2nd Phase Construction.
 */
void CUsbMscFileClassController::ConstructL()
	{
	TRACE_FUNC

	ReadMassStorageConfigL();
	}

/**
 Called by UsbMan when it wants to start the mass storage class. 
 
 @param aStatus The caller's request status, filled in with an error code
 */
void CUsbMscFileClassController::Start( TRequestStatus& aStatus )
	{
	TRACE_FUNC_ENTRY
	
	// The service state should always be idle when this function is called 
	// (guaranteed by CUsbSession).
	__ASSERT_DEBUG( iState == EUsbServiceIdle, 
	        User::Panic( KMsCcPanicCategory, EBadApiCall ) );

	TRequestStatus* reportStatus = &aStatus;

	iState = EUsbServiceStarting;

    TInt ret = StartServer();
    if ( ret != KErrNone )
        {
		iState = EUsbServiceIdle;
		User::RequestComplete( reportStatus, ret );
        TRACE_ERROR(( _L( "Error %d Can't start server." ), ret ))
        return;
        }
    
    TRAP( ret, SetupUnitsL() );
    if (ret != KErrNone)
        {
		iState = EUsbServiceIdle;
		User::RequestComplete( reportStatus, ret );
        TRACE_ERROR(( _L( "Error %d in SetupUnitsL." ), ret ))
        return;
        }


	// Connect to USB Mass Storage server
	ret = iMscFile.Connect();

	if ( ret != KErrNone )
		{
		iState = EUsbServiceIdle;
		User::RequestComplete( reportStatus, ret );
		TRACE_ERROR(( _L( "Error %d connecting to msc file server" ), ret ))
		return;
		}

	// StartL mass storage device
	ret = iMscFile.Start( iMsConfig );

	if (ret != KErrNone)
		{
		iState = EUsbServiceIdle;
		User::RequestComplete( reportStatus, ret );
		TRACE_ERROR(( _L( "Error %d starting msc file server" ), ret ))
		return;
		}

	iState = EUsbServiceStarted;

	User::RequestComplete(reportStatus, KErrNone);
	TRACE_FUNC_EXIT
	}

/**
 Called by UsbMan when it wants to stop the USB ACM class.
 
 @param aStatus KErrNone on success or a system wide error code
 */
void CUsbMscFileClassController::Stop( TRequestStatus& aStatus )
	{
	TRACE_FUNC_ENTRY
	
	// The service state should always be started when this function is called
	// (guaranteed by CUsbSession)
	__ASSERT_DEBUG( iState == EUsbServiceStarted, 
	                User::Panic( KMsCcPanicCategory, EBadApiCall ) );

	TRequestStatus* reportStatus = &aStatus;
	
	TInt ret = iMscFile.Stop();
	
	if ( ret != KErrNone )
		{
		iState = EUsbServiceStarted;
		User::RequestComplete( reportStatus, ret );
		TRACE_ERROR(( _L( "Error %d stopping msc file server" ), ret ))
		return;
		}	

	ret = iMscFile.Shutdown();
	if ( ret != KErrNone )
		{
		User::RequestComplete( reportStatus, ret );
		TRACE_ERROR(( _L( "Error %d stopping msc file server" ), ret ))
		}	
  else
	  {
		User::RequestComplete( reportStatus, KErrNone );
	  }    
	iMscFile.Close();
	
	iState = EUsbServiceIdle;
	TRACE_FUNC_EXIT
	}

/**
 Gets information about the descriptor which this class provides. Never called
 by usbMan.
 
 @param aDescriptorInfo Descriptor info structure filled in by this function
 */
void CUsbMscFileClassController::GetDescriptorInfo(TUsbDescriptor& /*aDescriptorInfo*/) const
	{
	__ASSERT_DEBUG( EFalse, User::Panic(KMsCcPanicCategory, EUnusedFunction));
	}

/**
 Standard active object RunL. Never called because this class has no
 asynchronous requests.
 */
void CUsbMscFileClassController::RunL()
	{
	__ASSERT_DEBUG( EFalse, User::Panic(KMsCcPanicCategory, EUnusedFunction) );
	}

/**
 Standard active object cancellation function. Never called because this
 class has no asynchronous requests.
 */
void CUsbMscFileClassController::DoCancel()
	{
	__ASSERT_DEBUG( EFalse, User::Panic(KMsCcPanicCategory, EUnusedFunction) );
	}

/**
 Standard active object error function. Never called because this class has
 no asynchronous requests, and hence its RunL is never called.
 
 @param aError The error code (unused)
 @return Always KErrNone to avoid an active scheduler panic
 */
TInt CUsbMscFileClassController::RunError(TInt /*aError*/)
	{
	__ASSERT_DEBUG( EFalse, User::Panic(KMsCcPanicCategory, EUnusedFunction) );
	return KErrNone;
	}

/**
 Read mass storage configuration info from the resource file
 */
void CUsbMscFileClassController::ReadMassStorageConfigL()
	{
	TRACE_FUNC_ENTRY

	// Try to connect to file server
	RFs fs;
	LEAVE_IF_ERROR( fs.Connect() );
	CleanupClosePushL( fs );

    TFileName fileName;
    const TDriveNumber KResourceDrive = EDriveZ;
    TDriveUnit driveUnit( KResourceDrive );
    TDriveName drive = driveUnit.Name();
    fileName.Insert( 0, drive );

    fileName += KUsbMsResource;

	RResourceFile resource;
	TRAPD( err, resource.OpenL( fs, fileName ) );

	if ( err != KErrNone )
		{
		TRACE_ERROR(( _L( "Error %d opening resource file" ), err ))
		CleanupStack::PopAndDestroy( &fs );
		return;
		}

	CleanupClosePushL( resource );

	resource.ConfirmSignatureL( KUsbMsResourceVersion );

	HBufC8* msConfigBuf = 0;
	TRAPD( ret, msConfigBuf = resource.AllocReadL( USBMS_CONFIG ) );
	if ( ret != KErrNone )
		{
		TRACE_ERROR(( _L( "Error %d opening mass storage config" ), ret ))
		CleanupStack::PopAndDestroy(2, &fs); 
		return;
		}
	CleanupStack::PushL( msConfigBuf );
	
	// The format of the USB resource structure is:
	
	/* 	
	 * 	STRUCT USBMASSSTORAGE_CONFIG
	 *	{
	 *	LTEXT	vendorId;           // no more than 8 characters
	 *	LTEXT	productId;          // no more than 16 characters
	 *	LTEXT	productRev;        	// no more than 4 characters
	 *	};
	 */
	 
	// Note that the resource must be read in this order!
	
	TResourceReader reader;
	reader.SetBuffer( msConfigBuf );

	TPtrC	vendorId		= reader.ReadTPtrC();
	TPtrC	productId		= reader.ReadTPtrC();
	TPtrC	productRev		= reader.ReadTPtrC();
	
	// populate iMsConfig, truncate if exceeding limit
	ConfigItem( vendorId, iMsConfig.iVendorId, 8 );
	ConfigItem( productId, iMsConfig.iProductId, 16 );
	ConfigItem( productRev, iMsConfig.iProductRev, 4 );
	
	// Debugging
	TRACE_INFO(( _L( "vendorId = %s" ),   &vendorId ))
	TRACE_INFO(( _L( "productId = %s" ),  &productId ))
	TRACE_INFO(( _L( "productRev = %s" ), &productRev ))
		
	CleanupStack::PopAndDestroy( 3, &fs ); // msConfigBuf, resource, fs
	TRACE_FUNC_EXIT
	}
	
/**
 Utility. Copies the data from TPtr to TBuf and checks data length 
 to make sure the source does not exceed the capacity of the target
 */
 void CUsbMscFileClassController::ConfigItem( const TPtrC& source, 
                                              TDes& target, 
                                              TInt maxLength )
 	{
 	if ( source.Length() < maxLength )
 		{
 		maxLength = source.Length();
 		}
 		
 	target.Copy( source.Ptr(), maxLength );	 	
 	}

void CUsbMscFileClassController::SetupUnitsL()
    {
    RFs fs;
    RUsbMscFile mscFile;
    
    LEAVE_IF_ERROR( mscFile.Connect() );
    CleanupClosePushL( mscFile );
    CRepository* repository = CRepository::NewLC(KCRUidUsbPhoneAsModemMscFileClassController);
    
	RArray<TUint32> lunKeys;
	CleanupClosePushL(lunKeys);
	
	repository->FindL(KCRMscFileLogicalUnitNumberColumn, KCRMscFileColumnMask, lunKeys);

	
	TInt count = lunKeys.Count();
	for (TInt i=0; i<count; i++)
		{
		TUint32 lunKey = lunKeys[i];
		TUint32 fullNameKey = ((lunKey&KCRMscFileRowMask)|KCRMscFileImageFullNameColumn);
		TRACE_INFO( (_L( "lunKey=%d,fullNameKey=%d" ), lunKey, fullNameKey) )

		TInt lun = 0;
		LEAVE_IF_ERROR(repository->Get(lunKey, lun));
		
		TFileName fullName;
		LEAVE_IF_ERROR(repository->Get(fullNameKey, fullName));	
		
		TRACE_INFO( (_L("lun=%d, fullName=%S"), lun, &fullName) );
	
		TInt protocol = 0; // It is not used. Bulk Transport(0x50), SCSI transparent command Set(0x06), PDT CD/DVD device(0x05) is supported only.
		
		LEAVE_IF_ERROR(mscFile.SetupLogicalUnit( fullName, protocol, lun ));
		}
    CleanupStack::PopAndDestroy( 3 ); // mscFile, repository, lunKeys
    }