// Copyright (c) 2000-2010 Nokia Corporation and/or its subsidiary(-ies).
// All rights reserved.
// This component and the accompanying materials are made available
// under the terms of the License "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:
// e32test/usb/t_usb_device/src/activerw.cpp
// USB Test Program T_USB_DEVICE, functional part.
// Device-side part, to work against T_USB_HOST running on the host.
// 
//

#include "general.h"									// CActiveControl, CActiveRW
#include "config.h"
#include "activerw.h"
#include "activetimer.h" 
#include "usblib.h"										// Helpers

_LIT(KFileName, "\\T_USBFILE.BIN");

extern RTest test;
extern TBool gVerbose;
extern TBool gSkip;
extern TBool gStopOnFail;
extern TBool gAltSettingOnNotify;
extern TInt8 gSettingNumber [128];
extern TInt gSoakCount;
extern CActiveRW* gRW[KMaxConcurrentTests];				// the USB read/write active object
extern IFConfigPtr gInterfaceConfig [128] [KMaxInterfaceSettings];
extern TInt gActiveTestCount;

static TInt gYieldRepeat = 0;
static const TInt KYieldRepeat = 100;

//
// --- class CActiveRW ---------------------------------------------------------
//

CActiveRW::CActiveRW(CConsoleBase* aConsole, RDEVCLIENT* aPort, RFs aFs, TUint16 aIndex, TBool aLastSetting)
	: CActive(EPriorityNormal),
		#ifndef USB_SC
		iWriteBuf((TUint8 *)NULL,0,0),		// temporary initialisation
		iReadBuf((TUint8 *)NULL,0,0),		// temporary initialisation	
	  	#endif
	  iConsole(aConsole),
	  iPort(aPort),
	  iBufSz(0),
	  iMaxPktSz(0),
	  iCurrentXfer(ETxferNone),
	  iXferMode(::ENone),
	  iDoStop(EFalse),
	  iPktNum(0),
	  iFs(aFs),
	  iRepeat(0),
	  iComplete(EFalse),
	  iResult(EFalse),
	  iIndex (aIndex),
	  iLastSetting (aLastSetting)
	{
	gActiveTestCount++;
	TUSB_VERBOSE_PRINT("CActiveRW::CActiveRW()");
	}


CActiveRW* CActiveRW::NewL(CConsoleBase* aConsole, RDEVCLIENT* aPort, RFs aFs, TUint16 aIndex, TBool aLastSetting)
	{

	CActiveRW* self = new (ELeave) CActiveRW(aConsole, aPort, aFs, aIndex, aLastSetting);
	CleanupStack::PushL(self);
	self->ConstructL();
	CActiveScheduler::Add(self);
	CleanupStack::Pop();									// self
	return self;
	}


void CActiveRW::ConstructL()
	{
	TUSB_VERBOSE_PRINT("CActiveRW::ConstructL()");

	// Create read timeout timer active object (but don't activate it yet)
	iTimeoutTimer = CActiveTimer::NewL(iConsole, iPort);
	if (!iTimeoutTimer)
		{
		TUSB_PRINT("Failed to create timeout timer");
		}
	}


CActiveRW::~CActiveRW()
	{
	TUSB_VERBOSE_PRINT("CActiveRW::~CActiveRW()");
	Cancel();												// base class
	delete iTimeoutTimer;
	#ifdef USB_SC
	if ((TENDPOINTNUMBER)iTestParams.outPipe <= KMaxEndpointsPerClient)
		{
		iSCReadBuf.Close();
		}
	if ((TENDPOINTNUMBER)iTestParams.inPipe <= KMaxEndpointsPerClient)
		{
		iSCWriteBuf.Close();
		}
	#else
	User::Free((TAny *)iReadBuf.Ptr());
	User::Free((TAny *)iWriteBuf.Ptr());
	#endif
	iFile.Close();
	gRW[iIndex] = NULL;
	gActiveTestCount--;
	}


