// 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;
SetTotalSizeInBytes(aInfo);
return KErrCompletion; // synchronous completion
}
DECLARE_STANDARD_PDD()
{
return new DPhysicalDeviceMediaAta;
}