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