void CActiveRW::SetTestParams(TestParamPtr aTpPtr)
	{
	iBufSz = aTpPtr->minSize;
	iPktNum = aTpPtr->packetNumber;
	
	iTestParams = *aTpPtr;

	gYieldRepeat = ((iTestParams.settingRepeat != 0) || (iIndex == 0))? 0 : KYieldRepeat;

	if ((TENDPOINTNUMBER)iTestParams.outPipe <= KMaxEndpointsPerClient)
		{
		#ifndef USB_SC
		TAny * newBuffer = User::Alloc(aTpPtr->maxSize);
		if (newBuffer == NULL)
			{
			TUSB_PRINT ("Failure to allocate heap memory");
			test(EFalse);
			}
		iReadBuf.Set((TUint8 *)newBuffer,0,(TInt)aTpPtr->maxSize);
		#endif
		TBuf8<KUsbDescSize_Endpoint> descriptor;
		TUSB_VERBOSE_PRINT2 ("GetEndpointDescriptor Alt Setting %d Endpoint %d",iTestParams.alternateSetting, iTestParams.outPipe);
		TInt r = iPort->GetEndpointDescriptor(iTestParams.alternateSetting, (TENDPOINTNUMBER)iTestParams.outPipe, descriptor);
		if ((TUint)r != iReadSize)
			{
			TUSB_PRINT("Failed to get endpoint descriptor");
			test(EFalse);
			return;
			}

		iMaxPktSz = EpSize(descriptor[KEpDesc_PacketSizeOffset],descriptor[KEpDesc_PacketSizeOffset+1]);		
		TUSB_VERBOSE_PRINT5 ("Out Endpoint 0x%x attributes 0x%x interface %d setting %d max packet size %d",
			descriptor[KEpDesc_AddressOffset],descriptor[KEpDesc_AttributesOffset],iTestParams.interfaceNumber,iTestParams.alternateSetting,iMaxPktSz);
		if (!gSkip && iMaxPktSz != (TUint)gInterfaceConfig[iTestParams.interfaceNumber][iTestParams.alternateSetting]->iInfoPtr->iEndpointData[iTestParams.outPipe-1].iSize)
			{
			TUSB_PRINT4("Error - Interface %d Setting %d Endpoint %d Max Packet Size %d",iTestParams.interfaceNumber,iTestParams.alternateSetting,iTestParams.outPipe,iMaxPktSz);
			test(EFalse);
			return;
			}
		}
	if ((TENDPOINTNUMBER)iTestParams.inPipe <= KMaxEndpointsPerClient)
		{
		#ifndef USB_SC
		TAny * newBuffer = User::Alloc(aTpPtr->maxSize);
		if (newBuffer == NULL)
			{
			TUSB_PRINT ("Failure to allocate heap memory");
			test(EFalse);
			}
		iWriteBuf.Set((TUint8 *)newBuffer,0,(TInt)aTpPtr->maxSize);
		#endif
		TBuf8<KUsbDescSize_Endpoint> descriptor;
		TUSB_VERBOSE_PRINT2 ("GetEndpointDescriptor Alt Setting %d Endpoint %d",iTestParams.alternateSetting, iTestParams.inPipe);
		TInt r = iPort->GetEndpointDescriptor(iTestParams.alternateSetting, (TENDPOINTNUMBER)iTestParams.inPipe, descriptor);
		if (r != KErrNone)
			{
			TUSB_PRINT("Failed to get endpoint descriptor");
			test(EFalse);
			return;
			}

		TInt maxPktSz = EpSize(descriptor[KEpDesc_PacketSizeOffset],descriptor[KEpDesc_PacketSizeOffset+1]);		
		TUSB_VERBOSE_PRINT5 ("In Endpoint 0x%x attributes 0x%x interface %d setting %d max packet size %d",
			descriptor[KEpDesc_AddressOffset],descriptor[KEpDesc_AttributesOffset],iTestParams.interfaceNumber,iTestParams.alternateSetting,maxPktSz);
		if (!gSkip && maxPktSz != gInterfaceConfig[iTestParams.interfaceNumber][iTestParams.alternateSetting]->iInfoPtr->iEndpointData[iTestParams.inPipe-1].iSize)
			{
			TUSB_PRINT4("Error - Interface %d Setting %d Endpoint %d Max Packet Size %d",iTestParams.interfaceNumber,iTestParams.alternateSetting,iTestParams.inPipe,maxPktSz);
			test(EFalse);
			return;
			}
		}
		
	}


void CActiveRW::SetTransferMode(TXferMode aMode)
	{
	iXferMode = aMode;
	if (aMode == EReceiveOnly || aMode == ETransmitOnly)
		{
		// For streaming transfers we do this only once.
		iBufSz = iTestParams.maxSize;
		}
	}

void CActiveRW::Suspend(TXferType aType)
	{
	if (aType == ESuspend)
		TUSB_VERBOSE_PRINT1("Index %d Suspend",iIndex);
	if (aType == EAltSetting)
		TUSB_VERBOSE_PRINT3("Index %d Suspend for Alternate Setting - interface %d setting %d",iIndex,iTestParams.interfaceNumber,iTestParams.alternateSetting);	
	iStatus = KRequestPending;
	iCurrentXfer = aType;
	if (!IsActive())
		{
		SetActive();
		}
	}

