/* * IOFireWireLibIsochChannel.cpp * IOFireWireFamily * * Created by NWG on Mon Mar 12 2001. * Copyright (c) 2001 Apple Computer, Inc. All rights reserved. * */ #import "IOFireWireLibIsochChannel.h" #import "IOFireWireLibIsochPort.h" #import "IOFireWireLibDevice.h" #import "IOFireWireLibPriv.h" #import namespace IOFireWireLib { // ============================================================ // IsochChannel // ============================================================ IsochChannel::IsochChannel( const IUnknownVTbl & interface, Device& userclient, bool inDoIRM, IOByteCount inPacketSize, IOFWSpeed inPrefSpeed) : IOFireWireIUnknown( interface ), mUserClient( userclient ), mKernChannelRef(0), mNotifyIsOn(false), mForceStopHandler(0), mUserRefCon(0), mTalker(0), mListeners( ::CFArrayCreateMutable( kCFAllocatorDefault, 0, NULL ) ), mRefInterface( reinterpret_cast( & GetInterface() ) ), mPrefSpeed( inPrefSpeed ) { if (!mListeners) throw kIOReturnNoMemory ; mUserClient.AddRef() ; uint32_t outputCnt = 1; uint64_t outputVal = 0; const uint64_t inputs[3] = {inDoIRM, inPacketSize, inPrefSpeed}; IOReturn err = IOConnectCallScalarMethod(mUserClient.GetUserClientConnection(), kIsochChannel_Allocate, inputs,3, &outputVal,&outputCnt); mKernChannelRef = (UserObjectHandle) outputVal; if(err) { throw err ; } } IsochChannel::~IsochChannel() { if(NotificationIsOn()) TurnOffNotification() ; if (mKernChannelRef) { uint32_t outputCnt = 0; const uint64_t inputs[1]={(const uint64_t)mKernChannelRef}; IOConnectCallScalarMethod(mUserClient.GetUserClientConnection(), kReleaseUserObject, inputs,1, NULL,&outputCnt); mKernChannelRef = 0 ; } if ( mTalker ) (**mTalker).Release( mTalker ) ; if (mListeners) { for( CFIndex index = 0, count = ::CFArrayGetCount( mListeners ); index < count; ++index ) { IOFireWireLibIsochPortRef port = (IOFireWireLibIsochPortRef) CFArrayGetValueAtIndex(mListeners, index) ; (**port).Release( port ) ; } CFRelease(mListeners) ; } mUserClient.Release() ; } // void // IsochChannel::ForceStop( ChannelRef refCon, IOReturn result, void** args, int numArgs ) // { // IsochChannel* me = (IsochChannel*) args[0] ; // // if (me->mForceStopHandler) // (me->mForceStopHandler)(me->mRefInterface, (UInt32)args[1]) ; // reason // // } IOReturn IsochChannel::SetTalker( IOFireWireLibIsochPortRef inTalker ) { (**inTalker).AddRef( inTalker ) ; mTalker = inTalker ; IsochPort * port = dynamic_cast( IOFireWireIUnknown::InterfaceMap::GetThis( inTalker ) ) ; if ( port ) { uint32_t outputCnt = 0; const uint64_t inputs[2] = {(const uint64_t)port->mKernPortRef, (const uint64_t)mKernChannelRef}; IOConnectCallScalarMethod(mUserClient.GetUserClientConnection(), kLocalIsochPort_SetChannel, inputs,2, NULL,&outputCnt); } return kIOReturnSuccess ; } IOReturn IsochChannel::AddListener( IOFireWireLibIsochPortRef inListener ) { CFArrayAppendValue(mListeners, inListener) ; (**inListener).AddRef( inListener ) ; IsochPort * port = dynamic_cast( IOFireWireIUnknown::InterfaceMap::GetThis( inListener ) ) ; if ( port ) { uint32_t outputCnt = 0; const uint64_t inputs[2] = {(const uint64_t)port->mKernPortRef, (const uint64_t)mKernChannelRef}; IOConnectCallScalarMethod(mUserClient.GetUserClientConnection(), kLocalIsochPort_SetChannel, inputs,2, NULL,&outputCnt); } return kIOReturnSuccess ; } IOReturn IsochChannel::AllocateChannel() { DebugLog( "+ IsochChannel::AllocateChannel\n" ) ; IOReturn result = kIOReturnSuccess ; IOFWSpeed portSpeed ; UInt64 portChans ; // Get best speed, minimum of requested speed and paths from talker to each listener mSpeed = mPrefSpeed ; // reduce speed to minimum of so far and what all ports can do, // and find valid channels UInt64 allowedChans = ~(UInt64)0 ; if(mTalker) { (**mTalker).GetSupported( mTalker, & portSpeed, & portChans); if(portSpeed < mSpeed) mSpeed = portSpeed; allowedChans &= portChans; } UInt32 listenCount = CFArrayGetCount(mListeners) ; IOFireWireLibIsochPortRef listen ; for (UInt32 listenIndex=0; listenIndex < listenCount; ++listenIndex) { listen = (IOFireWireLibIsochPortRef)CFArrayGetValueAtIndex( mListeners, listenIndex) ; (**listen).GetSupported( listen, & portSpeed, & portChans ); if(portSpeed < mSpeed) mSpeed = portSpeed; allowedChans &= portChans; } // call the kernel middle bits uint32_t outputCnt = 2; uint64_t outputVal[2]; const uint64_t inputs[4] = {(const uint64_t)mKernChannelRef, (const uint64_t)mSpeed, (allowedChans >> 32),(0xFFFFFFFF & allowedChans)}; result = IOConnectCallScalarMethod(mUserClient.GetUserClientConnection(), kIsochChannel_UserAllocateChannelBegin, inputs,4, outputVal,&outputCnt); mSpeed = (IOFWSpeed) (outputVal[0] & 0xFFFFFFFF); mChannel = outputVal[1] & 0xFFFFFFFF; if (kIOReturnSuccess == result) { // complete in user space UInt32 listenIndex = 0 ; while (kIOReturnSuccess == result && listenIndex < listenCount) { IOFireWireLibIsochPortRef port = (IOFireWireLibIsochPortRef)CFArrayGetValueAtIndex(mListeners, listenIndex) ; result = (**port).AllocatePort( port, mSpeed, mChannel ) ; ++listenIndex ; } if (kIOReturnSuccess == result && mTalker) result = (**mTalker).AllocatePort( mTalker, mSpeed, mChannel) ; } return result ; } IOReturn IsochChannel::ReleaseChannel() { DebugLog("+ IsochChannel::ReleaseChannel\n") ; IOReturn result = kIOReturnSuccess ; if(mTalker) { result = (**mTalker).ReleasePort( mTalker ); } DebugLogCond( result, "IsochChannel::ReleaseChannel: error 0x%08x calling ReleasePort() on talker\n", result) ; UInt32 listenCount = CFArrayGetCount(mListeners) ; IOFireWireLibIsochPortRef listen ; UInt32 index=0 ; while (kIOReturnSuccess == result && index < listenCount) { listen = (IOFireWireLibIsochPortRef)CFArrayGetValueAtIndex(mListeners, index) ; result = (**listen).ReleasePort( listen ); DebugLogCond(result, "IsochChannel::ReleaseChannel: error %p calling ReleasePort() on listener\n", listen) ; ++index ; } uint32_t outputCnt = 0; result = IOConnectCallScalarMethod(mUserClient.GetUserClientConnection(), mUserClient.MakeSelectorWithObject( kIsochChannel_UserReleaseChannelComplete_d, mKernChannelRef ), NULL,0, NULL,&outputCnt); return result ; } IOReturn IsochChannel::Start() { DebugLog("+ IsochChannel::Start\n") ; // Start all listeners, then start the talker UInt32 listenCount = CFArrayGetCount( mListeners ) ; IOFireWireLibIsochPortRef listen ; UInt32 listenIndex = 0 ; IOReturn error = kIOReturnSuccess ; while ( !error && listenIndex < listenCount ) { listen = (IOFireWireLibIsochPortRef) CFArrayGetValueAtIndex( mListeners, listenIndex) ; error = (**listen).Start( listen ) ; DebugLogCond( error, "IsochChannel::Start: error 0x%x starting channel\n", error ) ; ++listenIndex ; } if ( mTalker && !error ) error = (**mTalker).Start( mTalker ) ; if ( error ) Stop() ; DebugLog("-IsochChannel::Start error=%x\n", error) ; return error ; } IOReturn IsochChannel::Stop() { DebugLog( "+ IsochChannel::Stop\n" ) ; if (mTalker) (**mTalker).Stop( mTalker ) ; UInt32 listenCount = CFArrayGetCount( mListeners ) ; IOFireWireLibIsochPortRef listen ; for (UInt32 listenIndex=0; listenIndex < listenCount; ++listenIndex) { listen = (IOFireWireLibIsochPortRef)CFArrayGetValueAtIndex( mListeners, listenIndex ) ; (**listen).Stop( listen ) ; } return kIOReturnSuccess; } IOFireWireIsochChannelForceStopHandler IsochChannel::SetChannelForceStopHandler ( IOFireWireIsochChannelForceStopHandler stopProc, IOFireWireLibIsochChannelRef interface ) { DebugLog( "+IsochChannel::SetChannelForceStopHandler this=%p, proc=%p\n", this, stopProc ) ; IOFireWireIsochChannelForceStopHandler oldHandler = mForceStopHandler ; mForceStopHandler = stopProc ; io_connect_t connection = mUserClient.GetUserClientConnection() ; if ( mNotifyIsOn && connection ) { uint64_t refrncData[kOSAsyncRef64Count]; refrncData[kIOAsyncCalloutFuncIndex] = (uint64_t) mForceStopHandler; refrncData[kIOAsyncCalloutRefconIndex] = (unsigned long)interface; uint32_t outputCnt = 0; const uint64_t inputs[1]={(const uint64_t)mKernChannelRef}; IOReturn error = IOConnectCallAsyncScalarMethod(connection, kSetAsyncRef_IsochChannelForceStop, mUserClient.GetAsyncPort(), refrncData,kOSAsyncRef64Count, inputs,1, NULL,&outputCnt); #pragma unused( error ) DebugLogCond( error, "Error setting isoch channel force stop handler\n") ; } return oldHandler ; } void IsochChannel::SetRefCon( void* stopProcRefCon) { mUserRefCon = stopProcRefCon ; } void* IsochChannel::GetRefCon() { return mUserRefCon ; } Boolean IsochChannel::NotificationIsOn() { return mNotifyIsOn ; } Boolean IsochChannel::TurnOnNotification( IOFireWireLibIsochChannelRef interface ) { // if notification is already on, skip out. if (mNotifyIsOn) { return true ; } io_connect_t connection = mUserClient.GetUserClientConnection() ; if (!connection) { DebugLog("IsochChannel::TurnOnNotification: user client not open!\n") ; return false ; } IOReturn error = kIOReturnSuccess ; { uint64_t refrncData[kOSAsyncRef64Count]; refrncData[kIOAsyncCalloutFuncIndex] = (uint64_t) mForceStopHandler; refrncData[kIOAsyncCalloutRefconIndex] = (unsigned long)interface; uint32_t outputCnt = 0; const uint64_t inputs[1]={(const uint64_t)mKernChannelRef}; error = IOConnectCallAsyncScalarMethod(connection, kSetAsyncRef_IsochChannelForceStop, mUserClient.GetAsyncPort(), refrncData,kOSAsyncRef64Count, inputs,1, NULL,&outputCnt); } { unsigned count = ::CFArrayGetCount( mListeners ) ; unsigned index = 0 ; while( index < count && !error ) { // have kernel channel force stop proc call user dcl program force stop proc LocalIsochPort * port = dynamic_cast(IOFireWireIUnknown::InterfaceMap::GetThis( (IsochPort*) ::CFArrayGetValueAtIndex( mListeners, index ) ) ) ; if ( port ) { uint32_t outputCnt = 0; const uint64_t inputs[2] = {(const uint64_t)port->mKernPortRef, (const uint64_t)mKernChannelRef}; error = IOConnectCallScalarMethod(connection, kLocalIsochPort_SetChannel, inputs,2, NULL,&outputCnt); } ++index ; } } if ( !error && mTalker ) { LocalIsochPort * port = dynamic_cast( IOFireWireIUnknown::InterfaceMap::GetThis( mTalker ) ) ; if ( port ) { uint32_t outputCnt = 0; const uint64_t inputs[2] = {(const uint64_t)port->mKernPortRef, (const uint64_t)mKernChannelRef}; error = IOConnectCallScalarMethod(connection, kLocalIsochPort_SetChannel, inputs,2, NULL,&outputCnt); } } if ( !error ) { mNotifyIsOn = true ; } return ( error == kIOReturnSuccess ) ; } void IsochChannel::TurnOffNotification() { io_connect_t connection = mUserClient.GetUserClientConnection() ; // if notification isn't on, skip out. if ( !mNotifyIsOn || !connection ) { return ; } uint64_t refrncData[kOSAsyncRef64Count]; refrncData[kIOAsyncCalloutFuncIndex] = (uint64_t) 0; refrncData[kIOAsyncCalloutRefconIndex] = (unsigned long) 0; const uint64_t inputs[3] = {(const uint64_t)mKernChannelRef,0,(const uint64_t)this}; uint32_t outputCnt = 0; IOConnectCallAsyncScalarMethod(connection, kSetAsyncRef_IsochChannelForceStop, mUserClient.GetAsyncPort(), refrncData,kOSAsyncRef64Count, inputs,3, NULL,&outputCnt); mNotifyIsOn = false ; } void IsochChannel::ClientCommandIsComplete( FWClientCommandID commandID, IOReturn status) { } #pragma mark - IsochChannelCOM::Interface IsochChannelCOM::sInterface = { INTERFACEIMP_INTERFACE, 1, 0, & IsochChannelCOM::SSetTalker, & IsochChannelCOM::SAddListener, & IsochChannelCOM::SAllocateChannel, & IsochChannelCOM::SReleaseChannel, & IsochChannelCOM::SStart, & IsochChannelCOM::SStop, & IsochChannelCOM::SSetChannelForceStopHandler, & IsochChannelCOM::SSetRefCon, & IsochChannelCOM::SGetRefCon, & IsochChannelCOM::SNotificationIsOn, & IsochChannelCOM::STurnOnNotification, & IsochChannelCOM::STurnOffNotification, & IsochChannelCOM::SClientCommandIsComplete } ; // // --- ctor/dtor ----------------------- // IsochChannelCOM::IsochChannelCOM( Device& userclient, bool inDoIRM, IOByteCount inPacketSize, IOFWSpeed inPrefSpeed ) : IsochChannel( reinterpret_cast( sInterface ), userclient, inDoIRM, inPacketSize, inPrefSpeed ) { } IsochChannelCOM::~IsochChannelCOM() { } // // --- IUNKNOWN support ---------------- // IUnknownVTbl** IsochChannelCOM::Alloc( Device& userclient, Boolean inDoIRM, IOByteCount inPacketSize, IOFWSpeed inPrefSpeed ) { IsochChannelCOM* me = nil ; try { me = new IsochChannelCOM( userclient, inDoIRM, inPacketSize, inPrefSpeed ) ; } catch(...) { } return ( nil == me ) ? nil : reinterpret_cast(& me->GetInterface()) ; } HRESULT IsochChannelCOM::QueryInterface(REFIID iid, void ** ppv ) { HRESULT result = S_OK ; *ppv = nil ; CFUUIDRef interfaceID = CFUUIDCreateFromUUIDBytes(kCFAllocatorDefault, iid) ; if ( CFEqual(interfaceID, IUnknownUUID) || CFEqual(interfaceID, kIOFireWireIsochChannelInterfaceID) ) { *ppv = & GetInterface() ; AddRef() ; } else { *ppv = nil ; result = E_NOINTERFACE ; } CFRelease(interfaceID) ; return result ; } // // --- static methods ------------------ // IOReturn IsochChannelCOM::SSetTalker( ChannelRef self, IOFireWireLibIsochPortRef inTalker) { return IOFireWireIUnknown::InterfaceMap::GetThis(self)->SetTalker(inTalker) ; } IOReturn IsochChannelCOM::SAddListener( ChannelRef self, IOFireWireLibIsochPortRef inListener) { return IOFireWireIUnknown::InterfaceMap::GetThis(self)->AddListener(inListener) ; } IOReturn IsochChannelCOM::SAllocateChannel( ChannelRef self) { return IOFireWireIUnknown::InterfaceMap::GetThis(self)->AllocateChannel() ; } IOReturn IsochChannelCOM::SReleaseChannel( ChannelRef self) { return IOFireWireIUnknown::InterfaceMap::GetThis(self)->ReleaseChannel() ; } IOReturn IsochChannelCOM::SStart( ChannelRef self) { return IOFireWireIUnknown::InterfaceMap::GetThis(self)->Start() ; } IOReturn IsochChannelCOM::SStop( ChannelRef self) { return IOFireWireIUnknown::InterfaceMap::GetThis(self)->Stop() ; } IOFireWireIsochChannelForceStopHandler IsochChannelCOM::SSetChannelForceStopHandler( ChannelRef self, ForceStopHandler stopProc ) { return IOFireWireIUnknown::InterfaceMap::GetThis(self)->SetChannelForceStopHandler( stopProc, self ) ; } void IsochChannelCOM::SSetRefCon( ChannelRef self, void* refcon ) { return IOFireWireIUnknown::InterfaceMap::GetThis(self)->SetRefCon( refcon ) ; } void* IsochChannelCOM::SGetRefCon( ChannelRef self ) { return IOFireWireIUnknown::InterfaceMap::GetThis(self)->GetRefCon() ; } Boolean IsochChannelCOM::SNotificationIsOn( ChannelRef self ) { return IOFireWireIUnknown::InterfaceMap::GetThis(self)->NotificationIsOn() ; } Boolean IsochChannelCOM::STurnOnNotification( ChannelRef self ) { return IOFireWireIUnknown::InterfaceMap::GetThis(self)->TurnOnNotification( self ) ; } void IsochChannelCOM::STurnOffNotification( ChannelRef self) { IOFireWireIUnknown::InterfaceMap::GetThis(self)->TurnOffNotification() ; } void IsochChannelCOM::SClientCommandIsComplete( ChannelRef self, FWClientCommandID commandID, IOReturn status ) { IOFireWireIUnknown::InterfaceMap::GetThis(self)->ClientCommandIsComplete(commandID, status) ; } }