// Copyright (c) 1998-2009 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\pccd\t_pccdsr.cpp
// Stress test a single sector of Compact Flash card (ATA).
// 
//


//#define USE_F32_ACCESS
#include <e32test.h>
#include <e32svr.h>
#include <e32hal.h>
#include <e32uid.h>
#if defined (USE_F32_ACCESS)
#include <f32fsys.h>
#include <f32file.h>
#endif

#define ATA_PDD_NAME _L("MEDATA")

const TInt KAtaSectorSize=512;
const TInt KMaxSectors=8;
const TInt KMaxRdWrBufLen=(KAtaSectorSize*KMaxSectors); // 4K
const TInt KHeapSize=0x4000;
const TInt KMaxErr=8;

#if defined (USE_F32_ACCESS)
GLDEF_D	RFs TheFs;
#else
LOCAL_D	TBusLocalDrive TheDrive;
LOCAL_D TBool ChangedFlag;
#endif
RTest test(_L("Local Drive Stress test"));
LOCAL_D TBuf8<KMaxRdWrBufLen> wrBufPat1,wrBufPat2,rdBuf;

enum TOper {ENone,EWrite,ERead,ECompare};
class TErrInfo
	{
public:
	TErrInfo();
public:
	TInt iError;
	TOper iOperation;
	TInt iCycle;
	};

class TResult
	{
public:
	TResult();
	void Display(CConsoleBase *aConsole, TInt aCycles);
	void Add(TInt anError,TOper anOperation,TInt aCycle);
public:
	TInt iTotalErrs;
	TErrInfo iFirstErr;
	TErrInfo iLastErrs[KMaxErr];
	TInt iNextFreeErr;
	TBool iHadAnError;
	};


LOCAL_C TUint OperationToChar(TOper anOperation)
//
// Convert operation enum to corresponding display character
//
	{

	switch(anOperation)
		{
		case EWrite:
			return('W');
		case ERead:
			return('R');
		case ECompare:
			return('C');
		default:
			return('?');
		}
	}

TErrInfo::TErrInfo()
//
// Constructor
//
	{

	iError=KErrNone;
	iOperation=ENone;
	iCycle=0;
	}

TResult::TResult()
//
// Constructor
//
	{

	iNextFreeErr=0;
	iTotalErrs=0;
	iHadAnError=EFalse;
	}

void TResult::Display(CConsoleBase *aConsole, TInt aCycles)
//
// Display test results
//
	{

	TInt xStartPos=0;
	TInt yStartPos=7;

	aConsole->SetPos(xStartPos,yStartPos);
	test.Printf(_L("CYCLES-> %07d   ERRORS-> %d"),aCycles,iTotalErrs);

	aConsole->SetPos(xStartPos,yStartPos+1);
	test.Printf(_L("FIRST ERROR-> "));
	if (iHadAnError)
		test.Printf(_L("Error:%d Oper:%c Cycle:%07d"),iFirstErr.iError,OperationToChar(iFirstErr.iOperation),iFirstErr.iCycle);

	aConsole->SetPos(xStartPos,yStartPos+2);
	test.Printf(_L("LAST ERRORS->"));
	if (iHadAnError)
		{
		TInt i;
		aConsole->SetPos(xStartPos+3,yStartPos+3);
		test.Printf(_L("Error:  "));
		for (i=0;(i<KMaxErr && iLastErrs[i].iOperation!=ENone);i++)
			test.Printf(_L("% 7d,"),iLastErrs[i].iError);

		aConsole->SetPos(xStartPos+3,yStartPos+4);
		test.Printf(_L("Oper:   "));
		for (i=0;(i<KMaxErr && iLastErrs[i].iOperation!=ENone);i++)
			test.Printf(_L("      %c,"),OperationToChar(iLastErrs[i].iOperation));
 
		aConsole->SetPos(xStartPos+3,yStartPos+5);
		test.Printf(_L("Cycle:  "));
		for (i=0;(i<KMaxErr && iLastErrs[i].iOperation!=ENone);i++)
			test.Printf(_L("% 7d,"),iLastErrs[i].iCycle);
		}

	test.Printf(_L("\r\n"));
	}

void TResult::Add(TInt anError,TOper anOperation,TInt aCycle)
//
// Add a test result
//
	{

	iTotalErrs++;
	if (!iHadAnError)
		{
		iFirstErr.iError=anError;
		iFirstErr.iOperation=anOperation;
		iFirstErr.iCycle=aCycle;
		iHadAnError=ETrue;
		}
	if (iNextFreeErr>=KMaxErr)
		{
		for (TInt i=0;i<(KMaxErr-1);i++)
			iLastErrs[i]=iLastErrs[i+1];
		iNextFreeErr=(KMaxErr-1);
		}
	iLastErrs[iNextFreeErr].iError=anError;
	iLastErrs[iNextFreeErr].iOperation=anOperation;
	iLastErrs[iNextFreeErr].iCycle=aCycle;
	iNextFreeErr++;
	}

