1/* 2 * Copyright (c) 2001 Apple Computer, Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * The contents of this file constitute Original Code as defined in and 7 * are subject to the Apple Public Source License Version 1.1 (the 8 * "License"). You may not use this file except in compliance with the 9 * License. Please obtain a copy of the License at 10 * http://www.apple.com/publicsource and read it before using this file. 11 * 12 * This Original Code and all software distributed under the License are 13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER 14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the 17 * License for the specific language governing rights and limitations 18 * under the License. 19 * 20 * @APPLE_LICENSE_HEADER_END@ 21 */ 22/* 23 * Copyright (c) 1999-2002 Apple Computer, Inc. All rights reserved. 24 * 25 * HISTORY 26 * 13 February 2001 wgulland created. 27 * 28 */ 29 30#define DEBUGGING_LEVEL 0 // 1 = low; 2 = high; 3 = extreme 31#define DEBUGLOG IOLog 32 33// protected 34#import <IOKit/firewire/IOFWWorkLoop.h> 35 36// system 37#import <IOKit/IOWorkLoop.h> 38#import <IOKit/IOLocksPrivate.h> 39 40SInt32 IOFWWorkLoop::sLockGroupCount = 0; 41 42OSDefineMetaClassAndStructors( IOFWWorkLoop, IOWorkLoop ) 43 44// workLoop 45// 46// factory method 47 48IOFWWorkLoop * IOFWWorkLoop::workLoop() 49{ 50 IOFWWorkLoop *loop; 51 52 loop = OSTypeAlloc( IOFWWorkLoop ); 53 if( !loop ) 54 return loop; 55 56 if( !loop->init() ) 57 { 58 loop->release(); 59 loop = NULL; 60 } 61 62 return loop; 63} 64 65// init 66// 67// 68 69bool IOFWWorkLoop::init( void ) 70{ 71 bool success = true; 72 73 if( success ) 74 { 75 // create a unique lock group for this instance of the FireWire workloop 76 // this helps elucidate lock statistics 77 78 SInt32 count = OSIncrementAtomic( &sLockGroupCount ); 79 char name[64]; 80 81 snprintf( name, sizeof(name), "FireWire %d", (int)count ); 82 fLockGroup = lck_grp_alloc_init( name, LCK_GRP_ATTR_NULL ); 83 if( !fLockGroup ) 84 { 85 success = false; 86 } 87 } 88 89 if( success ) 90 { 91 gateLock = IORecursiveLockAllocWithLockGroup( fLockGroup ); 92 } 93 94 if( success ) 95 { 96 fRemoveSourceDeferredSet = OSSet::withCapacity( 1 ); 97 if( fRemoveSourceDeferredSet == NULL ) 98 { 99 success = false; 100 } 101 } 102 103 if( success ) 104 { 105 success = IOWorkLoop::init(); 106 } 107 108 return success; 109} 110 111// free 112// 113// 114 115void IOFWWorkLoop::free( void ) 116{ 117 if( fLockGroup ) 118 { 119 lck_grp_free( fLockGroup ); 120 fLockGroup = NULL; 121 } 122 123 if( fRemoveSourceDeferredSet ) 124 { 125 fRemoveSourceDeferredSet->release(); 126 fRemoveSourceDeferredSet = NULL; 127 } 128 129 IOWorkLoop::free(); 130} 131 132// removeEventSource 133// 134// 135 136IOReturn IOFWWorkLoop::removeEventSource(IOEventSource *toRemove) 137{ 138 IOReturn status = kIOReturnSuccess; 139 140 // the PM thread retains ioservices while fiddling with them 141 // that means the PM thread may be the thread that releases the final retain on an ioservices 142 // ioservices may remove and free event sources in their free routines 143 // removing and freeing event sources grabs the workloop lock 144 // if the PM has already put FireWire to sleep we would sleep any thread who grabs the workloop lock 145 // if we sleep the PM thread then sleep hangs. that's bad. 146 147 // if we could have a do over we should not sleep the entire workloop, but only those that belong 148 // to the core FireWire services. at this point though there are likely too many drivers relying 149 // on a full workloop sleep to change things safely. 150 151 // so for now we do these slightly crazy machinations to allow event source removal without sleeping the 152 // calling thread 153 154 // we only need to do this if the calling thread is the PM workloop, but I don't want to make 155 // assumptions about PM internals that may change so I'll do this for all threads 156 157 IOWorkLoop::closeGate(); 158 159 if( fRemoveSourceThread != NULL ) 160 { 161 IOLog( "IOFWWorkLoop::removeEventSource - fRemoveSourceThread = (%p) != NULL\n", fRemoveSourceThread ); 162 } 163 164 // remember who's removing the event source 165 fRemoveSourceThread = IOThreadSelf(); 166 167 // if we're asleep 168 if( fSleepToken ) 169 { 170 // we can't let this object be freed after we return since freeing a command gate grabs the workloop lock 171 // we will flush this set on wake 172 fRemoveSourceDeferredSet->setObject( toRemove ); 173 } 174 175 // do the actual removal, this will succeed since fRemoveSourceThread will be allowed to grab the lock 176 status = IOWorkLoop::removeEventSource( toRemove ); 177 178 // forget the thread 179 fRemoveSourceThread = NULL; 180 181 IOWorkLoop::openGate(); 182 183 return status; 184} 185 186// closeGate 187// 188// 189 190void IOFWWorkLoop::closeGate() 191{ 192 IOWorkLoop::closeGate(); 193 if( fSleepToken && 194 (fRemoveSourceThread != IOThreadSelf()) ) 195 { 196 IOReturn res; 197 do 198 { 199 res = sleepGate( fSleepToken, THREAD_ABORTSAFE ); 200 if( res == kIOReturnSuccess ) 201 break; 202 IOLog("sleepGate returned 0x%x\n", res); 203 } 204 while( true ); 205 } 206} 207 208// tryCloseGate 209// 210// 211 212bool IOFWWorkLoop::tryCloseGate() 213{ 214 bool ret; 215 ret = IOWorkLoop::tryCloseGate(); 216 if( ret && 217 fSleepToken && 218 (fRemoveSourceThread != IOThreadSelf()) ) 219 { 220 openGate(); 221 ret = false; 222 } 223 224 return ret; 225} 226 227// sleep 228// 229// 230 231IOReturn IOFWWorkLoop::sleep(void *token) 232{ 233 if( fSleepToken ) 234 { 235 DEBUGLOG( "IOFWWorkLoop::sleep: Already asleep: %p\n", token ); 236 return kIOReturnError; 237 } 238 239 fSleepToken = token; 240 openGate(); 241 242 return kIOReturnSuccess; 243} 244 245// wake 246// 247// 248 249IOReturn IOFWWorkLoop::wake(void *token) 250{ 251#if 0 252 if( fSleepToken != token ) 253 { 254 DEBUGLOG( "IOFWWorkLoop::wake: wrong token: %p<->%p\n", token, fSleepToken ); 255 return kIOReturnError; 256 } 257#endif 258 259 if( fSleepToken ) 260 { 261 IORecursiveLockLock( gateLock ); 262 263 void * the_token = fSleepToken; 264 fSleepToken = NULL; 265 266 // delete any event sources that were removed during sleep 267 fRemoveSourceDeferredSet->flushCollection(); 268 269 // wake up waiting threads 270 wakeupGate( the_token, false ); 271 } 272 else 273 { 274 closeGate(); 275 } 276 277 return kIOReturnSuccess; 278} 279