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