1 /* 2 * Copyright (c) 1998-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 <IOKit/IOInterruptEventSource.h> 30 #include <IOKit/IOKitDebug.h> 31 #include <IOKit/IOLib.h> 32 #include <IOKit/IOService.h> 33 #include <IOKit/IOInterrupts.h> 34 #include <IOKit/IOTimeStamp.h> 35 #include <IOKit/IOWorkLoop.h> 36 37 #if IOKITSTATS 38 39 #define IOStatisticsInitializeCounter() \ 40 do { \ 41 IOStatistics::setCounterType(IOEventSource::reserved->counter, kIOStatisticsInterruptEventSourceCounter); \ 42 } while (0) 43 44 #define IOStatisticsCheckForWork() \ 45 do { \ 46 IOStatistics::countInterruptCheckForWork(IOEventSource::reserved->counter); \ 47 } while (0) 48 49 #define IOStatisticsInterrupt() \ 50 do { \ 51 IOStatistics::countInterrupt(IOEventSource::reserved->counter); \ 52 } while (0) 53 54 #else 55 56 #define IOStatisticsInitializeCounter() 57 #define IOStatisticsCheckForWork() 58 #define IOStatisticsInterrupt() 59 60 #endif // IOKITSTATS 61 62 #define super IOEventSource 63 64 OSDefineMetaClassAndStructors(IOInterruptEventSource, IOEventSource) 65 OSMetaClassDefineReservedUnused(IOInterruptEventSource, 0); 66 OSMetaClassDefineReservedUnused(IOInterruptEventSource, 1); 67 OSMetaClassDefineReservedUnused(IOInterruptEventSource, 2); 68 OSMetaClassDefineReservedUnused(IOInterruptEventSource, 3); 69 OSMetaClassDefineReservedUnused(IOInterruptEventSource, 4); 70 OSMetaClassDefineReservedUnused(IOInterruptEventSource, 5); 71 OSMetaClassDefineReservedUnused(IOInterruptEventSource, 6); 72 OSMetaClassDefineReservedUnused(IOInterruptEventSource, 7); 73 74 bool IOInterruptEventSource::init(OSObject *inOwner, 75 Action inAction, 76 IOService *inProvider, 77 int inIntIndex) 78 { 79 bool res = true; 80 81 if ( !super::init(inOwner, (IOEventSourceAction) inAction) ) 82 return false; 83 84 provider = inProvider; 85 producerCount = consumerCount = 0; 86 autoDisable = explicitDisable = false; 87 intIndex = ~inIntIndex; 88 89 // Assumes inOwner holds a reference(retain) on the provider 90 if (inProvider) { 91 res = (kIOReturnSuccess == registerInterruptHandler(inProvider, inIntIndex)); 92 if (res) 93 intIndex = inIntIndex; 94 } 95 96 IOStatisticsInitializeCounter(); 97 98 return res; 99 } 100 101 IOReturn IOInterruptEventSource::registerInterruptHandler(IOService *inProvider, 102 int inIntIndex) 103 { 104 IOReturn ret; 105 int intType; 106 IOInterruptAction intHandler; 107 108 ret = inProvider->getInterruptType(inIntIndex, &intType); 109 if (kIOReturnSuccess != ret) 110 return (ret); 111 112 autoDisable = (intType == kIOInterruptTypeLevel); 113 if (autoDisable) { 114 intHandler = OSMemberFunctionCast(IOInterruptAction, 115 this, &IOInterruptEventSource::disableInterruptOccurred); 116 } 117 else 118 intHandler = OSMemberFunctionCast(IOInterruptAction, 119 this, &IOInterruptEventSource::normalInterruptOccurred); 120 121 ret = provider->registerInterrupt(inIntIndex, this, intHandler); 122 123 return (ret); 124 } 125 126 IOInterruptEventSource * 127 IOInterruptEventSource::interruptEventSource(OSObject *inOwner, 128 Action inAction, 129 IOService *inProvider, 130 int inIntIndex) 131 { 132 IOInterruptEventSource *me = new IOInterruptEventSource; 133 134 if (me && !me->init(inOwner, inAction, inProvider, inIntIndex)) { 135 me->release(); 136 return 0; 137 } 138 139 return me; 140 } 141 142 void IOInterruptEventSource::free() 143 { 144 if (provider && intIndex >= 0) 145 provider->unregisterInterrupt(intIndex); 146 147 super::free(); 148 } 149 150 void IOInterruptEventSource::enable() 151 { 152 if (provider && intIndex >= 0) { 153 provider->enableInterrupt(intIndex); 154 explicitDisable = false; 155 enabled = true; 156 } 157 } 158 159 void IOInterruptEventSource::disable() 160 { 161 if (provider && intIndex >= 0) { 162 provider->disableInterrupt(intIndex); 163 explicitDisable = true; 164 enabled = false; 165 } 166 } 167 168 void IOInterruptEventSource::setWorkLoop(IOWorkLoop *inWorkLoop) 169 { 170 if (inWorkLoop) super::setWorkLoop(inWorkLoop); 171 172 if (provider) { 173 if (!inWorkLoop) { 174 if (intIndex >= 0) { 175 provider->unregisterInterrupt(intIndex); 176 intIndex = ~intIndex; 177 } 178 } else if ((intIndex < 0) && (kIOReturnSuccess == registerInterruptHandler(provider, ~intIndex))) { 179 intIndex = ~intIndex; 180 } 181 } 182 183 if (!inWorkLoop) super::setWorkLoop(inWorkLoop); 184 } 185 186 const IOService *IOInterruptEventSource::getProvider() const 187 { 188 return provider; 189 } 190 191 int IOInterruptEventSource::getIntIndex() const 192 { 193 return intIndex; 194 } 195 196 bool IOInterruptEventSource::getAutoDisable() const 197 { 198 return autoDisable; 199 } 200 201 bool IOInterruptEventSource::checkForWork() 202 { 203 unsigned int cacheProdCount = producerCount; 204 int numInts = cacheProdCount - consumerCount; 205 IOInterruptEventAction intAction = (IOInterruptEventAction) action; 206 bool trace = (gIOKitTrace & kIOTraceIntEventSource) ? true : false; 207 208 IOStatisticsCheckForWork(); 209 210 if ( numInts > 0 ) 211 { 212 if (trace) 213 IOTimeStampStartConstant(IODBG_INTES(IOINTES_ACTION), 214 VM_KERNEL_UNSLIDE(intAction), (uintptr_t) owner, (uintptr_t) this, (uintptr_t) workLoop); 215 216 // Call the handler 217 (*intAction)(owner, this, numInts); 218 219 if (trace) 220 IOTimeStampEndConstant(IODBG_INTES(IOINTES_ACTION), 221 VM_KERNEL_UNSLIDE(intAction), (uintptr_t) owner, (uintptr_t) this, (uintptr_t) workLoop); 222 223 consumerCount = cacheProdCount; 224 if (autoDisable && !explicitDisable) 225 enable(); 226 } 227 228 else if ( numInts < 0 ) 229 { 230 if (trace) 231 IOTimeStampStartConstant(IODBG_INTES(IOINTES_ACTION), 232 VM_KERNEL_UNSLIDE(intAction), (uintptr_t) owner, (uintptr_t) this, (uintptr_t) workLoop); 233 234 // Call the handler 235 (*intAction)(owner, this, -numInts); 236 237 if (trace) 238 IOTimeStampEndConstant(IODBG_INTES(IOINTES_ACTION), 239 VM_KERNEL_UNSLIDE(intAction), (uintptr_t) owner, (uintptr_t) this, (uintptr_t) workLoop); 240 241 consumerCount = cacheProdCount; 242 if (autoDisable && !explicitDisable) 243 enable(); 244 } 245 246 return false; 247 } 248 249 void IOInterruptEventSource::normalInterruptOccurred 250 (void */*refcon*/, IOService */*prov*/, int /*source*/) 251 { 252 bool trace = (gIOKitTrace & kIOTraceIntEventSource) ? true : false; 253 254 IOStatisticsInterrupt(); 255 producerCount++; 256 257 if (trace) 258 IOTimeStampStartConstant(IODBG_INTES(IOINTES_SEMA), (uintptr_t) this, (uintptr_t) owner); 259 260 signalWorkAvailable(); 261 262 if (trace) 263 IOTimeStampEndConstant(IODBG_INTES(IOINTES_SEMA), (uintptr_t) this, (uintptr_t) owner); 264 } 265 266 void IOInterruptEventSource::disableInterruptOccurred 267 (void */*refcon*/, IOService *prov, int source) 268 { 269 bool trace = (gIOKitTrace & kIOTraceIntEventSource) ? true : false; 270 271 prov->disableInterrupt(source); /* disable the interrupt */ 272 273 IOStatisticsInterrupt(); 274 producerCount++; 275 276 if (trace) 277 IOTimeStampStartConstant(IODBG_INTES(IOINTES_SEMA), (uintptr_t) this, (uintptr_t) owner); 278 279 signalWorkAvailable(); 280 281 if (trace) 282 IOTimeStampEndConstant(IODBG_INTES(IOINTES_SEMA), (uintptr_t) this, (uintptr_t) owner); 283 } 284 285 void IOInterruptEventSource::interruptOccurred 286 (void *refcon, IOService *prov, int source) 287 { 288 if (autoDisable && prov) 289 disableInterruptOccurred(refcon, prov, source); 290 else 291 normalInterruptOccurred(refcon, prov, source); 292 } 293 294 IOReturn IOInterruptEventSource::warmCPU 295 (uint64_t abstime) 296 { 297 298 return ml_interrupt_prewarm(abstime); 299 } 300