// Copyright (c) 2008-2010 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:
// Implements a Session of a Symbian OS server for the RUsbMassStorage API
//
//

/**
 @file
 @internalTechnology
*/

#include <e32base.h>

#include "msctypes.h"
#include "shared.h"
#include "msgservice.h"
#include "cusbhostmslogicalunit.h"
#include "cusbhostmsdevice.h"
#include "cusbhostmssession.h"
#include "cusbhostmsdevicethread.h"

#include "OstTraceDefinitions.h"
#ifdef OST_TRACE_COMPILER_IN_USE
#include "cusbhostmsdevicethreadTraces.h"
#endif

/**
Constructor
*/
TDeviceHandler::TDeviceHandler(CUsbHostMsDevice& aDevice)
:   iDevice(aDevice)
    {
    }


/**
   Services messages directed to a device. Messages associated with a single
   Logical Unit contained by the device are forwarded to the Logical Unit
   Handler.

   @param aMessage
 */
void TDeviceHandler::HandleMessageL(const RMessage2& aMessage)
    {
    TLun lun = iDevice.GetAndSetLunL(aMessage);

    switch (aMessage.Function())
        {
    case EUsbHostMsSuspendLun:
        iDevice.SuspendLunL(lun);
        break;
    case EUsbHostMsUnRegisterLun:
        iDevice.RemoveLunL(lun);
        break;
    default:
        // Try Logical Unit Handler
        CUsbHostMsLogicalUnit& lu = iDevice.GetLuL(lun);
        TLogicalUnitHandler luHandler(lu);
        luHandler.HandleMessageL(aMessage);
        break;
        }
    }


/**
   Constructor

   @param aLu Reference to the logical unit object
 */
TLogicalUnitHandler::TLogicalUnitHandler(CUsbHostMsLogicalUnit& aLu)
:   iLu(aLu)
    {
    }


/**
   Services messages directed to a specific Logical Unit. Unrecognized messages
   will cause a PANIC.

   @param aMessage
 */
void TLogicalUnitHandler::HandleMessageL(const RMessage2& aMessage)
    {
    switch (aMessage.Function())
        {
    case EUsbHostMsNotifyChange:
        iLu.NotifyChange(aMessage);
        break;
    case EUsbHostMsCancelChangeNotifier:
        iLu.CancelChangeNotifierL();
        break;
    case EUsbHostMsForceRemount:
        iLu.ForceCompleteNotifyChangeL();
        break;
    case EUsbHostMsRead:
        iLu.ReadL(aMessage);
        break;
    case EUsbHostMsWrite:
        iLu.WriteL(aMessage);
        break;
    case EUsbHostMsErase:
        iLu.EraseL(aMessage);
        break;
    case EUsbHostMsCapacity:
        iLu.CapsL(aMessage);
        break;
    default:
        aMessage.Panic(KUsbHostMsSrvPncCat, EUsbMsPanicIllegalIPC);
        break;
        }
    }


void CUsbHostMsDeviceThread::DoStartServerL(TAny* aPtr)
    {
    CActiveScheduler* s = new(ELeave) CActiveScheduler;
    CActiveScheduler::Install(s);

    CUsbHostMsDeviceThread* iThread = (CUsbHostMsDeviceThread*)aPtr;
    CActiveScheduler::Add(iThread);

    iThread->Start();

    RThread::Rendezvous(KErrNone);

    //
    // Ready to run
    CActiveScheduler::Start();

    //
    // Cleanup the scheduler
    delete s;
    }


TInt CUsbHostMsDeviceThread::Entry(TAny* aPtr)
    {
    CTrapCleanup* cleanup = CTrapCleanup::New();
    if (!cleanup)
        {
        return KErrNoMemory;
        }

    TRAPD(error, DoStartServerL(aPtr));
    delete cleanup;
    return error;
    }


