1/*
2 *  IOFireWireLibNuDCL.cpp
3 *  IOFireWireFamily
4 *
5 *  Created by Niels on Thu Feb 27 2003.
6 *  Copyright (c) 2003 Apple Computer, Inc. All rights reserved.
7 *
8 *	$ Log:IOFireWireLibNuDCL.cpp,v $
9 */
10
11
12#import "IOFireWireLibPriv.h"
13#import "IOFireWireLibNuDCL.h"
14#import "IOFireWireLibNuDCLPool.h"
15#import "IOFireWireLibDevice.h"
16#import "IOFireWireLibCoalesceTree.h"
17#import <System/libkern/OSCrossEndian.h>
18
19namespace IOFireWireLib {
20
21	static IOByteCount
22	findOffsetInRanges ( IOVirtualAddress address, IOVirtualRange ranges[], unsigned rangeCount )
23	{
24		UInt32			index			= 0 ;
25		IOByteCount		distanceInRange ;
26		IOByteCount		offset = 0 ;
27
28		{
29			bool found = false ;
30			while ( !found && index < rangeCount )
31			{
32				distanceInRange = address - ranges[index].address ;
33				if ( found = ( distanceInRange < ranges[ index ].length ) )
34					offset += distanceInRange ;
35				else
36					offset += ranges[ index ].length ;
37
38				++index ;
39			}
40		}
41
42		return offset ;
43	}
44
45	#undef Class
46	#define Class NuDCL
47
48	NuDCL::NuDCL( NuDCLPool & pool, UInt32 numRanges, IOVirtualRange ranges[], NuDCLSharedData::Type type )
49	: fData( type )
50	, fPool( pool )
51	{
52		if ( numRanges > 6 )
53			throw kIOReturnBadArgument ;
54
55		// copy passed in ranges to our ranges array
56		bcopy( ranges, fData.ranges, numRanges * sizeof( IOVirtualRange ) ) ;
57		fData.rangeCount = numRanges ;
58	}
59
60	NuDCL::~NuDCL()
61	{
62	}
63
64	IOReturn
65	NuDCL::AppendRanges ( UInt32 numRanges, IOVirtualRange ranges[] )
66	{
67		if ( fData.rangeCount + numRanges > 6 )
68			return kIOReturnOverrun ;
69
70		bcopy( ranges, & fData.ranges[ fData.rangeCount ], numRanges * sizeof(IOVirtualRange) ) ;
71		fData.rangeCount += numRanges ;
72
73		return kIOReturnSuccess ;
74	}
75
76	IOReturn
77	NuDCL::SetRanges ( UInt32 numRanges, IOVirtualRange ranges[] )
78	{
79		fData.rangeCount = numRanges ;
80		bcopy( ranges, fData.ranges, numRanges * sizeof( IOVirtualRange ) ) ;
81
82		return kIOReturnSuccess ;
83	}
84
85	UInt32
86	NuDCL::GetRanges( UInt32 maxRanges, IOVirtualRange ranges[] ) const
87	{
88		unsigned count = MIN( maxRanges, fData.rangeCount ) ;
89		bcopy( fData.ranges, ranges, count * sizeof( IOVirtualRange ) ) ;
90
91		return count ;
92	}
93
94	IOReturn
95	NuDCL::GetSpan( IOVirtualRange& result ) const
96	{
97		if ( fData.rangeCount )
98		{
99			result.address = fData.ranges[0].address ;
100			IOVirtualAddress end = result.address + fData.ranges[0].length ;
101
102			for( unsigned index=2; index < fData.rangeCount; ++index )
103			{
104				result.address = MIN( result.address, fData.ranges[index].address ) ;
105				end = MAX( end, fData.ranges[index].address + fData.ranges[index].length ) ;
106			}
107
108			result.length = end - result.address ;
109		}
110		else
111		{
112			result = IOVirtualRangeMake( 0, 0 ) ;
113		}
114
115		return kIOReturnSuccess ;
116	}
117
118	IOByteCount
119	NuDCL::GetSize() const
120	{
121		IOByteCount result = 0 ;
122
123		for( unsigned index=0; index < fData.rangeCount; ++index )
124			result += fData.ranges[index].length ;
125
126		return result ;
127	}
128
129	IOReturn
130	NuDCL::AppendUpdateList( NuDCL* updateDCL )
131	{
132		if ( !fData.update.set )
133			fData.update.set = ::CFSetCreateMutable( kCFAllocatorDefault, 1, nil ) ;
134
135		if ( !fData.update.set )
136			return kIOReturnNoMemory ;
137
138		::CFSetSetValue( fData.update.set, updateDCL ) ;
139
140		return kIOReturnSuccess ;
141	}
142
143	IOReturn
144	NuDCL::SetUpdateList( CFSetRef updateList )
145	{
146		if ( fData.update.set )
147			::CFRelease( fData.update.set ) ;
148
149		if ( updateList )
150		{
151			fData.update.set = ::CFSetCreateMutableCopy( kCFAllocatorDefault, ::CFSetGetCount( updateList ), updateList ) ;
152
153			if ( !fData.update.set )
154				return kIOReturnNoMemory ;
155		}
156
157		return kIOReturnSuccess ;
158	}
159
160	void
161	NuDCL::EmptyUpdateList()
162	{
163		if ( fData.update.set )
164			::CFSetRemoveAllValues( fData.update.set ) ;
165	}
166
167	void
168	NuDCL::Print( FILE* file ) const
169	{
170		if ( fData.rangeCount > 0 )
171		{
172			fprintf( file, "\t\t\tranges:\n" ) ;
173			for( unsigned index=0; index < fData.rangeCount; ++index )
174			{
175#ifdef __LP64__
176				fprintf( file, "\t\t\t\t%u: < %llx, %u >\n", index, fData.ranges[index].address, fData.ranges[index].length ) ;
177#else
178				fprintf( file, "\t\t\t\t%u: < %x, %lu >\n", index, fData.ranges[index].address, fData.ranges[index].length ) ;
179#endif
180			}
181		}
182		if ( fData.branch.dcl )
183			fprintf( file, "\t\t\tbranch --> %p\n", fData.branch.dcl ) ;
184		if ( fData.callback )
185			fprintf( file, "\t\t\tcallback @%p\n", fData.callback ) ;
186		if ( fData.timeStamp.ptr )
187			fprintf( file, "\t\t\ttime stamp %p\n", fData.timeStamp.ptr ) ;
188		if ( fData.update.set )
189		{
190			CFIndex count = ::CFSetGetCount( fData.update.set ) ;
191			if ( count > 0 )
192			{
193				fprintf( file, "\t\t\tupdate {" ) ;
194
195				const void* values[ count ] ;
196				::CFSetGetValues( fData.update.set, values ) ;
197
198				for( CFIndex index=0; index < count; ++index )
199					fprintf( file, " %p", values[ index ] ) ;
200
201				fprintf( file, " }\n") ;
202			}
203		}
204		if ( fData.status.ptr )
205			fprintf( file, "\t\t\tstatus ptr %p\n", fData.status.ptr ) ;
206		fprintf( file, "\t\t\trefcon %p\n", fData.refcon ) ;
207	}
208
209	void
210	NuDCL::CoalesceBuffers( CoalesceTree & tree ) const
211	{
212		for( unsigned index=0; index < fData.rangeCount; ++index )
213		{
214			tree.CoalesceRange( fData.ranges[ index ] ) ;
215		}
216
217		if ( fData.timeStamp.ptr )
218		{
219			tree.CoalesceRange( IOVirtualRangeMake( (IOVirtualAddress) fData.timeStamp.ptr, sizeof( *fData.timeStamp.ptr ) ) ) ;
220		}
221
222		if ( fData.status.ptr )
223		{
224			tree.CoalesceRange( IOVirtualRangeMake( (IOVirtualAddress) fData.status.ptr, sizeof( *fData.status.ptr ) ) ) ;
225		}
226	}
227
228	IOByteCount
229	NuDCL::Export (
230		IOVirtualAddress * 		where,
231		IOVirtualRange			bufferRanges[],
232		unsigned				bufferRangeCount ) const
233	{
234
235		if (where)
236		{
237			unsigned updateSetCount = 0 ;
238			NuDCLExportData * exportedData = reinterpret_cast<NuDCLExportData *>( *where ) ;
239			*where += sizeof( NuDCLExportData );
240
241			exportedData->type = (UInt32) fData.type;
242			exportedData->refcon = (mach_vm_address_t) fData.refcon;
243			exportedData->flags = fData.flags;
244			exportedData->callback = (mach_vm_address_t) fData.callback;
245
246			// export buffer ranges
247			exportedData->rangeCount = fData.rangeCount;
248			for( unsigned index=0; index < exportedData->rangeCount; ++index )
249			{
250				exportedData->ranges[ index ].address = findOffsetInRanges( fData.ranges[ index ].address, bufferRanges, bufferRangeCount ) ;
251				exportedData->ranges[ index ].length = fData.ranges[ index ].length;
252			}
253
254			// export update list
255			uint64_t * exportList = NULL ;
256			if( fData.update.set )
257			{
258
259				updateSetCount = ::CFSetGetCount( fData.update.set ) ;
260				exportList = reinterpret_cast<uint64_t *>( *where ) ;
261				*where += sizeof( uint64_t) * updateSetCount ;
262
263
264				// We need to read the values out of the CFSet and into a buffer.
265				// For 64-bit mode, we just read directly into the export buffer.
266				// For 32-bit mode, we offset half way into the list portion of the export buffer as a temporary storage space
267#ifdef __LP64__
268				uint64_t *pDCLUpdateSetPointers = exportList;
269#else
270				UInt32 *pDCLUpdateSetPointers = (UInt32 *) &exportList[updateSetCount/2];
271#endif
272
273				// copy contents of update list to export data as array of NuDCL*
274				::CFSetGetValues( fData.update.set, reinterpret_cast<const void **>( pDCLUpdateSetPointers ) ) ;
275
276				// for each NuDCL* in update list in export data, change NuDCL* to corresponding NuDCL's fData->exportIndex
277				// field value
278				for( unsigned index=0; index < updateSetCount; ++index )
279				{
280					exportList[ index ] = ((NuDCL*)pDCLUpdateSetPointers[ index ])->GetExportIndex() ;
281				}
282
283				// stuff update list field in exported data with number of DCLs in update list
284				exportedData->updateCount = updateSetCount ;
285			}
286
287			// export user timestamp
288			if ( fData.timeStamp.ptr )
289				exportedData->timeStampOffset = findOffsetInRanges( (IOVirtualAddress)fData.timeStamp.ptr, bufferRanges, bufferRangeCount ) + 1 ;
290			else
291				exportedData->timeStampOffset = 0;
292
293			// export user status field
294			if ( fData.status.ptr )
295				exportedData->statusOffset = findOffsetInRanges( (IOVirtualAddress)fData.status.ptr, bufferRanges, bufferRangeCount ) + 1 ;
296			else
297				exportedData->statusOffset = 0;
298
299			// export branch
300			exportedData->branchIndex = fData.branch.dcl ? fData.branch.dcl->GetExportIndex() : 0 ;
301
302#ifndef __LP64__
303			ROSETTA_ONLY(
304				{
305						 for( unsigned index=0; index < exportedData->rangeCount; ++index )
306						 {
307							exportedData->ranges[ index ].address = CFSwapInt64( exportedData->ranges[ index ].address ) ;
308							exportedData->ranges[ index ].length = CFSwapInt64( exportedData->ranges[ index ].length ) ;
309						 }
310
311						 for( unsigned index=0; index < updateSetCount; ++index )
312						 {
313							exportList[ index ] = CFSwapInt64( exportList[ index ] ) ;
314						 }
315						 exportedData->updateCount = CFSwapInt64( exportedData->updateCount ) ;
316						 exportedData->timeStampOffset = CFSwapInt64( exportedData->timeStampOffset ) ;
317						 exportedData->statusOffset = CFSwapInt64( exportedData->statusOffset ) ;
318						 exportedData->type = (NuDCLSharedData::Type)CFSwapInt32( exportedData->type ) ;
319						 exportedData->callback = (mach_vm_address_t)CFSwapInt64(exportedData->callback ) ;
320						 exportedData->refcon = (mach_vm_address_t)CFSwapInt64(exportedData->refcon) ;
321						 exportedData->flags = CFSwapInt32( exportedData->flags | BIT(19) ) ;
322						 exportedData->rangeCount = CFSwapInt32( exportedData->rangeCount ) ;
323						 exportedData->branchIndex = CFSwapInt64( exportedData->branchIndex ) ;
324				}
325			) ;
326#endif
327		}
328
329		return  sizeof( NuDCLExportData ) + ( fData.update.set ? ::CFSetGetCount( fData.update.set ) * sizeof( uint64_t ) : 0 ) ;
330	}
331
332#pragma mark -
333
334	#undef super
335	#define super NuDCL
336
337	ReceiveNuDCL::ReceiveNuDCL( NuDCLPool & pool, UInt8 headerBytes, UInt32 numRanges, IOVirtualRange ranges[] )
338	: NuDCL( pool, numRanges, ranges, NuDCLSharedData::kReceiveType ),
339	  fReceiveData()
340	{
341		fReceiveData.headerBytes = headerBytes ;
342	}
343
344	IOReturn
345	ReceiveNuDCL::SetWaitControl ( bool wait )
346	{
347		fReceiveData.wait = wait ;
348
349		return kIOReturnSuccess ;
350	}
351
352	void
353	ReceiveNuDCL::Print( FILE* file ) const
354	{
355		fprintf( file, "\tRCV %p\thdr bytes=%d, wait=%s", this, fReceiveData.headerBytes, fReceiveData.wait ? "YES" : "NO" ) ;
356		if ( fReceiveData.wait )
357			fprintf( file, " (wait)" ) ;
358		fprintf( file, "\n" ) ;
359
360		super::Print( file ) ;
361	}
362
363	IOByteCount
364	ReceiveNuDCL::Export (
365		IOVirtualAddress * 		where,
366		IOVirtualRange			bufferRanges[],
367		unsigned				bufferRangeCount ) const
368	{
369		IOByteCount size = NuDCL::Export( where, bufferRanges, bufferRangeCount ) ;
370
371		if ( where )
372		{
373			ReceiveNuDCLExportData * exportedData = reinterpret_cast<ReceiveNuDCLExportData *>( *where ) ;
374			*where += sizeof( ReceiveNuDCLExportData );
375
376			exportedData->headerBytes = fReceiveData.headerBytes;
377			exportedData->wait = fReceiveData.wait;
378		}
379
380		return size + sizeof( ReceiveNuDCLExportData );
381	}
382
383
384#pragma mark -
385
386	#undef super
387	#define super NuDCL
388
389	SendNuDCL::SendNuDCL( NuDCLPool & pool, UInt32 numRanges, IOVirtualRange ranges[] )
390	: NuDCL( pool, numRanges, ranges, NuDCLSharedData::kSendType )
391	, fSendData()
392	{
393	}
394
395	void
396	SendNuDCL::Print( FILE* file ) const
397	{
398		fprintf( file, "\tSEND %p\thdr=", this ) ;
399		if ( fSendData.userHeader.ptr )
400		{
401			fprintf( file, "user @ %p, mask @ %p\n", fSendData.userHeader.ptr, fSendData.userHeaderMask.ptr ) ;
402		}
403		else
404		{
405			fprintf( file, "auto\n" ) ;
406		}
407
408		if ( fSendData.skipBranch.dcl )
409		{
410			fprintf( file, "\t\t\tskip --> %p\n", fSendData.skipBranch.dcl ) ;
411		}
412
413		if ( fSendData.skipCallback )
414		{
415			fprintf( file, "\t\t\tskip callback:%p refcon:%p\n", fSendData.skipCallback, fSendData.skipRefcon ) ;
416		}
417
418		super::Print( file ) ;
419	}
420
421	IOByteCount
422	SendNuDCL::Export (
423		IOVirtualAddress * 		where,
424		IOVirtualRange			bufferRanges[],
425		unsigned				bufferRangeCount ) const
426	{
427		IOByteCount size = NuDCL::Export( where, bufferRanges, bufferRangeCount ) ;
428
429		if ( where )
430		{
431			SendNuDCLExportData * exportedData = reinterpret_cast<SendNuDCLExportData *>( *where ) ;
432			*where += sizeof(SendNuDCLExportData);
433
434			exportedData->tagBits = fSendData.tagBits;
435			exportedData->syncBits = fSendData.syncBits;
436			exportedData->skipCallback = (mach_vm_address_t)fSendData.skipCallback;
437			exportedData->skipRefcon = (mach_vm_address_t)fSendData.skipRefcon;
438
439			if ( fSendData.skipBranch.dcl )
440				exportedData->skipBranchIndex = fSendData.skipBranch.dcl->GetExportIndex() ;
441			else
442				exportedData->skipBranchIndex = 0 ;
443
444			if ( fSendData.userHeader.ptr )
445				exportedData->userHeaderOffset = findOffsetInRanges( (IOVirtualAddress)fSendData.userHeader.ptr, bufferRanges, bufferRangeCount ) + 1 ;
446			else
447				exportedData->userHeaderOffset = 0;
448
449			if ( fSendData.userHeaderMask.ptr )
450				exportedData->userHeaderMaskOffset = findOffsetInRanges( (IOVirtualAddress)fSendData.userHeaderMask.ptr, bufferRanges, bufferRangeCount ) + 1;
451			else
452				exportedData->userHeaderMaskOffset = 0;
453
454#ifndef __LP64__
455			ROSETTA_ONLY(
456				{
457					exportedData->skipBranchIndex = CFSwapInt64( exportedData->skipBranchIndex ) ;
458					exportedData->skipCallback = (mach_vm_address_t)CFSwapInt64(exportedData->skipCallback ) ;
459					exportedData->skipRefcon = (mach_vm_address_t)CFSwapInt64( (UInt32)exportedData->skipRefcon ) ;
460					exportedData->userHeaderOffset = CFSwapInt64( exportedData->userHeaderOffset ) ;
461					exportedData->userHeaderMaskOffset = CFSwapInt64( exportedData->userHeaderMaskOffset ) ;
462				}
463			) ;
464#endif
465
466		}
467
468		return size + sizeof( SendNuDCLExportData );
469	}
470
471#pragma mark -
472
473	#undef super
474	#define super NuDCL
475
476	void
477	SkipCycleNuDCL::Print( FILE* file ) const
478	{
479		fprintf( file, "\tSKIP %p\n", this ) ;
480
481		super::Print( file ) ;
482	}
483
484} // namespace
485