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