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