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

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

Pester 1.1b1.

PSPowerManager: Fixed delegate method selectors to better reflect what
is going on (Apple's docs in IOKit Fundamentals help with this; the
kIOMessage*Sleep constants are really poorly named).

VERSION: Updated for 1.1b1.

PSSpeechAlert.h: Fixed company name.

PSAlert.[hm]: Added -prepareForAlarm: to support PSWakeAlert.

PSTimer.[hm]: Replacement for NSTimer that works properly across
sleep/wake cycles and will schedule wake timers.

PSAlerts.[hm]: Added -prepareForAlarm: to support PSWakeAlert.

Read Me.rtfd: Updated for 1.1b1.

PSAlarm.[hm]: Added -setWakeUp:, invoke -[PSAlerts prepareForAlarm],
replaced alarm timer NSTimer with PSTimer.

PSApplication.[hm]: Replaced dock update timer NSTimer with PSTimer.
Uncovered some issues, need to fix later. Enable alarm discard for
beta release.

PSWakeAlert.[hm]: Shared alert implementation for wakeup. Doesn't do
anything at trigger time, but uses new preparation interface to work
at alarm set time (should work for repeating alarms too, but I didn't
bother to test...)

PSAlarmSetController.m: Added support for PSWakeAlert. Save default
alert information on quit. Removed debug statements on hide/unhide;
it works fine regardless of whether the app is explicitly hidden or
the window hides itself.

PSAlarms.m: PSTimer support - invoke +[PSTimer setUp] to initialize
timer list.

File size: 8.9 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(powerManagerWillDemandSleep:)]) {
223                [delegate powerManagerWillDemandSleep: self];
224                IOAllowPowerChange(root_port, (long)messageArgument);
225            }
226            break;
227        case kIOMessageCanSystemSleep:
228            if ([delegate respondsToSelector: @selector(powerManagerShouldIdleSleep:)]) {
229                if ([delegate powerManagerShouldIdleSleep: self]) {
230                    IOAllowPowerChange(root_port, (long)messageArgument);
231                } else {
232                    IOCancelPowerChange(root_port, (long)messageArgument);
233                }
234            }
235            break;
236        case kIOMessageSystemHasPoweredOn:
237            if ([delegate respondsToSelector: @selector(powerManagerDidWake:)])
238                [delegate powerManagerDidWake: self];
239            break;
240    }
241}
242
243void
244powerCallback(void *refCon, io_service_t service, natural_t messageType, void *messageArgument)
245{
246    [(PSPowerManager *)refCon _messageReceived: messageType withArgument: messageArgument];
247}
248
249- (id)initWithDelegate:(id)aDelegate;
250{
251    if ( (self = [super init]) != nil) {
252        IONotificationPortRef notificationPort;
253
254        delegate = [aDelegate retain];
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.