2 * Copyright (c) 1998-2000, 2009-2010 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
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.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
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.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
29 #include <sys/cdefs.h>
32 #include <kern/thread_call.h>
35 #include <IOKit/assert.h>
36 #include <IOKit/system.h>
38 #include <IOKit/IOLib.h>
39 #include <IOKit/IOTimerEventSource.h>
40 #include <IOKit/IOWorkLoop.h>
42 #include <IOKit/IOTimeStamp.h>
43 #include <IOKit/IOKitDebug.h>
48 #include <libkern/Block.h>
51 #define super IOEventSource
52 OSDefineMetaClassAndStructors(IOTimerEventSource
, IOEventSource
)
53 OSMetaClassDefineReservedUsed(IOTimerEventSource
, 0);
54 OSMetaClassDefineReservedUsed(IOTimerEventSource
, 1);
55 OSMetaClassDefineReservedUsed(IOTimerEventSource
, 2);
56 OSMetaClassDefineReservedUnused(IOTimerEventSource
, 3);
57 OSMetaClassDefineReservedUnused(IOTimerEventSource
, 4);
58 OSMetaClassDefineReservedUnused(IOTimerEventSource
, 5);
59 OSMetaClassDefineReservedUnused(IOTimerEventSource
, 6);
60 OSMetaClassDefineReservedUnused(IOTimerEventSource
, 7);
64 #define IOStatisticsInitializeCounter() \
66 IOStatistics::setCounterType(IOEventSource::reserved->counter, kIOStatisticsTimerEventSourceCounter); \
69 #define IOStatisticsOpenGate() \
71 IOStatistics::countOpenGate(me->IOEventSource::reserved->counter); \
74 #define IOStatisticsCloseGate() \
76 IOStatistics::countCloseGate(me->IOEventSource::reserved->counter); \
79 #define IOStatisticsTimeout() \
81 IOStatistics::countTimerTimeout(me->IOEventSource::reserved->counter); \
86 #define IOStatisticsInitializeCounter()
87 #define IOStatisticsOpenGate()
88 #define IOStatisticsCloseGate()
89 #define IOStatisticsTimeout()
91 #endif /* IOKITSTATS */
94 // reserved != 0 means IOTimerEventSource::timeoutAndRelease is being used,
95 // not a subclassed implementation.
98 // Timeout handler function. This function is called by the kernel when
99 // the timeout interval expires.
103 IOTimerEventSource::invokeAction(IOTimerEventSource::Action action
, IOTimerEventSource
* ts
,
104 OSObject
* owner
, IOWorkLoop
* workLoop
)
106 bool trace
= (gIOKitTrace
& kIOTraceTimers
) ? true : false;
109 IOTimeStampStartConstant(IODBG_TIMES(IOTIMES_ACTION
),
110 VM_KERNEL_ADDRHIDE(action
), VM_KERNEL_ADDRHIDE(owner
));
112 if (kActionBlock
& flags
) ((IOTimerEventSource::ActionBlock
) actionBlock
)(ts
);
113 else (*action
)(owner
, ts
);
116 DTRACE_TMR3(iotescallout__expire
, Action
, action
, OSObject
, owner
, void, workLoop
);
120 IOTimeStampEndConstant(IODBG_TIMES(IOTIMES_ACTION
),
121 VM_KERNEL_UNSLIDE(action
), VM_KERNEL_ADDRHIDE(owner
));
124 void IOTimerEventSource::timeout(void *self
)
126 IOTimerEventSource
*me
= (IOTimerEventSource
*) self
;
128 IOStatisticsTimeout();
130 if (me
->enabled
&& me
->action
)
138 IOStatisticsCloseGate();
139 doit
= (Action
) me
->action
;
140 if (doit
&& me
->enabled
&& AbsoluteTime_to_scalar(&me
->abstime
))
142 me
->invokeAction(doit
, me
, me
->owner
, me
->workLoop
);
144 IOStatisticsOpenGate();
150 void IOTimerEventSource::timeoutAndRelease(void * self
, void * c
)
152 IOTimerEventSource
*me
= (IOTimerEventSource
*) self
;
153 /* The second parameter (a pointer) gets abused to carry an SInt32, so on LP64, "count"
154 must be cast to "long" before, in order to tell GCC we're not truncating a pointer. */
155 SInt32 count
= (SInt32
) (long) c
;
157 IOStatisticsTimeout();
159 if (me
->enabled
&& me
->action
)
162 wl
= me
->reserved
->workLoop
;
167 IOStatisticsCloseGate();
168 doit
= (Action
) me
->action
;
169 if (doit
&& (me
->reserved
->calloutGeneration
== count
))
171 me
->invokeAction(doit
, me
, me
->owner
, me
->workLoop
);
173 IOStatisticsOpenGate();
178 me
->reserved
->workLoop
->release();
182 // -- work loop delivery
184 bool IOTimerEventSource::checkForWork()
189 && (reserved
->calloutGenerationSignaled
== reserved
->calloutGeneration
)
190 && enabled
&& (doit
= (Action
) action
))
192 reserved
->calloutGenerationSignaled
= ~reserved
->calloutGeneration
;
193 invokeAction(doit
, this, owner
, workLoop
);
199 void IOTimerEventSource::timeoutSignaled(void * self
, void * c
)
201 IOTimerEventSource
*me
= (IOTimerEventSource
*) self
;
203 me
->reserved
->calloutGenerationSignaled
= (SInt32
)(long) c
;
204 if (me
->enabled
) me
->signalWorkAvailable();
209 void IOTimerEventSource::setTimeoutFunc()
211 thread_call_priority_t pri
;
214 if (reserved
) panic("setTimeoutFunc already %p, %p", this, reserved
);
216 // reserved != 0 means IOTimerEventSource::timeoutAndRelease is being used,
217 // not a subclassed implementation
218 reserved
= IONew(ExpansionData
, 1);
219 reserved
->calloutGenerationSignaled
= ~reserved
->calloutGeneration
;
223 thread_call_options_t tcoptions
= 0;
224 thread_call_func_t func
= NULL
;
226 switch (kIOTimerEventSourceOptionsPriorityMask
& options
)
228 case kIOTimerEventSourceOptionsPriorityHigh
:
229 pri
= THREAD_CALL_PRIORITY_HIGH
;
230 func
= &IOTimerEventSource::timeoutAndRelease
;
233 case kIOTimerEventSourceOptionsPriorityKernel
:
234 pri
= THREAD_CALL_PRIORITY_KERNEL
;
235 func
= &IOTimerEventSource::timeoutAndRelease
;
238 case kIOTimerEventSourceOptionsPriorityKernelHigh
:
239 pri
= THREAD_CALL_PRIORITY_KERNEL_HIGH
;
240 func
= &IOTimerEventSource::timeoutAndRelease
;
243 case kIOTimerEventSourceOptionsPriorityUser
:
244 pri
= THREAD_CALL_PRIORITY_USER
;
245 func
= &IOTimerEventSource::timeoutAndRelease
;
248 case kIOTimerEventSourceOptionsPriorityLow
:
249 pri
= THREAD_CALL_PRIORITY_LOW
;
250 func
= &IOTimerEventSource::timeoutAndRelease
;
253 case kIOTimerEventSourceOptionsPriorityWorkLoop
:
254 pri
= THREAD_CALL_PRIORITY_KERNEL
;
255 tcoptions
|= THREAD_CALL_OPTIONS_SIGNAL
;
256 if (kIOTimerEventSourceOptionsAllowReenter
& options
) break;
257 func
= &IOTimerEventSource::timeoutSignaled
;
264 assertf(func
, "IOTimerEventSource options 0x%x", options
);
265 if (!func
) return; // init will fail
267 if (THREAD_CALL_OPTIONS_SIGNAL
& tcoptions
) flags
|= kActive
;
268 else flags
|= kPassive
;
270 if (!(kIOTimerEventSourceOptionsAllowReenter
& options
)) tcoptions
|= THREAD_CALL_OPTIONS_ONCE
;
272 calloutEntry
= (void *) thread_call_allocate_with_options(func
,
273 (thread_call_param_t
) this, pri
, tcoptions
);
274 assert(calloutEntry
);
277 bool IOTimerEventSource::init(OSObject
*inOwner
, Action inAction
)
279 if (!super::init(inOwner
, (IOEventSource::Action
) inAction
) )
286 IOStatisticsInitializeCounter();
291 bool IOTimerEventSource::init(uint32_t options
, OSObject
*inOwner
, Action inAction
)
294 return (init(inOwner
, inAction
));
298 IOTimerEventSource::timerEventSource(uint32_t inOptions
, OSObject
*inOwner
, Action inAction
)
300 IOTimerEventSource
*me
= new IOTimerEventSource
;
302 if (me
&& !me
->init(inOptions
, inOwner
, inAction
)) {
311 IOTimerEventSource::timerEventSource(uint32_t options
, OSObject
*inOwner
, ActionBlock action
)
313 IOTimerEventSource
* tes
;
314 tes
= IOTimerEventSource::timerEventSource(options
, inOwner
, (Action
) NULL
);
315 if (tes
) tes
->setActionBlock((IOEventSource::ActionBlock
) action
);
320 #define _thread_call_cancel(tc) ((kActive & flags) ? thread_call_cancel_wait((tc)) : thread_call_cancel((tc)))
323 IOTimerEventSource::timerEventSource(OSObject
*inOwner
, Action inAction
)
325 return (IOTimerEventSource::timerEventSource(
326 kIOTimerEventSourceOptionsPriorityKernelHigh
,
330 void IOTimerEventSource::free()
333 __assert_only
bool freed
;
337 freed
= thread_call_free((thread_call_t
) calloutEntry
);
342 IODelete(reserved
, ExpansionData
, 1);
347 void IOTimerEventSource::cancelTimeout()
350 reserved
->calloutGeneration
++;
351 bool active
= _thread_call_cancel((thread_call_t
) calloutEntry
);
352 AbsoluteTime_to_scalar(&abstime
) = 0;
353 if (active
&& reserved
&& (kPassive
& flags
))
360 void IOTimerEventSource::enable()
363 if (kIOReturnSuccess
!= wakeAtTime(abstime
))
364 super::disable(); // Problem re-scheduling timeout ignore enable
367 void IOTimerEventSource::disable()
370 reserved
->calloutGeneration
++;
371 bool active
= _thread_call_cancel((thread_call_t
) calloutEntry
);
373 if (active
&& reserved
&& (kPassive
& flags
))
380 IOReturn
IOTimerEventSource::setTimeoutTicks(UInt32 ticks
)
382 return setTimeout(ticks
, kTickScale
);
385 IOReturn
IOTimerEventSource::setTimeoutMS(UInt32 ms
)
387 return setTimeout(ms
, kMillisecondScale
);
390 IOReturn
IOTimerEventSource::setTimeoutUS(UInt32 us
)
392 return setTimeout(us
, kMicrosecondScale
);
395 IOReturn
IOTimerEventSource::setTimeout(UInt32 interval
, UInt32 scale_factor
)
399 clock_interval_to_deadline(interval
, scale_factor
, &end
);
400 return wakeAtTime(end
);
403 #if !defined(__LP64__)
404 IOReturn
IOTimerEventSource::setTimeout(mach_timespec_t interval
)
406 AbsoluteTime end
, nsecs
;
408 clock_interval_to_absolutetime_interval
409 (interval
.tv_nsec
, kNanosecondScale
, &nsecs
);
410 clock_interval_to_deadline
411 (interval
.tv_sec
, NSEC_PER_SEC
, &end
);
412 ADD_ABSOLUTETIME(&end
, &nsecs
);
414 return wakeAtTime(end
);
418 IOReturn
IOTimerEventSource::setTimeout(AbsoluteTime interval
)
421 clock_absolutetime_interval_to_deadline(interval
, &end
);
422 return wakeAtTime(end
);
425 IOReturn
IOTimerEventSource::setTimeout(uint32_t options
,
426 AbsoluteTime abstime
, AbsoluteTime leeway
)
429 if (options
& kIOTimeOptionsContinuous
)
430 clock_continuoustime_interval_to_deadline(abstime
, &end
);
432 clock_absolutetime_interval_to_deadline(abstime
, &end
);
434 return wakeAtTime(options
, end
, leeway
);
437 IOReturn
IOTimerEventSource::wakeAtTimeTicks(UInt32 ticks
)
439 return wakeAtTime(ticks
, kTickScale
);
442 IOReturn
IOTimerEventSource::wakeAtTimeMS(UInt32 ms
)
444 return wakeAtTime(ms
, kMillisecondScale
);
447 IOReturn
IOTimerEventSource::wakeAtTimeUS(UInt32 us
)
449 return wakeAtTime(us
, kMicrosecondScale
);
452 IOReturn
IOTimerEventSource::wakeAtTime(UInt32 inAbstime
, UInt32 scale_factor
)
455 clock_interval_to_absolutetime_interval(inAbstime
, scale_factor
, &end
);
457 return wakeAtTime(end
);
460 #if !defined(__LP64__)
461 IOReturn
IOTimerEventSource::wakeAtTime(mach_timespec_t inAbstime
)
463 AbsoluteTime end
, nsecs
;
465 clock_interval_to_absolutetime_interval
466 (inAbstime
.tv_nsec
, kNanosecondScale
, &nsecs
);
467 clock_interval_to_absolutetime_interval
468 (inAbstime
.tv_sec
, kSecondScale
, &end
);
469 ADD_ABSOLUTETIME(&end
, &nsecs
);
471 return wakeAtTime(end
);
475 void IOTimerEventSource::setWorkLoop(IOWorkLoop
*inWorkLoop
)
477 super::setWorkLoop(inWorkLoop
);
478 if ( enabled
&& AbsoluteTime_to_scalar(&abstime
) && workLoop
)
482 IOReturn
IOTimerEventSource::wakeAtTime(AbsoluteTime inAbstime
)
484 return wakeAtTime(0, inAbstime
, 0);
487 IOReturn
IOTimerEventSource::wakeAtTime(uint32_t options
, AbsoluteTime inAbstime
, AbsoluteTime leeway
)
490 return kIOReturnNoResources
;
493 if ( enabled
&& AbsoluteTime_to_scalar(&inAbstime
) && AbsoluteTime_to_scalar(&abstime
) && workLoop
)
495 uint32_t tcoptions
= 0;
497 if (kIOTimeOptionsWithLeeway
& options
) tcoptions
|= THREAD_CALL_DELAY_LEEWAY
;
498 if (kIOTimeOptionsContinuous
& options
) tcoptions
|= THREAD_CALL_CONTINUOUS
;
502 if (kPassive
& flags
)
507 reserved
->workLoop
= workLoop
;
508 reserved
->calloutGeneration
++;
509 if (thread_call_enter_delayed_with_leeway((thread_call_t
) calloutEntry
,
510 (void *)(uintptr_t) reserved
->calloutGeneration
, inAbstime
, leeway
, tcoptions
)
511 && (kPassive
& flags
))
519 thread_call_enter_delayed_with_leeway((thread_call_t
) calloutEntry
,
520 NULL
, inAbstime
, leeway
, tcoptions
);
524 return kIOReturnSuccess
;