void  CUsbHostMsDeviceThread::RunL()
    {
    // called on completion of MessageRequest() or Resume()
    User::LeaveIfError(iStatus.Int());

    Lock();
    if (iUsbHostMsDevice)
        {
        if (iUsbHostMsDevice->IsSuspended())
            {

            RMessage2 msg = iRMessage2[iDequeueIndex];
            if (msg.Function() != EUsbHostMsSuspendLun)
                {
                // request resume
                Unlock();
                iUsbHostMsDevice->Resume(iStatus);
                SetActive();
                return;
                }
            }
        }

    // process message queue
    RMessage2 msg = iRMessage2[iDequeueIndex];

    iDequeueIndex++;

    if(iDequeueIndex >= KMaxNumMessage)
        iDequeueIndex = 0;
    if(iQueueFull)
        iQueueFull = EFalse;

    HandleMessage(msg);

    if ((iQueueIndex != iDequeueIndex) || iQueueFull)
        {
        // self completion
        TRequestStatus* status = &iStatus;
        User::RequestComplete(status, KErrNone);
        SetActive();
        }
    else
        {
        iUsbHostMsSession.MessageRequest(iStatus);
        SetActive();
        }
    Unlock();
    }


void CUsbHostMsDeviceThread::DoCancel()
    {
    TRequestStatus* status = &iStatus;
    User::RequestComplete(status, KErrCancel);
    }


TInt CUsbHostMsDeviceThread::RunError(TInt aError)
{
    OstTrace1(TRACE_SHOSTMASSSTORAGE_HOST, CUSBHOSTMSDEVICETHREAD_10,
              ">> HOST RunError returning %d", aError);
    return KErrNone;
}


TInt CUsbHostMsDeviceThread::QueueMsg(const RMessage2& aMsg)
    {
    if (iQueueFull)
        {
        return KErrOverflow;
        }

    Lock();

    iRMessage2[iQueueIndex] = aMsg;
    iQueueIndex++;

    if (iQueueIndex >= KMaxNumMessage)
        {
        iQueueIndex = 0;
        }

    if (iQueueIndex == iDequeueIndex)
        {
        iQueueFull = ETrue;
        }
    Unlock();
    return KErrNone;
    }


CUsbHostMsDeviceThread::CUsbHostMsDeviceThread(CUsbHostMsSession& aUsbHostMsSession, TUint aToken)
:   CActive(EPriorityStandard),
    iUsbHostMsSession(aUsbHostMsSession),
    iQueueFull(EFalse)
    {
    TBuf<32> nameBuf;
    nameBuf.Format(_L("Host Ms ThreadMutex%8x"), aToken);
    iMutex.CreateGlobal(nameBuf,EOwnerProcess);
    }

CUsbHostMsDeviceThread::~CUsbHostMsDeviceThread()
    {
    Cancel();
    iMutex.Close();
    }

CUsbHostMsDeviceThread* CUsbHostMsDeviceThread::NewL(CUsbHostMsSession& aUsbHostMsSession, TUint aToken)
    {
    CUsbHostMsDeviceThread* r = new (ELeave) CUsbHostMsDeviceThread(aUsbHostMsSession, aToken);
    return r;
    }


void CUsbHostMsDeviceThread::Start()
    {
    iUsbHostMsSession.MessageRequest(iStatus);
    SetActive();
    }


/**
 Handles the request (in the form of a message) received from the client
@param  aMessage    The received message
 */
void CUsbHostMsDeviceThread::HandleMessage(const RMessage2& aMessage)
    {
    TInt ret = KErrNotReady;
    OstTraceExt2(TRACE_SHOSTMASSSTORAGE_HOST, CUSBHOSTMSDEVICETHREAD_21,
              ">> HOST DispatchMessageL Function=%d %d",
              aMessage.Function(), aMessage.Int3());
    switch (aMessage.Function())
        {
    case EUsbHostMsRegisterInterface:
        TRAP(ret, RegisterInterfaceL(aMessage));
        break;
    case EUsbHostMsInitialiseInterface:
        TRAP(ret, InitialiseInterfaceL(aMessage));
        // CUsbInterfaceHandler::GetMaxLun() completes asynchronously
        if (ret)
            {
            // Error condition needs to be completed
            break;
            }
        return;
    case EUsbHostMsUnRegisterInterface:
        // Check whether all luns have got uninitialised.
        if (iUsbHostMsDevice)
            {
            for(TInt i = 0, j = iUsbHostMsDevice->GetMaxLun(); i <= j; i++)
                TRAP_IGNORE(iUsbHostMsDevice->RemoveLunL(i));
            }
        TRAP(ret, UnRegisterInterfaceL(aMessage));
        break;
    case EUsbHostMsRegisterLun:
        TRAP(ret, RegisterLogicalUnitL(aMessage));
        break;
    case EUsbHostMsGetNumLun:
        TRAP(ret, GetNumLunL(aMessage));
        break;
    case EUsbHostMsShutdown:
        ret = Shutdown();
        break;
    default:
        // Try Device Handler and Logical Unit Handler
        __ASSERT_DEBUG(iUsbHostMsDevice, User::Invariant());
        TDeviceHandler deviceHandler(*iUsbHostMsDevice);
        TRAP(ret, deviceHandler.HandleMessageL(aMessage));
        break;
        }
    OstTrace1(TRACE_SHOSTMASSSTORAGE_HOST, CUSBHOSTMSDEVICETHREAD_22,
              ">> HOST returning %d", ret);

    if (aMessage.Function() != EUsbHostMsNotifyChange)
        {
        aMessage.Complete(ret);
        }
    }


