1/* 2 * Copyright (c) 2003, 2012 Apple Computer, Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24#include <CoreFoundation/CoreFoundation.h> 25#include <IOKit/pwr_mgt/IOPMLibPrivate.h> 26#include <IOKit/IOCFSerialize.h> 27#include <mach/mach_port.h> 28#include <mach/vm_map.h> 29#include <servers/bootstrap.h> 30#include <bootstrap_priv.h> 31 32#include "IOSystemConfiguration.h" 33#include "IOPowerSources.h" 34#include "IOPowerSourcesPrivate.h" 35#include "IOPSKeys.h" 36#include "powermanagement.h" 37 38 39#define kSmartBattRequestUpdateIndex 4 40IOReturn IOPSRequestBatteryUpdate(int type) 41{ 42 io_registry_entry_t battery_reg = IO_OBJECT_NULL; 43 io_connect_t battery_connect = IO_OBJECT_NULL; 44 uint64_t utype = (uint64_t)type; 45 IOReturn ret = kIOReturnSuccess; 46 47 battery_reg = IOServiceGetMatchingService(kIOMasterPortDefault, 48 IOServiceMatching("AppleSmartBatteryManager")); 49 if (IO_OBJECT_NULL != battery_reg) 50 { 51 ret = IOServiceOpen(battery_reg, mach_task_self(), 0, &battery_connect); 52 if (kIOReturnSuccess == ret) { 53 IOConnectCallMethod(battery_connect, kSmartBattRequestUpdateIndex, &utype, 1, 54 NULL, 0, NULL, NULL, NULL, NULL); 55 IOServiceClose(battery_connect); 56 } 57 IOObjectRelease(battery_reg); 58 } else { 59 return kIOReturnNotFound; 60 } 61 return ret; 62} 63 64 65/* IOPSCopyInternalBatteriesArray 66 * 67 * Argument: 68 * CFTypeRef power_sources: The return value from IOPSCoyPowerSourcesInfo() 69 * Return value: 70 * CFArrayRef: all the batteries we found 71 * NULL if none are found 72 */ 73CFArrayRef 74IOPSCopyInternalBatteriesArray(CFTypeRef power_sources) 75{ 76 CFArrayRef array = isA_CFArray(IOPSCopyPowerSourcesList(power_sources)); 77 CFMutableArrayRef ret_arr; 78 CFTypeRef name = NULL; 79 CFDictionaryRef ps; 80 CFStringRef transport_type; 81 int i, count; 82 83 if(!array) return NULL; 84 count = CFArrayGetCount(array); 85 name = NULL; 86 87 ret_arr = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); 88 if(!ret_arr) goto exit; 89 90 for(i=0; i<count; i++) { 91 name = CFArrayGetValueAtIndex(array, i); 92 ps = isA_CFDictionary(IOPSGetPowerSourceDescription(power_sources, name)); 93 if(ps) { 94 transport_type = isA_CFString(CFDictionaryGetValue(ps, CFSTR(kIOPSTransportTypeKey))); 95 if(transport_type && CFEqual(transport_type, CFSTR(kIOPSInternalType))) 96 { 97 CFArrayAppendValue(ret_arr, name); 98 } 99 } 100 } 101 102 if(0 == CFArrayGetCount(ret_arr)) { 103 CFRelease(ret_arr); 104 ret_arr = NULL; 105 } 106 107exit: 108 CFRelease(array); 109 return ret_arr; 110} 111 112 113/* IOPSCopyUPSArray 114 * 115 * Argument: 116 * CFTypeRef power_sources: The return value from IOPSCoyPowerSourcesInfo() 117 * Return value: 118 * CFArrayRef: all the UPS's we found 119 * NULL if none are found 120 */ 121 CFArrayRef 122IOPSCopyUPSArray(CFTypeRef power_sources) 123{ 124 CFArrayRef array = isA_CFArray(IOPSCopyPowerSourcesList(power_sources)); 125 CFMutableArrayRef ret_arr; 126 CFTypeRef name = NULL; 127 CFDictionaryRef ps; 128 CFStringRef transport_type; 129 int i, count; 130 131 if(!array) return NULL; 132 count = CFArrayGetCount(array); 133 name = NULL; 134 135 ret_arr = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); 136 if(!ret_arr) goto exit; 137 138 // Iterate through power_sources 139 for(i=0; i<count; i++) { 140 name = CFArrayGetValueAtIndex(array, i); 141 ps = isA_CFDictionary(IOPSGetPowerSourceDescription(power_sources, name)); 142 if(ps) { 143 transport_type = isA_CFString(CFDictionaryGetValue(ps, CFSTR(kIOPSTransportTypeKey))); 144 if(transport_type && ( CFEqual(transport_type, CFSTR(kIOPSSerialTransportType)) || 145 CFEqual(transport_type, CFSTR(kIOPSUSBTransportType)) || 146 CFEqual(transport_type, CFSTR(kIOPSNetworkTransportType)) ) ) 147 { 148 CFArrayAppendValue(ret_arr, name); 149 } 150 } 151 } 152 153 if(0 == CFArrayGetCount(ret_arr)) { 154 CFRelease(ret_arr); 155 ret_arr = NULL; 156 } 157 158exit: 159 CFRelease(array); 160 return ret_arr; 161} 162 163/* IOPSGetActiveUPS 164 * 165 * Argument: 166 * CFTypeRef power_sources: The return value from IOPSCoyPowerSourcesInfo() 167 * Return value: 168 * CFTypeRef (actually a CFStringRef): the UPS we're paying attention to, since we only 169 * support one UPS at a time. 170 * NULL if none are found 171 */ 172CFTypeRef 173IOPSGetActiveUPS(CFTypeRef ps_blob) 174{ 175 CFTypeRef ret_ups; 176 CFArrayRef ups_arr; 177 178 ups_arr = IOPSCopyUPSArray(ps_blob); 179 if(!ups_arr) 180 { 181 return NULL; 182 } 183 184 ret_ups = CFArrayGetValueAtIndex(ups_arr, 0); 185 186 CFRelease(ups_arr); 187 188 return ret_ups; 189} 190 191/* IOPSGetActiveBattery 192 * 193 * Argument: 194 * CFTypeRef power_sources: The return value from IOPSCoyPowerSourcesInfo() 195 * Return value: 196 * CFTypeRef (actually a CFStringRef): the UPS we're paying attention to, since we only 197 * support one UPS at a time. 198 * NULL if none are found 199 */ 200CFTypeRef 201IOPSGetActiveBattery(CFTypeRef ps_blob) 202{ 203 CFTypeRef ret_ups; 204 CFArrayRef ups_arr; 205 206 ups_arr = IOPSCopyInternalBatteriesArray(ps_blob); 207 if(!ups_arr) 208 { 209 return NULL; 210 } 211 212 ret_ups = CFArrayGetValueAtIndex(ups_arr, 0); 213 214 CFRelease(ups_arr); 215 216 return ret_ups; 217} 218 219/*** 220 * int powerSourceSupported(CFStringRef) 221 * takes: CFSTR of kIOPMACPowerKey, kIOPMBatteryPowerKey, kIOPMUPSPowerKey 222 * returns true if this machine supports (has) that power type. 223 */ 224CFBooleanRef IOPSPowerSourceSupported(CFTypeRef ps_blob, CFStringRef ps_type) 225{ 226 227 if(!isA_CFString(ps_type)) 228 { 229 return kCFBooleanFalse; 230 } 231 232 if(CFEqual(ps_type, CFSTR(kIOPMACPowerKey))) 233 { 234 return kCFBooleanTrue; 235 } 236 237#if defined (__i386__) || defined (__x86_64__) 238 if (CFEqual(ps_type, CFSTR(kIOPMBatteryPowerKey))) 239 { 240 CFBooleanRef ret = kCFBooleanFalse; 241 io_registry_entry_t platform = IO_OBJECT_NULL; 242 CFDataRef systemTypeData = NULL; 243 int *systemType = 0; 244 245 platform = IORegistryEntryFromPath(kIOMasterPortDefault, 246 kIODeviceTreePlane ":/"); 247 248 if (IO_OBJECT_NULL == platform) { 249 return kCFBooleanFalse; 250 } 251 252 systemTypeData = (CFDataRef)IORegistryEntryCreateCFProperty( 253 platform, CFSTR("system-type"), 254 kCFAllocatorDefault, kNilOptions); 255 if (systemTypeData 256 && (CFDataGetLength(systemTypeData) > 0) 257 && (systemType = (int *)CFDataGetBytePtr(systemTypeData)) 258 && (2 == *systemType)) 259 { 260 ret = kCFBooleanTrue; 261 } else { 262 ret = kCFBooleanFalse; 263 } 264 if (systemTypeData) 265 CFRelease(systemTypeData); 266 IOObjectRelease(platform); 267 return ret; 268 } 269#else 270 if (ps_blob 271 && CFEqual(ps_type, CFSTR(kIOPMBatteryPowerKey)) 272 && IOPSGetActiveBattery(ps_blob)) 273 { 274 return kCFBooleanTrue; 275 } 276#endif 277 278 if (ps_blob 279 && CFEqual(ps_type, CFSTR(kIOPMUPSPowerKey)) 280 && IOPSGetActiveUPS(ps_blob)) 281 { 282 return kCFBooleanTrue; 283 } 284 285 return kCFBooleanFalse; 286} 287 288/****************************************************************************** 289 290 IOPSPowerSourceID - publishing a power source from user space 291 292 ******************************************************************************/ 293 294/* _pm_connect 295 * implemented in IOPMLibPrivate.c 296 */ 297IOReturn _pm_connect(mach_port_t *newConnection); 298IOReturn _pm_disconnect(mach_port_t connection); 299 300/* OpaqueIOPSPowerSourceID 301 * As typecast in the header: 302 * typedef struct OpaqueIOPSPowerSourceID *IOPSPowerSourceID; 303 */ 304struct OpaqueIOPSPowerSourceID { 305 CFMachPortRef configdConnection; 306 int psid; 307}; 308 309#define kMaxPSTypeLength 25 310#define kMaxSCDSKeyLength 1024 311 312IOReturn IOPSCreatePowerSource( 313 IOPSPowerSourceID *outPS) 314{ 315 IOPSPowerSourceID newPS = NULL; 316 mach_port_t pm_server = MACH_PORT_NULL; 317 318 int return_code = kIOReturnSuccess; 319 kern_return_t kr = KERN_SUCCESS; 320 321 if (!outPS) { 322 return kIOReturnBadArgument; 323 } 324 325 // newPS - This tracking structure must be freed by IOPSReleasePowerSource() 326 newPS = calloc(1, sizeof(struct OpaqueIOPSPowerSourceID)); 327 328 if (!newPS) { 329 return kIOReturnVMError; 330 } 331 332 return_code = _pm_connect(&pm_server); 333 if(kIOReturnSuccess != return_code) { 334 return_code = kIOReturnNotOpen; 335 goto fail; 336 } 337 338 kr = io_ps_new_pspowersource( 339 pm_server, 340 &newPS->psid, // out: integer psid 341 &return_code); // out: Return code 342 343 if(KERN_SUCCESS != kr) { 344 return_code = kIOReturnNotResponding; 345 } 346 347 348fail: 349 if (IO_OBJECT_NULL != pm_server) { 350 _pm_disconnect(pm_server); 351 } 352 353 if (kIOReturnSuccess == return_code) { 354 *outPS = newPS; 355 } else { 356 *outPS = NULL; 357 if (newPS) { 358 free(newPS); 359 } 360 } 361 return return_code; 362} 363 364IOReturn IOPSSetPowerSourceDetails( 365 IOPSPowerSourceID whichPS, 366 CFDictionaryRef details) 367{ 368 IOReturn ret = kIOReturnSuccess; 369 CFDataRef flatDetails; 370 mach_port_t pm_server = MACH_PORT_NULL; 371 372 if (!whichPS || !isA_CFDictionary(details)) 373 return kIOReturnBadArgument; 374 375 flatDetails = IOCFSerialize(details, 0); 376 if (!flatDetails) { 377 ret = kIOReturnBadArgument; 378 goto exit; 379 } 380 381 ret = _pm_connect(&pm_server); 382 if(kIOReturnSuccess != ret) { 383 ret = kIOReturnNotOpen; 384 goto exit; 385 } 386 387 // Pass the details off to powerd 388 io_ps_update_pspowersource(pm_server, 389 whichPS->psid, 390 (vm_offset_t) CFDataGetBytePtr(flatDetails), 391 CFDataGetLength(flatDetails), 392 (int *)&ret); 393 394 _pm_disconnect(pm_server); 395 396exit: 397 if (flatDetails) 398 CFRelease(flatDetails); 399 return ret; 400} 401 402IOReturn IOPSReleasePowerSource( 403 IOPSPowerSourceID whichPS) 404{ 405 mach_port_t pm_server = MACH_PORT_NULL; 406 407 if (!whichPS) 408 return kIOReturnBadArgument; 409 410 if (kIOReturnSuccess == _pm_connect(&pm_server)) { 411 // Pass the details off to powerd 412 io_ps_release_pspowersource(pm_server, 413 whichPS->psid); 414 415 _pm_disconnect(pm_server); 416 } 417 418 419 free(whichPS); 420 return kIOReturnSuccess; 421} 422 423 424IOReturn IOPSCopyChargeLog(CFAbsoluteTime sinceTime, CFDictionaryRef *chargeLog) 425{ 426 427 IOReturn rc = kIOReturnInternalError; 428 CFDataRef unfolder = NULL; 429 vm_offset_t logPtr = NULL; 430 mach_port_t pm_server = MACH_PORT_NULL; 431 kern_return_t kern_result; 432 mach_msg_type_number_t logSize = 0; 433 434 *chargeLog = NULL; 435 _pm_connect(&pm_server); 436 437 if(pm_server == MACH_PORT_NULL) 438 return kIOReturnNotOpen; 439 440 kern_result = io_ps_copy_chargelog(pm_server, sinceTime, 441 &logPtr, &logSize, &rc); 442 443 if ((KERN_SUCCESS != kern_result) || (rc != kIOReturnSuccess)) { 444 goto exit; 445 } 446 447 if (logSize == 0) { 448 rc = kIOReturnNotFound; 449 goto exit; 450 } 451 452 unfolder = CFDataCreateWithBytesNoCopy(0, (const UInt8 *)logPtr, (CFIndex)logSize, kCFAllocatorNull); 453 if (unfolder) 454 { 455 *chargeLog = CFPropertyListCreateWithData(0, unfolder, 456 kCFPropertyListMutableContainersAndLeaves, 457 NULL, NULL); 458 CFRelease(unfolder); 459 } 460 461 462exit: 463 464 if (logPtr && logSize) { 465 vm_deallocate(mach_task_self(), logPtr, logSize); 466 } 467 468 if (MACH_PORT_NULL != pm_server) 469 _pm_disconnect(pm_server); 470 471 return rc; 472 473 474} 475