#include <IOKit/assert.h>
#include <IOKit/storage/ata/IOATAHDDrive.h>
#include <IOKit/storage/ata/IOATAHDDriveNub.h>
#define super IOService
OSDefineMetaClassAndStructors( IOATAHDDrive, IOService )
void
IOATAHDDrive::sHandleConfigureDevice(IOATAHDDrive * self)
{
self->configureDevice(self->_ataDevice);
}
bool
IOATAHDDrive::init(OSDictionary * properties)
{
return (super::init(properties));
}
IOService *
IOATAHDDrive::probe(IOService * provider, SInt32 * score)
{
if (!super::probe(provider, score))
return 0;
IOATADevice * device = OSDynamicCast(IOATADevice, provider);
if (device == 0)
return 0;
if (device->getDeviceType() != reportATADeviceType())
return 0;
_unit = device->getUnit();
return this; }
bool
IOATAHDDrive::start(IOService * provider)
{
if (super::start(provider) == false)
return false;
_configThreadCall = (void *) thread_call_allocate(
(thread_call_func_t) sHandleConfigureDevice,
(thread_call_param_t) this);
if (!_configThreadCall)
return false;
_ataDevice = OSDynamicCast(IOATADevice, provider);
if (_ataDevice == 0)
return false;
_ataDevice->retain();
if (_ataDevice->open(this) == false)
return false;
if (inspectDevice(_ataDevice) == false)
return false;
_logSelectedTimingProtocol = true;
if (selectTimingProtocol() == false)
return false;
_cmdGate = IOCommandGate::commandGate(this);
if (_cmdGate == 0)
return false;
IOWorkLoop * workloop = _ataDevice->getWorkLoop();
if ((workloop == 0) ||
(workloop->addEventSource(_cmdGate) != kIOReturnSuccess))
return false;
_currentATAPowerState = kIOATAPowerStateActive;
PMinit();
provider->joinPMtree(this);
setIdleTimerPeriod(300);
if (_supportedFeatures & kIOATAFeaturePowerManagement)
initForPM();
return (createNub(provider));
}
void
IOATAHDDrive::stop(IOService * provider)
{
PMstop();
super::stop(provider);
}
void
IOATAHDDrive::free()
{
if (_configThreadCall) {
thread_call_cancel((thread_call_t) _configThreadCall);
thread_call_free((thread_call_t) _configThreadCall);
}
if (_cmdGate) {
if (_ataDevice && (_ataDevice->getWorkLoop()))
_ataDevice->getWorkLoop()->removeEventSource(_cmdGate);
_cmdGate->release();
}
if (_ataDevice)
_ataDevice->release();
super::free();
}
bool
IOATAHDDrive::inspectDevice(IOATADevice * ataDevice)
{
OSString * string;
ATAIdentify * identify;
string = OSDynamicCast(OSString,
ataDevice->getProperty(kATAPropertyModelNumber));
if (string) {
strncpy(_model, string->getCStringNoCopy(), 40);
_model[40] = '\0';
}
string = OSDynamicCast(OSString,
ataDevice->getProperty(kATAPropertyFirmwareRev));
if (string) {
strncpy(_revision, string->getCStringNoCopy(), 8);
_revision[8] = '\0';
}
identify = (ATAIdentify *) IOMalloc(sizeof(*identify));
if (!identify)
return false;
ataDevice->getIdentifyData(identify);
if (identify->commandSetsSupported1 & 0x8)
_supportedFeatures |= kIOATAFeaturePowerManagement;
if (identify->commandSetsSupported1 & 0x20)
_supportedFeatures |= kIOATAFeatureWriteCache;
IOFree(identify, sizeof(*identify));
setProperty(kIOATASupportedFeaturesKey,
_supportedFeatures,
sizeof(_supportedFeatures) * 8);
return true;
}
ATADeviceType
IOATAHDDrive::reportATADeviceType() const
{
return kATADeviceATA;
}
const char *
IOATAHDDrive::getDeviceTypeName()
{
return kIOBlockStorageDeviceTypeGeneric;
}
IOService * IOATAHDDrive::instantiateNub()
{
IOService * nub = new IOATAHDDriveNub;
return nub;
}
bool IOATAHDDrive::createNub(IOService * provider)
{
IOService * nub;
nub = instantiateNub();
if (nub == 0) {
IOLog("%s: instantiateNub() failed\n", getName());
return false;
}
nub->init();
if (!nub->attach(this))
IOPanic("IOATAHDDrive::createNub() unable to attach nub");
nub->registerService();
return true;
}
IOReturn IOATAHDDrive::doAsyncReadWrite(IOMemoryDescriptor * buffer,
UInt32 block,
UInt32 nblks,
IOStorageCompletion completion)
{
IOReturn ret;
IOATACommand * cmd = ataCommandReadWrite(buffer, block, nblks);
if (cmd == 0)
return kIOReturnNoMemory;
ret = asyncExecute(cmd, completion);
cmd->release();
return ret;
}
IOReturn IOATAHDDrive::doSyncReadWrite(IOMemoryDescriptor * buffer,
UInt32 block,
UInt32 nblks)
{
IOReturn ret;
IOATACommand * cmd = ataCommandReadWrite(buffer, block, nblks);
if (cmd == 0)
return kIOReturnNoMemory;
ret = syncExecute(cmd);
cmd->release();
return ret;
}
IOReturn IOATAHDDrive::doEjectMedia()
{
return kIOReturnUnsupported; }
IOReturn IOATAHDDrive::doFormatMedia(UInt64 byteCapacity)
{
return kIOReturnUnsupported;
}
UInt32 IOATAHDDrive::doGetFormatCapacities(UInt64 * capacities,
UInt32 capacitiesMaxCount) const
{
UInt32 blockCount = 0;
UInt32 blockSize = 0;
assert(_ataDevice);
if (_ataDevice->getDeviceCapacity(&blockCount, &blockSize) &&
(capacities != NULL) && (capacitiesMaxCount > 0))
{
UInt64 count = blockCount;
UInt64 size = blockSize;
*capacities = size * (count + 1);
return 1;
}
return 0;
}
IOReturn IOATAHDDrive::doLockUnlockMedia(bool doLock)
{
return kIOReturnUnsupported; }
IOReturn IOATAHDDrive::doSynchronizeCache()
{
IOReturn ret;
IOATACommand * cmd = ataCommandFlushCache();
if (cmd == 0)
return kIOReturnNoMemory;
ret = syncExecute(cmd, 60000);
cmd->release();
return ret;
}
IOReturn
IOATAHDDrive::doStart()
{
return kIOReturnSuccess;
}
IOReturn
IOATAHDDrive::doStop()
{
return kIOReturnSuccess;
}
char * IOATAHDDrive::getAdditionalDeviceInfoString()
{
return ("[ATA]");
}
char * IOATAHDDrive::getProductString()
{
return _model;
}
char * IOATAHDDrive::getRevisionString()
{
return _revision;
}
char * IOATAHDDrive::getVendorString()
{
return NULL;
}
IOReturn IOATAHDDrive::reportBlockSize(UInt64 * blockSize)
{
UInt32 blkCount = 0;
UInt32 blkSize = 0;
assert(_ataDevice);
if (!_ataDevice->getDeviceCapacity(&blkCount, &blkSize))
return kIOReturnNoDevice;
*blockSize = blkSize;
return kIOReturnSuccess;
}
IOReturn IOATAHDDrive::reportEjectability(bool * isEjectable)
{
*isEjectable = false;
return kIOReturnSuccess;
}
IOReturn IOATAHDDrive::reportLockability(bool * isLockable)
{
*isLockable = false;
return kIOReturnSuccess;
}
IOReturn IOATAHDDrive::reportPollRequirements(bool * pollRequired,
bool * pollIsExpensive)
{
*pollIsExpensive = false;
*pollRequired = false;
return kIOReturnSuccess;
}
IOReturn IOATAHDDrive::reportMaxReadTransfer(UInt64 blocksize, UInt64 * max)
{
*max = blocksize * kIOATAMaxBlocksPerXfer;
return kIOReturnSuccess;
}
IOReturn IOATAHDDrive::reportMaxWriteTransfer(UInt64 blocksize, UInt64 * max)
{
return reportMaxReadTransfer(blocksize, max);
}
IOReturn IOATAHDDrive::reportMaxValidBlock(UInt64 * maxBlock)
{
UInt32 blockCount = 0;
UInt32 blockSize = 0;
assert(_ataDevice && maxBlock);
if (!_ataDevice->getDeviceCapacity(&blockCount, &blockSize))
return kIOReturnNoDevice;
*maxBlock = blockCount;
return kIOReturnSuccess;
}
IOReturn IOATAHDDrive::reportMediaState(bool * mediaPresent, bool * changed)
{
*mediaPresent = true;
*changed = true;
return kIOReturnSuccess;
}
IOReturn IOATAHDDrive::reportRemovability(bool * isRemovable)
{
*isRemovable = false;
return kIOReturnSuccess;
}
IOReturn IOATAHDDrive::reportWriteProtection(bool * isWriteProtected)
{
*isWriteProtected = false;
return kIOReturnSuccess;
}
IOReturn
IOATAHDDrive::message(UInt32 type, IOService * provider, void * argument)
{
IOReturn ret = kIOReturnSuccess;
switch (type)
{
case kATAClientMsgBusReset:
_ataDevice->holdQueue(kATAQTypeNormalQ);
break;
case kATAClientMsgBusReset | kATAClientMsgDone:
configureDevice( _ataDevice );
break;
case kATAClientMsgSelectTiming | kATAClientMsgDone:
_ataDevice->releaseQueue(kATAQTypeNormalQ);
break;
default:
ret = super::message(type, provider, argument);
break;
}
return ret;
}