#include <IOKit/IOKitLib.h>
#include <IOKit/pwr_mgt/IOPMLib.h>
#include <IOKit/pwr_mgt/IOPM.h>
#include <time.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <assert.h>
#include "disk_power.h"
int PowerStateSummary( IOATAPowerState powerState );
char * PowerStateString( IOATAPowerState x, int opt_summary )
{
char * result;
if ( opt_summary )
{
switch ( PowerStateSummary( x ) )
{
case -1:
result = "ON";
break;
case 0: result = "OFF";
break;
default:
fprintf(stderr, "ERROR: %s: unknown IOATAPowerState %d\n", __FUNCTION__, (int)x);
result = "UNKNOWN";
break;
}
}
else
{
switch ( x )
{
case kIOATAPowerStateSystemSleep: result = "SystemSleep";
break;
case kIOATAPowerStateSleep:
result = "Sleep";
break;
case kIOATAPowerStateStandby:
result = "Standby";
break;
case kIOATAPowerStateIdle:
result = "Idle";
break;
case kIOATAPowerStateActive:
result = "Active";
break;
default:
fprintf(stderr, "ERROR: %s: unknown IOATAPowerState %d\n", __FUNCTION__, (int)x);
result = "UNKNOWN";
break;
}
}
return result;
}
IOATAPowerState PowerStatesMax( IOATAPowerStates * powerStates )
{
IOATAPowerState driverDesire = powerStates->driverDesire;
IOATAPowerState deviceDesire = powerStates->deviceDesire;
IOATAPowerState userDesire = powerStates->userDesire;
IOATAPowerState maxState = 0;
if ( driverDesire > maxState ) maxState = driverDesire;
if ( deviceDesire > maxState ) maxState = deviceDesire;
if ( userDesire > maxState ) maxState = userDesire;
return maxState;
}
int PowerStateSummary( IOATAPowerState powerState )
{
int result;
if ( powerState <= 0 )
result = powerState;
else
#if 1
if ( 0 <= powerState && powerState <= kIOATAPowerStateSleep )
result = 0;
else
result = -1;
#else
if ( 0 <= powerState && powerState <= kIOATAPowerStateStandby ) result = 0; else
if ( kIOATAPowerStateIdle <= powerState && powerState <= kIOATAPowerStateActive ) result = -1; else
{
fprintf(stderr, "ERROR: %s(%d): unexpected value.\n", __FUNCTION__, powerState);
exit(-1);
}
#endif
return result;
}
int GetATADeviceInfo( DiskDevice * device )
{
int result;
IOATAPowerStates * powerStates = & device->powerStates;
kern_return_t kr;
mach_port_t masterPort;
io_registry_entry_t service;
io_iterator_t iterator;
kr = IOMasterPort(MACH_PORT_NULL, &masterPort);
assert(KERN_SUCCESS==kr);
IOServiceGetMatchingServices(masterPort, IOServiceMatching ( "ATADeviceNub" ), & iterator );
if ( ! iterator )
{
result = -10;
goto Return;
}
while (( service = IOIteratorNext( iterator ) ))
{
CFStringRef str = nil;
CFMutableDictionaryRef properties = nil;
CFDictionaryRef physCharacteristics = nil;
io_iterator_t child_iterator;
io_registry_entry_t child;
char deviceModel[ 256 ];
bzero( deviceModel, sizeof deviceModel );
str = IORegistryEntryCreateCFProperty( service, CFSTR("device model"), kCFAllocatorDefault, kNilOptions );
if ( str )
{
CFStringGetCString( str, deviceModel, sizeof deviceModel, kCFStringEncodingMacRoman );
CFRelease( str );
}
char deviceInterconnect[ 256 ];
bzero(deviceInterconnect, sizeof deviceInterconnect );
char deviceLocation[ 256 ];
bzero(deviceLocation, sizeof deviceLocation );
IORegistryEntryCreateCFProperties( service, & properties, kCFAllocatorDefault, kNilOptions );
if ( properties )
{
physCharacteristics = CFDictionaryGetValue( properties, CFSTR("Protocol Characteristics") );
if ( physCharacteristics )
{
str = CFDictionaryGetValue( physCharacteristics, CFSTR("Physical Interconnect") );
if ( str )
{
CFStringGetCString( str, deviceInterconnect, sizeof deviceInterconnect, kCFStringEncodingMacRoman );
}
str = CFDictionaryGetValue( physCharacteristics, CFSTR("Physical Interconnect Location") );
if ( str )
{
CFStringGetCString( str, deviceLocation, sizeof deviceLocation, kCFStringEncodingMacRoman );
}
}
CFRelease( properties );
}
IORegistryEntryGetChildIterator( service, kIOServicePlane, & child_iterator );
while (( child = IOIteratorNext( child_iterator ) ))
{
int driverDesire, deviceDesire, userDesire;
if ( 0 == strlen(deviceInterconnect) )
{
str = IORegistryEntryCreateCFProperty( child, CFSTR("Physical Interconnect"), kCFAllocatorDefault, kNilOptions );
if ( str )
{
CFStringGetCString( str, deviceInterconnect, sizeof deviceInterconnect, kCFStringEncodingMacRoman );
CFRelease( str );
}
}
if ( 0 == strlen( deviceLocation ) )
{
str = IORegistryEntryCreateCFProperty( child, CFSTR("Physical Interconnect Location"), kCFAllocatorDefault, kNilOptions );
if ( str )
{
CFStringGetCString( str, deviceLocation, sizeof deviceLocation , kCFStringEncodingMacRoman );
CFRelease( str );
}
}
char deviceType[ 256 ];
bzero( deviceType, sizeof deviceType );
char powerState[ 256 ];
bzero( powerState, sizeof powerState );
str = IORegistryEntryCreateCFProperty( service, CFSTR("ata device type"), kCFAllocatorDefault, kNilOptions );
if ( str )
{
CFStringGetCString( str, deviceType, sizeof deviceType, kCFStringEncodingMacRoman );
CFRelease( str );
if ( 0 == strcmp( deviceType, "ata" ) ) {
IORegistryEntryCreateCFProperties( child, & properties, kCFAllocatorDefault, kNilOptions );
if ( properties )
{
str = CFDictionaryGetValue( properties, CFSTR("Power Management private data") );
if ( str )
{
CFStringGetCString( str, powerState, sizeof powerState, kCFStringEncodingMacRoman );
}
CFRelease( properties );
}
}
}
if ( 3 == sscanf ( powerState,
"{ this object = %*x, interested driver = %*x, driverDesire = %d, deviceDesire = %d, ourDesiredPowerState = %d, previousRequest = %*d }",
& driverDesire, & deviceDesire, & userDesire
)
)
{
device->timestamp = time( NULL );
device->name = strdup( deviceModel ); device->location = strdup( deviceLocation ); device->interconnect = strdup( deviceInterconnect );
powerStates->driverDesire = driverDesire;
powerStates->deviceDesire = deviceDesire;
powerStates->userDesire = userDesire;
IOObjectRelease( child_iterator );
IOObjectRelease( iterator );
result = 0;
goto Return;
}
}
IOObjectRelease( child_iterator );
}
IOObjectRelease( iterator );
result = -11;
goto Return;
Return:
return result;
}
int GetATADeviceInfoWithRetry( DiskDevice * diskDevice )
{
int err;
int retryNumber;
for ( retryNumber = 0; retryNumber < 10; retryNumber++ )
{
err = GetATADeviceInfo( diskDevice );
if ( noErr == err )
{
goto Return;
}
#if 0
char errorStringBuffer[ 256 ];
char * errorString = errorStringBuffer;
errorString += sprintf_timestamp_now( errorString );
errorString += sprintf(errorString, ": WARNING: %s: sleeping and retrying...\n", __FUNCTION__);
fputs( errorStringBuffer, stderr );
fflush(stdout);
#endif
sleep(1);
}
goto Return;
Return:
return err;
}