obex/obexprotocol/obexusbtransport/src/usbconn.cpp
changeset 0 d0791faffa3f
child 2 4843bb5893b6
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/obex/obexprotocol/obexusbtransport/src/usbconn.cpp	Tue Feb 02 01:11:40 2010 +0200
@@ -0,0 +1,1295 @@
+// Copyright (c) 1997-2009 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 <obexusbtransportinfo.h>
+#include "usbconn.h"
+#include <obex/transport/obexconnector.h>
+#include "logger.h"
+#include "obexusbfaults.h"
+
+#ifdef __FLOG_ACTIVE
+_LIT8(KLogComponent, "USB");
+#endif
+
+#ifdef _DEBUG
+_LIT(KPanicCat, "ObexUsbHandler");
+#endif
+
+
+//
+
+/**
+CObexUsbActiveWriter factory function
+
+@param	aTransport	Reference to a CObexUsbTransportController object. Required for a call to the CObexUsbActiveReader
+@param	aOwner		Reference to a MObexTransportNotify object
+@param	aUsb		Reference to a RDevUsbcClient object
+@param	aInfo		Reference to a TObexConnectionInfo object
+@param  aPacketSize The size of packets used on the underlying link, dependent on High Speed mode.
+@return Ownership of a new CObexUsbActiveWriter.
+*/
+CObexUsbActiveWriter* CObexUsbActiveWriter::NewL(MObexTransportNotify& aOwner, RDevUsbcClient& aUsb,
+										   TObexConnectionInfo& aInfo, TInt aPacketSize)
+	{
+	CObexUsbActiveWriter* self = new(ELeave) CObexUsbActiveWriter(aOwner, aUsb, aInfo, aPacketSize);
+	CleanupStack::PushL(self);
+	self->BaseConstructL();
+	CleanupStack::Pop(self);
+	return self;
+	}
+
+/**
+CObexUsbActiveWriter constructor
+
+@param	aTransport	Reference to a CObexUsbTransportController object. Required for a call to the CObexUsbActiveReader
+@param	aOwner		Reference to a MObexTransportNotify object
+@param	aUsb		Reference to a RDevUsbcClient object
+@param	aInfo		Reference to a TObexConnectionInfo object
+@param  aPacketSize The size of packets used on the underlying link, dependent on High Speed mode.
+*/
+CObexUsbActiveWriter::CObexUsbActiveWriter(MObexTransportNotify& aOwner, RDevUsbcClient& aUsb,
+										   TObexConnectionInfo& aInfo, TInt aPacketSize)
+: CObexWriterBase(EPriorityHigh, aOwner, aInfo),
+ iUsb(aUsb), iPacketSize(aPacketSize)
+	{
+	}
+
+
+/**
+CObexUsbActiveWriter destructor.
+*/
+CObexUsbActiveWriter::~CObexUsbActiveWriter()
+	{
+	LOG_FUNC
+
+	Cancel();
+	}
+
+
+/**
+Start actual transfer.  May be called several times by CObexActiveRW::RunL.
+Queues a write on USB endpoint.
+*/
+void CObexUsbActiveWriter::DoTransfer()
+	{
+	LOG_FUNC
+	LOG1(_L("CObexUsbActiveWriter::DoTransfer [Length=%d]"),iLocation.Length());
+	
+	iLocation.SetMax();
+	iUsb.Write(iStatus, KTransmitEndpoint, iLocation, iLocation.Length(), ETrue);
+	SetActive();
+	}
+
+
+/**
+Cancels an outstanding write.
+*/
+void CObexUsbActiveWriter::DoCancel()
+	{
+	LOG_FUNC
+
+	//Only cancel the write if there's more than 1 packet left to write.
+	//This restriction is imposed to prevent the SUCCESS response being
+	//cancelled when an ObexServer is handling a disconnect and bringing 
+	//the transport down.
+	if(Remaining() > iPacketSize)
+		{
+		iUsb.WriteCancel(KTransmitEndpoint);
+		}		
+	}
+		
+
+//
+
+/**
+CObexUsbActiveReader factory function
+
+@param	aOwner		Reference to a MObexTransportNotify object
+@param	aUsb		Reference to a RDevUsbcClient object
+@param	aInfo		Reference to a TObexConnectionInfo object
+@return Ownership of a new CObexUsbActiveWriter.
+*/
+CObexUsbActiveReader* CObexUsbActiveReader::NewL(MObexTransportNotify& aOwner, RDevUsbcClient& aUsb,
+										   TObexConnectionInfo& aInfo)
+	{
+	CObexUsbActiveReader* self = new(ELeave) CObexUsbActiveReader(aOwner, aUsb, aInfo);
+	CleanupStack::PushL(self);
+	self->BaseConstructL();
+	CleanupStack::Pop(self);
+	return self;
+	}
+
+/**
+CObexUsbActiveReader constructor
+
+@param	aOwner		Reference to a MObexTransportNotify object
+@param	aUsb		Reference to a RDevUsbcClient object
+@param	aInfo		Reference to a TObexConnectionInfo object
+*/
+CObexUsbActiveReader::CObexUsbActiveReader(MObexTransportNotify& aOwner, RDevUsbcClient& aUsb,
+										   TObexConnectionInfo& aInfo)
+: CObexReaderBase(EPriorityStandard, aOwner, aInfo), iUsb(aUsb)
+	{
+	}
+
+
+/**
+CObexUsbActiveReader destructor.
+*/
+CObexUsbActiveReader::~CObexUsbActiveReader()
+	{
+	LOG_FUNC
+
+	Cancel();
+	}
+
+
+/**
+Start actual transfer.  May be called several times by CObexActiveRW::RunL.
+Queues a read on USB endpoint.
+*/
+void CObexUsbActiveReader::DoTransfer()
+	{
+	LOG_FUNC
+	LOG1(_L("CObexUsbActiveReader::DoTransfer [maxLength=%d]"),iLocation.MaxLength());
+
+	iUsb.ReadUntilShort(iStatus, KReceiveEndpoint, iLocation);
+	SetActive();
+	}
+
+
+/**
+Cancels an outstanding read,
+ */
+void CObexUsbActiveReader::DoCancel()
+	{
+	FLOG(_L("CObexUsbActiveReader::DoCancel"));
+
+	iUsb.ReadCancel(KReceiveEndpoint);
+	}
+
+
+/**
+Return the maximum packet size
+@return TInt the maximum packet size for this transport
+*/
+TInt CObexUsbActiveReader::GetMaxPacketSize()
+	{
+	LOG_FUNC
+
+	return GetObexPacketDataLimit();
+	}
+
+
+/**
+Returns a initial packet size when the packet size of iPacket is not know.  This is used
+when determining the remaining bytes to be read.  
+@return TInt the initial packet size
+*/
+TInt CObexUsbActiveReader::GetInitialPacketSize ()
+	{
+	LOG_FUNC
+	
+	return GetObexPacketBufferSize();
+	}
+//
+
+/**
+CObexUsbConnector constructor.
+*/
+CObexUsbConnector::CObexUsbConnector(MObexTransportNotify& aObserver, TObexUsbTransportInfo& aUsbTransportInfo)
+ :	CObexConnector(aObserver), 
+	iObexTransportInfo(aUsbTransportInfo), 
+	iMode(EDefaultClient)
+	{
+	LOG_FUNC
+	// Note iObexTransportInfo reference is only kept so that it can be used in the ConstructL
+	// It may not be safe to use this variables outside of the construction phases
+	}
+
+
+/*
+Returns an instance of CObexUsbConnector
+
+@param	aController  	Reference to a MObexTransportNotify object (the CObexUsbTransportController that owns this)
+@param	aTransportInfo 	Reference to a TObexUsbTransportInfo object containing the transport information
+
+@return	A pointer to a CObexUsbConnector object
+*/
+CObexUsbConnector* CObexUsbConnector::NewL(MObexTransportNotify& aController, TObexUsbTransportInfo& aTransportInfo)
+	{
+	CObexUsbConnector* self = new(ELeave) CObexUsbConnector(aController, aTransportInfo);
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	CleanupStack::Pop(self);
+	return(self);
+	}
+
+
+/**
+Second phase construction.
+*/
+void CObexUsbConnector::ConstructL()
+	{
+	LOG_FUNC
+
+	OpenUsbL(0);
+
+	FLOG(_L("USB opened"));
+
+	iHandler  = CObexUsbHandler::NewL(iObexTransportInfo, *this, iUsb);
+	iCallback = new(ELeave) CAsyncCallBack(EPriorityHigh);
+	iStaller = CObexUsbStaller::NewL(iUsb, *this);
+	iHandler->Start();
+
+	BaseConstructL();
+	}
+
+
+/**
+CObexUsbConnector destructor. Releases endpoint and closes USB object if we have an open connection.
+*/
+CObexUsbConnector::~CObexUsbConnector()
+	{
+	LOG_FUNC
+
+	delete iCallback;
+	delete iHandler;
+	delete iStaller;
+	
+	if (iUsb.Handle())
+		{	
+		iUsb.Close();
+		}
+	}
+
+
+/**
+Attempt to open a client-type connection.
+This will succeed if the USB host has already opened a connection to us
+*/
+void CObexUsbConnector::ConnectL()
+	{
+	LOG_FUNC
+
+	if (iMode == EServer)
+		{
+		// Already performed a server-type connect, so can't act as a
+		// client as well.
+		FLOG(_L(" -- In server mode, give up"));
+		LEAVEIFERRORL(KErrNotSupported);		
+		}
+		
+		
+	FLOG(_L(" -- Not in server mode"));
+
+	iMode = EClient;
+	if (iTransportLinkAvailable)
+		{
+		FLOG(_L(" -- Link available, set a callback"));
+
+		TCallBack callback(LinkUp, this);
+		iCallback->Set(callback);
+		iCallback->CallBack();
+		iConnectRequested = ETrue;
+		}
+	else
+		{
+		FLOG(_L(" -- No link available, set a callback"));
+
+		TCallBack callback(NoDevice, this);
+		iCallback->Set(callback);
+		iCallback->CallBack();
+		}
+	}
+
+
+/*
+@param	aConn	A pointer to a connector object. Will be cast to CObexUsbConnector
+
+@return	KErrNone
+*/
+TInt CObexUsbConnector::LinkUp(TAny* aConn)
+	{
+	FLOG(_L("CObexUsbConnector::LinkUp"));
+
+	reinterpret_cast<CObexUsbConnector*>(aConn)->SignalUp();
+	return KErrNone;
+	}
+
+
+/*
+@param	aConn	A pointer to a connector object. Will be cast to CObexUsbConnector
+
+@return	KErrNone
+*/
+TInt CObexUsbConnector::NoDevice(TAny* aConn)
+	{
+	FLOG(_L("CObexUsbConnector::NoDevice"));
+
+	reinterpret_cast<CObexUsbConnector*>(aConn)->SignalDown(KErrIrObexClientPeerDoesNotHaveObex);
+	return KErrNone;
+	}
+
+
+/**
+Cancel an active connection attempt.  This is unlikely to succeed, as a client
+connection over USB will complete almost immediately.
+*/
+void CObexUsbConnector::CancelConnect()
+	{
+	LOG_FUNC
+
+	if (iMode == EClient)
+		{
+		iCallback->Cancel();
+		}
+	
+	iConnectRequested = EFalse;
+	}
+
+
+/**
+Start listening for a connection to us.
+*/
+void CObexUsbConnector::AcceptL()
+	{
+	LOG_FUNC
+
+	if (iMode == EClient)
+		{
+		// Already carried out a client side connection.  Therefore we
+		// can't be a server as well.
+		FLOG(_L(" -- In client mode, give up"));
+
+		LEAVEIFERRORL(KErrNotSupported);		
+		}
+	
+	FLOG(_L(" -- Not in client mode"));
+
+	iMode = EServer;
+	if (iTransportLinkAvailable)
+		{
+		FLOG(_L(" -- Transport link currently available, set a callback"));
+
+		TCallBack callback(LinkUp, this);
+		iCallback->Set(callback);
+		iCallback->CallBack();
+		}
+	else
+		{
+		FLOG(_L(" -- No link available, wait for one"));
+
+		iWaitingForLink = ETrue;
+		}
+	}
+
+/**
+Stop listening for connections.
+*/
+void CObexUsbConnector::CancelAccept()
+	{
+	LOG_FUNC
+
+	if (iMode == EServer)
+		{
+		iWaitingForLink = EFalse;
+		iCallback->Cancel();
+		}
+	}
+
+
+/**
+Called to indicate transport layer has come up.
+*/
+void CObexUsbConnector::TransportUp()
+	{
+	LOG_FUNC
+	
+	SetTransportPacketSize();
+
+	iTransportLinkAvailable = ETrue;
+	
+	if ((iMode == EServer) && (iWaitingForLink))
+		{
+		FLOG(_L(" -- Signalling link available"));
+
+		iWaitingForLink = EFalse;
+		SignalUp();
+		}
+	}
+
+/**
+Called to indicate transport layer has gone down.
+
+@param	aErr 	Any error code associated with disconnection. Defaults to KErrDisconnected.
+*/
+void CObexUsbConnector::TransportDown(TInt aErr)
+	{
+	LOG_FUNC
+	
+	ResetTransportPacketSize();
+	
+	iStaller->Cancel();
+
+	iTransportLinkAvailable = EFalse;
+		
+	if (iMode == EServer)
+		{
+		FLOG(_L(" -- In server mode, signal link down"));
+
+		SignalDown(aErr);
+		}
+	else
+		{
+		FLOG(_L(" -- In client mode"));
+
+		// Client or default client state
+		if (iConnectRequested)
+			{
+			FLOG(_L(" -- Outstanding client connection. Signal link down"));
+
+			iConnectRequested = EFalse;
+			SignalDown(aErr);
+			}
+		}
+	}
+
+/**
+Signals to the transport controller that the transport is up
+*/
+void CObexUsbConnector::SignalUp()
+	{
+	LOG_FUNC
+
+	// Indicate transport now up
+	TObexConnectionInfo sockinfo;
+	sockinfo.iMaxRecvSize = 0;	// set these to zero as we can't find them out
+	sockinfo.iMaxSendSize = 0;	// and they are not used
+	sockinfo.iSocketType = TObexConnectionInfo::EUsbStream;
+	Observer().TransportUp(sockinfo);
+	}
+
+
+/**
+Signals (with an error code) to the transport controller that the transport has gone down 
+*/
+void CObexUsbConnector::SignalDown(TInt aErr)
+	{
+	FLOG(_L("CObexUsbConnector::SignalDown"));
+
+	Observer().Error(aErr);
+	}
+
+
+/**
+The Obex server running over USB is not able to bring the transport
+down itself. It will only delete the transport object when an error
+has occurred (such as the link being brought down) and hence the
+transport is no longer valid.
+
+@return	ETrue
+*/
+TBool CObexUsbConnector::BringTransportDown()
+{
+	// This cancel function needs to be called here because it
+	// doesn't get called anywhere else. Bluetooth and IrDA rely
+	// on the socket shutdown to cancel the accept.
+	CancelAccept();
+	return ETrue;
+}
+
+
+/**
+Return the transport layer object for use by USB transport objects
+
+@return Pointer to the USB transport layer object
+*/
+RDevUsbcClient* CObexUsbConnector::TransportObject()
+	{
+	return &iUsb;
+	}
+
+
+/**
+Sets a bus stall condition on the IN endpoint.  This will cause the USB
+host to initiate a CLEAR_FEATURE sequence, which notifies the server that the
+Obex link has been reset (equivalent to dropping the Obex level connection)
+*/
+void CObexUsbConnector::SignalTransportError()
+	{
+	LOG_FUNC
+	
+	iStaller->Start();
+	iTransportLinkAvailable = EFalse;
+	iConnectRequested = EFalse;
+	}
+
+
+/**
+Stall cleared => transport available.
+This upcall has been separated from the TransportUp upcall for two
+reasons---it's called from a different class and it happens when an
+error condition has been cleared.  Currently it has no special
+meaning, but this could change in future.
+*/
+void CObexUsbConnector::StallCleared()
+	{
+	LOG_FUNC
+	
+	TransportUp();
+	}
+
+
+/**
+Opens USB and prepares it for use.
+
+@param	aUnit	According to the RDevUsbcClient documentation, this should be 0. 
+*/
+void CObexUsbConnector::OpenUsbL(TInt aUnit)
+	{
+	LOG1(_L("CObexUsbConnector::OpenUsbL called for unit=%d"), aUnit);
+	
+	// Load ldd
+	TInt err = User::LoadLogicalDevice(KUsbLddName);
+
+	if (err != KErrNone && err != KErrAlreadyExists)
+		{
+		LEAVEIFERRORL(err);
+		}
+
+	// Open the requested unit and initialise local state
+	LEAVEIFERRORL(iUsb.Open(aUnit));
+	}
+
+
+void CObexUsbConnector::ResetTransportPacketSize()
+	{
+	iPacketSize = 0;
+	}
+
+
+void CObexUsbConnector::SetTransportPacketSize()
+	{
+	if (iUsb.CurrentlyUsingHighSpeed())
+		{
+		iPacketSize = KMaxPacketTypeBulkHS;
+		}
+	else
+		{
+		iPacketSize = KMaxPacketTypeBulkFS;
+		}
+	}
+
+
+TInt CObexUsbConnector::GetTransportPacketSize()
+	{
+	return iPacketSize;
+	}
+
+
+//
+
+/**
+Constructs a CObexUsbHandler object
+
+@param	aUsbTransportInfo	Reference to a TObexTransportInfo object. Will be cast to TObexUsbTransportInfo.
+@param	aOwner				Reference to a MObexUsbConnector that owns this handler
+@param	aUsb				Reference to a RDevUsbcClient object.
+
+@return	A new CObexUsbHandler object
+*/
+CObexUsbHandler* CObexUsbHandler::NewL(TObexUsbTransportInfo& aUsbTransportInfo,
+									   MObexUsbConnector& aOwner,
+									   RDevUsbcClient& aUsb)
+	{
+	FLOG(_L("CObexUsbHandler::NewL"));
+	
+	CObexUsbHandler* self = new (ELeave) CObexUsbHandler(aUsbTransportInfo, aOwner, aUsb);
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	CleanupStack::Pop(self);
+	return self;
+	}
+
+
+/**
+CObexUsbHandler constructor.
+
+Note that the iObexTransportInfo may not be valid outside of the construction phases
+The constructor copies out most of the information it needs into member variables
+apart from the interface string descriptor, which is used from RegInterfacesL
+
+@param	aUsbTransportInfo	Reference to a TObexTransportInfo object. Will be cast to TObexUsbTransportInfo.
+@param	aOwner				Reference to a MObexUsbConnector that owns this handler
+@param	aUsb				Reference to a RDevUsbcClient object.
+*/
+CObexUsbHandler::CObexUsbHandler(TObexUsbTransportInfo& aUsbTransportInfo,
+								 MObexUsbConnector& aOwner,
+								 RDevUsbcClient& aUsb)
+ : CActive(EPriorityStandard), iObexTransportInfo(aUsbTransportInfo),
+   iOwner(aOwner), iUsb(aUsb)
+	{
+	LOG_FUNC
+	
+	CActiveScheduler::Add(this);
+
+	// Set interface performance configuration parameters
+	if (aUsbTransportInfo.iTransportName == KObexUsbProtocol)
+		// Default values if transport is normal USB - no extra bandwidth, no DMA
+		{
+		FLOG(_L("CObexUsbHandler - Using default USB protocol info"));
+		iBandwidthPriority = EUsbcBandwidthOUTDefault | EUsbcBandwidthINDefault;
+		iRequestDmaOnOutEndpoint = EFalse;
+		iRequestDmaOnInEndpoint = EFalse;
+		}
+	else if (aUsbTransportInfo.iTransportName == KObexUsbProtocolV2)
+		// If transport is extended USB take values from the protocol info
+		{
+		FLOG(_L("CObexUsbHandler - Using USB protocol extended info"));
+		TObexUsbV2TransportInfo& infoV2 = static_cast<TObexUsbV2TransportInfo&>(aUsbTransportInfo);
+		iBandwidthPriority       = infoV2.iBandwidthPriority;
+		iRequestDmaOnOutEndpoint = infoV2.iDmaOnOutEndpoint;
+		iRequestDmaOnInEndpoint  = infoV2.iDmaOnInEndpoint;
+		}
+	else
+		{
+		//aUsbTransportInfo.iTransportName is used to load the correct plug-in, so if this code
+		//is being executed it must have contained one of the options above. Else something
+		//has gone badly wrong.
+		__ASSERT_DEBUG(false, PANIC(KPanicCat, EUnknownUsbTransport));
+		}
+
+	LOG3(_L("CObexUsbHandler - iBandwidthPriority 0x%X, iDmaOnOutEndpoint, %d iDmaOnInEndpoint %d"),
+						iBandwidthPriority, iRequestDmaOnOutEndpoint, iRequestDmaOnInEndpoint);
+	}
+
+
+/**
+Second phase construction.
+*/
+void CObexUsbHandler::ConstructL()
+	{
+
+	// Check bandwidth priority to make sure it makes sense
+	// Reasoning is that the bitwise OR of the maximum values of IN and OUT priorities
+	// covers all the bits that can be set in iBandwidthPriority so, using this as a mask,
+	// anything set outside of this indicates that iBandwidthPriority is corrupt
+	if ( ( iBandwidthPriority & ~( EUsbcBandwidthOUTMaximum | EUsbcBandwidthINMaximum ) ) != 0 )
+		{
+		LEAVEIFERRORL(KErrArgument);
+		}
+
+	FLOG(_L("About to open USB comms interface connection"));
+
+	LEAVEIFERRORL(iUsbComms.Open(0));
+
+	FLOG(_L("Registering interfaces"));
+
+	RegInterfacesL();
+	}
+
+
+/**
+CObexUsbHandler destructor.
+*/
+CObexUsbHandler::~CObexUsbHandler()
+	{
+	LOG_FUNC
+	Cancel();
+
+	// Must release interfaces from highest alternate setting downwards
+	if (iUsb.Handle())
+		{
+#ifndef NO_ALTERNATE_USB_INTERFACE_SUPPORT
+		iUsb.ReleaseInterface(1 /*alternate setting*/);
+#endif
+		iUsb.ReleaseInterface(0 /*alternate setting*/);
+		}
+
+	if (iUsbComms.Handle())
+		{	
+		iUsbComms.ReleaseInterface(0 /*alternate setting*/);
+		}
+
+	iUsbComms.Close();
+	}
+
+
+/**
+Standard active object error function.
+
+@return	KErrNone because currently nothing should cause this to be called.
+*/
+TInt CObexUsbHandler::RunError(TInt /*aError*/)
+	{
+	return KErrNone;
+	}
+
+
+/**
+This function will be called upon a change in the state of the device
+(as set up in AcceptL).
+*/
+void CObexUsbHandler::RunL()
+	{
+	LOG1(_L("CObexUsbHandler::RunL called state=0x%X"), iUsbState);
+	
+	if (iStatus != KErrNone)
+		{
+		LOG1(_L("CObexUsbHandler::RunL() - Error = %d"),iStatus.Int());
+
+		iTransportUp = EFalse;
+		iOwner.TransportDown(iStatus.Int());
+		return;
+		}
+
+	// Check whether the alternate setting to use or the device state has changed.
+	// We have to check the device state to handle the unplugging of the cable. We can't
+	// rely on the always-outstanding read to complete with an error, because the driver
+	// doesn't have a well-defined error code for this.
+
+	if (iUsbState & KUsbAlternateSetting)
+		{
+		iUsbState &= ~KUsbAlternateSetting;
+
+		LOG1(_L("switched to alternate setting %d"), iUsbState);
+
+		if (iUsbState == KObexAlt0)
+			{
+			// alternate setting 0 - function inactive
+			if (iTransportUp)
+				{
+				FLOG(_L("CObexUsbHandler::RunL - transport layer going down..."));
+
+				iTransportUp = EFalse;
+				iOwner.TransportDown();
+				}
+			}
+		else if (iUsbState == KObexAlt1)
+			{
+			// alternate setting 1 - function active
+			if (!iTransportUp)
+				{
+				FLOG(_L("CObexUsbHandler::RunL - transport layer coming up..."));
+
+				// Now the transport is selected, DMA can be allocated to the endpoints, if required
+				AllocateDma();
+
+				iTransportUp = ETrue;
+
+				iOwner.TransportUp();
+				}
+			}
+		else
+			{
+			FLOG(_L("WARNING: unknown setting!"));
+			}
+		}
+	else
+		{
+		TUsbcDeviceState deviceState = static_cast<TUsbcDeviceState>(iUsbState);
+
+		switch(deviceState)
+			{
+			case EUsbcDeviceStateUndefined:
+			case EUsbcDeviceStateAttached:
+			case EUsbcDeviceStatePowered:
+			case EUsbcDeviceStateDefault:
+			case EUsbcDeviceStateAddress:
+#ifdef ERRONEOUS_SUSPEND_INDICATIONS
+			// On Lubbock, we sometimes get "suspended" when the cable is unplugged.
+			case EUsbcDeviceStateSuspended:
+#endif // ERRONEOUS_SUSPEND_INDICATIONS
+				if (iTransportUp)
+					{
+					FLOG(_L("CObexUsbHandler::RunL - transport layer going down..."));
+
+					iTransportUp = EFalse;
+					iOwner.TransportDown();
+					}
+				break;
+
+#ifndef ERRONEOUS_SUSPEND_INDICATIONS
+			case EUsbcDeviceStateSuspended:
+				break;
+#endif // !ERRONEOUS_SUSPEND_INDICATIONS
+
+			case EUsbcDeviceStateConfigured:
+				// Normally, we don't do anything here, because the transport only goes up when
+				// we actually receive the alternate interface change.
+#ifdef NO_ALTERNATE_USB_INTERFACE_SUPPORT
+				if (!iTransportUp)
+					{
+					FLOG(_L("Device now configured: transport layer coming up..."));
+
+					iTransportUp = ETrue;
+
+					// DMA can be allocated to the endpoints, if required
+					// This has to be done here for no alternate interface devices
+					// as they will not receive interface change notification
+					AllocateDma();
+
+					iOwner.TransportUp();
+					}
+#endif // NO_ALTERNATE_USB_INTERFACE_SUPPORT
+				break;
+
+			default:
+				__ASSERT_DEBUG(false, PANIC(KPanicCat, EUnknownUsbState));
+				break;
+			}
+		}
+
+	// Await further notification of a state change. We may have called Error(), which
+	// would already have set this notification request.
+	if (!IsActive())
+		{
+		iUsb.AlternateDeviceStatusNotify(iStatus, iUsbState);
+		SetActive();
+		}
+
+	FLOG(_L("CObexUsbHandler::RunL finished"));
+	}
+
+
+/**
+Standard active object cancellation function.
+*/
+void CObexUsbHandler::DoCancel()
+	{
+	LOG_FUNC
+
+	iUsb.AlternateDeviceStatusNotifyCancel();
+	}
+
+
+/**
+Registers the required comms and data interfaces.
+*/
+void CObexUsbHandler::RegInterfacesL()
+	{
+	LOG_FUNC
+
+	// Setup a CDC Communication Class interface
+	TUsbcInterfaceInfoBuf ifc;
+
+	// Extract the interface string from the transport info structure we were
+	// passed on construction. This isn't const because the SetInterface API
+	// requires it not to be.
+	// This is the only point where iObexTransportInfo is used directly
+	// Do not use iObexTransportInfo outside of construction phases
+	TPtrC string(iObexTransportInfo.iInterfaceStringDescriptor);
+
+	LOG1(_L("Using interface string \"%S\""), &string);
+		
+	ifc().iString = &string;
+	ifc().iClass.iClassNum = KObexClassNumber;		
+	ifc().iClass.iSubClassNum = KObexSubClassNumber;
+	ifc().iClass.iProtocolNum = KObexProtocolNumber;
+	ifc().iTotalEndpointsUsed = 0;
+	
+	// Indicate that this interface does not expect any control transfers 
+	// from EP0.
+	ifc().iFeatureWord |= KUsbcInterfaceInfo_NoEp0RequestsPlease;
+	
+	FLOG(_L("CObexUsbListeningConnector::RegInterfacesL - setting comms interface")); 
+	
+	TInt err = iUsbComms.SetInterface(0, ifc);
+
+	if (err != KErrNone) 
+		{ 
+		LOG1(_L("SetInterface failed with error %d"), err);
+		LEAVEIFERRORL(err);
+		}
+
+	// Get the interface number for later 
+	TInt intDescSize;
+	err = iUsbComms.GetInterfaceDescriptorSize(0,intDescSize);	
+	if(err != KErrNone)
+		{
+		LOG1(_L("Failed to get interface descriptor size. Err = %d"),err);
+		intDescSize = KObexDefaultInterfaceDescriptorLength; //Default is 100
+		}
+	HBufC8* interfaceDescriptor;
+	interfaceDescriptor = HBufC8::NewLC(intDescSize);
+	TPtr8 pIntDesc = interfaceDescriptor->Des();
+	iUsbComms.GetInterfaceDescriptor(0, pIntDesc);
+	TUint8 obexIntNo = interfaceDescriptor->Ptr()[2];
+	CleanupStack::PopAndDestroy();
+
+	TBuf8<KObexInterfaceDescriptorBlockLength> desc;
+
+	// Comms Class Header Functional Descriptor
+
+	desc.Append(KObexFunctionalDescriptorLength);
+	desc.Append(KUsbDescType_CS_Interface);
+	desc.Append(KHeaderFunctionalDescriptor);
+	desc.Append(KCdcVersionNumber[0]);
+	desc.Append(KCdcVersionNumber[1]);
+
+	// Obex Functional Descriptor
+
+	desc.Append(KObexFunctionalDescriptorLength);
+	desc.Append(KUsbDescType_CS_Interface);
+	desc.Append(KObexFunctionalDescriptor);
+	desc.Append(KWmcdcVersionNumber[0]);
+	desc.Append(KWmcdcVersionNumber[1]);
+
+	// Union Functional Descriptor
+	
+	desc.Append(KObexFunctionalDescriptorLength);
+	desc.Append(KUsbDescType_CS_Interface); 
+	desc.Append(KUnionFunctionalDescriptor);
+	desc.Append(obexIntNo); 
+	TInt dataInt = obexIntNo + 1;
+	desc.Append(static_cast<TUint8>(dataInt));		
+
+	err = iUsbComms.SetCSInterfaceDescriptorBlock(0, desc);
+	if (err != KErrNone)
+		{
+		LOG1(_L("SetCSInterfaceDescriptorBlock failed with error %d"), err);
+		LEAVEIFERRORL(err);
+		}
+
+	// Setup CDC Data Class interfaces
+
+	// Only set the "function inactive" interface (alternate setting 0)
+	// if the device can support alternate interfaces
+#ifndef NO_ALTERNATE_USB_INTERFACE_SUPPORT
+	TUsbcInterfaceInfoBuf dataifc;
+
+	dataifc().iString = NULL;
+	dataifc().iClass.iClassNum = KObexDataClass;		
+	dataifc().iClass.iSubClassNum = KObexDataSubClass;
+	dataifc().iClass.iProtocolNum = 0;
+	dataifc().iTotalEndpointsUsed = 0;
+
+	// Indicate that this interface does not expect any control transfers 
+	// from EP0.
+	dataifc().iFeatureWord |= KUsbcInterfaceInfo_NoEp0RequestsPlease;
+
+	FLOG(_L("Setting data class interface (alt setting 0")); 
+	
+	err = iUsb.SetInterface(0 /*alternate setting*/, dataifc, iBandwidthPriority);
+	if (err != KErrNone) 
+		{
+		LOG1(_L("Cannot set data interface (alternate setting 0): error %d"), err);
+		LEAVEIFERRORL(err);
+		}
+
+	// If anything below this point leaves, make sure this alternate setting
+	// gets released.
+	CleanupStack::PushL(TCleanupItem(CleanupReleaseInterface0, &iUsb));
+#endif // NO_ALTERNATE_USB_INTERFACE_SUPPORT
+
+	// Check the device has enough endpoints for the "function active" data interface
+	TUsbDeviceCaps dCaps;
+	LEAVEIFERRORL(iUsb.DeviceCaps(dCaps));
+	
+	TInt n = dCaps().iTotalEndpoints;
+	if (n < KObexMinNumEndpoints) 
+		{
+		LEAVEIFERRORL(KErrOverflow);
+		}
+	
+	// Use the hardware's HS capability to determine maximum bulk transfer packet size
+	TInt maxPacketSize = (dCaps().iHighSpeed) ? KMaxPacketTypeBulkHS : KMaxPacketTypeBulkFS;
+	
+		
+	// Get information on the available endpoints from the driver
+	TUsbcEndpointData data[KUsbcMaxEndpoints];
+	TPtr8 dataptr(REINTERPRET_CAST(TUint8*, data), sizeof(data), sizeof(data));
+	LEAVEIFERRORL(iUsb.EndpointCaps(dataptr));
+
+	// Check to see if there are suitably capable IN and OUT endpoints available
+	// and fill dataifc2 structure accordingly.
+	//
+	// NOTE: The order the iEndpointData array is filled matches the order used for the
+	// virtual endpoint numbers KTransmitEndpoint and KReceiveEndpoint - so change with caution!
+	//
+	// The virtual endpoint numbers are 1-based, whereas the iEndpointData array is 0 based
+	// hence the subtraction in the array indices
+	//
+	// NOTE: IN and OUT refer to the host so in this case:
+	//			IN  => device to PC = KTransmitEndpoint
+	//			OUT => PC to device = KReceiveEndpoint
+	TUsbcInterfaceInfoBuf dataifc2;
+	TBool foundIn = EFalse;
+	TBool foundOut = EFalse;
+	for (TInt i = 0; !(foundIn && foundOut) && i < n; i++)
+		{
+		const TUsbcEndpointCaps* caps = &data[i].iCaps;
+		if (data[i].iInUse)
+			{
+			continue;
+			}
+			
+		const TUint KBulkInFlags = KUsbEpTypeBulk | KUsbEpDirIn;
+		const TUint KBulkOutFlags = KUsbEpTypeBulk | KUsbEpDirOut;
+
+		if (!foundIn && (caps->iTypesAndDir & KBulkInFlags) == KBulkInFlags)
+			{
+			dataifc2().iEndpointData[KTransmitEndpoint - 1].iType  = KUsbEpTypeBulk;
+			dataifc2().iEndpointData[KTransmitEndpoint - 1].iDir   = KUsbEpDirIn;	
+			TInt maxSize = caps->MaxPacketSize();
+			if (maxSize > maxPacketSize)
+				{
+				maxSize = maxPacketSize;
+				}
+			dataifc2().iEndpointData[KTransmitEndpoint - 1].iSize  = maxSize;
+			foundIn = ETrue;
+			}
+		else if (!foundOut && (caps->iTypesAndDir & KBulkOutFlags) == KBulkOutFlags)
+			{
+			dataifc2().iEndpointData[KReceiveEndpoint - 1].iType = KUsbEpTypeBulk;
+			dataifc2().iEndpointData[KReceiveEndpoint - 1].iDir = KUsbEpDirOut;
+			TInt maxSize = caps->MaxPacketSize();
+			if (maxSize > maxPacketSize)
+				{
+				maxSize = maxPacketSize;
+				}
+			dataifc2().iEndpointData[KReceiveEndpoint - 1].iSize  = maxSize;
+			foundOut = ETrue;
+			}
+		}
+		
+	if (!(foundIn && foundOut)) 
+		{
+		LEAVEIFERRORL(KErrHardwareNotAvailable);
+		}
+	
+	// Set the active interface.
+	dataifc2().iString = NULL;
+	dataifc2().iClass.iClassNum = KObexDataClass;		
+	dataifc2().iClass.iSubClassNum = KObexDataSubClass;
+	dataifc2().iClass.iProtocolNum = 0;
+	dataifc2().iTotalEndpointsUsed = KObexTotalEndpoints;
+	
+	// Indicate that this interface does not expect any control transfers 
+	// from EP0.
+	dataifc2().iFeatureWord |= KUsbcInterfaceInfo_NoEp0RequestsPlease;
+		
+#ifdef NO_ALTERNATE_USB_INTERFACE_SUPPORT
+	// For devices that don't suport alternate interfaces, have to place the "function active"
+	// interface at alternate setting 0, although the CDC specification says if should be alternate setting 1
+	FLOG(_L("Setting data class interface (no alternate interface support)"));
+	
+	err = iUsb.SetInterface(0 /*alternate setting*/, dataifc2, iBandwidthPriority);
+
+	if (err != KErrNone) 
+		{
+		//FTRACE(FPrint(_L("Cannot set data interface (no alternate interface support): error %d"), err));
+		LOG1(_L("Cannot set data interface (no alternate interface support): error %d"), err);
+		LEAVEIFERRORL(err);
+		}
+#else
+	FLOG(_L("Setting data class interface (alternate setting 1)"));
+	
+	err = iUsb.SetInterface(1 /*alternate setting*/, dataifc2, iBandwidthPriority); 
+
+	if (err != KErrNone) 
+		{
+		//FTRACE(FPrint(_L("Cannot set data interface (alternate setting 1): error %d"), err));
+		LOG1(_L("Cannot set data interface (alternate setting 1): error %d"), err);
+		LEAVEIFERRORL(err);
+		}
+
+	CleanupStack::Pop(); // ReleaseInterface0
+#endif
+
+	FLOG(_L("CObexUsbHandler::RegInterfacesL - finished."));
+	}
+
+
+/**
+Utility function which releases the first alternate setting of the specified
+interface. Used when setting up Obex interfaces.
+
+@param	aInterface	The interface to release
+*/
+void CObexUsbHandler::CleanupReleaseInterface0(TAny* aInterface)
+	{
+	reinterpret_cast<RDevUsbcClient*>(aInterface)->ReleaseInterface(0 /*alternate setting*/);
+	}
+
+
+/**
+Accept an incoming connection.
+*/
+void CObexUsbHandler::Start()
+	{
+	LOG_FUNC
+
+	// Await notification of a state change (this is like waiting for a connect...).
+	iUsb.AlternateDeviceStatusNotify(iStatus, iUsbState);
+	SetActive();
+	}
+
+	
+/**
+Utility function to allocate DMA to the bulk endpoints - if they have been requested
+This function is called during a connect, once the device is configured,
+so if the allocations fail it wont complain in release builds but debug builds will
+panic with either EDmaAllocationFailedEndpointIn or EDmaAllocationFailedEndpointOut. 
+*/
+void CObexUsbHandler::AllocateDma()
+	{
+	LOG_FUNC
+
+	if (iRequestDmaOnInEndpoint)
+		{
+		TInt err = iUsb.AllocateEndpointResource(KTransmitEndpoint, EUsbcEndpointResourceDMA);
+		// The following log message is checked for in the test code - change with caution!
+		LOG1(_L("IN Endpoint DMA resource allocation result %d"), err);
+		(void)err; // to stop compilers complaining about unused variables
+		}
+
+	if (iRequestDmaOnOutEndpoint)
+		{
+		TInt err = iUsb.AllocateEndpointResource(KReceiveEndpoint, EUsbcEndpointResourceDMA);
+		// The following log message is checked for in the test code - change with caution!
+		LOG1(_L("OUT Endpoint DMA resource allocation result %d"), err);
+		(void)err; // to stop compilers complaining about unused variables
+		}
+	}
+
+
+//
+
+/**
+Constructs a CObexUsbStaller object
+
+@param	aUsb			Reference to a RDevUsbcClient object.
+@param	aOwner			Reference to a MObexUsbConnector that owns this handler
+
+@return	A new CObexUsbStaller object
+*/
+CObexUsbStaller* CObexUsbStaller::NewL(RDevUsbcClient& aUsb, MObexUsbConnector& aOwner)
+	{
+	CObexUsbStaller* self = new(ELeave) CObexUsbStaller(aUsb, aOwner);
+	return self;
+	}
+
+/**
+CObexUsbStaller constructor.
+
+@param	aUsb			Reference to a RDevUsbcClient object.
+@param	aOwner			Reference to a MObexUsbConnector that owns this handler
+*/
+CObexUsbStaller::CObexUsbStaller(RDevUsbcClient& aUsb, MObexUsbConnector& aOwner)
+	: CActive(EPriorityStandard), iUsb(aUsb), iOwner(aOwner)
+	{
+	CActiveScheduler::Add(this);
+	}
+
+
+/**
+CObexUsbStaller destructor.
+*/
+CObexUsbStaller::~CObexUsbStaller()
+	{
+	Cancel();
+	}
+
+
+/**
+Starts the staller
+*/
+void CObexUsbStaller::Start()
+	{
+	LOG_FUNC
+	
+	if (!IsActive())
+		{
+		FLOG(_L("Halting transmit endpoint..."));
+		iUsb.HaltEndpoint(KTransmitEndpoint);
+		iUsb.EndpointStatusNotify(iStatus, iEndpointStatus);
+		SetActive();
+		}
+	}
+	
+
+/**
+Called when the transmit endpoint has changed state
+*/
+void CObexUsbStaller::RunL()
+	{
+	LOG_FUNC
+	
+	TEndpointState endpointState;
+	iUsb.EndpointStatus(KTransmitEndpoint, endpointState);	
+#ifdef __FLOG_ACTIVE
+	switch (endpointState)
+		{
+		case EEndpointStateNotStalled:
+			{
+			FLOG(_L("Receive endpoint not stalled"));
+			break;
+			}
+		case EEndpointStateStalled:
+			{
+			FLOG(_L("Receive endpoint stalled"));
+			break;
+			}
+		case EEndpointStateUnknown:
+			{
+			FLOG(_L("Receive endpoint unknown state"));
+			break;
+			}
+		}
+#endif		 
+	if (endpointState == EEndpointStateNotStalled)
+		{
+		LOG1(_L("CObexUsbStallWatcher::RunL -- endpoint no longer stalled (0x%08x)"), iEndpointStatus);
+		iOwner.StallCleared();
+		}
+	else
+		{
+		LOG1(_L("CObexUsbStallWatcher::RunL -- endpoint still stalled (0x%08x)"), iEndpointStatus);
+		iUsb.EndpointStatusNotify(iStatus, iEndpointStatus);
+		SetActive();
+		}
+	}
+
+
+/**
+Standard active object error function.
+
+@return	KErrNone because currently nothing should cause this to be called.
+*/
+TInt CObexUsbStaller::RunError(TInt /*aError*/)
+	{
+	LOG_FUNC
+	
+	return KErrNone;
+	}
+	
+
+/**
+Standard active object cancellation function.
+*/	
+void CObexUsbStaller::DoCancel()
+	{
+	FLOG(_L("CObexUsbStaller -- Cancelling status notification"));
+	iUsb.EndpointStatusNotifyCancel();
+	}
+