1 /*
2  * Copyright (c) 1998-2000, 2009-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 <sys/cdefs.h>
30 
31 __BEGIN_DECLS
32 #include <kern/thread_call.h>
33 __END_DECLS
34 
35 #include <IOKit/assert.h>
36 #include <IOKit/system.h>
37 
38 #include <IOKit/IOLib.h>
39 #include <IOKit/IOTimerEventSource.h>
40 #include <IOKit/IOWorkLoop.h>
41 
42 #include <IOKit/IOTimeStamp.h>
43 #include <IOKit/IOKitDebug.h>
44 #if CONFIG_DTRACE
45 #include <mach/sdt.h>
46 #endif
47 
48 #define super IOEventSource
49 OSDefineMetaClassAndStructors(IOTimerEventSource, IOEventSource)
50 OSMetaClassDefineReservedUnused(IOTimerEventSource, 0);
51 OSMetaClassDefineReservedUnused(IOTimerEventSource, 1);
52 OSMetaClassDefineReservedUnused(IOTimerEventSource, 2);
53 OSMetaClassDefineReservedUnused(IOTimerEventSource, 3);
54 OSMetaClassDefineReservedUnused(IOTimerEventSource, 4);
55 OSMetaClassDefineReservedUnused(IOTimerEventSource, 5);
56 OSMetaClassDefineReservedUnused(IOTimerEventSource, 6);
57 OSMetaClassDefineReservedUnused(IOTimerEventSource, 7);
58 
59 #if IOKITSTATS
60 
61 #define IOStatisticsInitializeCounter() \
62 do { \
63 	IOStatistics::setCounterType(IOEventSource::reserved->counter, kIOStatisticsTimerEventSourceCounter); \
64 } while (0)
65 
66 #define IOStatisticsOpenGate() \
67 do { \
68 	IOStatistics::countOpenGate(me->IOEventSource::reserved->counter); \
69 } while (0)
70 
71 #define IOStatisticsCloseGate() \
72 do { \
73 	IOStatistics::countCloseGate(me->IOEventSource::reserved->counter); \
74 } while (0)
75 
76 #define IOStatisticsTimeout() \
77 do { \
78 	IOStatistics::countTimerTimeout(me->IOEventSource::reserved->counter); \
79 } while (0)
80 
81 #else
82 
83 #define IOStatisticsInitializeCounter()
84 #define IOStatisticsOpenGate()
85 #define IOStatisticsCloseGate()
86 #define IOStatisticsTimeout()
87 
88 #endif /* IOKITSTATS */
89 
90 //
91 // reserved != 0 means IOTimerEventSource::timeoutAndRelease is being used,
92 // not a subclassed implementation.
93 //
94 
95 // Timeout handler function. This function is called by the kernel when
96 // the timeout interval expires.
97 //
98 void IOTimerEventSource::timeout(void *self)
99 {
100     IOTimerEventSource *me = (IOTimerEventSource *) self;
101 
102     IOStatisticsTimeout();
103 
104     if (me->enabled && me->action)
105     {
106         IOWorkLoop *
107         wl = me->workLoop;
108         if (wl)
109         {
110             Action doit;
111             wl->closeGate();
112             IOStatisticsCloseGate();
113             doit = (Action) me->action;
114             if (doit && me->enabled && AbsoluteTime_to_scalar(&me->abstime))
115             {
116             	bool    trace = (gIOKitTrace & kIOTraceTimers) ? true : false;
117 
118             	if (trace)
119                 	IOTimeStampStartConstant(IODBG_TIMES(IOTIMES_ACTION),
120 											 VM_KERNEL_UNSLIDE(doit), (uintptr_t) me->owner);
121 
122                 (*doit)(me->owner, me);
123 #if CONFIG_DTRACE
124 		DTRACE_TMR3(iotescallout__expire, Action, doit, OSObject, me->owner, void, me->workLoop);
125 #endif
126 
127 				if (trace)
128                 	IOTimeStampEndConstant(IODBG_TIMES(IOTIMES_ACTION),
129 										   VM_KERNEL_UNSLIDE(doit), (uintptr_t) me->owner);
130             }
131             IOStatisticsOpenGate();
132             wl->openGate();
133         }
134     }
135 }
136 
137 void IOTimerEventSource::timeoutAndRelease(void * self, void * c)
138 {
139     IOTimerEventSource *me = (IOTimerEventSource *) self;
140 	/* The second parameter (a pointer) gets abused to carry an SInt32, so on LP64, "count"
141 	   must be cast to "long" before, in order to tell GCC we're not truncating a pointer. */
142 	SInt32 count = (SInt32) (long) c;
143 
144     IOStatisticsTimeout();
145 
146     if (me->enabled && me->action)
147     {
148         IOWorkLoop *
149         wl = me->reserved->workLoop;
150         if (wl)
151         {
152             Action doit;
153             wl->closeGate();
154             IOStatisticsCloseGate();
155             doit = (Action) me->action;
156             if (doit && (me->reserved->calloutGeneration == count))
157             {
158             	bool    trace = (gIOKitTrace & kIOTraceTimers) ? true : false;
159 
160             	if (trace)
161                 	IOTimeStampStartConstant(IODBG_TIMES(IOTIMES_ACTION),
162 											 VM_KERNEL_UNSLIDE(doit), (uintptr_t) me->owner);
163 
164                 (*doit)(me->owner, me);
165 #if CONFIG_DTRACE
166 		DTRACE_TMR3(iotescallout__expire, Action, doit, OSObject, me->owner, void, me->workLoop);
167 #endif
168 
169 				if (trace)
170                 	IOTimeStampEndConstant(IODBG_TIMES(IOTIMES_ACTION),
171 										   VM_KERNEL_UNSLIDE(doit), (uintptr_t) me->owner);
172             }
173             IOStatisticsOpenGate();
174             wl->openGate();
175         }
176     }
177 
178     me->reserved->workLoop->release();
179     me->release();
180 }
181 
182 void IOTimerEventSource::setTimeoutFunc()
183 {
184     // reserved != 0 means IOTimerEventSource::timeoutAndRelease is being used,
185     // not a subclassed implementation
186     reserved = IONew(ExpansionData, 1);
187     calloutEntry = (void *) thread_call_allocate((thread_call_func_t) &IOTimerEventSource::timeoutAndRelease,
188                                                  (thread_call_param_t) this);
189 }
190 
191 bool IOTimerEventSource::init(OSObject *inOwner, Action inAction)
192 {
193     if (!super::init(inOwner, (IOEventSource::Action) inAction) )
194         return false;
195 
196     setTimeoutFunc();
197     if (!calloutEntry)
198         return false;
199 
200     IOStatisticsInitializeCounter();
201 
202     return true;
203 }
204 
205 IOTimerEventSource *
206 IOTimerEventSource::timerEventSource(OSObject *inOwner, Action inAction)
207 {
208     IOTimerEventSource *me = new IOTimerEventSource;
209 
210     if (me && !me->init(inOwner, inAction)) {
211         me->release();
212         return 0;
213     }
214 
215     return me;
216 }
217 
218 void IOTimerEventSource::free()
219 {
220     if (calloutEntry) {
221         cancelTimeout();
222         thread_call_free((thread_call_t) calloutEntry);
223     }
224 
225     if (reserved)
226         IODelete(reserved, ExpansionData, 1);
227 
228     super::free();
229 }
230 
231 void IOTimerEventSource::cancelTimeout()
232 {
233     if (reserved)
234         reserved->calloutGeneration++;
235     bool active = thread_call_cancel((thread_call_t) calloutEntry);
236     AbsoluteTime_to_scalar(&abstime) = 0;
237     if (active && reserved)
238     {
239         release();
240         workLoop->release();
241     }
242 }
243 
244 void IOTimerEventSource::enable()
245 {
246     super::enable();
247     if (kIOReturnSuccess != wakeAtTime(abstime))
248         super::disable(); // Problem re-scheduling timeout ignore enable
249 }
250 
251 void IOTimerEventSource::disable()
252 {
253     if (reserved)
254         reserved->calloutGeneration++;
255     bool active = thread_call_cancel((thread_call_t) calloutEntry);
256     super::disable();
257     if (active && reserved)
258     {
259         release();
260         workLoop->release();
261     }
262 }
263 
264 IOReturn IOTimerEventSource::setTimeoutTicks(UInt32 ticks)
265 {
266     return setTimeout(ticks, kTickScale);
267 }
268 
269 IOReturn IOTimerEventSource::setTimeoutMS(UInt32 ms)
270 {
271     return setTimeout(ms, kMillisecondScale);
272 }
273 
274 IOReturn IOTimerEventSource::setTimeoutUS(UInt32 us)
275 {
276     return setTimeout(us, kMicrosecondScale);
277 }
278 
279 IOReturn IOTimerEventSource::setTimeout(UInt32 interval, UInt32 scale_factor)
280 {
281     AbsoluteTime end;
282 
283     clock_interval_to_deadline(interval, scale_factor, &end);
284     return wakeAtTime(end);
285 }
286 
287 #if !defined(__LP64__)
288 IOReturn IOTimerEventSource::setTimeout(mach_timespec_t interval)
289 {
290     AbsoluteTime end, nsecs;
291 
292     clock_interval_to_absolutetime_interval
293         (interval.tv_nsec, kNanosecondScale, &nsecs);
294     clock_interval_to_deadline
295         (interval.tv_sec, NSEC_PER_SEC, &end);
296     ADD_ABSOLUTETIME(&end, &nsecs);
297 
298     return wakeAtTime(end);
299 }
300 #endif
301 
302 IOReturn IOTimerEventSource::setTimeout(AbsoluteTime interval)
303 {
304     AbsoluteTime end;
305 
306     clock_get_uptime(&end);
307     ADD_ABSOLUTETIME(&end, &interval);
308 
309     return wakeAtTime(end);
310 }
311 
312 IOReturn IOTimerEventSource::wakeAtTimeTicks(UInt32 ticks)
313 {
314     return wakeAtTime(ticks, kTickScale);
315 }
316 
317 IOReturn IOTimerEventSource::wakeAtTimeMS(UInt32 ms)
318 {
319     return wakeAtTime(ms, kMillisecondScale);
320 }
321 
322 IOReturn IOTimerEventSource::wakeAtTimeUS(UInt32 us)
323 {
324     return wakeAtTime(us, kMicrosecondScale);
325 }
326 
327 IOReturn IOTimerEventSource::wakeAtTime(UInt32 inAbstime, UInt32 scale_factor)
328 {
329     AbsoluteTime end;
330     clock_interval_to_absolutetime_interval(inAbstime, scale_factor, &end);
331 
332     return wakeAtTime(end);
333 }
334 
335 #if !defined(__LP64__)
336 IOReturn IOTimerEventSource::wakeAtTime(mach_timespec_t inAbstime)
337 {
338     AbsoluteTime end, nsecs;
339 
340     clock_interval_to_absolutetime_interval
341         (inAbstime.tv_nsec, kNanosecondScale, &nsecs);
342     clock_interval_to_absolutetime_interval
343         (inAbstime.tv_sec, kSecondScale, &end);
344     ADD_ABSOLUTETIME(&end, &nsecs);
345 
346     return wakeAtTime(end);
347 }
348 #endif
349 
350 void IOTimerEventSource::setWorkLoop(IOWorkLoop *inWorkLoop)
351 {
352     super::setWorkLoop(inWorkLoop);
353     if ( enabled && AbsoluteTime_to_scalar(&abstime) && workLoop )
354         wakeAtTime(abstime);
355 }
356 
357 IOReturn IOTimerEventSource::wakeAtTime(AbsoluteTime inAbstime)
358 {
359     if (!action)
360         return kIOReturnNoResources;
361 
362     abstime = inAbstime;
363     if ( enabled && AbsoluteTime_to_scalar(&inAbstime) && AbsoluteTime_to_scalar(&abstime) && workLoop )
364     {
365         if (reserved)
366         {
367             retain();
368             workLoop->retain();
369             reserved->workLoop = workLoop;
370             reserved->calloutGeneration++;
371             if (thread_call_enter1_delayed((thread_call_t) calloutEntry,
372                     (void *)(uintptr_t) reserved->calloutGeneration, inAbstime))
373             {
374                 release();
375                 workLoop->release();
376             }
377         }
378         else
379             thread_call_enter_delayed((thread_call_t) calloutEntry, inAbstime);
380     }
381 
382     return kIOReturnSuccess;
383 }
384