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
49OSDefineMetaClassAndStructors(IOTimerEventSource, IOEventSource)
50OSMetaClassDefineReservedUnused(IOTimerEventSource, 0);
51OSMetaClassDefineReservedUnused(IOTimerEventSource, 1);
52OSMetaClassDefineReservedUnused(IOTimerEventSource, 2);
53OSMetaClassDefineReservedUnused(IOTimerEventSource, 3);
54OSMetaClassDefineReservedUnused(IOTimerEventSource, 4);
55OSMetaClassDefineReservedUnused(IOTimerEventSource, 5);
56OSMetaClassDefineReservedUnused(IOTimerEventSource, 6);
57OSMetaClassDefineReservedUnused(IOTimerEventSource, 7);
58
59#if IOKITSTATS
60
61#define IOStatisticsInitializeCounter() \
62do { \
63	IOStatistics::setCounterType(IOEventSource::reserved->counter, kIOStatisticsTimerEventSourceCounter); \
64} while (0)
65
66#define IOStatisticsOpenGate() \
67do { \
68	IOStatistics::countOpenGate(me->IOEventSource::reserved->counter); \
69} while (0)
70
71#define IOStatisticsCloseGate() \
72do { \
73	IOStatistics::countCloseGate(me->IOEventSource::reserved->counter); \
74} while (0)
75
76#define IOStatisticsTimeout() \
77do { \
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//
98void 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
137void 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
182void 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
191bool 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
205IOTimerEventSource *
206IOTimerEventSource::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
218void 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
231void 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
244void IOTimerEventSource::enable()
245{
246    super::enable();
247    if (kIOReturnSuccess != wakeAtTime(abstime))
248        super::disable(); // Problem re-scheduling timeout ignore enable
249}
250
251void 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
264IOReturn IOTimerEventSource::setTimeoutTicks(UInt32 ticks)
265{
266    return setTimeout(ticks, kTickScale);
267}
268
269IOReturn IOTimerEventSource::setTimeoutMS(UInt32 ms)
270{
271    return setTimeout(ms, kMillisecondScale);
272}
273
274IOReturn IOTimerEventSource::setTimeoutUS(UInt32 us)
275{
276    return setTimeout(us, kMicrosecondScale);
277}
278
279IOReturn 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__)
288IOReturn 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
302IOReturn 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
312IOReturn IOTimerEventSource::wakeAtTimeTicks(UInt32 ticks)
313{
314    return wakeAtTime(ticks, kTickScale);
315}
316
317IOReturn IOTimerEventSource::wakeAtTimeMS(UInt32 ms)
318{
319    return wakeAtTime(ms, kMillisecondScale);
320}
321
322IOReturn IOTimerEventSource::wakeAtTimeUS(UInt32 us)
323{
324    return wakeAtTime(us, kMicrosecondScale);
325}
326
327IOReturn 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__)
336IOReturn 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
350void IOTimerEventSource::setWorkLoop(IOWorkLoop *inWorkLoop)
351{
352    super::setWorkLoop(inWorkLoop);
353    if ( enabled && AbsoluteTime_to_scalar(&abstime) && workLoop )
354        wakeAtTime(abstime);
355}
356
357IOReturn 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