diff -r 000000000000 -r d0791faffa3f mtptransports/mtpusbtransport/usbsic_imp/src/cmtpusbepbase.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mtptransports/mtpusbtransport/usbsic_imp/src/cmtpusbepbase.cpp Tue Feb 02 01:11:40 2010 +0200 @@ -0,0 +1,1087 @@ +// Copyright (c) 2006-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 "cmtpusbconnection.h" +#include "cmtpusbepbase.h" +#include "mtpusbpanic.h" +#include "cmtpusbcontainer.h" +#include "mtpbuildoptions.hrh" +#include "mtpusbprotocolconstants.h" +#include + +const TUint KUSBHeaderSize = 12; +#define UNUSED_VAR(a) (a)=(a) + +/** +Destructor +*/ +CMTPUsbEpBase::~CMTPUsbEpBase() + { + __FLOG(_L8("CMTPUsbEpBase::~CMTPUsbEpBase - Entry")); + Cancel(); + iPacketBuffer.Close(); + __FLOG(_L8("CMTPUsbEpBase::~CMTPUsbEpBase - Exit")); + __FLOG_CLOSE; + } + +/** +Provides the logical endpoint number of the endpoint. +@return The logical endpoint number. +*/ +TEndpointNumber CMTPUsbEpBase::EndpointNumber() const + { + return iConnection.EndpointNumber(iId); + } + +/** +Provides the internal endpoint identifier of the endpoint. +@return The internal endpoint identifier. +*/ +TUint CMTPUsbEpBase::Id() const + { + return iId; + } + +/** +Constructor +@param aId The internal endpoint identifier of the endpoint. +@param aPriority the priority of the active object assigned. +@param aConnection MTP USB device class transport connection which controls +the endpoint. +*/ +CMTPUsbEpBase::CMTPUsbEpBase(TUint aId, TPriority aPriority, CMTPUsbConnection& aConnection) : + CActive(aPriority), + iId(aId), + iReceiveChunkData(NULL, 0), + iReceiveData(NULL, 0), + iSendChunkData(NULL, 0), + iSendData(NULL, 0), + iIsFirstChunk(EFalse), + iConnection(aConnection) + { + CActiveScheduler::Add(this); + } + +/** +Second phase constructor. +*/ +#ifdef __FLOG_ACTIVE +void CMTPUsbEpBase::ConstructL(const TDesC8& aComponentName) +#else +void CMTPUsbEpBase::ConstructL() +#endif + { + __FLOG_OPEN(KMTPSubsystem, aComponentName); + __FLOG(_L8("CMTPUsbEpBase::ConstructL - Entry")); + __FLOG(_L8("CMTPUsbEpBase::ConstructL - Exit")); + } + +/** +Sets the MaxPacketSize for the endpoint. +@param aSize The maximum packet size. +@leave One of the system wide error codes, if a processing failure occurs. +*/ +void CMTPUsbEpBase::SetMaxPacketSizeL(TUint aSize) + { + __FLOG(_L8("CMTPUsbEpBase::SetMaxPacketSizeL - Entry")); + iPacketSizeMax = aSize; + __FLOG_VA((_L8("Endpoint %d maximum packetsize = %u"), iId, iPacketSizeMax)); + // Allocate the packet buffer. + iPacketBuffer.ReAllocL(iPacketSizeMax); + __FLOG(_L8("CMTPUsbEpBase::SetMaxPacketSizeL - Exit")); + } + +/** +Creates a stall condition on the endpoint. +*/ +void CMTPUsbEpBase::Stall() + { + __FLOG(_L8("CMTPUsbEpBase::Stall - Entry")); + __FLOG_VA((_L8("CMTPUsbEpBase state on entry = 0x%08X"), iState)); + Cancel(); + RDevUsbcClient& ldd(Connection().Ldd()); + const TEndpointNumber number(EndpointNumber()); + TEndpointState state; + ldd.EndpointStatus(number, state); + __FLOG_VA((_L8("EndpointStatus = %d"), state)); + if (state != EEndpointStateStalled) + { + __FLOG_VA((_L8("Halting endpoint = %d"), number)); + ldd.HaltEndpoint(number); + } + SetStreamState(EStalled); + __FLOG(_L8("CMTPUsbEpBase::Stall - Exit")); + } + +/** +Clears a stall condition on the endpoint. +*/ +void CMTPUsbEpBase::StallClear() + { + __FLOG(_L8("CMTPUsbEpBase::StallClear - Entry")); + __FLOG_VA((_L8("CMTPUsbEpBase state on entry = 0x%08X"), iState)); + RDevUsbcClient& ldd(Connection().Ldd()); + const TEndpointNumber number(EndpointNumber()); + TEndpointState state; + ldd.EndpointStatus(number, state); + __FLOG_VA((_L8("EndpointStatus = %d"), state)); + if (state != EEndpointStateNotStalled) + { + __FLOG_VA((_L8("Clearing halt on endpoint = %d"), number)); + Connection().Ldd().ClearHaltEndpoint(number); + } + SetStreamState(EIdle); + __FLOG(_L8("CMTPUsbEpBase::StallClear - Exit")); + } + +/** +Indicates whether the endpoint is currently in a stalled condition. +@return ETrue if the endpoint is in a stalled condition, otherwise EFalse. +*/ +TBool CMTPUsbEpBase::Stalled() const + { + return (iState == EStalled); + } + +/** +Determines the relative order of the two endpoints based on their IDs. +@return Zero, if the two objects are equal; a negative value, if the first +endpoint's ID is less than the second, or; a positive value, if the first +endpoint's ID is greater than the second. +*/ +TInt CMTPUsbEpBase::LinearOrder(const CMTPUsbEpBase& aL, const CMTPUsbEpBase& aR) + { + return (aL.iId - aR.iId); + } + +/** +Provides the MTP USB device class transport connection which controls the +endpoint. +@return The MTP USB device class transport connection. +*/ +CMTPUsbConnection& CMTPUsbEpBase::Connection() const + { + return iConnection; + } + +/** +Forces the completion of a transfer in progress. This will +reset the data streams without having to stall the endpoints. + +This is needed because Windows does not expect endpoints to +stall on error conditions. + +@param aReason error code describing the reason for cancelling. +@leave Any of the system wide error codes. +*/ + +void CMTPUsbEpBase::CancelReceiveL(TInt aReason) + { + __FLOG(_L8("CMTPUsbEpBase::CancelReceiveL - Entry")); + + if (DataStreamDirection() == EReceivingState) + { + __FLOG(_L8("Cancel in EReceivingState")); + // Cancel any outstanding request. + Cancel(); + + // Notify the connection and reset the receive data stream. + ResetReceiveDataStream(); + ReceiveDataCompleteL(aReason, *iReceiveDataSink); + // Flush incoming data, otherwise device and PC may get out of sync + FlushRxDataL(); + } + + __FLOG(_L8("CMTPUsbEpBase::CancelReceiveL - Exit")); + } + +/** +Forces the completion of a transfer in progress. This will +reset the data streams without having to stall the endpoints. + +This is needed because Windows does not expect endpoints to +stall on error conditions. + +@param aReason error code describing the reason for cancelling. +@leave Any of the system wide error codes. +*/ + +void CMTPUsbEpBase::CancelSendL(TInt aReason) + { + __FLOG(_L8("CMTPUsbEpBase::CancelSendL - Entry")); + + if (DataStreamDirection() == ESendingState) + { + __FLOG(_L8("Cancel in ESendingState")); + // Cancel any outstanding request. + Cancel(); + // Notify the connection and reset the send data stream. + ResetSendDataStream(); + SendDataCompleteL(aReason, *iSendDataSource); + } + + __FLOG(_L8("CMTPUsbEpBase::CancelSendL - Exit")); + } + +/** +Initiates an asynchronous data receive sequence. +@param aSink The receive data sink buffer. +@leave One of the system wide error codes, if a processing failure occurs. +*/ +void CMTPUsbEpBase::ReceiveDataL(MMTPType& aSink) + { + __FLOG(_L8("CMTPUsbEpBase::ReceiveDataL - Entry")); + __FLOG_VA((_L8("CMTPUsbEpBase state on entry = 0x%08X"), iState)); + if(iState != EIdle) + { + Cancel(); + } + + + __ASSERT_DEBUG(iState == EIdle, Panic(EMTPUsbBadState)); + + iReceiveDataSink = &aSink; + iReceiveDataCommit = iReceiveDataSink->CommitRequired(); + SetStreamState(EReceiveInitialising); + InitiateFirstChunkReceiveL(); + + __FLOG_VA((_L8("CMTPUsbEpBase state on exit = 0x%08X"), iState)); + __FLOG(_L8("CMTPUsbEpBase::ReceiveDataL - Exit")); + } + +/** +Resumes a halted data receive sequence. +@param aSink The receive data sink buffer. +@leave One of the system wide error codes, if a processing failure occurs. +*/ + +void CMTPUsbEpBase::ResumeReceiveDataL(MMTPType& aSink) + { + __FLOG(_L8("CMTPUsbEpBase::ResumeReceiveDataL - Entry")); + __FLOG_VA((_L8("CMTPUsbEpBase state on entry = 0x%08X"), iState)); + __ASSERT_DEBUG(iState == EIdle, Panic(EMTPUsbBadState)); + + iReceiveDataSink = &aSink; + iReceiveDataCommit = iReceiveDataSink->CommitRequired(); + + iChunkStatus = iReceiveDataSink->NextWriteChunk(iReceiveChunkData); + // The first chunk is going to be read. + iReceiveData.Set(iReceiveChunkData); + __FLOG_VA((_L8("Issuing ReadUntilShort request on endpoint %d"), EndpointNumber())); + __FLOG_VA((_L8("Receive chunk capacity = %d bytes, length = %d bytes"), iReceiveChunkData.MaxLength(), iReceiveChunkData.Length())); + __FLOG_VA((_L8("Chunk status = %d"), iChunkStatus)); + Connection().Ldd().ReadUntilShort(iStatus, EndpointNumber(), iReceiveData); + SetStreamState(EReceiveInProgress); + SetActive(); + __FLOG(_L8("CMTPUsbEpBase::ResumeReceiveDataL - Exit")); + } + +/** +Signals the data transfer controller that an asynchronous data receive +sequence has completed. +@leave One of the system wide error codes, if a processing failure occurs. +@panic MTPUsb 2 In debug builds only, if the derived class has not fully +implemented the receive data path. +*/ +void CMTPUsbEpBase::ReceiveDataCompleteL(TInt /*aError*/, MMTPType& /*aSink*/) + { + __FLOG(_L8("CMTPUsbEpBase::ReceiveDataCompleteL - Entry")); + __DEBUG_ONLY(Panic(EMTPUsbNotSupported)); + __FLOG(_L8("CMTPUsbEpBase::ReceiveDataCompleteL - Exit")); + } + +/** +Initiates an asynchronous data send sequence. +@param aSource The send data source buffer. +@leave One of the system wide error codes, if a processing failure occurs. +*/ +void CMTPUsbEpBase::SendDataL(const MMTPType& aSource) + { + __FLOG(_L8("CMTPUsbEpBase::SendDataL - Entry")); + __FLOG_VA((_L8("CMTPUsbEpBase state on entry = 0x%08X"), iState)); + __ASSERT_DEBUG(iState == EIdle, Panic(EMTPUsbBadState)); + + iSendDataSource = &aSource; + SetStreamState(ESendInitialising); + ProcessSendDataStreamL(); + + __FLOG_VA((_L8("CMTPUsbEpBase state on exit = 0x%08X"), iState)); + __FLOG(_L8("CMTPUsbEpBase::SendDataL - Exit")); + } + +/** +Signals tthe data transfer controller that an asynchronous data send sequence +has completed. +@leave One of the system wide error codes, if a processing failure occurs. +@panic MTPUsb 2 In debug builds only, if the derived class has not fully +implemented the send data path. +*/ +void CMTPUsbEpBase::SendDataCompleteL(TInt /*aError*/, const MMTPType& /*aSource*/) + { + __FLOG(_L8("CMTPUsbEpBase::SendDataCompleteL - Entry")); + __DEBUG_ONLY(Panic(EMTPUsbNotSupported)); + __FLOG(_L8("CMTPUsbEpBase::SendDataCompleteL - Exit")); + } + +void CMTPUsbEpBase::DoCancel() + { + __FLOG(_L8("CMTPUsbEpBase::DoCancel - Entry")); + __FLOG_VA((_L8("CMTPUsbEpBase state on entry = 0x%08X"), iState)); + switch (iState & EStateDirection) + { + case EReceivingState: + __FLOG_VA((_L8("Issuing ReadCancel on endpoint %d"), EndpointNumber())); + Connection().Ldd().ReadCancel(EndpointNumber()); + ResetReceiveDataStream(); + break; + + case ESendingState: + __FLOG_VA((_L8("Issuing WriteCancel on endpoint %d"), EndpointNumber())); + Connection().Ldd().WriteCancel(EndpointNumber()); + ResetSendDataStream(); + break; + + default: + break; + } + __FLOG(_L8("CMTPUsbEpBase::DoCancel - Exit")); + } + +TInt CMTPUsbEpBase::RunError(TInt aError) + { + __FLOG(_L8("CMTPUsbEpBase::RunError - Entry")); + __FLOG_VA((_L8("Error = %d"), aError)); + + // Cancel any outstanding request. + Cancel(); + + // Notify the protocol layer of the error. + TInt32 streamDirection = DataStreamDirection(); + if (streamDirection == EReceivingState) + { + __FLOG(_L8("Error in EReceivingState")); + // Notify the connection and reset the receive data stream. + MMTPType& data(*iReceiveDataSink); + ResetReceiveDataStream(); + TRAPD(err, ReceiveDataCompleteL(aError, data)); + UNUSED_VAR(err); + } + else if (streamDirection == ESendingState) + { + __FLOG(_L8("Error in ESendingState")); + // Notify the connection and reset the send data stream. + const MMTPType& data(*iSendDataSource); + ResetSendDataStream(); + TRAPD(err, SendDataCompleteL(aError, data)); + UNUSED_VAR(err); + } + + __FLOG(_L8("CMTPUsbEpBase::RunError - Exit")); + return KErrNone; + } + +void CMTPUsbEpBase::RunL() + { + __FLOG(_L8("CMTPUsbEpBase::RunL - Entry")); + __FLOG_VA((_L8("Current endpoint is %d"), EndpointNumber())); + __FLOG_VA((_L8("CMTPUsbEpBase state on entry = 0x%08X"), iState)); + + switch (DataStreamDirection()) + { + case EReceivingState: + __FLOG_VA((_L8("Receive data completion status = %d"), iStatus.Int())); + if (iStatus != KErrNone) + { + // Abnormal completion. + SetStreamState(EReceiveComplete); + } + else + { + // Reissue the request if we got a null packet because the upper layers are not + // interested in null packets. + if ((iReceiveData.Length() == 0) && (iReceiveData.MaxLength() > 0)) + { + Connection().Ldd().ReadUntilShort(iStatus, EndpointNumber(), iReceiveData); + SetActive(); + return; + } + // Update the chunk data length. + iReceiveChunkData.SetLength(iReceiveChunkData.Length() + iReceiveData.Length()); + if (iIsFirstChunk) + { + // process the first chunk. + ProcessFirstReceivedChunkL(); + } + else + { + ResumeReceiveDataStreamL(); + } + } + + if (iState == EReceiveComplete) + { + // Reset the receive data stream and notify the connection. + MMTPType& data(*iReceiveDataSink); + ResetReceiveDataStream(); + ReceiveDataCompleteL(iStatus.Int(), data); + } + break; + + case ESendingState: + __FLOG_VA((_L8("Send data stream completion status = %d"), iStatus.Int())); + if (iStatus != KErrNone) + { + // Abnormal completion. + SetStreamState(ESendComplete); + } + else + { + ProcessSendDataStreamL(); + } + + if (iState == ESendComplete) + { + // Reset the send data stream and notify the connection. + const MMTPType& data(*iSendDataSource); + ResetSendDataStream(); + SendDataCompleteL(iStatus.Int(), data); + } + break; + + default: + __FLOG_VA((_L8("Invalid data stream state, status = %d"), iStatus.Int())); + Panic(EMTPUsbBadState); + break; + } + + __FLOG_VA((_L8("IsActive = %d"), IsActive())); + __FLOG(_L8("CMTPUsbEpBase::RunL - Exit")); + } + +/** +Provides the current data stream direction. +@return The current data stream direction (EIdle, EReceivingState, or +ESendingState). +@see TState. +*/ +TInt32 CMTPUsbEpBase::DataStreamDirection() const + { + return (iState & EStateDirection); + } + +/** +Resets the receive data stream by clearing all receive buffer pointers and +setting the stream state to EIdle. +*/ +void CMTPUsbEpBase::ResetReceiveDataStream() + { + __FLOG(_L8("CMTPUsbEpBase::ResetReceiveDataStream - Entry")); + iReceiveChunkData.Set(NULL, 0, 0); + iReceiveData.Set(NULL, 0, 0); + iReceiveDataSink = NULL; + SetStreamState(EIdle); + __FLOG(_L8("CMTPUsbEpBase::ResetReceiveDataStream - Exit")); + } + +/** +Resets the receive data stream by clearing all receive buffer pointers and +setting the stream state to EIdle. +*/ +void CMTPUsbEpBase::ResetSendDataStream() + { + __FLOG(_L8("CMTPUsbEpBase::ResetSendDataStream - Entry")); + iSendChunkData.Set(NULL, 0); + iSendData.Set(NULL, 0); + iSendDataSource = NULL; + SetStreamState(EIdle); + __FLOG(_L8("CMTPUsbEpBase::ResetSendDataStream - Exit")); + } + +/** +This method verify if the received first chunk data is a valid +USB header for BulkOut EP. +@pre this method should only be called after the USB header is received. +@return ETrue if the received first chunk data is a vaild USB header, otherwise EFalse. +*/ +TBool CMTPUsbEpBase::ValidateUSBHeaderL() + { + __FLOG(_L8("CMTPUsbEpBase::ValidateUSBHeader - Entry")); + + TBool result(EFalse); + TUint16 containerType(Connection().BulkContainer().Uint16L(CMTPUsbContainer::EContainerType)); + iDataLength = Connection().BulkContainer().Uint32L(CMTPUsbContainer::EContainerLength); + + //Due to an issue of Windows OS, the value of CMTPUsbContainer::EContainerLength is incorrect if the + //object >= 4G-12. The value should be KMaxTUint32 in this kind of cases, but in current Windows + //implementation it will be a value between 0 and 11. + //Here we reset the iDateLength to the actual size of iReceiveDataSink as a walkaround. + if(containerType == 2 && (iDataLength <= 11 || iDataLength == KMaxTUint32)) + { + __FLOG(_L8("iDataLength <= 11, change to size of receive data sink")); + iDataLength = iReceiveDataSink->Size(); + } + + __FLOG_VA((_L8("containerType = %u , dataLength = %lu bytes"), containerType, iDataLength)); + + if (iDataLength >= KUSBHeaderSize && + (containerType == EMTPUsbContainerTypeCommandBlock || containerType == EMTPUsbContainerTypeDataBlock)) + { + result = ETrue; + iDataCounter = 0; +#ifdef _DEBUG + RDebug::Print(_L("Find the valid usb header------------------------------------------------------\n")); + TUint16 code(Connection().BulkContainer().Uint16L(CMTPUsbContainer::ECode)); + TUint32 transactionID(Connection().BulkContainer().Uint32L(CMTPUsbContainer::ETransactionID)); + RDebug::Print(_L("ContainerLength = 0x%x, containerType = 0x%x , code = 0x%x , transactionID = 0x%x "), iDataLength, containerType, code, transactionID); + } + else + { + RDebug::Print(_L("inValid usb Header read...........................................................")); +#endif + } + __FLOG(_L8("CMTPUsbEpBase::ValidateUSBHeader - Exit")); + return result; + } + +/** +Initiates the first chunk received data. +*/ +void CMTPUsbEpBase::InitiateFirstChunkReceiveL() + { + __FLOG(_L8("CMTPUsbEpBase::InitiateFirstChunkReceiveL - Entry")); + + __FLOG(_L8("Fetching first write data chunk")); + iChunkStatus = iReceiveDataSink->FirstWriteChunk(iReceiveChunkData); + // The first chunk is going to be read. + iIsFirstChunk = ETrue; + iReceiveData.Set(iReceiveChunkData); + __FLOG_VA((_L8("Issuing ReadUntilShort request on endpoint %d"), EndpointNumber())); + __FLOG_VA((_L8("Receive chunk capacity = %d bytes, length = %d bytes"), iReceiveChunkData.MaxLength(), iReceiveChunkData.Length())); + __FLOG_VA((_L8("Chunk status = %d"), iChunkStatus)); + Connection().Ldd().ReadUntilShort(iStatus, EndpointNumber(), iReceiveData); + SetStreamState(EReceiveInProgress); + SetActive(); + + __FLOG(_L8("Request issued")); + __FLOG(_L8("CMTPUsbEpBase::InitiateFirstChunkReceiveL - Exit")); + } + +/** +Processes the first received chunk data. +*/ +void CMTPUsbEpBase::ProcessFirstReceivedChunkL() + { + __FLOG(_L8("CMTPUsbEpBase::ProcessFirstReceivedChunkL - Entry")); + + // Reset it back. + iIsFirstChunk = EFalse; + + if (iReceiveChunkData.MaxLength() == KUSBHeaderSize + && iReceiveChunkData.Length() == KUSBHeaderSize) + { + // USB header received from BulkOut EP. + // USB Header validation + if (!ValidateUSBHeaderL()) + { + // If device has received trash data, flush the rest of the packet and try again. + // This will occur when cancelling a transfer and the PC sends buffered data after + // the cancellation. + TRequestStatus status; + do + { + // Keep looking for headers. + // The case we are trying to catch is when we have 12 garbage bytes followed by 12 good bytes. + // In this case the ReadOneOrMore is acting on the next packet rather than the current packet. + // If the garbage data is a multiple of 12 bytes, we should still be able to catch the next good + // header. Otherwise the ReadOneOrMore will return will <12 bytes and we will fall through + // to the retry code below. + iReceiveData.Zero(); + Connection().Ldd().ReadOneOrMore(status, EndpointNumber(), iReceiveData, KUSBHeaderSize); + User::WaitForRequest(status); + } while (iReceiveData.Length()==KUSBHeaderSize && !ValidateUSBHeaderL()); + + if(!ValidateUSBHeaderL()) + { + InitiateFirstChunkReceiveL(); + return; + } + } + + if ((iDataLength - KUSBHeaderSize) == 0) + { + // only USB header. + SetStreamState(EReceiveComplete); + } + } + else if (iReceiveChunkData.MaxLength() == iReceiveChunkData.Length()) + { + // USB Control request setup or data packet is received from Control EP. + // All the desired data should be received. + SetStreamState(EReceiveComplete); + } + + __FLOG_VA((_L8("CMTPUsbEpBase state = 0x%08X"), iState)); + + if (iState == EReceiveComplete) + { + // All data is received just using the first chunk. It could be a USB Command block without parameters + // or USB control request setup or data. + __FLOG_VA((_L8("Received = %d bytes, write data chunk capacity = %d bytes"), iReceiveChunkData.Length(), iReceiveChunkData.MaxLength())); + +#ifdef MTP_DEBUG_FLOG_HEX_DUMP + __FLOG_HEXDUMP((iReceiveChunkData, _L8("Received data "))); +#endif + + // Commit the received data if required. + if (iReceiveDataCommit) + { + __FLOG(_L8("Commiting write data chunk")); + iReceiveDataSink->CommitChunkL(iReceiveChunkData); + } + } + // Receive more data. + else + { + ResumeReceiveDataStreamL(); + } + + __FLOG(_L8("CMTPUsbEpBase::ProcessFirstReceivedChunkL - Exit")); + } + +/** +Implements the receive data streaming algorithm. It is called after the first chunk data is received provided +there is still more data to be received. +*/ +void CMTPUsbEpBase::ResumeReceiveDataStreamL() + { + __FLOG(_L8("CMTPUsbEpBase::ResumeReceiveDataStreamL - Entry")); + __FLOG_VA((_L8("CMTPUsbEpBase state on entry = 0x%08X"), iState)); + TBool endStream(EFalse); + TBool lastChunkCommited(EFalse); + TBool nullPacketReceived(EFalse); + MMTPType *needCommit = NULL; + // Process the received chunk (if any). + iDataCounter += iReceiveData.Length(); + __FLOG_VA((_L8("iDataLength = %lu bytes"), iDataLength)); + __FLOG_VA((_L8("iDataCounter = %lu bytes"), iDataCounter)); + + if (iDataCounter == iDataLength) + { + endStream = ETrue; + nullPacketReceived = ((iState == EReceiveCompleting) && (iReceiveData.Length() == 0)); + } + + __FLOG_VA((_L8("Received = %d bytes, write data chunk capacity = %d bytes"), iReceiveChunkData.Length(), iReceiveChunkData.MaxLength())); +#ifdef MTP_DEBUG_FLOG_HEX_DUMP + __FLOG_HEXDUMP((iReceiveChunkData, _L8("Received data "))); +#endif + __FLOG_VA((_L8("End of stream = %d"), endStream)); + + // Commit the received data if required. + if (iReceiveDataCommit) + { + if ((iChunkStatus != KMTPChunkSequenceCompletion) + && !endStream + && (iReceiveChunkData.Length() == iReceiveChunkData.MaxLength())) + { + // Two cases are covered here: + // 1. MTP file receiving: MTP type file never returns KMTPChunkSequenceCompletion,It can be received + // one part after another. Also it can be commited mutiple times. + // 2. Other MTP datatype receiving during the middle of data stream + __FLOG(_L8("Commiting write data chunk")); + needCommit = iReceiveDataSink->CommitChunkL(iReceiveChunkData); + lastChunkCommited = ETrue; + } + else if ((iChunkStatus != KMTPChunkSequenceCompletion) + && endStream + && !nullPacketReceived) + { + // It should be the end of MTP type file receiving since it never returns KMTPChunkSequenceCompletion. + // it can be commited mutiple times. + __FLOG(_L8("Commiting write data chunk")); + needCommit = iReceiveDataSink->CommitChunkL(iReceiveChunkData); + } + else if ((iChunkStatus == KMTPChunkSequenceCompletion) + && endStream + && !nullPacketReceived) + { + // The last chunk data which type is any other MTP data type than MTP file type. + // It will not be commited until all the chunk data is received. + __FLOG(_L8("Commiting write data chunk")); + needCommit = iReceiveDataSink->CommitChunkL(iReceiveChunkData); + } + } + + // Fetch the next read data chunk. + switch (iState) + { + case EReceiveInProgress: + if (iReceiveDataCommit) // Commiting the received data is required + { + if (lastChunkCommited) + { + __FLOG(_L8("Fetching next write data chunk")); + iChunkStatus = iReceiveDataSink->NextWriteChunk(iReceiveChunkData, iDataLength - KUSBHeaderSize); + } + } + else + { + __FLOG(_L8("Fetching next write data chunk")); + iChunkStatus = iReceiveDataSink->NextWriteChunk(iReceiveChunkData, iDataLength - KUSBHeaderSize); + } + break; + + case EReceiveCompleting: + __FLOG(_L8("Write data chunk sequence completing")); + __FLOG_VA((_L8("Null packet received = %d"), nullPacketReceived)); + break; + + case EIdle: + default: + __FLOG(_L8("Invalid receive data stream state")); + Panic(EMTPUsbBadState); + break; + } + __FLOG_VA((_L8("Chunk status = %d"), iChunkStatus)); + + // Update the data stream state. + switch (iChunkStatus) + { + case KErrNone: + if (endStream) + { + __FLOG(_L8("Terminating packet received.")); + SetStreamState(EReceiveComplete); + } + else + { + // Full (intermediate) packet data received. + SetStreamState(EReceiveInProgress); + } + break; + + case KMTPChunkSequenceCompletion: + + if (endStream) + { + __FLOG(_L8("Terminating packet received.")); + SetStreamState(EReceiveComplete); + } + else + { + // Processing the last received data chunk. + // It will be processed once or mutiple times. + SetStreamState(EReceiveCompleting); + } + break; + + default: + User::Leave(iChunkStatus); + break; + } + + // If necessary, process the next chunk. + if (iState != EReceiveComplete) + { + __FLOG_VA((_L8("Issuing ReadUntilShort request on endpoint %d"), EndpointNumber())); + __FLOG_VA((_L8("Receive chunk capacity = %d bytes, length = %d bytes"), iReceiveChunkData.MaxLength(), iReceiveChunkData.Length())); + __FLOG_VA((_L8("iReceiveChunkData pointer address is %08x"), iReceiveChunkData.Ptr())); + // TDesC8's Right() method is not used here, because the parameter passed in like iReceiveChunkData.MaxLength() - iReceiveChunkData.Length()is greater than + // the length of the descriptor, the function extracts the whole of the descriptor. + if(iDataLength-iDataCounter < iReceiveChunkData.MaxLength() - iReceiveChunkData.Length()) + { + iReceiveData.Set(const_cast(iReceiveChunkData.Ptr() + iReceiveChunkData.Length()), 0, iDataLength - iDataCounter); + } + else + { + iReceiveData.Set(const_cast(iReceiveChunkData.Ptr() + iReceiveChunkData.Length()), 0, iReceiveChunkData.MaxLength() - iReceiveChunkData.Length()); + } + Connection().Ldd().ReadUntilShort(iStatus, EndpointNumber(), iReceiveData); + SetActive(); + if(needCommit != NULL) + { + TPtr8 tmp(NULL, 0, 0); + needCommit->CommitChunkL(tmp); + } + __FLOG(_L8("Request issued")); + } + + __FLOG_VA((_L8("CMTPUsbEpBase state on exit = 0x%08X"), iState)); + __FLOG(_L8("CMTPUsbEpBase::ResumeReceiveDataStreamL - Exit")); + } + +/** +Implements the send data streaming algorithm. This algorithm regulates the +sequence of data chunks making up the send data stream to ensure that data is +passed to the USB device interface in units of wMaxPacketSize integral length. +The algorithm attempts to avoid re-buffering unless absolutely necessary, as +follows: + 1. If the data chunk size is greater than or equal to the endpoint's + wMaxPacketSize, then the maximum wMaxPacketSize integral data portion + is sent directly. Any residual data is buffered in a wMaxPacketSize + packet buffer. + 2. If the data chunk size is less than the endpoint's wMaxPacketSize, then + the data is buffered in the packet buffer. As soon as the packet buffer + is filled it is sent. +@leave One of the system wide error codes, if a processing failure occurs. +*/ +void CMTPUsbEpBase::ProcessSendDataStreamL() + { + __FLOG(_L8("CMTPUsbEpBase::ProcessSendDataStreamL - Entry")); + __FLOG_VA((_L8("CMTPUsbEpBase state on entry = 0x%08X"), iState)); + + // Clear the send data stream data pointer. + iSendData.Set(KNullDesC8); + + TUint chunkAvailableLen(iSendChunkData.Length()); + if (!chunkAvailableLen) + { + // Fetch the next read data chunk. + switch (iState) + { + case ESendInitialising: + __FLOG(_L8("Fetching first read data chunk")); + iChunkStatus = iSendDataSource->FirstReadChunk(iSendChunkData); + iPacketBuffer.Zero(); + break; + + case ESendInProgress: + __FLOG(_L8("Fetching next read data chunk")); + iChunkStatus = iSendDataSource->NextReadChunk(iSendChunkData); + break; + + case ESendCompleting: + break; + + case EIdle: + default: + __FLOG(_L8("Invalid send data stream state")); + Panic(EMTPUsbBadState); + break; + } + + // Fetch the new chunk data size available. + chunkAvailableLen = iSendChunkData.Length(); + + // Update data stream state. + switch (iChunkStatus) + { + case KErrNone: + SetStreamState(ESendInProgress); + break; + + case KMTPChunkSequenceCompletion: + if (iState == ESendCompleting) + { + SetStreamState(ESendComplete); + } + else + { + SetStreamState(ESendCompleting); + } + break; + + default: + User::Leave(iChunkStatus); + break; + } + } + + __FLOG_VA((_L8("Chunk status = %d"), iChunkStatus)); + + // Process the buffered residual and/or available chunk data. + TUint bufferedLen(iPacketBuffer.Length()); + TUint chunkIntegralLen((chunkAvailableLen / iPacketSizeMax) * iPacketSizeMax); + TUint chunkResidualLen(chunkAvailableLen % iPacketSizeMax); + TBool zlp(EFalse); + __FLOG_VA((_L8("Buffered residual data = %u bytes"), bufferedLen)); + __FLOG_VA((_L8("Chunk data available = %u bytes"), chunkAvailableLen)); + __FLOG_VA((_L8("Chunk data packet integral portion = %u bytes"), chunkIntegralLen)); + __FLOG_VA((_L8("Chunk data packet residual portion = %u bytes"), chunkResidualLen)); + + if (bufferedLen) + { + // Data is buffered in the packet buffer. Fill the available packet buffer space. + if (chunkAvailableLen) + { + // Fill the packet buffer. + TUint consumedLen(0); + TUint unconsumedLen(0); + TUint capacity(iPacketBuffer.MaxLength() - iPacketBuffer.Length()); + if (chunkAvailableLen > capacity) + { + consumedLen = capacity; + unconsumedLen = (chunkAvailableLen - consumedLen); + } + else + { + consumedLen = chunkAvailableLen; + } + __FLOG_VA((_L8("Buffering %u bytes"), consumedLen)); + iPacketBuffer.Append(iSendChunkData.Left(consumedLen)); + + // Update the available chunk data to reflect only the unconsumed portion. + __FLOG_VA((_L8("Residual chunk data = %u bytes"), unconsumedLen)); + if (unconsumedLen) + { + iSendChunkData.Set(iSendChunkData.Right(unconsumedLen)); + } + else + { + iSendChunkData.Set(NULL, 0); + } + } + + // Send the packet buffer when full. + if ((iState == ESendCompleting) || (iPacketBuffer.Size() == iPacketBuffer.MaxSize())) + { + iSendData.Set(iPacketBuffer); + iPacketBuffer.Zero(); + } + + // Set the end of stream flag. + zlp = ((iState == ESendCompleting) && (iSendChunkData.Length() == 0)); + } + else if (iState == ESendInProgress) + { + // Send the chunk data packet integral portion. + if (chunkIntegralLen) + { + iSendData.Set(iSendChunkData.Left(chunkIntegralLen)); + } + + // Buffer the chunk data packet residual portion. + if (chunkResidualLen) + { + __FLOG_VA((_L8("Buffering %u bytes"), chunkResidualLen)); + iPacketBuffer.Append(iSendChunkData.Right(chunkResidualLen)); + } + + // All data has been consumed and/or buffered. + iSendChunkData.Set(NULL, 0); + } + else if (iState == ESendCompleting) + { + // Send all available chunk data. + iSendData.Set(iSendChunkData); + zlp = ETrue; + + // All data has been consumed. + iSendChunkData.Set(NULL, 0); + } + + // Send the available data or reschedule to process the next chunk. + TUint sendBytes(iSendData.Length()); + if ( sendBytes||zlp ) + { + __FLOG_VA((_L8("Issuing Write request on endpoint %d, Zlp = %d"), EndpointNumber(), zlp)); + __FLOG_VA((_L8("Send data length = %d bytes"), iSendData.Length())); + Connection().Ldd().Write(iStatus, EndpointNumber(), iSendData, sendBytes, zlp); + SetActive(); + __FLOG(_L8("Request issued")); + } + else if (iState != ESendComplete) + { + iStatus = KRequestPending; + TRequestStatus* status = &iStatus; + SetActive(); + User::RequestComplete(status, KErrNone); + } + + __FLOG_VA((_L8("CMTPUsbEpBase state on exit = 0x%08X"), iState)); + __FLOG(_L8("CMTPUsbEpBase::ProcessSendDataStreamL - Exit")); + } + +/** +Sets the data stream state variable. +@param aState The new data stream state. +*/ +void CMTPUsbEpBase::SetStreamState(TInt aState) + { + __FLOG(_L8("SetStreamState - Entry")); + iState = aState; + __FLOG_VA((_L8("Stream state set to 0x%08X"), iState)); + __FLOG(_L8("SetStreamState - Exit")); + } + +// Fix so that cancelling works. +/* + * Flush USB driver received data + * + */ +const TInt KFlushBufferMaxLen = 50*1024; // 50K bytes +#define INTERVAL_FOR_READ_TRASH_DATA (1000*50) // 50 Miliseconds +#define INTERVAL_FOR_FLUSH_TRASH_DATA (9*INTERVAL_FOR_READ_TRASH_DATA) // 450 Miliseconds +// if there is no data read in flushRxData, wait for 1.5 second at most in case forever waiting +#define INTERVAL_FOR_FLUSH_TRASH_DATA_IF_NO_DATA_READ (30*INTERVAL_FOR_READ_TRASH_DATA) //1.5 SECOND + +void CMTPUsbEpBase::FlushRxDataL() + { + + // create the read buff + RBuf8 readBuf; + readBuf.CreateL(KFlushBufferMaxLen); + + TUint32 uRestTimeToWait = INTERVAL_FOR_FLUSH_TRASH_DATA_IF_NO_DATA_READ; + + do{ + + // get the data size in the receive buffer ready to read + TInt nbytes = 0; + TInt err = Connection().Ldd().QueryReceiveBuffer(EndpointNumber(), nbytes); +#ifdef _DEBUG + RDebug::Print(_L("FlushRxDataL()--1---err is %d , nbytes is %d"), err, nbytes); +#endif + + // has data, read it + if( (err == KErrNone) && (nbytes > 0) ) + { + + // synchronously read the data + TRequestStatus status; + Connection().Ldd().ReadOneOrMore(status, EndpointNumber(), readBuf); + User::WaitForRequest(status); + + if(status.Int() != KErrNone) break; + + // whenever some data read, reset the rest wait time . + uRestTimeToWait = INTERVAL_FOR_FLUSH_TRASH_DATA; + +#ifdef _DEBUG + RDebug::Print(_L("FlushRxDataL()---Reset the rest wait time")); +#endif + } + else + { + // wait for the data from the usb channel. + User::After(INTERVAL_FOR_READ_TRASH_DATA); + // reduce the rest time to wait + uRestTimeToWait -= INTERVAL_FOR_READ_TRASH_DATA ; + } + +#ifdef _DEBUG + RDebug::Print(_L("FlushRxDataL()---uRestTimeToWait is %d"), uRestTimeToWait); +#endif + + }while( uRestTimeToWait > 0); + + readBuf.Close(); + +}