1/* 2 * Copyright (c) 1998-2000 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#if __ppc__ 24 25#include <IOKit/IOLib.h> 26 27#include <IOKit/IOBufferMemoryDescriptor.h> 28#include "FWDebugging.h" 29 30#include <IOKit/sbp2/IOFireWireSBP2LSIWorkaroundDescriptor.h> 31#include <IOKit/sbp2/IOFireWireSBP2ORB.h> 32 33#define kMinPacketSize 16 34#define kLowestPacketLimit 256 35 36///////////////////////////////////////////////////////////////////////////////////////////// 37 38// IOFireWireSBP2LSIRange 39// 40// this class keeps track of physical ranges 41 42// 43// The algorithm needs to keep track of a series of physical segments. This 44// is accomplished by keeping a series of IOFireWireSBP2LSIRange instances in 45// an OSArray. The OSArray gives us the ability to dynamically insert and 46// remove from the set of segments. 47// 48// Each IOFireWireSBP2LSIRange instance keeps track of the segment's physical 49// address and length, along with the data's original descriptor and offset 50// and potentially a second buffer's address for double buffered segments. 51// The class supplies methods to aid in the creation and destruction of buffers 52// and the syncronization of the buffer with the original data. 53// 54 55class IOFireWireSBP2LSIRange : public OSObject 56{ 57 OSDeclareDefaultStructors(IOFireWireSBP2LSIRange) 58 59protected: 60 IOFireWireSBP2LSIWorkaroundDescriptor * parentDesc; 61 62public: 63 64 void * buffer; 65 66 IOMemoryDescriptor * memory; 67 IOPhysicalAddress address; 68 IOByteCount length; 69 IOByteCount offset; 70 71 virtual void initWithParent( IOFireWireSBP2LSIWorkaroundDescriptor * desc ); 72 virtual void markForBuffering( void ); 73 virtual IOReturn allocateBuffer( IODirection direction ); 74 75 virtual IOReturn syncBufferForInput( void ); 76 virtual IOReturn syncBufferForOutput( void ); 77}; 78 79OSDefineMetaClassAndStructors(IOFireWireSBP2LSIRange, OSObject) 80 81// initWithParent 82// 83// 84 85void IOFireWireSBP2LSIRange::initWithParent( IOFireWireSBP2LSIWorkaroundDescriptor * desc ) 86{ 87 parentDesc = desc; 88 buffer = NULL; 89} 90 91// markForBuffering 92// 93// if we get marked for buffering. we will alocate a 94// double buffer when allocateBuffer is called 95 96void IOFireWireSBP2LSIRange::markForBuffering( void ) 97{ 98 buffer = (void*)0xffffffff; 99 address = NULL; 100} 101 102// allocateBuffer 103// 104// if markForBuffering was previously called, allocate 105// a double buffer. 106 107IOReturn IOFireWireSBP2LSIRange::allocateBuffer( IODirection /* direction */ ) 108{ 109 IOReturn status = kIOReturnSuccess; 110 111 if( buffer == ((void*)0xffffffff) ) 112 { 113 buffer = parentDesc->bufferAllocatorNewBuffer( &address ); 114 if( !buffer ) 115 status = kIOReturnNoMemory; 116 } 117 118 return status; 119} 120 121// syncBufferForInput 122// 123// copies data from the buffers to the original descriptor, 124// if we are being buffered. 125 126IOReturn IOFireWireSBP2LSIRange::syncBufferForInput( void ) 127{ 128 IOReturn status = kIOReturnSuccess; 129 130 if( buffer ) 131 { 132 memory->writeBytes( offset, buffer, length ); 133 } 134 135 return status; 136} 137 138// syncBufferForOutput 139// 140// copies the data from the original descriptor to the buffer, 141// if we are being buffered. 142 143IOReturn IOFireWireSBP2LSIRange::syncBufferForOutput( void ) 144{ 145 IOReturn status = kIOReturnSuccess; 146 147 if( buffer ) 148 { 149 memory->readBytes( offset, buffer, length ); 150 } 151 152 return status; 153} 154 155///////////////////////////////////////////////////////////////////////////////////////////// 156 157OSDefineMetaClassAndStructors(IOFireWireSBP2LSIWorkaroundDescriptor, IOGeneralMemoryDescriptor) 158 159OSMetaClassDefineReservedUnused(IOFireWireSBP2LSIWorkaroundDescriptor, 0); 160OSMetaClassDefineReservedUnused(IOFireWireSBP2LSIWorkaroundDescriptor, 1); 161OSMetaClassDefineReservedUnused(IOFireWireSBP2LSIWorkaroundDescriptor, 2); 162OSMetaClassDefineReservedUnused(IOFireWireSBP2LSIWorkaroundDescriptor, 3); 163OSMetaClassDefineReservedUnused(IOFireWireSBP2LSIWorkaroundDescriptor, 4); 164OSMetaClassDefineReservedUnused(IOFireWireSBP2LSIWorkaroundDescriptor, 5); 165OSMetaClassDefineReservedUnused(IOFireWireSBP2LSIWorkaroundDescriptor, 6); 166OSMetaClassDefineReservedUnused(IOFireWireSBP2LSIWorkaroundDescriptor, 7); 167OSMetaClassDefineReservedUnused(IOFireWireSBP2LSIWorkaroundDescriptor, 8); 168 169///////////////////////////////////////////////////////////////////////////////////////////// 170// range allocator 171// 172// allocates ranges from a permanent pool then by allocating memory if allowed 173 174IOReturn IOFireWireSBP2LSIWorkaroundDescriptor::rangeAllocatorInitialize( UInt32 rangeCount ) 175{ 176 IOReturn status = kIOReturnSuccess; 177 178 FWLSILOGALLOC( ("LSILOG : allocating %ld permanent ranges\n", rangeCount) ); 179 180 // init fields 181 182 if( status == kIOReturnSuccess ) 183 { 184 fAllocatedRangesCount = 0; // unnecessary 185 186 fPermanentRanges = OSArray::withCapacity( rangeCount ? rangeCount : 1 ); 187 if( !fPermanentRanges ) 188 status = kIOReturnNoMemory; 189 } 190 191 // create permanent ranges 192 193 if( status == kIOReturnSuccess ) 194 { 195 UInt32 i; 196 197 for( i = 0; 198 status == kIOReturnSuccess && i < rangeCount; 199 i++ ) 200 { 201 IOFireWireSBP2LSIRange * range = NULL; 202 203 if( status == kIOReturnSuccess ) 204 { 205 range = new IOFireWireSBP2LSIRange; 206 if( !range ) 207 status = kIOReturnNoMemory; 208 } 209 210 if( status == kIOReturnSuccess ) 211 { 212 if( !fPermanentRanges->setObject( i, range ) ) 213 status = kIOReturnError; 214 } 215 216 if( range ) 217 range->release(); 218 } 219 220 FWKLOGASSERT( rangeCount == i ); 221 } 222 223 FWLSILOGALLOC( ("LSILOG : successfully allocated %ld permanent ranges, status = 0x%08lx\n", 224 fPermanentRanges->getCount(), status) ); 225 226 return status; 227} 228 229void IOFireWireSBP2LSIWorkaroundDescriptor::rangeAllocatorDeallocateAllRanges( void ) 230{ 231 FWLSILOGALLOC( ("LSILOG : reset allocated ranges count. new allocCount = 0\n") ); 232 233 fAllocatedRangesCount = 0; 234} 235 236IOFireWireSBP2LSIRange * IOFireWireSBP2LSIWorkaroundDescriptor::rangeAllocatorNewRange( void ) 237{ 238 IOFireWireSBP2LSIRange * range = NULL; 239 240 // use a preallocated range if possible 241 if( fAllocatedRangesCount < fPermanentRanges->getCount() ) 242 { 243 range = (IOFireWireSBP2LSIRange *) 244 fPermanentRanges->getObject( fAllocatedRangesCount ); 245 246 range->retain(); // 1 ref for us and 1 ref for them 247 248 fAllocatedRangesCount++; 249 250 FWLSILOGALLOC( ("LSILOG : allocating from permanent ranges. new allocCount = %ld\n", 251 fAllocatedRangesCount ) ); 252 } 253 else if( !fFixedCapacity ) // create one if we can 254 { 255 FWLSILOGALLOC( ("LSILOG : creating new range\n") ); 256 range = new IOFireWireSBP2LSIRange; 257 } 258 259 return range; 260} 261 262void IOFireWireSBP2LSIWorkaroundDescriptor::rangeAllocatorFree( void ) 263{ 264 FWLSILOGALLOC( ("LSILOG : free all ranges\n") ); 265 if( fPermanentRanges ) 266 fPermanentRanges->release(); // releases all the ranges, too 267} 268 269///////////////////////////////////////////////////////////////////////////////////////////// 270// buffer allocator 271// 272// allocates buffers from a permanent pool then by allocating memory if allowed. 273// dishes out buffers in kMinPacketSize*2 increments 274 275IOReturn IOFireWireSBP2LSIWorkaroundDescriptor::bufferAllocatorInitialize 276 ( IOByteCount requestedBufferSize ) 277{ 278 IOReturn status = kIOReturnSuccess; 279 280 // calc page count 281 IOByteCount bufferSize = ((requestedBufferSize + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1)); 282 UInt32 pageCount = bufferSize / PAGE_SIZE; 283 284 FWKLOGASSERT( (PAGE_SIZE % (kMinPacketSize*2)) == 0 ); 285 286 FWLSILOGALLOC( ("LSILOG : allocating %ld permanent pages of buffer\n", pageCount) ); 287 288 if( status == kIOReturnSuccess ) 289 { 290 fAllocatedBytesCount = 0; // unnecessary 291 292 fBufferDescriptors = OSArray::withCapacity( pageCount ? pageCount : 1 ); 293 if( !fBufferDescriptors ) 294 status = kIOReturnNoMemory; 295 } 296 297 if( status == kIOReturnSuccess ) 298 { 299 UInt32 i; 300 301 for( i = 0; 302 status == kIOReturnSuccess && i < pageCount; 303 i++ ) 304 { 305 ::IOBufferMemoryDescriptor * bufferDesc = NULL; 306 307 if( status == kIOReturnSuccess ) 308 { 309 bufferDesc = ::IOBufferMemoryDescriptor::withOptions( kIODirectionOutIn | kIOMemoryUnshared, PAGE_SIZE, PAGE_SIZE ); 310 if( !bufferDesc ) 311 status = kIOReturnNoMemory; 312 } 313 314 if( status == kIOReturnSuccess ) 315 { 316 status = bufferDesc->prepare(); 317 } 318 319 if( status == kIOReturnSuccess ) 320 { 321 if( !fBufferDescriptors->setObject( i, bufferDesc ) ) 322 status = kIOReturnError; 323 } 324 325 if( bufferDesc ) 326 bufferDesc->release(); 327 } 328 329 FWKLOGASSERT( pageCount == i ); 330 331 // we use i instead of rangeCount so we free correctly on error 332 fPermanentPages = i; 333 } 334 335 FWLSILOGALLOC( ("LSILOG : successfully allocated %ld permanent pages, status = 0x%08lx\n", 336 fPermanentPages, status) ); 337 338 return status; 339} 340 341void IOFireWireSBP2LSIWorkaroundDescriptor::bufferAllocatorDeallocateAllBuffers( void ) 342{ 343 // it's all free now 344 fAllocatedBytesCount = 0; 345 346 FWLSILOGALLOC( ("LSILOG : reset allocated bytes count. new allocByteCount = 0\n") ); 347 348 FWLSILOGALLOC( ("LSILOG : freeing %ld pages of non-permanent buffer\n", 349 fBufferDescriptors->getCount() - fPermanentPages) ); 350 351 // remove all but the permanent pages 352 UInt32 i = 0; 353 while( (i = fBufferDescriptors->getCount()) > fPermanentPages ) 354 { 355 fBufferDescriptors->removeObject(i-1); 356 } 357} 358 359void * IOFireWireSBP2LSIWorkaroundDescriptor::bufferAllocatorNewBuffer( 360 IOPhysicalAddress * address ) 361{ 362 IOReturn status = kIOReturnSuccess; 363 void * buffer = NULL; 364 365 ::IOBufferMemoryDescriptor * bufferDesc = NULL; 366 367 UInt32 aligned_page = fAllocatedBytesCount & ~(PAGE_SIZE-1); 368 UInt32 page = aligned_page / PAGE_SIZE; 369 UInt32 offset = fAllocatedBytesCount - aligned_page; 370 371 fAllocatedBytesCount += kMinPacketSize*2; // max possible buffer size 372 373 FWKLOGASSERT( page == ((fAllocatedBytesCount) & ~(PAGE_SIZE-1)) ); 374 375 if( page < fBufferDescriptors->getCount() ) 376 { 377 bufferDesc = (::IOBufferMemoryDescriptor *) 378 fBufferDescriptors->getObject( page ); 379 380 FWLSILOGALLOC( ("LSILOG : allocating from permanent pages. new allocByteCount = %ld\n", 381 fAllocatedBytesCount) ); 382 } 383 else if( !fFixedCapacity ) 384 { 385 FWLSILOGALLOC( ("LSILOG : creating new page. new allocByteCount = %ld\n", 386 fAllocatedBytesCount) ); 387 388 bufferDesc = ::IOBufferMemoryDescriptor::withOptions( kIODirectionOutIn | kIOMemoryUnshared, PAGE_SIZE, PAGE_SIZE ); 389 if( !bufferDesc ) 390 status = kIOReturnNoMemory; 391 392 if( status == kIOReturnSuccess ) 393 { 394 bufferDesc->prepare(); 395 } 396 397 if( status == kIOReturnSuccess ) 398 { 399 if( !fBufferDescriptors->setObject( page, bufferDesc ) ) 400 status = kIOReturnError; 401 } 402 403 if( bufferDesc ) 404 bufferDesc->release(); 405 406 } 407 408 if( status == kIOReturnSuccess ) 409 { 410 IOByteCount seg_size = 0; 411 *address = bufferDesc->getPhysicalSegment( 0, &seg_size ) + offset; 412 buffer = (void*)((UInt32)bufferDesc->getBytesNoCopy() + offset); 413 } 414 415 FWLSILOGALLOC( ("LSILOG : return buffer = 0x%lx\n", buffer) ); 416 417 return buffer; 418} 419 420void IOFireWireSBP2LSIWorkaroundDescriptor::bufferAllocatorFree( void ) 421{ 422 FWLSILOGALLOC( ("LSILOG : free all pages\n") ); 423 424 if( fBufferDescriptors ) 425 fBufferDescriptors->release(); // releases all in collection 426} 427 428///////////////////////////////////////////////////////////////////////////////////////////// 429// rangeTable allocator 430// 431// allocates memory for the range table, potentially reallocating 432// a new block of memory if allowed. 433 434IOReturn IOFireWireSBP2LSIWorkaroundDescriptor::rangeTableAllocatorInitialize 435 ( UInt32 entries ) 436{ 437 IOReturn status = kIOReturnSuccess; 438 439 if( status == kIOReturnSuccess ) 440 { 441 fRangeTableSize = sizeof( IOPhysicalRange ) * entries; 442 fRangeTable = (IOPhysicalRange*) IOMalloc( fRangeTableSize ); 443 if( fRangeTable == NULL ) 444 status = kIOReturnError; 445 } 446 447 FWLSILOGALLOC( ("LSILOG : created %d entry range table. status = 0x%08lx\n", 448 entries, status) ); 449 450 return status; 451} 452 453IOPhysicalRange * IOFireWireSBP2LSIWorkaroundDescriptor::rangeTableAllocatorNewTable 454 ( UInt32 entries ) 455{ 456 IOReturn status = kIOReturnSuccess; 457 458 IOPhysicalRange * buffer = NULL; 459 460 UInt32 requestedSize = sizeof( IOPhysicalRange ) * entries; 461 462 if( requestedSize <= fRangeTableSize ) 463 { 464 FWLSILOGALLOC( ("LSILOG : requested entry count = %ld. using existing range table\n", 465 entries) ); 466 467 buffer = fRangeTable; 468 } 469 else if( !fFixedCapacity ) 470 { 471 FWLSILOGALLOC( ("LSILOG : requested entry count = %ld. creating new range table\n", 472 entries) ); 473 474 if( fRangeTable ) 475 { 476 IOFree( fRangeTable, fRangeTableSize ); 477 fRangeTable = NULL; 478 } 479 480 fRangeTable = (IOPhysicalRange*) IOMalloc( requestedSize ); 481 if( !fRangeTable ) 482 status = kIOReturnNoMemory; 483 484 if( status == kIOReturnSuccess ) 485 { 486 buffer = fRangeTable; 487 } 488 } 489 490 FWLSILOGALLOC( ("LSILOG : return rangeTable = 0x%08lx\n", buffer) ); 491 492 return buffer; 493} 494 495void IOFireWireSBP2LSIWorkaroundDescriptor::rangeTableAllocatorFree( void ) 496{ 497 FWLSILOGALLOC( ("LSILOG : free range table\n") ); 498 499 if( fRangeTable ) 500 { 501 IOFree( fRangeTable, fRangeTableSize ); 502 fRangeTable = NULL; 503 } 504} 505 506///////////////////////////////////////////////////////////////////////////////////////////// 507 508// 509// disable other initialization methods 510// 511 512bool IOFireWireSBP2LSIWorkaroundDescriptor::initWithAddress( 513 void * /* address */ , 514 IOByteCount /* withLength */ , 515 IODirection /* withDirection */ ) 516{ 517 return false; 518} 519 520bool IOFireWireSBP2LSIWorkaroundDescriptor::initWithAddress( 521 vm_address_t /* address */ , 522 IOByteCount /* withLength */ , 523 IODirection /* withDirection */ , 524 task_t /* withTask */ ) 525{ 526 return false; 527} 528 529bool IOFireWireSBP2LSIWorkaroundDescriptor::initWithPhysicalAddress( 530 IOPhysicalAddress /* address */ , 531 IOByteCount /* withLength */ , 532 IODirection /* withDirection */ ) 533{ 534 return false; 535} 536 537 538bool IOFireWireSBP2LSIWorkaroundDescriptor::initWithPhysicalRanges( 539 IOPhysicalRange * /* ranges */ , 540 UInt32 /* withCount */ , 541 IODirection /* withDirection */ , 542 bool /* asReference */ ) 543{ 544 return false; 545} 546 547bool IOFireWireSBP2LSIWorkaroundDescriptor::initWithRanges 548 ( IOVirtualRange * ranges, 549 UInt32 withCount, 550 IODirection withDirection, 551 task_t withTask, 552 bool asReference ) 553{ 554 // this method is now protected so clients can't call it 555 // but initWithPhysicalRanges calls this so we route it to the superclass 556 return IOGeneralMemoryDescriptor::initWithRanges( ranges, withCount, withDirection, withTask, asReference ); 557} 558 559 560// withCapacity 561// 562// create a new IOFireWireSBP2LSIWorkaroundDescriptor with a initial capacity 563// 564// creates a descriptor with a set amount of storage which can always be obtained 565// without an allocation. The decriptor will grow dynamically to handle larger requests 566// unless fixedCapacity is true. 567// 568 569IOFireWireSBP2LSIWorkaroundDescriptor * IOFireWireSBP2LSIWorkaroundDescriptor::withCapacity 570 ( UInt32 maxElements, IOByteCount permanentBufferSpace, bool fixedCapacity ) 571{ 572 IOFireWireSBP2LSIWorkaroundDescriptor *me = new IOFireWireSBP2LSIWorkaroundDescriptor; 573 574 if( me && !me->initWithCapacity( maxElements, permanentBufferSpace, fixedCapacity ) ) 575 { 576 me->release(); 577 me = NULL; 578 } 579 580 return me; 581} 582 583// initWithCapacity 584// 585// 586 587bool IOFireWireSBP2LSIWorkaroundDescriptor::initWithCapacity 588 ( UInt32 permanentRanges, IOByteCount permanentBufferSpace, bool fixedCapacity ) 589{ 590 IOReturn status = kIOReturnSuccess; 591 592 if( status == kIOReturnSuccess ) 593 { 594 fFixedCapacity = fixedCapacity; 595 596 status = rangeAllocatorInitialize( permanentRanges ); 597 } 598 599 if( status == kIOReturnSuccess ) 600 { 601 status = bufferAllocatorInitialize( permanentBufferSpace ); 602 } 603 604 if( status == kIOReturnSuccess ) 605 { 606 fRanges = OSArray::withCapacity( permanentRanges ? permanentRanges : 1 ); 607 if( !fRanges ) 608 status = kIOReturnNoMemory; 609 } 610 611 if( status == kIOReturnSuccess ) 612 { 613 status = rangeTableAllocatorInitialize( permanentRanges ); 614 } 615 616 return ( status == kIOReturnSuccess ); 617} 618 619// resetToInitialCapacity 620// 621// We allow the descriptor to be reinited. We also allow the a a minimum capacity 622// which we will never need memory allocations for. This routine returns the memory 623// descriptor to its original minimimum capacity state. 624 625IOReturn IOFireWireSBP2LSIWorkaroundDescriptor::resetToInitialCapacity( void ) 626{ 627 IOReturn status = kIOReturnSuccess; 628 629 // 630 // release the referenced descriptor 631 // 632 633 if( fOriginalDesc ) 634 fOriginalDesc->release(); 635 636 // 637 // free buffers 638 // 639 640 bufferAllocatorDeallocateAllBuffers(); 641 642 // 643 // reset ranges 644 // 645 646 if( fRanges ) 647 fRanges->flushCollection(); // release ranges 648 649 rangeAllocatorDeallocateAllRanges(); 650 651 return status; 652} 653 654// withDescriptor 655// 656// create a new IOFireWireSBP2LSIWorkaroundDescriptor 657// 658// len and offset are the length of and offset into the segment in desc which 659// will be used for the transfer. The new descriptor will perform 660// the workaround only on this segment. The resulting descriptor will encompass only this 661// segment and this segment will begin at offset zero not its former offset in the orginal 662// descriptor. 663// 664 665IOFireWireSBP2LSIWorkaroundDescriptor * IOFireWireSBP2LSIWorkaroundDescriptor::withDescriptor 666 ( IOMemoryDescriptor * desc, IOByteCount offset, IOByteCount len, 667 IODirection direction ) 668{ 669 IOReturn status = kIOReturnSuccess; 670 IOFireWireSBP2LSIWorkaroundDescriptor *me = NULL; 671 672 if( status == kIOReturnSuccess ) 673 { 674 me = new IOFireWireSBP2LSIWorkaroundDescriptor; 675 if( !me ) 676 status = kIOReturnNoMemory; 677 } 678 679 if( status == kIOReturnSuccess ) 680 { 681 if( !me->initWithCapacity( 0, 0, false ) ) 682 status = kIOReturnError; 683 } 684 685 if( status == kIOReturnSuccess ) 686 { 687 if( !me->initWithDescriptor( desc, offset, len, direction ) ) 688 status = kIOReturnError; 689 } 690 691 if( me && status != kIOReturnSuccess ) 692 { 693 me->release(); 694 me = NULL; 695 } 696 697 return me; 698} 699 700// initWithDescriptor 701// 702// 703 704bool IOFireWireSBP2LSIWorkaroundDescriptor::initWithDescriptor 705 ( IOMemoryDescriptor * desc, IOByteCount offset, IOByteCount length, 706 IODirection direction ) 707{ 708 IOReturn status = kIOReturnSuccess; 709 710 UInt32 count = 0; 711 IOPhysicalRange * rangeTable = NULL; 712 713 // 714 // return descriptor to its initial state if needed 715 // 716 717 if( status == kIOReturnSuccess ) 718 { 719 status = resetToInitialCapacity(); 720 } 721 722 // 723 // setup vars and verify args 724 // 725 726 if( status == kIOReturnSuccess ) 727 { 728 // store source descriptor, offset, ... 729 730 fDirection = direction; 731 fOriginalDesc = desc; 732 fOffset = offset; 733 734 if( !desc ) 735 status = kIOReturnError; 736 } 737 738 if( status == kIOReturnSuccess ) 739 { 740 fOriginalDesc->retain(); 741 } 742 743 if( status == kIOReturnSuccess ) 744 { 745 // ... and length 746 747 fLength = length; 748 if( fLength == 0 ) 749 fLength = desc->getLength(); 750 } 751 752 if( status == kIOReturnSuccess ) 753 { 754 // there is nothing we can do if the whole 755 // transfer is smaller than 16 bytes 756 757 if( fLength < kMinPacketSize ) 758 status = kIOReturnError; 759 } 760 761 if( status == kIOReturnSuccess ) 762 { 763 // creates the fRanges array based on the physical segments 764 // of the original descriptor 765 766 status = initializeRangesArray(); 767 } 768 769#if LSILOGGING 770 { 771 FWLSILOG( ("LSILOG : original descriptor\n") ); 772 for( UInt32 i = 0; i < fRanges->getCount(); i++ ) 773 { 774 IOFireWireSBP2LSIRange * range = (IOFireWireSBP2LSIRange*)fRanges->getObject(i); 775 FWLSILOG( ("LSILOG : range #%ld addr = 0x%08lx len = %d buffered = %s\n", 776 i, range->address, range->length, range->buffer == NULL ? "no" : "yes") ); 777 778 #if 0 779 if( i % 10 == 0 ) 780 IOSleep(1000); 781 #endif 782 } 783 } 784#endif 785 786 // 787 // run _the eric algorithm_ now ! 788 // 789 790 if( status == kIOReturnSuccess ) 791 { 792 // step one : 793 // Fix up individual segments smaller than 16 bytes by double-buffering 794 795 status = recalculateSmallSegments(); 796 } 797 798#if 0 799 { 800 FWLSILOG( ("LSILOG : descriptor after recalculateSmallSegments\n") ); 801 for( UInt32 i = 0; i < fRanges->getCount(); i++ ) 802 { 803 IOFireWireSBP2LSIRange * range = (IOFireWireSBP2LSIRange*)fRanges->getObject(i); 804 FWLSILOG( ("LSILOG : range #%ld addr = 0x%08lx len = %d buffered = %s\n", 805 i, range->address, range->length, range->buffer == NULL ? "no" : "yes") ); 806 #if 0 807 if( i % 10 == 0 ) 808 IOSleep(1000); 809 #endif 810 } 811 } 812#endif 813 814 if( status == kIOReturnSuccess ) 815 { 816 // step two : 817 // prevent > 60k segments 818 819 status = splitLargeSegments(); 820 } 821 822#if 0 823 { 824 FWLSILOG( ("LSILOG : descriptor after splitLargeSegments\n") ); 825 for( UInt32 i = 0; i < fRanges->getCount(); i++ ) 826 { 827 IOFireWireSBP2LSIRange * range = (IOFireWireSBP2LSIRange*)fRanges->getObject(i); 828 FWLSILOG( ("LSILOG : range #%ld addr = 0x%08lx len = %d buffered = %s\n", 829 i, range->address, range->length, range->buffer == NULL ? "no" : "yes") ); 830 #if 0 831 if( i % 10 == 0 ) 832 IOSleep(1000); 833 #endif 834 } 835 } 836#endif 837 838 if( status == kIOReturnSuccess ) 839 { 840 // step three : 841 // prevent < 16 byte packets 842 843 status = resegmentOddLengthSegments(); 844 } 845 846#if 0 847 { 848 FWLSILOG( ("LSILOG : descriptor after resegmentOddLengthSegments\n") ); 849 for( UInt32 i = 0; i < fRanges->getCount(); i++ ) 850 { 851 IOFireWireSBP2LSIRange * range = (IOFireWireSBP2LSIRange*)fRanges->getObject(i); 852 FWLSILOG( ("LSILOG : range #%ld addr = 0x%08lx len = %d buffered = %s\n", 853 i, range->address, range->length, range->buffer == NULL ? "no" : "yes") ); 854 #if 0 855 if( i % 10 == 0 ) 856 IOSleep(1000); 857 #endif 858 } 859 } 860#endif 861 862 // 863 // initialize buffers 864 // 865 866 if( status == kIOReturnSuccess ) 867 { 868 // setup double buffering 869 870 status = initializeBuffers(); 871 872 FWLSILOGALLOC( ("LSILOG : attempted to initialize buffers. status = 0x%08lx\n", status) ); 873 } 874 875#if LSILOGGING 876 { 877 FWLSILOG( ("LSILOG : fixed descriptor\n") ); 878 for( UInt32 i = 0; i < fRanges->getCount(); i++ ) 879 { 880 IOFireWireSBP2LSIRange * range = (IOFireWireSBP2LSIRange*)fRanges->getObject(i); 881 FWLSILOG( ("LSILOG : range #%ld addr = 0x%08lx len = %d buffered = %s\n", 882 i, range->address, range->length, range->buffer == NULL ? "no" : "yes") ); 883 #if 0 884 if( i % 10 == 0 ) 885 IOSleep(1000); 886 #endif 887 } 888 } 889#endif 890 891 // 892 // create range table 893 // 894 895 if( status == kIOReturnSuccess ) 896 { 897 count = fRanges->getCount(); 898 rangeTable = rangeTableAllocatorNewTable( count ); 899 if( !rangeTable ) 900 status = kIOReturnError; 901 902 FWLSILOGALLOC( ("LSILOG : attempted to allocate range table. status = 0x%08lx\n", status) ); 903 } 904 905 // 906 // fill out range table 907 // 908 909 if( status == kIOReturnSuccess ) 910 { 911 for( UInt32 i = 0; i < count; i++ ) 912 { 913 IOFireWireSBP2LSIRange * range = (IOFireWireSBP2LSIRange*)fRanges->getObject( i ); 914 if( !range ) 915 { 916 status = kIOReturnError; 917 break; 918 } 919 920 rangeTable[i].address = range->address; 921 rangeTable[i].length = range->length; 922 } 923 924 FWLSILOGALLOC( ("LSILOG : attempted to fillout range table. status = 0x%08lx\n", status) ); 925 } 926 927 // 928 // init super 929 // 930 931 if( status == kIOReturnSuccess ) 932 { 933 if( !IOGeneralMemoryDescriptor::initWithPhysicalRanges 934 ( rangeTable, count, fDirection, true ) ) 935 status = kIOReturnError; 936 937 FWLSILOGALLOC( ("LSILOG : attempted to initialize decriptor. status = 0x%08lx\n", status) ); 938 } 939 940 return ( status == kIOReturnSuccess ); 941} 942 943///////////////////////////////////////////////////////////////////////////////////////////// 944 945// initializeRangesArray 946// 947// creates the fRanges array based on the physical segments of the original 948// descriptor 949 950IOReturn IOFireWireSBP2LSIWorkaroundDescriptor::initializeRangesArray( void ) 951{ 952 IOReturn status = kIOReturnSuccess; 953 954 IOPhysicalAddress phys = NULL; 955 IOByteCount seg_length = 0; 956 IOByteCount i; 957 UInt32 seg_count; 958 959 // 960 // add each segment to the array 961 // 962 963 i = fOffset; 964 seg_count = 0; 965 while( (status == kIOReturnSuccess) && 966 (phys = fOriginalDesc->getPhysicalSegment( i, &seg_length )) ) 967 { 968 FWLSILOGALLOC( ("LSILOG : creating range for segment %ld. offset = 0x%08lx, len = %ld\n", 969 seg_count, i, seg_length) ); 970 971 // clip to length 972 if( i + seg_length > fLength ) 973 seg_length = fLength - i; 974 975 // create a new range 976 IOFireWireSBP2LSIRange * range = rangeAllocatorNewRange(); 977 if( !range ) 978 status = kIOReturnNoMemory; 979 980 if( status == kIOReturnSuccess ) 981 { 982 range->memory = fOriginalDesc; 983 range->address = phys; 984 range->length = seg_length; 985 range->offset = i; 986 987 range->initWithParent( this ); 988 989 // this won't allocate memory because if we've used up 990 // our range space the range allocation would have failed 991 992 if( !fRanges->setObject( seg_count, range ) ) 993 status = kIOReturnError; 994 } 995 996 if( range ) 997 { 998 range->release(); 999 range = NULL; 1000 } 1001 1002 FWLSILOGALLOC( ("LSILOG : finished creating range for segment %ld. status = 0x%08lx\n", 1003 seg_count, status) ); 1004 1005 i += seg_length; 1006 seg_count++; 1007 1008 } 1009 1010 return status; 1011} 1012 1013// recalculateSmallSegments 1014// 1015// Fix up individual segments smaller than 16 bytes by double-buffering 1016// 1017// Assume: Total I/O must be 16 bytes or more (otherwise bridge just can't 1018// do it safely) 1019// 1020// If we make a segment double-buffered we will set its pointer 1021// to 0xffffffff to denote this. We will create the actual buffer later once 1022// we're done tweaking everything. It is quite possible that we will merge this 1023// with another buffered segment and this buffer will not need to be allocated 1024 1025IOReturn IOFireWireSBP2LSIWorkaroundDescriptor::recalculateSmallSegments( void ) 1026{ 1027 IOReturn status = kIOReturnSuccess; 1028 1029 UInt32 i = 0; 1030 while( i < fRanges->getCount() ) 1031 { 1032 IOFireWireSBP2LSIRange * range = (IOFireWireSBP2LSIRange*)fRanges->getObject(i); 1033 1034 if( range->length < kMinPacketSize ) 1035 { 1036 IOByteCount bytesNeeded = (kMinPacketSize - range->length); 1037 1038 // must double buffer this part, and must make segment at least 16 bytes 1039 // must combine with part of an adjacent segment to reach 16 byte size 1040 1041 if( i == (fRanges->getCount() - 1) ) 1042 { 1043 // this is the final segment. we know that the previous 1044 // segment plus this segment must be at least 16 bytes. 1045 // we are going to either steal from the previous segment 1046 // or merge with it. 1047 1048 IOFireWireSBP2LSIRange * prevRange = 1049 (IOFireWireSBP2LSIRange*)fRanges->getObject(i-1); 1050 1051 if( prevRange->length < (kMinPacketSize*2) ) 1052 { 1053 // previous segment is too small to steal from, so merge with it 1054 1055 prevRange->markForBuffering(); // double buffer 1056 prevRange->length += range->length; // add our part 1057 1058 FWLSILOGALLOC( ("LSILOG : range #%ld - added %ld bytes to previous range\n", i, range->length) ); 1059 1060 fRanges->removeObject(i); // remove ourselves 1061 } 1062 else 1063 { 1064 // previous segment is large - steal what we need from the end 1065 1066 prevRange->length -= bytesNeeded; // get what we need 1067 // to be exactly 16 bytes 1068 1069 range->markForBuffering(); // double buffer 1070 range->length = kMinPacketSize; // make us 16 1071 1072 FWLSILOGALLOC( ("LSILOG : range #%ld - stole %ld bytes from previous range\n", i, bytesNeeded) ); 1073 } 1074 1075 i++; //next loop iteration 1076 } 1077 else 1078 { 1079 // not the final segment. steal from the next segment 1080 1081 IOFireWireSBP2LSIRange * nextRange = 1082 (IOFireWireSBP2LSIRange*)fRanges->getObject(i+1); 1083 1084 if( range->length + nextRange->length >= kMinPacketSize ) 1085 { 1086 // we can steal enough bytes (16 - range.length) from nextRange.length 1087 // to solve this 1088 1089 // Note: nextRange might be left with fewer than 16 bytes. If so, it will 1090 // be fixed on the next iteration. 1091 1092 nextRange->length -= bytesNeeded; // get what we need 1093 nextRange->address += bytesNeeded; // bump his pointer by what we stole 1094 1095 range->length = kMinPacketSize; // make us 16 1096 range->markForBuffering(); // double buffer it 1097 1098 FWLSILOGALLOC( ("LSILOG : range #%ld - stole %ld bytes from next range\n", i, bytesNeeded) ); 1099 1100 i++; // next loop iteration 1101 } 1102 else 1103 { 1104 // the yuck case 1105 // nextRange.length is too small to completely solve our problem 1106 // combine with it and delete the next range, but then reprocess 1107 // so we can steal yet more bytes to make it okay 1108 1109 range->length += nextRange->length; // get all his bytes 1110 range->markForBuffering(); // double buffer it 1111 1112 FWLSILOGALLOC( ("LSILOG : range #%ld - stole all %ld bytes from next range\n", i, nextRange->length) ); 1113 1114 fRanges->removeObject(i+1); // leave no evidence 1115 1116 // don't do i++ because we want to reprocess the same range 1117 } 1118 } 1119 } 1120 else 1121 { 1122 FWLSILOGALLOC( ("LSILOG : range #%ld - range has enough bytes\n", i) ); 1123 1124 i++; 1125 } 1126 1127 #if 0 1128 IOSleep(500); 1129 #endif 1130 } 1131 1132 return status; 1133} 1134 1135// splitLargeSegments 1136// 1137// prevent > 60k segments 1138// 1139// Segments might be larger than 60k. If so, the SBP-2 layer might break them up. 1140// We don't know how SBP-2 will do that; it might create a segment that triggers 1141// the bug! So we break up all segments to 60k or less so that the SBP-2 layer 1142// won't mess with them 1143 1144IOReturn IOFireWireSBP2LSIWorkaroundDescriptor::splitLargeSegments( void ) 1145{ 1146 IOReturn status = kIOReturnSuccess; 1147 1148 UInt32 i = 0; 1149 while( i < fRanges->getCount() ) 1150 { 1151 IOFireWireSBP2LSIRange * range = (IOFireWireSBP2LSIRange*)fRanges->getObject(i); 1152 1153 if( range->length > kFWSBP2MaxPageClusterSize ) 1154 { 1155 IOFireWireSBP2LSIRange * nextRange = NULL; 1156 1157 if( status == kIOReturnSuccess ) 1158 { 1159 // create a new range 1160 nextRange = rangeAllocatorNewRange(); 1161 if( nextRange ) 1162 status = kIOReturnSuccess; 1163 } 1164 1165 if( status == kIOReturnSuccess ) 1166 { 1167 // trim down to exactly 60k; put the remainder in nextRange 1168 // Note: ok if nextRange is an unfortunate size (say, < 16 bytes) 1169 // we will fix that in step 3 1170 1171 nextRange->memory = fOriginalDesc; 1172 nextRange->address = range->address + kFWSBP2MaxPageClusterSize; 1173 // start at the end of range 1174 nextRange->length = range->length - kFWSBP2MaxPageClusterSize; 1175 // remainder 1176 1177 range->length = kFWSBP2MaxPageClusterSize; // just the first 60k 1178 1179 nextRange->initWithParent( this ); 1180 1181 if( !fRanges->setObject( i+1, nextRange ) ) 1182 status = kIOReturnError; 1183 } 1184 1185 if( nextRange ) 1186 { 1187 nextRange->release(); 1188 nextRange = NULL; 1189 } 1190 } 1191 1192 i++; 1193 } 1194 1195 return status; 1196} 1197 1198// resegmentOddLengthSegments 1199// 1200// prevent < 16 byte packets 1201// 1202// All segments are now at least 16 bytes. None will be broken up by SBP-2, but 1203// an odd-length segment (not multiple of packet size) could enf in a < 16 byte 1204// packet. For example, a segment of 2056 bytes would be transferred by the 1205// bridge as one 2048 byte packet and one 8 byte packet. 1206// 1207// For each segment find out if this is possible. If so, the segment can be 1208// broken into (probably unequal) halves to prevent it. For example, break 1209// the 2056 byte segment into 2032 and 24 byte degments. Bridge must use 1210// 2032 byte packet (or 1024 and 1008, or 512, 512, 512 and 496) followed 1211// by a 24 byte packet. One extra PTE will be needed in SBP-2. 1212// 1213// Assume: Extra PTE is better than double-buffering entire segment. If memory 1214// is mostly contiguous, most segments will be about 60k. One extra PTE per 1215// 60k may double the PTE count, but it was low to begin with. If memory is 1216// mostly discontiguous, most PTEs are already perfect 4k pages and won't 1217// need to be broken up - probably only the first and the final PTE will be 1218// broken up. PTE increase is small. 1219// 1220// We specify packet size in the ORB, but SBP-2 can override it with a smaller 1221// value. Assume smallest size ever kLowestPacketLimit is 256. It's okay if 1222// we assume too low of a kLowestPacketLimit (just mildly inefficient sometimes.) 1223 1224IOReturn IOFireWireSBP2LSIWorkaroundDescriptor::resegmentOddLengthSegments( void ) 1225{ 1226 IOReturn status = kIOReturnSuccess; 1227 1228 UInt32 i = 0; 1229 while( i < fRanges->getCount() ) 1230 { 1231 IOFireWireSBP2LSIRange * range = (IOFireWireSBP2LSIRange*)fRanges->getObject(i); 1232 UInt32 finalPacketSize = range->length % kLowestPacketLimit; 1233 1234 // might target end up with < 16 bytes in the final packet 1235 if( finalPacketSize != 0 && finalPacketSize < kMinPacketSize ) 1236 { 1237 IOFireWireSBP2LSIRange * nextRange = NULL; 1238 1239 // final packet would have been less than 16 bytes. But total segment is 1240 // at least 257 bytes. Why? We removed all < 16 byte segments, so this 1241 // segment must be at least 257 bytes for (range->length % kLowestPacketLimit) 1242 // < kMinPacketSize. So we can steal 16 bytes to form a new segment, leaving 1243 // a safe (big) segment 1244 1245 if( status == kIOReturnSuccess ) 1246 { 1247 // create a new range 1248 nextRange = rangeAllocatorNewRange(); 1249 if( nextRange ) 1250 status = kIOReturnSuccess; 1251 } 1252 1253 if( status == kIOReturnSuccess ) 1254 { 1255 range->length -= kMinPacketSize; // 16 is always safe 1256 1257 nextRange->memory = fOriginalDesc; 1258 nextRange->address = range->address + range->length; 1259 // old starting address 1260 nextRange->length = kMinPacketSize; // new size 1261 1262 nextRange->initWithParent( this ); 1263 1264 if( !fRanges->setObject( i+1, nextRange ) ) 1265 status = kIOReturnError; 1266 } 1267 1268 if( nextRange ) 1269 { 1270 nextRange->release(); 1271 nextRange = NULL; 1272 } 1273 1274 } 1275 1276 i++; 1277 } 1278 1279 return status; 1280} 1281 1282// intializeBuffers 1283// 1284// setup double buffering 1285// 1286// go through and recalculate the offset for each segment and allocate 1287// a buffer if necessary. we calculate the offset here and let the 1288// instances decide if they wish to allocate a buffer 1289 1290IOReturn IOFireWireSBP2LSIWorkaroundDescriptor::initializeBuffers( void ) 1291{ 1292 IOReturn status = kIOReturnSuccess; 1293 1294 UInt32 count = fRanges->getCount(); 1295 UInt32 offset = fOffset; 1296 for( UInt32 i = 0; i < count; i++ ) 1297 { 1298 IOFireWireSBP2LSIRange * range = (IOFireWireSBP2LSIRange*)fRanges->getObject(i); 1299 range->offset = offset; 1300 offset += range->length; 1301 status = range->allocateBuffer( fDirection ); 1302 if( status != kIOReturnSuccess ) 1303 break; 1304 } 1305 1306 return status; 1307} 1308 1309///////////////////////////////////////////////////////////////////////////////////////////// 1310 1311// syncBuffersForOutput 1312// 1313// copies data from the original source descriptor to the buffers. we just 1314// tell all instances to do it and they decided if they really need to. 1315// 1316// this should be called before an output transfer is initiated. 1317 1318IOReturn IOFireWireSBP2LSIWorkaroundDescriptor::syncBuffersForOutput( void ) 1319{ 1320 IOReturn status = kIOReturnSuccess; 1321 1322 // 1323 // sync buffers for output 1324 // 1325 1326 UInt32 count = fRanges->getCount(); 1327 for( UInt32 i = 0; i < count; i++ ) 1328 { 1329 IOFireWireSBP2LSIRange * range = (IOFireWireSBP2LSIRange*)fRanges->getObject( i ); 1330 status = range->syncBufferForOutput(); 1331 if( status != kIOReturnSuccess ) 1332 break; 1333 } 1334 1335 return status; 1336} 1337 1338// syncBuffersForInput 1339// 1340// copies data from the buffers to the original source descriptor. we just 1341// tell all instances to do it and they decided if they really need to. 1342// 1343// this should be called after an input transfer is complete. 1344 1345IOReturn IOFireWireSBP2LSIWorkaroundDescriptor::syncBuffersForInput( void ) 1346{ 1347 IOReturn status = kIOReturnSuccess; 1348 1349 // 1350 // sync buffers for input 1351 // 1352 1353 UInt32 count = fRanges->getCount(); 1354 for( UInt32 i = 0; i < count; i++ ) 1355 { 1356 IOFireWireSBP2LSIRange * range = (IOFireWireSBP2LSIRange*)fRanges->getObject( i ); 1357 status = range->syncBufferForInput(); 1358 if( status != kIOReturnSuccess ) 1359 break; 1360 } 1361 1362 return status; 1363} 1364 1365// free 1366// 1367// 1368 1369void IOFireWireSBP2LSIWorkaroundDescriptor::free( void ) 1370{ 1371 // 1372 // release the original 1373 // 1374 1375 if( fOriginalDesc ) 1376 fOriginalDesc->release(); 1377 1378 // 1379 // free the table, now... 1380 // 1381 1382 rangeTableAllocatorFree(); 1383 1384 // 1385 // free the buffers 1386 // 1387 1388 bufferAllocatorFree(); 1389 1390 // 1391 // free range allocator 1392 // 1393 1394 if( fRanges ) 1395 fRanges->release(); 1396 1397 rangeAllocatorFree(); 1398 1399 1400 IOGeneralMemoryDescriptor::free(); 1401} 1402 1403#endif 1404