1/*
2 * Copyright (c) 1998-2002 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License").  You may not use this file except in compliance with the
9 * License.  Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
11 *
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
17 * License for the specific language governing rights and limitations
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22
23// public
24#import <IOKit/firewire/IOFireWireLib.h>
25
26// private
27#include "IOFireWireLibVectorCommand.h"
28#include "IOFireWireLibCommand.h"
29#import "IOFireWireLibDevice.h"
30#import "IOFireWireLibPriv.h"
31
32namespace IOFireWireLib
33{
34
35	IOFireWireLibVectorCommandInterface VectorCommand::sInterface =
36	{
37		INTERFACEIMP_INTERFACE,
38		1, 1, // version/revision
39
40		&VectorCommand::SSubmit,
41		&VectorCommand::SSubmitWithRefconAndCallback,
42		&VectorCommand::SIsExecuting,
43		&VectorCommand::SSetCallback,
44		&VectorCommand::SSetRefCon,
45		&VectorCommand::SGetRefCon,
46		&VectorCommand::SSetFlags,
47		&VectorCommand::SGetFlags,
48		&VectorCommand::SEnsureCapacity,
49		&VectorCommand::SAddCommand,
50		&VectorCommand::SRemoveCommand,
51		&VectorCommand::SInsertCommandAtIndex,
52		&VectorCommand::SGetCommandAtIndex,
53		&VectorCommand::SGetIndexOfCommand,
54		&VectorCommand::SRemoveCommandAtIndex,
55		&VectorCommand::SRemoveAllCommands,
56		&VectorCommand::SGetCommandCount
57	};
58
59	CFArrayCallBacks VectorCommand::sArrayCallbacks =
60	{
61		0,										// version
62		&VectorCommand::SRetainCallback,		// retain
63		&VectorCommand::SReleaseCallback,		// release
64		NULL,									// copyDescription
65		NULL,									// equal - NULL means pointer equality will be used
66	};
67
68	// Alloc
69	//
70	//
71
72	IUnknownVTbl**	VectorCommand::Alloc(	Device& 						userclient,
73											IOFireWireLibCommandCallback	callback,
74											void*							inRefCon )
75	{
76		VectorCommand * me = new VectorCommand( userclient, callback, inRefCon );
77		if( !me )
78			return nil;
79
80		return reinterpret_cast<IUnknownVTbl**>(&me->GetInterface());
81	}
82
83	// VectorCommand
84	//
85	//
86
87	VectorCommand::VectorCommand( Device & userClient,
88					IOFireWireLibCommandCallback inCallback, void* inRefCon )
89	:	IOFireWireIUnknown( reinterpret_cast<const IUnknownVTbl &>( sInterface ) ),
90		mUserClient( userClient ),
91		mKernCommandRef( 0 ),
92		mRefCon( inRefCon ),
93		mCallback( inCallback ),
94		mFlags( 0 ),
95		mInflightCount( 0 ),
96		mSubmitBuffer( NULL ),
97		mSubmitBufferSize( 0 ),
98		mResultBuffer( NULL ),
99		mResultBufferSize( 0 )
100	{
101		mUserClient.AddRef();
102
103		mCommandArray = CFArrayCreateMutable(	kCFAllocatorDefault,
104												0, // unlimited capacity
105												&sArrayCallbacks );
106		if( mCommandArray == NULL )
107		{
108			throw kIOReturnNoMemory;
109		}
110
111		// output data
112		UserObjectHandle kernel_ref = 0;
113		size_t outputStructCnt = sizeof(kernel_ref);
114
115		// send it down
116		IOReturn status = IOConnectCallStructMethod(	mUserClient.GetUserClientConnection(),
117														kVectorCommandCreate,
118														NULL, 0,
119														&kernel_ref, &outputStructCnt );
120		if( status != kIOReturnSuccess )
121		{
122			throw status;
123		}
124
125		mKernCommandRef = kernel_ref;
126	}
127
128	// ~VectorCommand
129	//
130	//
131
132	VectorCommand::~VectorCommand()
133	{
134		if( mKernCommandRef )
135		{
136			IOReturn result = kIOReturnSuccess;
137
138			uint32_t outputCnt = 0;
139			const uint64_t inputs[1]={ (const uint64_t)mKernCommandRef };
140			result = IOConnectCallScalarMethod(	mUserClient.GetUserClientConnection(),
141												kReleaseUserObject,
142												inputs, 1,
143												NULL, &outputCnt);
144
145			DebugLogCond( result, "VectorCommand::~VectorCommand: command release returned 0x%08x\n", result );
146		}
147
148		// free any current storage
149		if( mSubmitBuffer != NULL )
150		{
151			vm_deallocate( mach_task_self(), (vm_address_t)mSubmitBuffer, mSubmitBufferSize );
152			mSubmitBuffer = NULL;
153			mSubmitBufferSize = 0;
154		}
155
156		if( mResultBuffer != NULL )
157		{
158			vm_deallocate( mach_task_self(), (vm_address_t)mResultBuffer, mResultBufferSize );
159			mResultBuffer = NULL;
160			mResultBufferSize = 0;
161		}
162
163		if( mCommandArray )
164		{
165			CFRelease( mCommandArray );
166			mCommandArray = NULL;
167		}
168
169		mUserClient.Release();
170	}
171
172	// QueryInterface
173	//
174	//
175
176	HRESULT
177	VectorCommand::QueryInterface( REFIID iid, LPVOID* ppv )
178	{
179		HRESULT		result = S_OK;
180		*ppv = nil;
181
182		CFUUIDRef	interfaceID	= CFUUIDCreateFromUUIDBytes( kCFAllocatorDefault, iid );
183
184		if( CFEqual(interfaceID, IUnknownUUID) || CFEqual(interfaceID, kIOFireWireVectorCommandInterfaceID) )
185		{
186			*ppv = &GetInterface();
187			AddRef();
188		}
189		else
190		{
191			*ppv = nil;
192			result = E_NOINTERFACE;
193		}
194
195		CFRelease( interfaceID );
196		return result;
197	}
198
199	// EnsureCapacity
200	//
201	//
202
203	IOReturn VectorCommand::SEnsureCapacity( IOFireWireLibVectorCommandRef self, UInt32 capacity )
204	{
205		return IOFireWireIUnknown::InterfaceMap<VectorCommand>::GetThis(self)->EnsureCapacity( capacity );
206	}
207
208	IOReturn VectorCommand::EnsureCapacity( UInt32 capacity )
209	{
210		IOReturn status = kIOReturnSuccess;
211
212		mach_vm_size_t required_submit_size = capacity * sizeof(CommandSubmitParams);
213		mach_vm_size_t required_result_size = capacity * sizeof(CommandSubmitResult);
214
215		// do we have enough space?
216		if( (mSubmitBufferSize < required_submit_size) ||
217			(mResultBufferSize < required_result_size) )
218		{
219			//
220			// allocate new buffers
221			//
222
223			// allocate the submit buffer
224			vm_address_t submit_buffer = NULL;
225			status = vm_allocate( mach_task_self(), &submit_buffer, required_submit_size, true /*anywhere*/ ) ;
226			if ( !submit_buffer && (status == kIOReturnSuccess) )
227			{
228				status = kIOReturnNoMemory;
229			}
230
231			// allocate the result buffer
232			vm_address_t result_buffer = NULL;
233			status = vm_allocate( mach_task_self(), &result_buffer, required_result_size, true /*anywhere*/ ) ;
234			if ( !result_buffer && (status == kIOReturnSuccess) )
235			{
236				status = kIOReturnNoMemory;
237			}
238
239			//
240			// send the buffers to the kernel
241			//
242
243			if( status == kIOReturnSuccess )
244			{
245				// send the buffer to the kernel
246
247				// inputs
248				const uint64_t inputs[4] = { (const uint64_t)submit_buffer,
249											 (const uint64_t)required_submit_size,
250											 (const uint64_t)result_buffer,
251											 (const uint64_t)required_result_size };
252
253				// outputs
254				uint32_t output_count = 0;
255
256				status = IOConnectCallScalarMethod(	mUserClient.GetUserClientConnection(),
257													mUserClient.MakeSelectorWithObject( kVectorCommandSetBuffers, mKernCommandRef ),
258													inputs, 4,
259													NULL, &output_count);
260			}
261
262			//
263			// free any prior storage
264			//
265
266			if( status == kIOReturnSuccess )
267			{
268				// this must be done after calling the kernel so the kernel can release it's wiring, else we'd hang
269				// we also only do this if the above kernel call was successful as we may hang if the memory is still wired
270
271				if( mSubmitBuffer != NULL )
272				{
273					vm_deallocate( mach_task_self(), (vm_address_t)mSubmitBuffer, mSubmitBufferSize );
274					mSubmitBuffer = NULL;
275					mSubmitBufferSize = 0;
276				}
277
278				if( mResultBuffer != NULL )
279				{
280					vm_deallocate( mach_task_self(), (vm_address_t)mResultBuffer, mResultBufferSize );
281					mResultBuffer = NULL;
282					mResultBufferSize = 0;
283				}
284			}
285
286			//
287			// remember our storage
288			//
289
290			if( status == kIOReturnSuccess )
291			{
292				mSubmitBuffer = (CommandSubmitParams*)submit_buffer;
293				mSubmitBufferSize = required_submit_size;
294
295				mResultBuffer = (CommandSubmitResult*)result_buffer;
296				mResultBufferSize = required_result_size;
297			}
298		}
299
300		return status;
301	}
302
303	// Submit
304	//
305	//
306
307	IOReturn
308	VectorCommand::SSubmit( IOFireWireLibVectorCommandRef self )
309	{
310		return IOFireWireIUnknown::InterfaceMap<VectorCommand>::GetThis(self)->Submit();
311	}
312
313	IOReturn
314	VectorCommand::Submit()
315	{
316		IOReturn status = kIOReturnSuccess;
317
318		if( mInflightCount != 0 )
319		{
320			status = kIOReturnNotReady;
321		}
322
323		CFIndex count = 0;
324		if( status == kIOReturnSuccess )
325		{
326			count = CFArrayGetCount( mCommandArray );
327			status = EnsureCapacity( count );
328		}
329
330		if( status == kIOReturnSuccess )
331		{
332			// reset vector status
333			mStatus = kIOReturnSuccess;
334
335			for( CFIndex index = 0; (status == kIOReturnSuccess) && (index < count); index++ )
336			{
337				IOFireWireLibCommandRef command = (IOFireWireLibCommandRef)CFArrayGetValueAtIndex( mCommandArray, index );
338
339				Cmd * cmd = IOFireWireIUnknown::InterfaceMap<Cmd>::GetThis(command);
340
341				status = cmd->PrepareForVectorSubmit( &mSubmitBuffer[index] );
342			}
343		}
344
345		if( status == kIOReturnSuccess )
346		{
347			// send the requests to the kernel
348
349			// async ref
350			uint64_t async_ref[kOSAsyncRef64Count];
351			async_ref[kIOAsyncCalloutFuncIndex] = (uint64_t) 0;
352			async_ref[kIOAsyncCalloutRefconIndex] = (unsigned long) 0;
353
354			// inputs
355			const uint64_t inputs[2] = { (const uint64_t)&SVectorCompletionHandler,
356										 (const uint64_t)this };
357			// outputs
358			uint32_t output_count = 0;
359
360			status = IOConnectCallAsyncScalarMethod( mUserClient.GetUserClientConnection(),
361												  mUserClient.MakeSelectorWithObject( kVectorCommandSubmit, mKernCommandRef ),
362												  mUserClient.GetAsyncPort(),
363												  async_ref, kOSAsyncRef64Count,
364												  inputs, 2,
365												  NULL, &output_count);
366
367			mInflightCount = count;
368
369//			printf( "VectorCommand::Submit - IOConnectCallAsyncStructMethod status = 0x%08lx\n", status );
370		}
371
372		if( status == kIOReturnSuccess )
373		{
374			for( CFIndex index = 0; (status == kIOReturnSuccess) && (index < count); index++ )
375			{
376				IOFireWireLibCommandRef command = (IOFireWireLibCommandRef)CFArrayGetValueAtIndex( mCommandArray, index );
377
378				Cmd * cmd = IOFireWireIUnknown::InterfaceMap<Cmd>::GetThis(command);
379
380				cmd->VectorIsExecuting();
381			}
382		}
383
384#if 0
385		if( status == kIOReturnSuccess )
386		{
387			// reset vector status
388			mStatus = kIOReturnSuccess;
389
390			for( CFIndex index = 0; index < count; index++ )
391			{
392				IOFireWireLibCommandRef command = (IOFireWireLibCommandRef)CFArrayGetValueAtIndex( mCommandArray, index );
393				mInflightCount++;
394				(*command)->Submit( command );
395				// our commands seem to call the completion handler on error
396				// so pretend all was success
397			}
398		}
399#endif
400
401//		printf( "VectorCommand::Submit - status = 0x%08lx\n", status );
402
403		return status;
404	}
405
406	// SubmitWithRefconAndCallback
407	//
408	//
409
410	IOReturn VectorCommand::SSubmitWithRefconAndCallback( IOFireWireLibVectorCommandRef self, void* refCon, IOFireWireLibCommandCallback inCallback )
411	{
412		(*self)->SetRefCon( self, refCon );
413		(*self)->SetCallback( self, inCallback );
414		return (*self)->Submit( self );
415	}
416
417	// IsExecuting
418	//
419	//
420
421	Boolean VectorCommand::SIsExecuting( IOFireWireLibVectorCommandRef self )
422	{
423		VectorCommand * me = IOFireWireIUnknown::InterfaceMap<VectorCommand>::GetThis(self);
424		return (me->mInflightCount != 0);
425	}
426
427	// VectorCompletionHandler
428	//
429	//
430
431	void
432	VectorCommand::SVectorCompletionHandler(
433		void*				refcon,
434		IOReturn			result,
435		void*				quads[],
436		UInt32				numQuads )
437	{
438		VectorCommand *	me = (VectorCommand*)refcon;
439		me->VectorCompletionHandler( result, quads, numQuads );
440	}
441
442	void
443	VectorCommand::VectorCompletionHandler(
444		IOReturn			result,
445		void*				quads[],
446		UInt32				numQuads )
447	{
448		//printf( "VectorCommand::VectorCompletionHandler - status = 0x%08lx\n", result );
449
450		CFIndex count = CFArrayGetCount( mCommandArray );
451
452		for( CFIndex index = 0; (index < count); index++ )
453		{
454			Cmd * cmd = (Cmd*)mResultBuffer[index].refCon;
455
456			IOReturn status = mResultBuffer[index].result;
457
458			// pack 'em up like the kernel would
459			void * args[3];
460			args[0] = (void*)mResultBuffer[index].bytesTransferred;
461			args[1] = (void*)mResultBuffer[index].ackCode;
462			args[2] = (void*)mResultBuffer[index].responseCode;
463
464			// call the completion routine
465			cmd->CommandCompletionHandler( cmd, status, args, 3 );
466
467			if( mInflightCount > 0 )
468			{
469				mInflightCount--;
470			}
471		}
472
473		(*mCallback)( mRefCon, mStatus );
474	}
475
476	// SetCallback
477	//
478	//
479
480	void VectorCommand::SSetCallback( IOFireWireLibVectorCommandRef self, IOFireWireLibCommandCallback inCallback )
481	{
482		VectorCommand * me = IOFireWireIUnknown::InterfaceMap<VectorCommand>::GetThis(self);
483		me->mCallback = inCallback;
484	}
485
486	// SetRefCon
487	//
488	//
489
490	void VectorCommand::SSetRefCon( IOFireWireLibVectorCommandRef self, void* refCon )
491	{
492		VectorCommand * me = IOFireWireIUnknown::InterfaceMap<VectorCommand>::GetThis(self);
493		me->mRefCon = refCon;
494	}
495
496	// GetRefCon
497	//
498	//
499
500	void * VectorCommand::SGetRefCon( IOFireWireLibVectorCommandRef self )
501	{
502		VectorCommand * me = IOFireWireIUnknown::InterfaceMap<VectorCommand>::GetThis(self);
503		return me->mRefCon;
504	}
505
506	// SetFlags
507	//
508	//
509
510	void VectorCommand::SSetFlags( IOFireWireLibVectorCommandRef self, UInt32 inFlags )
511	{
512		VectorCommand * me = IOFireWireIUnknown::InterfaceMap<VectorCommand>::GetThis(self);
513		me->mFlags = inFlags;
514	}
515
516	// GetFlags
517	//
518	//
519
520	UInt32 VectorCommand::SGetFlags( IOFireWireLibVectorCommandRef self )
521	{
522		VectorCommand * me = IOFireWireIUnknown::InterfaceMap<VectorCommand>::GetThis(self);
523		return me->mFlags;
524	}
525
526	// AddCommand
527	//
528	//
529
530	void VectorCommand::SAddCommand( IOFireWireLibVectorCommandRef self, IOFireWireLibCommandRef command )
531	{
532		VectorCommand * me = IOFireWireIUnknown::InterfaceMap<VectorCommand>::GetThis(self);
533
534		CFArrayAppendValue( me->mCommandArray, command );
535	}
536
537	// RemoveCommand
538	//
539	//
540
541	void VectorCommand::SRemoveCommand( IOFireWireLibVectorCommandRef self, IOFireWireLibCommandRef command )
542	{
543		VectorCommand * me = IOFireWireIUnknown::InterfaceMap<VectorCommand>::GetThis(self);
544
545		CFMutableArrayRef array = me->mCommandArray;
546
547		CFRange search_range = CFRangeMake( 0, CFArrayGetCount( array ) );
548		CFIndex index = kCFNotFound;
549
550		// search for all instances of a given command
551		while( (index = CFArrayGetFirstIndexOfValue( array, search_range, command)) != kCFNotFound )
552		{
553			// remove the index
554			CFArrayRemoveValueAtIndex( array, index );
555
556			// recalc the search range
557			search_range = CFRangeMake( 0, CFArrayGetCount( array ) );
558		}
559	}
560
561	// InsertCommandAtIndex
562	//
563	//
564
565	void VectorCommand::SInsertCommandAtIndex( IOFireWireLibVectorCommandRef self, IOFireWireLibCommandRef command, UInt32 index )
566	{
567		VectorCommand * me = IOFireWireIUnknown::InterfaceMap<VectorCommand>::GetThis(self);
568
569		CFArrayInsertValueAtIndex( me->mCommandArray, index, command );
570	}
571
572	// CommandAtIndex
573	//
574	//
575
576	IOFireWireLibCommandRef VectorCommand::SGetCommandAtIndex( IOFireWireLibVectorCommandRef self, UInt32 index )
577	{
578		VectorCommand * me = IOFireWireIUnknown::InterfaceMap<VectorCommand>::GetThis(self);
579
580		return (IOFireWireLibCommandRef)CFArrayGetValueAtIndex( me->mCommandArray, index );
581	}
582
583	// GetIndexOfCommand
584	//
585	//
586
587	UInt32 VectorCommand::SGetIndexOfCommand( IOFireWireLibVectorCommandRef self, IOFireWireLibCommandRef command )
588	{
589		VectorCommand * me = IOFireWireIUnknown::InterfaceMap<VectorCommand>::GetThis(self);
590
591		CFRange search_range = CFRangeMake( 0, CFArrayGetCount( me->mCommandArray ) );
592
593		return CFArrayGetFirstIndexOfValue( me->mCommandArray, search_range, command );
594	}
595
596	// RemoveCommandAtIndex
597	//
598	//
599
600	void VectorCommand::SRemoveCommandAtIndex( IOFireWireLibVectorCommandRef self, UInt32 index )
601	{
602		VectorCommand * me = IOFireWireIUnknown::InterfaceMap<VectorCommand>::GetThis(self);
603
604		CFArrayRemoveValueAtIndex( me->mCommandArray, index );
605	}
606
607	// RemoveAllCommands
608	//
609	//
610
611	void VectorCommand::SRemoveAllCommands( IOFireWireLibVectorCommandRef self )
612	{
613		VectorCommand * me = IOFireWireIUnknown::InterfaceMap<VectorCommand>::GetThis(self);
614
615		CFArrayRemoveAllValues( me->mCommandArray );
616	}
617
618	// GetCommandCount
619	//
620	//
621
622	UInt32 VectorCommand::SGetCommandCount( IOFireWireLibVectorCommandRef self )
623	{
624		VectorCommand * me = IOFireWireIUnknown::InterfaceMap<VectorCommand>::GetThis(self);
625
626		return CFArrayGetCount( me->mCommandArray );
627	}
628
629	// RetainCallback
630	//
631	//
632
633	const void * VectorCommand::SRetainCallback( CFAllocatorRef allocator, const void * value )
634	{
635		IUnknownVTbl** iUnknown = (IUnknownVTbl**)value;
636		(*iUnknown)->AddRef( iUnknown );
637
638		return value;
639	}
640
641	// ReleaseCallback
642	//
643	//
644
645	void VectorCommand::SReleaseCallback( CFAllocatorRef allocator, const void * value )
646	{
647		IUnknownVTbl** iUnknown = (IUnknownVTbl**)value;
648		(*iUnknown)->Release( iUnknown );
649	}
650
651}