void CActiveRW::Resume()
	{	
	TUSB_VERBOSE_PRINT3("Index %d Resume interface %d setting %d",iIndex,iTestParams.interfaceNumber,iTestParams.alternateSetting);
	TRequestStatus* status = &iStatus;
	User::RequestComplete(status,KErrNone);
	if (!IsActive())
		{
		SetActive();
		}
	}

void CActiveRW::Yield()
	{
	TUSB_VERBOSE_PRINT1("Index %d Scheduler Yield",iIndex);
	// removes the active object from the scheduler queue then adds it back in again
	Deque();	
	CActiveScheduler::Add(this);
	}

void CActiveRW::ResumeAltSetting(TUint aAltSetting)
	{
	if (iCurrentXfer == EAltSetting && iTestParams.alternateSetting == aAltSetting)
		{
		Resume();
		}
	}

void CActiveRW::StartOrSuspend()
	{
	TInt altSetting;
	
	iPort->GetAlternateSetting (altSetting);
	if (iTestParams.alternateSetting != altSetting)
		{
		Suspend(EAltSetting);
		}
	else
		{
		#ifdef USB_SC
		TInt r;
		if (iTestParams.alternateSetting != gSettingNumber[iTestParams.interfaceNumber])
			{
			gSettingNumber[iTestParams.interfaceNumber] = iTestParams.alternateSetting;
			r = iPort->StartNextOutAlternateSetting(ETrue);
			TUSB_VERBOSE_PRINT1("StartNextOutAlternateSetting retValue %d",r);
			test_Value(r, (r >= KErrNone) || (r == KErrNotReady)   || (r == KErrGeneral));
			}
		TUSB_VERBOSE_PRINT4 ("CActiveRW::StartOrSuspend() interface %d setting %d Out %d In %d",iTestParams.interfaceNumber,iTestParams.alternateSetting,iTestParams.outPipe,iTestParams.inPipe);
		if ((TENDPOINTNUMBER)iTestParams.outPipe <= KMaxEndpointsPerClient)
			{
			r = iPort->OpenEndpoint(iSCReadBuf,iTestParams.outPipe);
			test_KErrNone(r);
			}
		if ((TENDPOINTNUMBER)iTestParams.inPipe <= KMaxEndpointsPerClient)
			{
			r = iPort->OpenEndpoint(iSCWriteBuf,iTestParams.inPipe);
			test_KErrNone(r);
			}
		#endif
		if (iXferMode == EReceiveOnly)
			{
			// read data and process any available
			iReadSize = ReadData();
			if (iReadSize != 0)
				{
				ProcessReadXfer();
				}
			}
		else
			{
			SendData();										// or we send data
			if (iXferMode == ETransmitOnly)
				{
				iPktNum++;				
				iRepeat++;		
				}
			}	
		}
	}

