localconnectivityservice/dun/plugins/src/usb/DunUsbPlugin.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Tue, 31 Aug 2010 16:03:15 +0300
branchRCL_3
changeset 19 0aa8cc770c8a
permissions -rw-r--r--
Revision: 201032 Kit: 201035

/*
* 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 <c32comm.h>
#include <d32usbc.h>
#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; i<count; i++ )
        {
        retTemp = InitOnePort( &iUsbPorts[i] );
        if ( retTemp != KErrNone )
            {
            FTRACE(FPrint( _L( "CDunUsbPlugin::InitPorts() (not ready) complete" )));
            return KErrGeneral;
            }
        }
    FTRACE(FPrint( _L( "CDunUsbPlugin::InitPorts() complete" )));
    return KErrNone;
    }

// ---------------------------------------------------------------------------
// Creates empty usable USB ports that can be used by DUN
// ---------------------------------------------------------------------------
//
TInt CDunUsbPlugin::CreateAllPorts()
    {
    FTRACE(FPrint( _L( "CDunUsbPlugin::CreateAllPorts()" ) ));
    // Connect to comms server
    TInt retTemp = CDunUtils::ConnectCommsServer( iCommServer );
    if ( retTemp != KErrNone )
        {
        FTRACE(FPrint( _L( "CDunUsbPlugin::CreateAllPorts() (ERROR) complete" )));
        return retTemp;
        }
    // Load communications module
    retTemp = iCommServer.LoadCommModule( KUsbCsyName );
    if ( retTemp!=KErrNone && retTemp!=KErrAlreadyExists )
        {
        FTRACE(FPrint( _L( "CDunUsbPlugin::CreateAllPorts() (LoadCommModule failed) complete" ) ));
        return retTemp;
        }
    // Stop waiter now (just to be sure) before explicitly fetching port data
    iUsbConfig->Stop();
    // 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; i<count; i++ )
        {
        FTRACE(FPrint( _L( "CDunUsbPlugin::FreeChannels() i=%d" ), i));
        if ( iUsbPorts[i].iUsbPort.SubSessionHandle() )
            {
            iTransporter->FreeChannel( &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; i<count; i++ )
        {
        if ( &iUsbPorts[i].iUsbPort == aConnId )
            {
            aEntity = &iUsbPorts[i];
            FTRACE(FPrint( _L( "CDunUsbPlugin::GetEntityByConnId() complete")) );
            return i;
            }
        }
    aEntity = NULL;
    FTRACE(FPrint( _L( "CDunUsbPlugin::GetEntityByConnId() (not found) complete")) );
    return KErrNotFound;
    }

// ---------------------------------------------------------------------------
// Gets port's entity by port number
// ---------------------------------------------------------------------------
//
TInt CDunUsbPlugin::GetEntityByPortNumber( TInt aPortNum,
                                           TUsbPortEntity*& aEntity )
    {
    FTRACE(FPrint( _L( "CDunUsbPlugin::GetEntityByPortNumber()")) );
    TInt i;
    TInt count = iUsbPorts.Count();
    for ( i=0; i<count; i++ )
        {
        if ( iUsbPorts[i].iPortNum == aPortNum )
            {
            aEntity = &iUsbPorts[i];
            FTRACE(FPrint( _L( "CDunUsbPlugin::GetEntityByPortNumber() complete")) );
            return i;
            }
        }
    aEntity = NULL;
    FTRACE(FPrint( _L( "CDunUsbPlugin::GetEntityByPortNumber() (not found) complete")) );
    return KErrNotFound;
    }

// ---------------------------------------------------------------------------
// Gets first free port's index and entity
// ---------------------------------------------------------------------------
//
TInt CDunUsbPlugin::GetFirstFreeEntity( TUsbPortEntity*& aEntity )
    {
    FTRACE(FPrint( _L( "CDunUsbPlugin::GetFirstFreeEntity()")) );
    TInt i;
    TInt count = iUsbPorts.Count();
    for ( i=0; i<count; i++ )
        {
        if ( iUsbPorts[i].iPortNum == KErrNotFound &&
             !iUsbPorts[i].iUsbPort.SubSessionHandle() )
            {
            aEntity = &iUsbPorts[i];
            FTRACE(FPrint( _L( "CDunUsbPlugin::GetFirstFreeEntity() complete")) );
            return i;
            }
        }
    aEntity = NULL;
    FTRACE(FPrint( _L( "CDunUsbPlugin::GetFirstFreeEntity() (not found) complete")) );
    return KErrNotFound;
    }

// ---------------------------------------------------------------------------
// From class MDunLocalMediaPlugin.
// CDunUsbPlugin::ConstructL
// ---------------------------------------------------------------------------
//
void CDunUsbPlugin::ConstructL( MDunServerCallback* aServer,
                                CDunTransporter* aTransporter )
    {
    FTRACE(FPrint( _L( "CDunUsbPlugin::ConstructL()" ) ));
    if ( !aServer || !aTransporter )
        {
        FTRACE(FPrint( _L( "CDunUsbPlugin::ConstructL() not initialized!" ) ));
        User::Leave( KErrGeneral );
        }
    iServer = aServer;
    iTransporter = aTransporter;
    FTRACE(FPrint( _L( "CDunUsbPlugin::ConstructL() complete" ) ));
    }

// ---------------------------------------------------------------------------
// From class MDunLocalMediaPlugin.
// Gets called when server changes a plugin's state
// ---------------------------------------------------------------------------
//
TInt CDunUsbPlugin::NotifyServerStateChange( TDunPluginState aPluginState )
    {
    FTRACE(FPrint(_L("CDunUsbPlugin::NotifyServerStateChange()")));
    TInt retTemp;
    switch ( aPluginState )
        {
        case EDunStateTryListen:
            if ( PluginState() != EDunStateLoaded )
                {
                FTRACE(FPrint(_L("CDunUsbPlugin::NotifyServerStateChange() (not ready) complete")));
                return KErrNotReady;
                }
            // Change to listening mode
            TRAPD( retTrap, ConstructListenerL() );
            if ( retTrap != KErrNone )
                {
                FTRACE(FPrint(_L("CDunUsbPlugin::NotifyServerStateChange() (ERROR) complete (%d)"), retTrap));
                return retTrap;
                }
            break;
        case EDunStateTryUninitialize:
            if ( PluginState() == EDunStateUninitialized )
                {
                FTRACE(FPrint(_L("CDunUsbPlugin::NotifyServerStateChange() (not ready) complete")));
                return KErrNotReady;
                }
            // Uninitialize
            retTemp = Uninitialize();
            if ( retTemp != KErrNone )
                {
                FTRACE(FPrint(_L("CDunUsbPlugin::NotifyServerStateChange() (not ready) complete (%d)"), retTemp));
                return retTemp;
                }
            break;
        default:
            FTRACE(FPrint(_L("CDunUsbPlugin::NotifyServerStateChange() (unknown state) complete")));
            return KErrNotSupported;
        }
    FTRACE(FPrint(_L("CDunUsbPlugin::NotifyServerStateChange() complete")));
    return KErrNone;
    }

// ---------------------------------------------------------------------------
// From class MDunLocalMediaPlugin.
// Gets called when server needs to know the active connection
// ---------------------------------------------------------------------------
//
TConnId CDunUsbPlugin::ActiveConnection()
    {
    FTRACE(FPrint(_L("CDunUsbPlugin::ActiveConnection()")));
    FTRACE(FPrint(_L("CDunUsbPlugin::ActiveConnection() (not found) complete")));
    return NULL;
    }

// ---------------------------------------------------------------------------
// From class MDunListenCallback.
// Gets called when new channel must be created
// ---------------------------------------------------------------------------
//
TInt CDunUsbPlugin::NotifyChannelAllocate( TBool& /*aNoFreeChans*/ )
    {
    FTRACE(FPrint(_L("CDunUsbPlugin::NotifyChannelAllocate()")));
    if ( PluginState() != EDunStateListening )
        {
        FTRACE(FPrint(_L("CDunUsbPlugin::NotifyChannelAllocate() (not ready) complete")));
        return KErrNotReady;
        }
    ReportStateChangeUp( EDunStateTryChannel );
    TInt retTemp = InitPorts();
    if ( retTemp != KErrNone )
        {
        FTRACE(FPrint(_L("CDunUsbPlugin::NotifyChannelAllocate() (ERROR) complete")));
        return retTemp;
        }
    // Channels allocated so start to wait for possible port config change
    retTemp = iUsbConfig->IssueRequest();
    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 (<KErrUsbDriverBase) or to the case when personality
        // is changed from USBUI and LOCOD/USBWatcher is too slow to notify the
        // change (KErrAccessDenied).
        if ( aConnReason.iErrorCode < KErrUsbDriverBase ||
             aConnReason.iErrorCode == KErrAccessDenied )
            {
            FTRACE(FPrint( _L( "CDunUsbPlugin::NotifyProgressChangeL() (shutdown now) complete")) );
            iShutdown = ETrue;
            return;
            }
        }
    if ( iShutdown )
        {
        FTRACE(FPrint( _L( "CDunUsbPlugin::NotifyProgressChangeL() (shutdown) complete")) );
        return;
        }
    // Now indications are down indications from local media side
    if ( foundEntity->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;
    }