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() \ 40do { \ 41 IOStatistics::setCounterType(IOEventSource::reserved->counter, kIOStatisticsInterruptEventSourceCounter); \ 42} while (0) 43 44#define IOStatisticsCheckForWork() \ 45do { \ 46 IOStatistics::countInterruptCheckForWork(IOEventSource::reserved->counter); \ 47} while (0) 48 49#define IOStatisticsInterrupt() \ 50do { \ 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 64OSDefineMetaClassAndStructors(IOInterruptEventSource, IOEventSource) 65OSMetaClassDefineReservedUnused(IOInterruptEventSource, 0); 66OSMetaClassDefineReservedUnused(IOInterruptEventSource, 1); 67OSMetaClassDefineReservedUnused(IOInterruptEventSource, 2); 68OSMetaClassDefineReservedUnused(IOInterruptEventSource, 3); 69OSMetaClassDefineReservedUnused(IOInterruptEventSource, 4); 70OSMetaClassDefineReservedUnused(IOInterruptEventSource, 5); 71OSMetaClassDefineReservedUnused(IOInterruptEventSource, 6); 72OSMetaClassDefineReservedUnused(IOInterruptEventSource, 7); 73 74bool 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 101IOReturn 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 126IOInterruptEventSource * 127IOInterruptEventSource::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 142void IOInterruptEventSource::free() 143{ 144 if (provider && intIndex >= 0) 145 provider->unregisterInterrupt(intIndex); 146 147 super::free(); 148} 149 150void IOInterruptEventSource::enable() 151{ 152 if (provider && intIndex >= 0) { 153 provider->enableInterrupt(intIndex); 154 explicitDisable = false; 155 enabled = true; 156 } 157} 158 159void IOInterruptEventSource::disable() 160{ 161 if (provider && intIndex >= 0) { 162 provider->disableInterrupt(intIndex); 163 explicitDisable = true; 164 enabled = false; 165 } 166} 167 168void 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 186const IOService *IOInterruptEventSource::getProvider() const 187{ 188 return provider; 189} 190 191int IOInterruptEventSource::getIntIndex() const 192{ 193 return intIndex; 194} 195 196bool IOInterruptEventSource::getAutoDisable() const 197{ 198 return autoDisable; 199} 200 201bool 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 249void 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 266void 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 285void 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 294IOReturn IOInterruptEventSource::warmCPU 295 (uint64_t abstime) 296{ 297 298 return ml_interrupt_prewarm(abstime); 299} 300