// Copyright (c) 1996-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_crrdrv.cpp
// Test the Compact Rom (Programmable) card media driver
// 
//

#include <e32test.h>
#include <e32svr.h>
#include <e32uid.h>
#define TWOMEG

LOCAL_D RTest test(_L("T_CRRDRV"));

#if defined (__MARM__)

const TInt KTestBufLen=0x100;

#define PDD_NAME _L("MEDCRR")
const TInt KDriveNumber=1;					// D:
#if defined (TWOMEG)
const TInt KMaxDriveLen=0x00200000;			// 2M
#else
const TInt KMaxDriveLen=0x00600000;			// 6M
#endif
const TUint16 KMagicSum=0x1234;
const TInt KCRomPrivateAreaLen=0x800;		// 1st 2K of CRom contains CIS


const TInt KCRomCisDataLength=0xD0;

LOCAL_D TBuf8<KTestBufLen> wrBuf,rdBuf;
#if defined (TWOMEG)
LOCAL_D const TText8 CompactRomCisData[KCRomCisDataLength]={
	0x01,0xff,0x03,0xff,0x1c,0xff,0x01,0xff,0xff,0xff,0x1c,0xff,0x04,0xff,0x02,0xff,
	0x1b,0xff,0x01,0xff,0xff,0xff,0x20,0xff,0x04,0xff,0x00,0xff,0x00,0xff,0x00,0xff,
	0x00,0xff,0x15,0xff,0x24,0xff,0x05,0xff,0x00,0xff,0x50,0xff,0x73,0xff,0x69,0xff,
	0x6f,0xff,0x6e,0xff,0x20,0xff,0x50,0xff,0x4c,0xff,0x43,0xff,0x00,0xff,0x43,0xff,
	0x6f,0xff,0x6d,0xff,0x70,0xff,0x61,0xff,0x63,0xff,0x74,0xff,0x20,0xff,0x52,0xff,
	0x6f,0xff,0x6d,0xff,0x20,0xff,0x43,0xff,0x61,0xff,0x72,0xff,0x64,0xff,0x00,0xff,
	0x56,0xff,0x30,0xff,0x2e,0xff,0x30,0xff,0x30,0xff,0x00,0xff,0xff,0xff,0x80,0xff,
	0x03,0xff,0x58,0xff,0x06,0xff,0xff,0xff,0x21,0xff,0x02,0xff,0x01,0xff,0x00,0xff,
	0x40,0xff,0x0b,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x08,0xff,0x00,0xff,0x00,0xff,
	0x00,0xff,0x00,0xff,0x01,0xff,0x00,0xff,0x00,0xff,0x41,0xff,0x0a,0xff,0x01,0xff,
	0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xf8,0xff,0x1f,0xff,
	0x00,0xff,0x14,0xff,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
	0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff
	};
#else
LOCAL_D const TText8 CompactRomCisData[KCRomCisDataLength]={
	0x01,0xff,0x03,0xff,0x1c,0xff,0x01,0xff,0xff,0xff,0x1c,0xff,0x04,0xff,0x02,0xff,
	0x1b,0xff,0x01,0xff,0xff,0xff,0x20,0xff,0x04,0xff,0x00,0xff,0x00,0xff,0x00,0xff,
	0x00,0xff,0x15,0xff,0x24,0xff,0x05,0xff,0x00,0xff,0x50,0xff,0x73,0xff,0x69,0xff,
	0x6f,0xff,0x6e,0xff,0x20,0xff,0x50,0xff,0x4c,0xff,0x43,0xff,0x00,0xff,0x43,0xff,
	0x6f,0xff,0x6d,0xff,0x70,0xff,0x61,0xff,0x63,0xff,0x74,0xff,0x20,0xff,0x52,0xff,
	0x6f,0xff,0x6d,0xff,0x20,0xff,0x43,0xff,0x61,0xff,0x72,0xff,0x64,0xff,0x00,0xff,
	0x56,0xff,0x30,0xff,0x2e,0xff,0x30,0xff,0x30,0xff,0x00,0xff,0xff,0xff,0x80,0xff,
	0x0b,0xff,0x58,0xff,0x06,0xff,0x08,0xff,0x76,0xff,0x58,0xff,0x06,0xff,0x08,0xff,
	0x76,0xff,0x58,0xff,0x06,0xff,0xff,0xff,0x21,0xff,0x02,0xff,0x01,0xff,0x00,0xff,
	0x40,0xff,0x0b,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x08,0xff,0x00,0xff,0x00,0xff,
	0x00,0xff,0x00,0xff,0x01,0xff,0x00,0xff,0x00,0xff,0x41,0xff,0x0a,0xff,0x01,0xff,
	0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xf8,0xff,0x5f,0xff,
	0x00,0xff,0x14,0xff,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff
	};