void CActiveRW::RunL()
	{
	#ifdef USB_SC
	TInt r = 0;
	#else
	TInt altSetting;	
	#endif
	
	TUSB_VERBOSE_PRINT("CActiveRW::RunL()");

	if ((iStatus != KErrNone) && (iStatus != KErrEof))
		{
		TUSB_PRINT1("Error %d in RunL", iStatus.Int());
		}
	if (iDoStop)
		{
		TUSB_PRINT("Stopped");
		iDoStop = EFalse;
		return;
		}
	switch (iCurrentXfer)
		{
	case EWaitSetting:
		#ifdef USB_SC
		if ((TENDPOINTNUMBER)iTestParams.outPipe <= KMaxEndpointsPerClient)
			{
			r = iSCReadBuf.Close();
			test_KErrNone(r);
			}
		if ((TENDPOINTNUMBER)iTestParams.inPipe <= KMaxEndpointsPerClient)
			{
			r = iSCWriteBuf.Close();
			test_KErrNone(r);
			}
		#endif
		if (iTestParams.settingRepeat  && ((iRepeat < iTestParams.repeat) || !iLastSetting))
			{
			gRW[iTestParams.afterIndex]->Resume();			
			}
		Suspend(ESuspend);	
		break;
			
	case ESuspend:
		#ifdef USB_SC
		TUSB_VERBOSE_PRINT3("Index %d Resumed interface %d setting test=%d",iIndex,iTestParams.interfaceNumber,iTestParams.alternateSetting);	
		if (iTestParams.alternateSetting != gSettingNumber[iTestParams.interfaceNumber])
			{
			r = iPort->StartNextOutAlternateSetting(ETrue);
			TUSB_VERBOSE_PRINT1("StartNextOutAlternateSetting retValue %d",r);
			test_Value(r, (r >= KErrNone) || (r == KErrNotReady)  || (r == KErrGeneral));
			if (r != KErrNotReady)
				{
				gSettingNumber[iTestParams.interfaceNumber] = r;
				}
			if (iTestParams.alternateSetting != gSettingNumber[iTestParams.interfaceNumber])
				{
				Suspend(EAltSetting);
				break;
				}
			}
		#else
		iPort->GetAlternateSetting (altSetting);
		TUSB_VERBOSE_PRINT4("Index %d Resumed interface %d setting test=%d actual=%d",iIndex,iTestParams.interfaceNumber,iTestParams.alternateSetting,altSetting);	
		if (gAltSettingOnNotify)
			{
			if (iTestParams.alternateSetting != altSetting)
				{
				Suspend(EAltSetting);
				break;
				}
			}
		#endif
			
		// If alternate setting is ok drops through to EAltSetting to start next read or write
		iCurrentXfer = EAltSetting;
			
	case EAltSetting:
		#ifdef USB_SC
		if (iTestParams.alternateSetting != gSettingNumber[iTestParams.interfaceNumber])
			{
			r = iPort->StartNextOutAlternateSetting(ETrue);
			TUSB_VERBOSE_PRINT1("StartNextOutAlternateSetting retValue %d",r);
			test_Value(r, (r >= KErrNone) || (r == KErrNotReady)   || (r == KErrGeneral));
			if (r != KErrNotReady)
				{
				gSettingNumber[iTestParams.interfaceNumber] = r;
				}
			if (iTestParams.alternateSetting != gSettingNumber[iTestParams.interfaceNumber])
				{
				Suspend(EAltSetting);
				break;
				}
			}
		TUSB_VERBOSE_PRINT4 ("CActiveRW::Runl() EAltSetting interface %d setting %d Out %d In %d",iTestParams.interfaceNumber,iTestParams.alternateSetting,iTestParams.outPipe,iTestParams.inPipe);
		if ((TENDPOINTNUMBER)iTestParams.outPipe <= KMaxEndpointsPerClient)
			{
			r = iPort->OpenEndpoint(iSCReadBuf,iTestParams.outPipe);
			test_KErrNone(r);
			}
		if ((TENDPOINTNUMBER)iTestParams.inPipe <= KMaxEndpointsPerClient)
			{
			r = iPort->OpenEndpoint(iSCWriteBuf,iTestParams.inPipe);
			test_KErrNone(r);
			}
		#endif
		if (iXferMode == EReceiveOnly)
			{
			// read data and process any available
			iReadSize = ReadData();
			if (iReadSize != 0)
				{
				ProcessReadXfer();
				}		
			}
		else
			{
			SendData();										// or we send data
			if (iXferMode == ETransmitOnly)
				{
				iPktNum++;				
				iRepeat++;		
				}
			}
		break;

	case EWriteXfer:
		ProcessWriteXfer();
		break;
		
	case EReadXfer:
		#ifdef USB_SC
		iReadSize = ReadData();
		if (iReadSize != 0)
			{
			ProcessReadXfer();
			}
		#else
		iReadSize = iReadBuf.Length();
		ProcessReadXfer();
		#endif
		break;
		
	default:
		TUSB_PRINT("Oops. (Shouldn't end up here...)");
		break;
		}
	return;
	}

void CActiveRW::ProcessWriteXfer()
	{
	if (iXferMode == ETransmitOnly)
		{
		if (iTestParams.settingRepeat && iRepeat)
			{	
			if (((iRepeat % iTestParams.settingRepeat) == 0) || (iRepeat >= iTestParams.repeat))
				{
				if ((iRepeat < iTestParams.repeat) || !iLastSetting)
					{
					#ifdef USB_SC
					if ((TENDPOINTNUMBER)iTestParams.inPipe <= KMaxEndpointsPerClient)
						{
						test_KErrNone(iSCWriteBuf.Close());
						}
					#endif
					gRW[iTestParams.afterIndex]->Resume();			
					}
				if (iRepeat < iTestParams.repeat)
					{
					Suspend(ESuspend);	
					return;					
					}
				}
			}

		if ((iTestParams.repeat == 0) || (iRepeat < iTestParams.repeat))
			{
			// Yield the scheduler to ensure other activeObjects can run
			if (iRepeat && gYieldRepeat)
				{
				if ((iRepeat % gYieldRepeat) == 0)
					{
					Yield();
					}
				}
			SendData();							// next we send data			
			iPktNum++;				
			iRepeat++;
			}
		else
			{
			TestComplete(ETrue);
			}
		}
	else
		{
		// read data and process any available
		iReadSize = ReadData();
		if (iReadSize != 0)
			{
			ProcessReadXfer();
			}
		}

	return;
	}

