testexecfw/statsrv/device/source/statapi/src/stat_packetisation.cpp
changeset 0 3e07fef1e154
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/testexecfw/statsrv/device/source/statapi/src/stat_packetisation.cpp	Mon Mar 08 15:03:44 2010 +0800
@@ -0,0 +1,600 @@
+/*
+* Copyright (c) 2005-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: 
+*
+*/
+
+
+
+ /**************************************************************************************
+ *
+ * Packetisation transport for STAT -- for packet based network layers
+ *
+ *************************************************************************************/
+#include <e32std.h>
+#include <e32base.h>
+
+/**************************************************************************************
+ *
+ * Local Includes
+ *
+ *************************************************************************************/
+#include "assert.h"
+#include "stat.h"
+#include "stat_packetisation.h"
+#include "dataconsumer_memory.h"
+#include "dataconsumer_file.h"
+
+/*************************************************************************
+ *
+ * Definitions
+ *
+ *************************************************************************/
+#define KMinDataSize		64
+#define KMinPacketSize		KHeaderSize + KMinDataSize
+
+ /**************************************************************************************
+ *
+ * CStatTransportPacketisation - Construction
+ *
+ *************************************************************************************/
+CStatTransportPacketisation *CStatTransportPacketisation::NewL( MNotifyStatEngine *aStatEngine, MStatNetwork *aStatNetwork, TUint aPacketSize )
+{
+    CStatTransportPacketisation *self = new (ELeave) CStatTransportPacketisation();
+    CleanupStack::PushL(self);
+	self->ConstructL( aStatEngine, aStatNetwork, aPacketSize );
+	CleanupStack::Pop();
+    return self;
+}
+
+CStatTransportPacketisation::CStatTransportPacketisation() :
+	iDataSupplier( NULL )
+{
+}
+
+void CStatTransportPacketisation::ConstructL( MNotifyStatEngine *aStatEngine, MStatNetwork *aStatNetwork, TUint aPacketSize )
+{
+	// check params
+	asserte( aStatEngine != NULL );
+	asserte( aStatNetwork != NULL );
+	asserte( aPacketSize >= KMinPacketSize );
+
+	// setup general members
+	iStatEngine = aStatEngine;
+	iStatNetwork = aStatNetwork;
+	iTransportStatus = EIdle;
+	iSendStatus = ESendIdle;
+	iRecvStatus = EReceiveIdle;
+	iDataPacketSize = aPacketSize;
+
+	// setup send buffer members
+	iSendFragment = NULL;
+	iSendCommand = 0;
+	iSendDataLength = 0;
+	iSendDataWritten = 0;
+	iSendDataAcknowledged = 0;
+
+	// setup recv buffer members
+	iRecvCommand = 0;
+	iRecvDataLength = 0;
+	iRecvDataReceived = 0;
+}
+
+CStatTransportPacketisation::~CStatTransportPacketisation()
+{
+	if( iSendFragment != NULL )
+		{
+		delete iSendFragment;
+		iSendFragment = NULL;
+		}
+	if( iDataConsumer )
+		{
+		iDataConsumer->Delete( );
+		iDataConsumer = NULL;
+		}
+}
+
+/**************************************************************************************
+ *
+ * CStatTransportPacketisation - MStatApiTransport - all calls except for send are 
+ * passed straight through to the network layer (a flag is recorded for receive as
+ * well). See the STAT design spec doc for the packetisation protocol.
+ *
+ *************************************************************************************/
+TInt CStatTransportPacketisation::InitialiseL( void )
+{
+	return iStatNetwork->InitialiseL( this );
+}
+
+TInt CStatTransportPacketisation::ConnectL( TDesC *aRemoteHost )
+{
+	return iStatNetwork->ConnectL( aRemoteHost );
+}
+
+TInt CStatTransportPacketisation::RequestReceive( void )
+{
+	// set the state and wait for the header
+	iTransportStatus = EReceivePending;
+	iRecvStatus = EReceivingInitialHeader;
+	return iStatNetwork->RequestReceive( KHeaderSize );
+}
+
+TInt CStatTransportPacketisation::Disconnect( void )
+{
+	return iStatNetwork->Disconnect();
+}
+
+TInt CStatTransportPacketisation::Release( void )
+{
+	return iStatNetwork->Release();
+}
+
+TText8 *CStatTransportPacketisation::Error( void )
+{
+	return iStatNetwork->Error();
+}
+
+
+TInt CStatTransportPacketisation::RequestSend( const TUint aCommand, MDataSupplier *const aDataSupplier)
+	{
+	TInt err = KErrNone;
+
+	// make sure a packet can fit the header and at least some data -- otherwise it's useless
+	asserte( iDataPacketSize >= KMinPacketSize );
+
+	// check the state is as expected
+	asserte( iTransportStatus == EIdle );
+	asserte( iSendStatus == ESendIdle );
+
+	// Take a copy of the pointer to the data supplier.
+	iDataSupplier = aDataSupplier;
+
+	// save the command and its data
+	iSendCommand = aCommand;
+	iSendDataLength = 0;
+	if(iDataSupplier)
+		{
+		TInt dataLength = 0;
+		err = iDataSupplier->GetTotalSize( dataLength );
+
+		if( err == KErrNone )
+			{
+				iSendDataLength = static_cast<TUint>(dataLength);
+			}
+		}
+	iSendDataWritten = 0;
+
+	if( err == KErrNone )
+	{
+		// create and send the initial header
+		SendHeaderPacket( iSendCommand, iSendDataLength );
+		err = KSTErrAsynchronous;
+	}
+
+	return (err);
+}
+
+
+
+/**************************************************************************************
+ *
+ * CStatTransportPacketisation - MNotifyStatTransport - all events except HandleSend
+ * and HandleReceive are sent straight through to the engine.  
+ *
+ *************************************************************************************/
+void CStatTransportPacketisation::HandleInitialise( TInt aResult )
+{
+	iStatEngine->HandleInitialise( aResult );
+}
+
+void CStatTransportPacketisation::HandleConnect( TInt aResult )
+{
+	iStatEngine->HandleConnect( aResult );
+}
+
+void CStatTransportPacketisation::HandleDisconnect( TInt aResult )
+{
+	iStatEngine->HandleDisconnect( aResult );
+}
+
+void CStatTransportPacketisation::HandleRelease( TInt aResult )
+{
+	iStatEngine->HandleRelease( aResult );
+}
+
+void CStatTransportPacketisation::HandleError( TInt aError, void *aErrorData )
+{
+	iStatEngine->HandleError( aError, aErrorData );
+}
+
+void CStatTransportPacketisation::HandleInfo( const TDesC *aInfo )
+{
+	iStatEngine->HandleInfo( aInfo );
+}
+
+/**************************************************************************************
+ *
+ * CStatTransportPacketisation - HandleSend
+ *
+ *************************************************************************************/
+void CStatTransportPacketisation::HandleSend( TInt aResult )
+{
+	TUint remainingBytes, packetSize, err;
+	
+	// handle each state
+	switch( iSendStatus ) {
+
+	case ESendingInitialHeader:
+
+		// wait for an acknowledgement for the header
+		iTransportStatus = EReceivePending;
+		iRecvStatus = EReceivingFragmentAck;
+		iSendStatus = ESendIdle;
+		err = iStatNetwork->RequestReceive( KHeaderSize );
+		asserte( err == KSTErrAsynchronous );                   
+		break;
+
+	case ESendingFragmentHeader:
+
+		// if there is data left to send then send it -- otherwise we have completed a send
+		if( iSendDataWritten == iSendDataLength ) {
+			NotifyEngineSend( aResult );
+		} else {
+			remainingBytes = iSendDataLength - iSendDataWritten;
+			packetSize = ((remainingBytes < iDataPacketSize) ? remainingBytes : iDataPacketSize);
+
+			asserte( packetSize <= iDataPacketSize );
+
+			iSendFragment = HBufC8::NewMax(packetSize);
+			if(iSendFragment)
+			{
+				TInt dataCopied = 0;
+				iDataSupplier->GetData( *iSendFragment, packetSize, dataCopied );
+
+				// When we do the sending the data we send
+				// is of size 'bufferSize' but we must only
+				// add 'packetSize' to the running total of
+				// data written.
+				iTransportStatus = ESendPending;
+				iSendStatus = ESendingFragmentData;
+				iSendDataWritten += packetSize;
+				err = iStatNetwork->RequestSend( iSendFragment, packetSize );
+				asserte( err == KSTErrAsynchronous );
+			}
+		}
+		break;
+
+	case ESendingFragmentData:
+
+		// if we have just sent some data then free the buffer
+		asserte( iSendFragment != NULL );
+		delete iSendFragment;
+		iSendFragment = NULL;
+
+		// if we are not packetising data then we have completed the send otherwise we are 
+		// packetising data then the remote host will send an acknowledgement
+		if( iSendDataLength <= iDataPacketSize ) {
+			NotifyEngineSend( aResult );
+		} else {
+			iTransportStatus = EReceivePending;
+			iRecvStatus = EReceivingFragmentAck;
+			iSendStatus = ESendIdle;
+			err = iStatNetwork->RequestReceive( KHeaderSize );
+			asserte( err == KSTErrAsynchronous );
+		}
+		break;
+		
+	case ESendingFragmentAck:
+
+		// if we have just sent a fragment ack then wait for the next fragment header
+		iTransportStatus = EReceivePending;
+		iRecvStatus = EReceivingFragmentHeader;
+		iSendStatus = ESendIdle;
+		err = iStatNetwork->RequestReceive( KHeaderSize );
+		asserte( err == KSTErrAsynchronous );
+		break;
+
+	case ESendIdle:
+		;
+		break;
+	}
+}
+
+/**************************************************************************************
+ *
+ * CStatTransportPacketisation - HandleReceive
+ *
+ *************************************************************************************/
+void CStatTransportPacketisation::HandleReceive( TInt aResult, TDesC8 *aData, TUint aDataLength )
+{
+	TUint *pData, packetSize, remainingBytes, recvCommand, recvLength = 0;
+	TInt err;
+
+	
+	
+	// make sure that we thought we were waiting to receive
+	asserte( aResult == KErrNone );
+	asserte( iTransportStatus == EReceivePending );
+	asserte( iRecvStatus != EReceiveIdle );
+	
+	
+	// Handle a special case where we receive a modem query
+	// from the (Windows) system.  There is a chance that we
+	// will recieve a series of modem query strings of three
+	// lots of 'AT'.  When that happens we need to ignore it.
+	// Usually we use the 'L' macro and let the preprocessor
+	// sort out the length of the character but we have to
+	// force it here because in a UNICODE build the serial
+	// port still delivers 8 bit characters.
+	static const TInt modemQueryLength = 8;
+	static const TLitC8<modemQueryLength + 1> modemQuery = { modemQueryLength, "AT\rAT\rAT" };
+
+	if( (static_cast<TInt>(aDataLength) == modemQueryLength) && 
+			(0 == aData->Compare(modemQuery.operator const TDesC8&()) ))
+	{
+		return;
+	}
+	// End special case of modem query.
+	
+	// STEP 1: process the incoming message
+	switch( iRecvStatus ) {
+
+	case EReceivingInitialHeader:
+
+		// extract the header info and setup the local receive state 
+		pData = (TUint*)aData->Ptr();
+		iRecvCommand = iStatNetwork->NtoHl( pData[0] );
+		iRecvDataLength = iStatNetwork->NtoHl( pData[1] );
+
+		// if this is a resync then abort the connection
+		if( iRecvCommand == RESYNC_ID ) {
+			iStatEngine->HandleError( KSTErrResyncCommand, (void*)0 );
+			iSendStatus = ESendIdle;
+			iRecvStatus = EReceiveIdle;
+			iTransportStatus = EIdle;
+			return;
+		}
+
+		// allocate the data buffers 
+		asserte( iRecvDataReceived == 0 );
+		if( iRecvDataLength > 0 )
+			{
+			// Determine if we use a memory receive buffer
+			// or a temporary file.  Purely arbitary size.
+			static const unsigned int maxSizeOfMemoryConsumer = (1024 * 4);
+
+			if( iRecvDataLength < maxSizeOfMemoryConsumer )
+				{
+				iDataConsumer = CDataConsumerMemory::NewL( );
+				}
+			else
+				{
+				iDataConsumer = CDataConsumerFile::NewL( );
+				}
+			}
+
+		// If the data length is greater that iDataPacketSize then the desktop will
+		// packetise the data into fragments each with a header. If the data is not
+		// packetised then the initial header is equivalent to a fragment header.
+		if( iRecvDataLength <= iDataPacketSize ) {
+			iRecvStatus = EReceivingFragmentHeader;
+			recvCommand = iRecvCommand;
+			recvLength = iRecvDataLength;
+		}
+		break;
+
+	case EReceivingFragmentHeader:
+		
+		// check that this is a fragment header as expected
+		asserte( aDataLength == KHeaderSize );
+		pData = (TUint*)aData->Ptr();
+		recvCommand = iStatNetwork->NtoHl( pData[0] );
+		recvLength = iStatNetwork->NtoHl( pData[1] );
+
+		// if this is not the expected command (e.g. a resync) then throw an error 
+		if( (recvCommand != iRecvCommand) ) {
+			iStatEngine->HandleError( KSTErrUnexpectedFragmentCommand, (void*)recvCommand );
+			iSendStatus = ESendIdle;
+			iRecvStatus = EReceiveIdle;
+			iTransportStatus = EIdle;
+			return;
+		}
+		break;
+
+	case EReceivingData:
+		{
+		// received data is placed into the receive buffer
+		asserte( (iRecvDataReceived + aDataLength) <= iRecvDataLength );
+		iDataConsumer->AddData( *aData );
+		iRecvDataReceived += aDataLength;
+		break;
+		}
+	case EReceivingFragmentAck:
+		
+		// check that this is a fragment ack as expected
+		asserte( aDataLength == KHeaderSize );
+		pData = (TUint*)aData->Ptr();
+		recvCommand = iStatNetwork->NtoHl( pData[0] );
+		recvLength = iStatNetwork->NtoHl( pData[1] );
+
+		// if this is not the expected command (e.g. a resync) then throw an error 
+		if( (recvCommand != iSendCommand) ) {
+			iStatEngine->HandleError( KSTErrUnexpectedFragmentCommand, (void*)recvCommand );
+			iSendStatus = ESendIdle;
+			iRecvStatus = EReceiveIdle;
+			iTransportStatus = EIdle;
+			return;
+		}
+		asserte( recvLength == 0 );
+
+		// update the number of bytes acknowledged
+		iSendDataAcknowledged = iSendDataWritten;
+		break;
+
+	case EReceiveIdle:
+		;
+		break;
+	}
+
+	// STEP 2: decide what to do next
+	switch( iRecvStatus ) {
+
+	case EReceivingInitialHeader:
+
+		// send an acknowledgement for the initial header
+		SendAckPacket( iRecvCommand );
+		break;
+
+	case EReceivingFragmentHeader:
+
+		// if there is more data then wait for it -- otherwise the receive is complete
+		if( recvLength > 0 ) {
+			iTransportStatus = EReceivePending;
+			iRecvStatus = EReceivingData;
+			err = iStatNetwork->RequestReceive( recvLength );	
+			asserte( err == KSTErrAsynchronous );
+		} else {
+			NotifyEngineReceive( aResult );
+		}
+		break;
+
+	case EReceivingData:
+
+		// If we have received some packetised data then we have to acknowledge it. Otherwise
+		// we have completed a receive and should notify the engine and clean up.
+		if( iRecvDataLength > iDataPacketSize)
+			SendAckPacket( iRecvCommand );
+		else
+			NotifyEngineReceive( aResult );
+		break;
+
+	case EReceivingFragmentAck:
+
+		// if the desktop has just acknowledged a fragment then we send the next fragment header,
+		// even if there are no more bytes to send.	
+		remainingBytes = iSendDataLength - iSendDataWritten;
+		packetSize = ((remainingBytes < iDataPacketSize) ? remainingBytes : iDataPacketSize);
+		SendHeaderPacket( iSendCommand, packetSize );
+		break;
+
+	case EReceiveIdle:
+		;
+		break;
+	}
+}
+
+/**************************************************************************************
+ *
+ * Helper functions
+ *
+ *************************************************************************************/
+void CStatTransportPacketisation::SendHeaderPacket( TUint aCommand, TUint aDataLength )
+{
+	TUint err;
+
+	// construct the header in iSendHeader
+	BuildHeader( aCommand, aDataLength );
+
+	// set the state
+	iTransportStatus = ESendPending;
+	iRecvStatus = EReceiveIdle;
+	iSendStatus = ((aDataLength > iDataPacketSize) ? ESendingInitialHeader : ESendingFragmentHeader);
+
+	// send it
+	err = iStatNetwork->RequestSend( &iSendHeader, KHeaderSize );
+	asserte( err == KSTErrAsynchronous );
+}
+
+void CStatTransportPacketisation::SendAckPacket( TUint aCommand )
+{
+	TUint err;
+
+	// construct the header in iSendHeader
+	BuildHeader( aCommand, 0 );
+
+	// set the state
+	iTransportStatus = ESendPending;
+	iRecvStatus = EReceiveIdle;
+	iSendStatus = ESendingFragmentAck;
+
+	// send it
+	err = iStatNetwork->RequestSend( &iSendHeader, KHeaderSize );
+	asserte( err == KSTErrAsynchronous );
+}
+
+void CStatTransportPacketisation::BuildHeader( TUint aCommand, TUint aDataLength )
+{
+	TInt nbCommand, nbLength;
+	TUint *buff;
+
+	// create the header
+	nbCommand = iStatNetwork->HtoNl( aCommand );
+	nbLength = iStatNetwork->HtoNl( aDataLength );
+	iSendHeader.SetLength( 0 );
+	buff = (TUint*)iSendHeader.Ptr();
+	buff[0] = nbCommand;
+	buff[1] = nbLength;
+	iSendHeader.SetLength( KHeaderSize );
+}
+
+void CStatTransportPacketisation::NotifyEngineSend( TUint aResult )
+{
+	// check the state is as expected
+	asserte( iSendDataWritten == iSendDataLength );
+	asserte( (iSendDataLength <= iDataPacketSize) || (iSendDataAcknowledged == iSendDataLength) );
+	
+	// clear the state
+	iSendCommand = 0;
+	iSendDataLength = 0;
+	iSendDataWritten = 0;
+	iSendDataAcknowledged = 0;
+
+	iTransportStatus = EIdle;
+	iSendStatus = ESendIdle;
+
+	// notify the engine
+	iStatEngine->HandleSend( aResult );
+}
+
+void CStatTransportPacketisation::NotifyEngineReceive( TUint aResult )
+{
+	// check the state is as expected
+	asserte( iRecvDataLength == iRecvDataReceived || iRecvDataLength==(unsigned long)-1);
+	asserte( (iRecvDataLength == 0) || (iDataConsumer != NULL) );
+	
+	// clear the send state (allows sending from the receive handler) but don't clear
+	// the recvstatus since the buffers are still in use. This could be solved by 
+	// creating multiple buffers but there is no need now and this would be slow
+	iTransportStatus = EIdle;
+	iSendStatus = ESendIdle;
+
+	// notify the engine 
+	iStatEngine->HandleReceive( aResult, iRecvCommand, iDataConsumer );
+
+	// clear the status state
+	iRecvStatus = EReceiveIdle;
+
+	// clear the recv buffer state
+	iRecvCommand = 0;
+	iRecvDataLength = 0;
+	iRecvDataReceived = 0;
+	iRecvDataAcknowledged = 0;
+
+	if(iDataConsumer)
+		{
+		iDataConsumer->Delete( );
+		iDataConsumer = NULL;
+		}
+}
+