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 super::setWorkLoop(inWorkLoop); 171 172 if (!provider) 173 return; 174 175 if ( !inWorkLoop ) { 176 if (intIndex >= 0) { 177 provider->unregisterInterrupt(intIndex); 178 intIndex = ~intIndex; 179 } 180 } else if ((intIndex < 0) && (kIOReturnSuccess == registerInterruptHandler(provider, ~intIndex))) { 181 intIndex = ~intIndex; 182 } 183 } 184 185 const IOService *IOInterruptEventSource::getProvider() const 186 { 187 return provider; 188 } 189 190 int IOInterruptEventSource::getIntIndex() const 191 { 192 return intIndex; 193 } 194 195 bool IOInterruptEventSource::getAutoDisable() const 196 { 197 return autoDisable; 198 } 199 200 bool IOInterruptEventSource::checkForWork() 201 { 202 unsigned int cacheProdCount = producerCount; 203 int numInts = cacheProdCount - consumerCount; 204 IOInterruptEventAction intAction = (IOInterruptEventAction) action; 205 bool trace = (gIOKitTrace & kIOTraceIntEventSource) ? true : false; 206 207 IOStatisticsCheckForWork(); 208 209 if ( numInts > 0 ) 210 { 211 if (trace) 212 IOTimeStampStartConstant(IODBG_INTES(IOINTES_ACTION), 213 VM_KERNEL_UNSLIDE(intAction), (uintptr_t) owner, (uintptr_t) this, (uintptr_t) workLoop); 214 215 // Call the handler 216 (*intAction)(owner, this, numInts); 217 218 if (trace) 219 IOTimeStampEndConstant(IODBG_INTES(IOINTES_ACTION), 220 VM_KERNEL_UNSLIDE(intAction), (uintptr_t) owner, (uintptr_t) this, (uintptr_t) workLoop); 221 222 consumerCount = cacheProdCount; 223 if (autoDisable && !explicitDisable) 224 enable(); 225 } 226 227 else if ( numInts < 0 ) 228 { 229 if (trace) 230 IOTimeStampStartConstant(IODBG_INTES(IOINTES_ACTION), 231 VM_KERNEL_UNSLIDE(intAction), (uintptr_t) owner, (uintptr_t) this, (uintptr_t) workLoop); 232 233 // Call the handler 234 (*intAction)(owner, this, -numInts); 235 236 if (trace) 237 IOTimeStampEndConstant(IODBG_INTES(IOINTES_ACTION), 238 VM_KERNEL_UNSLIDE(intAction), (uintptr_t) owner, (uintptr_t) this, (uintptr_t) workLoop); 239 240 consumerCount = cacheProdCount; 241 if (autoDisable && !explicitDisable) 242 enable(); 243 } 244 245 return false; 246 } 247 248 void IOInterruptEventSource::normalInterruptOccurred 249 (void */*refcon*/, IOService */*prov*/, int /*source*/) 250 { 251 bool trace = (gIOKitTrace & kIOTraceIntEventSource) ? true : false; 252 253 IOStatisticsInterrupt(); 254 producerCount++; 255 256 if (trace) 257 IOTimeStampStartConstant(IODBG_INTES(IOINTES_SEMA), (uintptr_t) this, (uintptr_t) owner); 258 259 signalWorkAvailable(); 260 261 if (trace) 262 IOTimeStampEndConstant(IODBG_INTES(IOINTES_SEMA), (uintptr_t) this, (uintptr_t) owner); 263 } 264 265 void IOInterruptEventSource::disableInterruptOccurred 266 (void */*refcon*/, IOService *prov, int source) 267 { 268 bool trace = (gIOKitTrace & kIOTraceIntEventSource) ? true : false; 269 270 prov->disableInterrupt(source); /* disable the interrupt */ 271 272 IOStatisticsInterrupt(); 273 producerCount++; 274 275 if (trace) 276 IOTimeStampStartConstant(IODBG_INTES(IOINTES_SEMA), (uintptr_t) this, (uintptr_t) owner); 277 278 signalWorkAvailable(); 279 280 if (trace) 281 IOTimeStampEndConstant(IODBG_INTES(IOINTES_SEMA), (uintptr_t) this, (uintptr_t) owner); 282 } 283 284 void IOInterruptEventSource::interruptOccurred 285 (void *refcon, IOService *prov, int source) 286 { 287 if (autoDisable && prov) 288 disableInterruptOccurred(refcon, prov, source); 289 else 290 normalInterruptOccurred(refcon, prov, source); 291 } 292 293 IOReturn IOInterruptEventSource::warmCPU 294 (uint64_t abstime) 295 { 296 297 return ml_interrupt_prewarm(abstime); 298 } 299