#endif

/* A sample multifunction CIS to test out tuple parsing by blowing onto CRom card.
const TInt KMfcCisDataLen=0x180;
LOCAL_D const TText8 MfcCisData[KMfcCisDataLen]={
	0x13,0xff,0x03,0xff,0x43,0xff,0x49,0xff,0x53,0xff,0x06,0xff,0x10,0xff,0x03,0xff,
	0x00,0xff,0x30,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x60,0xff,0x00,0xff,
	0x00,0xff,0x00,0xff,0x00,0xff,0x90,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,
	0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
	0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
	0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
	0x01,0xff,0x03,0xff,0xda,0xff,0x00,0xff,0xff,0xff,0x1c,0xff,0x04,0xff,0x02,0xff,
	0xda,0xff,0x00,0xff,0xff,0xff,0x21,0xff,0x02,0xff,0x02,0xff,0x00,0xff,0x1a,0xff,
	0x05,0xff,0x01,0xff,0x01,0xff,0x00,0xff,0x02,0xff,0x0f,0xff,0x1b,0xff,0x07,0xff,
	0xc1,0xff,0x41,0xff,0x09,0xff,0x01,0xff,0xb5,0xff,0x1e,0xff,0x49,0xff,0x14,0xff,
	0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
	0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
	0x01,0xff,0x03,0xff,0xda,0xff,0x00,0xff,0xff,0xff,0x1c,0xff,0x04,0xff,0x02,0xff,
	0xda,0xff,0x00,0xff,0xff,0xff,0x21,0xff,0x02,0xff,0x06,0xff,0x00,0xff,0x1a,0xff,
	0x05,0xff,0x01,0xff,0x02,0xff,0x00,0xff,0x04,0xff,0x0f,0xff,0x1b,0xff,0x06,0xff,
	0xc2,0xff,0x41,0xff,0x09,0xff,0x01,0xff,0x55,0xff,0x49,0xff,0x14,0xff,0x00,0xff,
	0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
	0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
	0x01,0xff,0x03,0xff,0xda,0xff,0x00,0xff,0xff,0xff,0x1c,0xff,0x04,0xff,0x02,0xff,
	0xda,0xff,0x00,0xff,0xff,0xff,0x21,0xff,0x02,0xff,0x03,0xff,0x00,0xff,0x1a,0xff,
	0x05,0xff,0x01,0xff,0x03,0xff,0x00,0xff,0x06,0xff,0x0f,0xff,0x1b,0xff,0x06,0xff,
	0xc3,0xff,0x41,0xff,0x09,0xff,0x01,0xff,0x55,0xff,0x49,0xff,0x14,0xff,0x00,0xff,
	0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
	0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff
	}; */


LOCAL_C void ProgressBar(TInt aPos,TInt anEndPos,TInt anXPos)
//
// Display progress of local drive operation on screen (1-16 dots)
//
	{
	static TInt prev;
	TInt curr;
	if ((curr=(aPos-1)/(anEndPos>>4))>prev)
		{ // Update progress bar
		test.Console()->SetPos(anXPos);
		for (TInt i=curr;i>=0;i--)
			test.Printf(_L("."));
		}
	prev=curr;
	}

