Fix for bug 2283 (RVCT 4.0 support is missing from PDK 3.0.h)
Have multiple extension sections in the bld.inf, one for each version
of the compiler. The RVCT version building the tools will build the
runtime libraries for its version, but make sure we extract all the other
versions from zip archives. Also add the archive for RVCT4.
// 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.iConnectionBusType=EConnectionBusInternal;
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;
}