diff -r 73b88125830c -r b8d1455fddc0 testconns/statapi/device/source/statapi/src/stat_packetisation.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/testconns/statapi/device/source/statapi/src/stat_packetisation.cpp Mon Oct 04 02:58:21 2010 +0300 @@ -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 +#include + +/************************************************************************************** + * + * 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(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 modemQuery = { modemQueryLength, "AT\rAT\rAT" }; + + if( (static_cast(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; + } +} +