1/*
2 * Copyright (c) 2006 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#include <CoreFoundation/CoreFoundation.h>
24
25#include <mach/mach.h>
26#include <mach/mach_init.h>
27#include <notify.h>
28
29#include <IOKit/IOKitLib.h>
30#include <IOKit/IOCFSerialize.h>
31#include <IOKit/pwr_mgt/IOPM.h>
32#include "IOSystemConfiguration.h"
33#include "IOPMLib.h"
34
35#define arrayCnt(var) (sizeof(var) / sizeof(var[0]))
36
37io_connect_t IOPMFindPowerManagement( mach_port_t master_device_port )
38{
39    io_connect_t    fb;
40    kern_return_t    kr;
41    io_service_t    obj = MACH_PORT_NULL;
42
43    obj = IORegistryEntryFromPath( master_device_port,
44                        kIOPowerPlane ":/IOPowerConnection/IOPMrootDomain");
45    if( obj ) {
46        kr = IOServiceOpen( obj,mach_task_self(), 0, &fb);
47        if ( kr == kIOReturnSuccess ) {
48            IOObjectRelease(obj);
49            return fb;
50        }
51        IOObjectRelease(obj);
52    }
53    return 0;
54}
55
56
57IOReturn IOPMGetAggressiveness (
58    io_connect_t fb,
59    unsigned long type,
60    unsigned long * lAggressiveness )
61{
62
63    uint64_t inData = type;
64    uint64_t aggressiveness = 0;
65    uint32_t len = 1;
66    kern_return_t err = IOConnectCallScalarMethod(fb, kPMGetAggressiveness,
67                &inData, 1, &aggressiveness, &len);
68    *lAggressiveness = aggressiveness;
69
70    if (err)
71        return kIOReturnError;
72    else
73        return err;
74}
75
76
77IOReturn IOPMSetAggressiveness (
78    io_connect_t fb,
79    unsigned long type,
80    unsigned long aggressiveness )
81{
82    uint64_t inData[] = { type, aggressiveness };
83    uint64_t rtn = 0;
84    uint32_t len = 1;
85    kern_return_t err = IOConnectCallScalarMethod(fb, kPMSetAggressiveness,
86               inData, arrayCnt(inData), &rtn, &len);
87
88    if (err)
89        return kIOReturnError;
90    else
91        return (IOReturn) rtn;
92}
93
94
95IOReturn IOPMSleepSystem ( io_connect_t fb )
96{
97    uint64_t rtn = 0;
98    uint32_t len = 1;
99    kern_return_t err = IOConnectCallScalarMethod(fb, kPMSleepSystem,
100                NULL, 0, &rtn, &len);
101
102    if (err)
103    return kIOReturnError;
104    else
105    return (IOReturn) rtn;
106}
107
108/* Private call for Apple Internal use only */
109IOReturn IOPMSleepSystemWithOptions ( io_connect_t fb, CFDictionaryRef options )
110{
111    uint64_t rtn = 0;
112    size_t len = sizeof(uint32_t);
113    kern_return_t err;
114    CFDataRef serializedOptions = NULL;
115
116    if( !options ) {
117        return IOPMSleepSystem( fb );
118    }
119
120    serializedOptions = IOCFSerialize( options, 0 );
121
122    if (!serializedOptions)
123    {
124        return kIOReturnInternalError;
125    }
126
127    /* kPMSleepSystemOptions
128     * in: serialized CFDictionary of options
129     * out: IOReturn code returned from sleepSystem
130     */
131    err = IOConnectCallStructMethod(
132                fb,
133                kPMSleepSystemOptions,
134                CFDataGetBytePtr(serializedOptions), /* inputStruct */
135                CFDataGetLength(serializedOptions), /* inputStructCnt */
136                &rtn, /* outputStruct */
137                &len); /* outputStructCnt */
138
139    CFRelease(serializedOptions);
140
141    if (kIOReturnSuccess != err)
142        return err;
143    else
144        return (IOReturn) rtn;
145}
146
147
148IOReturn IOPMCopyBatteryInfo( mach_port_t masterPort, CFArrayRef * oInfo )
149{
150    io_registry_entry_t         root_domain;
151    IOReturn                    kr = kIOReturnUnsupported;
152
153    *oInfo = NULL;
154
155    // ********************************************************************
156    // For PPC machines (with PMU), battery location is published under
157    // IOPMrootDomain
158    root_domain = IORegistryEntryFromPath( masterPort,
159                        kIOPowerPlane ":/IOPowerConnection/IOPMrootDomain");
160    if(!root_domain) return kIOReturnUnsupported;
161    *oInfo = IORegistryEntryCreateCFProperty(
162                            root_domain, CFSTR(kIOBatteryInfoKey),
163                            kCFAllocatorDefault, kNilOptions);
164    IOObjectRelease(root_domain);
165
166    if(*oInfo) {
167        // Successfully read battery info from IOPMrootDomain
168        return kIOReturnSuccess;
169    }
170
171
172    // ********************************************************************
173    // For non-PMU based batteries with IOPMPowerSource conforming classes
174    // Scan IORegistry for IOPMPowerSource nodes with IOLegacyBatteryInfo
175    // - Toss all IOLegacyBatteryInfo dictionaries into an OSArray
176    int                     batt_count = 0;
177    io_registry_entry_t     battery;
178    io_iterator_t           ioreg_batteries;
179    CFMutableArrayRef       legacyArray = CFArrayCreateMutable(
180                                kCFAllocatorDefault, 1, &kCFTypeArrayCallBacks);
181
182    if(!legacyArray) return kIOReturnNoMemory;
183
184    kr = IOServiceGetMatchingServices(
185                    MACH_PORT_NULL,
186                    IOServiceMatching("IOPMPowerSource"),
187                    &ioreg_batteries);
188    if(KERN_SUCCESS != kr) {
189        CFRelease(legacyArray);
190        return kIOReturnError;
191    }
192
193    while( (battery = (io_registry_entry_t)IOIteratorNext(ioreg_batteries)) )
194    {
195        CFDictionaryRef     legacyDict;
196
197        legacyDict = IORegistryEntryCreateCFProperty( battery,
198                                CFSTR(kIOPMPSLegacyBatteryInfoKey),
199                                kCFAllocatorDefault,
200                                0);
201
202        if(!legacyDict) continue;
203
204        batt_count++;
205        CFArrayAppendValue(legacyArray, legacyDict);
206        CFRelease(legacyDict);
207        IOObjectRelease(battery);
208    }
209    IOObjectRelease(ioreg_batteries);
210
211    if(batt_count > 0) {
212        *oInfo = legacyArray;
213    } else {
214        CFRelease(legacyArray);
215
216        // Returns kIOReturnUnsupported if no batteries found
217        return kIOReturnUnsupported;
218    }
219
220    return kIOReturnSuccess;
221}
222
223
224io_connect_t IORegisterApp(
225    void * refcon,
226    io_service_t theDriver,
227    IONotificationPortRef * thePortRef,
228    IOServiceInterestCallback callback,
229    io_object_t * notifier )
230{
231    io_connect_t        fb = MACH_PORT_NULL;
232    kern_return_t        kr;
233
234    *notifier = MACH_PORT_NULL;
235
236    if ( theDriver == MACH_PORT_NULL ) goto failure_exit;
237
238    kr = IOServiceOpen(theDriver, mach_task_self(), 0, &fb);
239
240    if ( (kr != kIOReturnSuccess) || (fb == MACH_PORT_NULL) )  {
241        goto failure_exit;
242    }
243
244    kr = IOServiceAddInterestNotification(
245                            *thePortRef, theDriver, kIOAppPowerStateInterest,
246                            callback, refcon, notifier);
247
248    if ( kr == KERN_SUCCESS ) {
249        // Successful exit case
250        return fb;
251    }
252
253failure_exit:
254    if ( fb != MACH_PORT_NULL ) {
255        IOServiceClose(fb);
256    }
257    if ( *notifier != MACH_PORT_NULL ) {
258        IOObjectRelease(*notifier);
259    }
260    return MACH_PORT_NULL;
261}
262
263
264io_connect_t IORegisterForSystemPower ( void * refcon,
265                                        IONotificationPortRef * thePortRef,
266                                        IOServiceInterestCallback callback,
267                                        io_object_t * root_notifier )
268{
269    io_connect_t                fb = MACH_PORT_NULL;
270    IONotificationPortRef       notify = NULL;
271    kern_return_t               kr;
272    io_service_t                obj = MACH_PORT_NULL;
273
274    *root_notifier = MACH_PORT_NULL;
275
276    notify = IONotificationPortCreate(MACH_PORT_NULL);
277
278    obj = IORegistryEntryFromPath( MACH_PORT_NULL,
279                    kIOPowerPlane ":/IOPowerConnection/IOPMrootDomain");
280
281    if( obj == MACH_PORT_NULL ) goto failure_exit;
282
283    kr = IOServiceOpen( obj,mach_task_self(), 0, &fb);
284
285    if ( (kr != kIOReturnSuccess) || (fb == MACH_PORT_NULL) )  {
286        goto failure_exit;
287    }
288
289    kr = IOServiceAddInterestNotification(
290                            notify,obj,kIOAppPowerStateInterest,
291                            callback,refcon,root_notifier);
292
293    IOObjectRelease(obj);
294    if ( kr == KERN_SUCCESS ) {
295        // Successful exit case
296        *thePortRef = notify;
297        return fb;
298    }
299
300failure_exit:
301    if ( obj != MACH_PORT_NULL ) {
302        IOObjectRelease(obj);
303    }
304    if ( notify != MACH_PORT_NULL ) {
305        IONotificationPortDestroy(notify);
306    }
307    if ( fb != MACH_PORT_NULL ) {
308        IOServiceClose(fb);
309    }
310    if ( *root_notifier != MACH_PORT_NULL ) {
311        IOObjectRelease(*root_notifier);
312    }
313
314    return MACH_PORT_NULL;
315}
316
317
318IOReturn IODeregisterApp ( io_object_t * notifier )
319{
320    if ( *notifier ) {
321        IOObjectRelease(*notifier);
322        *notifier = MACH_PORT_NULL;
323    }
324    return kIOReturnSuccess;
325}
326
327
328IOReturn IODeregisterForSystemPower ( io_object_t * root_notifier )
329{
330    if ( *root_notifier ) {
331        IOObjectRelease(*root_notifier);
332        *root_notifier = MACH_PORT_NULL;
333    }
334    return kIOReturnSuccess;
335}
336
337
338IOReturn IOAllowPowerChange(io_connect_t kernelPort, long notificationID)
339{
340    uint64_t inData = notificationID;
341    kern_return_t err = IOConnectCallScalarMethod(
342                                kernelPort, kPMAllowPowerChange,
343                                &inData, 1, NULL, NULL);
344
345    if (err) {
346        return kIOReturnError;
347    } else {
348        return err;
349    }
350}
351
352
353IOReturn IOCancelPowerChange ( io_connect_t kernelPort, long notificationID )
354{
355    uint64_t inData = notificationID;
356    kern_return_t err = IOConnectCallScalarMethod(
357                                kernelPort, kPMCancelPowerChange,
358                                &inData, 1, NULL, NULL);
359
360    if (err) {
361        return kIOReturnError;
362    } else {
363        return err;
364    }
365}
366
367
368boolean_t IOPMSleepEnabled ( void )
369{
370    io_registry_entry_t         root;
371    boolean_t                   flag = false;
372    CFTypeRef                   data = NULL;
373
374    root = IORegistryEntryFromPath(MACH_PORT_NULL,
375                    kIOPowerPlane ":/IOPowerConnection/IOPMrootDomain");
376    if ( !root ) return false;
377
378    data = IORegistryEntryCreateCFProperty(
379                            root, CFSTR("IOSleepSupported"),
380                            kCFAllocatorDefault, kNilOptions);
381    if ( data ) {
382        flag = true;
383        CFRelease(data);
384    }
385
386    IOObjectRelease(root);
387    return flag;
388}
389
390/**************************************************
391*
392* System Load Advisory
393*
394* Reads system load state out of SCDynamicStore.
395* PM configd plugin maintains state.
396*
397**************************************************/
398
399#define kSLALevelPath       CFSTR("/IOKit/PowerManagement/SystemLoad")
400#define kSLADetailedPath    CFSTR("/IOKit/PowerManagement/SystemLoad/Detailed")
401
402/* IOGetSystemLoadAdvisory
403 * In case of error, or inability to find system load advisory level,
404 * returns kIOSystemLoadAdvisoryLevelOK.
405 */
406IOSystemLoadAdvisoryLevel IOGetSystemLoadAdvisory( void )
407{
408    IOSystemLoadAdvisoryLevel   _gt = kIOSystemLoadAdvisoryLevelOK;
409    int                         notifyToken = 0;
410    int                         status;
411    uint64_t                    newval;
412
413    status = notify_register_check(kIOSystemLoadAdvisoryNotifyName, &notifyToken);
414    if (NOTIFY_STATUS_OK == status)
415    {
416        notify_get_state(notifyToken, &newval);
417        notify_cancel(notifyToken);
418        _gt = (IOSystemLoadAdvisoryLevel)newval;
419    }
420
421    return _gt;
422}
423
424/* IOCopyLoadAdvisoryLevelDetailed
425 * In case of error, or inability to find system load advisory level,
426 * returns NULL.
427 */
428CFDictionaryRef IOCopySystemLoadAdvisoryDetailed(void)
429{
430    CFDictionaryRef     gtDetailed = NULL;
431    SCDynamicStoreRef   storage = NULL;
432    CFStringRef         gtDetailedKey = SCDynamicStoreKeyCreate(
433                            kCFAllocatorDefault,
434                            CFSTR("%@%@"),
435                            _io_kSCDynamicStoreDomainState,
436                            kSLADetailedPath);
437
438    storage = SCDynamicStoreCreate(
439                            kCFAllocatorDefault,
440                            CFSTR("IOKit IOGetSystemLoadAdvisoryDetailed"),
441                            NULL,
442                            NULL);
443
444    if (!storage || !gtDetailedKey) {
445        goto exit;
446    }
447    gtDetailed = isA_CFDictionary(SCDynamicStoreCopyValue(storage, gtDetailedKey));
448exit:
449    if (gtDetailedKey) CFRelease(gtDetailedKey);
450    if (storage) CFRelease(storage);
451    return gtDetailed;
452}
453
454