void CActiveRW::ProcessReadXfer()
	{
	if ((iReadOffset + iReadSize) > iBufSz)
		{
		TUSB_PRINT2("*** rcv'd too much data: 0x%x (expected: 0x%x)", iReadOffset + iReadSize, iBufSz);
		test(EFalse);
		}

	if (iXferMode == EReceiveOnly)
		{
		if (iReadOffset == 0)
			{
			#ifdef USB_SC
			const TUint32 num = *reinterpret_cast<const TUint32*>(iSCReadData);
			#else
			const TUint32 num = *reinterpret_cast<const TUint32*>(iReadBuf.Ptr());
			#endif	
			if (num != iPktNum)
				{
				TUSB_PRINT3("*** Repeat %d rcv'd wrong pkt number: 0x%x (expected: 0x%x)", iRepeat, num, iPktNum);
				iPktNum = num;
				test(EFalse);
				}
			}
		if (iDiskAccessEnabled)
			{
			// Write out to disk previous completed Read
			#ifdef USB_SC
			TPtr8 readBuf((TUint8 *)iSCReadData,iReadSize,iReadSize);
			WriteBufferToDisk(readBuf, iReadSize);
			#else	
			TUSB_VERBOSE_PRINT2("Max Buffer Size = %d (iReadBuf.Size(): %d)", iTestParams.maxSize, iReadBuf.Size());
			WriteBufferToDisk(iReadBuf, iTestParams.maxSize);
			#endif
			}		
		iReadOffset += iReadSize;
		if (iReadOffset >= iBufSz)
			{
			iReadOffset = 0;			
			}
		else
			{
			#ifdef USB_SC
			iReadSize = ReadData();
			if (iReadSize)
				{
				ProcessReadXfer();
				}
			#endif
			return;
			}
		iPktNum++;
		iRepeat++;
		iReadSize = 0;
		if (iTestParams.settingRepeat)
			{	
			if (((iRepeat % iTestParams.settingRepeat) == 0) || (iRepeat >= iTestParams.repeat))
				{
				#ifdef USB_SC
				if ((TENDPOINTNUMBER)iTestParams.outPipe <= KMaxEndpointsPerClient)
					{
					test_KErrNone(iSCReadBuf.Close());
					}
				#endif
				if ((iRepeat < iTestParams.repeat) || !iLastSetting)
					{
					gRW[iTestParams.afterIndex]->Resume();			
					}
				if (iRepeat < iTestParams.repeat)
					{
					Suspend(ESuspend);	
					return;					
					}
				}
			}
		if ((iTestParams.repeat == 0) || (iRepeat < iTestParams.repeat))
			{
			// Yield the scheduler to ensure other activeObjects can run
			if (iRepeat && gYieldRepeat)
				{
				if ((iRepeat % gYieldRepeat) == 0)
					{
					Yield();
					}
				}
			#ifdef USB_SC
			TRequestStatus* status = &iStatus;
			User::RequestComplete(status,KErrNone);
			if (!IsActive())
				{
				SetActive();
				}	
			#else
			iReadSize = ReadData();			
			#endif
			}
		else
			{
			TestComplete(ETrue);
			}
		}
	else
		{
		if (iXferMode == ELoopComp)
			{
			test(CompareBuffers());
			}
		else if (iBufSz > 3)
			{
			if (iReadOffset == 0)
				{
				#ifdef USB_SC
				const TUint32 num = *reinterpret_cast<const TUint32*>(iSCReadData);
				#else
				const TUint32 num = *reinterpret_cast<const TUint32*>(iReadBuf.Ptr());
				#endif
				if (num != iPktNum)
					{
					TUSB_PRINT2("*** rcv'd wrong pkt number: 0x%x (expected: 0x%x)", num, iPktNum);
					}
				}
			}
		iReadOffset += iReadSize;
		if (iReadOffset >= iBufSz)
			{
			iReadOffset = 0;			
			}
		else
			{
			iReadSize = ReadData();
			if (iReadSize)
				{
				ProcessReadXfer();
				}
			return;
			}
		if ((TUint)iBufSz == iTestParams.maxSize)
			{
			iBufSz = iTestParams.minSize;
			}
		else
			{
			iBufSz++;
			}
		iPktNum++;
		iRepeat++;
		iReadSize = 0;
		if (iTestParams.settingRepeat)
			{	
			if (((iRepeat % iTestParams.settingRepeat) == 0) || (iRepeat >= iTestParams.repeat))
				{
				if (iRepeat < iTestParams.repeat)
					{
					SendWaitSetting();
					return;					
					}
				else
					{
					#ifdef USB_SC
					if ((TENDPOINTNUMBER)iTestParams.outPipe <= KMaxEndpointsPerClient)
						{
						test_KErrNone(iSCReadBuf.Close());
						}
					if ((TENDPOINTNUMBER)iTestParams.inPipe <= KMaxEndpointsPerClient)
						{
						test_KErrNone(iSCWriteBuf.Close());
						}
					#endif	
					if (!iLastSetting)
						{
						gRW[iTestParams.afterIndex]->Resume();			
						}
					}
				}
			}
			
		if ((iTestParams.repeat == 0) || (iRepeat < iTestParams.repeat))
			{
			// Yield the scheduler to ensure other activeObjects can run
			if (iRepeat && gYieldRepeat)
				{
				if ((iRepeat % gYieldRepeat) == 0)
					{
					Yield();
					}
				}
			SendData();				
			}
		else
			{
			TestComplete(ETrue);
			}	
		}

	return;
	}

