diff -r 453dfc402455 -r 0aa8cc770c8a localconnectivityservice/dun/plugins/src/usb/DunUsbPlugin.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/localconnectivityservice/dun/plugins/src/usb/DunUsbPlugin.cpp Tue Aug 31 16:03:15 2010 +0300 @@ -0,0 +1,781 @@ +/* +* Copyright (c) 2006-2007 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: DUN USB plugin +* +*/ + + +#include +#include +#include "DunPlugin.h" +#include "DunUtils.h" +#include "DunUsbPlugin.h" +#include "DunDebug.h" + +_LIT( KUsbCsyName, "ECACM" ); +_LIT( KUsbPortName, "ACM" ); +_LIT( KUsbPortPort, "::" ); +_LIT( KUsbLddName, "EUSBC" ); +_LIT( KUsbChannelName, "DUNUSB::" ); + +const TInt KCharactersInTInt = 10; // For "2147483648" +const TUint8 KDefaultAcmProtocolNum = 0x01; // Hayes compatible modem + +// ======== MEMBER FUNCTIONS ======== + +// --------------------------------------------------------------------------- +// CDunUsbPlugin::CDunUsbPlugin +// --------------------------------------------------------------------------- +// +CDunUsbPlugin::CDunUsbPlugin() : + iServer( NULL ), + iUsbListen( NULL ), + iUsbConfig( NULL ), + iTransporter( NULL ) + { + } + +// --------------------------------------------------------------------------- +// Destructor. +// --------------------------------------------------------------------------- +// +CDunUsbPlugin::~CDunUsbPlugin() + { + FTRACE(FPrint( _L( "CDunUsbPlugin::~CDunUsbPlugin()" ) )); + Uninitialize(); + FTRACE(FPrint( _L( "CDunUsbPlugin::~CDunUsbPlugin() complete" ) )); + } + +// --------------------------------------------------------------------------- +// State of this plugin +// --------------------------------------------------------------------------- +// +TDunPluginState CDunUsbPlugin::PluginState() + { + return iServer->GetPluginStateByUid( KDunUsbPluginUid ); + } + +// --------------------------------------------------------------------------- +// Constructs a listener object for this plugin +// --------------------------------------------------------------------------- +// +void CDunUsbPlugin::ConstructListenerL() + { + FTRACE(FPrint(_L("CDunUsbPlugin::ConstructListenerL()"))); + if ( PluginState() != EDunStateLoaded ) + { + FTRACE(FPrint(_L("CDunUsbPlugin::ConstructListenerL() (not ready) complete"))); + User::Leave( KErrNotReady ); + } + ReportStateChangeUp( EDunStateTryListen ); + if ( iUsbListen ) + { + FTRACE(FPrint(_L("CDunUsbPlugin::ConstructListenerL() (already exists) complete"))); + User::Leave( KErrAlreadyExists ); + } + InitUsbL(); + CDunUsbListen* listen = CDunUsbListen::NewL( iServer, this, iUsbServer ); + CleanupStack::PushL( listen ); + TInt retTemp = listen->IssueRequestL(); + CleanupStack::Pop( listen ); + iUsbListen = listen; + // Here return value of KErrAlreadyExists means the device is already + // configured + // In this case we have to switch directly to channeled mode + ReportStateChangeUp( EDunStateListening ); + if ( retTemp == KErrAlreadyExists ) + { + TBool noFreeChans = EFalse; + // noFreeChans will be omitted (not needed to set to RComm) + NotifyChannelAllocate( noFreeChans ); // Create channel and change state + } + FTRACE(FPrint(_L("CDunUsbPlugin::ConstructListenerL() complete"))); + } + +// --------------------------------------------------------------------------- +// Initializes USB by loading LDD +// --------------------------------------------------------------------------- +// +void CDunUsbPlugin::InitUsbL() + { + FTRACE(FPrint( _L( "CDunUsbPlugin::InitUsbL()") )); + // Load Logical device driver for USB + FTRACE(FPrint( _L( "CDunUsbPlugin::InitUsbL() LoadLogicalDevice") )); + TInt retTemp = User::LoadLogicalDevice( KUsbLddName ); + if ( retTemp!=KErrAlreadyExists && retTemp!=KErrNone ) + { + User::Leave( retTemp ); + } + // Connect to the USB Manager server + FTRACE(FPrint( _L( "CDunUsbPlugin::InitUsbL() Connect()") )); + User::LeaveIfError( iUsbServer.Connect() ); + // Create USB configuration accessor and listener + iUsbConfig = CDunUsbConfig::NewL( iServer, this, KDefaultAcmProtocolNum ); + FTRACE(FPrint( _L( "CDunUsbPlugin::InitUsbL() complete") )); + } + +// --------------------------------------------------------------------------- +// Initializes all usable USB ports for DUN +// --------------------------------------------------------------------------- +// +TInt CDunUsbPlugin::InitPorts() + { + FTRACE(FPrint( _L( "CDunUsbPlugin::InitPorts() (num=%d)" ), iUsbPorts.Count())); + TInt i; + TInt retTemp = CreateAllPorts(); + if ( retTemp != KErrNone ) + { + FTRACE(FPrint( _L( "CDunUsbPlugin::InitPorts() (ERROR) complete" ))); + return retTemp; + } + TInt count = iUsbPorts.Count(); + for ( i=0; iStop(); + // Find out what ports can be supported and append them to array + TBool validConfig; + TUsbPortEntity entity; + for ( TInt i=0;; i++ ) + { + retTemp = iUsbConfig->GetConfigValidityByIndex( i, validConfig ); + if ( retTemp != KErrNone ) + { + break; + } + if ( !validConfig ) + { + continue; + } + // Valid config found, append it to array + entity.iPortNum = i; + retTemp = iUsbPorts.Append( entity ); + if ( retTemp != KErrNone ) + { + FTRACE(FPrint( _L( "CDunUsbPlugin::CreateAllPorts() (append failed!) complete" ) )); + return retTemp; + } + } + FTRACE(FPrint( _L( "CDunUsbPlugin::CreateAllPorts() complete" ) )); + return KErrNone; + } + +// --------------------------------------------------------------------------- +// Initializes one USB port for DUN +// --------------------------------------------------------------------------- +// +TInt CDunUsbPlugin::InitOnePort( TUsbPortEntity* aEntity ) + { + FTRACE(FPrint( _L( "CDunUsbPlugin::InitOnePort()" ))); + if ( !aEntity ) + { + FTRACE(FPrint( _L( "CDunUsbPlugin::InitOnePort() (not initialized!) complete" ))); + return KErrGeneral; + } + TInt retTemp; + TName portName; + portName.Copy( KUsbPortName ); + portName.Append( KUsbPortPort ); + portName.AppendNum( aEntity->iPortNum ); + retTemp = aEntity->iUsbPort.Open( iCommServer, + portName, + ECommExclusive, + ECommRoleDCE ); + if ( retTemp != KErrNone ) + { + FTRACE(FPrint( _L( "CDunUsbPlugin::InitOnePort() Open failed (%d)" ), retTemp)); + return retTemp; + } + TRAPD( retTrap, SetChannelL(aEntity) ); + if ( retTrap != KErrNone ) + { + iTransporter->FreeChannel( &aEntity->iUsbPort ); + FTRACE(FPrint( _L( "CDunUsbPlugin::InitOnePort() (trapped!) complete" ))); + return retTrap; + } + FTRACE(FPrint( _L( "CDunUsbPlugin::InitOnePort() complete" ))); + return KErrNone; + } + +// --------------------------------------------------------------------------- +// Sets channel for one USB port +// --------------------------------------------------------------------------- +// +void CDunUsbPlugin::SetChannelL( TUsbPortEntity* aEntity ) + { + FTRACE(FPrint( _L( "CDunUsbPlugin::SetChannelL()" ))); + if ( !aEntity ) + { + FTRACE(FPrint( _L( "CDunUsbPlugin::SetChannelL() (not initialized!) complete" ))); + User::Leave( KErrGeneral ); + } + HBufC8* channelName = HBufC8::NewMaxLC( KUsbChannelName().Length() + + KCharactersInTInt ); + TPtr8 channelNamePtr = channelName->Des(); + channelNamePtr.Copy( KUsbChannelName ); + channelNamePtr.AppendNum( aEntity->iPortNum ); + iTransporter->AllocateChannelL( &aEntity->iUsbPort, + KDunUsbPluginUid, + channelNamePtr, + ETrue, + this ); + AddSkippedErrorL( KErrUsbInterfaceChange, &aEntity->iUsbPort ); + iTransporter->AddConnMonCallbackL( &aEntity->iUsbPort, + this, + EDunReaderUpstream, + EFalse ); + iTransporter->AddConnMonCallbackL( &aEntity->iUsbPort, + this, + EDunWriterUpstream, + EFalse ); + iTransporter->AddConnMonCallbackL( &aEntity->iUsbPort, + this, + EDunReaderDownstream, + EFalse ); + iTransporter->AddConnMonCallbackL( &aEntity->iUsbPort, + this, + EDunWriterDownstream, + EFalse ); + iTransporter->IssueTransferRequestsL( &aEntity->iUsbPort ); + CleanupStack::PopAndDestroy( channelName ); + FTRACE(FPrint( _L( "CDunUsbPlugin::SetChannelL() complete" ))); + } + +// --------------------------------------------------------------------------- +// Adds skipped error code to Transporter +// The skipped error is added to local media's read and write operations +// --------------------------------------------------------------------------- +// +void CDunUsbPlugin::AddSkippedErrorL( TInt aError, RComm* aComm ) + { + iTransporter->AddSkippedErrorL( aError, aComm, EDunReaderUpstream ); + iTransporter->AddSkippedErrorL( aError, aComm, EDunWriterDownstream ); + } + +// --------------------------------------------------------------------------- +// Sets new state +// New state must be one more than the old state +// --------------------------------------------------------------------------- +// +TInt CDunUsbPlugin::ReportStateChangeUp( TDunPluginState aPluginState ) + { + FTRACE(FPrint(_L("CDunUsbPlugin::ReportStateChangeUp()"))); + TInt retTemp = iServer->NotifyPluginStateChangeUp( aPluginState, + KDunUsbPluginUid ); + if ( retTemp != KErrNone ) + { + FTRACE(FPrint(_L("CDunUsbPlugin::ReportStateChangeUp() (ERROR) complete"))); + return retTemp; + } + FTRACE(FPrint(_L("CDunUsbPlugin::ReportStateChangeUp() complete"))); + return KErrNone; + } + +// --------------------------------------------------------------------------- +// Sets new state +// New state must be one less than the old state +// --------------------------------------------------------------------------- +// +TInt CDunUsbPlugin::ReportStateChangeDown( TDunPluginState aPluginState ) + { + FTRACE(FPrint(_L("CDunUsbPlugin::ReportStateChangeDown()"))); + TInt retTemp = iServer->NotifyPluginStateChangeDown( aPluginState, + KDunUsbPluginUid ); + if ( retTemp != KErrNone ) + { + FTRACE(FPrint(_L("CDunUsbPlugin::ReportStateChangeDown() (ERROR) complete"))); + return retTemp; + } + FTRACE(FPrint(_L("CDunUsbPlugin::ReportStateChangeDown() complete"))); + return KErrNone; + } + +// --------------------------------------------------------------------------- +// Frees existing channels +// --------------------------------------------------------------------------- +// +TInt CDunUsbPlugin::FreeChannels() + { + FTRACE(FPrint(_L("CDunUsbPlugin::FreeChannels()"))); + if ( PluginState() != EDunStateTryUninitialize ) + { + FTRACE(FPrint( _L( "CDunUsbPlugin::FreeChannels() (not ready) complete" ) )); + return KErrNotReady; + } + TInt i; + TInt count = iUsbPorts.Count(); + for ( i=0; iFreeChannel( &iUsbPorts[i].iUsbPort ); + iUsbPorts[i].iUsbPort.SetSignals( 0, KSignalDCEOutputs ); + iUsbPorts[i].iUsbPort.Close(); + } + } + iUsbPorts.Close(); + if ( iCommServer.Handle() != KNullHandle ) + { + iCommServer.UnloadCommModule( KUsbCsyName ); + iCommServer.Close(); + } + FTRACE(FPrint(_L("CDunUsbPlugin::FreeChannels() complete"))); + return KErrNone; + } + +// --------------------------------------------------------------------------- +// Uninitializes this plugin +// --------------------------------------------------------------------------- +// +TInt CDunUsbPlugin::Uninitialize() + { + FTRACE(FPrint( _L( "CDunUsbPlugin::Uninitialize()" ) )); + ReportStateChangeDown( EDunStateTryUninitialize ); + // Free channel(s), ignore errors + FreeChannels(); + delete iUsbListen; // delete before iUsbServer close + iUsbListen = NULL; + delete iUsbConfig; + iUsbConfig = NULL; + if ( iUsbServer.Handle() != KNullHandle ) + { + iUsbServer.Close(); + } + User::FreeLogicalDevice( KUsbLddName ); + ReportStateChangeUp( EDunStateUninitialized ); + ReportStateChangeUp( EDunStateTryLoad ); + ReportStateChangeUp( EDunStateLoaded ); + FTRACE(FPrint( _L( "CDunUsbPlugin::Uninitialize() complete" ) )); + return KErrNone; + } + +// --------------------------------------------------------------------------- +// Gets port's index and entity by connection ID +// --------------------------------------------------------------------------- +// +TInt CDunUsbPlugin::GetEntityByConnId( TConnId aConnId, + TUsbPortEntity*& aEntity ) + { + FTRACE(FPrint( _L( "CDunUsbPlugin::GetEntityByConnId()")) ); + TInt i; + TInt count = iUsbPorts.Count(); + for ( i=0; iIssueRequest(); + if ( retTemp != KErrNone ) + { + FTRACE(FPrint(_L("CDunUsbPlugin::NotifyChannelAllocate() (ERROR) complete"))); + return retTemp; + } + iShutdown = EFalse; + ReportStateChangeUp( EDunStateChanneled ); + FTRACE(FPrint(_L("CDunUsbPlugin::NotifyChannelAllocate() complete"))); + return KErrNone; + } + +// --------------------------------------------------------------------------- +// From class MDunListenCallback. +// Gets called when an existing channel must be freed +// --------------------------------------------------------------------------- +// +TInt CDunUsbPlugin::NotifyChannelFree() + { + FTRACE(FPrint(_L("CDunUsbPlugin::NotifyChannelFree()"))); + if ( PluginState() != EDunStateChanneled ) + { + FTRACE(FPrint(_L("CDunUsbPlugin::NotifyChannelFree() (not ready) complete"))); + return KErrNotReady; + } + // Cable removed or PC sleep, hibernate or reset. + // Just remove channels. + ReportStateChangeDown( EDunStateTryUninitialize ); + TInt retTemp = FreeChannels(); + if ( retTemp != KErrNone ) + { + FTRACE(FPrint(_L("CDunUsbPlugin::NotifyChannelFree() (ERROR) complete"))); + return retTemp; + } + ReportStateChangeUp( EDunStateUninitialized ); + ReportStateChangeUp( EDunStateTryLoad ); + ReportStateChangeUp( EDunStateLoaded ); + ReportStateChangeUp( EDunStateTryListen ); + ReportStateChangeUp( EDunStateListening ); + FTRACE(FPrint(_L("CDunUsbPlugin::NotifyChannelFree() complete"))); + return KErrNone; + } + +// --------------------------------------------------------------------------- +// From class MDunConnMon. +// Gets called when line status changes or when any type of error is detected +// --------------------------------------------------------------------------- +// +void CDunUsbPlugin::NotifyProgressChangeL( + TConnId aConnId, + TDunConnectionReason aConnReason ) + { + FTRACE(FPrint( _L( "CDunUsbPlugin::NotifyProgressChangeL()")) ); + // Find matching failed ID + TUsbPortEntity* foundEntity = NULL; + TInt foundIndex = GetEntityByConnId( aConnId, foundEntity ); + if ( !foundEntity ) + { + FTRACE(FPrint( _L( "CDunUsbPlugin::NotifyProgressChangeL() (not found) complete")) ); + User::Leave( KErrNotFound ); + } + if ( aConnReason.iReasonType == EDunReasonTypeRW || + aConnReason.iReasonType == EDunReasonTypeRunL ) + { + // The following check will make it possible for CDunUsbListen to react + // to cable removal (iUsbPort.SubSessionHandle() ) + { + iTransporter->FreeChannel( &foundEntity->iUsbPort ); + foundEntity->iUsbPort.SetSignals( 0, KSignalDCEOutputs ); + foundEntity->iUsbPort.Close(); + } + TInt retTemp = InitOnePort( foundEntity ); + if ( retTemp != KErrNone ) + { + FTRACE(FPrint( _L( "CDunUsbPlugin::NotifyProgressChangeL() (ERROR) complete")) ); + User::Leave( KErrGeneral ); + } + // Now this plugin was basically "restarted", but only for one + // channel. Because transporter has channels with waiters, notify + // server to reopen queued plugin(s) + iServer->NotifyPluginReopenRequest(); + FTRACE(FPrint( _L( "CDunUsbPlugin::NotifyProgressChangeL() complete")) ); + } + +// --------------------------------------------------------------------------- +// From class MDunBufferCorrection. +// Gets called when request to change local media's buffer size +// --------------------------------------------------------------------------- +// +TInt CDunUsbPlugin::NotifyBufferCorrection( TInt aLength ) + { + FTRACE(FPrint( _L( "CDunUsbPlugin::NotifyBufferCorrection()")) ); + /* + * This method modifies the default buffer length to match the maximum value + * used by "CanHandleZLP=0" configuration option. This length is nearest length + * divisible by 64 - 1. With slow USB compared to high speed HSDPA some products + * can start to collect data to Dataport which results in full packet writes to + * USB. However, the default full packet must not be divisible by 64, which results + * in the ACM to send the full packet to LDD plus one extra packet with one byte + * (disabling interrupts for a long time with current non-DMA USB driver). + */ + TInt newLength = aLength; + if ( newLength % 64 == 0 ) + { + newLength = ((aLength >> 6) << 6) - 1; + } + FTRACE(FPrint( _L( "CDunUsbPlugin::NotifyBufferCorrection() complete")) ); + return newLength; + } + +// --------------------------------------------------------------------------- +// From class MDunUsbConfig. +// Gets called when one or more ACM configurations are added +// --------------------------------------------------------------------------- +// +TInt CDunUsbPlugin::NotifyConfigAddition( TInt aIndex ) + { + FTRACE(FPrint( _L( "CDunUsbPlugin::NotifyConfigAddition()")) ); + // Configuration added so aIndex is the port number to be added to array. + // This port number must not already exist in the array. + TUsbPortEntity* foundEntity = NULL; + TInt foundIndex = GetEntityByPortNumber( aIndex, foundEntity ); + if ( foundEntity ) + { + FTRACE(FPrint( _L( "CDunUsbPlugin::NotifyConfigAddition() (already exists) complete")) ); + return KErrAlreadyExists; + } + // Now either find port with KErrNotFound set as port number or if that is + // not found then try to append to array + foundIndex = GetFirstFreeEntity( foundEntity ); + if ( !foundEntity ) // free not found so add new + { + // Now append the new port to array + TUsbPortEntity newEntity; + newEntity.iPortNum = aIndex; + TInt retTemp = iUsbPorts.Append( newEntity ); + if ( retTemp != KErrNone ) + { + FTRACE(FPrint( _L( "CDunUsbPlugin::NotifyConfigAddition() (append failed!) complete")) ); + return KErrGeneral; + } + // entity not valid here so set now + foundEntity = &iUsbPorts[iUsbPorts.Count()-1]; + } + else // free found so change array + { + foundEntity->iPortNum = aIndex; + } + // Open port and create channel for it + TInt retTemp = InitOnePort( foundEntity ); + if ( retTemp != KErrNone ) + { + FTRACE(FPrint( _L( "CDunUsbPlugin::NotifyConfigAddition() (ERROR) complete" ))); + return KErrGeneral; + } + FTRACE(FPrint( _L( "CDunUsbPlugin::NotifyConfigAddition() complete")) ); + return KErrNone; + } + +// --------------------------------------------------------------------------- +// From class MDunUsbConfig. +// Gets called when one or more ACM configurations are removed +// --------------------------------------------------------------------------- +// +TInt CDunUsbPlugin::NotifyConfigRemoval( TInt aIndex ) + { + FTRACE(FPrint( _L( "CDunUsbPlugin::NotifyConfigRemoval()")) ); + // Configuration removed so aIndex is the port number to be added to array. + // This port number must already exist in the array. + TUsbPortEntity* foundEntity = NULL; + TInt foundIndex = GetEntityByPortNumber( aIndex, foundEntity ); + if ( !foundEntity ) + { + FTRACE(FPrint( _L( "CDunUsbPlugin::NotifyConfigRemoval() (not found) complete")) ); + return KErrNotFound; + } + // Now free channel and mark as unused + iTransporter->FreeChannel( &foundEntity->iUsbPort ); + foundEntity->iUsbPort.SetSignals( 0, KSignalDCEOutputs ); + foundEntity->iUsbPort.Close(); + iUsbPorts[foundIndex].iPortNum = KErrNotFound; + FTRACE(FPrint( _L( "CDunUsbPlugin::NotifyConfigRemoval() complete")) ); + return KErrNone; + } + +// ======== GLOBAL FUNCTIONS ======== + +// --------------------------------------------------------------------------- +// NewLocalPluginL implements factory construction for +// the class CDunUsbPlugin. +// The function is exported at ordinal 1. +// Returns: Pointer: The new instance of CDunUsbPlugin +// --------------------------------------------------------------------------- +// +EXPORT_C MDunLocalMediaPlugin* NewLocalPluginL() + { + return new (ELeave) CDunUsbPlugin; + }