kernel/eka/drivers/medata/pccd_ata.cpp
changeset 0 a41df078684a
child 6 0173bcd7697c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kernel/eka/drivers/medata/pccd_ata.cpp	Mon Oct 19 15:55:17 2009 +0100
@@ -0,0 +1,1795 @@
+// 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:
+// e32\drivers\medata\pccd_ata.cpp
+// 
+//
+
+#include "pbusmedia.h"
+#include "platform.h"
+#include "ata.h"
+
+const TInt KErrBadDrqOnRead=-64;
+const TInt KErrBadDrqOnWrite=-65;
+const TInt KErrBadDrq=-66;
+const TInt KErrAta=-67;
+const TInt KErrBadPccdConfig=-68;
+//const TInt KErrDriveChunkNotOpen=-69;
+
+const TInt KAtaDriverPriority=KMediaDriverPriorityNormal;
+//const TInt KAtaDriverPriority=KMediaDriverPriorityHigh;
+
+_LIT(KPddName, "Media.Ata");
+//_LIT(KPddName, "Media.Ata2");
+
+// One of these
+#define SELECT_CONTIGUOUS_IO_CONFIG
+//#define SELECT_PRIMARY_IO_CONFIG
+//#define SELECT_MEMORY_CONFIG
+
+//#define FORCE_8BIT_ACCESSES
+
+// Special debug options
+//#define SHOW_CARD_ERRORS
+//#define DEBUG_WITH_HW_TRIGGER
+//#define COUNT_TIMEOUTS
+
+#if (defined(SHOW_CARD_ERRORS))
+#define __KTRACE_CARD_ERROR(a,p) {p;}
+#elif (defined(_DEBUG))
+#define __KTRACE_CARD_ERROR(a,p) {if((KDebugNum(a)))p;}
+#else
+#define __KTRACE_CARD_ERROR(a,p)
+#endif
+
+#include <pccard.h>
+
+const TInt KMaxSectorsPerRead=32;
+const TInt KMaxSectorsPerWrite=8;				 
+const TInt KMaxSectorsPerFormat=8;				 
+const TInt KMaxBytesPerRead=(KMaxSectorsPerRead<<KAtaSectorShift);
+const TInt KMaxBytesPerWrite=(KMaxSectorsPerWrite<<KAtaSectorShift);
+
+// Sector buffer size must be a multiple of sector size and at least as large
+// as KMaxSectorsPerWrite.
+const TInt KSectBufSizeInSectors=8;
+const TInt KSectBufSizeInBytes=(KSectBufSizeInSectors<<KAtaSectorShift);
+const TInt KSectBufSizeInBytesMinusOneSector=(KSectBufSizeInBytes-KAtaSectorSize);
+
+const TInt KIdleCurrentInMilliAmps=1; 
+const TInt KReadCurrentInMilliAmps=39;
+const TInt KWriteCurrentInMilliAmps=46;
+
+const TInt KNotBusySyncTimeout=5;
+const TInt KNotBusySyncRetryCount=10;
+const TInt KDriveReadySyncTimeout=5;
+
+#ifdef _DEBUG
+const TInt KNotBusyTestInterval=30;		// Check for not-busy once every 30ms
+const TInt KBusyTimeOut=67;				// Timeout after this many tests (67*30ms=2010ms)
+#else
+const TInt KNotBusyTestInterval=5;		// Check for not-busy once every 5ms
+const TInt KBusyTimeOut=400;			// Timeout after this many tests (400*5ms=2010ms)
+#endif
+
+class DPhysicalDeviceMediaAta : public DPhysicalDevice
+	{
+public:
+	DPhysicalDeviceMediaAta();
+	virtual TInt Install();
+	virtual void GetCaps(TDes8& aDes) const;
+	virtual TInt Create(DBase*& aChannel, TInt aMediaId, const TDesC8* anInfo, const TVersion& aVer);
+	virtual TInt Validate(TInt aDeviceType, const TDesC8* anInfo, const TVersion& aVer);
+	virtual TInt Info(TInt aFunction, TAny* a1);
+	};
+
+class DPcCardMediaDriverAta : public DMediaDriver
+	{
+public:
+	DPcCardMediaDriverAta(TInt aMediaId);
+	virtual TInt Request(TLocDrvRequest& aRequest);
+	virtual TInt PartitionInfo(TPartitionInfo& anInfo);
+	virtual void NotifyPowerDown();
+	virtual void NotifyEmergencyPowerDown();
+	virtual void Close();
+public:
+	enum TCardStatus {ECardIdle,ECardRead,ECardWrite,ECardFormat};
+	inline TUint8 AtaRegister8(TUint aReg);
+	inline void SetAtaRegister8(TUint8 aValue,TUint aReg);
+	void ModifyAtaRegister8(TUint aClearMask,TUint aSetMask,TUint aReg);
+	void SelectDrive(TAtaDriveSelect aDrive);
+	TBool WaitForNotBusy();
+	TBool DriveReadyForCommand(TInt aDrive);
+	void SetLbaSectorAddress(TUint aSector);
+	void SetChsSectorAddress(TUint aSector);
+	TInt IssueAtaCommand(TUint8 aCmd,TUint aFirstSector,TInt aSectorCount);
+	TInt EmptySectBufferToTrg(TUint8 *aSrc,TUint aTrgOffset,TInt aLen);
+	TInt LoadSectBufferFromSrc(TInt aLen, TUint8* aBuf);
+	TInt LoadSectBufferFromDrive(TAny *aBuf);
+	TInt EmptySectBufferToDrive(TUint8 *aBuf);
+	TInt TransferSectBufferFromDrive(TAny *aBuf);
+	TInt FinishCommand();
+	TInt CheckForError();
+	TInt ProcessError(TInt anError);
+	TInt SectorBoundaryReadCheck(TUint aStartSector,TUint aStartSectOffset,Int64 anEndPos);
+	TInt SectorRead(TUint aFirstSector,TUint8 *aBuf,TInt aSectorCount=1,TUint8 aCmd=KAtaCmdReadSectors);
+	TInt CheckDevice(TBool aCheckPower);
+	TInt InitiateWriteCommand(TUint aFirstSector,TInt aSectorCount,TUint8 *aSectBuffer);
+	TInt ReadSectorsCommand(TUint aFirstSector,TUint aBufOffset,TInt aLen);
+	TInt IdentifyDrive();
+	void DumpIdentifyDriveInfo();
+	TInt ConfigAutoPowerDown();
+	TInt Open();
+	TInt DoOpen();
+	void DoClose();
+	void Reset();
+	TBool CardPoweredDown();
+	void IncrementSectBufPtr();
+	static void CardIreqDfcFunction(TAny* aPtr);
+	static void TimerDfcFunction(TAny* aPtr);
+	static void AsyncBusyTimerCallBack(TAny* aMediaDriver);
+	static void SyncBusyTimerCallBack(TAny* aBusyFlag);
+	static void CardIntCallBack(TAny* aPtr, TInt anId);
+	TBool DoCardNotBusy(TInt &anErr);
+	TBool CmdDfc();
+	TBool DoCmdDfc(TInt &anErr);
+	void Complete(TInt anError);
+	TInt Caps(TLocalDriveCapsV6& anInfo);
+	TInt DoRead();
+	TInt DoWrite();
+	TInt DoFormat();
+	TInt InitiateAsyncRead();
+	TInt InitiateAsyncWrite();
+public:
+	DPcCardSocket* iSocket;
+	TLocDrvRequest* iCurrentReq;
+	TPBusCallBack iCardIntCallBack;
+	RPccdWindow iDriveChunk;
+	TPccdMemType iMemoryType;
+	TDriveParameters iDriveInfo;
+	NTimer iBusyTimeout;
+	TDfc iCardIreqDfc;
+	TDfc iTimerDfc;
+	TInt iNotBusyTickCount;
+	TInt iCommandError;
+	TCardStatus iCardStatus;
+	TUint iCmdInOffset; 	// Progress counter for data received from src device
+	TUint iCmdOutOffset;	// Progress counter for data delivered to target device
+	TUint iCmdEndOffset;	// Marks point when transfer associated with command is complete
+	TUint iCmdLength;		// Transfer length remaining
+	TUint iNextSector;		// Next sector to transfer
+	TUint8 *iSectBufPtr;	// Progress counter for tranfering data between card and sector buffer
+	TUint iSectBufOffset;	// Offset within sector buffer to start of data involved in command (Reads only)
+	TBool iLastSectorBufUsed;
+	TUint iHiddenSectors;
+	TInt iCardFuncNum;
+	TUint8 iSectorBuf[KSectBufSizeInBytes]; // Keep on 4byte boundary - put this last
+	TUint8 iLastSectorBuf[KAtaSectorSize];	// Holds last sector data for unaligned write
+#if (defined(_DEBUG) || defined(SHOW_CARD_ERRORS))
+	TInt iDbgLastError;
+	TInt iDbgPos;
+	TInt iDbgLen;
+#endif
+#ifdef COUNT_TIMEOUTS
+	TInt iInts;
+	TInt iTimeouts;
+	TInt iImmediateNotBusy;
+	TInt iChainedReads;
+	TInt iChainedWrites;
+	TBool iNewTimeOut;
+	TInt iIndex;
+	TInt iInfo[8];
+#endif
+	};
+
+void DPcCardMediaDriverAta::Complete(TInt anError)
+	{
+	__KTRACE_OPT(KFAIL,Kern::Printf("mdrqc %08x %d",iCurrentReq,anError));
+#ifdef COUNT_TIMEOUTS
+	if (iNewTimeOut)
+		{
+		iNewTimeOut=EFalse;
+		Kern::Printf("I=%d T=%d M=%d CR=%d CW=%d",iInts,iTimeouts,iImmediateNotBusy,iChainedReads,iChainedWrites);
+		TInt i;
+		for (i=0; i<iIndex; i+=2)
+			Kern::Printf("%d: %08x %08x",i,iInfo[i],iInfo[i+1]);
+		iIndex=0;
+		}
+#endif
+	TLocDrvRequest* pR=iCurrentReq;
+	if (pR)
+		{
+		iCurrentReq=NULL;
+		DMediaDriver::Complete(*pR,anError);
+		}
+	}
+
+inline TUint8 DPcCardMediaDriverAta::AtaRegister8(TUint aReg)
+//
+// Read from an 8 bit ATA register
+//
+	{
+
+	return iDriveChunk.Read8(aReg);
+	}
+
+inline void DPcCardMediaDriverAta::SetAtaRegister8(TUint8 aValue,TUint aReg)
+//
+// Write to an 8 bit ATA register
+//
+	{
+
+	iDriveChunk.Write8(aReg,aValue);
+	}
+
+void DPcCardMediaDriverAta::ModifyAtaRegister8(TUint aClearMask,TUint aSetMask,TUint aReg)
+//
+// Modify an 8 bit ATA register
+//
+	{
+
+	SetAtaRegister8((TUint8)((AtaRegister8(aReg)&(~aClearMask))|aSetMask),aReg);
+	}
+
+void DPcCardMediaDriverAta::SelectDrive(TAtaDriveSelect aDrive)
+//
+// Modify an 8 bit ATA register
+//
+	{
+
+	ModifyAtaRegister8(KAtaDrvHeadDrive1,(0xA0|aDrive),KAtaSelectDriveHeadRdWr8);
+	}
+
+TBool DPcCardMediaDriverAta::WaitForNotBusy()
+//
+// Poll busy flag (while card accesses the data buffer and command register). 
+// 8mS timeout.
+//
+	{
+
+	// Before we start a timed loop, just check it isn't already not busy
+	__KTRACE_OPT(KPBUSDRV,Kern::Printf("WfnB"));
+	if (!(AtaRegister8(KAtaStatusRd8)&KAtaStatusBusy))
+		return(ETrue);
+#if (defined(_DEBUG) || defined(SHOW_CARD_ERRORS))
+	TUint c=NKern::TickCount();
+#endif
+	volatile TBool timedOut=EFalse;
+	NTimer busyTimeout(SyncBusyTimerCallBack,(TAny*)&timedOut);
+	busyTimeout.OneShot(NKern::TimerTicks(KNotBusySyncTimeout));
+	FOREVER
+		{
+		if (!(AtaRegister8(KAtaStatusRd8)&KAtaStatusBusy)||timedOut)
+			break;
+		}
+	if (!timedOut)
+		busyTimeout.Cancel();
+	else
+		{
+		TInt retry=KNotBusySyncRetryCount;
+		while ((AtaRegister8(KAtaStatusRd8)&KAtaStatusBusy) && retry)
+			{
+			NKern::Sleep(1);
+			retry--;
+			}
+		}
+	TBool ret=(AtaRegister8(KAtaStatusRd8) & KAtaStatusBusy);
+#if (defined(_DEBUG) || defined(SHOW_CARD_ERRORS))
+	c=NKern::TickCount()-c;
+	if (ret)
+		{
+		__KTRACE_CARD_ERROR(KPBUSDRV,Kern::Printf("<Ata:WaitForNotBusy-timeout(%xH) %d ms",AtaRegister8(KAtaStatusRd8),c));
+ 		}
+	else
+		{
+		__KTRACE_CARD_ERROR(KPBUSDRV,Kern::Printf("<Ata:WaitForNotBusy-OK %d ms",c));
+		}
+#endif
+	return(!ret);
+	}
+
+TBool DPcCardMediaDriverAta::DriveReadyForCommand(TInt aDrive)
+//
+// Wait until selected drive is able to accept a command (5ms timeout).
+//					
+	{
+
+	// Select the drive were waiting on 
+	SelectDrive((aDrive==1)?ESelectDrive1:ESelectDrive0);
+	if (!WaitForNotBusy())
+		return(EFalse);
+
+	volatile TBool timedOut=EFalse;
+	NTimer busyTimeout(SyncBusyTimerCallBack,(TAny*)&timedOut);
+	busyTimeout.OneShot(NKern::TimerTicks(KDriveReadySyncTimeout));
+	FOREVER
+		{
+		if (AtaRegister8(KAtaStatusRd8)&KAtaStatusRdy||timedOut)
+			break;
+		}
+	if (!timedOut)
+		busyTimeout.Cancel();
+	TBool ret=(AtaRegister8(KAtaStatusRd8) & KAtaStatusRdy);
+#if (defined(_DEBUG) || defined(SHOW_CARD_ERRORS))
+	if (!ret)
+		__KTRACE_CARD_ERROR(KPBUSDRV,Kern::Printf("<Ata:DriveReadyForCommand-Fail(%xH)",AtaRegister8(KAtaStatusRd8)));
+#endif
+	return(ret);
+	}
+
+void DPcCardMediaDriverAta::SetLbaSectorAddress(TUint aSector)
+//
+// Setup the sector address ATA registers (LBA mode)
+//
+	{
+	__KTRACE_OPT(KPBUSDRV,Kern::Printf(">Ata:SetLbaSectorAddress (LBA: %xH)",aSector));
+
+	SetAtaRegister8((TUint8)aSector,KAtaLba7_0RdWr8);
+	SetAtaRegister8((TUint8)(aSector>>8),KAtaLba15_8RdWr8);
+	SetAtaRegister8((TUint8)(aSector>>16),KAtaLba23_16RdWr8);
+	TUint8 lba27_24=(TUint8)((aSector>>24)&0x0F);
+	ModifyAtaRegister8(KAtaDrvHeadLba27_24,(KAtaDrvHeadLbaOn|lba27_24),KAtaSelectDriveHeadRdWr8);
+	}
+
+void DPcCardMediaDriverAta::SetChsSectorAddress(TUint aSector)
+//
+// Setup the sector address ATA registers (CHS mode)
+//
+	{
+
+	TUint cylinder=0,head=0,sector=1;
+	if (iDriveInfo.iSectorsPerCylinder>0&&iDriveInfo.iSectorsPerTrack>0)
+		{
+		cylinder=aSector/iDriveInfo.iSectorsPerCylinder;
+		TUint remainder=aSector%iDriveInfo.iSectorsPerCylinder;
+		head=remainder/iDriveInfo.iSectorsPerTrack;
+		sector=(remainder%iDriveInfo.iSectorsPerTrack)+1;
+		}
+
+	__KTRACE_OPT(KPBUSDRV,Kern::Printf("Ata:SetChsSectorAddress (C: %xH H: %xH S: %xH)",cylinder,head,sector));
+	SetAtaRegister8((TUint8)sector,KAtaSectorNoRdWr8);
+	SetAtaRegister8((TUint8)cylinder,KAtaCylinderLowRdWr8);
+	SetAtaRegister8((TUint8)(cylinder>>8),KAtaCylinderHighRdWr8);
+	ModifyAtaRegister8((KAtaDrvHeadLbaOn|KAtaDrvHeadLba27_24),((TUint8)(head&0x0F)),KAtaSelectDriveHeadRdWr8);
+	}
+
+TInt DPcCardMediaDriverAta::IssueAtaCommand(TUint8 aCmd,TUint aFirstSector,TInt aSectorCount)
+//
+// Issue an ATA command (Drive 0 only for now).
+//
+	{
+
+	__KTRACE_OPT(KPBUSDRV,Kern::Printf(">Ata:IssueAtaCommand(C:%x, FS:%x, SC:%x)",aCmd,aFirstSector,aSectorCount));
+	__KTRACE_OPT(KFAIL,Kern::Printf("A%02x,%d",aCmd,aSectorCount));
+	if (!WaitForNotBusy())
+		return(KErrTimedOut);
+	if (!DriveReadyForCommand(0))
+		return KErrTimedOut;
+	
+	if (aSectorCount==KMaxSectorsPerCmd)
+		aSectorCount=0;
+	SetAtaRegister8((TUint8)aSectorCount,KAtaSectorCountRdWr8);
+	if (iDriveInfo.iSupportsLba)
+		SetLbaSectorAddress(aFirstSector);
+	else
+		SetChsSectorAddress(aFirstSector);
+	__TRACE_TIMING(0x103);
+	SetAtaRegister8(aCmd,KAtaCommandWr8);	// Issue the command
+	Kern::NanoWait(400);					// Busy flag not asserted for 400ns
+	__KTRACE_OPT(KPBUSDRV,Kern::Printf("<Ata:IssueAtaCommand"));
+	return(KErrNone);
+	}
+
+TInt DPcCardMediaDriverAta::LoadSectBufferFromDrive(TAny *aBuf)
+//
+// Read a sector from the ATA card
+//
+	{
+
+	if (!(AtaRegister8(KAtaStatusRd8)&KAtaStatusDrq))
+		{
+		__KTRACE_CARD_ERROR(KPBUSDRV,Kern::Printf(">Ata:LoadSectBufferFromDrive-Bad drq(%xH)",AtaRegister8(KAtaStatusRd8)));
+		return KErrBadDrqOnRead;
+		}
+
+	if (__IS_COMMON_MEM(iMemoryType))
+		iDriveChunk.Read(KAtaDataRdWrWinBase16,aBuf,KAtaSectorSize); // Use 1K window
+	else if (iMemoryType==EPccdIo16Mem)
+		iDriveChunk.ReadHWordMultiple(KAtaDataRdWr16,aBuf,(KAtaSectorSize>>1));
+	else
+		iDriveChunk.ReadByteMultiple(KAtaDataRdWr8,aBuf,KAtaSectorSize); // Must be EPccdIo8Mem
+
+	return(KErrNone);
+	}
+
+TInt DPcCardMediaDriverAta::TransferSectBufferFromDrive(TAny *aBuf)
+//
+// Read a sector from the ATA card
+//
+	{
+
+	if (!WaitForNotBusy())
+		return(KErrTimedOut);
+	return(LoadSectBufferFromDrive(aBuf));
+	}
+
+TInt DPcCardMediaDriverAta::EmptySectBufferToDrive(TUint8 *aBuf)
+//
+// Write a sector to the ATA card.
+//
+	{
+
+	if (!(AtaRegister8(KAtaStatusRd8)&KAtaStatusDrq))
+		{
+		__KTRACE_CARD_ERROR(KPBUSDRV,Kern::Printf(">Ata:EmptySectBufferToDrive-Bad drq(%xH)",AtaRegister8(KAtaStatusRd8)));
+		return KErrBadDrqOnWrite;
+		}
+
+	if (__IS_COMMON_MEM(iMemoryType))
+		iDriveChunk.Write(KAtaDataRdWrWinBase16,aBuf,KAtaSectorSize); // Use 1K window
+	else if (iMemoryType==EPccdIo16Mem)
+		iDriveChunk.WriteHWordMultiple(KAtaDataRdWr16,aBuf,(KAtaSectorSize>>1));
+	else
+		iDriveChunk.WriteByteMultiple(KAtaDataRdWr8,aBuf,KAtaSectorSize); // Must be EPccdIo8Mem
+
+	return(KErrNone); 
+	}
+
+TInt DPcCardMediaDriverAta::FinishCommand()
+//
+// Called each time a command has been issued to check if an error occured.
+//
+	{
+
+	if (!WaitForNotBusy())
+		return(KErrTimedOut);
+	return(CheckForError());
+	}
+
+TInt DPcCardMediaDriverAta::CheckForError()
+//
+// Called each time a command has been issued to check if an error occured.
+//
+	{
+
+	TUint8 status=AtaRegister8(KAtaStatusRd8);
+	if (status&KAtaStatusDrq)
+		{
+		__KTRACE_CARD_ERROR(KPBUSDRV,Kern::Printf("Ata:CheckForError-DRQ fail"));
+		return KErrBadDrq;
+		}
+	if (status&KAtaStatusDwf)
+		{
+		__KTRACE_CARD_ERROR(KPBUSDRV,Kern::Printf("Ata:CheckForError-DWF fail"));
+		return(KErrWrite);	
+		}
+	if (status&KAtaStatusErr)
+		{
+		__KTRACE_CARD_ERROR(KPBUSDRV,Kern::Printf("Ata:CheckForError-ERR fail"));
+		return KErrAta;
+		}
+	return(KErrNone);
+	}
+
+TInt DPcCardMediaDriverAta::ProcessError(TInt anError)
+//
+// An error has occured - lets get more information
+//
+	{
+
+	if (anError==KErrAta&&AtaRegister8(KAtaStatusRd8)&KAtaStatusErr)
+		{
+		TUint8 basic=AtaRegister8(KAtaErrorRd8);
+#if (defined(__EPOC32__) && (defined(_DEBUG) || defined(SHOW_CARD_ERRORS)))
+		TUint8 extended=0xFF;		// invalid
+		SetAtaRegister8(KAtaCmdRequestSense,KAtaCommandWr8);	// Issue command - request sense
+		Kern::NanoWait(400);					// Busy flag not asserted for 400ns
+		WaitForNotBusy();
+		if (!(AtaRegister8(KAtaStatusRd8)&KAtaStatusErr))
+			extended=AtaRegister8(KAtaErrorRd8);
+		__KTRACE_CARD_ERROR(KPBUSDRV,Kern::Printf("Ata:ProcessError-Basic:%xH Ext:%xH)",basic,extended));
+#endif 
+		if (basic&KAtaErrorUnc)
+			return(KErrDied);
+		else if (basic&(KAtaErrorBbk|KAtaErrorIdnf))
+			return(KErrCorrupt);
+		else if (basic&KAtaErrorAbort)
+			return(KErrCancel);
+		else
+			return(KErrUnknown);
+		}
+	else
+		return(anError);
+	}
+
+TInt DPcCardMediaDriverAta::EmptySectBufferToTrg(TUint8 *aSrc,TUint aTrgOffset,TInt aLen)
+//
+// Empty data from sector buffer (at specified offset within buffer) into
+// destination descriptor.
+//
+	{
+
+	TPtrC8 buf(aSrc,aLen);
+	return iCurrentReq->WriteRemote(&buf,aTrgOffset);
+	}
+
+TInt DPcCardMediaDriverAta::LoadSectBufferFromSrc(TInt aLen, TUint8* aBuf)
+//
+// Load data from source descriptor into sector buffer
+// Always called within exec. function.
+//
+	{
+
+	TPtr8 buf(aBuf,aLen);
+	TInt r=iCurrentReq->ReadRemote(&buf,iCmdInOffset);
+	return r;
+	}
+
+TInt DPcCardMediaDriverAta::ReadSectorsCommand(TUint aFirstSector,TUint aBufOffset,TInt aLen)
+//
+// Start off a read of multiple sectors from the drive (command completed under interrupt/dfc).
+//
+	{
+	__KTRACE_OPT(KPBUSDRV,Kern::Printf(">Ata:ReadSectors F:%x Off:%x L:%x",aFirstSector,aBufOffset,aLen));
+
+	__ASSERT_DEBUG(aBufOffset<(TUint)KAtaSectorSize,Kern::PanicCurrentThread(_L("ReadSectorsCommand"),0));
+	__ASSERT_DEBUG(aLen>0,Kern::PanicCurrentThread(_L("ReadSectorsCommand"),0));
+
+	// Sector count - allow for not starting on sector boundary by adding buffer offset.  Allow for
+	// not ending on sector boundary by adding sectorsize-1. Then divide by sector size.
+	TInt tferSectorCount=(aLen+aBufOffset+KAtaSectorSizeMinusOne)>>KAtaSectorShift;
+	iNextSector+=tferSectorCount;
+	__ASSERT_DEBUG(tferSectorCount<=KMaxSectorsPerRead,Kern::PanicCurrentThread(_L("ReadSectorsCommand"),0));
+
+	if (!iSocket->CardIsReady())
+		return(KErrNotReady);
+	AtaRegister8(KAtaStatusRd8);	// Clear any pending interrupt
+	iSocket->InterruptEnable(EPccdIntIReq,0); // Enable card interrupt
+	TInt err=IssueAtaCommand(KAtaCmdReadSectors,aFirstSector,tferSectorCount);
+	if (err!=KErrNone)
+		{
+		iSocket->InterruptDisable(EPccdIntIReq); // Disable card interrupt
+		iCardIreqDfc.Cancel();
+		return err;
+		}
+	iCommandError=KErrNone;
+	iNotBusyTickCount=KBusyTimeOut;
+	iBusyTimeout.OneShot(NKern::TimerTicks(KNotBusyTestInterval));
+	return(KErrNone);
+	}
+
+TInt DPcCardMediaDriverAta::InitiateWriteCommand(TUint aFirstSector,TInt aSectorCount,TUint8 *aSectBuffer)
+//
+// Start off an asynchronous write to the card. If successful, it leaves with a 
+// timeout(ms timer) queued and card interrupts enabled.
+//
+	{
+	__KTRACE_OPT(KPBUSDRV,Kern::Printf(">Ata:InitWrCmd F:%x C:%x",aFirstSector,aSectorCount));
+
+	__ASSERT_DEBUG((aSectorCount>0 && aSectorCount<=KMaxSectorsPerWrite),Kern::PanicCurrentThread(_L("InitiateWriteCommand"),0));
+
+	TInt err=IssueAtaCommand(KAtaCmdWriteSectors,aFirstSector,aSectorCount);
+	if (err!=KErrNone)
+		return(err);
+	if (!iSocket->CardIsReady())
+		return(KErrNotReady);
+	if (!WaitForNotBusy())
+		return(ProcessError(KErrTimedOut));
+	AtaRegister8(KAtaStatusRd8);	// Clear any pending interrupt
+	iSocket->InterruptEnable(EPccdIntIReq,0); // Enable card interrupt
+	err=EmptySectBufferToDrive(aSectBuffer);
+	if (err!=KErrNone)
+		{
+		iSocket->InterruptDisable(EPccdIntIReq); // Disable card interrupt
+		iCardIreqDfc.Cancel();
+		return(ProcessError(err));
+		}
+	iCommandError=KErrNone;
+	iNotBusyTickCount=KBusyTimeOut;
+	iBusyTimeout.OneShot(NKern::TimerTicks(KNotBusyTestInterval));
+	iNextSector+=aSectorCount;
+	return(KErrNone);
+	}
+
+TInt DPcCardMediaDriverAta::SectorBoundaryReadCheck(TUint aStartSector,TUint aStartSectOffset,Int64 anEndPos)
+//
+// Check for write request which doesn't lie entirely on a sector boundary and perform
+// the appropriate sectors reads prior to writing if necessary
+//
+	{
+
+	TInt err;
+	TUint endSectOffset=((TUint)anEndPos&(~KAtaSectorMask));
+	anEndPos-=1;
+	anEndPos>>=KAtaSectorShift;
+	TUint endSector=(TUint)anEndPos; // Sector number can't exceed 32bits
+	TInt sectors=(endSector-aStartSector)+1;
+	iLastSectorBufUsed=EFalse;
+
+	if (aStartSectOffset||endSectOffset)
+		{
+		// If it requires a read of two consecutive sectors then read them in one go
+		if (aStartSectOffset && endSectOffset && sectors==2)
+			return(SectorRead(aStartSector,&iSectorBuf[0],2,KAtaCmdReadSectors));
+		else 
+			{
+			// If write starts off a sector boundary or is a single sector write ending 
+			// off a sector boundary then read first sector
+			if (aStartSectOffset || (endSectOffset && sectors==1))
+				{
+				if ((err=SectorRead(aStartSector,&iSectorBuf[0]))!=KErrNone)
+					return(err);
+				}
+			// If write doesn't end on a sector boundary then read the last sector
+			if (endSectOffset && sectors>1)
+				{
+				TUint8* p=iSectorBuf;
+				if (sectors<=KMaxSectorsPerWrite)
+					p+=(sectors-1)<<KAtaSectorShift;
+				else
+					{
+					p=iLastSectorBuf;
+					iLastSectorBufUsed=ETrue;
+					}
+				return(SectorRead(endSector,p));
+				}
+			}
+		}
+	return(KErrNone);
+	}
+
+TInt DPcCardMediaDriverAta::SectorRead(TUint aFirstSector,TUint8 *aBuf,TInt aSectorCount,TUint8 aCmd)
+//
+// Read either 1 or 2 sectors into the sector buffer (synchronously)
+//
+	{
+
+	TInt err;
+	if ( (err=IssueAtaCommand(aCmd,aFirstSector,aSectorCount))==KErrNone)
+		{
+		if (
+			(err=TransferSectBufferFromDrive(aBuf))!=KErrNone||
+			(aSectorCount>1&&(err=TransferSectBufferFromDrive((TAny*)(aBuf+KAtaSectorSize)))!=KErrNone)||
+			(err=FinishCommand())!=KErrNone
+		   )
+			err=ProcessError(err);
+		}
+	if (!iSocket->CardIsReady())
+		err=KErrNotReady; // If media change - return not ready rather than anything else.
+	return(err);
+	}
+
+void DPcCardMediaDriverAta::DumpIdentifyDriveInfo()
+//
+// Debug option to display the drive indentification info.
+//
+	{
+#ifdef _DEBUG
+	if (KDebugNum(KPBUSDRV))
+		{
+		for (TInt i=0;i<128;i+=8)
+			{
+			Kern::Printf("%02x%02x %02x%02x %02x%02x %02x%02x",iSectorBuf[i],iSectorBuf[i+1],iSectorBuf[i+2],iSectorBuf[i+3],iSectorBuf[i+4],\
+						iSectorBuf[i+5],iSectorBuf[i+6],iSectorBuf[i+7]);
+			}
+		}
+#endif 
+	}
+
+TInt DPcCardMediaDriverAta::IdentifyDrive()
+//
+// Get drive charateristics
+//
+	{
+	__KTRACE_OPT(KPBUSDRV,Kern::Printf(">Ata:IdentifyDrive"));
+
+	TInt err;
+	if ((err=SectorRead(0,&iSectorBuf[0],1,KAtaCmdIdentifyDrive))!=KErrNone)
+		return(err);
+
+	DumpIdentifyDriveInfo();
+	TBool lba=EFalse;
+	Int64 s=0;
+	if (*(TUint16*)(&iSectorBuf[KAtaIdCapabilities])&KAtaIdCapLbaSupported)
+		{
+		// Card indicates it supports LBA
+		// Media size (in sectors) - 2 x halfwords @ KAtaIdTotalSectorsInLba.
+		iDriveInfo.iTotalSectorsInLba=*(TInt*)(&iSectorBuf[KAtaIdTotalSectorsInLba]);
+		s=iDriveInfo.iTotalSectorsInLba;
+		s<<=KAtaSectorShift;
+		if (s>0)
+			lba=ETrue; // Epson PC card reports LBA supported but LBA size in bytes becomes zero
+		} 
+
+	iDriveInfo.iSupportsLba = lba;
+	if (!lba)
+		{
+		// LBA not supported
+		if (*(TUint16*)(&iSectorBuf[KAtaIdTranslationParams])&KAtaIdTrParamsValid)
+			{
+			// Current translation parameters are valid
+			iDriveInfo.iCylinders=*(TUint16*)(&iSectorBuf[KAtaIdCurrentCylinders]);
+			iDriveInfo.iHeads=*(TUint16*)(&iSectorBuf[KAtaIdCurrentHeads]);
+			iDriveInfo.iSectorsPerTrack=*(TUint16*)(&iSectorBuf[KAtaIdCurrentSectorsPerTrack]);
+			}
+		else
+			{ 
+			// Use defaults
+			iDriveInfo.iCylinders=*(TUint16*)(&iSectorBuf[KAtaIdDefaultCylinders]);
+			iDriveInfo.iHeads=*(TUint16*)(&iSectorBuf[KAtaIdDefaultHeads]);
+			iDriveInfo.iSectorsPerTrack=*(TUint16*)(&iSectorBuf[KAtaIdDefaultSectorsPerTrack]);
+			} 
+		iDriveInfo.iSectorsPerCylinder=(iDriveInfo.iHeads*iDriveInfo.iSectorsPerTrack);
+		s=iDriveInfo.iCylinders;
+		s*=iDriveInfo.iSectorsPerCylinder;
+		s<<=KAtaSectorShift;
+		if (iDriveInfo.iSectorsPerCylinder<=0||iDriveInfo.iSectorsPerTrack<=0)
+			return(KErrCorrupt);
+		}
+
+	__KTRACE_OPT(KPBUSDRV,Kern::Printf("LBA      : %xH",iDriveInfo.iSupportsLba));
+	__KTRACE_OPT(KPBUSDRV,Kern::Printf("Cylinders: %xH",iDriveInfo.iCylinders));
+	__KTRACE_OPT(KPBUSDRV,Kern::Printf("Heads    : %xH",iDriveInfo.iHeads));
+	__KTRACE_OPT(KPBUSDRV,Kern::Printf("Sectors  : %xH",iDriveInfo.iSectorsPerTrack));
+	__KTRACE_OPT(KPBUSDRV,Kern::Printf("<Ata:IdentifyDrive (TS:%lxH)",s));
+	SetTotalSizeInBytes(s);
+	return(KErrNone);
+	}
+
+TInt DPcCardMediaDriverAta::ConfigAutoPowerDown()
+//
+// Set auto power down period to something sensible
+//
+	{
+
+	TInt err;
+	if ( (err=IssueAtaCommand(KAtaCmdIdle,0,200))==KErrNone) // 200x5mS=1S (aFirstSector doesn't matter).
+		{
+		if ((err=FinishCommand())!=KErrNone)
+			err=ProcessError(err);
+		}
+	__KTRACE_OPT(KPBUSDRV,Kern::Printf("<Ata:ConfigAutoPowerDown-%d",err));
+	return(err);
+	}
+
+TInt DPcCardMediaDriverAta::DoOpen()
+//
+// Open the media driver.
+//
+	{
+
+	__KTRACE_OPT(KPBUSDRV,Kern::Printf(">Ata:DoOpen"));
+	iCardIreqDfc.SetDfcQ(&iSocket->iDfcQ);
+	iTimerDfc.SetDfcQ(&iSocket->iDfcQ);
+
+	// Card ought to be ready but check we haven't had media change (this causes creation of card functions).
+	TInt err=iSocket->CardIsReadyAndVerified();	// this may also fail with OOM or corrupt
+	if (err!=KErrNone)
+		return err;
+
+	// Perform CIS validation - get back info. on card functions present. 
+	TPccdType pt;
+	if (iSocket->VerifyCard(pt)!=KErrNone)
+		return(KErrUnknown);
+
+	__KTRACE_OPT(KPBUSDRV,Kern::Printf("Ata:DoOpen-Check for ATA function, FuncCount=%d",pt.iFuncCount));
+
+	// Check for ATA fixed disk function.
+	TCisReader cisRd; 
+	TInt f;
+	TBool isAta=EFalse;
+	for (f=0;f<pt.iFuncCount;f++)
+		{
+		if (pt.iFuncType[f]==EFixedDiskCard)
+			{
+			// Fixed disk function - check its an ATA. 
+			TBuf8<KLargeTplBufSize> tpl;
+			cisRd.SelectCis(iSocket->iSocketNumber,f);
+			// Must start just after FuncId tuple.
+			if (cisRd.FindReadTuple(KCisTplFuncId,tpl)!=KErrNone)
+				continue;
+			while (cisRd.FindReadTuple(KCisTplFunce,tpl)==KErrNone)
+				{
+				if (tpl[2]==0x01 && tpl[3]==0x01)	// Interface type - ATA
+					isAta=ETrue;
+				}
+			if (isAta)
+				break;
+			}
+		}
+	if (!isAta)
+		return(KErrUnknown);
+	__KTRACE_OPT(KPBUSDRV,Kern::Printf("Ata:DoOpen-Is ATA"));
+
+	// Determine best card configuration
+	cisRd.Restart();
+	TPcCardConfig cf;
+	TInt opt=KInvalidConfOpt;
+	while(cisRd.FindReadConfig(cf)==KErrNone)
+		{
+		if (
+			 cf.IsMachineCompatible(iSocket->iSocketNumber,KPccdCompatNoVccCheck) && // Hitachi card has no 3.3V config entry
+#if defined (SELECT_MEMORY_CONFIG)
+			 (cf.iValidChunks==1 && __IS_COMMON_MEM(cf.iChnk[0].iMemType) && cf.iChnk[0].iMemLen==0x800)
+#elif defined (SELECT_PRIMARY_IO_CONFIG)
+			 (cf.iValidChunks==2 && __IS_IO_MEM(cf.iChnk[0].iMemType) && __IS_IO_MEM(cf.iChnk[1].iMemType) && cf.iChnk[0].iMemBaseAddr==0x1F0)
+#else
+			 // Choose 16 byte -  contiguous i/o option
+			 (cf.iValidChunks==1 && __IS_IO_MEM(cf.iChnk[0].iMemType) && cf.iChnk[0].iMemLen==0x10)
+#endif
+		   ) 
+			{
+			opt=cf.iConfigOption;
+			break;
+			}
+		}
+	if (opt==KInvalidConfOpt)
+		return(KErrNotSupported);
+	__KTRACE_OPT(KPBUSDRV,Kern::Printf("Ata:DoOpen-ConfigOpt(%d)",opt));
+	__KTRACE_OPT(KPBUSDRV,Kern::Printf("Ata:DoOpen-Pulsed Ints-%d",(cf.iInterruptInfo&KPccdIntPulse)));
+
+	// Now configure the card. First configure it to memory mode. 
+	cf.iConfigOption=0;
+	err=iSocket->RequestConfig(f,this,cf,0);
+	if (err!=KErrNone)
+		return(err);
+	if (cf.iRegPresent&KConfigAndStatusRegM)
+		iSocket->WriteConfigReg(f,KConfigAndStatusReg,0); 
+	if (cf.iRegPresent&KSocketAndCopyRegM)
+		iSocket->WriteConfigReg(f,KSocketAndCopyReg,0);	// No twin cards
+	iSocket->ReleaseConfig(f,this);
+
+	cf.iConfigOption=(cf.iInterruptInfo&KPccdIntPulse)?opt:(opt|KConfOptLevIReqM);
+//	cf.iConfigOption=(opt|KConfOptLevIReqM); // Force level mode interrupts
+#if defined (FORCE_8BIT_ACCESSES)
+#if defined (SELECT_MEMORY_CONFIG)
+	cf.iChnk[0].iMemType=EPccdCommon8Mem;	// Force it to 8bit Common.
+#else
+	cf.iChnk[0].iMemType=EPccdIo8Mem;		// Force it to 8bit I/O.
+#endif
+#endif
+
+	if ((err=iSocket->RequestConfig(f,this,cf,0))!=KErrNone)
+		return(err);
+
+	// Read back the config option register to verify it has been setup
+	TUint8 v;
+	if ((err=iSocket->ReadConfigReg(f,0,v))!=KErrNone||v!=(TUint8)cf.iConfigOption)
+		return KErrBadPccdConfig;
+	iCardFuncNum=f;
+	__KTRACE_OPT(KPBUSDRV,Kern::Printf("Ata:DoOpen-Configured(%1xH) OK",v));
+
+	TUint flag=(cf.iActiveSignals&KSigWaitRequired)?KPccdRequestWait:0;
+	err=iDriveChunk.Create(iSocket,cf.iChnk[0],cf.iAccessSpeed,flag);
+	if (err!=KErrNone)
+		return(err);
+	iDriveChunk.SetupChunkHw();
+	iMemoryType=cf.iChnk[0].iMemType;
+	__KTRACE_OPT(KPBUSDRV,Kern::Printf("Ata:DoOpen-Requested Mem OK"));
+
+	iSocket->InterruptDisable(EPccdIntIReq);
+	iCardIntCallBack.iSocket=iSocket;
+	iCardIntCallBack.Add();
+	SetAtaRegister8(0,KAtaDeviceCtlWr8);	// Enable interrupts
+
+	SetCurrentConsumption(KIdleCurrentInMilliAmps);
+	__KTRACE_OPT(KPBUSDRV,Kern::Printf("<Ata:DoOpen-OK"));
+	return(KErrNone);
+	}
+
+TBool DPcCardMediaDriverAta::CardPoweredDown()
+//
+// Returns EFalse as long as card hasn't received power down (emergency or normal).
+//
+	{
+
+	// Emergency power down may result in card being powered down before PC Card
+	// Controller status is updated - check for EPD as well as PC Card Contoller.
+	return(!Kern::PowerGood() || !iSocket->CardIsReady());
+	}
+
+void DPcCardMediaDriverAta::IncrementSectBufPtr()
+//
+//
+//
+	{
+
+	iSectBufPtr+=KAtaSectorSize;
+	if ((iSectBufPtr-&iSectorBuf[0])>=KSectBufSizeInBytes)
+		iSectBufPtr=&iSectorBuf[0];
+	}
+
+TInt DPcCardMediaDriverAta::Request(TLocDrvRequest& m)
+	{
+	__KTRACE_OPT(KLOCDRV,Kern::Printf("Ata:Req %08x id %d",&m,m.Id()));
+	TInt r=KErrNotSupported;
+	TInt id=m.Id();
+	if (id==DLocalDrive::ECaps)
+		{
+		TLocalDriveCapsV6& c=*(TLocalDriveCapsV6*)m.RemoteDes();
+		r=Caps(c);
+		c.iSize=m.Drive()->iPartitionLen;
+		c.iPartitionType=m.Drive()->iPartitionType;
+		return r;
+		}
+	if (iCurrentReq)
+		{
+		// a request is already in progress, so hold on to this one
+		__KTRACE_OPT(KLOCDRV,Kern::Printf("Ata:Req %08x ret 1",&m));
+		return KMediaDriverDeferRequest;
+		}
+	iCurrentReq=&m;
+	switch (id)
+		{
+		case DLocalDrive::ERead:
+			r=DoRead();
+			break;
+		case DLocalDrive::EWrite:
+			r=DoWrite();
+			break;
+		case DLocalDrive::EFormat:
+			r=DoFormat();
+			break;
+		case DLocalDrive::EEnlarge:
+		case DLocalDrive::EReduce:
+		default:
+			r=KErrNotSupported;
+			break;
+		}
+	__KTRACE_OPT(KLOCDRV,Kern::Printf("Ata:Req %08x cmp %d",&m,r));
+	if (r!=KErrNone)
+		iCurrentReq=NULL;
+	return r;
+	}
+
+void DPcCardMediaDriverAta::NotifyPowerDown()
+	{
+	__KTRACE_OPT(KPBUSDRV,Kern::Printf(">Ata:NotifyPowerDown"));
+	Complete(KErrNotReady);
+	Reset();
+	}
+
+void DPcCardMediaDriverAta::NotifyEmergencyPowerDown()
+	{
+	__KTRACE_OPT(KPBUSDRV,Kern::Printf(">Ata:NotifyEmergencyPowerDown"));
+	TInt r=KErrNotReady;
+	if (iCritical)
+		r=KErrAbort;
+	EndInCritical();
+	Complete(r);
+	Reset();
+	}
+
+void DPcCardMediaDriverAta::TimerDfcFunction(TAny* aPtr)
+//
+// DFC callback from not-busy timer
+//
+	{
+	DPcCardMediaDriverAta &md=*(DPcCardMediaDriverAta*)aPtr;
+	__KTRACE_OPT2(KPBUSDRV,KFAIL,Kern::Printf(">Ata:TimerDfcFunction TC:%d",md.iNotBusyTickCount));
+	TInt r=1;
+	if (!md.CardPoweredDown())
+		{
+		md.iDriveChunk.SetupChunkHw();
+		TUint8 status=md.AtaRegister8(KAtaStatusRd8); // Clears interrupt
+		if (!(status&KAtaStatusBusy))
+			{
+			// It is not-busy so process it as we would from an interrupt
+			__KTRACE_OPT2(KPBUSDRV,KFAIL,Kern::Printf("Card not busy"));
+			r=KErrNone;
+#ifdef COUNT_TIMEOUTS
+			++md.iTimeouts;
+			md.iNewTimeOut=ETrue;
+			if (md.iIndex<=6)
+				{
+				md.iInfo[md.iIndex++]=md.iCmdInOffset;
+				md.iInfo[md.iIndex++]=md.iCmdEndOffset;
+				}
+#endif
+			}
+		else
+			{
+			// still busy so count tick and restart timer
+			if (--md.iNotBusyTickCount==0)
+				{
+				__KTRACE_OPT2(KPBUSDRV,KFAIL,Kern::Printf("Timed out"));
+				r=KErrTimedOut;		// time out the request
+				}
+			else
+				{
+				__KTRACE_OPT2(KPBUSDRV,KFAIL,Kern::Printf("Restart timer"));
+				md.iBusyTimeout.OneShot(NKern::TimerTicks(KNotBusyTestInterval));
+				}
+			}
+		}
+	else
+		r=KErrNotReady;
+	__KTRACE_OPT2(KPBUSDRV,KFAIL,Kern::Printf("<Ata:TimerDfcFunction r=%d",r));
+	if (r<=0)
+		{
+		// card is not-busy, powered down or timed out
+		md.iSocket->InterruptDisable(EPccdIntIReq); // Disable IREQ in controller
+		md.iCardIreqDfc.Cancel();	// so we don't run this twice
+		md.iCommandError=r;
+		CardIreqDfcFunction(&md);
+		}
+	}
+
+void DPcCardMediaDriverAta::CardIntCallBack(TAny* aMediaDriver, TInt)
+//
+// Card Interrupt callback
+//
+	{
+	__TRACE_TIMING(0x104);
+	DPcCardMediaDriverAta &md=*(DPcCardMediaDriverAta*)aMediaDriver;
+	md.iSocket->InterruptDisable(EPccdIntIReq); // Disable IREQ in controller
+	md.iCardIreqDfc.Add();
+#ifdef COUNT_TIMEOUTS
+	++md.iInts;
+#endif
+	}
+
+void DPcCardMediaDriverAta::CardIreqDfcFunction(TAny* aPtr)
+	{
+
+	DPcCardMediaDriverAta &md=*(DPcCardMediaDriverAta*)aPtr;
+	TInt err=md.iCommandError;
+	TBool queueDfc=md.CardPoweredDown();
+	if (queueDfc)
+		err=KErrNotReady;
+	else if (err!=KErrNone)
+		queueDfc=ETrue;		// timed out
+	else
+		{
+		md.iDriveChunk.SetupChunkHw();
+		TUint8 status=md.AtaRegister8(KAtaStatusRd8); // Clears interrupt
+		if (!(status&KAtaStatusBusy))
+			queueDfc=md.DoCardNotBusy(err);
+		}
+	TBool cmd_done=EFalse;
+	if (queueDfc)
+		{
+		md.iCommandError=err;
+		cmd_done=md.CmdDfc();
+		}
+	if (!queueDfc)
+		{
+		// command still executing so reenable interrupts
+		md.iSocket->InterruptEnable(EPccdIntIReq,0);
+		}
+	if (!cmd_done)
+		{
+		// quickly check if card is already not busy
+		// this handles cards which return data very quickly
+		md.iDriveChunk.SetupChunkHw();
+		TUint8 status=md.AtaRegister8(KAtaStatusRd8); // Clears interrupt
+		if (!(status&KAtaStatusBusy))
+			{
+			md.iSocket->InterruptDisable(EPccdIntIReq);
+			md.iCardIreqDfc.Enque();
+#ifdef COUNT_TIMEOUTS
+			++md.iImmediateNotBusy;
+#endif
+			}
+		else
+			{
+			md.iBusyTimeout.Cancel();
+			md.iTimerDfc.Cancel();
+			md.iBusyTimeout.OneShot(NKern::TimerTicks(KNotBusyTestInterval));
+			}
+		}
+	}
+
+TBool DPcCardMediaDriverAta::DoCardNotBusy(TInt &anErr)
+//
+// Card not busy interrupt - return ETrue when need to queue dfc
+//
+	{
+
+	anErr=KErrNone;
+	if (iCardStatus==ECardWrite || iCardStatus==ECardFormat)
+		{
+		// For write/format commands we only update the out-pointer when the card becomes
+		// not-busy following the point we transfered the block of data to the card.
+		iCmdOutOffset+=(KAtaSectorSize-iSectBufOffset);
+		iSectBufOffset=0;
+		if (iCmdOutOffset<iCmdEndOffset)
+			{
+			TUint8* pB=iSectBufPtr;
+			if (iLastSectorBufUsed && iCmdLength-iCmdOutOffset<=(TUint)KAtaSectorSize)
+				pB=iLastSectorBuf;	// use different buffer for last sector of unaligned write
+			anErr=EmptySectBufferToDrive(pB);
+			if (anErr!=KErrNone)
+				return(ETrue);
+			if (iCardStatus==ECardWrite)
+				iSectBufPtr+=KAtaSectorSize;
+			}
+		else
+			{
+			anErr=CheckForError();
+			return(ETrue);
+			}
+		}
+	if (iCardStatus==ECardRead)
+		{
+		// For read commands we only update the in-pointer here. The out-pointer is
+		// updated by the DFC as data is written to target thread
+
+		// Before we read the data from the card, perform the in-pointer incremeting now. 
+		// If we are about to queue a DFC then let the DFC read from the card. Otherwise,
+		// reading from the card may result in the next sector of data being available
+		// before we're ready for it.
+
+		// If first interrupt after command initiated then allow for sector buffer offset
+		TInt toEOBuf=(iCmdInOffset==0)?(KAtaSectorSize-iSectBufOffset):KAtaSectorSize;
+		TInt remaining=(TInt)(iCmdEndOffset-iCmdInOffset);
+		iCmdInOffset+=Min(remaining,toEOBuf);
+		if ((iSectBufPtr-&iSectorBuf[0])>=KSectBufSizeInBytesMinusOneSector || iCmdInOffset>=iCmdEndOffset)
+			return(ETrue); // Queue a DFC
+		else
+			{
+			anErr=LoadSectBufferFromDrive(iSectBufPtr);
+			if (anErr!=KErrNone)
+				return(ETrue);
+			IncrementSectBufPtr();
+			}
+		}
+	return(EFalse);
+	}
+
+void DPcCardMediaDriverAta::SyncBusyTimerCallBack(TAny* aBusyFlag)
+//
+// Wait for not busy timeout callback
+//
+	{
+
+	*(TBool*)aBusyFlag=ETrue;
+	}
+
+void DPcCardMediaDriverAta::AsyncBusyTimerCallBack(TAny* aMediaDriver)
+//
+// Wait for not busy timeout callback
+//
+	{
+
+	__KTRACE_OPT(KFAIL,Kern::Printf("!,"));
+	DPcCardMediaDriverAta &md=*(DPcCardMediaDriverAta*)aMediaDriver;
+	md.iTimerDfc.Add();
+	}
+
+TBool DPcCardMediaDriverAta::CmdDfc()
+//
+// Dfc to complete an asynchronous command
+//
+	{
+
+	TInt err;
+
+	__KTRACE_OPT(KPBUSDRV,Kern::Printf(">Ata:CmdDfc status %d in %x out %x len %x",iCardStatus,iCmdInOffset,iCmdOutOffset,iCmdLength));
+
+	iBusyTimeout.Cancel();
+	iTimerDfc.Cancel();
+	if (DoCmdDfc(err))	// this requeues timer and reenables interrupts if command still executing
+		{
+		// Command has been completed for one reason or other
+		EndInCritical();
+		if (err!=KErrNone)
+			err=ProcessError(err);
+
+		// If emergency power down on write/format - return abort rather than anything else.
+		if (!Kern::PowerGood() && iCardStatus!=ECardRead)
+			err=KErrAbort;
+		// If media change - return not ready rather than anything else.
+		else if (!iSocket->CardIsReady())
+			err=KErrNotReady;
+		__KTRACE_OPT(KFAIL,Kern::Printf("a%d",err));
+		if (err==KErrNone)
+			{
+			// may need to issue another command here
+			if (iCmdOutOffset<iCmdLength)
+				{
+				if (iCardStatus==ECardRead)
+					{
+					__KTRACE_OPT(KFAIL,Kern::Printf("+R"));
+					__KTRACE_OPT(KPBUSDRV,Kern::Printf(">Ata:NewReadCmd"));
+					iSectBufOffset=0;
+#ifdef COUNT_TIMEOUTS
+					++iChainedReads;
+#endif
+					err=InitiateAsyncRead();
+					if (err==KErrNone)
+						return ETrue;
+					}
+				else
+					{
+					__KTRACE_OPT(KFAIL,Kern::Printf("+W"));
+					__KTRACE_OPT(KPBUSDRV,Kern::Printf(">Ata:NewWriteCmd"));
+#ifdef COUNT_TIMEOUTS
+					++iChainedWrites;
+#endif
+					err=InitiateAsyncWrite();
+					if (err==KErrNone)
+						return ETrue;
+					}
+				}
+			}
+		iCardStatus=ECardIdle;		// Command now complete
+		SetCurrentConsumption(KIdleCurrentInMilliAmps);
+
+#if (defined(_DEBUG) || defined(SHOW_CARD_ERRORS))
+		if (err!=KErrNone&&err!=KErrTooBig)
+			{
+			__KTRACE_CARD_ERROR(KPBUSDRV,Kern::Printf("<DFC(L:%d P:%xH)-%d",iDbgLen,iDbgPos,err));
+			iDbgLastError=err;
+			}
+#endif
+		__TRACE_TIMING(0x109);
+		Complete(err);
+		return ETrue;
+		}
+	return EFalse;
+	}
+
+TBool DPcCardMediaDriverAta::DoCmdDfc(TInt &anErr)
+//
+// Return ETrue when complete
+//
+	{
+
+	if (CardPoweredDown())
+		{
+		anErr=KErrNotReady;
+		return(ETrue);
+		}
+	anErr=iCommandError;
+	if (iCommandError!=KErrNone)
+		return(ETrue);
+
+	if (iCardStatus==ECardWrite||iCardStatus==ECardFormat)
+		return(ETrue);
+	else
+		{
+		// Read command, we postponed loading the last sector
+		__TRACE_TIMING(0x105);
+		if ((anErr=LoadSectBufferFromDrive(iSectBufPtr))!=KErrNone)
+			return(ETrue);
+
+		// In case command still executing, queue another timeout and re-enable interrupts
+		iSocket->InterruptEnable(EPccdIntIReq,0);
+		iNotBusyTickCount=KBusyTimeOut;
+		iBusyTimeout.OneShot(NKern::TimerTicks(KNotBusyTestInterval));
+
+		__TRACE_TIMING(0x106);
+		IncrementSectBufPtr();
+		if ((anErr=EmptySectBufferToTrg(&iSectorBuf[0]+iSectBufOffset,iCmdOutOffset,iCmdInOffset-iCmdOutOffset))!=KErrNone)
+			{
+			iSocket->InterruptDisable(EPccdIntIReq); // Disable IREQ in controller
+			iBusyTimeout.Cancel();
+			iTimerDfc.Cancel();
+			iCardIreqDfc.Cancel();
+			return(ETrue);
+			}
+		__TRACE_TIMING(0x107);
+		iSectBufOffset=0;		// From now on we always start on sector boundary
+		iCmdOutOffset=iCmdInOffset;
+		if (iCmdOutOffset<iCmdEndOffset)
+			return(EFalse);
+		iSocket->InterruptDisable(EPccdIntIReq); // Disable IREQ in controller
+		iBusyTimeout.Cancel();
+		iTimerDfc.Cancel();
+		iCardIreqDfc.Cancel();
+		anErr=FinishCommand();	// This functions involves polling for not busy
+		return(ETrue);
+		}
+	__TRACE_TIMING(0x108);
+	}
+
+DPhysicalDeviceMediaAta::DPhysicalDeviceMediaAta()
+//
+// Constructor
+//
+	{
+	iUnitsMask=0x1;
+	iVersion=TVersion(KMediaDriverInterfaceMajorVersion,KMediaDriverInterfaceMinorVersion,KMediaDriverInterfaceBuildVersion);
+	}
+
+TInt DPhysicalDeviceMediaAta::Install()
+//
+// Install the PC Card ATA Media PDD.
+//
+	{
+	return SetName(&KPddName);
+	}
+
+void DPhysicalDeviceMediaAta::GetCaps(TDes8 &/* aDes */) const
+//
+// Return the media drivers capabilities.
+//
+	{
+	}
+								 
+TInt DPhysicalDeviceMediaAta::Create(DBase*& aChannel, TInt aMediaId, const TDesC8* /*anInfo*/, const TVersion& aVer)
+//
+// Create a PC Card ATA media driver.
+//
+	{
+	if (!Kern::QueryVersionSupported(iVersion,aVer))
+		return KErrNotSupported;
+	DPcCardMediaDriverAta* pD=new DPcCardMediaDriverAta(aMediaId);
+	aChannel=pD;
+	TInt r=KErrNoMemory;
+	if (pD)
+		r=pD->Open();
+	if (r==KErrNone)
+		pD->OpenMediaDriverComplete(KErrNone);
+	return r;
+	}
+
+TInt DPhysicalDeviceMediaAta::Validate(TInt aDeviceType, const TDesC8* anInfo, const TVersion& aVer)
+	{
+	if (!Kern::QueryVersionSupported(iVersion,aVer))
+		return KErrNotSupported;
+	if (aDeviceType!=MEDIA_DEVICE_PCCARD)
+		return KErrNotSupported;
+	return KErrNone;
+	}
+
+TInt DPhysicalDeviceMediaAta::Info(TInt aFunction, TAny*)
+//
+// Return the priority of this media driver
+//
+	{
+	if (aFunction==EPriority)
+		return KAtaDriverPriority;
+	return KErrNotSupported;
+	}
+
+DPcCardMediaDriverAta::DPcCardMediaDriverAta(TInt aMediaId)
+//
+// Constructor.
+//
+	:	DMediaDriver(aMediaId),
+		iCardIntCallBack(CardIntCallBack,this,KPccdIntMaskIReq),
+		iBusyTimeout(AsyncBusyTimerCallBack,this),
+		iCardIreqDfc(CardIreqDfcFunction,this,2),
+		iTimerDfc(TimerDfcFunction,this,2)
+	{
+
+	iMemoryType=EPccdIo8Mem;
+//	iCommandError=KErrNone;
+//	TUint iCmdInOffset=0;
+//	TUint iCmdOutOffset=0;
+//	iCmdEndOffset=0;
+//	iSectBufPtr=NULL;
+//	iSectBufOffset=0;
+	iCardStatus=ECardIdle;
+//	iHiddenSectors=0;
+	iCardFuncNum=-1;
+	}
+
+TInt DPcCardMediaDriverAta::Open()
+//
+// Open the media driver.
+//
+	{
+
+	// Open the driver and get drive characteristics
+	iSocket=((DPcCardSocket*)((DPBusPrimaryMedia*)iPrimaryMedia)->iSocket);
+	__KTRACE_OPT(KPBUSDRV,Kern::Printf(">Ata:Open on socket %d",iSocket->iSocketNumber));
+	TInt r=DoOpen();
+	if (r==KErrNone)
+		r=IdentifyDrive();
+	if (r!=KErrNone)
+		{
+		DoClose();
+		if (!iSocket->CardIsReady())
+			r=KErrNotReady; // If media change - return not ready rather than anything else.
+		}
+	__KTRACE_OPT(KPBUSDRV,Kern::Printf("<Ata:Open-%d",r));
+	return r;
+	}
+
+TInt DPcCardMediaDriverAta::CheckDevice(TBool aCheckPower)
+//
+// Check the device before initiating a command
+//
+	{
+	
+	if (iSocket->CardIsReadyAndVerified()!=KErrNone)
+		return KErrNotReady;
+	if (aCheckPower && Kern::MachinePowerStatus()<ELow)
+		return KErrBadPower;
+	return KErrNone;
+	}
+
+TInt DPcCardMediaDriverAta::DoRead()
+//
+// Read from specified area of media.
+//
+	{
+
+	Int64 aPos=iCurrentReq->Pos();
+	TInt aLength=(TInt)iCurrentReq->Length();
+	TInt err;
+	iCmdOutOffset=0;	// Progress monitor - data delivered to target
+#if (defined(_DEBUG) || defined(SHOW_CARD_ERRORS))
+	iDbgPos=(TUint)aPos;
+	iDbgLen=aLength;
+#endif
+	err=CheckDevice(EFalse);
+	if (err==KErrNone)
+		{
+		iDriveChunk.SetupChunkHw(); // Enable our h/w chunk
+	
+		TUint sectorBufOffset=(TUint)aPos&(~KAtaSectorMask);
+		Int64 firstSector=(aPos>>KAtaSectorShift);
+		iCmdLength=aLength;
+
+		SetCurrentConsumption(KReadCurrentInMilliAmps);
+
+		// Start an asynchronous read
+		iCmdInOffset=0; 					// Data received from card
+		iNextSector=(TUint)firstSector;
+		iSectBufOffset=sectorBufOffset;
+		err=InitiateAsyncRead();
+		if (err==KErrNone)
+			return KErrNone;
+
+		iCardStatus=ECardIdle;
+		SetCurrentConsumption(KIdleCurrentInMilliAmps);
+		if (!iSocket->CardIsReady())
+			err=KErrNotReady; // If media change - return not ready rather than anything else.
+		}
+
+#if (defined(_DEBUG) || defined(SHOW_CARD_ERRORS))
+	__KTRACE_CARD_ERROR(KPBUSDRV,Kern::Printf("<Ata:Read(L:%d P:%xH)-%d",iDbgLen,iDbgPos,err));
+	iDbgLastError=err;
+#endif
+	return err;
+	}
+
+TInt DPcCardMediaDriverAta::InitiateAsyncRead()
+	{
+	// Start an asynchronous read
+	TInt cmdLen=Min(TInt(iCmdLength-iCmdInOffset),TInt(KMaxBytesPerRead-iSectBufOffset)); // Leave it on sector boundary if another read required
+	iCmdEndOffset=iCmdInOffset+cmdLen;	// Marks point when transfer is complete
+	iSectBufPtr=&iSectorBuf[0];
+	iCardStatus=ECardRead;
+	__TRACE_TIMING(0x102);
+	return ReadSectorsCommand(iNextSector,iSectBufOffset,cmdLen);	// Sector number can't exceed 32bits
+	}
+
+TInt DPcCardMediaDriverAta::DoWrite()
+//
+// Write to specified area of media.
+//
+	{
+
+	Int64 aPos=iCurrentReq->Pos();
+	TInt aLength=(TInt)iCurrentReq->Length();
+	TInt err;
+#if (defined(_DEBUG) || defined(SHOW_CARD_ERRORS))
+	iDbgPos=(TUint)aPos;
+	iDbgLen=aLength;
+#endif
+	err=CheckDevice(ETrue);
+	if (err==KErrNone)
+		{
+		iDriveChunk.SetupChunkHw(); // Enable our h/w chunk
+		TUint sectorBufOffset=(TUint)aPos&(~KAtaSectorMask);
+		Int64 firstSector=(aPos>>KAtaSectorShift);
+		iCmdLength=aLength;
+
+		// for unaligned writes, first need to read the first and/or last sector
+		err=SectorBoundaryReadCheck((TUint)firstSector,sectorBufOffset,aPos+iCmdLength);
+		if (err==KErrNone) // Sector number can't exceed 32bits
+			{
+			// Time to actually start the write. First alter the current consumption 
+			// and save the data required to complete the write (in the ISR)
+			SetCurrentConsumption(KWriteCurrentInMilliAmps);
+			iSectBufOffset=sectorBufOffset;
+			iCmdInOffset=0;
+			iCmdOutOffset=0;		// Progress monitor - data delivered to card
+			iNextSector=(TUint)firstSector;
+			iCardStatus=ECardWrite;
+			err=InitiateAsyncWrite();
+			if (err==KErrNone)
+				return KErrNone;
+			SetCurrentConsumption(KIdleCurrentInMilliAmps);
+			}
+		iCardStatus=ECardIdle;
+		if (!Kern::PowerGood())
+			err=KErrAbort; // If emergency power down - return abort rather than anything else.
+		else if (!iSocket->CardIsReady())
+			err=KErrNotReady; // If media change - return not ready rather than anything else.
+		}
+
+#if (defined(_DEBUG) || defined(SHOW_CARD_ERRORS))
+	__KTRACE_CARD_ERROR(KPBUSDRV,Kern::Printf("<Ata:Write(L:%d P:%xH)-%d",iDbgLen,iDbgPos,err));
+	iDbgLastError=err;
+#endif
+	return err;
+	}
+
+TInt DPcCardMediaDriverAta::InitiateAsyncWrite()
+	{
+	iCmdOutOffset=iCmdInOffset;
+	TInt remain=iCmdLength-iCmdInOffset;
+	TInt cmdLen=Min(remain, KMaxBytesPerWrite-iSectBufOffset);
+	TInt sectCount=(cmdLen+iSectBufOffset+KAtaSectorSize-1)>>KAtaSectorShift;
+	iCmdEndOffset=iCmdOutOffset+cmdLen;
+	iSectBufPtr=iSectorBuf;
+	TUint8* pB=iSectorBuf;
+	TInt r=KErrNone;
+	if (iCardStatus==ECardWrite)
+		{
+		if (iLastSectorBufUsed && cmdLen==remain)
+			{
+			// load data for sectors other than last into sector buffer
+			TInt lengthExcludingLastSector=cmdLen &~ (KAtaSectorSize-1);
+			if (lengthExcludingLastSector==cmdLen)
+				lengthExcludingLastSector-=KAtaSectorSize;
+			if (lengthExcludingLastSector)
+				{
+				r=LoadSectBufferFromSrc(lengthExcludingLastSector,iSectorBuf+iSectBufOffset);
+				if (r!=KErrNone)
+					return r;
+				iCmdInOffset+=lengthExcludingLastSector;	// make sure we get right data for last sector
+				}
+			else
+				pB=iLastSectorBuf;
+			// load last sector data into last sector buffer
+			r=LoadSectBufferFromSrc(cmdLen-lengthExcludingLastSector,iLastSectorBuf);
+			if (r!=KErrNone)
+				return r;
+			iCmdInOffset+=(cmdLen-lengthExcludingLastSector);
+			}
+		else
+			{
+			// Load the the data from source in one go
+			r=LoadSectBufferFromSrc(cmdLen,iSectorBuf+iSectBufOffset);
+			if (r!=KErrNone)
+				return r;
+			iCmdInOffset+=cmdLen;
+			}
+		}
+	else
+		iCmdInOffset+=cmdLen;	// format command
+	r=InCritical();		// this returns KErrNotReady if we are about to do postponed media change or power down
+	if (r==KErrNone)
+		{
+		r=InitiateWriteCommand(iNextSector,sectCount,pB);
+		__KTRACE_OPT(KPBUSDRV,Kern::Printf("InitWrCmd ret %d",r));
+		if (iCardStatus==ECardWrite)
+			iSectBufPtr+=KAtaSectorSize;
+		}
+	if (r!=KErrNone)
+		{
+		if (!Kern::PowerGood())
+			r=KErrAbort; // If emergency power down - return abort rather than anything else.
+		else if (!iSocket->CardIsReady())
+			r=KErrNotReady; // If media change - return not ready rather than anything else.
+		EndInCritical();
+		}
+	return r;
+	}
+
+TInt DPcCardMediaDriverAta::DoFormat()
+//
+// Format the specified area of the media. 
+//
+	{
+	
+	Int64 aPos=iCurrentReq->Pos();
+	TInt aLength=(TInt)iCurrentReq->Length();
+	TInt err;
+#if (defined(_DEBUG) || defined(SHOW_CARD_ERRORS))
+	iDbgPos=(TUint)aPos;
+	iDbgLen=aLength;
+#endif
+	err=CheckDevice(ETrue);
+	if (err==KErrNone)
+		{
+		iDriveChunk.SetupChunkHw(); // Enable our h/w chunk
+		memset(iSectorBuf,0xff,KAtaSectorSize);
+		Int64 firstSector=(aPos>>KAtaSectorShift);
+		TInt sectCount=(aLength+KAtaSectorSizeMinusOne)>>KAtaSectorShift;
+		iCmdLength=(sectCount<<KAtaSectorShift);
+		sectCount=Min(KMaxSectorsPerFormat,(aLength+KAtaSectorSizeMinusOne)>>KAtaSectorShift);
+		SetCurrentConsumption(KWriteCurrentInMilliAmps);
+
+		iLastSectorBufUsed=EFalse;
+		iCmdInOffset=0;
+		iCmdOutOffset=0;		// Progress monitor - data delivered to card
+		iSectBufOffset=0;
+		iSectBufPtr=&iSectorBuf[0];
+		iNextSector=(TUint)firstSector;
+		iCardStatus=ECardFormat;
+		err=InitiateAsyncWrite();
+		if (err==KErrNone)
+			return KErrNone;
+
+		iCardStatus=ECardIdle;
+		SetCurrentConsumption(KIdleCurrentInMilliAmps);
+
+		if (!Kern::PowerGood())
+			err=KErrAbort; // If emergency power down - return abort rather than anything else.
+		else if (!iSocket->CardIsReady())
+			err=KErrNotReady; // If media change - return not ready rather than anything else.
+		}
+
+#if (defined(_DEBUG) || defined(SHOW_CARD_ERRORS))
+	__KTRACE_CARD_ERROR(KPBUSDRV,Kern::Printf("<Ata:Format(L:%d P:%xH)-%d",iDbgLen,iDbgPos,err));
+	iDbgLastError=err;
+#endif
+	return err;
+	}
+
+void DPcCardMediaDriverAta::Close()
+//
+// Close the media driver - also called on media change
+//
+	{
+
+	__KTRACE_OPT(KPBUSDRV,Kern::Printf(">Ata:Close"));
+	EndInCritical();
+	Complete(KErrNotReady);
+	DoClose();
+	DMediaDriver::Close();
+	}
+
+void DPcCardMediaDriverAta::DoClose()
+//
+// Close the media driver
+//
+	{
+
+	__KTRACE_OPT(KPBUSDRV,Kern::Printf(">Ata:DoClose"));
+	iCardIntCallBack.Remove();
+	iDriveChunk.Close();
+	if (iCardFuncNum>=0)
+		{
+		iSocket->ReleaseConfig(iCardFuncNum,this);
+		iCardFuncNum=-1;
+		}
+	Reset();
+	__KTRACE_CARD_ERROR(KPBUSDRV,Kern::Printf("<Ata:DoClose(%d)",iDbgLastError));
+	}
+
+void DPcCardMediaDriverAta::Reset()
+	{
+	__KTRACE_OPT(KPBUSDRV,Kern::Printf(">Ata:Reset"));
+	iBusyTimeout.Cancel();			// In case its currently queued
+	iCardIreqDfc.Cancel();			// In case its currently queued
+	iTimerDfc.Cancel();
+	iCardStatus=ECardIdle;
+	SetCurrentConsumption(0);
+	}
+
+#ifdef KLOCDRV
+void DebugDump(const TMBRPartitionEntry& a)
+	{
+	Kern::Printf("BootInd =%02x StartHead=%02x StartSect=%02x StartCyl=%02x",
+			a.iX86BootIndicator, a.iStartHead, a.iStartSector, a.iStartCylinder);
+	Kern::Printf("PartType=%02x EndHead  =%02x EndSect  =%02x EndCyl  =%02x",
+			a.iPartitionType, a.iEndHead, a.iEndSector, a.iEndCylinder);
+	Kern::Printf("FirstSect=%08x NumSectors=%08x", a.iFirstSector, a.iNumSectors);
+	}
+
+void DebugDump(const TPartitionInfo& a)
+	{
+	Kern::Printf("PartitionInfo: (C:%d)",a.iPartitionCount);
+	TInt i;
+	for (i=0; i<KMaxPartitionEntries; ++i)
+		{
+		const TPartitionEntry& e=a.iEntry[i];
+		Kern::Printf("   Partition %d: B=%lxH L=%lxH I=%04x T=%04x", i, e.iPartitionBaseAddr,
+						e.iPartitionLen, e.iBootIndicator, e.iPartitionType );
+		}
+	}
+#endif
+
+void SetPartitionEntry(TPartitionEntry* aDest, const TMBRPartitionEntry* aSrc)
+//
+// Set the partition entry details
+//
+	{
+
+	aDest->iPartitionBaseAddr=aSrc->iFirstSector;
+	aDest->iPartitionBaseAddr<<=KAtaSectorShift;
+	aDest->iPartitionLen=aSrc->iNumSectors;
+	aDest->iPartitionLen<<=KAtaSectorShift;
+	aDest->iBootIndicator=aSrc->iX86BootIndicator;
+	aDest->iPartitionType=aSrc->iPartitionType;
+	}
+
+TInt DPcCardMediaDriverAta::PartitionInfo(TPartitionInfo& anInfo)
+//
+// Return partition information on the media.
+//
+	{
+
+	__KTRACE_OPT(KPBUSDRV,Kern::Printf(">Ata:PartitionInfo"));
+	if (iSocket->CardIsReadyAndVerified()!=KErrNone)
+		return(KErrNotReady);
+	TInt partitionCount=anInfo.iPartitionCount=0;
+
+	// Read the first sector and check for a Master Boot Record
+	TInt err;
+	if ((err=SectorRead(0,&iSectorBuf[0]))!=KErrNone)
+		return(err);
+	if (*(TUint16*)(&iSectorBuf[KMBRSignatureOffset])!=KMBRSignature)
+		return(KErrCorrupt);
+
+	// Move the partition entries to a 4 byte boundary
+	memmove(&iSectorBuf[0],&iSectorBuf[KMBRFirstPartitionOffset],(sizeof(TMBRPartitionEntry)*KMBRMaxPrimaryPartitions));
+
+	// Search for a x86 default boot partition - let this be the first
+	TMBRPartitionEntry *pe=(TMBRPartitionEntry*)(&iSectorBuf[0]);
+	TInt i;
+	TInt defaultPartitionNumber=-1;
+	for (i=0;i<KMBRMaxPrimaryPartitions;i++,pe++)
+		{
+		if (pe->IsDefaultBootPartition())
+			{
+			SetPartitionEntry(anInfo.iEntry, pe);
+			defaultPartitionNumber=i;
+			iHiddenSectors=pe->iFirstSector;
+			partitionCount++;
+			break;
+			}
+		}
+
+	// Now add any other partitions
+	pe=(TMBRPartitionEntry*)(&iSectorBuf[0]);	// Reset it
+	for (i=0;i<KMBRMaxPrimaryPartitions;i++,pe++)
+		{
+		__KTRACE_OPT(KLOCDRV, Kern::Printf("Partition %d:",i));
+		__KTRACE_OPT(KLOCDRV, DebugDump(*pe));
+		if (defaultPartitionNumber==i)
+			continue;	// Already sorted
+		if (pe->IsValidDosPartition() || pe->IsValidFAT32Partition())
+			{
+			SetPartitionEntry(anInfo.iEntry+partitionCount, pe);
+			partitionCount++;
+			}
+		}
+	anInfo.iPartitionCount=partitionCount;
+	anInfo.iMediaSizeInBytes=TotalSizeInBytes();
+
+	__KTRACE_OPT(KLOCDRV, DebugDump(anInfo));
+
+	PartitionInfoComplete(KErrNone);
+	return KErrNone;
+	}
+
+TInt DPcCardMediaDriverAta::Caps(TLocalDriveCapsV6& aInfo)
+//
+// Return the capabilities of the media
+//
+	{
+	aInfo.iType=EMediaHardDisk;
+	aInfo.iBattery=EBatNotSupported;
+	aInfo.iDriveAtt=KDriveAttLocal|KDriveAttRemovable;
+	aInfo.iMediaAtt=KMediaAttFormattable;
+	aInfo.iFileSystemId=KDriveFileSysFAT;
+	aInfo.iHiddenSectors=iHiddenSectors;
+	aInfo.iBlockSize=KAtaSectorSize;
+	return KErrCompletion;	// synchronous completion
+	}
+
+DECLARE_STANDARD_PDD()
+	{
+	return new DPhysicalDeviceMediaAta;
+	}
+