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