#include <IOKit/IOTypes.h>
#include "IOATATypes.h"
#include "IOATADevice.h"
#include "IOATAController.h"
#include "ATADeviceNub.h"
#include "IOATADevConfig.h"
#include <IOKit/IOSyncer.h>
enum{
kDoIDDataComplete,
kDoSetFeatureComplete
};
struct completionInfo{
UInt32 whatToDo;
IOSyncer* sync;
};
#ifdef DLOG
#undef DLOG
#endif
#ifdef ATA_DEBUG
#define DLOG(fmt, args...) IOLog(fmt, ## args)
#else
#define DLOG(fmt, args...)
#endif
#define kIDBufferBytes 512
#define super IOATADevice
OSDefineMetaClassAndStructors( ATADeviceNub, IOATADevice )
OSMetaClassDefineReservedUnused(ATADeviceNub, 0);
OSMetaClassDefineReservedUnused(ATADeviceNub, 1);
OSMetaClassDefineReservedUnused(ATADeviceNub, 2);
OSMetaClassDefineReservedUnused(ATADeviceNub, 3);
OSMetaClassDefineReservedUnused(ATADeviceNub, 4);
OSMetaClassDefineReservedUnused(ATADeviceNub, 5);
OSMetaClassDefineReservedUnused(ATADeviceNub, 6);
OSMetaClassDefineReservedUnused(ATADeviceNub, 7);
OSMetaClassDefineReservedUnused(ATADeviceNub, 8);
OSMetaClassDefineReservedUnused(ATADeviceNub, 9);
OSMetaClassDefineReservedUnused(ATADeviceNub, 10);
OSMetaClassDefineReservedUnused(ATADeviceNub, 11);
OSMetaClassDefineReservedUnused(ATADeviceNub, 12);
OSMetaClassDefineReservedUnused(ATADeviceNub, 13);
OSMetaClassDefineReservedUnused(ATADeviceNub, 14);
OSMetaClassDefineReservedUnused(ATADeviceNub, 15);
OSMetaClassDefineReservedUnused(ATADeviceNub, 16);
OSMetaClassDefineReservedUnused(ATADeviceNub, 17);
OSMetaClassDefineReservedUnused(ATADeviceNub, 18);
OSMetaClassDefineReservedUnused(ATADeviceNub, 19);
OSMetaClassDefineReservedUnused(ATADeviceNub, 20);
ATADeviceNub*
ATADeviceNub::ataDeviceNub( IOATAController* provider, ataUnitID unit, ataDeviceType devType)
{
ATADeviceNub* nub = new ATADeviceNub;
if( ! nub )
return 0L;
if( !nub->init( provider, unit, devType) )
{
nub->release();
return 0L;
}
return nub;
}
bool
ATADeviceNub::init(IOATAController* provider, ataUnitID unit, ataDeviceType devType)
{
if( !super::init( (OSDictionary*) 0L) )
return false;
_provider = provider;
_unitNumber = unit;
_deviceType = devType;
buffer = (UInt8*) IOMalloc( kIDBufferBytes );
if( !buffer )
return false;
IOReturn err = kATANoErr;
err = getDeviceID();
if( err )
{
DLOG("ATADeviceNub failed identify device %ld\n", (long int) err);
IOFree( buffer, kIDBufferBytes);
return false;
}
publishProperties();
publishBusProperties();
publishVendorProperties();
IOFree( buffer, kIDBufferBytes);
buffer = 0L;
return true;
}
bool
ATADeviceNub::attach(IOService* provider )
{
IOATAController* controller = OSDynamicCast( IOATAController, provider);
if( !controller )
{
DLOG("ATANub: Provider not IOATAController\n");
return false;
}
if( !super::attach( provider) )
return false;
return true;
}
IOATACommand*
ATADeviceNub::allocCommand( void )
{
IOATABusCommand64* cmd = IOATABusCommand64::allocateCmd32();
return (IOATACommand*) cmd;
}
void
ATADeviceNub::freeCommand( IOATACommand* inCommand)
{
inCommand->release();
}
IOReturn
ATADeviceNub::executeCommand(IOATACommand* command)
{
IOSyncer* mySync = 0L;
IOATABusCommand* cmd = OSDynamicCast( IOATABusCommand, command);
if( !cmd )
return -1;
if( cmd->getCallbackPtr() == 0L)
{
mySync = IOSyncer::create();
cmd->syncer = mySync;
}
IOReturn err = _provider->executeCommand( this, cmd);
if( mySync )
{
mySync->wait();
err = cmd->getResult();
}
return err;
}
IOReturn
ATADeviceNub::getDeviceID( void )
{
OSString* string;
IOMemoryDescriptor* desc = IOMemoryDescriptor::withAddress((void *) buffer,
kIDBufferBytes,
kIODirectionIn);
if( !desc )
{
string = OSString::withCString( "failed" );
setProperty( "Alloc descriptor", (OSObject *)string );
string->release();
return -1;
}
IOATABusCommand* cmd = (IOATABusCommand*) allocCommand();
if(!cmd)
{
string = OSString::withCString( "failed" );
setProperty( "Alloc command", (OSObject *)string );
string->release();
return -1;
}
cmd->setOpcode( kATAFnExecIO);
cmd->setFlags(mATAFlagIORead);
cmd->setUnit( _unitNumber );
cmd->setTimeoutMS( 30000);
cmd->setBuffer ( desc);
cmd->setPosition ((IOByteCount) 0);
cmd->setByteCount ((IOByteCount) kIDBufferBytes);
cmd->setDevice_Head( ((UInt8)_unitNumber) << 4);
if(_deviceType == kATADeviceType)
{
cmd->setCommand ( kATAcmdDriveIdentify );
} else {
cmd->setCommand ( 0xA1 ); }
cmd->setCallbackPtr ( (IOATACompletionFunction*) MyATACallback);
completionInfo* completion = (completionInfo*)IOMalloc(sizeof(completionInfo));
completion->whatToDo = kDoIDDataComplete;
completion->sync = IOSyncer::create();
cmd->refCon = (void*) completion;
cmd->refCon2 = (void*) this;
desc->prepare(kIODirectionIn);
DLOG("Sending ID command to bus controller\n");
IOReturn err = executeCommand( cmd);
DLOG("Command returned error = %ld\n",(long int)err );
if(!err)
{
completion->sync->wait();
}
desc->complete( kIODirectionIn );
IOFree( completion, sizeof(completionInfo));
if( cmd->getResult() )
{
err = cmd->getResult();
}
freeCommand(cmd);
#if defined(__BIG_ENDIAN__)
swapBytes16( buffer, kIDBufferBytes);
#else
swapBytes16( &buffer[46], 8); swapBytes16( &buffer[54], 40); swapBytes16( &buffer[20], 20); #endif
return err;
}
void
ATADeviceNub::publishBusProperties( void )
{
OSString* string;
IOATABusInfo* theInfo = IOATABusInfo::atabusinfo();
if( !theInfo )
{
DLOG("ATANub IOATABusInfo alloc fail\n");
return;
}
if(_provider->provideBusInfo( theInfo ))
{
DLOG("ATANub provide info failed\n");
theInfo->release();
return;
}
switch( theInfo->getSocketType() )
{
case kInternalATASocket:
string = OSString::withCString( kATAInternalSocketString );
break;
case kMediaBaySocket:
string = OSString::withCString( kATAMediaBaySocketString );
break;
case kPCCardSocket:
string = OSString::withCString( kATAPCCardSocketString );
break;
case kInternalSATA:
string = OSString::withCString( kATAInternalSATAString );
break;
case kSATABay:
string = OSString::withCString( kATASATABayString );
break;
case kInternalSATA2:
string = OSString::withCString( kATAInternalSATA2 );
break;
case kSATA2Bay:
string = OSString::withCString( kATASATA2BayString );
break;
default:
string = OSString::withCString( kATAUnkownSocketString );
break;
}
setProperty( kATASocketKey, (OSObject *)string );
string->release();
theInfo->release();
}
void
ATADeviceNub::publishProperties( void )
{
OSString* string;
switch( _deviceType )
{
case kATADeviceType:
string = OSString::withCString( kATATypeATAString );
break;
case kATAPIDeviceType:
string = OSString::withCString( kATATypeATAPIString );
break;
default:
string = OSString::withCString( kATATypeUnknownString );
break;
}
setProperty( kATADevPropertyKey, (OSObject *)string );
string->release();
OSNumber* number = OSNumber::withNumber( _unitNumber, 32 );
setProperty( kATAUnitNumberKey, (OSObject *) number);
setProperty( "IOUnit", (OSObject *) number);
number->release();
if( _unitNumber == 0 )
{
setLocation("0");
} else {
setLocation("1");
}
}
void
ATADeviceNub::publishVendorProperties(void)
{
if( IOATADevConfig::sDriveSupports48BitLBA( ( const UInt16*) buffer ) )
{
UInt32 upperLBA, lowerLBA;
IOATADevConfig::sDriveExtendedLBASize( &upperLBA, &lowerLBA, ( const UInt16*) buffer );
UInt64 largeLBASize = 0;
largeLBASize = ( ((UInt64) upperLBA) << 32) | ((UInt64) lowerLBA );
OSNumber* extendedCapacity = OSNumber::withNumber( largeLBASize, 64 );
setProperty( "extended LBA capacity", (OSObject *) extendedCapacity);
extendedCapacity->release();
}
buffer[94] = 0;
buffer[40] = 0;
OSString* modelNum = OSString::withCString((const char*) &buffer[54]);
buffer[54] = 0;
OSString* firmVers = OSString::withCString((const char*) &buffer[46]);
OSString* serial = OSString::withCString( (const char*) &buffer[20]);
setProperty( kATAVendorPropertyKey, (OSObject *)modelNum );
setProperty( kATARevisionPropertyKey, (OSObject *)firmVers );
setProperty( kATASerialNumPropertyKey, (OSObject *)serial );
serial->release();
modelNum->release();
firmVers->release();
}
void
ATADeviceNub::MyATACallback(IOATACommand* command )
{
if( command->getResult() )
{
DLOG("Command result error = %ld\n",(long int)command->getResult() );
}
ATADeviceNub* self = (ATADeviceNub*) command->refCon2;
self->processCallback( command );
}
void
ATADeviceNub::processCallback(IOATACommand* command )
{
completionInfo* completer = (completionInfo*) command->refCon;
switch( completer->whatToDo )
{
case kDoIDDataComplete:
completer->sync->signal();
break;
case kDoSetFeatureComplete:
completer->sync->signal();
default:
break;
}
}
void
ATADeviceNub::swapBytes16( UInt8* dataBuffer, IOByteCount length)
{
IOByteCount i;
UInt8 c;
unsigned char* firstBytePtr;
for (i = 0; i < length; i+=2)
{
firstBytePtr = dataBuffer; c = *dataBuffer++; *firstBytePtr = *dataBuffer; *dataBuffer++= c; }
}