1/* 2 * Copyright (c) 1998-2002 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/* 24 * Copyright (c) 1999-2002 Apple Computer, Inc. All rights reserved. 25 * 26 * IOFireWireIRM 27 * 28 * HISTORY 29 * 30 */ 31 32// public 33#include <IOKit/firewire/IOFireWireFamilyCommon.h> 34 35// private 36#include "IOFireWireIRM.h" 37 38//#define FWLOCALLOGGING 1 39#include "FWDebugging.h" 40 41#define kChannel31Mask 0x00000001 42 43OSDefineMetaClassAndStructors( IOFireWireIRM, OSObject ) 44 45// create 46// 47// 48 49IOFireWireIRM * IOFireWireIRM::create( IOFireWireController * controller ) 50{ 51 IOReturn status = kIOReturnSuccess; 52 IOFireWireIRM * me; 53 54 if( status == kIOReturnSuccess ) 55 { 56 me = OSTypeAlloc( IOFireWireIRM ); 57 if( me == NULL ) 58 status = kIOReturnNoMemory; 59 } 60 61 if( status == kIOReturnSuccess ) 62 { 63 bool success = me->initWithController( controller ); 64 if( !success ) 65 { 66 status = kIOReturnError; 67 } 68 } 69 70 if( status != kIOReturnSuccess ) 71 { 72 me = NULL; 73 } 74 75 FWLOCALKLOG(( "IOFireWireIRM::create() - created new IRM 0x%08lx\n", (UInt32)me )); 76 77 return me; 78} 79 80// initWithController 81// 82// 83 84bool IOFireWireIRM::initWithController(IOFireWireController * control) 85{ 86 IOReturn status = kIOReturnSuccess; 87 88 bool success = OSObject::init(); 89 FWPANICASSERT( success == true ); 90 91 fControl = control; 92 93 fIRMNodeID = kFWBadNodeID; 94 fOurNodeID = kFWBadNodeID; 95 fGeneration = 0; 96 97 // 98 // create BROADCAST_CHANNEL register 99 // 100 101 fBroadcastChannelBuffer = OSSwapHostToBigInt32( kBroadcastChannelInitialValues ); 102 fBroadcastChannelAddressSpace = IOFWPseudoAddressSpace::simpleRWFixed( fControl, FWAddress(kCSRRegisterSpaceBaseAddressHi, kCSRBroadcastChannel), 103 sizeof(fBroadcastChannelBuffer), &fBroadcastChannelBuffer ); 104 FWPANICASSERT( fBroadcastChannelAddressSpace != NULL ); 105 106 status = fBroadcastChannelAddressSpace->activate(); 107 FWPANICASSERT( status == kIOReturnSuccess ); 108 109 // 110 // create lock command 111 // 112 113 fLockCmdInUse = false; 114 fLockCmd = OSTypeAlloc( IOFWCompareAndSwapCommand ); 115 FWPANICASSERT( fLockCmd != NULL ); 116 fLockCmd->initAll( fControl, 0, FWAddress(), NULL, NULL, 0, IOFireWireIRM::lockCompleteStatic, this ); 117 118 FWLOCALKLOG(( "IOFireWireIRM::initWithController() - IRM intialized\n" )); 119 120 return true; 121} 122 123// free 124// 125// 126 127void IOFireWireIRM::free() 128{ 129 FWLOCALKLOG(( "IOFireWireIRM::free() - freeing IRM 0x%08lx\n", (UInt32)this )); 130 131 // 132 // free lock command 133 // 134 135 if( fLockCmd != NULL ) 136 { 137 // cancel the command if its in use. 138 if( fLockCmdInUse ) 139 { 140 fLockCmd->cancel( kIOFireWireBusReset ); 141 } 142 143 fLockCmd->release(); 144 fLockCmd = NULL; 145 } 146 147 // 148 // free BROADCAST_CHANNEL register 149 // 150 151 if( fBroadcastChannelAddressSpace != NULL) 152 { 153 fBroadcastChannelAddressSpace->deactivate(); 154 fBroadcastChannelAddressSpace->release(); 155 fBroadcastChannelAddressSpace = NULL; 156 } 157 158 OSObject::free(); 159} 160 161// isIRMActive 162// 163// 164 165bool IOFireWireIRM::isIRMActive( void ) 166{ 167 // this irm should be active if we're the IRM node and we're 168 // not the only node on the bus 169 170 return (fOurNodeID == fIRMNodeID && (fOurNodeID & 0x3f) != 0); 171} 172 173// processBusReset 174// 175// 176 177void IOFireWireIRM::processBusReset( UInt16 ourNodeID, UInt16 irmNodeID, UInt32 generation ) 178{ 179 FWLOCALKLOG(( "IOFireWireIRM::processBusReset() - bus reset occurred\n" )); 180 181 FWLOCALKLOG(( "IOFireWireIRM::processBusReset() - ourNodeID = 0x%04x, irmNodeID = 0x%04x, generation = %d\n", ourNodeID, irmNodeID, generation )); 182 183 // node id's and generation 184 185 fIRMNodeID = irmNodeID; 186 fOurNodeID = ourNodeID; 187 fGeneration = generation; 188 189 if( isIRMActive() ) 190 { 191 192 // stop any command in progress. any inflight commands should already 193 // have been canceled by the bus reset before this point. 194 // this is just an extra precaution 195 196 // lockComplete will run with status = kIOFireWireBusReset before we 197 // return from cancel. fLockCmdInUse is cleared by lockComplete. 198 199 // calling cancel on commands that are not busy will still call 200 // complete, so we must make sure a command is in use before cancelling it. 201 202 // commands do not have a reliable API for tracking usage, so we 203 // use fLockCmdInUse instead 204 205 if( fLockCmdInUse ) 206 { 207 fLockCmd->cancel( kIOFireWireBusReset ); 208 } 209 210 // initialize fOldChannelsAvailable31_0 and fLockRetries 211 fLockRetries = 8; 212 fOldChannelsAvailable31_0 = OSSwapHostToBigInt32( 0xffffffff ); // don't really need to swap of course 213 214 allocateBroadcastChannel(); 215 } 216 else 217 { 218 FWLOCALKLOG(( "IOFireWireIRM::processBusReset() - clear valid bit in BROADCAST_CHANNEL register\n" )); 219 fBroadcastChannelBuffer = OSSwapHostToBigInt32(kBroadcastChannelInitialValues); 220 } 221 222} 223 224// allocateBroadcastChannel 225// 226// 227 228void IOFireWireIRM::allocateBroadcastChannel( void ) 229{ 230 IOReturn status = kIOReturnSuccess; 231 232 FWLOCALKLOG(( "IOFireWireIRM::allocateBroadcastChannel() - attempting to allocate broadcast channel\n" )); 233 234 FWAddress address( kCSRRegisterSpaceBaseAddressHi, kCSRChannelsAvailable31_0 ); 235 address.nodeID = fIRMNodeID; 236 237 UInt32 host_channels_available = OSSwapBigToHostInt32( fOldChannelsAvailable31_0 ); 238 host_channels_available &= ~kChannel31Mask; 239 fNewChannelsAvailable31_0 = OSSwapHostToBigInt32( host_channels_available ); 240 241 fLockCmd->reinit( fGeneration, address, &fOldChannelsAvailable31_0, &fNewChannelsAvailable31_0, 1, IOFireWireIRM::lockCompleteStatic, this ); 242 243 // the standard async commands call complete with an error before 244 // returning an error from submit. 245 246 fLockCmdInUse = true; 247 status = fLockCmd->submit(); 248} 249 250// lockComplete 251// 252// 253 254void IOFireWireIRM::lockCompleteStatic( void *refcon, IOReturn status, IOFireWireNub *device, IOFWCommand *fwCmd ) 255{ 256 IOFireWireIRM * me = (IOFireWireIRM*)refcon; 257 me->lockComplete( status ); 258} 259 260void IOFireWireIRM::lockComplete( IOReturn status ) 261{ 262 bool done = true; 263 264 fLockCmdInUse = false; 265 266 if( status == kIOReturnSuccess ) 267 { 268 // update fOldChannelsAvailable31_0 and fLockRetries 269 bool tryAgain = !fLockCmd->locked( &fOldChannelsAvailable31_0 ); 270 if( tryAgain && fLockRetries-- ) 271 { 272 FWLOCALKLOG(( "IOFireWireIRM::lockComplete() - allocation attempt failed, will retry\n" )); 273 allocateBroadcastChannel(); 274 275 done = false; 276 } 277 } 278 279 // done means we did not resubmit this command 280 if( done ) 281 { 282 // if this command was completed because of a bus reset, 283 // we will retry the channel allocation in a moment when 284 // processBusReset is called, therefore don't set the 285 // channel as valid just yet. 286 // 287 // otherwise, if no bus reset processing is pending we 288 // pretend we've allocated the channel even if we failed to do so. 289 290#if FWLOCALLOGGING 291 if( status == kIOReturnSuccess ) 292 { 293 FWLOCALKLOG(( "IOFireWireIRM::lockComplete() - successfully allocated broadcast channel\n" )); 294 } 295 else 296 { 297 FWLOCALKLOG(( "IOFireWireIRM::lockComplete() - failed to allocate broadcast channel\n" )); 298 } 299#endif 300 301 if( status != kIOFireWireBusReset ) 302 { 303 FWLOCALKLOG(( "IOFireWireIRM::lockComplete() - set valid bit in BROADCAST_CHANNEL register\n" )); 304 305 fBroadcastChannelBuffer = OSSwapHostToBigInt32( kBroadcastChannelInitialValues | kBroadcastChannelValidMask ); 306 } 307 } 308} 309 310#pragma mark - 311 312OSDefineMetaClassAndStructors( IOFireWireIRMAllocation, OSObject ); 313OSMetaClassDefineReservedUnused(IOFireWireIRMAllocation, 0); 314OSMetaClassDefineReservedUnused(IOFireWireIRMAllocation, 1); 315OSMetaClassDefineReservedUnused(IOFireWireIRMAllocation, 2); 316OSMetaClassDefineReservedUnused(IOFireWireIRMAllocation, 3); 317OSMetaClassDefineReservedUnused(IOFireWireIRMAllocation, 4); 318OSMetaClassDefineReservedUnused(IOFireWireIRMAllocation, 5); 319OSMetaClassDefineReservedUnused(IOFireWireIRMAllocation, 6); 320OSMetaClassDefineReservedUnused(IOFireWireIRMAllocation, 7); 321 322// IRMAllocationThreadInfo 323// 324// A little struct for keeping track of our this pointer and generation 325// when transitioning to a second thread during bandwidth reallocation. 326 327struct IRMAllocationThreadInfo 328{ 329 IOFireWireIRMAllocation * fIRMAllocation; 330 UInt32 fGeneration; 331 IOFireWireController * fControl; 332 IORecursiveLock * fLock; 333 UInt8 fIsochChannel; 334 UInt32 fBandwidthUnits; 335}; 336 337// IOFireWireIRMAllocation::init 338// 339// 340bool IOFireWireIRMAllocation::init( IOFireWireController * control, 341 Boolean releaseIRMResourcesOnFree, 342 AllocationLostNotificationProc allocationLostProc, 343 void *pLostProcRefCon) 344{ 345 if (!OSObject::init()) 346 return false ; 347 348 // Allocate a lock 349 fLock = IORecursiveLockAlloc () ; 350 if ( ! fLock ) 351 return false ; 352 353 // Initialize some class members 354 fControl = control; 355 fAllocationGeneration = 0xFFFFFFFF; 356 fAllocationLostProc = allocationLostProc; 357 fLostProcRefCon = pLostProcRefCon; 358 fReleaseIRMResourcesOnFree = releaseIRMResourcesOnFree; 359 fBandwidthUnits = 0; 360 fIsochChannel = 64; 361 362 fControl->addToIRMAllocationSet(this); 363 364 isAllocated = false; 365 return true; 366} 367 368// IOFireWireIRMAllocation::release 369// 370// 371void IOFireWireIRMAllocation::release() const 372{ 373 DebugLog( "IOFireWireIRMAllocation::release, retain count before release = %d\n",getRetainCount() ) ; 374 375 // Take the lock 376 IORecursiveLockLock(fLock); 377 378 int retainCnt = getRetainCount(); 379 380 if ( retainCnt == 2 ) 381 { 382 if( isAllocated == false ) 383 { 384 fControl->removeFromIRMAllocationSet((IOFireWireIRMAllocation*)this); 385 } 386 else 387 { 388 // The controller has an extra retain on the IOFireWireIRMAllocation object 389 // because it's in the array used to restore allocations after a bus-reset. 390 // We now need to remove it from the controller's array, so it's no longer 391 // auto-restored after bus-reset! 392 fControl->removeIRMAllocation((IOFireWireIRMAllocation*)this); 393 } 394 } 395 396 OSObject::release(); 397 398 // Bypass unlock if we just did the last release! 399 if (retainCnt != 1) 400 IORecursiveLockUnlock(fLock); 401} 402 403// IOFireWireIRMAllocation::free 404// 405// 406void IOFireWireIRMAllocation::free( void ) 407{ 408 DebugLog( "IOFireWireIRMAllocation::free\n") ; 409 410 // Take the lock 411 IORecursiveLockLock(fLock); 412 413 414 // If we need to release the isoch resources, do so now! 415 if (isAllocated) 416 { 417 if (fReleaseIRMResourcesOnFree) 418 { 419 if (fBandwidthUnits > 0) 420 fControl->releaseIRMBandwidthInGeneration(fBandwidthUnits,fAllocationGeneration); 421 if (fIsochChannel < 64) 422 fControl->releaseIRMChannelInGeneration(fIsochChannel,fAllocationGeneration); 423 } 424 // Note: we already removed this allocation from the controller's array! Don't need to do it here! 425 } 426 427 // Free the lock 428 if ( fLock ) 429 IORecursiveLockFree( fLock ) ; 430 431 OSObject::free(); 432} 433 434// IOFireWireIRMAllocation::allocateIsochResources 435// 436// 437IOReturn IOFireWireIRMAllocation::allocateIsochResources(UInt8 isochChannel, UInt32 bandwidthUnits) 438{ 439 IOReturn res = kIOReturnError; 440 UInt32 irmGeneration; 441 UInt16 irmNodeID; 442 443 // Take the lock 444 IORecursiveLockLock(fLock); 445 446 if (!isAllocated) 447 { 448 // Initialize some class members 449 fAllocationGeneration = 0xFFFFFFFF; 450 451 // Get the current generation 452 fControl->getIRMNodeID(irmGeneration, irmNodeID); 453 454 res = kIOReturnSuccess; 455 456 if (isochChannel < 64) 457 { 458 // Attempt to allocate isoch channel 459 res = fControl->allocateIRMChannelInGeneration(isochChannel,irmGeneration); 460 } 461 462 if ((res == kIOReturnSuccess) && (bandwidthUnits > 0)) 463 { 464 // Attempt to allocate isoch bandwidth 465 res = fControl->allocateIRMBandwidthInGeneration(bandwidthUnits,irmGeneration); 466 if (res != kIOReturnSuccess) 467 { 468 // Need to free the isoch channel (note: will fail if generation has changed) 469 fControl->releaseIRMChannelInGeneration(isochChannel,irmGeneration); 470 } 471 } 472 473 if (res == kIOReturnSuccess) 474 { 475 fIsochChannel = isochChannel; 476 fBandwidthUnits = bandwidthUnits; 477 fAllocationGeneration = irmGeneration; 478 isAllocated = true; 479 480 // Register this object with the controller 481 fControl->addIRMAllocation(this); 482 } 483 } 484 485 // Unlock the lock 486 IORecursiveLockUnlock(fLock); 487 488 FWTrace( kFWTIsoch, kTPIsochIRMAllocateIsochResources, (uintptr_t)(fControl->getLink()), fIsochChannel, fBandwidthUnits, res ); 489 490 return res; 491} 492 493// IOFireWireIRMAllocation::deallocateIsochResources 494// 495// 496IOReturn IOFireWireIRMAllocation::deallocateIsochResources(void) 497{ 498 IOReturn res = kIOReturnError; 499 500 // Take the lock 501 IORecursiveLockLock(fLock); 502 503 if (isAllocated) 504 { 505 if (fBandwidthUnits > 0) 506 fControl->releaseIRMBandwidthInGeneration(fBandwidthUnits,fAllocationGeneration); 507 if (fIsochChannel < 64) 508 fControl->releaseIRMChannelInGeneration(fIsochChannel,fAllocationGeneration); 509 510 // Unregister this object with the controller 511 fControl->removeIRMAllocation(this); 512 513 isAllocated = false; 514 fBandwidthUnits = 0; 515 fIsochChannel = 64; 516 fAllocationGeneration = 0xFFFFFFFF; 517 } 518 519 // Unlock the lock 520 IORecursiveLockUnlock(fLock); 521 522 return res; 523} 524 525// IOFireWireIRMAllocation::areIsochResourcesAllocated 526// 527// 528Boolean IOFireWireIRMAllocation::areIsochResourcesAllocated(UInt8 *pAllocatedIsochChannel, UInt32 *pAllocatedBandwidthUnits) 529{ 530 531 *pAllocatedIsochChannel = fIsochChannel; 532 *pAllocatedBandwidthUnits = fBandwidthUnits; 533 return isAllocated; 534} 535 536// IOFireWireIRMAllocation::GetRefCon 537// 538// 539void * IOFireWireIRMAllocation::GetRefCon(void) 540{ 541 return fLostProcRefCon; 542} 543 544// IOFireWireIRMAllocation::SetRefCon 545// 546// 547void IOFireWireIRMAllocation::SetRefCon(void* refCon) 548{ 549 fLostProcRefCon = refCon; 550} 551 552// IOFireWireIRMAllocation::handleBusReset 553// 554// 555void IOFireWireIRMAllocation::handleBusReset(UInt32 generation) 556{ 557 // Take the lock 558 IORecursiveLockLock(fLock); 559 560 if (!isAllocated) 561 { 562 IORecursiveLockUnlock(fLock); 563 return; 564 } 565 566 if (fAllocationGeneration == generation) 567 { 568 IORecursiveLockUnlock(fLock); 569 return; 570 } 571 572 // Spawn a thread to do the reallocation 573 IRMAllocationThreadInfo * threadInfo = (IRMAllocationThreadInfo *)IOMalloc( sizeof(IRMAllocationThreadInfo) ); 574 if( threadInfo ) 575 { 576 threadInfo->fGeneration = generation; 577 threadInfo->fIRMAllocation = this; 578 threadInfo->fControl = fControl; 579 threadInfo->fLock = fLock; 580 threadInfo->fIsochChannel = fIsochChannel; 581 threadInfo->fBandwidthUnits = fBandwidthUnits; 582 583 retain(); // retain ourself for the thread to use 584 585 thread_t thread; 586 if( kernel_thread_start((thread_continue_t)threadFunc, threadInfo, &thread ) == KERN_SUCCESS ) 587 { 588 thread_deallocate(thread); 589 } 590 } 591 592 // Unlock the lock 593 IORecursiveLockUnlock(fLock); 594} 595 596// IOFireWireIRMAllocation::setReleaseIRMResourcesOnFree 597// 598// 599void IOFireWireIRMAllocation::setReleaseIRMResourcesOnFree(Boolean doRelease) 600{ 601 fReleaseIRMResourcesOnFree = doRelease; 602} 603 604// IOFireWireIRMAllocation::getAllocationGeneration 605// 606// 607UInt32 IOFireWireIRMAllocation::getAllocationGeneration(void) 608{ 609 return fAllocationGeneration; 610} 611 612// IOFireWireIRMAllocation::failedToRealloc 613// 614// 615void IOFireWireIRMAllocation::failedToRealloc(void) 616{ 617 // Notify client, and mark as to not reallocate in the future! 618 619 if (fAllocationLostProc) 620 fAllocationLostProc(fLostProcRefCon,this); 621 622 // Unregister this object with the controller 623 fControl->removeIRMAllocation(this); 624 625 isAllocated = false; 626 fAllocationGeneration = 0xFFFFFFFF; 627} 628 629// IOFireWireIRMAllocation::threadFunc 630// 631// 632void IOFireWireIRMAllocation::threadFunc( void * arg ) 633{ 634 IOReturn res = kIOReturnSuccess; 635 IRMAllocationThreadInfo * threadInfo = (IRMAllocationThreadInfo *)arg; 636 IOFireWireIRMAllocation *pIRMAllocation = threadInfo->fIRMAllocation; 637 IORecursiveLock * fLock = threadInfo->fLock; 638 UInt32 generation = threadInfo->fGeneration; 639 UInt32 irmGeneration; 640 UInt16 irmNodeID; 641 642 // Take the lock 643 IORecursiveLockLock(fLock); 644 645 // Get the current generation 646 threadInfo->fControl->getIRMNodeID(irmGeneration, irmNodeID); 647 648 if ((irmGeneration == generation) && (pIRMAllocation->getAllocationGeneration() != 0xFFFFFFFF)) 649 { 650 if (threadInfo->fIsochChannel < 64) 651 { 652 // Attempt to reallocate isoch channel 653 res = threadInfo->fControl->allocateIRMChannelInGeneration(threadInfo->fIsochChannel,generation); 654 } 655 656 if ((res == kIOReturnSuccess) && (threadInfo->fBandwidthUnits > 0)) 657 { 658 // Attempt to reallocate isoch bandwidth 659 res = threadInfo->fControl->allocateIRMBandwidthInGeneration(threadInfo->fBandwidthUnits,generation); 660 if (res != kIOReturnSuccess) 661 { 662 // Need to free the isoch channel (note: will fail if generation has changed) 663 threadInfo->fControl->releaseIRMChannelInGeneration(threadInfo->fIsochChannel,generation); 664 } 665 } 666 667 if ((res != kIOReturnSuccess) && (res != kIOFireWireBusReset)) 668 { 669 // We failed to reallocate (and not due to a bus-reset). 670 pIRMAllocation->failedToRealloc(); 671 } 672 } 673 674 // Unlock the lock 675 IORecursiveLockUnlock(fLock); 676 677 // clean up thread info 678 IOFree( threadInfo, sizeof(threadInfo) ); 679 pIRMAllocation->release(); // retain occurred in handleBusReset 680 681 FWTrace( kFWTIsoch, kTPIsochIRMThreadFunc, (uintptr_t)(threadInfo->fControl->getLink()), threadInfo->fIsochChannel, threadInfo->fBandwidthUnits, res ); 682} 683