/**
Client request to shut down the server

return KErrNone
*/
TInt CUsbHostMsDeviceThread::Shutdown()
    {
    CActiveScheduler::Stop();
    return KErrNone;
    }

void CUsbHostMsDeviceThread::GetNumLunL(const RMessage2& aMessage)
    {
    if (!iUsbHostMsDevice)
        {
        User::Leave(KErrNotReady);
        }

    TUint32 maxLun = iUsbHostMsDevice->GetMaxLun() + 1;
    TPtrC8 pLun((TUint8*)&maxLun,sizeof(TUint32));
    aMessage.WriteL(0,pLun);
    }


void CUsbHostMsDeviceThread::RegisterInterfaceL(const RMessage2& aMessage)
    {
    THostMassStorageConfig msDeviceConfig;
    TPtr8 ptr((TUint8*)&msDeviceConfig,sizeof(THostMassStorageConfig));
    aMessage.ReadL(0, ptr);

    OstTrace1(TRACE_SHOSTMASSSTORAGE_HOST, CUSBHOSTMSDEVICETHREAD_30,
              "RegisterInterfaceL Token=%d ", msDeviceConfig.iInterfaceToken);

    iUsbHostMsDevice = CUsbHostMsDevice::NewL(msDeviceConfig);
    }


void CUsbHostMsDeviceThread::InitialiseInterfaceL(const RMessage2& aMessage)
    {
    if (!iUsbHostMsDevice)
        {
        User::Leave(KErrNotReady);
        }

    TRAPD(err, iUsbHostMsDevice->InitialiseL(aMessage));
    if (err != KErrNone)
        {
        delete iUsbHostMsDevice;
        iUsbHostMsDevice = NULL;
        User::Leave(err);
        }
    }


void CUsbHostMsDeviceThread::UnRegisterInterfaceL(const RMessage2& aMessage)
    {
    if (!iUsbHostMsDevice)
        {
        User::Leave(KErrNotReady);
        }

    TRAPD(err, iUsbHostMsDevice->UnInitialiseL());
    delete iUsbHostMsDevice;
    iUsbHostMsDevice = NULL;
    User::LeaveIfError(err);
    }


void CUsbHostMsDeviceThread::RegisterLogicalUnitL(const RMessage2& aMessage)
    {
    if (!iUsbHostMsDevice)
        {
        User::Leave(KErrNotReady);
        }

    TUint32 iLunId = aMessage.Int0() + 1; // Subssessions need a positive value to store in the handles. We represent Luns as LunId+1
    TPtrC8 pLun((TUint8*)&iLunId, sizeof(TUint32));
    aMessage.WriteL(3, pLun);
    iLunId -= 1;    // We represent LunId in MSC from 0 to MaxLun-1 as represented in BOT
    OstTrace1(TRACE_SHOSTMASSSTORAGE_HOST, CUSBHOSTMSDEVICETHREAD_31,
              "RegisterLogicalUnitL LUN=%d ", iLunId);
    iUsbHostMsDevice->AddLunL(iLunId);
    iUsbHostMsDevice->InitLunL(iLunId);
    }