void CActiveRW::SendWaitSetting()
	{
	__ASSERT_ALWAYS(!IsActive(), User::Panic(KActivePanic, 662));
	#ifdef	USB_SC
	TAny* inBuffer;
	TUint inBufLength;
	test_KErrNone(iSCWriteBuf.GetInBufferRange(inBuffer, inBufLength));
	test_KErrNone(iSCWriteBuf.WriteBuffer(inBuffer,KWaitSettingSize,FALSE,iStatus));
	#else
	iWriteBuf.SetLength(KWaitSettingSize);
	iPort->Write(iStatus, (TENDPOINTNUMBER)iTestParams.inPipe, iWriteBuf, KWaitSettingSize);
	#endif
	iCurrentXfer = EWaitSetting;
	if (!IsActive())
		{
		SetActive();
		}
	}

	
void CActiveRW::SendData()
	{
	__ASSERT_ALWAYS(!IsActive(), User::Panic(KActivePanic, 663));
	#ifdef	USB_SC
	TUint8* inBuffer;
	TUint inBufLength;
	test_KErrNone(iSCWriteBuf.GetInBufferRange((TAny*&)inBuffer, inBufLength));
	if (iDiskAccessEnabled)
		{
		TPtr8 writeBuf((TUint8 *)inBuffer,iBufSz,iBufSz);
		ReadBufferFromDisk(writeBuf, iBufSz);
		}
	if (iBufSz > 3)
		*reinterpret_cast<TUint32*>(inBuffer) = iPktNum;
	
	if (iXferMode == ELoopComp)
		{
		for (TUint i = 4; i < iBufSz; i++)
			{
			*(inBuffer+i) = static_cast<TUint8>((iPktNum+i) & 0x000000ff);
			}
		}
	TUSB_VERBOSE_PRINT3("SendData interface %d setting %d endpoint %d",iTestParams.interfaceNumber,iTestParams.alternateSetting,iTestParams.inPipe);
	iCurrentXfer = EWriteXfer;
	TInt r = iSCWriteBuf.WriteBuffer(inBuffer, iBufSz, FALSE, iStatus);
	test_KErrNone(r);
	#else
	if (iDiskAccessEnabled)
		{
		ReadBufferFromDisk(iWriteBuf, iBufSz);
		}
	iWriteBuf.SetLength(iBufSz);
	if (iBufSz > 3)
		*reinterpret_cast<TUint32*>(const_cast<TUint8*>(iWriteBuf.Ptr())) = iPktNum;
	if (iXferMode == ELoopComp)
		{
		for (TUint i = 4; i < iBufSz; i++)
			{
			iWriteBuf[i] = static_cast<TUint8>((iPktNum+i) & 0x000000ff);
			}
		}

	TUSB_VERBOSE_PRINT3("SendData interface %d setting %d endpoint %d",iTestParams.interfaceNumber,iTestParams.alternateSetting,iTestParams.inPipe);
	iCurrentXfer = EWriteXfer;
	iPort->Write(iStatus, (TENDPOINTNUMBER)iTestParams.inPipe, iWriteBuf, iBufSz);
	#endif
	if (!IsActive())
		{
		SetActive();
		}
	}

TInt CActiveRW::WriteToDisk(TChar aDriveLetter)
	{
	iDiskAccessEnabled = ETrue;
	TInt r = KErrNone;

	iFileName.Format(_L("%c:"), aDriveLetter.operator TUint());
	iFileName.Append(KFileName);
	TUSB_PRINT1("\nFilename = %S", &iFileName);

	// open the record file
	r = iFile.Replace(iFs, iFileName, EFileWrite);
	iFileOffset = 0;
	if (r != KErrNone)
		{
		TUSB_PRINT1("RFile::Replace() returned %d", r);
		iDiskAccessEnabled = EFalse;
		return r;
		}
		
	return r;
	}


