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

Last change on this file since 59 was 53, checked in by Nicholas Riley, 21 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.