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

Last change on this file since 119 was 119, checked in by Nicholas Riley, 17 years ago

MoreSecurity?, wakein.[hm]: Removed authenticated wake timer setting
code: it was unneeded and incomplete, but remains in case it needs to
be resurrected later.

PSPowerManager.[hm]: removed -authorize, exception, references to
MoreSecurity? and wakein tool (-_execWakeToolWithRequestDictionary:).
Simplified writeDataProperty by using IOConnectSetCFProperty instead
of IOConnectSetCFProperties. Fixed logic error in override which
caused timer not to be set as needed.

PSTimer.m: Display alert panel if errors encountered while setting
wake time.

File size: 9.0 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
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
91    data = CFDataCreate(kCFAllocatorDefault, bytes, size);
92    NSCAssert(data != NULL, @"writeDataProperty: CFDataCreate failed");
93    [(NSData *)data autorelease];
94
95    kr = IOConnectSetCFProperty(handle, name, data);
96    NSCAssert1(kr == KERN_SUCCESS, @"writeDataProperty: IOConnectSetCFProperty returned an error of type %08lx", (unsigned long)kr);
97}
98
99/* ==========================================
100* Write a data property to the PMU driver
101* Arguments
102*     pmuReference - the IORegistry device to write to
103*     propertyName - Name of the property to write to
104*     data - Data to write
105*     dataSize - Data size
106* =========================================== */
107void
108writePMUProperty(io_object_t pmuReference, CFStringRef propertyName, void *data, size_t dataSize)
109{
110    io_connect_t conObj;
111    openDevice(pmuReference, PMU_MAGIC_PASSWORD, &conObj);
112    writeDataProperty(conObj, propertyName, (unsigned char *)data, dataSize);
113    closeDevice(conObj);
114}
115
116
117/* ==========================================
118* Look through the registry and search for an
119* IONetworkInterface objects with the given
120* name.
121* If a match is found, the object is returned.
122* =========================================== */
123
124io_service_t
125getInterfaceWithName(mach_port_t masterPort, char *className)
126{
127    io_service_t obj;
128
129    obj = IOServiceGetMatchingService(masterPort, IOServiceMatching(className));
130
131    NSCAssert(obj != NULL, @"getInterfaceWithName: IOServiceGetMatchingService returned NULL");
132
133    return obj;
134}
135
136/* ==========================================
137* Find the PMU in the IORegistry
138* =========================================== */
139io_service_t
140openPMUComPort(void)
141{
142    static mach_port_t masterPort;
143    kern_return_t kr;
144
145    // Get a master port to talk with the mach_kernel
146    kr = IOMasterPort(bootstrap_port, &masterPort);
147    NSCAssert1(kr == KERN_SUCCESS, @"openPMUComPort: IOMasterPort returned an error of type %08lx", (unsigned long)kr);
148
149    return getInterfaceWithName(masterPort, "ApplePMU");
150}
151
152
153/* ==========================================
154* Release our reference to the PMU in the IORegistry
155* =========================================== */
156void
157closePMUComPort(io_object_t pmuRef)
158{
159    IOObjectRelease(pmuRef);
160}
161
162@implementation PSPowerManager
163
164+ (BOOL)autoWakeSupported;
165{
166    io_service_t pmuReference = openPMUComPort();
167    if (pmuReference == NULL) return NO;
168    closePMUComPort(pmuReference);
169    return YES;
170}
171
172+ (io_service_t)_pmuReference;
173{
174    io_service_t pmuReference = openPMUComPort();
175    NSAssert(pmuReference != NULL, NSLocalizedString(@"Couldn't find PMU in IORegistry. This computer may not support automatic wake from sleep.", "Assertion message: couldn't open ApplePMU"));
176    return pmuReference;
177}
178
179+ (NSDate *)wakeTime;
180{
181    io_service_t pmuReference = [self _pmuReference];
182    NSNumber *autoWakeTime;
183    unsigned long long rawWakeTime;
184   
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 - [[NSTimeZone systemTimeZone] secondsFromGMT] - 18446744072475736320LLU];
193}
194
195+ (void)setWakeInterval:(unsigned long)wakeInterval;
196{
197    io_service_t pmuReference = [self _pmuReference];
198    writePMUProperty(pmuReference, CFSTR("AutoWake"), (unsigned long *)&wakeInterval, sizeof(wakeInterval));
199    closePMUComPort(pmuReference);
200}
201
202+ (void)setWakeTime:(NSDate *)time overrideIfEarlier:(BOOL)override;
203{
204    unsigned long wakeInterval;
205
206    if (time == nil) {
207        wakeInterval = 0;
208        override = YES;
209    } else {
210        wakeInterval = [time timeIntervalSinceNow];
211        if (wakeInterval == 0) wakeInterval++; // 0 will disable
212        if (!override) {
213            NSDate *wakeTime = [self wakeTime];
214            override = (wakeTime == nil || [wakeTime compare: time] == NSOrderedAscending);
215        }
216    }
217
218    if (override) {
219        [self setWakeInterval: wakeInterval];
220    }
221}
222
223+ (void)clearWakeTime;
224{
225    [self setWakeTime: nil overrideIfEarlier: YES];
226}
227
228// modified from RegisterForSleep sample code
229
230- (void)_messageReceived:(natural_t)messageType withArgument:(void *)messageArgument;
231{
232    switch (messageType) {
233        case kIOMessageSystemWillSleep:
234            if ([delegate respondsToSelector: @selector(powerManagerWillDemandSleep:)]) {
235                [delegate powerManagerWillDemandSleep: self];
236                IOAllowPowerChange(root_port, (long)messageArgument);
237            }
238            break;
239        case kIOMessageCanSystemSleep:
240            if ([delegate respondsToSelector: @selector(powerManagerShouldIdleSleep:)]) {
241                if ([delegate powerManagerShouldIdleSleep: self]) {
242                    IOAllowPowerChange(root_port, (long)messageArgument);
243                } else {
244                    IOCancelPowerChange(root_port, (long)messageArgument);
245                }
246            }
247            break;
248        case kIOMessageSystemHasPoweredOn:
249            if ([delegate respondsToSelector: @selector(powerManagerDidWake:)])
250                [delegate powerManagerDidWake: self];
251            break;
252    }
253}
254
255void
256powerCallback(void *refCon, io_service_t service, natural_t messageType, void *messageArgument)
257{
258    [(PSPowerManager *)refCon _messageReceived: messageType withArgument: messageArgument];
259}
260
261- (id)initWithDelegate:(id)aDelegate;
262{
263    if ( (self = [super init]) != nil) {
264        IONotificationPortRef notificationPort;
265
266        delegate = [aDelegate retain];
267        root_port = IORegisterForSystemPower(self, &notificationPort, powerCallback, &notifier);
268        NSAssert(root_port != NULL, @"IORegisterForSystemPower failed");
269
270        CFRunLoopAddSource(CFRunLoopGetCurrent(), IONotificationPortGetRunLoopSource(notificationPort), kCFRunLoopDefaultMode);
271    }
272    return self;
273}
274
275- (void)dealloc;
276{
277    IODeregisterForSystemPower(&notifier);
278    [delegate release];
279    [super dealloc];
280}
281
282@end
Note: See TracBrowser for help on using the repository browser.