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     if (inWorkLoop) super::setWorkLoop(inWorkLoop);
171 
172     if (provider) {
173 	if (!inWorkLoop) {
174 	    if (intIndex >= 0) {
175 		provider->unregisterInterrupt(intIndex);
176 		intIndex = ~intIndex;
177 	    }
178 	} else if ((intIndex < 0) && (kIOReturnSuccess == registerInterruptHandler(provider, ~intIndex))) {
179 	    intIndex = ~intIndex;
180 	}
181     }
182 
183     if (!inWorkLoop) super::setWorkLoop(inWorkLoop);
184 }
185 
186 const IOService *IOInterruptEventSource::getProvider() const
187 {
188     return provider;
189 }
190 
191 int IOInterruptEventSource::getIntIndex() const
192 {
193     return intIndex;
194 }
195 
196 bool IOInterruptEventSource::getAutoDisable() const
197 {
198     return autoDisable;
199 }
200 
201 bool IOInterruptEventSource::checkForWork()
202 {
203     unsigned int cacheProdCount = producerCount;
204     int numInts = cacheProdCount - consumerCount;
205     IOInterruptEventAction intAction = (IOInterruptEventAction) action;
206 	bool trace = (gIOKitTrace & kIOTraceIntEventSource) ? true : false;
207 
208     IOStatisticsCheckForWork();
209 
210 	if ( numInts > 0 )
211 	{
212 		if (trace)
213 			IOTimeStampStartConstant(IODBG_INTES(IOINTES_ACTION),
214 						 VM_KERNEL_UNSLIDE(intAction), (uintptr_t) owner, (uintptr_t) this, (uintptr_t) workLoop);
215 
216 		// Call the handler
217 		(*intAction)(owner, this, numInts);
218 
219 		if (trace)
220 			IOTimeStampEndConstant(IODBG_INTES(IOINTES_ACTION),
221 					       VM_KERNEL_UNSLIDE(intAction), (uintptr_t) owner, (uintptr_t) this, (uintptr_t) workLoop);
222 
223 		consumerCount = cacheProdCount;
224 		if (autoDisable && !explicitDisable)
225 			enable();
226 	}
227 
228 	else if ( numInts < 0 )
229 	{
230 		if (trace)
231 			IOTimeStampStartConstant(IODBG_INTES(IOINTES_ACTION),
232 						 VM_KERNEL_UNSLIDE(intAction), (uintptr_t) owner, (uintptr_t) this, (uintptr_t) workLoop);
233 
234 		// Call the handler
235 		(*intAction)(owner, this, -numInts);
236 
237 		if (trace)
238 			IOTimeStampEndConstant(IODBG_INTES(IOINTES_ACTION),
239 					       VM_KERNEL_UNSLIDE(intAction), (uintptr_t) owner, (uintptr_t) this, (uintptr_t) workLoop);
240 
241 		consumerCount = cacheProdCount;
242 		if (autoDisable && !explicitDisable)
243 			enable();
244 	}
245 
246     return false;
247 }
248 
249 void IOInterruptEventSource::normalInterruptOccurred
250     (void */*refcon*/, IOService */*prov*/, int /*source*/)
251 {
252 	bool trace = (gIOKitTrace & kIOTraceIntEventSource) ? true : false;
253 
254     IOStatisticsInterrupt();
255     producerCount++;
256 
257 	if (trace)
258 	    IOTimeStampStartConstant(IODBG_INTES(IOINTES_SEMA), (uintptr_t) this, (uintptr_t) owner);
259 
260     signalWorkAvailable();
261 
262 	if (trace)
263 	    IOTimeStampEndConstant(IODBG_INTES(IOINTES_SEMA), (uintptr_t) this, (uintptr_t) owner);
264 }
265 
266 void IOInterruptEventSource::disableInterruptOccurred
267     (void */*refcon*/, IOService *prov, int source)
268 {
269 	bool trace = (gIOKitTrace & kIOTraceIntEventSource) ? true : false;
270 
271     prov->disableInterrupt(source);	/* disable the interrupt */
272 
273     IOStatisticsInterrupt();
274     producerCount++;
275 
276 	if (trace)
277 	    IOTimeStampStartConstant(IODBG_INTES(IOINTES_SEMA), (uintptr_t) this, (uintptr_t) owner);
278 
279     signalWorkAvailable();
280 
281 	if (trace)
282 	    IOTimeStampEndConstant(IODBG_INTES(IOINTES_SEMA), (uintptr_t) this, (uintptr_t) owner);
283 }
284 
285 void IOInterruptEventSource::interruptOccurred
286     (void *refcon, IOService *prov, int source)
287 {
288     if (autoDisable && prov)
289         disableInterruptOccurred(refcon, prov, source);
290     else
291         normalInterruptOccurred(refcon, prov, source);
292 }
293 
294 IOReturn IOInterruptEventSource::warmCPU
295     (uint64_t abstime)
296 {
297 
298 	return ml_interrupt_prewarm(abstime);
299 }
300