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