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/IOFilterInterruptEventSource.h>
30 #include <IOKit/IOService.h>
31 #include <IOKit/IOKitDebug.h>
32 #include <IOKit/IOTimeStamp.h>
33 #include <IOKit/IOWorkLoop.h>
34 #include <IOKit/IOInterruptAccountingPrivate.h>
35 #include <libkern/Block.h>
36 
37 #if IOKITSTATS
38 
39 #define IOStatisticsInitializeCounter() \
40 do { \
41 	IOStatistics::setCounterType(IOEventSource::reserved->counter, kIOStatisticsFilterInterruptEventSourceCounter); \
42 } while (0)
43 
44 #define IOStatisticsInterrupt() \
45 do { \
46 	IOStatistics::countInterrupt(IOEventSource::reserved->counter); \
47 } while (0)
48 
49 #else
50 
51 #define IOStatisticsInitializeCounter()
52 #define IOStatisticsInterrupt()
53 
54 #endif /* IOKITSTATS */
55 
56 #define super IOInterruptEventSource
57 
58 OSDefineMetaClassAndStructors
59     (IOFilterInterruptEventSource, IOInterruptEventSource)
60 OSMetaClassDefineReservedUnused(IOFilterInterruptEventSource, 0);
61 OSMetaClassDefineReservedUnused(IOFilterInterruptEventSource, 1);
62 OSMetaClassDefineReservedUnused(IOFilterInterruptEventSource, 2);
63 OSMetaClassDefineReservedUnused(IOFilterInterruptEventSource, 3);
64 OSMetaClassDefineReservedUnused(IOFilterInterruptEventSource, 4);
65 OSMetaClassDefineReservedUnused(IOFilterInterruptEventSource, 5);
66 OSMetaClassDefineReservedUnused(IOFilterInterruptEventSource, 6);
67 OSMetaClassDefineReservedUnused(IOFilterInterruptEventSource, 7);
68 
69 /*
70  * Implement the call throughs for the private protection conversion
71  */
72 bool IOFilterInterruptEventSource::init(OSObject *inOwner,
73                                         Action inAction,
74                                         IOService *inProvider,
75                                         int inIntIndex)
76 {
77     return false;
78 }
79 
80 IOInterruptEventSource *
81 IOFilterInterruptEventSource::interruptEventSource(OSObject *inOwner,
82                                                    Action inAction,
83                                                    IOService *inProvider,
84                                                    int inIntIndex)
85 {
86     return 0;
87 }
88 
89 bool
90 IOFilterInterruptEventSource::init(OSObject *inOwner,
91                                    Action inAction,
92                                    Filter inFilterAction,
93                                    IOService *inProvider,
94                                    int inIntIndex)
95 {
96     if ( !super::init(inOwner, inAction, inProvider, inIntIndex) )
97         return false;
98 
99     if (!inFilterAction)
100         return false;
101 
102     filterAction = inFilterAction;
103 
104     IOStatisticsInitializeCounter();
105 
106     return true;
107 }
108 
109 IOFilterInterruptEventSource *IOFilterInterruptEventSource
110 ::filterInterruptEventSource(OSObject *inOwner,
111                              Action inAction,
112                              Filter inFilterAction,
113                              IOService *inProvider,
114                              int inIntIndex)
115 {
116     IOFilterInterruptEventSource *me = new IOFilterInterruptEventSource;
117 
118     if (me
119     && !me->init(inOwner, inAction, inFilterAction, inProvider, inIntIndex)) {
120         me->release();
121         return 0;
122     }
123 
124     return me;
125 }
126 
127 
128 IOFilterInterruptEventSource *IOFilterInterruptEventSource
129 ::filterInterruptEventSource(OSObject *inOwner,
130                              IOService *inProvider,
131                              int inIntIndex,
132                              ActionBlock inAction,
133                              FilterBlock inFilterAction)
134 {
135     IOFilterInterruptEventSource *me = new IOFilterInterruptEventSource;
136 
137     FilterBlock filter = Block_copy(inFilterAction);
138     if (!filter) return 0;
139 
140     if (me
141     && !me->init(inOwner, (Action) NULL, (Filter) filter, inProvider, inIntIndex)) {
142         me->release();
143 	    Block_release(filter);
144         return 0;
145     }
146     me->flags |= kFilterBlock;
147     me->setActionBlock((IOEventSource::ActionBlock) inAction);
148 
149     return me;
150 }
151 
152 
153 void IOFilterInterruptEventSource::free( void )
154 {
155 	if ((kFilterBlock & flags) && filterActionBlock) Block_release(filterActionBlock);
156 
157     super::free();
158 }
159 
160 void IOFilterInterruptEventSource::signalInterrupt()
161 {
162 	bool trace = (gIOKitTrace & kIOTraceIntEventSource) ? true : false;
163 
164     IOStatisticsInterrupt();
165     producerCount++;
166 
167 	if (trace)
168 	    IOTimeStampStartConstant(IODBG_INTES(IOINTES_SEMA), VM_KERNEL_ADDRHIDE(this), VM_KERNEL_ADDRHIDE(owner));
169 
170     signalWorkAvailable();
171 
172 	if (trace)
173 	    IOTimeStampEndConstant(IODBG_INTES(IOINTES_SEMA), VM_KERNEL_ADDRHIDE(this), VM_KERNEL_ADDRHIDE(owner));
174 
175 }
176 
177 
178 IOFilterInterruptEventSource::Filter
179 IOFilterInterruptEventSource::getFilterAction() const
180 {
181 	if (kFilterBlock & flags) return NULL;
182     return filterAction;
183 }
184 
185 IOFilterInterruptEventSource::FilterBlock
186 IOFilterInterruptEventSource::getFilterActionBlock() const
187 {
188 	if (kFilterBlock & flags) return filterActionBlock;
189 	return (NULL);
190 }
191 
192 void IOFilterInterruptEventSource::normalInterruptOccurred
193     (void */*refcon*/, IOService */*prov*/, int /*source*/)
194 {
195     bool 	filterRes;
196     uint64_t	startTime = 0;
197     uint64_t	endTime = 0;
198 	bool	trace = (gIOKitTrace & kIOTraceIntEventSource) ? true : false;
199 
200 	if (trace)
201 		IOTimeStampStartConstant(IODBG_INTES(IOINTES_FILTER),
202 					 VM_KERNEL_UNSLIDE(filterAction), VM_KERNEL_ADDRHIDE(owner), VM_KERNEL_ADDRHIDE(this), VM_KERNEL_ADDRHIDE(workLoop));
203 
204     if (IOInterruptEventSource::reserved->statistics) {
205         if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingFirstLevelTimeIndex)) {
206             startTime = mach_absolute_time();
207         }
208     }
209 
210     // Call the filter.
211     if (kFilterBlock & flags) filterRes = (filterActionBlock)(this);
212     else                      filterRes = (*filterAction)(owner, this);
213 
214     if (IOInterruptEventSource::reserved->statistics) {
215         if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingFirstLevelCountIndex)) {
216             IA_ADD_VALUE(&IOInterruptEventSource::reserved->statistics->interruptStatistics[kInterruptAccountingFirstLevelCountIndex], 1);
217         }
218 
219         if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingFirstLevelTimeIndex)) {
220             endTime = mach_absolute_time();
221             IA_ADD_VALUE(&IOInterruptEventSource::reserved->statistics->interruptStatistics[kInterruptAccountingFirstLevelTimeIndex], endTime - startTime);
222         }
223     }
224 
225 	if (trace)
226 		IOTimeStampEndConstant(IODBG_INTES(IOINTES_FILTER),
227 				VM_KERNEL_ADDRHIDE(filterAction), VM_KERNEL_ADDRHIDE(owner),
228 				VM_KERNEL_ADDRHIDE(this), VM_KERNEL_ADDRHIDE(workLoop));
229 
230     if (filterRes)
231         signalInterrupt();
232 }
233 
234 void IOFilterInterruptEventSource::disableInterruptOccurred
235     (void */*refcon*/, IOService *prov, int source)
236 {
237     bool 	filterRes;
238     uint64_t	startTime = 0;
239     uint64_t	endTime = 0;
240 	bool	trace = (gIOKitTrace & kIOTraceIntEventSource) ? true : false;
241 
242 	if (trace)
243 		IOTimeStampStartConstant(IODBG_INTES(IOINTES_FILTER),
244 					 VM_KERNEL_UNSLIDE(filterAction), VM_KERNEL_ADDRHIDE(owner), VM_KERNEL_ADDRHIDE(this), VM_KERNEL_ADDRHIDE(workLoop));
245 
246     if (IOInterruptEventSource::reserved->statistics) {
247         if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingFirstLevelTimeIndex)) {
248             startTime = mach_absolute_time();
249         }
250     }
251 
252     // Call the filter.
253     if (kFilterBlock & flags) filterRes = (filterActionBlock)(this);
254     else                      filterRes = (*filterAction)(owner, this);
255 
256     if (IOInterruptEventSource::reserved->statistics) {
257         if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingFirstLevelCountIndex)) {
258             IA_ADD_VALUE(&IOInterruptEventSource::reserved->statistics->interruptStatistics[kInterruptAccountingFirstLevelCountIndex], 1);
259         }
260 
261         if (IA_GET_STATISTIC_ENABLED(kInterruptAccountingFirstLevelTimeIndex)) {
262             endTime = mach_absolute_time();
263             IA_ADD_VALUE(&IOInterruptEventSource::reserved->statistics->interruptStatistics[kInterruptAccountingFirstLevelTimeIndex], endTime - startTime);
264         }
265     }
266 
267 	if (trace)
268 		IOTimeStampEndConstant(IODBG_INTES(IOINTES_FILTER),
269 				       VM_KERNEL_UNSLIDE(filterAction), VM_KERNEL_ADDRHIDE(owner), VM_KERNEL_ADDRHIDE(this), VM_KERNEL_ADDRHIDE(workLoop));
270 
271     if (filterRes) {
272         prov->disableInterrupt(source);	/* disable the interrupt */
273         signalInterrupt();
274     }
275 }
276