--- /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 <usb_std.h>
+#include <barsc.h>
+#include <barsread.h>
+#include <cusbclasscontrollerbase.h>
+#include <musbclasscontrollernotify.h>
+#include <usb/usbncm.h>
+#include <random.h>
+
+#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;
+ }