TInt CActiveRW::ReadFromDisk(TChar aDriveLetter, TInt aMaxFileSize)
	{
	iDiskAccessEnabled = ETrue;
	TInt r = KErrNone;

	iFileName.Format(_L("%c:"), aDriveLetter.operator TUint());
	iFileName.Append(KFileName);
	TUSB_PRINT1("\nFilename = %S", &iFileName);
	TUSB_PRINT1("File size: %d", aMaxFileSize);

	// First create the file & fill it
	r = iFile.Replace(iFs, iFileName, EFileWrite);
	if (r != KErrNone)
		{
		TUSB_PRINT1("RFile::Replace() returned %d", r);
		iDiskAccessEnabled = EFalse;
		return r;
		}
	const TInt KBufferSize = 4 * 1024;
	TBuf8<KBufferSize> buffer;
	buffer.SetLength(KBufferSize);
	for (TInt n = 0; n < KBufferSize; n++)
		{
		buffer[n] = static_cast<TUint8>(n & 0x000000ff);
		}
	TUSB_PRINT("Writing data to file (this may take some minutes...)");
	for (TInt n = 0; n < aMaxFileSize; n += KBufferSize)
		{
		r = iFile.Write(buffer, KBufferSize);
		if (r != KErrNone)
			{
			TUSB_PRINT1("RFile::Write() returned %d (disk full?)", r);
			iFile.Close();
			iDiskAccessEnabled = EFalse;
			return r;
			}
		}
	TUSB_PRINT("Done.");
	iFile.Close();
	// Now open the file for reading
	r = iFile.Open(iFs, iFileName, EFileRead);
	if (r != KErrNone)
		{
		TUSB_PRINT1("RFile::Open() returned %d", r);
		iDiskAccessEnabled = EFalse;
		return r;
		}
	iFileOffset = 0;

	return r;
	}


void CActiveRW::WriteBufferToDisk(TDes8& aBuffer, TInt aLen)
	{
	TUSB_VERBOSE_PRINT1("CActiveRW::WriteBufferToDisk(), len = %d", aLen);
	TInt r = iFile.Write(aBuffer, aLen);
	if (r != KErrNone)
		{
		TUSB_PRINT2("Error writing to %S (%d)", &iFileName, r);
		iDiskAccessEnabled = EFalse;
		return;
		}
	iFileOffset += aLen;
	}


void CActiveRW::ReadBufferFromDisk(TDes8& aBuffer, TInt aLen)
	{
	const TInt r = iFile.Read(aBuffer, aLen);
	if (r != KErrNone)
		{
		TUSB_PRINT2("Error reading from %S (%d)", &iFileName, r);
		iDiskAccessEnabled = EFalse;
		return;
		}
	TInt readLen = aBuffer.Length();
	TUSB_VERBOSE_PRINT1("CActiveRW::ReadBufferFromDisk(), len = %d\n", readLen);
	if (readLen < aLen)
		{
		TUSB_PRINT3("Only %d bytes of %d read from file %S)",
					readLen, aLen, &iFileName);
		iDiskAccessEnabled = EFalse;
		return;
		}
	iFileOffset += aLen;
	}


TUint CActiveRW::ReadData()
	{
	__ASSERT_ALWAYS(!IsActive(), User::Panic(KActivePanic, 664));
	iCurrentXfer = EReadXfer;
	#ifdef	USB_SC
	TUint readSize = 0;			// note that this returns zero when asynchronous read is pending 
	TInt r = 0;
	do
		{
		r = iSCReadBuf.GetBuffer (iSCReadData,readSize,iReadZlp,iStatus);
		test_Value(r, (r == KErrNone) || (r == KErrCompletion) || (r == KErrEof));
		TUSB_VERBOSE_PRINT4("Get Buffer Return code %d Status %d DataPtr 0x%x Size %d", r, iStatus.Int(),iSCReadData,readSize);	
		}
	while ((r == KErrCompletion && readSize == 0) || (r == KErrEof));
	if (r == KErrCompletion)
		{
		return readSize;
		}
	else
		{
		if (!IsActive())
			{
			SetActive();
			}
		return 0;
		}
	#else
	iReadBuf.SetLength (0);
	if (iBufSz <= iMaxPktSz)
		{
		// Testing the packet version of Read()
		TUSB_VERBOSE_PRINT3("ReadData (single packet) interface %d setting %d endpoint %d",iTestParams.interfaceNumber,iTestParams.alternateSetting,iTestParams.outPipe);
		iPort->ReadPacket(iStatus, (TENDPOINTNUMBER)iTestParams.outPipe, iReadBuf, iBufSz);
		}
	else if ((TUint)iBufSz == iTestParams.maxSize)
		{
		// Testing the two-parameter version
		TUSB_VERBOSE_PRINT3("ReadData (w/o length) interface %d setting %d endpoint %d",iTestParams.interfaceNumber,iTestParams.alternateSetting,iTestParams.outPipe);
		iPort->Read(iStatus, (TENDPOINTNUMBER)iTestParams.outPipe, iReadBuf);
		}
	else
		{
		// otherwise, we use the universal default version
		// Testing the three-parameter version
		TUSB_VERBOSE_PRINT3("ReadData (normal) interface %d setting %d endpoint %d",iTestParams.interfaceNumber,iTestParams.alternateSetting,iTestParams.outPipe);
		iPort->Read(iStatus, (TENDPOINTNUMBER)iTestParams.outPipe, iReadBuf, iBufSz);
		}
	if (!IsActive())
		{
		SetActive();
		}
	return 0;
	#endif
	}


