source: trunk/Cocoa/Pester/Source/PSPowerManager.m @ 53

Last change on this file since 53 was 53, checked in by Nicholas Riley, 19 years ago

Updated for Pester 1.1a5 (very limited release).

Pester 1.1a4 was never released.

File size: 8.8 KB
Line 
1//
2//  PSPowerManager.m
3//  Pester
4//
5//  Created by Nicholas Riley on Mon Dec 23 2002.
6//  Copyright (c) 2002 Nicholas Riley. All rights reserved.
7//
8
9#import "PSPowerManager.h"
10
11#import <IOKit/pwr_mgt/IOPMLib.h>
12#import <IOKit/IOMessage.h>
13#import <CoreFoundation/CoreFoundation.h>
14
15/*
16 * Copyright (c) 2002 Apple Computer, Inc. All rights reserved.
17 *
18 * @APPLE_LICENSE_HEADER_START@
19 *
20 * The contents of this file constitute Original Code as defined in and
21 * are subject to the Apple Public Source License Version 1.1 (the
22 * "License").  You may not use this file except in compliance with the
23 * License.  Please obtain a copy of the License at
24 * http://www.apple.com/publicsource and read it before using this file.
25 *
26 * This Original Code and all software distributed under the License are
27 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
28 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
29 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
30 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
31 * License for the specific language governing rights and limitations
32 * under the License.
33 *
34 * @APPLE_LICENSE_HEADER_END@
35 */
36
37/* Sample code to set an automatic wakeup timer to wake machines from sleep.
38
39When a machine is asleep, most hardware (including the processor) is
40powered off. The PMU chip is one of the few things left powered on, and it's
41able to generate a wakeup event on a timer.
42This code shows how to set the wakeup timer within the PMU.
43*/
44
45// From autowake.cpp:
46
47// #define kAppleVIAUserClientMagicCookie 0x101face // or 0x101beef -- for PMU
48// #define kAppleVIAUserClientMagicCookie 0x101beef // or 0x101face  -- for PMU
49
50// The difference is 101beef is only for superusers and 101face works for
51// non-privileged users.  I have not determined which calls are only available
52// for superusers
53
54#define PMU_MAGIC_PASSWORD      0x0101FACE // BEEF
55
56/* ==========================================
57* Close a device user client
58* =========================================== */
59static kern_return_t
60closeDevice(io_connect_t con)
61{
62    kern_return_t ret = IOServiceClose(con);
63
64    NSCAssert1(ret == kIOReturnSuccess, @"closeDevice: IOServiceClose returned an error of type %08lx", (unsigned long)ret);
65
66    return ret;
67}
68
69/* ==========================================
70* Open an IORegistry device user client
71* =========================================== */
72static void
73openDevice(io_object_t obj, unsigned int type, io_connect_t * con)
74{
75    kern_return_t ret = IOServiceOpen(obj, mach_task_self(), type, con);
76
77    NSCAssert1(ret == kIOReturnSuccess, @"openDevice: IOServiceOpen returned an error of type %08lx", (unsigned long)ret);
78}
79
80/* ===========================================
81* Changes the string for a registry
82* property.
83* ===========================================  */
84void
85writeDataProperty(io_object_t handle, CFStringRef name,
86                  unsigned char * bytes, unsigned int size)
87{
88    kern_return_t kr = kIOReturnNoMemory;
89    CFDataRef data;
90    CFMutableDictionaryRef dict = 0;
91
92    data = CFDataCreate(kCFAllocatorDefault, bytes, size);
93    NSCAssert(data != NULL, @"writeDataProperty: CFDataCreate failed");
94    [(NSData *)data autorelease];
95
96    dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
97    NSCAssert(data != NULL, @"writeDataProperty: CFDictionaryCreateMutable failed");
98    [(NSMutableDictionary *)dict autorelease];
99
100    CFDictionarySetValue(dict, name, data);
101    kr = IOConnectSetCFProperties(handle, dict);
102    NSCAssert1(kr == KERN_SUCCESS, @"writeDataProperty: IOConnectSetCFProperties returned an error of type %08lx", (unsigned long)kr);
103}
104
105/* ==========================================
106* Write a data property to the PMU driver
107* Arguments
108*     pmuReference - the IORegistry device to write to
109*     propertyName - Name of the property to write to
110*     data - Data to write
111*     dataSize - Data size
112* =========================================== */
113void
114writePMUProperty(io_object_t pmuReference, CFStringRef propertyName, void *data, size_t dataSize)
115{
116    io_connect_t conObj;
117    openDevice(pmuReference, PMU_MAGIC_PASSWORD, &conObj);
118    writeDataProperty(conObj, propertyName, (unsigned char *)data, dataSize);
119    closeDevice(conObj);
120}
121
122
123/* ==========================================
124* Look through the registry and search for an
125* IONetworkInterface objects with the given
126* name.
127* If a match is found, the object is returned.
128* =========================================== */
129
130io_service_t
131getInterfaceWithName(mach_port_t masterPort, char *className)
132{
133    io_service_t obj;
134
135    obj = IOServiceGetMatchingService(masterPort, IOServiceMatching(className));
136
137    NSCAssert(obj != NULL, @"getInterfaceWithName: IOServiceGetMatchingService returned NULL");
138
139    return obj;
140}
141
142/* ==========================================
143* Find the PMU in the IORegistry
144* =========================================== */
145io_service_t
146openPMUComPort(void)
147{
148    static mach_port_t masterPort;
149    kern_return_t kr;
150
151    // Get a master port to talk with the mach_kernel
152    kr = IOMasterPort(bootstrap_port, &masterPort);
153    NSCAssert1(kr == KERN_SUCCESS, @"openPMUComPort: IOMasterPort returned an error of type %08lx", (unsigned long)kr);
154
155    return getInterfaceWithName(masterPort, "ApplePMU");
156}
157
158
159/* ==========================================
160* Release our reference to the PMU in the IORegistry
161* =========================================== */
162void
163closePMUComPort(io_object_t pmuRef)
164{
165    IOObjectRelease(pmuRef);
166}
167
168@implementation PSPowerManager
169
170+ (BOOL)autoWakeSupported;
171{
172    io_service_t pmuReference = openPMUComPort();
173    if (pmuReference == NULL) return NO;
174    closePMUComPort(pmuReference);
175    return YES;
176}
177
178+ (NSDate *)wakeTime;
179{
180    io_service_t pmuReference = openPMUComPort();
181    NSNumber *autoWakeTime;
182    unsigned long long rawWakeTime;
183   
184    NSAssert(pmuReference != NULL, @"Couldn’t find PMU in IORegistry. This computer may not support automatic wake from sleep.");
185    autoWakeTime = (NSNumber *)IORegistryEntryCreateCFProperty(pmuReference, CFSTR("AutoWake"), NULL, 0);
186    closePMUComPort(pmuReference);
187
188    if (autoWakeTime == nil) return nil;
189    rawWakeTime = [autoWakeTime unsignedLongLongValue];
190    if (rawWakeTime == 0) return nil;
191    // XXX no idea what the epoch is supposed to be, but this works...
192    return [NSDate dateWithTimeIntervalSinceReferenceDate: rawWakeTime - 18446744072475718320LLU];
193}
194
195+ (void)setWakeTime:(NSDate *)time;
196{
197    unsigned long wakeTime;
198    io_service_t pmuReference = openPMUComPort();
199    NSAssert(pmuReference != NULL, @"Couldn’t find PMU in IORegistry. This computer may not support automatic wake from sleep.");
200
201    if (time == nil) wakeTime = 0;
202    else {
203        wakeTime = [time timeIntervalSinceNow];
204        if (wakeTime == 0) wakeTime++; // 0 will disable
205    }
206    writePMUProperty(pmuReference, CFSTR("AutoWake"), (unsigned long *)&wakeTime, sizeof(wakeTime));
207   
208    closePMUComPort(pmuReference);
209}
210
211+ (void)clearWakeTime;
212{
213    [self setWakeTime: nil];
214}
215
216// modified from RegisterForSleep sample code
217
218- (void)_messageReceived:(natural_t)messageType withArgument:(void *)messageArgument;
219{
220    switch (messageType) {
221        case kIOMessageSystemWillSleep:
222            if ([delegate respondsToSelector: @selector(powerManagerWillSleep:)])
223                [delegate powerManagerWillSleep: self];
224            IOAllowPowerChange(root_port, (long)messageArgument);
225            break;
226        case kIOMessageCanSystemSleep:
227            if ([delegate respondsToSelector: @selector(powerManagerShouldSleep:)]) {
228                if ([delegate powerManagerShouldSleep: self]) {
229                    IOAllowPowerChange(root_port, (long)messageArgument);
230                } else {
231                    IOCancelPowerChange(root_port, (long)messageArgument);
232                }
233            }
234            break;
235        case kIOMessageSystemHasPoweredOn:
236            if ([delegate respondsToSelector: @selector(powerManagerDidWake:)])
237                [delegate powerManagerDidWake: self];
238            break;
239    }
240}
241
242void
243powerCallback(void *refCon, io_service_t service, natural_t messageType, void *messageArgument)
244{
245    [(PSPowerManager *)refCon _messageReceived: messageType withArgument: messageArgument];
246}
247
248- (id)initWithDelegate:(id)aDelegate;
249{
250    if ( (self = [super init]) != nil) {
251        IONotificationPortRef notificationPort;
252
253        delegate = [aDelegate retain];
254
255        root_port = IORegisterForSystemPower(self, &notificationPort, powerCallback, &notifier);
256        NSAssert(root_port != NULL, @"IORegisterForSystemPower failed");
257
258        CFRunLoopAddSource(CFRunLoopGetCurrent(), IONotificationPortGetRunLoopSource(notificationPort), kCFRunLoopDefaultMode);
259    }
260    return self;
261}
262
263- (void)dealloc;
264{
265    IODeregisterForSystemPower(&notifier);
266    [delegate release];
267    [super dealloc];
268}
269
270@end
Note: See TracBrowser for help on using the repository browser.