diff -r d3e8e7d462dd -r f92a4f87e424 usbmgmt/usbmgr/device/classdrivers/ncm/classcontroller/src/ncmclasscontroller.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usbmgmt/usbmgr/device/classdrivers/ncm/classcontroller/src/ncmclasscontroller.cpp Tue Aug 31 17:01:47 2010 +0300 @@ -0,0 +1,397 @@ +/* +* Copyright (c) 2010 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: +* +*/ + +/** + * @file + * @internalComponent + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "ncmclasscontroller.h" +#include "ncmconnectionmanager.h" +#include "ncmclientmanager.h" + +// For OST tracing +#include "OstTraceDefinitions.h" +#ifdef OST_TRACE_COMPILER_IN_USE +#include "ncmclasscontrollerTraces.h" +#endif + + +using namespace UsbNcm; +_LIT(KNcmControllerPanic, "UsbNcmCC"); // must be <=16 chars +const TInt KNcmStartupPriority = 3; + +// MTU size. +extern const TUint KEthernetFrameSize = 8192; + +// Lengths of the various bits of the NCM descriptor. Taken from the NCM Specification rev 1.0. +const TInt KNcmInterfaceDescriptorLength = 3; +const TInt KNcmCcHeaderDescriptorLength = 5; +const TInt KNcmFunctionalDescriptorLength = 4; +const TInt KNcmCcUfdDescriptorLength = 5; +const TInt KNcmDataClassHeaderDescriptorLength = 5; +const TInt KNcmDeviceDescriptorLength = 18; +const TInt KNcmConfigurationDescriptorLength = 9; +const TInt KNcmCommunicationClassEndpointOutDescriptorLength = 9; +const TInt KNcmNotificationEndpointDescriptorLength = 7; +const TInt KNcmDataClassInterfaceDescriptorLength = 9; +const TInt KNcmDataClassEndpointInDescriptorLength = 7; +const TInt KNcmDataClassEndpointOutDescriptorLength = 7; + +const TInt KNcmDescriptorLength = KNcmInterfaceDescriptorLength + + KNcmCcHeaderDescriptorLength + KNcmFunctionalDescriptorLength + + KNcmCcUfdDescriptorLength + KNcmDataClassHeaderDescriptorLength + + KNcmDeviceDescriptorLength + KNcmConfigurationDescriptorLength + + KNcmCommunicationClassEndpointOutDescriptorLength + + KNcmNotificationEndpointDescriptorLength + + KNcmDataClassInterfaceDescriptorLength + + KNcmDataClassEndpointInDescriptorLength + + KNcmDataClassEndpointOutDescriptorLength; + +// Panic codes +enum TNcmClassControllerPanicCode + { + ENcmPanicBadState = 1, + ENcmPanicOutstandingRequestFromDevice, + ENcmPanicAlreadyActive, + ENcmPanicUnhandledError, + ENcmPanicBadApiCallStart, // Attempt to call Start() when in illegal state + ENcmPanicBadApiCallStop, // Attempt to call Stop() when in illegal state + ENcmPanicUnexpectedIapState, + ENcmPanicUnexpectedError + }; + +/** + * Constructs a CNcmClassController object. + * @param aOwner USB Device that owns and manages the class + * @return Ownership of a new CNcmClassController object + */ +CNcmClassController* CNcmClassController::NewL( + MUsbClassControllerNotify & aOwner) + { + OstTraceFunctionEntry0( CNCMCLASSCONTROLLER_NEWL_ENTRY ); + + CNcmClassController* self = + new (ELeave) CNcmClassController(aOwner); + CleanupStack::PushL(self); + self->ConstructL(); + CleanupStack::Pop(self); + OstTraceFunctionExit1( CNCMCLASSCONTROLLER_NEWL_EXIT, ( TUint )( self ) ); + return self; + } + +/** + * Constructor. + * @param aOwner USB Device that owns and manages the class + */ +CNcmClassController::CNcmClassController( + MUsbClassControllerNotify& aOwner) : + CUsbClassControllerPlugIn(aOwner, KNcmStartupPriority) + { + OstTraceFunctionEntryExt( CNCMCLASSCONTROLLER_CNCMCLASSCONTROLLER_ENTRY, this ); + + iState = EUsbServiceIdle; // needs explicit initialisation as non-zero + + OstTraceFunctionExit1( CNCMCLASSCONTROLLER_CNCMCLASSCONTROLLER_EXIT, this ); + } + +/** + * Method to perform second phase construction. + */ +void CNcmClassController::ConstructL() + { + OstTraceFunctionEntry1( CNCMCLASSCONTROLLER_CONSTRUCTL_ENTRY, this ); + +#ifndef OVERDUMMY_NCMCC + TInt err = KErrNone; + + OstTrace0(TRACE_NORMAL, CNCMCLASSCONTROLLER_CONSTRUCTL, "About to load eusbcsc!"); + _LIT(KUsbSCLDDName, "eusbcsc"); + err = User::LoadLogicalDevice(KUsbSCLDDName); + OstTrace1( TRACE_NORMAL, CNCMCLASSCONTROLLER_CONSTRUCTL_LOAD_CSC_LDD, "LoadLogicalDevice() returns %d!", err ); + if (err != KErrNone && err != KErrAlreadyExists) + { + User::Leave(err); + } + OstTrace0(TRACE_NORMAL, CNCMCLASSCONTROLLER_CONSTRUCTL_CSC_LDD_LOADED, "Ldd eusbcsc loaded!"); +#endif // OVERDUMMY_NCMCC + + RandomMacAddressL(); // Create a random MAC address for NCM host side interface. + iConnectionMan = CNcmConnectionManager::NewL(*this, iHostMacAddress, iDataBufferSize, iNcmInternalSvr); + iClientMgr = new (ELeave) CNcmClientManager(iHostMacAddress); + OstTraceFunctionExit1( CNCMCLASSCONTROLLER_CONSTRUCTL_EXIT, this ); + } + +void CNcmClassController::RandomMacAddressL() + { + OstTraceFunctionEntry1( CNCMCLASSCONTROLLER_RANDOMMACADDRESSL_ENTRY, this ); + + //Create the NCM interface MAC address randomly + iHostMacAddress.SetLength(KEthernetAddressLength); + TRandom::RandomL(iHostMacAddress); + + //Mark it a locally administered address + iHostMacAddress[0] = 0x02; + iHostMacAddress[1] = 0x00; + iHostMacAddress[2] = 0x00; + OstTraceFunctionExit1( CNCMCLASSCONTROLLER_RANDOMMACADDRESSL_EXIT, this ); + } + +/** + * Destructor. + */ +CNcmClassController::~CNcmClassController() + { + OstTraceFunctionEntry1( CNCMCLASSCONTROLLER_CNCMCLASSCONTROLLER_ENTRY_DESTROY, this ); + Cancel(); + + // Close internal server to release some resource + iNcmInternalSvr.Close(); + delete iClientMgr; + delete iConnectionMan; + +#ifndef OVERDUMMY_NCMCC + OstTrace0(TRACE_NORMAL, CNCMCLASSCONTROLLER_CNCMCLASSCONTROLLER_PRE_UNLOAD_CSC_LDD, "About to unload Usbcsc!"); + _LIT(KUsbSCLDDName, "Usbcsc"); + TInt err = User::FreeLogicalDevice(KUsbSCLDDName); + OstTrace1(TRACE_NORMAL, CNCMCLASSCONTROLLER_CNCMCLASSCONTROLLER_UNLOAD_LDD_RESULT, "FreeLogicalDevice() returns %d!", err); +#endif // OVERDUMMY_NCMCC + OstTraceFunctionExit1( CNCMCLASSCONTROLLER_CNCMCLASSCONTROLLER_EXIT_DESTROY, this ); + } + +/** + * Called by UsbMan to start this class. + * @param aStatus Will be completed with success or failure. + */ +void CNcmClassController::Start(TRequestStatus& aStatus) + { + OstTraceFunctionEntryExt( CNCMCLASSCONTROLLER_START_ENTRY, this ); + OstTrace1(TRACE_NORMAL, CNCMCLASSCONTROLLER_START_PRINT_STATE, "iState=%d", iState); + + //Start() should only be called if the CC is idle state + __ASSERT_DEBUG((iState == EUsbServiceIdle), + User::Panic(KNcmControllerPanic, ENcmPanicBadApiCallStart)); + + // NB We enforce that the device doesn't re-post requests on us. + __ASSERT_DEBUG(!iReportStatus, + User::Panic(KNcmControllerPanic, ENcmPanicOutstandingRequestFromDevice)); + + // We should not be active before start + __ASSERT_DEBUG(!IsActive(), User::Panic(KNcmControllerPanic, ENcmPanicAlreadyActive)); + + aStatus = KRequestPending; + iReportStatus = &aStatus; + + iState = EUsbServiceStarting; + + // According to the latest discussion of 100ms limitation to + // each personality starting, the calling to iConnectionMan->Start + // will happen later. A controller will set sevice state to started + // immediately after calling to RDevUsbcScClient::SetInterface() + + TInt err = KErrNone; + + // Can not leave. Trap any error from SetNcmInterfacesL() + TRAP(err, iClientMgr->SetNcmInterfacesL(iDataBufferSize)); + User::RequestComplete(iReportStatus, err); + + if (KErrNone == err) + { + // Set service state to started + iState = EUsbServiceStarted; + + iStatus = KRequestPending; + SetActive(); + TRequestStatus* status = &iStatus; + User::RequestComplete(status, err); + } + OstTraceFunctionExit1( CNCMCLASSCONTROLLER_START_EXIT, this ); + } + +/** + * Called by UsbMan to stop this class. + * @param aStatus Will be completed with success or failure. + */ +void CNcmClassController::Stop(TRequestStatus& aStatus) + { + OstTraceFunctionEntryExt( CNCMCLASSCONTROLLER_STOP_ENTRY, this ); + OstTrace1( TRACE_NORMAL, CNCMCLASSCONTROLLER_STOP_PRINT_STATE, "iState=%d", iState ); + + __ASSERT_DEBUG(((iState == EUsbServiceStarted)||(iState == EUsbServiceStarting)), + User::Panic(KNcmControllerPanic, ENcmPanicBadApiCallStop)); + + // NB We enforce that the device doesn't re-post requests on us. + __ASSERT_DEBUG(!iReportStatus, + User::Panic(KNcmControllerPanic, ENcmPanicOutstandingRequestFromDevice)); + + if (IsActive()) // Networking connetion building is ongoing! + { + Cancel(); + } + else + { + iConnectionMan->Stop(); + } + + aStatus = KRequestPending; + iReportStatus = &aStatus; + User::RequestComplete(iReportStatus, KErrNone); + + iConnectingToNcmPktDrv = EFalse; + iState = EUsbServiceIdle; + OstTraceFunctionExit1( CNCMCLASSCONTROLLER_STOP_EXIT, this ); + } + +/** + * Returns information about the interfaces supported by this class. + * @param aDescriptorInfo Will be filled in with interface information. + */ +void CNcmClassController::GetDescriptorInfo( + TUsbDescriptor& aDescriptorInfo) const + { + OstTraceFunctionEntryExt( CNCMCLASSCONTROLLER_GETDESCRIPTORINFO_ENTRY, this ); + + aDescriptorInfo.iNumInterfaces = 2; + aDescriptorInfo.iLength = KNcmDescriptorLength; + OstTraceFunctionExit1( CNCMCLASSCONTROLLER_GETDESCRIPTORINFO_EXIT, this ); + } + +void CNcmClassController::McmoErrorIndication(TInt aError) + { + OstTraceFunctionEntryExt( CNCMCLASSCONTROLLER_MCMOERRORINDICATION_ENTRY, this ); + + Owner().UccnError(aError); + + OstTraceFunctionExit1( CNCMCLASSCONTROLLER_MCMOERRORINDICATION_EXIT, this ); + } + +/** + * Called when connection manager completes. + */ +void CNcmClassController::RunL() + { + OstTraceFunctionEntry1( CNCMCLASSCONTROLLER_RUNL_ENTRY, this ); + TInt completionCode = iStatus.Int(); + OstTraceExt2( TRACE_NORMAL, CNCMCLASSCONTROLLER_RUNL_PRINT_INFO, "iState=%d; completionCode=%d", iState, completionCode ); + + // Goto RunError() to handle any exception + User::LeaveIfError(completionCode); + + // We should only be in starting state when connection manager completes. Stopping is a synchronous call. + __ASSERT_DEBUG(EUsbServiceStarted == iState, User::Panic(KNcmControllerPanic, + ENcmPanicBadState)); + + switch (iState) + { + case EUsbServiceStarted: + { + OstTrace0( TRACE_NORMAL, CNCMCLASSCONTROLLER_RUNL_STARTED, "EUsbServiceStarted" ); + if (iConnectingToNcmPktDrv) + { + OstTrace0( TRACE_FLOW, CNCMCLASSCONTROLLER_RUNL_TRANSFER_HANDLES, "An Ethernet connection over NCM built. Transfer interface handlers to NCM internal server." ); + // NCM Packet Driver Loaded in C32, now transfer LDD handles to packet driver side through NCM internal server + iClientMgr->TransferInterfacesL(iNcmInternalSvr); + + delete iClientMgr; + iClientMgr = NULL; + } + else + { + OstTrace0( TRACE_NORMAL, CNCMCLASSCONTROLLER_RUNL_BUILT_NCM_CONNECTION, "Going to build ethernet connection over NCM!" ); + iConnectingToNcmPktDrv = ETrue; + iStatus = KRequestPending; + SetActive(); + iConnectionMan->Start(iStatus); + } + + break; + } + + default: + { + OstTrace0( TRACE_ERROR, CNCMCLASSCONTROLLER_RUNL_DEFAULT, "Default::SHOULD NOT BE HERE!!!" ); + User::Panic(KNcmControllerPanic, ENcmPanicBadState); + break; + } + } + OstTraceFunctionExit1( CNCMCLASSCONTROLLER_RUNL_EXIT, this ); + } + +/** + * Implements cancellation of an outstanding request. + */ +void CNcmClassController::DoCancel() + { + OstTraceFunctionEntry1( CNCMCLASSCONTROLLER_DOCANCEL_ENTRY, this ); + + OstTraceExt2( TRACE_NORMAL, CNCMCLASSCONTROLLER_DOCANCEL_INFO_PRINT, "iState=%d; iReportStatus=%p", iState, iReportStatus ); + + // Update our iState. If we're starting, then roll back to idle. If we're + // stopping, role back to started. Nothing else is legal. + switch (iState) + { + case EUsbServiceStarted: + OstTrace0( TRACE_NORMAL, CNCMCLASSCONTROLLER_DOCANCEL_IDLE, "EUsbServiceIdle" ); + iState = EUsbServiceIdle; + if (iConnectingToNcmPktDrv) + { + iConnectionMan->StartCancel(); + } + __ASSERT_DEBUG(!iReportStatus, User::Panic(KNcmControllerPanic, ENcmPanicUnhandledError)); + + OstTraceFunctionExit1( CNCMCLASSCONTROLLER_DOCANCEL_EXIT, this ); + return; + + default: + OstTrace1(TRACE_FATAL, CNCMCLASSCONTROLLER_DOCANCEL_DEFAULT, "Default::Should not be here:iState=%d", iState); + User::Panic(KNcmControllerPanic, ENcmPanicBadState); + break; + } + + // Complete the client's request. + __ASSERT_DEBUG(iReportStatus, User::Panic(KNcmControllerPanic, ENcmPanicUnhandledError)); + User::RequestComplete(iReportStatus, KErrCancel); + + OstTraceFunctionExit1( CNCMCLASSCONTROLLER_DOCANCEL_EXIT_DUP1, this ); + } + +/** + * Called when RunL leaves. Does nothing, just return KErrNone. + * @return Error code. Simply return KErrNone. + */ +TInt CNcmClassController::RunError(TInt aError) + { + OstTraceFunctionEntryExt( CNCMCLASSCONTROLLER_RUNERROR_ENTRY, this ); + + if (KErrCancel != aError) + { + // Report this failure to the observer + // Finally this will report to usbman and NCM class will be stopped. + McmoErrorIndication(aError); + } + + OstTraceFunctionExitExt( CNCMCLASSCONTROLLER_RUNERROR_EXIT, this, KErrNone ); + return KErrNone; + }