void CActiveRW::Stop()
	{
	if (!IsActive())
		{
		TUSB_PRINT("CActiveRW::Stop(): Not active");
		return;
		}
	TUSB_PRINT("Cancelling outstanding transfer requests\n");
	iBufSz = 0;
	iPktNum = 0;
	iDoStop = ETrue;
	iCurrentXfer = ETxferNone;
	Cancel();
	}


void CActiveRW::DoCancel()
	{
	TUSB_VERBOSE_PRINT("CActiveRW::DoCancel()");
	// Canceling the transfer requests can be done explicitly
	// for every transfer...
	iPort->WriteCancel((TENDPOINTNUMBER)iTestParams.inPipe);
	iPort->ReadCancel((TENDPOINTNUMBER)iTestParams.outPipe);
	// or like this:
	// iPort->EndpointTransferCancel(~0);	
	}


TBool CActiveRW::CompareBuffers()
	{
	TUSB_VERBOSE_PRINT2("CActiveRW::CompareBuffers() ReadOffset %d ReadSize %d",iReadOffset,iReadSize);
	#ifdef USB_SC
	TUint8 *readPtr = reinterpret_cast<TUint8*>(iSCReadData);
	TUint8* writePtr;
	TUint inBufLength;
	test_KErrNone(iSCWriteBuf.GetInBufferRange((TAny*&)writePtr, inBufLength));
	writePtr += iReadOffset;
	#endif
	for (TUint i = 0; i < iReadSize; i++)
		{
		#ifdef USB_SC
		if (*readPtr != *writePtr)
			{
			TUSB_PRINT3 ("*** Error while comparing tx & rx buffers packet 0x%x length %d index %d",iPktNum, iReadSize,i + iReadOffset); 
			TUSB_PRINT2 ("*** Read byte 0x%x Write byte 0x%x",*readPtr,*writePtr); 
			return EFalse;
			}
		readPtr++;
		writePtr++;
		#else
		if (iReadBuf[i] != iWriteBuf[i + iReadOffset])
			{
			TUSB_PRINT3 ("*** Error while comparing tx & rx buffers packet 0x%x length %d index %d",iPktNum, iReadSize,i + iReadOffset); 
			TUSB_PRINT5 ("WriteBuf Start 0x%x 0x%x 0x%x 0x%x 0x%x",
				iWriteBuf[i], iWriteBuf[i+1], iWriteBuf[i+2], iWriteBuf[i+3], iWriteBuf[i+4]); 
			TUSB_PRINT5 ("ReadBuf Start 0x%x 0x%x 0x%x 0x%x 0x%x",
				iReadBuf[i], iReadBuf[i+1], iReadBuf[i+2], iReadBuf[i+3], iReadBuf[i+4]);
			if (iReadSize >= 10)
				{
				TUSB_PRINT5 ("WriteBuf End 0x%x 0x%x 0x%x 0x%x 0x%x",
					iWriteBuf[iReadSize-5], iWriteBuf[iReadSize-4], iWriteBuf[iReadSize-3], iWriteBuf[iReadSize-2], iWriteBuf[iReadSize-1]); 
				TUSB_PRINT5 ("ReadBuf End 0x%x 0x%x 0x%x 0x%x 0x%x",
					iReadBuf[iReadSize-5], iReadBuf[iReadSize-4], iReadBuf[iReadSize-3], iReadBuf[iReadSize-2], iReadBuf[iReadSize-1]); 
				}
			return EFalse;
			}
		#endif
		}
	return ETrue;
	}

void CActiveRW::TestComplete(TBool aResult)
	{
	TUSB_VERBOSE_PRINT("CActiveRW::TestComplete()");

	iResult = aResult;
	
	if (iComplete || !iResult || iTestParams.repeat == 0)
		{
		test(iResult);
		test.End();
		gRW[iIndex] = NULL;
		delete this;
		}
	else
		{
		iComplete = ETrue;
		}
	}

// -eof-
