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