1 /*
2  * Copyright (c) 1998-2010 Apple 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 <IOKit/IOInterruptEventSource.h>
30 #include <IOKit/IOKitDebug.h>
31 #include <IOKit/IOLib.h>
32 #include <IOKit/IOService.h>
33 #include <IOKit/IOInterrupts.h>
34 #include <IOKit/IOTimeStamp.h>
35 #include <IOKit/IOWorkLoop.h>
36 
37 #if IOKITSTATS
38 
39 #define IOStatisticsInitializeCounter() \
40 do { \
41 	IOStatistics::setCounterType(IOEventSource::reserved->counter, kIOStatisticsInterruptEventSourceCounter); \
42 } while (0)
43 
44 #define IOStatisticsCheckForWork() \
45 do { \
46 	IOStatistics::countInterruptCheckForWork(IOEventSource::reserved->counter); \
47 } while (0)
48 
49 #define IOStatisticsInterrupt() \
50 do { \
51 	IOStatistics::countInterrupt(IOEventSource::reserved->counter); \
52 } while (0)
53 
54 #else
55 
56 #define IOStatisticsInitializeCounter()
57 #define IOStatisticsCheckForWork()
58 #define IOStatisticsInterrupt()
59 
60 #endif // IOKITSTATS
61 
62 #define super IOEventSource
63 
64 OSDefineMetaClassAndStructors(IOInterruptEventSource, IOEventSource)
65 OSMetaClassDefineReservedUnused(IOInterruptEventSource, 0);
66 OSMetaClassDefineReservedUnused(IOInterruptEventSource, 1);
67 OSMetaClassDefineReservedUnused(IOInterruptEventSource, 2);
68 OSMetaClassDefineReservedUnused(IOInterruptEventSource, 3);
69 OSMetaClassDefineReservedUnused(IOInterruptEventSource, 4);
70 OSMetaClassDefineReservedUnused(IOInterruptEventSource, 5);
71 OSMetaClassDefineReservedUnused(IOInterruptEventSource, 6);
72 OSMetaClassDefineReservedUnused(IOInterruptEventSource, 7);
73 
74 bool IOInterruptEventSource::init(OSObject *inOwner,
75 				  Action inAction,
76 				  IOService *inProvider,
77 				  int inIntIndex)
78 {
79     bool res = true;
80 
81     if ( !super::init(inOwner, (IOEventSourceAction) inAction) )
82         return false;
83 
84     provider = inProvider;
85     producerCount = consumerCount = 0;
86     autoDisable = explicitDisable = false;
87     intIndex = ~inIntIndex;
88 
89     // Assumes inOwner holds a reference(retain) on the provider
90     if (inProvider) {
91         res = (kIOReturnSuccess == registerInterruptHandler(inProvider, inIntIndex));
92 	if (res)
93 	    intIndex = inIntIndex;
94     }
95 
96     IOStatisticsInitializeCounter();
97 
98     return res;
99 }
100 
101 IOReturn IOInterruptEventSource::registerInterruptHandler(IOService *inProvider,
102 				  int inIntIndex)
103 {
104     IOReturn ret;
105     int intType;
106     IOInterruptAction intHandler;
107 
108     ret = inProvider->getInterruptType(inIntIndex, &intType);
109     if (kIOReturnSuccess != ret)
110 	return (ret);
111 
112     autoDisable = (intType == kIOInterruptTypeLevel);
113     if (autoDisable) {
114 	intHandler = OSMemberFunctionCast(IOInterruptAction,
115 	    this, &IOInterruptEventSource::disableInterruptOccurred);
116     }
117     else
118 	intHandler = OSMemberFunctionCast(IOInterruptAction,
119 	    this, &IOInterruptEventSource::normalInterruptOccurred);
120 
121     ret = provider->registerInterrupt(inIntIndex, this, intHandler);
122 
123     return (ret);
124 }
125 
126 IOInterruptEventSource *
127 IOInterruptEventSource::interruptEventSource(OSObject *inOwner,
128 					     Action inAction,
129 					     IOService *inProvider,
130 					     int inIntIndex)
131 {
132     IOInterruptEventSource *me = new IOInterruptEventSource;
133 
134     if (me && !me->init(inOwner, inAction, inProvider, inIntIndex)) {
135         me->release();
136         return 0;
137     }
138 
139     return me;
140 }
141 
142 void IOInterruptEventSource::free()
143 {
144     if (provider && intIndex >= 0)
145         provider->unregisterInterrupt(intIndex);
146 
147     super::free();
148 }
149 
150 void IOInterruptEventSource::enable()
151 {
152     if (provider && intIndex >= 0) {
153         provider->enableInterrupt(intIndex);
154         explicitDisable = false;
155         enabled = true;
156     }
157 }
158 
159 void IOInterruptEventSource::disable()
160 {
161     if (provider && intIndex >= 0) {
162         provider->disableInterrupt(intIndex);
163         explicitDisable = true;
164         enabled = false;
165     }
166 }
167 
168 void IOInterruptEventSource::setWorkLoop(IOWorkLoop *inWorkLoop)
169 {
170     super::setWorkLoop(inWorkLoop);
171 
172     if (!provider)
173     	return;
174 
175     if ( !inWorkLoop ) {
176 	if (intIndex >= 0) {
177 	    provider->unregisterInterrupt(intIndex);
178 	    intIndex = ~intIndex;
179 	}
180     } else if ((intIndex < 0) && (kIOReturnSuccess == registerInterruptHandler(provider, ~intIndex))) {
181 	intIndex = ~intIndex;
182     }
183 }
184 
185 const IOService *IOInterruptEventSource::getProvider() const
186 {
187     return provider;
188 }
189 
190 int IOInterruptEventSource::getIntIndex() const
191 {
192     return intIndex;
193 }
194 
195 bool IOInterruptEventSource::getAutoDisable() const
196 {
197     return autoDisable;
198 }
199 
200 bool IOInterruptEventSource::checkForWork()
201 {
202     unsigned int cacheProdCount = producerCount;
203     int numInts = cacheProdCount - consumerCount;
204     IOInterruptEventAction intAction = (IOInterruptEventAction) action;
205 	bool trace = (gIOKitTrace & kIOTraceIntEventSource) ? true : false;
206 
207     IOStatisticsCheckForWork();
208 
209 	if ( numInts > 0 )
210 	{
211 		if (trace)
212 			IOTimeStampStartConstant(IODBG_INTES(IOINTES_ACTION),
213 						 VM_KERNEL_UNSLIDE(intAction), (uintptr_t) owner, (uintptr_t) this, (uintptr_t) workLoop);
214 
215 		// Call the handler
216 		(*intAction)(owner, this, numInts);
217 
218 		if (trace)
219 			IOTimeStampEndConstant(IODBG_INTES(IOINTES_ACTION),
220 					       VM_KERNEL_UNSLIDE(intAction), (uintptr_t) owner, (uintptr_t) this, (uintptr_t) workLoop);
221 
222 		consumerCount = cacheProdCount;
223 		if (autoDisable && !explicitDisable)
224 			enable();
225 	}
226 
227 	else if ( numInts < 0 )
228 	{
229 		if (trace)
230 			IOTimeStampStartConstant(IODBG_INTES(IOINTES_ACTION),
231 						 VM_KERNEL_UNSLIDE(intAction), (uintptr_t) owner, (uintptr_t) this, (uintptr_t) workLoop);
232 
233 		// Call the handler
234 		(*intAction)(owner, this, -numInts);
235 
236 		if (trace)
237 			IOTimeStampEndConstant(IODBG_INTES(IOINTES_ACTION),
238 					       VM_KERNEL_UNSLIDE(intAction), (uintptr_t) owner, (uintptr_t) this, (uintptr_t) workLoop);
239 
240 		consumerCount = cacheProdCount;
241 		if (autoDisable && !explicitDisable)
242 			enable();
243 	}
244 
245     return false;
246 }
247 
248 void IOInterruptEventSource::normalInterruptOccurred
249     (void */*refcon*/, IOService */*prov*/, int /*source*/)
250 {
251 	bool trace = (gIOKitTrace & kIOTraceIntEventSource) ? true : false;
252 
253     IOStatisticsInterrupt();
254     producerCount++;
255 
256 	if (trace)
257 	    IOTimeStampStartConstant(IODBG_INTES(IOINTES_SEMA), (uintptr_t) this, (uintptr_t) owner);
258 
259     signalWorkAvailable();
260 
261 	if (trace)
262 	    IOTimeStampEndConstant(IODBG_INTES(IOINTES_SEMA), (uintptr_t) this, (uintptr_t) owner);
263 }
264 
265 void IOInterruptEventSource::disableInterruptOccurred
266     (void */*refcon*/, IOService *prov, int source)
267 {
268 	bool trace = (gIOKitTrace & kIOTraceIntEventSource) ? true : false;
269 
270     prov->disableInterrupt(source);	/* disable the interrupt */
271 
272     IOStatisticsInterrupt();
273     producerCount++;
274 
275 	if (trace)
276 	    IOTimeStampStartConstant(IODBG_INTES(IOINTES_SEMA), (uintptr_t) this, (uintptr_t) owner);
277 
278     signalWorkAvailable();
279 
280 	if (trace)
281 	    IOTimeStampEndConstant(IODBG_INTES(IOINTES_SEMA), (uintptr_t) this, (uintptr_t) owner);
282 }
283 
284 void IOInterruptEventSource::interruptOccurred
285     (void *refcon, IOService *prov, int source)
286 {
287     if (autoDisable && prov)
288         disableInterruptOccurred(refcon, prov, source);
289     else
290         normalInterruptOccurred(refcon, prov, source);
291 }
292 
293 IOReturn IOInterruptEventSource::warmCPU
294     (uint64_t abstime)
295 {
296 
297 	return ml_interrupt_prewarm(abstime);
298 }
299