GLDEF_C TInt E32Main()
    {
	TBuf<64> b;

	test.Title();
	TDriveInfoV1Buf diBuf;
	UserHal::DriveInfo(diBuf);
	TDriveInfoV1 &di=diBuf();
	test.Printf(_L("Select Local Drive (C-%c): "),'C'+(di.iTotalSupportedDrives-1));
	TChar c;
	TInt drv;
	FOREVER
		{
		c=(TUint)test.Getch();
		c.UpperCase();
		drv=((TUint)c)-'C';
		if (drv>=0&&drv<di.iTotalSupportedDrives)
			break;
		}
	test.Printf(_L("%c:\r\n"),'C'+drv);

	TInt rdWrLen;
#if !defined (USE_F32_ACCESS)
	test.Printf(_L("Select total sectors to write(1-8): "));
	FOREVER
		{
		c=(TUint)test.Getch();
		rdWrLen=((TUint)c)-'0';
		if (rdWrLen>=1&&rdWrLen<=8)
			break;
		}
	test.Printf(_L("%dSector(s)\r\n"),rdWrLen);
	rdWrLen*=KAtaSectorSize;
#else
	rdWrLen=(KAtaSectorSize*2)+1;
#endif

	b.Format(_L("Init test on drive %c:"),'C'+drv);
	test.Start(b);

	TInt r;
#if defined (USE_F32_ACCESS)
	r=TheFs.Connect();
	test(r==KErrNone);

	RFile f;
	b.Format(_L("%c:\\TEMP.BIN"),'C'+drv);
	r=f.Replace(TheFs,b,EFileShareAny|EFileStream|EFileWrite);
	test(r==KErrNone);
	b.Format(_L("Start testing (%c:\\TEMP.BIN):"),'C'+drv);
#else
	r=User::LoadPhysicalDevice(ATA_PDD_NAME);
	test(r==KErrNone || r==KErrAlreadyExists);

	ChangedFlag=EFalse;
	TheDrive.Connect(drv,ChangedFlag);

	TLocalDriveCapsV2Buf info;
	test(TheDrive.Caps(info)==KErrNone);
	test(info().iType==EMediaHardDisk);
	TInt trgPos=I64LOW(info().iSize)-rdWrLen;	  // Hammer the very end of the disk
	b.Format(_L("Start testing (sector %xH):"),trgPos/KAtaSectorSize);
#endif

	test.Next(b);
	wrBufPat1.SetLength(rdWrLen);
	TInt j;
	for (j=0;j<rdWrLen;j++)
		wrBufPat1[j]=(TUint8)j;
	wrBufPat2.SetLength(rdWrLen);
	for (j=0;j<rdWrLen;j++)
		wrBufPat2[j]=(TUint8)((rdWrLen-1)-j);

	TInt cycles=0;
	TResult results;
	TBool toggleTest=EFalse;

	TRequestStatus kStat;
	test.Console()->Read(kStat);
	FOREVER
		{
		if ((cycles%10)==0)
			results.Display(test.Console(),cycles);

		TInt res;
#if defined (USE_F32_ACCESS)
		TInt len=(toggleTest)?rdWrLen:1;

		wrBufPat1.SetLength(len);
		if ((res=f.SetSize(len))==KErrNone)
			res=f.Write(0,wrBufPat1);		// Write test (Pos=0)
		if (res!=KErrNone)
			results.Add(res,EWrite,cycles);

		// Read test
		rdBuf.Fill(0,len);
		res=f.Read(0,rdBuf,len);
		if (res!=KErrNone)
			results.Add(res,ERead,cycles);
		if (rdBuf.Compare(wrBufPat1)!=0)
			results.Add(0,ECompare,cycles);
#else
		TDes8* wrBuf=(toggleTest)?&wrBufPat2:&wrBufPat1; // Change pattern written
		
		res=TheDrive.Write(trgPos,*wrBuf);	// Write test
		if (res!=KErrNone)
			results.Add(res,EWrite,cycles);
		
		rdBuf.Fill(0,rdWrLen);
		res=TheDrive.Read(trgPos,rdWrLen,rdBuf);	// Read test
		if (res!=KErrNone)
			results.Add(res,ERead,cycles);
		if (rdBuf.Compare(*wrBuf)!=0)
			results.Add(0,ECompare,cycles);
#endif
		cycles++;
		toggleTest^=0x01;

		if (kStat!=KRequestPending)
			{
			TKeyCode c=test.Console()->KeyCode();
			if (c==EKeySpace)
				break;
			test.Console()->Read(kStat);
			}
		}

	test.Next(_L("Close"));
#if defined (USE_F32_ACCESS)
	f.Close();
	TheFs.Close();
#else
	TheDrive.Disconnect();
#endif

	test.End();
	return(0);
	}
  
