]> git.saurik.com Git - apple/system_cmds.git/blobdiff - update.tproj/disk_power.c
system_cmds-300.tar.gz
[apple/system_cmds.git] / update.tproj / disk_power.c
diff --git a/update.tproj/disk_power.c b/update.tproj/disk_power.c
new file mode 100644 (file)
index 0000000..e0d76fe
--- /dev/null
@@ -0,0 +1,350 @@
+#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 );
+
+/*
+-- 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()
+