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