IOATAHDCommand.cpp [plain text]
#include <IOKit/assert.h>
#include <IOKit/IOSyncer.h>
#include <IOKit/storage/ata/IOATAHDDrive.h>
bool
IOATAHDDrive::selectTimingProtocol()
{
bool ret;
UInt8 ataReadCmd;
UInt8 ataWriteCmd;
ATATimingProtocol timing;
char * protocolName;
ret = _ataDevice->getTimingsSupported(&timing);
if (ret == false)
{
IOLog("%s: getTimingsSupported() error\n", getName());
timing = kATATimingPIO;
}
if (timing & (kATATimingUltraDMA66 | kATATimingUltraDMA33 | kATATimingDMA))
{
if (timing & kATATimingUltraDMA66)
{
protocolName = "U-DMA/66";
timing = kATATimingUltraDMA66;
}
else if (timing & kATATimingUltraDMA33)
{
protocolName = "U-DMA/33";
timing = kATATimingUltraDMA33;
}
else
{
protocolName = "DMA";
timing = kATATimingDMA;
}
selectCommandProtocol(true);
switch ( _ataProtocol )
{
case kATAProtocolDMAQueued:
ataReadCmd = kIOATACommandReadDMAQueued;
ataWriteCmd = kIOATACommandWriteDMAQueued;
break;
case kATAProtocolDMA:
default:
ataReadCmd = kIOATACommandReadDMA;
ataWriteCmd = kIOATACommandWriteDMA;
}
}
else
{
protocolName = "PIO";
timing = kATATimingPIO;
ataReadCmd = kIOATACommandReadPIO;
ataWriteCmd = kIOATACommandWritePIO;
selectCommandProtocol(false);
}
_timingProtocol = timing;
_ataReadCmd = ataReadCmd;
_ataWriteCmd = ataWriteCmd;
ret = true;
ret = _ataDevice->selectTiming( _timingProtocol, false );
if (ret == false)
{
IOLog("%s: %s selectTiming() failed\n", getName(), protocolName);
if (_timingProtocol != kATATimingPIO)
{
protocolName = "PIO";
_timingProtocol = kATATimingPIO;
_ataReadCmd = kIOATACommandReadPIO;
_ataWriteCmd = kIOATACommandWritePIO;
selectCommandProtocol(false);
ret = _ataDevice->selectTiming( _timingProtocol, false );
if (ret == false)
IOLog("%s: %s selectTiming() retry failed\n",
getName(), protocolName);
}
}
if (ret && _logSelectedTimingProtocol)
IOLog("%s: Using %s transfers\n", getName(), protocolName);
return ret;
}
bool
IOATAHDDrive::selectCommandProtocol(bool isDMA)
{
ATAProtocol protocolsSupported;
if ( _ataDevice->getProtocolsSupported( &protocolsSupported ) == false )
{
IOLog("%s: getProtocolsSupported() failed\n", getName());
return false;
}
if ( (protocolsSupported & kATAProtocolDMAQueued) != 0 )
{
#if 0
_ataProtocol = kATAProtocolDMAQueued;
#else
_ataProtocol = kATAProtocolDMA;
#endif
}
else if ( (protocolsSupported & kATAProtocolDMA) != 0 )
{
_ataProtocol = kATAProtocolDMA;
}
else
{
_ataProtocol = kATAProtocolPIO;
}
return true;
}
bool
IOATAHDDrive::configureDevice(IOATADevice * device)
{
bool ret;
ret = device->selectTiming( _timingProtocol, true );
if (ret == false) {
IOLog("%s: selectTiming() failed\n", getName());
return false;
}
return true;
}
void
IOATAHDDrive::setupReadWriteTaskFile(ATATaskfile * taskfile,
ATAProtocol protocol,
UInt8 command,
UInt32 block,
UInt32 nblks)
{
bzero( taskfile, sizeof(ATATaskfile) );
taskfile->protocol = protocol;
taskfile->regmask = ATARegtoMask(kATARegSectorNumber) |
ATARegtoMask(kATARegCylinderLow) |
ATARegtoMask(kATARegCylinderHigh) |
ATARegtoMask(kATARegDriveHead) |
ATARegtoMask(kATARegSectorCount) |
ATARegtoMask(kATARegFeatures) |
ATARegtoMask(kATARegCommand);
taskfile->resultmask = 0;
taskfile->ataRegs[kATARegSectorNumber] = block & 0x0ff;
taskfile->ataRegs[kATARegCylinderLow] = (block >> 8) & 0xff;
taskfile->ataRegs[kATARegCylinderHigh] = (block >> 16) & 0xff;
taskfile->ataRegs[kATARegDriveHead] = ((block >> 24) & 0x0f) |
kATAModeLBA | (_unit << 4);
if ( protocol == kATAProtocolDMAQueued )
{
taskfile->ataRegs[kATARegFeatures] =
(nblks == kIOATAMaxBlocksPerXfer) ? 0 : nblks;
taskfile->ataRegs[kATARegSectorCount] = 0;
}
else
{
taskfile->ataRegs[kATARegFeatures] = 0;
taskfile->ataRegs[kATARegSectorCount] =
(nblks == kIOATAMaxBlocksPerXfer) ? 0 : nblks;
}
taskfile->ataRegs[kATARegCommand] = command;
}
IOATACommand *
IOATAHDDrive::ataCommandReadWrite(IOMemoryDescriptor * buffer,
UInt32 block,
UInt32 nblks)
{
ATATaskfile taskfile;
bool isWrite;
IOATACommand * cmd = allocateCommand();
assert(buffer);
if (!cmd) return 0;
isWrite = (buffer->getDirection() == kIODirectionOut) ?
true : false;
#ifdef DEBUG_LOG
IOLog("%s::ataCommandReadWrite %08x (%d) %s %d %d\n",
getName(),
buffer,
buffer->getLength(),
isWrite ? "WR" : "RD",
block,
nblks);
#endif
#if 0 // used for testing - force PIO mode
setupReadWriteTaskFile(&taskfile,
kATAProtocolPIO,
isWrite ? kIOATACommandWritePIO :
kIOATACommandReadPIO,
block,
nblks);
#else
setupReadWriteTaskFile(&taskfile,
_ataProtocol,
isWrite ? _ataWriteCmd : _ataReadCmd,
block,
nblks);
#endif
ATA_CLIENT_DATA(cmd)->buffer = buffer;
cmd->setTaskfile(&taskfile);
cmd->setPointers(buffer,
buffer->getLength(),
isWrite);
return cmd;
}
IOATACommand *
IOATAHDDrive::ataCommandSetFeatures(UInt8 features,
UInt8 SectorCount,
UInt8 SectorNumber,
UInt8 CylinderLow,
UInt8 CyclinderHigh)
{
ATATaskfile taskfile;
IOATACommand * cmd = allocateCommand();
if (!cmd) return 0;
taskfile.protocol = kATAProtocolPIO;
taskfile.regmask = ATARegtoMask(kATARegSectorNumber) |
ATARegtoMask(kATARegCylinderLow) |
ATARegtoMask(kATARegCylinderHigh) |
ATARegtoMask(kATARegDriveHead) |
ATARegtoMask(kATARegSectorCount) |
ATARegtoMask(kATARegCommand);
taskfile.resultmask = ATARegtoMask(kATARegError) |
ATARegtoMask(kATARegStatus);
taskfile.ataRegs[kATARegFeatures] = features;
taskfile.ataRegs[kATARegSectorNumber] = SectorNumber;
taskfile.ataRegs[kATARegCylinderLow] = CylinderLow;
taskfile.ataRegs[kATARegCylinderHigh] = CyclinderHigh;
taskfile.ataRegs[kATARegDriveHead] = kATAModeLBA | (_unit << 4);
taskfile.ataRegs[kATARegSectorCount] = SectorCount;
taskfile.ataRegs[kATARegCommand] = kIOATACommandSetFeatures;
cmd->setTaskfile(&taskfile);
cmd->setPointers(0, 0, false);
return cmd;
}
IOATACommand *
IOATAHDDrive::ataCommandFlushCache()
{
ATATaskfile taskfile;
IOATACommand * cmd = allocateCommand();
if (!cmd) return 0;
taskfile.protocol = kATAProtocolPIO;
taskfile.regmask = ATARegtoMask(kATARegDriveHead) |
ATARegtoMask(kATARegCommand);
taskfile.resultmask = ATARegtoMask(kATARegError) |
ATARegtoMask(kATARegSectorNumber) |
ATARegtoMask(kATARegCylinderLow) |
ATARegtoMask(kATARegCylinderHigh) |
ATARegtoMask(kATARegDriveHead) |
ATARegtoMask(kATARegStatus);
taskfile.ataRegs[kATARegDriveHead] = kATAModeLBA | (_unit << 4);
taskfile.ataRegs[kATARegCommand] = kIOATACommandFlushCache;
cmd->setTaskfile(&taskfile);
cmd->setPointers(0, 0, false);
return cmd;
}
IOATACommand *
IOATAHDDrive::ataCommandStandby()
{
ATATaskfile taskfile;
IOATACommand * cmd = allocateCommand();
if (!cmd) return 0;
taskfile.protocol = kATAProtocolPIO;
taskfile.regmask = ATARegtoMask(kATARegDriveHead) |
ATARegtoMask(kATARegCommand);
taskfile.resultmask = ATARegtoMask(kATARegError) |
ATARegtoMask(kATARegStatus);
taskfile.ataRegs[kATARegDriveHead] = kATAModeLBA | (_unit << 4);
taskfile.ataRegs[kATARegCommand] = kIOATACommandStandbyImmediate;
cmd->setTaskfile(&taskfile);
cmd->setPointers(0, 0, false);
return cmd;
}
void
IOATAHDDrive::sHandleCommandCompletion(IOATAHDDrive * self,
IOATACommand * cmd)
{
ATAResults results;
IOATADevice * device;
IOATAClientData * clientData;
assert(cmd);
device = cmd->getDevice(kIOATADevice);
assert(device);
clientData = ATA_CLIENT_DATA(cmd);
assert(clientData);
if ((cmd->getResults(&results) != kIOReturnSuccess) &&
(clientData->maxRetries-- > 0))
{
cmd->execute();
return;
}
#if 0
cmd->getResults(&results);
if (clientData->maxRetries-- > 2) {
cmd->execute();
return;
}
#endif
#ifdef DEBUG_LOG
IOLog("%s: sHandleCommandCompletion %08x %08x %08x %08x %d\n",
getName(), device, cmd, refcon, results.returnCode,
results.bytesTransferred);
#endif
clientData->returnCode = results.returnCode;
if (clientData->isSync) {
assert(clientData->completion.syncLock);
clientData->completion.syncLock->signal(); }
else {
IOStorage::complete(clientData->completion.async,
results.returnCode,
(UInt64) results.bytesTransferred);
}
if (clientData->buffer)
clientData->buffer->release();
cmd->release();
}
IOReturn
IOATAHDDrive::syncExecute(IOATACommand * cmd,
UInt32 timeout,
UInt retries,
IOMemoryDescriptor * senseData)
{
IOATAClientData * clientData = ATA_CLIENT_DATA(cmd);
if ( _pmReady )
{
activityTickle( kIOPMSuperclassPolicy1, 1 );
}
cmd->retain();
cmd->setPointers(senseData,
senseData ? senseData->getLength() : 0,
false,
true );
cmd->setTimeout(timeout);
cmd->setCallback(this,
(CallbackFn) &IOATAHDDrive::sHandleCommandCompletion,
(void *) cmd);
if (clientData->buffer)
clientData->buffer->retain();
clientData->maxRetries = retries;
clientData->completion.syncLock = IOSyncer::create();
clientData->isSync = true;
cmd->execute();
clientData->completion.syncLock->wait();
return clientData->returnCode;
}
IOReturn
IOATAHDDrive::asyncExecute(IOATACommand * cmd,
IOStorageCompletion completion,
UInt32 timeout,
UInt retries)
{
IOATAClientData * clientData = ATA_CLIENT_DATA(cmd);
if ( _pmReady )
{
activityTickle( kIOPMSuperclassPolicy1, 1 );
}
cmd->retain();
cmd->setTimeout(timeout);
cmd->setCallback(this,
(CallbackFn) &IOATAHDDrive::sHandleCommandCompletion,
(void *) cmd);
if (clientData->buffer)
clientData->buffer->retain();
clientData->maxRetries = retries;
clientData->isSync = false;
clientData->completion.async = completion;
return (cmd->execute() ? kIOReturnSuccess : kIOReturnNoResources);
}
IOATACommand *
IOATAHDDrive::allocateCommand()
{
return _ataDevice->allocCommand(kIOATADevice, sizeof(IOATAClientData));
}