GLDEF_C TInt E32Main()
    {
	TInt i;
	TBuf<32> b;

	test.Title();

//	UserSvr::ForceRemountMedia(ERemovableMedia0); 	// Generate media change (in case normal CROM driver open on target).
	User::After(300000);		  					// Allow 0.3s for controller to detect door closed.

	test.Start(_L("Load CROM(repro) Media Driver"));
	TInt r=User::LoadPhysicalDevice(PDD_NAME);
	test(r==KErrNone||r==KErrAlreadyExists);
    
	b.Format(_L("Connect to local drive (%c:)"),KDriveNumber+'C');
	test.Next(b);
	TBusLocalDrive theCRomDrive;
	TBool changeFlag=FALSE;
	test(theCRomDrive.Connect(KDriveNumber,changeFlag)==KErrNone);

	test.Next(_L("CROM drive: capabilities"));
//	UserSvr::ForceRemountMedia(ERemovableMedia0); 	// Generate media change (in case normal CROM driver open on target).
	User::After(300000);		  					// Allow 0.3s for controller to detect door closed.
	TLocalDriveCapsV2Buf info;
	test(theCRomDrive.Caps(info)==KErrNone);
	test(info().iSize==(TInt64)KMaxDriveLen);
	test(info().iType==EMediaFlash);
	test(info().iBattery==EBatNotSupported);
	test(info().iDriveAtt==(KDriveAttLocal|KDriveAttRemovable));
	test(info().iMediaAtt==KMediaAttFormattable);
	test(info().iFileSystemId==KDriveFileSysFAT);

	test.Next(_L("CROM drive: format"));
	TFormatInfo fi;
	test.Printf(_L("Formatting "));
	TInt ret;
	while((ret=theCRomDrive.Format(fi))!=KErrEof)
		{
		ProgressBar((fi.i512ByteSectorsFormatted<<9),KMaxDriveLen,11);
		test(ret==KErrNone);
		} 
	test.Printf(_L("\r\nReading    "));
	TInt len;
    TInt msgHandle = KLocalMessageHandle;
	for (i=0;i<KMaxDriveLen;i+=len)
		{
		ProgressBar(i,KMaxDriveLen,11);
		len=Min(KTestBufLen,(KMaxDriveLen-i));
		rdBuf.Fill(0x55,len); // Sets length too
 		test(theCRomDrive.Read(i,len,&rdBuf,msgHandle,0)==KErrNone);
		wrBuf.Fill(0xFF,len); // Sets length too
		if ((ret=rdBuf.Compare(wrBuf))!=0)
			{
			test.Printf(_L(" (C:%d@%xH)\r\n"),ret,i);
 			test(0);
			}
		}
	test.Printf(_L("\r\n"));

	wrBuf.SetLength(KTestBufLen);
	for (i=0;i<KTestBufLen;i++)
		wrBuf[i]=(TUint8)i;
/*
	test.Next(_L("CROM drive: Write at start of Drive"));
 	test(theCRomDrive.Write(0,KTestBufLen,&wrBuf,msgHandle,0)==KErrNone);
	rdBuf.Fill(0,KTestBufLen);
 	test(theCRomDrive.Read(0,KTestBufLen,&rdBuf,msgHandle,0)==KErrNone);
  	test(rdBuf.Compare(wrBuf)==0);

	test.Next(_L("CROM drive: Write at end of Drive"));
 	test(theCRomDrive.Write(KMaxDriveLen-KTestBufLen,KTestBufLen,&wrBuf,msgHandle,0)==KErrNone);
	rdBuf.Fill(0,KTestBufLen);
 	test(theCRomDrive.Read(KMaxDriveLen-KTestBufLen,KTestBufLen,&rdBuf,msgHandle,0)==KErrNone);
  	test(rdBuf.Compare(wrBuf)==0);

	test.Next(_L("CROM drive: Write across 64K block boundary"));
 	test(theCRomDrive.Write((0x10000-(KTestBufLen>>1)),KTestBufLen,&wrBuf,msgHandle,0)==KErrNone);
	rdBuf.Fill(0,KTestBufLen);
 	test(theCRomDrive.Read((0x10000-(KTestBufLen>>1)),KTestBufLen,&rdBuf,msgHandle,0)==KErrNone);
  	test(rdBuf.Compare(wrBuf)==0);

	test.Next(_L("CROM drive: Write across 28F016 device boundary"));
 	test(theCRomDrive.Write((0x200000-(KTestBufLen>>1)),KTestBufLen,&wrBuf,msgHandle,0)==KErrNone);
	rdBuf.Fill(0,KTestBufLen);
 	test(theCRomDrive.Read((0x200000-(KTestBufLen>>1)),KTestBufLen,&rdBuf,msgHandle,0)==KErrNone);
  	test(rdBuf.Compare(wrBuf)==0);
*/
	test.Next(_L("CROM drive: Write/Read the entire disk"));
	TInt rdwrTestLen=(KTestBufLen-3); // Test it works off word boundary
	TUint *p=(TUint*)&wrBuf[0];
	TInt j;
	test.Printf(_L("Writing    "));
	for (i=0,j=0;i<KMaxDriveLen;i+=len,j++) 
		{
		ProgressBar(i,KMaxDriveLen,11);
		len=Min(rdwrTestLen,(KMaxDriveLen-i));
		(*p)=j;
		test(theCRomDrive.Write(i,len,&wrBuf,msgHandle,0)==KErrNone);
		}
	test.Printf(_L("\r\nReading    "));
	for (i=0,j=0;i<KMaxDriveLen;i+=len,j++)
		{
		ProgressBar(i,KMaxDriveLen,11);
		len=Min(rdwrTestLen,(KMaxDriveLen-i));
		rdBuf.Fill(0,len);
 		if ((ret=theCRomDrive.Read(i,len,&rdBuf,msgHandle,0))!=KErrNone)
			{
			test.Printf(_L(" (R:%d@%xH)\r\n"),ret,j);
			test(0);
			}
		(*p)=j;
		wrBuf.SetLength(len);
 		if ((ret=rdBuf.Compare(wrBuf))!=0)
			{
			test.Printf(_L(" (C:%d@%xH)\r\n"),ret,j);
			test(0);
			}
		}
	test.Printf(_L("\r\n"));

	test.Next(_L("CROM drive: Re-format"));
	test.Printf(_L("Formatting "));
	while((ret=theCRomDrive.Format(fi))!=KErrEof)
		{
		ProgressBar((fi.i512ByteSectorsFormatted<<9),KMaxDriveLen,11);
		test(ret==KErrNone);
		} 
	test.Printf(_L("\r\n"));

	test.Next(_L("CROM drive: Write CIS at start of Drive"));
    TPtrC8 buf(&CompactRomCisData[0],KCRomCisDataLength);
//    TPtrC8 buf(&MfcCisData[0],KMfcCisDataLen); // ???
 	test(theCRomDrive.Write(0,buf)==KErrNone);

	rdBuf.Fill(0,KTestBufLen);
 	test(theCRomDrive.Read(0,KCRomCisDataLength,rdBuf)==KErrNone);
  	test(rdBuf.Compare(buf)==0); 

	test.Next(_L("Calculating & writing checksum"));
	TUint16 checksum=0; // 16bit arithmetic
	test.Printf(_L("Reading"));
	for (i=0;i<KMaxDriveLen;i+=len)
		{
		ProgressBar(i,KMaxDriveLen,11);
		len=Min(KTestBufLen,(KMaxDriveLen-i));
 		test(theCRomDrive.Read(i,len,rdBuf)==KErrNone);
		test((len%2)==0); // 16bit checksum - length must never be odd
		for (TInt j=0;j<len;j+=2)
			checksum+=*(TUint16*)(rdBuf.Ptr()+j);
		}
	checksum-=0xFFFF; // Subtract for checksum itself
	TUint16 adjust=KMagicSum-checksum;
	test.Printf(_L("\r\n   (CS:%xH)\r\n"),adjust);
	TPtrC8 cs((TUint8*)&adjust,2);
	test(theCRomDrive.Write(KCRomPrivateAreaLen-2,cs)==KErrNone);

	test.Next(_L("CROM drive: Caps following media change"));
//	UserSvr::ForceRemountMedia(ERemovableMedia0);	 // Generate media change	
	User::After(300000);							// Allow 0.3s after power down for controller to detect door closed.
	test(changeFlag!=FALSE);
	test(theCRomDrive.Caps(info)==KErrNone);
	test(info().iSize==(TInt64)KMaxDriveLen);
	test(info().iType==EMediaFlash);

	b.Format(_L("Disconnect from local drive (%c:)"),KDriveNumber+'C');
	test.Next(b);
	theCRomDrive.Disconnect();

	TFindPhysicalDevice fDr;
	test.Next(_L("Unload CROM(prog) Media Driver"));
	TFullName drivName;
	fDr.Find(_L("Media.RomPrg"));
	test(fDr.Next(drivName)==KErrNone);
	test(User::FreePhysicalDevice(drivName)==KErrNone);
//	UserSvr::ForceRemountMedia(ERemovableMedia0); // Generate media change (allow normal CROM driver on next target access).

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

#else
TInt E32Main()
	{
	test.Title();
	test.Start(_L("T_CRRDRV only runs under ARM build."));
	test.End();
	return(KErrNone);					  
	}
#endif
  
