1 /* 2 * Copyright (c) 2001-2002 Apple Computer, Inc. All rights reserved. 3 * 4 * @APPLE_OSREFERENCE_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. The rights granted to you under the License 10 * may not be used to create, or enable the creation or redistribution of, 11 * unlawful or unlicensed copies of an Apple operating system, or to 12 * circumvent, violate, or enable the circumvention or violation of, any 13 * terms of an Apple operating system software license agreement. 14 * 15 * Please obtain a copy of the License at 16 * http://www.opensource.apple.com/apsl/ and read it before using this file. 17 * 18 * The Original Code and all software distributed under the License are 19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 23 * Please see the License for the specific language governing rights and 24 * limitations under the License. 25 * 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ 27 */ 28 29 #include "IOPMPowerStateQueue.h" 30 #include "IOKit/IOLocks.h" 31 #undef super 32 #define super IOEventSource 33 OSDefineMetaClassAndStructors(IOPMPowerStateQueue, IOEventSource); 34 35 #ifndef __ppc__ /* ppc does this right and doesn't need these routines */ 36 static 37 void * OSDequeueAtomic(void * volatile * inList, SInt32 inOffset) 38 { 39 /* The _pointer_ is volatile, not the listhead itself */ 40 void * volatile oldListHead; 41 void * volatile newListHead; 42 43 do { 44 oldListHead = *inList; 45 if (oldListHead == NULL) { 46 break; 47 } 48 49 newListHead = *(void * volatile *) (((char *) oldListHead) + inOffset); 50 } while (! OSCompareAndSwap((UInt32)oldListHead, 51 (UInt32)newListHead, (volatile UInt32 *)inList)); 52 return oldListHead; 53 } 54 55 static 56 void OSEnqueueAtomic(void * volatile * inList, void * inNewLink, SInt32 inOffset) 57 { 58 /* The _pointer_ is volatile, not the listhead itself */ 59 void * volatile oldListHead; 60 void * volatile newListHead = inNewLink; 61 void * volatile * newLinkNextPtr = (void * volatile *) (((char *) inNewLink) + inOffset); 62 63 do { 64 oldListHead = *inList; 65 *newLinkNextPtr = oldListHead; 66 } while (! OSCompareAndSwap((UInt32)oldListHead, (UInt32)newListHead, 67 (volatile UInt32 *)inList)); 68 } 69 #endif /* ! __ppc__ */ 70 71 72 IOPMPowerStateQueue *IOPMPowerStateQueue::PMPowerStateQueue(OSObject *inOwner) 73 { 74 IOPMPowerStateQueue *me = new IOPMPowerStateQueue; 75 76 if(me && !me->init(inOwner, 0) ) 77 { 78 me->release(); 79 return NULL; 80 } 81 82 return me; 83 } 84 85 bool IOPMPowerStateQueue::init(OSObject *owner, Action action) 86 { 87 if(!(super::init(owner, (IOEventSource::Action) action))) return false; 88 89 // Queue of powerstate changes 90 changes = NULL; 91 #ifndef __ppc__ 92 if (!(tmpLock = IOLockAlloc())) panic("IOPMPowerStateQueue::init can't alloc lock"); 93 #endif 94 return true; 95 } 96 97 98 bool IOPMPowerStateQueue::unIdleOccurred(IOService *inTarget, unsigned long inState) 99 { 100 PowerChangeEntry *new_one = NULL; 101 102 new_one = (PowerChangeEntry *)IOMalloc(sizeof(PowerChangeEntry)); 103 if(!new_one) return false; 104 105 new_one->actionType = IOPMPowerStateQueue::kUnIdle; 106 new_one->state = inState; 107 new_one->target = inTarget; 108 109 // Change to queue 110 #ifndef __ppc__ 111 IOLockLock(tmpLock); 112 #endif 113 OSEnqueueAtomic((void **)&changes, (void *)new_one, 0); 114 #ifndef __ppc__ 115 IOLockUnlock(tmpLock); 116 #endif 117 signalWorkAvailable(); 118 119 return true; 120 } 121 122 bool IOPMPowerStateQueue::featureChangeOccurred( 123 uint32_t inState, 124 IOService *inTarget) 125 { 126 PowerChangeEntry *new_one = NULL; 127 128 new_one = (PowerChangeEntry *)IOMalloc(sizeof(PowerChangeEntry)); 129 if(!new_one) return false; 130 131 new_one->actionType = IOPMPowerStateQueue::kPMFeatureChange; 132 new_one->state = inState; 133 new_one->target = inTarget; 134 135 // Change to queue 136 #ifdef __i386__ 137 IOLockLock(tmpLock); 138 #endif 139 OSEnqueueAtomic((void **)&changes, (void *)new_one, 0); 140 #ifdef __i386__ 141 IOLockUnlock(tmpLock); 142 #endif 143 signalWorkAvailable(); 144 145 return true; 146 } 147 148 149 // checkForWork() is called in a gated context 150 bool IOPMPowerStateQueue::checkForWork() 151 { 152 PowerChangeEntry *theNode; 153 uint32_t theState; 154 IOService *theTarget; 155 uint16_t theAction; 156 157 // Dequeue and process the state change request 158 #ifndef __ppc__ 159 IOLockLock(tmpLock); 160 #endif 161 if((theNode = (PowerChangeEntry *)OSDequeueAtomic((void **)&changes, 0))) 162 { 163 #ifndef __ppc__ 164 IOLockUnlock(tmpLock); 165 #endif 166 theState = theNode->state; 167 theTarget = theNode->target; 168 theAction = theNode->actionType; 169 IOFree((void *)theNode, sizeof(PowerChangeEntry)); 170 171 switch (theAction) 172 { 173 case kUnIdle: 174 theTarget->command_received((void *)theState, 0, 0, 0); 175 break; 176 177 case kPMFeatureChange: 178 theTarget->messageClients(theState, theTarget); 179 break; 180 } 181 } 182 #ifndef __ppc__ 183 else { 184 IOLockUnlock(tmpLock); 185 } 186 #endif 187 // Return true if there's more work to be done 188 if(changes) return true; 189 else return false; 190 } 191