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