1 /*
2  * Copyright (c) 1998-2000 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 Copyright (c) 1998 Apple Computer, Inc.  All rights reserved.
30 
31 HISTORY
32     1998-7-13	Godfrey van der Linden(gvdl)
33         Created.
34 */
35 #include <IOKit/IOInterruptEventSource.h>
36 #include <IOKit/IOLib.h>
37 #include <IOKit/IOService.h>
38 #include <IOKit/IOInterrupts.h>
39 #include <IOKit/IOTimeStamp.h>
40 #include <IOKit/IOWorkLoop.h>
41 
42 #if KDEBUG
43 
44 #define IOTimeTypeStampS(t)						\
45 do {									\
46     IOTimeStampStart(IODBG_INTES(t),					\
47                      (uintptr_t) this, (uintptr_t) owner);	\
48 } while(0)
49 
50 #define IOTimeTypeStampE(t)						\
51 do {									\
52     IOTimeStampEnd(IODBG_INTES(t),					\
53                    (uintptr_t) this, (uintptr_t) owner);		\
54 } while(0)
55 
56 #define IOTimeStampLatency()						\
57 do {									\
58     IOTimeStampEnd(IODBG_INTES(IOINTES_LAT),				\
59                    (uintptr_t) this, (uintptr_t) owner);		\
60 } while(0)
61 
62 #else /* !KDEBUG */
63 #define IOTimeTypeStampS(t)
64 #define IOTimeTypeStampE(t)
65 #define IOTimeStampLatency()
66 #endif /* KDEBUG */
67 
68 #define super IOEventSource
69 
70 OSDefineMetaClassAndStructors(IOInterruptEventSource, IOEventSource)
71 OSMetaClassDefineReservedUnused(IOInterruptEventSource, 0);
72 OSMetaClassDefineReservedUnused(IOInterruptEventSource, 1);
73 OSMetaClassDefineReservedUnused(IOInterruptEventSource, 2);
74 OSMetaClassDefineReservedUnused(IOInterruptEventSource, 3);
75 OSMetaClassDefineReservedUnused(IOInterruptEventSource, 4);
76 OSMetaClassDefineReservedUnused(IOInterruptEventSource, 5);
77 OSMetaClassDefineReservedUnused(IOInterruptEventSource, 6);
78 OSMetaClassDefineReservedUnused(IOInterruptEventSource, 7);
79 
80 bool IOInterruptEventSource::init(OSObject *inOwner,
81 				  Action inAction,
82 				  IOService *inProvider,
83 				  int inIntIndex)
84 {
85     bool res = true;
86 
87     if ( !super::init(inOwner, (IOEventSourceAction) inAction) )
88         return false;
89 
90     provider = inProvider;
91     producerCount = consumerCount = 0;
92     autoDisable = explicitDisable = false;
93     intIndex = -1;
94 
95     // Assumes inOwner holds a reference(retain) on the provider
96     if (inProvider) {
97         int intType;
98 
99         res = (kIOReturnSuccess
100                     == inProvider->getInterruptType(inIntIndex, &intType));
101         if (res) {
102             IOInterruptAction intHandler;
103 
104             autoDisable = (intType == kIOInterruptTypeLevel);
105             if (autoDisable) {
106                 intHandler = OSMemberFunctionCast(IOInterruptAction,
107 		    this, &IOInterruptEventSource::disableInterruptOccurred);
108             }
109             else
110                 intHandler = OSMemberFunctionCast(IOInterruptAction,
111 		    this, &IOInterruptEventSource::normalInterruptOccurred);
112 
113             res = (kIOReturnSuccess == inProvider->registerInterrupt
114                                         (inIntIndex, this, intHandler));
115             if (res)
116                 intIndex = inIntIndex;
117         }
118     }
119 
120     return res;
121 }
122 
123 IOInterruptEventSource *
124 IOInterruptEventSource::interruptEventSource(OSObject *inOwner,
125 					     Action inAction,
126 					     IOService *inProvider,
127 					     int inIntIndex)
128 {
129     IOInterruptEventSource *me = new IOInterruptEventSource;
130 
131     if (me && !me->init(inOwner, inAction, inProvider, inIntIndex)) {
132         me->release();
133         return 0;
134     }
135 
136     return me;
137 }
138 
139 void IOInterruptEventSource::free()
140 {
141     if (provider && intIndex != -1)
142         provider->unregisterInterrupt(intIndex);
143 
144     super::free();
145 }
146 
147 void IOInterruptEventSource::enable()
148 {
149     if (provider && intIndex != -1) {
150         provider->enableInterrupt(intIndex);
151         explicitDisable = false;
152         enabled = true;
153     }
154 }
155 
156 void IOInterruptEventSource::disable()
157 {
158     if (provider && intIndex != -1) {
159         provider->disableInterrupt(intIndex);
160         explicitDisable = true;
161         enabled = false;
162     }
163 }
164 
165 const IOService *IOInterruptEventSource::getProvider() const
166 {
167     return provider;
168 }
169 
170 int IOInterruptEventSource::getIntIndex() const
171 {
172     return intIndex;
173 }
174 
175 bool IOInterruptEventSource::getAutoDisable() const
176 {
177     return autoDisable;
178 }
179 
180 bool IOInterruptEventSource::checkForWork()
181 {
182     unsigned int cacheProdCount = producerCount;
183     int numInts = cacheProdCount - consumerCount;
184     IOInterruptEventAction intAction = (IOInterruptEventAction) action;
185 
186     if (numInts > 0) {
187 
188         IOTimeStampLatency();
189         IOTimeTypeStampS(IOINTES_CLIENT);
190             IOTimeStampConstant(IODBG_INTES(IOINTES_ACTION),
191                                 (uintptr_t) intAction, (uintptr_t) owner);
192             (*intAction)(owner, this,  numInts);
193         IOTimeTypeStampE(IOINTES_CLIENT);
194 
195         consumerCount = cacheProdCount;
196         if (autoDisable && !explicitDisable)
197             enable();
198     }
199     else if (numInts < 0) {
200         IOTimeStampLatency();
201         IOTimeTypeStampS(IOINTES_CLIENT);
202             IOTimeStampConstant(IODBG_INTES(IOINTES_ACTION),
203                                 (uintptr_t) intAction, (uintptr_t) owner);
204              (*intAction)(owner, this, -numInts);
205         IOTimeTypeStampE(IOINTES_CLIENT);
206 
207         consumerCount = cacheProdCount;
208         if (autoDisable && !explicitDisable)
209             enable();
210     }
211 
212     return false;
213 }
214 
215 void IOInterruptEventSource::normalInterruptOccurred
216     (void */*refcon*/, IOService */*prov*/, int /*source*/)
217 {
218 IOTimeTypeStampS(IOINTES_INTCTXT);
219 IOTimeStampLatency();
220 
221     producerCount++;
222 
223 IOTimeTypeStampS(IOINTES_SEMA);
224     signalWorkAvailable();
225 IOTimeTypeStampE(IOINTES_SEMA);
226 
227 IOTimeTypeStampE(IOINTES_INTCTXT);
228 }
229 
230 void IOInterruptEventSource::disableInterruptOccurred
231     (void */*refcon*/, IOService *prov, int source)
232 {
233 IOTimeTypeStampS(IOINTES_INTCTXT);
234 IOTimeStampLatency();
235 
236     prov->disableInterrupt(source);	/* disable the interrupt */
237 
238     producerCount++;
239 
240 IOTimeTypeStampS(IOINTES_SEMA);
241     signalWorkAvailable();
242 IOTimeTypeStampE(IOINTES_SEMA);
243 
244 IOTimeTypeStampE(IOINTES_INTCTXT);
245 }
246 
247 void IOInterruptEventSource::interruptOccurred
248     (void *refcon, IOService *prov, int source)
249 {
250     if (autoDisable && prov)
251         disableInterruptOccurred(refcon, prov, source);
252     else
253         normalInterruptOccurred(refcon, prov, source);
254 }
255