#include #include #include #include #include #include #include #include #include "disk_power.h" int PowerStateSummary( IOATAPowerState powerState ); /* -- PowerStateString() */ char * PowerStateString( IOATAPowerState x, int opt_summary ) { char * result; if ( opt_summary ) { switch ( PowerStateSummary( x ) ) { case -1: result = "ON"; break; case 0: // This is the only value where the two scales collide. result = "OFF"; break; default: fprintf(stderr, "ERROR: %s: unknown IOATAPowerState %d\n", __FUNCTION__, (int)x); result = "UNKNOWN"; break; } } else { switch ( x ) { case kIOATAPowerStateSystemSleep: // This is the only value where the two scales collide. 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; } // PowerStateString() /* -- PowerStatesMax() */ 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; } // PowerStatesMax() /* -- PowerStateSummary() */ /* Returns -- -1 == ON -- 0 == OFF -- Can be used together with the positive values denoting IOATAPowerState's. -- But you have to be careful not to confuse with OFF == 0 == kIOATAPowerStateSystemSleep. */ int PowerStateSummary( IOATAPowerState powerState ) { int result; // Summarizing twice does nothing. Idempotent. if ( powerState <= 0 ) result = powerState; else #if 1 if ( 0 <= powerState && powerState <= kIOATAPowerStateSleep ) result = 0; else result = -1; #else if ( 0 <= powerState && powerState <= kIOATAPowerStateStandby ) // Spun down. result = 0; // OFF else if ( kIOATAPowerStateIdle <= powerState && powerState <= kIOATAPowerStateActive ) // Spun up. result = -1; // ON else { fprintf(stderr, "ERROR: %s(%d): unexpected value.\n", __FUNCTION__, powerState); exit(-1); } #endif return result; } // PowerStateSummary() /* -- GetATADeviceInfo() */ // Fairly often this returns -11, meaning that it was unable to locate a matching device. // If this happens, just wait awhile and try again. // // See GetATADeviceInfoWithRetry() // 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); /* look for drives */ 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; // Device Model 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 ); } // Device Interconnect & Device Location 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 ) { // device interconnect str = CFDictionaryGetValue( physCharacteristics, CFSTR("Physical Interconnect") ); if ( str ) { CFStringGetCString( str, deviceInterconnect, sizeof deviceInterconnect, kCFStringEncodingMacRoman ); } // device location 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; // fill in interconnect info if we don't already have it 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 ); } } // Device Type char deviceType[ 256 ]; bzero( deviceType, sizeof deviceType ); // Power State char powerState[ 256 ]; bzero( powerState, sizeof powerState ); // find out what type of device this is - ATAPI will be added as SCSI devices str = IORegistryEntryCreateCFProperty( service, CFSTR("ata device type"), kCFAllocatorDefault, kNilOptions ); if ( str ) { CFStringGetCString( str, deviceType, sizeof deviceType, kCFStringEncodingMacRoman ); CFRelease( str ); if ( 0 == strcmp( deviceType, "ata" ) ) // regular ATA disks (not ATAPI) { 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 ); // copy of the original device->location = strdup( deviceLocation ); // copy of the original device->interconnect = strdup( deviceInterconnect ); // copy of the original powerStates->driverDesire = driverDesire; powerStates->deviceDesire = deviceDesire; powerStates->userDesire = userDesire; IOObjectRelease( child_iterator ); IOObjectRelease( iterator ); result = 0; goto Return; } } // while (child...) IOObjectRelease( child_iterator ); } // while (service...) IOObjectRelease( iterator ); result = -11; goto Return; Return: return result; } // GetATADeviceInfo() /* -- GetATADeviceInfoWithRetry() */ // Devices are often momentarily busy, e.g., when spinning up. Retry up to 10 times, 1 second apart. 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); } // Failure. goto Return; Return: return err; } // GetATADeviceInfoWithRetry()