usbmgmt/usbmgr/device/classdrivers/acm/classimplementation/ecacm/src/CdcControlInterface.cpp
changeset 0 c9bc50fca66e
child 15 f92a4f87e424
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usbmgmt/usbmgr/device/classdrivers/acm/classimplementation/ecacm/src/CdcControlInterface.cpp	Tue Feb 02 02:02:59 2010 +0200
@@ -0,0 +1,509 @@
+/*
+* 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 <e32base.h>
+#include <e32svr.h>
+#include <c32comm.h>
+#include "CdcControlInterface.h"
+#include "CdcControlInterfaceReader.h"
+#include "ClassDescriptor.h"
+#include "CdcAcmClass.h"
+#include "AcmPanic.h"
+#include "AcmUtils.h"
+#include <usb/usblogger.h>
+
+#ifdef __FLOG_ACTIVE
+_LIT8(KLogComponent, "ECACM");
+#endif
+
+CCdcControlInterface::CCdcControlInterface(const TUint8 aProtocolNum, const TDesC16& aIfcName)
+/**
+ * Constructor using interface name.
+ * @param aProtocolNum Contains the Table 17 protocol number.
+ * @param aIfcName contains the interface name
+ */
+ :	CCdcInterfaceBase(aIfcName), 
+	iSerialState(0xFFFF),
+	iProtocolNum(aProtocolNum)
+	{
+	}
+
+CCdcControlInterface* CCdcControlInterface::NewL(CCdcAcmClass& aParent, const TUint8 aProtocolNum, const TDesC16& aIfcName)
+/**
+ * Create a new CCDCCommClass object and construct it using interface name
+ * This call will return an object with a valid USB configuration
+ *
+ * @param aParent Pointer to the Port using this object
+ * @param aProtocolNum contains the Table 17 protocol number.
+ * @param aIfcName Contains the interface name
+ * @return A pointer to the new object
+ */
+	{
+	LOG_STATIC_FUNC_ENTRY
+
+	LOGTEXT2(_L("\tControl Ifc Name = %S"), &aIfcName);
+
+	CCdcControlInterface* self = new(ELeave) CCdcControlInterface(aProtocolNum, aIfcName);
+	CleanupStack::PushL(self);
+	self->ConstructL(aParent);
+	CLEANUPSTACK_POP(self);
+	return self;
+	}
+
+void CCdcControlInterface::ConstructL(CCdcAcmClass& aParent)
+/**
+ * 2nd-phase construction.
+ * This call registers the object with the USB device driver
+ *
+ * @param aParent The ACM class.
+ */
+	{
+	BaseConstructL();
+
+	iReader  = CCdcControlInterfaceReader::NewL(aParent, iLdd);
+
+	LOGTEXT2(_L8("\tcreated CdcControlInterface iProtocolNum = %d"), iProtocolNum);
+	}
+
+TInt CCdcControlInterface::SetUpInterface()
+/**
+ * Set up the interface for use. This involves finding a "Interrupt IN" 
+ * endpoint and, if found, configuring the interface.
+ */
+	{
+	LOGTEXT(_L8(">>CCdcControlInterface::SetUpInterface"));
+
+	TUsbDeviceCaps dCaps;
+	TInt ret = iLdd.DeviceCaps(dCaps);
+	LOGTEXT(_L8("\tchecking result of DeviceCaps"));
+	if ( ret )
+		{
+		LOGTEXT2(_L8("<<CCdcControlInterface::SetUpInterface ret=%d"), ret);
+		return ret;
+		}
+
+	const TUint KRequiredNumberOfEndpoints = 1; // in addition to endpoint 0.
+
+	const TUint totalEndpoints = static_cast<TUint>(dCaps().iTotalEndpoints);
+	LOGTEXT2(_L8("\tiTotalEndpoints = %d"), totalEndpoints);
+	if ( totalEndpoints < KRequiredNumberOfEndpoints )
+		{
+		LOGTEXT2(_L8("<<CCdcControlInterface::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("<<CCdcControlInterface::SetUpInterface ret=%d"), ret);
+		return ret;
+		}
+
+	// Set the active interface
+	TUsbcInterfaceInfoBuf ifc;
+	TBool epFound = EFalse;
+	for ( TUint i = 0 ; i < totalEndpoints ; i++ )
+		{
+		const TUsbcEndpointCaps* caps = &data[i].iCaps;
+		__ASSERT_DEBUG(caps, 
+			_USB_PANIC(KAcmPanicCat, EPanicInternalError));
+
+		if (data[i].iInUse)
+			{
+			continue;
+			}
+
+		if ((caps->iTypesAndDir & (KUsbEpTypeInterrupt | KUsbEpDirIn)) == 
+			(KUsbEpTypeInterrupt | KUsbEpDirIn))
+			{
+			// EEndpoint1 is interrupt endpoint
+			ifc().iEndpointData[0].iType  = KUsbEpTypeInterrupt;
+			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 Int at 64
+			TInt maxSize = Min(caps->MaxPacketSize(), KMaxPacketTypeInterrupt);
+			
+			ifc().iEndpointData[0].iSize  = maxSize;
+
+			ifc().iEndpointData[0].iInterval = KPollInterval; 
+			epFound = ETrue;
+			break;
+			}
+		}
+	LOGTEXT(_L8("\tchecking epFound"));
+	if ( !epFound )
+		{
+		LOGTEXT2(_L8("<<CCdcControlInterface::SetUpInterface ret=%d"), 
+			KErrGeneral);
+		return KErrGeneral;
+		}
+
+	ifc().iString = &iIfcName;
+	ifc().iTotalEndpointsUsed = KRequiredNumberOfEndpoints;
+	// Codes taken from USBCDC 1.1.
+	ifc().iClass.iClassNum	  = 0x02; // Table 15- Communication Interface Class
+	ifc().iClass.iSubClassNum = 0x02; // Table 16- Abstract Control Model
+	ifc().iClass.iProtocolNum = iProtocolNum; // Table 17
+
+	LOGTEXT(_L8("\tabout to call SetInterface"));
+	// Zero effectively indicates that alternate interfaces are not used.
+	ret = iLdd.SetInterface(0, ifc);
+
+	LOGTEXT2(_L8("<<CCdcControlInterface::SetUpInterface ret=%d"), ret);
+	return ret;
+	}
+
+TInt CCdcControlInterface::SetupClassSpecificDescriptor(
+							TUint8 aDataInterfaceNumber)
+/**
+ * Setup the Class Descriptors
+ *
+ * @param aDataInterfaceNumber The interface number of the data class
+ * @return Error.
+ */
+	{
+	LOG_FUNC
+	LOGTEXT2(_L8("\taDataInterfaceNumber = %d"), aDataInterfaceNumber);
+
+	TInt res;
+
+	TUsbCsClassDescriptor descriptor;
+
+	// Header Functional Descriptor- table 26
+	descriptor.iHdrSize 		 = 0x05; // bFunctionLength
+	descriptor.iHdrType 		 = 0x24; // bDescriptorType- CS_INTERFACE
+	descriptor.iHdrSubType		 = 0x00; // Table 25- Header FD
+	descriptor.iHdrBcdCDC		 = 0x0110; // release number
+
+	// Abstract Control Management Functional Descriptor- table 28
+	descriptor.iAcmSize 		 = 0x04; // bFunctionLength
+	descriptor.iAcmType 		 = 0x24; // bDescriptorType- CS_INTERFACE
+	descriptor.iAcmSubType		 = 0x02; // Table 25- ACM FD
+	descriptor.iAcmCapabilities  = 0x0f; // capabilities- all
+
+	// Union functional descriptor- table 33
+	descriptor.iUnSize			 = 0x05; // bFunctionLength
+	descriptor.iUnType			 = 0x24; // bDescriptorType- CS_INTERFACE
+	descriptor.iUnSubType		 = 0x06; // Table 25- Union FD
+	// Set the control interface as the master...
+	res = GetInterfaceNumber(descriptor.iUnMasterInterface);
+	// ... and the data interface as the slave.
+	descriptor.iUnSlaveInterface = aDataInterfaceNumber;
+
+#if defined(DISABLE_ACM_CF_COUNTRY_SETTING)
+
+	// no functional descriptor needed
+
+#elif defined(ENABLE_ACM_CF_COUNTRY_SETTING)
+
+	// CDC Country Selection Functional Descriptor
+	descriptor.iCsSize			 = 0x04 + (0x02*KUsbCommNumCountries); // bFunctionLength
+	descriptor.iCsType			 = 0x24; // bDescriptorType- CS_INTERFACE
+	descriptor.iCsSubType		 = 0x07; // Table 25- Country Selection FD
+	descriptor.iCsRelDate		 = 0x07; // Release date of ISO3166 country codes.
+	descriptor.iCsCountryCode[0] = KUsbCommCountryCode0; // Country cide
+
+#endif
+
+	if ( res )
+		{
+		LOGTEXT2(_L8("\t***GetInterfaceNumber=%d"), res);
+		return res;
+		}
+
+	LOGTEXT(_L8("\tabout to call SetCSInterfaceDescriptorBlock"));
+	res = iLdd.SetCSInterfaceDescriptorBlock(0, descriptor.Des());
+	if ( res )
+		{
+		LOGTEXT2(_L8("\t***SetCSInterfaceDescriptorBlock=%d"), res);
+		return res;
+		}
+
+	return KErrNone;
+	}
+
+CCdcControlInterface::~CCdcControlInterface()
+/**
+ * Destructor
+ */
+	{
+	LOG_FUNC
+
+	delete iReader;
+	}
+
+/**
+ * Special data object used to send a NETWORK_CONNECTION notification
+ * up to the (USB) Host, using whatever size is available on the
+ * control endpoint (derived in the driver below)
+ */
+const TUint KUSBNotificationNetworkConnectionSize = 8;
+
+NONSHARABLE_CLASS(TUSBNotificationNetworkConnection)
+	{
+public:
+	TUint8	bmRequestType;	///< Request type
+	TUint8	bNotification;	///< Notification number
+	TUint16 wValue; 		///< Notification value
+	TUint16 wIndex; 		///< Notification index
+	TUint16 wLength;		///< Notification length
+public:
+	TDes8&	PackBuffer();
+
+private:
+	TBuf8<KUSBNotificationNetworkConnectionSize> iBuffer;
+	};
+
+TDes8& TUSBNotificationNetworkConnection::PackBuffer()
+/**
+ * This function packs the TUSBNotificationSerialState class into a 
+ * byte buffer with the correct byte alignment for transmission on 
+ * the little-endian USB bus.
+ */
+	{
+	iBuffer.SetLength(KUSBNotificationNetworkConnectionSize);
+
+	iBuffer[0] = bmRequestType;
+	iBuffer[1] = bNotification;
+	iBuffer[2] = static_cast<TUint8>( wValue	& 0x00ff);
+	iBuffer[3] = static_cast<TUint8>((wValue	& 0xff00) >> 8);
+	iBuffer[4] = static_cast<TUint8>( wIndex	& 0x00ff);
+	iBuffer[5] = static_cast<TUint8>((wIndex	& 0xff00) >> 8);
+	iBuffer[6] = static_cast<TUint8>( wLength & 0x00ff);
+	iBuffer[7] = static_cast<TUint8>((wLength & 0xff00) >> 8);
+
+	return iBuffer;
+	}
+
+TInt CCdcControlInterface::SendNetworkConnection(TBool aValue)
+/**
+ * Sends a Network Connection message to the host.
+ * Note that this function has not been tested. It is included for 
+ * completeness as it may need to be used in modem (DCE) mode. However, it is 
+ * unclear how a C32 client would indicate to the CSY that a network 
+ * connection had been established.
+ *
+ * @param aValue	ETrue if Network Connected
+ * @return Error.
+ */
+	{
+	LOGTEXT2(_L8(">>CCdcControlInterface::SendNetworkConnection aValue=%d"), 
+		aValue);
+
+	// form the message and prime it down to the interrupt handler
+	// (that is 'interrupt' in the USB sense)
+
+	// Note that this does not need to be aware of endian-ness, this
+	// is taken care of in the PackBuffer() function.
+	TUSBNotificationNetworkConnection notification;
+
+	notification.bmRequestType = 0xA1;
+	notification.bNotification = 0x00; // NETWORK_CONNECTION
+	notification.wValue 	   = static_cast<TUint16>((aValue)?0x0001:0x0000); //1 - connected, 0 - disconnected
+	notification.wIndex 	   = static_cast<TUint16>((aValue)?0x0001:0x0000);
+	notification.wLength       = 0x00;
+
+	TInt ret = WriteData(EEndpoint1, 
+		notification.PackBuffer(), 
+		notification.PackBuffer().Length());
+
+	LOGTEXT2(_L8("<<CCdcControlInterface::SendNetworkConnection ret=%d"), ret);
+	return ret;
+	}
+
+/**
+ * Special data object used to send a SERIAL_STATE notification
+ * up to the (USB) Host, using whatever size is available on the
+ * control endpoint (derived in the driver below)
+ */
+const TUint KUSBNotificationSerialStateSize = 10;
+
+NONSHARABLE_CLASS(TUSBNotificationSerialState)
+	{
+public:
+	TUint8	bmRequestType;	///< Request type
+	TUint8	bNotification;	///< Notification number
+	TUint16 wValue; 		///< Notification value
+	TUint16 wIndex; 		///< Notification index
+	TUint16 wLength;		///< Notification length
+	TUint16 wData;			///< 2-byte data payload
+public:
+	TDes8&	PackBuffer();
+
+private:
+	TBuf8<KUSBNotificationSerialStateSize> iBuffer;
+	};
+
+TDes8& TUSBNotificationSerialState::PackBuffer()
+/**
+ * This function packs the TUSBNotificationSerialState class into a 
+ * byte buffer with the correct byte alignment for transmission on 
+ * the little-endian USB bus.
+ */
+	{
+	iBuffer.SetLength(KUSBNotificationSerialStateSize);
+
+	iBuffer[0] = bmRequestType;
+	iBuffer[1] = bNotification;
+	iBuffer[2] = static_cast<TUint8>( wValue	& 0x00ff);
+	iBuffer[3] = static_cast<TUint8>((wValue	& 0xff00) >> 8);
+	iBuffer[4] = static_cast<TUint8>( wIndex	& 0x00ff);
+	iBuffer[5] = static_cast<TUint8>((wIndex	& 0xff00) >> 8);
+	iBuffer[6] = static_cast<TUint8>( wLength & 0x00ff);
+	iBuffer[7] = static_cast<TUint8>((wLength & 0xff00) >> 8);
+	iBuffer[8] = static_cast<TUint8>( wData & 0x00ff);
+	iBuffer[9] = static_cast<TUint8>((wData & 0xff00) >> 8);
+
+	return iBuffer;
+	}
+
+TInt CCdcControlInterface::SendSerialState(TBool aOverRun, 
+									 TBool aParity, 
+									 TBool aFraming, 
+									 TBool aRing,
+									 TBool aBreak, 
+									 TBool aTxCarrier, 
+									 TBool aRxCarrier)
+/**
+ * Sends a Serial State message to the host
+ * 
+ * @param aOverRun True if data discarded due to overrun
+ * @param aParity True if a parity error has occured
+ * @param aFraming True if a framing error has occured
+ * @param aRing True if the device is Ringing
+ * @param aBreak True if the device is detecting a break condition
+ * @param aTxCarrier True if the transmit carrier is present
+ * @param aRxCarrier True if the receive carrier is present
+ * @return Error.
+ */
+	{
+	LOG_FUNC
+	LOGTEXT2(_L8("\taOverRun=%d"), aOverRun);
+	LOGTEXT2(_L8("\taParity=%d"), aParity);
+	LOGTEXT2(_L8("\taFraming=%d"), aFraming);
+	LOGTEXT2(_L8("\taRing=%d"), aRing);
+	LOGTEXT2(_L8("\taBreak=%d"), aBreak);
+	LOGTEXT2(_L8("\taTxCarrier=%d"), aTxCarrier);
+	LOGTEXT2(_L8("\taRxCarrier=%d"), aRxCarrier);
+
+	// First work out what might need to be sent by assembling the bits into 
+	// the correct places. See CDC spec table 69 (UART state bitmap values).
+	TUint16 data = static_cast<TUint16>
+					(
+						(aRxCarrier 	) |
+						(aTxCarrier << 1) |
+						(aBreak 	<< 2) |
+						(aRing		<< 3) |
+						(aFraming	<< 4) |
+						(aParity	<< 5) |
+						(aOverRun	<< 6)
+					);
+
+	// now check to see if this has created a different state than
+	// last time it was sent, if it is the same, don't bother to
+	// send it off.
+	if ( data == iSerialState )
+		{
+		LOGTEXT(_L8("\tdata == iSerialState"));
+		return KErrNone;
+		}
+
+	// state is different, store local to the class object ready for
+	// testing next time through
+	iSerialState = data;
+
+	// now form the message and prime it down to the interrupt handler
+	// (that is 'interrupt' in the USB sense)
+
+	// Note that this does not need to be aware of endian-ness, this
+	// is taken care of in the PackBuffer() function.
+	TUSBNotificationSerialState notification;
+
+	notification.bmRequestType = 0xA1;
+	notification.bNotification = 0x20; // SERIAL_STATE
+	notification.wValue 	   = 0x0000;
+	notification.wIndex 	   = 0x0000;
+	notification.wLength	   = 0x0002;
+	notification.wData		   = data;
+
+	TInt ret = WriteData(	EEndpoint1, 
+							notification.PackBuffer(), 
+							notification.PackBuffer().Length());
+	LOGTEXT2(_L8("\tWriteData = %d"), ret);
+
+	return ret;
+	}
+
+TInt CCdcControlInterface::WriteData(TEndpointNumber aEndPoint, 
+							   TDes8& aDes, 
+							   TInt aLength)
+/**
+ *
+ *
+ * @param aEndPoint
+ * @param aDes
+ * @param aLength
+ */
+	{
+	LOG_FUNC
+	LOGTEXT2(_L8("\taEndpoint=%d"), aEndPoint);
+
+	TInt ret;
+	RTimer timer;
+	ret = timer.CreateLocal();
+	if ( ret )
+		{
+		LOGTEXT2(_L8("\ttimer.CreateLocal = %d- returning"), ret);
+		return ret;
+		}
+	TRequestStatus status;
+	TRequestStatus timerStatus;
+	LOGTEXT(_L8("\tAttempting to write data to control interface"));
+	iLdd.Write(status, aEndPoint, aDes, aLength);
+	timer.After(timerStatus, KWriteDataTimeout);
+	User::WaitForRequest(status, timerStatus);
+	if ( timerStatus != KRequestPending )
+		{
+		// Timeout occurred, silently ignore error condition.
+		// Assuming that the line has been disconnected
+		LOGTEXT(_L8("CCdcControlInterface::WriteData() - Timeout occurred"));
+		iLdd.WriteCancel(aEndPoint);
+		User::WaitForRequest(status);
+		ret = timerStatus.Int();
+		}
+	else
+		{
+		LOGTEXT(_L8("CCdcControlInterface::WriteData() - Write completed"));
+		timer.Cancel();
+		User::WaitForRequest(timerStatus);
+		ret = status.Int();
+		}
+
+	LOGTEXT2(_L8("\treturning %d"), ret);
+	return ret;
+	}
+
+//
+// End of file