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 * Copyright (c) 1999-2002 Apple Computer, Inc.  All rights reserved.
24 *
25 * HISTORY
26 *
27 */
28
29#include <IOKit/assert.h>
30#include <IOKit/firewire/IOFireWireController.h>
31#include <IOKit/firewire/IOFWIsochChannel.h>
32#include <IOKit/firewire/IOFWLocalIsochPort.h>
33#include <IOKit/firewire/IOFWCommand.h>
34#include <IOKit/firewire/IOFireWireDevice.h>
35#import <IOKit/firewire/IOFWDCLProgram.h>
36#import <IOKit/firewire/IOFireWireLink.h>
37
38#include <libkern/c++/OSSet.h>
39#include <libkern/c++/OSCollectionIterator.h>
40#include "FWDebugging.h"
41
42#include <IOKit/firewire/IOFWUtils.h>
43
44OSDefineMetaClassAndStructors(IOFWIsochChannel, OSObject)
45OSMetaClassDefineReservedUnused(IOFWIsochChannel, 0);
46OSMetaClassDefineReservedUnused(IOFWIsochChannel, 1);
47OSMetaClassDefineReservedUnused(IOFWIsochChannel, 2);
48OSMetaClassDefineReservedUnused(IOFWIsochChannel, 3);
49
50#pragma mark -
51//////////////////////////////////////////////////////////////////////////////////////////
52
53// init
54//
55//
56
57bool IOFWIsochChannel::init(	IOFireWireController *		control,
58								bool 						doIRM,
59								UInt32 						packetSize,
60								IOFWSpeed 					prefSpeed,
61								ForceStopNotificationProc *	stopProc,
62								void *						stopRefCon )
63{
64	bool success = true;
65
66	FWKLOG(( "IOFWIsochChannel::init - doIRM = %d\n", doIRM ));
67
68	DebugLog("fPacketSize = %d\n", (uint32_t)packetSize) ;
69
70	success = OSObject::init();
71
72	if( success )
73	{
74		fControl = control;
75		fDoIRM = doIRM;
76		fPacketSize = packetSize;
77		fPrefSpeed = prefSpeed;
78		fStopProc = stopProc;
79		fStopRefCon = stopRefCon;
80		fTalker = NULL;
81
82		fChannel = 64;		// no channel allocated, 64 is an illegal channel
83		fBandwidth = 0;		// no bandwidth allocated
84
85		// Bandwidth allocation is not done on the main FireWire workloop so
86		// we need to allocate a lock for synchronizing access to the resources
87		// used in bandwidth allocation
88
89		fLock = IOLockAlloc();
90		if( fLock == NULL )
91		{
92			success = false;
93		}
94	}
95
96	if( success )
97	{
98		fListeners = OSSet::withCapacity( 1 );
99		if( fListeners == NULL  )
100		{
101			success = false;
102		}
103	}
104
105	//
106	// create command for reading bandwidth available and channel available register
107	//
108
109	if( success )
110	{
111		fReadCmd = OSTypeAlloc( IOFWReadQuadCommand );
112		if( fReadCmd == NULL )
113		{
114			success = false;
115		}
116	}
117
118	if( success )
119	{
120	    fReadCmd->initAll( fControl, 0, FWAddress(), NULL, 0, NULL, NULL );
121	}
122
123	//
124	// create lock cmd for updating the bandwidth available and channel available registers
125	//
126
127	if( success )
128	{
129		fLockCmd = OSTypeAlloc( IOFWCompareAndSwapCommand );
130		if( fLockCmd == NULL )
131		{
132			success = false;
133		}
134	}
135
136    if( success )
137	{
138	    fLockCmd->initAll( fControl, 0, FWAddress(), NULL, NULL, 0, NULL, NULL );
139	}
140
141    return success;
142}
143
144// free
145//
146//
147
148void IOFWIsochChannel::free()
149{
150    if( fListeners )
151    {
152	    fListeners->release();
153		fListeners = NULL;
154	}
155
156	if( fReadCmd )
157    {
158	    fReadCmd->release();
159		fReadCmd = NULL;
160    }
161
162	if( fLockCmd )
163    {
164	    fLockCmd->release();
165		fLockCmd = NULL;
166	}
167
168	if( fLock )
169	{
170		IOLockFree( fLock );
171		fLock = NULL;
172	}
173
174	OSObject::free();
175}
176
177#pragma mark -
178//////////////////////////////////////////////////////////////////////////////////////////
179
180// checkMemoryInRange
181//
182//
183
184IOReturn IOFWIsochChannel::checkMemoryInRange( IOMemoryDescriptor * memory )
185{
186	IOReturn status = kIOReturnSuccess;
187
188	if( memory == NULL )
189	{
190		status = kIOReturnBadArgument;
191	}
192
193	//
194	// setup
195	//
196
197	bool memory_prepared = false;
198	if( status == kIOReturnSuccess )
199	{
200		status = memory->prepare( kIODirectionInOut );
201	}
202
203	if( status == kIOReturnSuccess )
204	{
205		memory_prepared = true;
206	}
207
208	UInt64 length = 0;
209	if( status == kIOReturnSuccess )
210	{
211		length = memory->getLength();
212		if( length == 0 )
213		{
214			status = kIOReturnError;
215		}
216	}
217
218	//
219	// create IODMACommand
220	//
221
222	IODMACommand * dma_command = NULL;
223	if( status == kIOReturnSuccess )
224	{
225		dma_command = IODMACommand::withSpecification(
226												kIODMACommandOutputHost64,		// segment function
227												64,								// max address bits
228												length,							// max segment size
229												(IODMACommand::MappingOptions)(IODMACommand::kMapped | IODMACommand::kIterateOnly),		// IO mapped & don't bounce buffer
230												length,							// max transfer size
231												0,								// page alignment
232												NULL,							// mapper
233												NULL );							// refcon
234		if( dma_command == NULL )
235			status = kIOReturnError;
236
237	}
238
239	if( status == kIOReturnSuccess )
240	{
241		// set memory descriptor and don't prepare it
242		status = dma_command->setMemoryDescriptor( memory, false );
243	}
244
245	bool dma_command_prepared = false;
246	if( status == kIOReturnSuccess )
247	{
248		status = dma_command->prepare( 0, length, true );
249	}
250
251	if( status == kIOReturnSuccess )
252	{
253		dma_command_prepared = true;
254	}
255
256	//
257	// check ranges
258	//
259
260	if( status == kIOReturnSuccess )
261	{
262		UInt64 offset = 0;
263		UInt64 mask = fControl->getFireWirePhysicalAddressMask();
264		while( (offset < length) && (status == kIOReturnSuccess) )
265		{
266			IODMACommand::Segment64 segments[10];
267			UInt32 num_segments = 10;
268			status = dma_command->gen64IOVMSegments( &offset, segments, &num_segments );
269			if( status == kIOReturnSuccess )
270			{
271				for( UInt32 i = 0; i < num_segments; i++ )
272				{
273				//	IOLog( "checkSegments - segments[%d].fIOVMAddr = 0x%016llx, fLength = %d\n", i, segments[i].fIOVMAddr, segments[i].fLength  );
274
275					if( (segments[i].fIOVMAddr & (~mask)) )
276					{
277				//		IOLog( "checkSegmentsFailed - 0x%016llx & 0x%016llx\n", segments[i].fIOVMAddr, mask );
278						status = kIOReturnNotPermitted;
279						break;
280					}
281				}
282			}
283		}
284	}
285
286	//
287	// clean up
288	//
289
290	if( dma_command_prepared )
291	{
292		dma_command->complete();
293		dma_command_prepared = false;
294	}
295
296	if( dma_command )
297	{
298		dma_command->clearMemoryDescriptor();
299		dma_command->release();
300		dma_command = NULL;
301	}
302
303	if( memory_prepared )
304	{
305		memory->complete();
306		memory_prepared = false;
307	}
308
309	return status;
310
311}
312
313// setTalker
314//
315//
316
317IOReturn IOFWIsochChannel::setTalker( IOFWIsochPort *talker )
318{
319	IOReturn error = kIOReturnSuccess;
320    fTalker = talker;
321
322	IOFWLocalIsochPort * localIsochPort = OSDynamicCast( IOFWLocalIsochPort, talker );
323	if ( localIsochPort )
324	{
325		IODCLProgram * program = localIsochPort->getProgramRef();
326		IOMemoryMap * map = program->getBufferMap();
327		if( map == NULL )
328		{
329			error = kIOReturnNoMemory;
330		}
331
332		IOMemoryDescriptor * memory = NULL;
333		if( error == kIOReturnSuccess )
334		{
335			memory = map->getMemoryDescriptor();
336			if( memory == NULL )
337			{
338				error = kIOReturnNoMemory;
339			}
340		}
341
342#if 0
343		if( error == kIOReturnSuccess )
344		{
345			error = checkMemoryInRange( memory );
346		}
347#endif
348
349		if( error == kIOReturnSuccess )
350		{
351
352			program->setForceStopProc( fStopProc, fStopRefCon, this );
353			program->release();
354		}
355	}
356
357    return error;
358}
359
360// addListener
361//
362//
363
364IOReturn IOFWIsochChannel::addListener( IOFWIsochPort *listener )
365{
366	IOReturn error = kIOReturnSuccess;
367
368    if( !fListeners->setObject( listener ) )
369    {
370		error = kIOReturnNoMemory;
371	}
372
373	if ( !error )
374	{
375		IOFWLocalIsochPort * localIsochPort = OSDynamicCast( IOFWLocalIsochPort, listener ) ;
376		if ( localIsochPort )
377		{
378			IODCLProgram * program = localIsochPort->getProgramRef();
379			IOMemoryMap * map = program->getBufferMap();
380			if( map == NULL )
381			{
382				error = kIOReturnNoMemory;
383			}
384
385			IOMemoryDescriptor * memory = NULL;
386			if( error == kIOReturnSuccess )
387			{
388				memory = map->getMemoryDescriptor();
389				if( memory == NULL )
390				{
391					error = kIOReturnNoMemory;
392				}
393			}
394
395			if( error == kIOReturnSuccess )
396			{
397				error = checkMemoryInRange( memory );
398			}
399
400			if( error == kIOReturnSuccess )
401			{
402				program->setForceStopProc( fStopProc, fStopRefCon, this ) ;
403				program->release() ;
404			}
405		}
406	}
407
408	return error ;
409}
410
411// start
412//
413//
414
415IOReturn IOFWIsochChannel::start()
416{
417	DebugLog("channel %p start\n", this ) ;
418
419    OSIterator *listenIterator;
420    IOFWIsochPort *listen;
421	IOReturn error = kIOReturnSuccess ;
422
423	// <rdar://problem/4033119>
424	// the user requested doIRM, but we don't have an allocation..
425	// they should have called allocateChannel()...
426	if ( fDoIRM && fChannel == 64 )
427	{
428		return kIOReturnNotReady ;
429	}
430
431    //
432	// start all listeners
433	//
434
435	listenIterator = OSCollectionIterator::withCollection( fListeners );
436    if( listenIterator )
437	{
438        listenIterator->reset();
439        while( !error && (listen = (IOFWIsochPort *)listenIterator->getNextObject()) )
440		{
441            error = listen->start();
442        }
443        listenIterator->release();
444    }
445
446	//
447	// start the talker
448	//
449
450	if( !error && fTalker )
451	{
452		error = fTalker->start();
453	}
454
455	if (error)
456	{
457		DebugLog("channel %p start - error=%x\n", this, error ) ;
458
459	}
460	else
461	{
462		DebugLog("channel %p start - started on isoch channel %d\n", this, (uint32_t)fChannel ) ;
463	}
464
465    return error ;
466}
467
468// stop
469//
470//
471
472IOReturn IOFWIsochChannel::stop()
473{
474	DebugLog("channel %p stop\n", this ) ;
475
476    OSIterator *listenIterator;
477    IOFWIsochPort *listen;
478
479    //
480	// stop all listeners
481	//
482
483	listenIterator = OSCollectionIterator::withCollection( fListeners );
484    if( listenIterator )
485	{
486        listenIterator->reset();
487		while( (listen = (IOFWIsochPort *)listenIterator->getNextObject()) )
488		{
489            listen->stop();
490        }
491		listenIterator->release();
492    }
493
494	//
495	// stop talker
496	//
497
498	if( fTalker )
499	{
500		fTalker->stop();
501	}
502
503    return kIOReturnSuccess;
504}
505
506#pragma mark -
507//////////////////////////////////////////////////////////////////////////////////////////
508
509// allocateChannel
510//
511// cannot be called on the workloop
512
513IOReturn IOFWIsochChannel::allocateChannel()
514{
515	DebugLog("channel %p allocateChannel\n", this ) ;
516
517	IOReturn 			status = kIOReturnSuccess;
518
519	IOFWIsochPort *		listen = NULL;
520	OSIterator *		listenIterator 		= NULL;
521
522	//
523	// get best speed and find supported channels
524	//
525
526	if( status == kIOReturnSuccess )
527	{
528		UInt64 				portChans;
529		IOFWSpeed 			portSpeed;
530		UInt64 				allowedChans;
531		IOFWSpeed			speed;
532
533		// Find minimum of requested speed and paths from talker to each listener
534		// Reduce speed to minimum of what all ports can do, find supported channels
535
536		speed = fPrefSpeed;
537
538		//
539		// get supported channels and max speed from talker
540		//
541
542        allowedChans = ~(UInt64)0;
543        if( fTalker )
544		{
545            fTalker->getSupported( portSpeed, portChans );
546            if( portSpeed < speed )
547			{
548                speed = portSpeed;
549            }
550			allowedChans &= portChans;
551        }
552
553		//
554		// get supported channels and max speed from all listeners
555		//
556
557		listenIterator = OSCollectionIterator::withCollection( fListeners );
558        if( listenIterator )
559		{
560            while( (listen = (IOFWIsochPort *)listenIterator->getNextObject()) )
561			{
562                listen->getSupported( portSpeed, portChans );
563                if( portSpeed < speed )
564				{
565                    speed = portSpeed;
566                }
567				allowedChans &= portChans;
568            }
569        }
570
571		//
572        // do bandwidth and channel allocation
573		//
574
575		// allocateChannelBegin sets up fSpeed, fGeneration, fBandwidth, and fChannel
576
577		status = allocateChannelBegin( speed, allowedChans );
578	}
579
580	//
581	// allocate hardware resources for all listeners
582	//
583
584	if( status == kIOReturnSuccess )
585	{
586        if( listenIterator )
587		{
588            listenIterator->reset();
589            while( (listen = (IOFWIsochPort *)listenIterator->getNextObject()) &&
590				   (status == kIOReturnSuccess) )
591			{
592                status = listen->allocatePort( fSpeed, fChannel );
593            }
594		}
595	}
596
597	//
598	// allocate hardware resources for talker
599	//
600
601	if( status == kIOReturnSuccess )
602	{
603        if( fTalker )
604		{
605            status = fTalker->allocatePort( fSpeed, fChannel );
606		}
607	}
608
609	//
610	// clean up
611	//
612
613    if( listenIterator )
614    {
615	    listenIterator->release();
616	}
617
618    if( status != kIOReturnSuccess )
619	{
620		// on error release anything we may have allocated
621
622        releaseChannel();
623    }
624
625	return status;
626}
627
628// allocateChannelBegin
629//
630// cannot be called on the workloop
631
632IOReturn IOFWIsochChannel::allocateChannelBegin( 	IOFWSpeed		inSpeed,
633													UInt64			inAllowedChans,
634													UInt32 *		outChannel )
635{
636	DebugLog( "IOFWIsochChannel<%p>::allocateChannelBegin() - entered, inSpeed = %d, fDoIRM = %d, inAllowedChans = 0x%016llx, fChannel=%d, fBandwidth=%d\n", this, inSpeed, fDoIRM, inAllowedChans, (uint32_t)fChannel, (uint32_t)fBandwidth );
637
638	IOReturn		status = kIOReturnSuccess;
639
640	IOLockLock( fLock );
641
642	fSpeed = ( inSpeed == kFWSpeedMaximum ) ? fControl->getLink()->getPhySpeed() : inSpeed ;
643
644	if( fDoIRM )
645	{
646		UInt32 			oldIRM[3];
647
648		// reserve bandwidth:
649		// bandwidth is in units of quads at 1600 Mbs
650		UInt32 bandwidth = 0;
651		if( fPacketSize != 0 )
652		{
653			bandwidth = (fPacketSize/4 + 3) * 16 / (1 << inSpeed);
654		}
655
656		do
657		{
658			FWAddress			addr ;
659			UInt32				generation ;
660
661			status = kIOReturnSuccess ;
662
663			//
664			// get IRM nodeID into addr.nodeID
665			//
666
667			fControl->getIRMNodeID( generation, addr.nodeID );
668
669			DebugLog( "IOFWIsochChannel<%p>::allocateChannelBegin() - generation %d\n", this, (uint32_t)generation );
670
671			//
672			// Make sure we're at least one second from the last bus-reset
673			//
674
675			{
676				unsigned delayRetries = 15;
677				while (delayRetries > 0)
678				{
679					UInt32			currentGeneration;
680					AbsoluteTime	currentUpTime;
681					UInt64			nanos;
682					UInt32			delayInMSec;
683
684					IOFWGetAbsoluteTime( & currentUpTime );
685					SUB_ABSOLUTETIME( & currentUpTime, fControl->getResetTime() ) ;
686					absolutetime_to_nanoseconds( currentUpTime, & nanos ) ;
687					if (nanos < 1000000000LL)
688					{
689						delayInMSec = (UInt32) ((1000000000LL - nanos)/1000000LL);
690
691						DebugLog( "IOFWIsochChannel<%p>::allocateChannelBegin() - delaying for %d msec\n", this, (uint32_t)delayInMSec);
692
693						// Delay until it's been one second from last bus-reset
694						IOSleep(delayInMSec);
695
696						// get generation
697						fControl->getIRMNodeID( currentGeneration, addr.nodeID );
698						if (currentGeneration == generation)
699						{
700							break;
701						}
702						else
703						{
704							generation = currentGeneration; // Another bus-reset occurred, do the delay check again
705						}
706					}
707					else
708					{
709						break;
710					}
711
712					delayRetries--;
713				}
714
715				// Make sure we didn't use all the delay retries
716				if (delayRetries == 0)
717				{
718					DebugLog("IOFWIsochChannel<%p>::allocateChannelBegin() - timed out waiting for bus resets to end\n", this) ;
719					status = kIOReturnTimeout;
720				}
721			}
722
723			if( status == kIOReturnSuccess )
724			{
725				// read IRM into oldIRM[3] (up to 5 retries) ;
726
727				addr.addressHi = kCSRRegisterSpaceBaseAddressHi ;
728				addr.addressLo = kCSRBandwidthAvailable ;
729
730				fReadCmd->reinit( generation, addr, oldIRM, 3 );
731				fReadCmd->setMaxPacket( 4 );		// many cameras don't like block reads to IRM registers, eg. Canon GL-1
732
733				status = fReadCmd->submit();
734
735				DebugLogCond( status, "IOFWIsochChannel<%p>::allocateChannelBegin() line %u - read command got error 0x%x\n", this, __LINE__, status ) ;
736				DebugLog("IOFWIsochChannel<%p>::allocateChannelBegin() - oldIRM set to %08x %08x %08x\n", this,
737							OSSwapBigToHostInt32(oldIRM[0]), OSSwapBigToHostInt32(oldIRM[1]), OSSwapBigToHostInt32(oldIRM[2]) ) ;
738			}
739
740			//
741			// Claim bandwidth from IRM
742			//
743
744			if ( fBandwidth == 0 )
745			{
746				UInt32 old_bandwidth = OSSwapBigToHostInt32( oldIRM[0] );
747				if( ( status == kIOReturnSuccess ) && ( old_bandwidth < bandwidth ) )
748				{
749					DebugLog("IOFWIsochChannel<%p>::allocateChannelBegin() - no bandwidth left\n", this) ;
750					status = kIOReturnNoSpace;
751				}
752
753				if( status == kIOReturnSuccess )
754				{
755					UInt32			newVal = OSSwapHostToBigInt32(old_bandwidth - bandwidth);
756
757					fLockCmd->reinit( generation, addr, &oldIRM[0], &newVal, 1 );
758
759					status = fLockCmd->submit();
760
761					if ( status )
762					{
763						DebugLog("IOFWIsochChannel<%p>::allocateChannelBegin() line %u - lock command got error 0x%x\n", this, __LINE__, status ) ;
764					}
765					else
766					{
767						if ( !fLockCmd->locked( &oldIRM[0] ) )
768						{
769							DebugLog("IOFWIsochChannel<%p>::allocateChannelBegin() - lock for bandwidth failed\n", this) ;
770							status = kIOReturnCannotLock;
771						}
772					}
773
774					if( status == kIOReturnSuccess )
775					{
776						DebugLog("IOFWIsochChannel<%p>::allocateChannelBegin() - allocated bandwidth %u\n", this, (uint32_t)bandwidth) ;
777
778						fBandwidth = bandwidth;
779					}
780				}
781			}
782
783			//
784			// claim channel from IRM
785			//
786
787			// (if we have an error here, the bandwidth wasn't allocated)
788			if ( status == kIOReturnSuccess && fChannel == 64 )
789			{
790				unsigned channel ;
791
792				UInt32 old_channel_hi = OSSwapBigToHostInt32( oldIRM[1] );
793				UInt32 old_channel_lo = OSSwapBigToHostInt32( oldIRM[2] );
794
795				// mask inAllowedChans by channels IRM has available
796				inAllowedChans &= (UInt64)(old_channel_lo) | ((UInt64)old_channel_hi << 32);
797
798				for( channel = 0; channel < 64; ++channel )
799				{
800					if( inAllowedChans & ((UInt64)1 << (63 - channel)) )
801					{
802						break;
803					}
804				}
805
806				if( channel == 64 )
807				{
808					DebugLog("IOFWIsochChannel<%p>::allocateChannelBegin() - no acceptable free channels\n", this) ;
809					status = kIOReturnNoResources;
810				}
811
812				if( status == kIOReturnSuccess )
813				{
814					UInt32 *		oldPtr;
815					UInt32			newVal ;
816
817					// Claim channel
818					if( channel < 32 )
819					{
820						addr.addressLo = kCSRChannelsAvailable31_0;
821						oldPtr = &oldIRM[1];
822						newVal = OSSwapHostToBigInt32( old_channel_hi & ~(1<<(31 - channel)) );
823					}
824					else
825					{
826						addr.addressLo = kCSRChannelsAvailable63_32;
827						oldPtr = &oldIRM[2];
828						newVal = OSSwapHostToBigInt32( old_channel_lo & ~(1 << (63 - channel)) );
829					}
830
831					fLockCmd->reinit( generation, addr, oldPtr, &newVal, 1 );
832					status = fLockCmd->submit();
833
834					if ( status )
835					{
836						DebugLog("IOFWIsochChannel<%p>::allocateChannelBegin() - lock command got error 0x%x\n", this, status ) ;
837					}
838					else
839					{
840						if( !fLockCmd->locked(oldPtr) )
841						{
842							DebugLog("IOFWIsochChannel<%p>::allocateChannelBegin() - couldn't claim channel %u\n", this, channel) ;
843							status = kIOReturnCannotLock;
844						}
845					}
846				}
847
848				if( status == kIOReturnSuccess )
849				{
850					DebugLog("IOFWIsochChannel<%p>::allocateChannelBegin() - allocated channel %u\n", this, channel) ;
851
852					fChannel = channel;
853					if( outChannel != NULL )
854					{
855						*outChannel = channel;
856					}
857				}
858				else if ( fBandwidth != 0 )
859				{
860					// Release the bandwidth we allocated, since we did not get the channel
861					fControl->releaseIRMBandwidthInGeneration(fBandwidth, generation);
862				}
863			}
864
865			if( status == kIOReturnSuccess )
866			{
867				fGeneration = generation;
868
869				// tell controller we would like to hear about bus resets
870				fControl->addAllocatedChannel(this);
871			}
872
873			if( status == kIOFireWireBusReset )
874			{
875				// we lost any allocations if there was a bus reset
876				fBandwidth = 0;
877				fChannel = 64;
878			}
879
880		} while( (status == kIOFireWireBusReset) || (status == kIOReturnCannotLock) );
881	}
882	else
883	{
884		//
885		// return a channel even if we aren't doing the IRM management
886		//
887
888		unsigned channel ;
889
890		{
891			for( channel = 0; channel < 64; channel++ )
892			{
893				if( inAllowedChans & ((UInt64)1 << (63 - channel)) )
894				{
895					break;
896				}
897			}
898
899			if( channel == 64 )
900			{
901				DebugLog("IOFWIsochChannel<%p>::allocateChannelBegin() - couldn't find acceptable channel\n", this) ;
902				status = kIOReturnNoResources;
903			}
904		}
905
906		if( status == kIOReturnSuccess )
907		{
908			DebugLog( "IOFWIsochChannel<%p>::allocateChannelBegin() - allocated channel = %d\n", this, channel );
909
910			fChannel = channel;
911			if( outChannel != NULL )
912			{
913				*outChannel = channel;
914			}
915		}
916	}
917
918	IOLockUnlock( fLock );
919
920	FWTrace( kFWTIsoch, kTPIsochAllocateChannelBegin, (uintptr_t)(fControl->getLink()), fChannel, fBandwidth, status );
921	DebugLogCond( status, "IOFWIsochChannel<%p>::allocateChannelBegin() - exited with status = 0x%x\n", this, status );
922
923    return status;
924}
925
926// ChannelThreadInfo
927//
928// A little struct for keeping track of our this pointer and generation
929// when transitioning to a second thread during bandwidth reallocation.
930
931struct ChannelThreadInfo
932{
933    IOFWIsochChannel *		fChannel;
934    UInt32 					fGeneration;
935};
936
937// handleBusReset
938//
939//
940
941void IOFWIsochChannel::handleBusReset()
942{
943	//
944	// setup thread info and spawn a thread
945	//
946
947	ChannelThreadInfo * threadInfo = (ChannelThreadInfo *)IOMalloc( sizeof(ChannelThreadInfo) );
948	if( threadInfo )
949	{
950		threadInfo->fGeneration = fControl->getGeneration();
951		threadInfo->fChannel = this;
952		retain();	// retain ourself for the thread to use
953
954		thread_t		thread;
955		if( kernel_thread_start((thread_continue_t)threadFunc, threadInfo, &thread ) == KERN_SUCCESS )
956		{
957			thread_deallocate(thread);
958		}
959	}
960}
961
962// threadFunc
963//
964//
965
966void IOFWIsochChannel::threadFunc( void * arg )
967{
968	//
969	// extract info from thread info
970	//
971
972    ChannelThreadInfo * threadInfo = (ChannelThreadInfo *)arg;
973    IOFWIsochChannel * 	channel = threadInfo->fChannel;
974	UInt32 				generation = threadInfo->fGeneration;
975
976	//
977	// realloc bandwidth
978	//
979
980	channel->reallocBandwidth( generation );
981
982	//
983	// clean up thread info
984	//
985
986	IOFree( threadInfo, sizeof(threadInfo) );
987    channel->release();		// retain occurred in handleBusReset
988}
989
990// reallocBandwidth
991//
992// cannot be called on the workloop
993
994void IOFWIsochChannel::reallocBandwidth( UInt32 generation )
995{
996	IOReturn result = kIOReturnSuccess;
997
998    FWAddress addr( kCSRRegisterSpaceBaseAddressHi, kCSRBandwidthAvailable );
999
1000	IOLockLock( fLock );
1001
1002	InfoLog( "IOFWIsochChannel<%p>::reallocBandwidth() - bandwidth %ld, channel = %ld\n", this, fBandwidth, fChannel );
1003
1004	if( result == kIOReturnSuccess )
1005	{
1006		UInt32 current_generation;
1007		UInt16 irm;
1008		fControl->getIRMNodeID( current_generation, irm );
1009		addr.nodeID = irm;
1010		if( current_generation != generation )
1011		{
1012			result = kIOFireWireBusReset;
1013		}
1014
1015//		FWKLOG(( "IOFWIsochChannel::reallocBandwidth - realloc generation %d, current generation = %d\n", generation, current_generation ));
1016	}
1017
1018	if( result == kIOReturnSuccess )
1019	{
1020		// check to make sure we don't allocate twice on a generation
1021
1022		if( fGeneration == generation )
1023		{
1024			result = kIOReturnError;
1025		}
1026	}
1027
1028	//
1029	// reallocate bandwidth
1030	//
1031
1032	if( fBandwidth != 0 )
1033	{
1034		UInt32 oldVal = 0;
1035
1036		//
1037		// read the bandwidth available register
1038		//
1039
1040		if( result == kIOReturnSuccess )
1041		{
1042            fReadCmd->reinit( generation, addr, &oldVal, 1 );
1043            result = fReadCmd->submit();
1044		}
1045
1046		//
1047		// compare swap loop
1048		//
1049
1050		bool done = false;
1051		while( (result == kIOReturnSuccess) && !done )
1052		{
1053			UInt32 newVal = 0;
1054			UInt32 old_bandwidth = OSSwapBigToHostInt32( oldVal );
1055
1056			// make sure there's space
1057			if( old_bandwidth < fBandwidth )
1058			{
1059				result = kIOReturnNoSpace;
1060			}
1061
1062			if( result == kIOReturnSuccess )
1063			{
1064				newVal = OSSwapHostToBigInt32(old_bandwidth - fBandwidth);
1065
1066				fLockCmd->reinit( generation, addr, &oldVal, &newVal, 1 );
1067				result = fLockCmd->submit();
1068			}
1069
1070			if( result == kIOReturnSuccess )
1071			{
1072				done = fLockCmd->locked(&oldVal);
1073			}
1074		}
1075
1076		if( result == kIOReturnNoSpace )
1077		{
1078			DebugLog( "IOFWIsochChannel<%p>::reallocBandwidth() - failed to reallocate bandwidth = %d, channel = %d\n", this, (uint32_t)fBandwidth, (uint32_t)fChannel );
1079
1080			// Couldn't reallocate bandwidth!
1081			fBandwidth = 0;
1082			fChannel = 64;	// this will keep us from reallocating the channel
1083		}
1084		else
1085		{
1086			InfoLog( "IOFWIsochChannel<%p>::reallocBandwidth() - reallocated bandwidth = %ld\n", this, fBandwidth );
1087		}
1088	}
1089
1090	//
1091	// reallocate channel
1092	//
1093
1094	if( fChannel != 64 )
1095	{
1096		UInt32 	mask = 0;
1097		UInt32	oldVal = 0;
1098
1099		//
1100		// read the channels available register
1101		//
1102
1103		if( result == kIOReturnSuccess )
1104		{
1105			if( fChannel <= 31 )
1106			{
1107                addr.addressLo = kCSRChannelsAvailable31_0;
1108                mask = 1 << (31-fChannel);
1109            }
1110            else
1111			{
1112                addr.addressLo = kCSRChannelsAvailable63_32;
1113                mask = 1 << (63-fChannel);
1114            }
1115
1116			fReadCmd->reinit( generation, addr, &oldVal, 1 );
1117            result = fReadCmd->submit();
1118		}
1119
1120		//
1121		// compare swap loop
1122		//
1123
1124		bool done = false;
1125		while( (result == kIOReturnSuccess) && !done )
1126		{
1127			UInt32 old_channels_avail = OSSwapBigToHostInt32( oldVal );
1128			UInt32 newVal = OSSwapHostToBigInt32( old_channels_avail & ~mask );
1129			if( newVal == oldVal )
1130			{
1131				// Channel already allocated!
1132				result = kIOFireWireChannelNotAvailable ;
1133			}
1134
1135			if( result == kIOReturnSuccess )
1136			{
1137				fLockCmd->reinit( generation, addr, &oldVal, &newVal, 1 );
1138                result = fLockCmd->submit();
1139			}
1140
1141			if( result == kIOReturnSuccess )
1142			{
1143				done = fLockCmd->locked( &oldVal );
1144			}
1145		}
1146
1147		if( result == kIOFireWireChannelNotAvailable )
1148		{
1149			DebugLog( "IOFWIsochChannel<%p>::reallocBandwidth() - failed to reallocate channel = %d\n", this, (uint32_t)fChannel );
1150
1151			// Couldn't reallocate the channel
1152			fChannel = 64;
1153			// fBandwidth will be set to 0 once it is released in the call to releaseChannel below
1154		}
1155		else
1156		{
1157			InfoLog( "IOFWIsochChannel<%p>::reallocBandwidth() - reallocated channel = %ld\n", this, fChannel );
1158		}
1159	}
1160
1161	fGeneration = generation;
1162
1163	FWTrace( kFWTIsoch, kTPIsochReallocBandwidth, (uintptr_t)(fControl->getLink()), fChannel, fBandwidth, result );
1164	DebugLogCond( result, "IOFWIsochChannel<%p>::reallocBandwidth() - exited with result = 0x%x\n", this, result );
1165
1166	IOLockUnlock( fLock );
1167
1168	if( result == kIOReturnNoSpace || result == kIOFireWireChannelNotAvailable )
1169	{
1170        // Couldn't reallocate bandwidth or channel
1171
1172		stop();
1173
1174		// fChannel and fBandwidth have been left in such a way that releaseChannel()
1175		// will know to release both, one, or none
1176
1177		releaseChannel();
1178
1179		if ( fStopProc )
1180		{
1181			(*fStopProc)( fStopRefCon, this, kIOFireWireChannelNotAvailable );
1182		}
1183    }
1184
1185}
1186
1187// releaseChannel
1188//
1189//
1190
1191IOReturn IOFWIsochChannel::releaseChannel()
1192{
1193	DebugLog("IOFWIsochChannel<%p>::releaseChannel()\n", this ) ;
1194
1195    OSIterator *listenIterator;
1196    IOFWIsochPort *listen;
1197
1198    if( fTalker )
1199	{
1200        fTalker->releasePort();
1201    }
1202
1203	listenIterator = OSCollectionIterator::withCollection(fListeners);
1204    if( listenIterator )
1205	{
1206        while( (listen = (IOFWIsochPort *)listenIterator->getNextObject()) )
1207		{
1208            listen->releasePort();
1209        }
1210        listenIterator->release();
1211    }
1212
1213    return releaseChannelComplete();
1214}
1215
1216// releaseChannelComplete
1217//
1218//
1219
1220IOReturn IOFWIsochChannel::releaseChannelComplete()
1221{
1222	IOReturn result = kIOReturnSuccess;
1223
1224    // release bandwidth and channel
1225
1226	FWKLOG(( "IOFWIsochChannel::releaseChannelComplete - entered, fDoIRM = %d\n", fDoIRM ));
1227
1228	if( fDoIRM )
1229	{
1230		FWAddress 	addr( kCSRRegisterSpaceBaseAddressHi, kCSRBandwidthAvailable );
1231		UInt32		generation = 0;
1232
1233		IOLockLock( fLock );
1234
1235		//
1236		// Tell the controller that we don't need to know about
1237		// bus resets before doing anything else, since a bus reset
1238		// sets us into the state we want (no allocated bandwidth).
1239		//
1240
1241		if( result == kIOReturnSuccess )
1242		{
1243			fControl->removeAllocatedChannel( this );
1244		}
1245
1246		if( result == kIOReturnSuccess )
1247		{
1248			UInt16 irm;
1249			fControl->getIRMNodeID( generation, irm );
1250			addr.nodeID = irm;
1251
1252			FWKLOG(( "IOFWIsochChannel::releaseChannelComplete - generation = %d allocated generation = %d\n", generation, fGeneration ));
1253
1254			if( generation != fGeneration )
1255			{
1256				result = kIOFireWireBusReset;
1257				fBandwidth = 0;
1258				fChannel = 64;
1259			}
1260		}
1261
1262		//
1263		// release bandwidth
1264		//
1265
1266		if( fBandwidth != 0 )
1267		{
1268			UInt32 oldVal = 0;
1269
1270			//
1271			// read the bandwidth available register
1272			//
1273
1274			if( result == kIOReturnSuccess )
1275			{
1276				fReadCmd->reinit( generation, addr, &oldVal, 1 );
1277				result = fReadCmd->submit();
1278			}
1279
1280			//
1281			// compare swap loop
1282			//
1283
1284			bool done = false;
1285			while( (result == kIOReturnSuccess) && !done )
1286			{
1287				UInt32 old_bandwidth = OSSwapBigToHostInt32( oldVal );
1288				UInt32 newVal = OSSwapHostToBigInt32( old_bandwidth + fBandwidth );
1289
1290				fLockCmd->reinit( generation, addr, &oldVal, &newVal, 1 );
1291				result = fLockCmd->submit();
1292
1293				if( result == kIOReturnSuccess )
1294				{
1295					done = fLockCmd->locked(&oldVal);
1296				}
1297			}
1298
1299			// error or not, we've released our bandwidth
1300			fBandwidth = 0;
1301
1302			FWKLOG(( "IOFWIsochChannel::releaseChannelComplete - released bandwidth\n" ));
1303
1304			if( result != kIOFireWireBusReset )
1305			{
1306				// as long as we didn't get a bus reset error, let's pretend
1307				// this was successful so we can give channel deallocation a try
1308
1309				result = kIOReturnSuccess;
1310			}
1311		}
1312
1313		//
1314		// release channel
1315		//
1316
1317		if( fChannel != 64 )
1318		{
1319			UInt32 	mask = 0;
1320			UInt32	oldVal = 0;
1321
1322			//
1323			// read the channels available register
1324			//
1325
1326			if( result == kIOReturnSuccess )
1327			{
1328				if( fChannel <= 31 )
1329				{
1330					addr.addressLo = kCSRChannelsAvailable31_0;
1331					mask = 1 << (31-fChannel);
1332				}
1333				else
1334				{
1335					addr.addressLo = kCSRChannelsAvailable63_32;
1336					mask = 1 << (63-fChannel);
1337				}
1338
1339				fReadCmd->reinit( generation, addr, &oldVal, 1 );
1340				result = fReadCmd->submit();
1341			}
1342
1343			//
1344			// compare swap loop
1345			//
1346
1347			bool done = false;
1348			while( (result == kIOReturnSuccess) && !done )
1349			{
1350				UInt32 old_channels_avail = OSSwapBigToHostInt32( oldVal );
1351				UInt32 newVal = OSSwapHostToBigInt32( old_channels_avail | mask );
1352
1353				fLockCmd->reinit( generation, addr, &oldVal, &newVal, 1 );
1354				result = fLockCmd->submit();
1355
1356				if( result == kIOReturnSuccess )
1357				{
1358					done = fLockCmd->locked( &oldVal );
1359				}
1360			}
1361
1362			FWKLOG(( "IOFWIsochChannel::releaseChannelComplete - released channel\n" ));
1363
1364			// error or not, we've released the channel
1365			fChannel = 64;
1366		}
1367
1368		// error or not we've released our channel and bandwidth
1369
1370		fBandwidth = 0;
1371		fChannel = 64;
1372
1373		fGeneration = generation;
1374
1375		IOLockUnlock( fLock );
1376	}
1377
1378    return kIOReturnSuccess;
1379}
1380
1381// updateBandwidth
1382//
1383// deprecated
1384
1385IOReturn IOFWIsochChannel::updateBandwidth( bool /* claim */ )
1386{
1387	DebugLog("driver calling deprecated IOFWIsochChannel::updateBandwidth on channel %p\n", this ) ;
1388
1389	return kIOReturnUnsupported;
1390}
1391
1392