usbmgmt/usbmgr/device/classdrivers/acm/classimplementation/ecacm/src/CdcDataInterface.cpp
changeset 0 c9bc50fca66e
child 6 96e575696901
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usbmgmt/usbmgr/device/classdrivers/acm/classimplementation/ecacm/src/CdcDataInterface.cpp	Tue Feb 02 02:02:59 2010 +0200
@@ -0,0 +1,581 @@
+/*
+* 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:
+*
+*/
+
+#include <e32std.h>
+#include "CdcDataInterface.h"
+#include "ActiveReader.h"
+#include "ActiveWriter.h"
+#include "AcmPanic.h"
+#include "AcmUtils.h"
+#include "ActiveReadOneOrMoreReader.h"
+#include "ActiveDataAvailableNotifier.h"
+#include "inifile.h"
+#include <usb/usblogger.h>
+
+#ifdef __FLOG_ACTIVE
+_LIT8(KLogComponent, "ECACM");
+#endif
+
+#ifdef __HEADLESS_ACM_TEST_CODE__
+#pragma message ("Building headless ACM (performance test code for RDevUsbcClient)")
+#endif // __HEADLESS_ACM_TEST_CODE__
+
+CCdcDataInterface::CCdcDataInterface(const TDesC16& aIfcName)
+/**
+ * Overloaded Constructor using interface name.
+ * @param aIfcName contains the interface name
+ */
+ :	CCdcInterfaceBase(aIfcName),
+ 	iPacketSize(KDefaultMaxPacketTypeBulk)
+	{
+	}
+
+CCdcDataInterface* CCdcDataInterface::NewL(const TDesC16& aIfcName)
+/**
+ * Create a new CCdcDataInterface object and construct it using interface name
+ * This call will return an object with a valid USB configuration
+ *
+ * @param aParent Observer.
+ * @param aIfcName Contains the interface name
+ * @return A pointer to the new object
+ */
+	{
+	LOG_STATIC_FUNC_ENTRY
+
+	LOGTEXT2(_L("\tData Ifc Name = %S"), &aIfcName);
+
+	CCdcDataInterface* self = new (ELeave) CCdcDataInterface(aIfcName);
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	CLEANUPSTACK_POP(self);
+	return self;
+	}
+
+void CCdcDataInterface::ConstructL()
+/**
+ * Construct the object
+ * This call registers the object with the USB device driver
+ *
+ * @param aParent Observer.
+ */
+	{
+	BaseConstructL();
+
+	iReadOneOrMoreReader  = CActiveReadOneOrMoreReader::NewL(*this, iLdd, EEndpoint2);
+	iReader = CActiveReader::NewL(*this, iLdd, EEndpoint2);
+	iDataAvailableNotifier = CActiveDataAvailableNotifier::NewL(*this, iLdd, EEndpoint2);
+	iWriter = CActiveWriter::NewL(*this, iLdd, EEndpoint1);
+	iLinkState = CLinkStateNotifier::NewL(*this, iLdd);
+	
+	iLinkState->Start();
+
+	LOGTEXT(_L8("\tchecking ecacm.ini"));
+	iHostCanHandleZLPs = EFalse;
+	GetHostZLPHandlingFromFile();
+	LOGTEXT(_L8("\tfinished checking ecacm.ini"));
+	}
+
+TInt CCdcDataInterface::SetUpInterface()
+/**
+ * Retrieves the device capabilities and searches for suitable input and 
+ * output bulk endpoints. If suitable endpoints are found, an interface 
+ * descriptor for the endpoints is registered with the LDD.
+ */
+	{
+	LOGTEXT(_L8(">>CCdcDataInterface::SetUpInterface"));
+
+	TUsbDeviceCaps dCaps;
+	TInt ret = iLdd.DeviceCaps(dCaps);
+	LOGTEXT(_L8("\tchecking result of DeviceCaps"));
+	if ( ret )
+		{
+		LOGTEXT2(_L8("<<CCdcDataInterface::SetUpInterface ret=%d"), ret);
+		return ret;
+		}
+
+	TInt maxBulkPacketSize = (dCaps().iHighSpeed) ? KMaxPacketTypeBulkHS : KMaxPacketTypeBulkFS;
+	const TUint KRequiredNumberOfEndpoints = 2;
+
+	const TUint totalEndpoints = static_cast<TUint>(dCaps().iTotalEndpoints);
+	LOGTEXT2(_L8("\tiTotalEndpoints = %d"), totalEndpoints);
+	if ( totalEndpoints < KRequiredNumberOfEndpoints )
+		{
+		LOGTEXT2(_L8("<<CCdcDataInterface::SetUpInterface ret=%d"), 
+			KErrGeneral);
+		return KErrGeneral;
+		}
+	
+	// Endpoints
+	TUsbcEndpointData data[KUsbcMaxEndpoints];
+	TPtr8 dataptr(reinterpret_cast<TUint8*>(data), sizeof(data), sizeof(data));
+	ret = iLdd.EndpointCaps(dataptr);
+	LOGTEXT(_L8("\tchecking result of EndpointCaps"));
+	if ( ret )
+		{
+		LOGTEXT2(_L8("<<CCdcDataInterface::SetUpInterface ret=%d"), ret);
+		return ret;
+		}
+
+	// 
+	TUsbcInterfaceInfoBuf ifc;
+	TBool foundIn = EFalse;
+	TBool foundOut = EFalse;
+
+	for ( TUint i = 0; !(foundIn && foundOut) && i < totalEndpoints; i++ )
+		{
+		const TUsbcEndpointCaps* caps = &data[i].iCaps;
+		__ASSERT_DEBUG(caps, 
+			_USB_PANIC(KAcmPanicCat, EPanicInternalError));
+		if (data[i].iInUse)
+			continue;
+
+		//get the max packet size it can potentially support
+		//it's possible that it can support Isoch (1023) which is greater
+		//than max for Bulk at 64
+		const TInt maxSize = Min (	maxBulkPacketSize, 
+									caps->MaxPacketSize() );
+
+		const TUint KBulkInFlags = KUsbEpTypeBulk | KUsbEpDirIn;
+		const TUint KBulkOutFlags = KUsbEpTypeBulk | KUsbEpDirOut;
+
+		// use EEndPoint1 for TX (IN) EEndpoint1
+		if (! foundIn && (caps->iTypesAndDir & KBulkInFlags) == KBulkInFlags)
+			{
+
+			ifc().iEndpointData[0].iType  = KUsbEpTypeBulk;
+			ifc().iEndpointData[0].iDir   = KUsbEpDirIn; 
+
+			//get the max packet size it can potentially support
+			//it's possible that it can support Isoch (1023) which is greater
+			//than max for Bulk at 64
+			ifc().iEndpointData[0].iSize  = maxSize;
+			foundIn = ETrue;
+			}
+		// use EEndPoint2 for RX (OUT) endpoint
+		else if (	!foundOut 
+			&&		(caps->iTypesAndDir & KBulkOutFlags) == KBulkOutFlags
+			)
+			{
+			// EEndpoint2 is going to be our RX (OUT, read) endpoint
+			ifc().iEndpointData[1].iType  = KUsbEpTypeBulk;
+			ifc().iEndpointData[1].iDir   = KUsbEpDirOut;
+
+			//get the max packet size it can potentially support
+			//it's possible that it can support Isoch (1023) which is greater
+			//than max for Bulk at 64
+			ifc().iEndpointData[1].iSize  = maxSize;
+			foundOut = ETrue;
+			}
+		}
+
+	if (! (foundIn && foundOut))
+		{
+		LOGTEXT2(_L8("<<CCdcDataInterface::SetUpInterface ret=%d"), 
+			KErrGeneral);
+		return KErrGeneral;
+		}
+
+		// If the device supports USB High-speed, then we request 64KB buffers
+		// (otherwise the default 4KB ones will do).
+
+	TUint bandwidthPriority = (EUsbcBandwidthOUTDefault | EUsbcBandwidthINDefault);
+	if (dCaps().iHighSpeed)
+		{
+		bandwidthPriority = (EUsbcBandwidthOUTPlus2 | EUsbcBandwidthINPlus2);
+		}
+
+	ifc().iString = &iIfcName;
+	ifc().iTotalEndpointsUsed = KRequiredNumberOfEndpoints;
+	ifc().iClass.iClassNum	  = 0x0A; // Table 18- Data Interface Class
+	ifc().iClass.iSubClassNum = 0x00; // Section 4.6- unused.
+	ifc().iClass.iProtocolNum = 0x00; // Table 19- no class specific protocol required
+
+	// Indicate that this interface does not expect any control transfers 
+	// from EP0.
+	ifc().iFeatureWord |= KUsbcInterfaceInfo_NoEp0RequestsPlease;
+
+	LOGTEXT(_L8("\tcalling SetInterface"));
+	// Zero effectively indicates that alternate interfaces are not used.
+	ret = iLdd.SetInterface(0, ifc, bandwidthPriority);
+
+	LOGTEXT2(_L8("<<CCdcDataInterface::SetUpInterface ret=%d"), ret);
+	return ret;
+	}
+
+
+void CCdcDataInterface::MLSOStateChange(TInt aPacketSize)
+	{
+	iPacketSize = aPacketSize;
+	}
+
+
+CCdcDataInterface::~CCdcDataInterface()
+/**
+ * Destructor. Cancel and destroy the child classes.
+ */
+	{
+	LOG_FUNC
+
+	delete iLinkState;
+	delete iReadOneOrMoreReader;
+	delete iReader;
+	delete iWriter;
+	delete iDataAvailableNotifier;
+	}
+
+void CCdcDataInterface::Write(MWriteObserver& aObserver, 
+						  const TDesC8& aDes, 
+						  TInt aLen)
+/**
+ * Write data down the interface
+ * 
+ * @param aObserver The observer to notify of completion.
+ * @param aDes Descriptor containing the data to be sent
+ * @param aLen Length of the data to be sent
+ */
+	{
+	LOG_FUNC
+
+	__ASSERT_DEBUG(!iWriteObserver, 
+		_USB_PANIC(KAcmPanicCat, EPanicInternalError));
+
+	iWriteObserver = &aObserver;
+
+	__ASSERT_DEBUG(iWriter, _USB_PANIC(KAcmPanicCat, EPanicInternalError));
+	
+	if ( iHostCanHandleZLPs )
+		{
+		
+		// Make sure the driver sends a Zero Length Packet if required (i.e. if the 
+		// data size is an exact multiple of the endpoint's packet size). 
+		TBool requestZlp = ( aLen % iPacketSize ) ? EFalse : ETrue;
+		iWriter->Write(aDes, aLen, requestZlp); 
+		}
+	else
+		{
+		iWriter->Write(aDes, aLen, EFalse); 
+		}
+		
+	LOGTEXT(_L8("<<CCdcDataInterface::Write"));
+	}
+
+void CCdcDataInterface::WriteCompleted(TInt aError)
+/**
+ * Called when a write request completes.
+ *
+ * @param aError Error.
+ */
+	{
+	LOGTEXT2(_L8(">>CCdcDataInterface::WriteCompleted aError=%d"), aError);
+
+#ifdef __HEADLESS_ACM_TEST_CODE__
+	// Issue another Read or ReadOneOrMore as appropriate.
+	// If the Write completed with an error, we panic, as it's invalidating 
+	// the test.
+	__ASSERT_DEBUG(aError == KErrNone, 
+		_USB_PANIC(KAcmPanicCat, EPanicInternalError));
+	switch ( iHeadlessReadType )
+		{
+	case ERead:
+		LOGTEXT2(_L8("__HEADLESS_ACM_TEST_CODE__- issuing Read for %d bytes"), 
+			iHeadlessReadLength);
+		__ASSERT_DEBUG(iReader, _USB_PANIC(KAcmPanicCat, EPanicInternalError));
+		iReader->Read(iHeadlessAcmBuffer, iHeadlessReadLength);
+		break;
+	case EReadOneOrMore:
+		LOGTEXT2(_L8("__HEADLESS_ACM_TEST_CODE__- issuing ReadOneOrMore for %d bytes"), 
+			iHeadlessReadLength);
+		__ASSERT_DEBUG(iReadOneOrMoreReader, _USB_PANIC(KAcmPanicCat, EPanicInternalError));
+		iReadOneOrMoreReader->ReadOneOrMore(iHeadlessAcmBuffer, iHeadlessReadLength);
+		break;
+	case EUnknown:
+	default:
+		_USB_PANIC(KAcmPanicCat, EPanicInternalError);
+		break;
+		}
+#else
+	// In case the write observer wants to post another write synchronously on 
+	// being informed that this write has completed, use this little 'temp' 
+	// fiddle.
+	__ASSERT_DEBUG(iWriteObserver, 
+		_USB_PANIC(KAcmPanicCat, EPanicInternalError));
+	MWriteObserver* temp = iWriteObserver;
+	iWriteObserver = NULL;
+	LOGTEXT(_L8("\tcalling WriteCompleted on observer"));
+	temp->WriteCompleted(aError);
+#endif // __HEADLESS_ACM_TEST_CODE__
+
+	LOGTEXT(_L8("<<CCdcDataInterface::WriteCompleted"));
+	}
+
+void CCdcDataInterface::CancelWrite()
+/**
+ * Cancel an outstanding write request
+ */
+	{
+	LOG_FUNC
+	
+	__ASSERT_DEBUG(iWriter, _USB_PANIC(KAcmPanicCat, EPanicInternalError));
+
+	iWriter->Cancel();
+
+	iWriteObserver = NULL;
+	}
+
+void CCdcDataInterface::Read(MReadObserver& aObserver, 
+							 TDes8& aDes, 
+							 TInt aMaxLen)
+/**
+ * Read data from the interface. As the LDD supports an appropriate function, 
+ * this request can be passed straight down. 
+ *
+ * @param aObserver The observer to notify of completion.
+ * @param aDes Descriptor to put the read data in
+ * @param aMaxLen Number of bytes to read
+ */
+	{
+	LOG_FUNC
+
+#ifdef __HEADLESS_ACM_TEST_CODE__
+	LOGTEXT(_L8("__HEADLESS_ACM_TEST_CODE__"));
+	// Issue a Read using our special internal buffer.
+	iHeadlessReadType = ERead;
+	iHeadlessReadLength = aMaxLen;
+	static_cast<void>(&aObserver);
+	static_cast<void>(&aDes);
+	__ASSERT_DEBUG(aMaxLen <= iHeadlessAcmBuffer.MaxLength(), 
+		_USB_PANIC(KAcmPanicCat, EPanicInternalError));
+	__ASSERT_DEBUG(iReader, _USB_PANIC(KAcmPanicCat, EPanicInternalError));
+	iReader->Read(iHeadlessAcmBuffer, aMaxLen);
+#else
+	__ASSERT_DEBUG(!iReadObserver, 
+		_USB_PANIC(KAcmPanicCat, EPanicInternalError));
+	iReadObserver = &aObserver;
+
+	__ASSERT_DEBUG(iReader, _USB_PANIC(KAcmPanicCat, EPanicInternalError));
+	iReader->Read(aDes, aMaxLen);
+#endif // __HEADLESS_ACM_TEST_CODE__
+	}
+
+void CCdcDataInterface::ReadOneOrMore(MReadOneOrMoreObserver& aObserver, 
+								  TDes8& aDes, 
+								  TInt aMaxLen)
+/**
+ * Read a given amount of data from the interface, but complete if any data 
+ * arrives.
+ *
+ * @param aObserver The observer to notify of completion.
+ * @param aDes Descriptor to put the read data in
+ * @param aMaxLen Number of bytes to read
+ */
+	{
+	LOG_FUNC
+
+#ifdef __HEADLESS_ACM_TEST_CODE__
+	LOGTEXT(_L8("__HEADLESS_ACM_TEST_CODE__"));
+	// Issue a ReadOneOrMore using our special internal buffer.
+	iHeadlessReadType = EReadOneOrMore;
+	iHeadlessReadLength = aMaxLen;
+	static_cast<void>(&aObserver);
+	static_cast<void>(&aDes);
+	__ASSERT_DEBUG(aMaxLen <= iHeadlessAcmBuffer.MaxLength(), 
+		_USB_PANIC(KAcmPanicCat, EPanicInternalError));
+	__ASSERT_DEBUG(iReadOneOrMoreReader, 
+		_USB_PANIC(KAcmPanicCat, EPanicInternalError));
+	iReadOneOrMoreReader->ReadOneOrMore(iHeadlessAcmBuffer, aMaxLen);
+#else
+	__ASSERT_DEBUG(!iReadOneOrMoreObserver, 
+		_USB_PANIC(KAcmPanicCat, EPanicInternalError));
+	iReadOneOrMoreObserver = &aObserver;
+
+	__ASSERT_DEBUG(iReadOneOrMoreReader, 
+		_USB_PANIC(KAcmPanicCat, EPanicInternalError));
+	iReadOneOrMoreReader->ReadOneOrMore(aDes, aMaxLen);
+#endif // __HEADLESS_ACM_TEST_CODE__
+	}
+
+void CCdcDataInterface::ReadOneOrMoreCompleted(TInt aError)
+/**
+ * The completion function, called when a ReadOneOrMore request is completed 
+ * by the LDD.
+ *
+ * @param aError The result of the read request.
+ */
+	{
+	LOGTEXT2(_L8(">>CCdcDataInterface::ReadOneOrMoreCompleted aError=%d"), 
+		aError);
+
+#ifdef __HEADLESS_ACM_TEST_CODE__
+	LOGTEXT2(_L8("__HEADLESS_ACM_TEST_CODE__- issuing Write for %d bytes"),
+		iHeadlessAcmBuffer.Length());
+	// Write back the data just read.
+	// If the ReadOneOrMore completed with an error, we panic, as it's 
+	// invalidating the test.
+	__ASSERT_DEBUG(aError == KErrNone, 
+		_USB_PANIC(KAcmPanicCat, EPanicInternalError));
+	__ASSERT_DEBUG(iWriter, _USB_PANIC(KAcmPanicCat, EPanicInternalError));
+	iWriter->Write(iHeadlessAcmBuffer, iHeadlessAcmBuffer.Length(), EFalse); 
+#else
+	__ASSERT_DEBUG(iReadOneOrMoreObserver, 
+		_USB_PANIC(KAcmPanicCat, EPanicInternalError));
+
+	// See comment in WriteCompleted.
+	MReadOneOrMoreObserver* temp = iReadOneOrMoreObserver;
+	iReadOneOrMoreObserver = NULL;
+	LOGTEXT(_L8("\tcalling ReadOneOrMoreCompleted on observer"));
+	temp->ReadOneOrMoreCompleted(aError);
+#endif // __HEADLESS_ACM_TEST_CODE__
+
+	LOGTEXT(_L8("<<CCdcDataInterface::ReadOneOrMoreCompleted"));
+	}
+
+void CCdcDataInterface::ReadCompleted(TInt aError)
+/**
+ * Called by the active reader object when it completes.
+ *
+ * @param aError Error.
+ */
+	{
+	LOGTEXT2(_L8(">>CCdcDataInterface::ReadCompleted aError=%d"), aError);
+
+#ifdef __HEADLESS_ACM_TEST_CODE__
+	LOGTEXT2(_L8("__HEADLESS_ACM_TEST_CODE__- issuing Write for %d bytes"),
+		iHeadlessAcmBuffer.Length());
+	// Write back the data just read.
+	// If the Read completed with an error, we panic, as it's invalidating the 
+	// test.				 
+	__ASSERT_DEBUG(aError == KErrNone,
+		_USB_PANIC(KAcmPanicCat, EPanicInternalError));
+	__ASSERT_DEBUG(iWriter, _USB_PANIC(KAcmPanicCat, EPanicInternalError));
+	iWriter->Write(iHeadlessAcmBuffer, iHeadlessAcmBuffer.Length(), EFalse); 
+#else
+	__ASSERT_DEBUG(iReadObserver, 
+		_USB_PANIC(KAcmPanicCat, EPanicInternalError));
+
+	// See comment in WriteCompleted.
+	MReadObserver* temp = iReadObserver;
+	iReadObserver = NULL;
+	LOGTEXT(_L8("\tcalled ReadCompleted on observer"));
+	temp->ReadCompleted(aError);
+#endif // __HEADLESS_ACM_TEST_CODE__
+
+	LOGTEXT(_L8("<<CCdcDataInterface::ReadCompleted"));
+	}
+
+void CCdcDataInterface::CancelRead()
+/**
+ * Cancel an outstanding read request
+ */
+	{
+	LOG_FUNC
+
+	__ASSERT_DEBUG(iReader, _USB_PANIC(KAcmPanicCat, EPanicInternalError));
+	__ASSERT_DEBUG(iReadOneOrMoreReader, 
+		_USB_PANIC(KAcmPanicCat, EPanicInternalError));
+
+	iReader->Cancel();
+	iReadOneOrMoreReader->Cancel();
+	iReadObserver = NULL;
+	iReadOneOrMoreObserver = NULL;
+	}
+
+
+void CCdcDataInterface::GetHostZLPHandlingFromFile()
+/**
+ * Opens the ECACM.ini file to check on the capabilities of the host device.
+ * If the ini file cannot be found or read successfully, the default setting i.e. the
+ * host device CANNOT handle Zero Length Packets (set in CCdcAcmClass::ConstructL()),
+ * is kept.		
+ */
+	{
+	LOG_FUNC
+	
+	CIniFile* iniFile = NULL;
+
+	_LIT(KEcacmIniFilename, "ecacm.ini" );
+	_LIT(KEcacmIniFilePath, "\\system\\data\\" );
+	
+	TRAPD(error, iniFile = CIniFile::NewL(KEcacmIniFilename, KEcacmIniFilePath));
+
+	if (error == KErrNone)
+		{
+		TInt hostHandlesZLPs = 1;
+	
+		_LIT(KHostUSBDeviceDriver, "HostUSBDeviceDriver");
+		_LIT(KCanHandleZLP, "CanHandleZLP");
+	
+		if ( iniFile->FindVar(KHostUSBDeviceDriver, KCanHandleZLP(), hostHandlesZLPs))
+			{			
+			iHostCanHandleZLPs = (( hostHandlesZLPs == 1 ) ? ETrue : EFalse ); 
+		
+			LOGTEXT2(_L8("\tecacm.ini: CanHandleZLP=%d"), hostHandlesZLPs);
+			}
+			
+		delete iniFile;	
+		}
+	}
+	
+void CCdcDataInterface::NotifyDataAvailableCompleted(TInt aError)
+/**
+ * Called by the active data available notifier object when it completes.
+ *
+ * @param aError Error.
+ */
+	{
+	LOGTEXT2(_L8(">>CCdcDataInterface::NotifyDataAvailableCompleted aError=%d"), aError);	
+	
+	// See comment in WriteCompleted.
+	MNotifyDataAvailableObserver* temp = iNotifyDataAvailableObserver;
+	iNotifyDataAvailableObserver = NULL;
+	LOGTEXT(_L8("\tcalled NotifyDataAvailableCompleted on observer"));
+	temp->NotifyDataAvailableCompleted(aError);
+	
+	LOGTEXT(_L8("<<CCdcDataInterface::NotifyDataAvailableCompleted"));
+	}
+	
+void CCdcDataInterface::NotifyDataAvailable(MNotifyDataAvailableObserver& aObserver)
+/**
+ * Complete if any data arrives.
+ *
+ * @param aObserver The observer to notify of completion.
+ */
+	{
+	LOG_FUNC
+		
+	__ASSERT_DEBUG(!iNotifyDataAvailableObserver, _USB_PANIC(KAcmPanicCat, EPanicInternalError));
+	iNotifyDataAvailableObserver = &aObserver;
+
+	__ASSERT_DEBUG(iDataAvailableNotifier, 
+		_USB_PANIC(KAcmPanicCat, EPanicInternalError));
+	iDataAvailableNotifier->NotifyDataAvailable();
+	}
+
+void CCdcDataInterface::CancelNotifyDataAvailable()
+/**
+ * Cancel notification of arrival of data.
+ */
+	{
+	LOG_FUNC
+
+	__ASSERT_DEBUG(iDataAvailableNotifier, _USB_PANIC(KAcmPanicCat, EPanicInternalError));
+
+	iDataAvailableNotifier->Cancel();
+	iNotifyDataAvailableObserver = NULL;	
+	}
+
+//
+// End of file