1/*
2 *  IOFireWireLibIsochChannel.cpp
3 *  IOFireWireFamily
4 *
5 *  Created by NWG on Mon Mar 12 2001.
6 *  Copyright (c) 2001 Apple Computer, Inc. All rights reserved.
7 *
8 */
9
10#import "IOFireWireLibIsochChannel.h"
11#import "IOFireWireLibIsochPort.h"
12#import "IOFireWireLibDevice.h"
13#import "IOFireWireLibPriv.h"
14
15#import <IOKit/iokitmig.h>
16
17namespace IOFireWireLib {
18
19	// ============================================================
20	// IsochChannel
21	// ============================================================
22
23	IsochChannel::IsochChannel( const IUnknownVTbl & interface, Device& userclient, bool inDoIRM, IOByteCount inPacketSize, IOFWSpeed inPrefSpeed)
24	: IOFireWireIUnknown( interface ),
25	  mUserClient( userclient ),
26	  mKernChannelRef(0),
27	  mNotifyIsOn(false),
28	  mForceStopHandler(0),
29	  mUserRefCon(0),
30	  mTalker(0),
31	  mListeners( ::CFArrayCreateMutable( kCFAllocatorDefault, 0, NULL ) ),
32	  mRefInterface( reinterpret_cast<ChannelRef>( & GetInterface() ) ),
33	  mPrefSpeed( inPrefSpeed )
34	{
35		if (!mListeners)
36			throw kIOReturnNoMemory ;
37
38		mUserClient.AddRef() ;
39
40		uint32_t outputCnt = 1;
41		uint64_t outputVal = 0;
42		const uint64_t inputs[3] = {inDoIRM, inPacketSize, inPrefSpeed};
43		IOReturn err = IOConnectCallScalarMethod(mUserClient.GetUserClientConnection(),
44												 kIsochChannel_Allocate,
45												 inputs,3,
46												 &outputVal,&outputCnt);
47		mKernChannelRef = (UserObjectHandle) outputVal;
48
49		if(err)
50		{
51			throw err ;
52		}
53	}
54
55
56	IsochChannel::~IsochChannel()
57	{
58		if(NotificationIsOn())
59			TurnOffNotification() ;
60
61		if (mKernChannelRef)
62		{
63			uint32_t outputCnt = 0;
64			const uint64_t inputs[1]={(const uint64_t)mKernChannelRef};
65
66			IOConnectCallScalarMethod(mUserClient.GetUserClientConnection(),
67									  kReleaseUserObject,
68									  inputs,1,
69									  NULL,&outputCnt);
70
71			mKernChannelRef = 0 ;
72		}
73
74		if ( mTalker )
75			(**mTalker).Release( mTalker ) ;
76
77		if (mListeners)
78		{
79			for( CFIndex index = 0, count = ::CFArrayGetCount( mListeners ); index < count; ++index )
80			{
81				IOFireWireLibIsochPortRef	port = (IOFireWireLibIsochPortRef) CFArrayGetValueAtIndex(mListeners, index) ;
82				(**port).Release( port ) ;
83			}
84			CFRelease(mListeners) ;
85		}
86
87		mUserClient.Release() ;
88	}
89
90//	void
91//	IsochChannel::ForceStop( ChannelRef refCon, IOReturn result, void** args, int numArgs )
92//	{
93//		IsochChannel*	me = (IsochChannel*) args[0] ;
94//
95//		if (me->mForceStopHandler)
96//			(me->mForceStopHandler)(me->mRefInterface, (UInt32)args[1]) ;	// reason
97//
98//	}
99
100	IOReturn
101	IsochChannel::SetTalker(
102		IOFireWireLibIsochPortRef	 		inTalker )
103	{
104		(**inTalker).AddRef( inTalker ) ;
105		mTalker	= inTalker ;
106
107		IsochPort * port = dynamic_cast<LocalIsochPort*>( IOFireWireIUnknown::InterfaceMap<IsochPort>::GetThis( inTalker ) ) ;
108
109		if ( port )
110		{
111			uint32_t outputCnt = 0;
112			const uint64_t inputs[2] = {(const uint64_t)port->mKernPortRef, (const uint64_t)mKernChannelRef};
113			IOConnectCallScalarMethod(mUserClient.GetUserClientConnection(),
114									  kLocalIsochPort_SetChannel,
115									  inputs,2,
116									  NULL,&outputCnt);
117		}
118
119		return kIOReturnSuccess ;
120	}
121
122	IOReturn
123	IsochChannel::AddListener(
124		IOFireWireLibIsochPortRef	inListener )
125	{
126		CFArrayAppendValue(mListeners, inListener) ;
127		(**inListener).AddRef( inListener ) ;
128
129		IsochPort * port = dynamic_cast<LocalIsochPort*>( IOFireWireIUnknown::InterfaceMap<IsochPort>::GetThis( inListener ) ) ;
130
131		if ( port )
132		{
133			uint32_t outputCnt = 0;
134			const uint64_t inputs[2] = {(const uint64_t)port->mKernPortRef, (const uint64_t)mKernChannelRef};
135			IOConnectCallScalarMethod(mUserClient.GetUserClientConnection(),
136									  kLocalIsochPort_SetChannel,
137									  inputs,2,
138									  NULL,&outputCnt);
139		}
140
141		return kIOReturnSuccess ;
142	}
143
144	IOReturn
145	IsochChannel::AllocateChannel()
146	{
147		DebugLog( "+ IsochChannel::AllocateChannel\n" ) ;
148
149		IOReturn	result		= kIOReturnSuccess ;
150
151		IOFWSpeed	portSpeed ;
152		UInt64		portChans ;
153
154		// Get best speed, minimum of requested speed and paths from talker to each listener
155		mSpeed = mPrefSpeed ;
156
157		// reduce speed to minimum of so far and what all ports can do,
158		// and find valid channels
159		UInt64	allowedChans = ~(UInt64)0 ;
160
161		if(mTalker) {
162			(**mTalker).GetSupported( mTalker, & portSpeed, & portChans);
163			if(portSpeed < mSpeed)
164				mSpeed = portSpeed;
165			allowedChans &= portChans;
166		}
167
168		UInt32						listenCount = CFArrayGetCount(mListeners) ;
169		IOFireWireLibIsochPortRef	listen ;
170		for (UInt32 listenIndex=0; listenIndex < listenCount; ++listenIndex)
171		{
172			listen = (IOFireWireLibIsochPortRef)CFArrayGetValueAtIndex( mListeners, listenIndex) ;
173			(**listen).GetSupported( listen, & portSpeed, & portChans );
174
175			if(portSpeed < mSpeed)
176				mSpeed = portSpeed;
177			allowedChans &= portChans;
178		}
179
180		// call the kernel middle bits
181		uint32_t outputCnt = 2;
182		uint64_t outputVal[2];
183		const uint64_t inputs[4] = {(const uint64_t)mKernChannelRef, (const uint64_t)mSpeed, (allowedChans >> 32),(0xFFFFFFFF & allowedChans)};
184		result = IOConnectCallScalarMethod(mUserClient.GetUserClientConnection(),
185										   kIsochChannel_UserAllocateChannelBegin,
186												 inputs,4,
187												 outputVal,&outputCnt);
188		mSpeed = (IOFWSpeed) (outputVal[0] & 0xFFFFFFFF);
189		mChannel =  outputVal[1] & 0xFFFFFFFF;
190
191		if (kIOReturnSuccess == result)
192		{
193			// complete in user space
194			UInt32 listenIndex = 0 ;
195			while (kIOReturnSuccess == result && listenIndex < listenCount)
196			{
197				IOFireWireLibIsochPortRef port = (IOFireWireLibIsochPortRef)CFArrayGetValueAtIndex(mListeners, listenIndex) ;
198				result = (**port).AllocatePort( port, mSpeed, mChannel ) ;
199				++listenIndex ;
200			}
201
202			if (kIOReturnSuccess == result && mTalker)
203				result = (**mTalker).AllocatePort( mTalker, mSpeed, mChannel) ;
204		}
205
206		return result ;
207	}
208
209	IOReturn
210	IsochChannel::ReleaseChannel()
211	{
212		DebugLog("+ IsochChannel::ReleaseChannel\n") ;
213
214		IOReturn 	result = kIOReturnSuccess ;
215
216		if(mTalker) {
217			result = (**mTalker).ReleasePort( mTalker );
218		}
219
220		DebugLogCond( result, "IsochChannel::ReleaseChannel: error 0x%08x calling ReleasePort() on talker\n", result) ;
221
222		UInt32							listenCount	= CFArrayGetCount(mListeners) ;
223		IOFireWireLibIsochPortRef		listen ;
224
225		UInt32	index=0 ;
226		while (kIOReturnSuccess == result && index < listenCount)
227		{
228			listen = (IOFireWireLibIsochPortRef)CFArrayGetValueAtIndex(mListeners, index) ;
229			result = (**listen).ReleasePort( listen );
230
231			DebugLogCond(result, "IsochChannel::ReleaseChannel: error %p calling ReleasePort() on listener\n", listen) ;
232
233			++index ;
234		}
235
236		uint32_t outputCnt = 0;
237		result = IOConnectCallScalarMethod(mUserClient.GetUserClientConnection(),
238										   mUserClient.MakeSelectorWithObject( kIsochChannel_UserReleaseChannelComplete_d, mKernChannelRef ),
239										   NULL,0,
240										   NULL,&outputCnt);
241
242		return result ;
243	}
244
245	IOReturn
246	IsochChannel::Start()
247	{
248		DebugLog("+ IsochChannel::Start\n") ;
249
250		// Start all listeners, then start the talker
251		UInt32 						listenCount = CFArrayGetCount( mListeners ) ;
252		IOFireWireLibIsochPortRef	listen ;
253		UInt32						listenIndex = 0 ;
254		IOReturn					error = kIOReturnSuccess ;
255
256		while ( !error && listenIndex < listenCount )
257		{
258			listen = (IOFireWireLibIsochPortRef) CFArrayGetValueAtIndex( mListeners, listenIndex) ;
259			error = (**listen).Start( listen ) ;
260
261			DebugLogCond( error, "IsochChannel::Start: error 0x%x starting channel\n", error ) ;
262
263			++listenIndex ;
264		}
265
266		if ( mTalker && !error )
267			error = (**mTalker).Start( mTalker ) ;
268
269		if ( error )
270			Stop() ;
271
272		DebugLog("-IsochChannel::Start error=%x\n", error) ;
273
274		return error ;
275	}
276
277	IOReturn
278	IsochChannel::Stop()
279	{
280		DebugLog( "+ IsochChannel::Stop\n" ) ;
281
282		if (mTalker)
283			(**mTalker).Stop( mTalker ) ;
284
285		UInt32 						listenCount = CFArrayGetCount( mListeners ) ;
286		IOFireWireLibIsochPortRef 	listen ;
287		for (UInt32 listenIndex=0; listenIndex < listenCount; ++listenIndex)
288		{
289			listen = (IOFireWireLibIsochPortRef)CFArrayGetValueAtIndex( mListeners, listenIndex ) ;
290			(**listen).Stop( listen ) ;
291		}
292
293		return kIOReturnSuccess;
294	}
295
296	IOFireWireIsochChannelForceStopHandler
297	IsochChannel::SetChannelForceStopHandler (
298		IOFireWireIsochChannelForceStopHandler stopProc,
299		IOFireWireLibIsochChannelRef interface )
300	{
301		DebugLog( "+IsochChannel::SetChannelForceStopHandler this=%p, proc=%p\n", this, stopProc ) ;
302
303		IOFireWireIsochChannelForceStopHandler oldHandler = mForceStopHandler ;
304		mForceStopHandler = stopProc ;
305
306		io_connect_t connection = mUserClient.GetUserClientConnection() ;
307
308		if ( mNotifyIsOn && connection )
309		{
310			uint64_t refrncData[kOSAsyncRef64Count];
311			refrncData[kIOAsyncCalloutFuncIndex] = (uint64_t) mForceStopHandler;
312			refrncData[kIOAsyncCalloutRefconIndex] = (unsigned long)interface;
313			uint32_t outputCnt = 0;
314			const uint64_t inputs[1]={(const uint64_t)mKernChannelRef};
315
316			IOReturn error = IOConnectCallAsyncScalarMethod(connection,
317															kSetAsyncRef_IsochChannelForceStop,
318															mUserClient.GetAsyncPort(),
319															refrncData,kOSAsyncRef64Count,
320															inputs,1,
321															NULL,&outputCnt);
322
323			#pragma unused( error )
324			DebugLogCond( error, "Error setting isoch channel force stop handler\n") ;
325		}
326
327		return oldHandler ;
328	}
329
330	void
331	IsochChannel::SetRefCon(
332		void*			stopProcRefCon)
333	{
334		mUserRefCon = stopProcRefCon ;
335	}
336
337	void*
338	IsochChannel::GetRefCon()
339	{
340		return mUserRefCon ;
341	}
342
343	Boolean
344	IsochChannel::NotificationIsOn()
345	{
346		return mNotifyIsOn ;
347	}
348
349	Boolean
350	IsochChannel::TurnOnNotification( IOFireWireLibIsochChannelRef interface )
351	{
352		// if notification is already on, skip out.
353		if (mNotifyIsOn)
354		{
355			return true ;
356		}
357
358		io_connect_t			connection			= mUserClient.GetUserClientConnection() ;
359
360		if (!connection)
361		{
362			DebugLog("IsochChannel::TurnOnNotification: user client not open!\n") ;
363			return false ;
364		}
365
366		IOReturn error = kIOReturnSuccess ;
367		{
368			uint64_t refrncData[kOSAsyncRef64Count];
369			refrncData[kIOAsyncCalloutFuncIndex] = (uint64_t) mForceStopHandler;
370			refrncData[kIOAsyncCalloutRefconIndex] = (unsigned long)interface;
371			uint32_t outputCnt = 0;
372			const uint64_t inputs[1]={(const uint64_t)mKernChannelRef};
373
374			error = IOConnectCallAsyncScalarMethod(connection,
375												   kSetAsyncRef_IsochChannelForceStop,
376												   mUserClient.GetAsyncPort(),
377												   refrncData,kOSAsyncRef64Count,
378												   inputs,1,
379												   NULL,&outputCnt);
380		}
381
382		{
383			unsigned count = ::CFArrayGetCount( mListeners ) ;
384			unsigned index = 0 ;
385			while( index < count && !error )
386			{
387				// have kernel channel force stop proc call user dcl program force stop proc
388
389				LocalIsochPort * port = dynamic_cast<LocalIsochPort*>(IOFireWireIUnknown::InterfaceMap<IsochPort>::GetThis( (IsochPort*) ::CFArrayGetValueAtIndex( mListeners, index ) ) ) ;
390				if ( port )
391				{
392
393					uint32_t outputCnt = 0;
394					const uint64_t inputs[2] = {(const uint64_t)port->mKernPortRef, (const uint64_t)mKernChannelRef};
395					error = IOConnectCallScalarMethod(connection,
396													  kLocalIsochPort_SetChannel,
397													  inputs,2,
398													  NULL,&outputCnt);
399				}
400
401				++index ;
402			}
403		}
404
405		if ( !error && mTalker )
406		{
407			LocalIsochPort * port = dynamic_cast<LocalIsochPort*>( IOFireWireIUnknown::InterfaceMap<IsochPort>::GetThis( mTalker ) ) ;
408			if ( port )
409			{
410				uint32_t outputCnt = 0;
411				const uint64_t inputs[2] = {(const uint64_t)port->mKernPortRef, (const uint64_t)mKernChannelRef};
412				error = IOConnectCallScalarMethod(connection,
413												  kLocalIsochPort_SetChannel,
414												  inputs,2,
415												  NULL,&outputCnt);
416			}
417		}
418
419		if ( !error )
420		{
421			mNotifyIsOn = true ;
422		}
423
424		return ( error == kIOReturnSuccess ) ;
425	}
426
427	void
428	IsochChannel::TurnOffNotification()
429	{
430		io_connect_t			connection			= mUserClient.GetUserClientConnection() ;
431
432		// if notification isn't on, skip out.
433		if ( !mNotifyIsOn || !connection )
434		{
435			return ;
436		}
437
438		uint64_t refrncData[kOSAsyncRef64Count];
439		refrncData[kIOAsyncCalloutFuncIndex] = (uint64_t) 0;
440		refrncData[kIOAsyncCalloutRefconIndex] = (unsigned long) 0;
441		const uint64_t inputs[3] = {(const uint64_t)mKernChannelRef,0,(const uint64_t)this};
442		uint32_t outputCnt = 0;
443		IOConnectCallAsyncScalarMethod(connection,
444									   kSetAsyncRef_IsochChannelForceStop,
445									   mUserClient.GetAsyncPort(),
446									   refrncData,kOSAsyncRef64Count,
447									   inputs,3,
448									   NULL,&outputCnt);
449		mNotifyIsOn = false ;
450	}
451
452	void
453	IsochChannel::ClientCommandIsComplete(
454		FWClientCommandID 				commandID,
455		IOReturn 						status)
456	{
457	}
458
459#pragma mark -
460	IsochChannelCOM::Interface IsochChannelCOM::sInterface =
461	{
462		INTERFACEIMP_INTERFACE,
463		1, 0,
464
465		& IsochChannelCOM::SSetTalker,
466		& IsochChannelCOM::SAddListener,
467		& IsochChannelCOM::SAllocateChannel,
468		& IsochChannelCOM::SReleaseChannel,
469		& IsochChannelCOM::SStart,
470		& IsochChannelCOM::SStop,
471
472		& IsochChannelCOM::SSetChannelForceStopHandler,
473		& IsochChannelCOM::SSetRefCon,
474		& IsochChannelCOM::SGetRefCon,
475		& IsochChannelCOM::SNotificationIsOn,
476		& IsochChannelCOM::STurnOnNotification,
477		& IsochChannelCOM::STurnOffNotification,
478		& IsochChannelCOM::SClientCommandIsComplete
479	} ;
480
481	//
482	// --- ctor/dtor -----------------------
483	//
484
485	IsochChannelCOM::IsochChannelCOM( Device& userclient, bool inDoIRM, IOByteCount inPacketSize, IOFWSpeed inPrefSpeed )
486	: IsochChannel( reinterpret_cast<const IUnknownVTbl &>( sInterface ), userclient, inDoIRM, inPacketSize, inPrefSpeed )
487	{
488	}
489
490	IsochChannelCOM::~IsochChannelCOM()
491	{
492	}
493
494	//
495	// --- IUNKNOWN support ----------------
496	//
497
498	IUnknownVTbl**
499	IsochChannelCOM::Alloc( Device& userclient, Boolean inDoIRM, IOByteCount inPacketSize, IOFWSpeed inPrefSpeed )
500	{
501		IsochChannelCOM*	me = nil ;
502
503		try {
504			me = new IsochChannelCOM( userclient, inDoIRM, inPacketSize, inPrefSpeed ) ;
505		} catch(...) {
506		}
507
508		return ( nil == me ) ? nil : reinterpret_cast<IUnknownVTbl**>(& me->GetInterface()) ;
509	}
510
511	HRESULT
512	IsochChannelCOM::QueryInterface(REFIID iid, void ** ppv )
513	{
514		HRESULT		result = S_OK ;
515		*ppv = nil ;
516
517		CFUUIDRef	interfaceID	= CFUUIDCreateFromUUIDBytes(kCFAllocatorDefault, iid) ;
518
519		if ( CFEqual(interfaceID, IUnknownUUID) ||  CFEqual(interfaceID, kIOFireWireIsochChannelInterfaceID) )
520		{
521			*ppv = & GetInterface() ;
522			AddRef() ;
523		}
524		else
525		{
526			*ppv = nil ;
527			result = E_NOINTERFACE ;
528		}
529
530		CFRelease(interfaceID) ;
531		return result ;
532	}
533
534	//
535	// --- static methods ------------------
536	//
537
538	IOReturn
539	IsochChannelCOM::SSetTalker( ChannelRef self,
540		IOFireWireLibIsochPortRef 		inTalker)
541	{
542		return IOFireWireIUnknown::InterfaceMap<IsochChannelCOM>::GetThis(self)->SetTalker(inTalker) ;
543	}
544
545	IOReturn
546	IsochChannelCOM::SAddListener(
547		ChannelRef 	self,
548		IOFireWireLibIsochPortRef 		inListener)
549	{
550		return IOFireWireIUnknown::InterfaceMap<IsochChannelCOM>::GetThis(self)->AddListener(inListener) ;
551	}
552
553	IOReturn
554	IsochChannelCOM::SAllocateChannel(
555		ChannelRef 	self)
556	{
557		return IOFireWireIUnknown::InterfaceMap<IsochChannelCOM>::GetThis(self)->AllocateChannel() ;
558	}
559
560	IOReturn
561	IsochChannelCOM::SReleaseChannel(
562		ChannelRef 	self)
563	{
564		return IOFireWireIUnknown::InterfaceMap<IsochChannelCOM>::GetThis(self)->ReleaseChannel() ;
565	}
566
567	IOReturn
568	IsochChannelCOM::SStart(
569		ChannelRef 	self)
570	{
571		return IOFireWireIUnknown::InterfaceMap<IsochChannelCOM>::GetThis(self)->Start() ;
572	}
573
574	IOReturn
575	IsochChannelCOM::SStop(
576		ChannelRef 	self)
577	{
578		return IOFireWireIUnknown::InterfaceMap<IsochChannelCOM>::GetThis(self)->Stop() ;
579	}
580
581	IOFireWireIsochChannelForceStopHandler
582	IsochChannelCOM::SSetChannelForceStopHandler( ChannelRef self, ForceStopHandler stopProc )
583	{
584		return IOFireWireIUnknown::InterfaceMap<IsochChannelCOM>::GetThis(self)->SetChannelForceStopHandler( stopProc, self ) ;
585	}
586
587	void
588	IsochChannelCOM::SSetRefCon( ChannelRef self, void* refcon )
589	{
590		return IOFireWireIUnknown::InterfaceMap<IsochChannelCOM>::GetThis(self)->SetRefCon( refcon ) ;
591	}
592
593	void*
594	IsochChannelCOM::SGetRefCon( ChannelRef self )
595	{
596		return IOFireWireIUnknown::InterfaceMap<IsochChannelCOM>::GetThis(self)->GetRefCon() ;
597	}
598
599	Boolean
600	IsochChannelCOM::SNotificationIsOn( ChannelRef self )
601	{
602		return IOFireWireIUnknown::InterfaceMap<IsochChannelCOM>::GetThis(self)->NotificationIsOn() ;
603	}
604
605	Boolean
606	IsochChannelCOM::STurnOnNotification( ChannelRef self )
607	{
608		return IOFireWireIUnknown::InterfaceMap<IsochChannelCOM>::GetThis(self)->TurnOnNotification( self ) ;
609	}
610
611	void
612	IsochChannelCOM::STurnOffNotification( ChannelRef self)
613	{
614		IOFireWireIUnknown::InterfaceMap<IsochChannelCOM>::GetThis(self)->TurnOffNotification() ;
615	}
616
617	void
618	IsochChannelCOM::SClientCommandIsComplete( ChannelRef self, FWClientCommandID commandID, IOReturn status )
619	{
620		IOFireWireIUnknown::InterfaceMap<IsochChannelCOM>::GetThis(self)->ClientCommandIsComplete(commandID, status) ;
621	}
622}
623