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/*
24 * Copyright (c) 1999-2002 Apple Computer, Inc.  All rights reserved.
25 *
26 * IOFireWireIRM
27 *
28 * HISTORY
29 *
30 */
31
32// public
33#include <IOKit/firewire/IOFireWireFamilyCommon.h>
34
35// private
36#include "IOFireWireIRM.h"
37
38//#define FWLOCALLOGGING 1
39#include "FWDebugging.h"
40
41#define kChannel31Mask 0x00000001
42
43OSDefineMetaClassAndStructors( IOFireWireIRM, OSObject )
44
45// create
46//
47//
48
49IOFireWireIRM * IOFireWireIRM::create( IOFireWireController * controller )
50{
51    IOReturn 		status = kIOReturnSuccess;
52    IOFireWireIRM * me;
53
54    if( status == kIOReturnSuccess )
55    {
56        me = OSTypeAlloc( IOFireWireIRM );
57        if( me == NULL )
58            status = kIOReturnNoMemory;
59    }
60
61    if( status == kIOReturnSuccess )
62    {
63        bool success = me->initWithController( controller );
64		if( !success )
65		{
66			status = kIOReturnError;
67		}
68    }
69
70    if( status != kIOReturnSuccess )
71    {
72        me = NULL;
73    }
74
75	FWLOCALKLOG(( "IOFireWireIRM::create() - created new IRM 0x%08lx\n", (UInt32)me ));
76
77    return me;
78}
79
80// initWithController
81//
82//
83
84bool IOFireWireIRM::initWithController(IOFireWireController * control)
85{
86	IOReturn status = kIOReturnSuccess;
87
88	bool success = OSObject::init();
89	FWPANICASSERT( success == true );
90
91	fControl = control;
92
93	fIRMNodeID = kFWBadNodeID;
94	fOurNodeID = kFWBadNodeID;
95	fGeneration = 0;
96
97	//
98    // create BROADCAST_CHANNEL register
99	//
100
101	fBroadcastChannelBuffer = OSSwapHostToBigInt32( kBroadcastChannelInitialValues );
102    fBroadcastChannelAddressSpace = IOFWPseudoAddressSpace::simpleRWFixed( fControl, FWAddress(kCSRRegisterSpaceBaseAddressHi, kCSRBroadcastChannel),
103																			 sizeof(fBroadcastChannelBuffer), &fBroadcastChannelBuffer );
104	FWPANICASSERT( fBroadcastChannelAddressSpace != NULL );
105
106	status = fBroadcastChannelAddressSpace->activate();
107	FWPANICASSERT( status == kIOReturnSuccess );
108
109	//
110	// create lock command
111	//
112
113	fLockCmdInUse = false;
114	fLockCmd = OSTypeAlloc( IOFWCompareAndSwapCommand );
115	FWPANICASSERT( fLockCmd != NULL );
116	fLockCmd->initAll( fControl, 0, FWAddress(), NULL, NULL, 0, IOFireWireIRM::lockCompleteStatic, this );
117
118	FWLOCALKLOG(( "IOFireWireIRM::initWithController() - IRM intialized\n" ));
119
120	return true;
121}
122
123// free
124//
125//
126
127void IOFireWireIRM::free()
128{
129	FWLOCALKLOG(( "IOFireWireIRM::free() - freeing IRM 0x%08lx\n", (UInt32)this ));
130
131	//
132	// free lock command
133	//
134
135	if( fLockCmd != NULL )
136	{
137		// cancel the command if its in use.
138		if( fLockCmdInUse )
139		{
140			fLockCmd->cancel( kIOFireWireBusReset );
141		}
142
143        fLockCmd->release();
144		fLockCmd = NULL;
145	}
146
147	//
148	// free BROADCAST_CHANNEL register
149	//
150
151	if( fBroadcastChannelAddressSpace != NULL)
152	{
153		fBroadcastChannelAddressSpace->deactivate();
154        fBroadcastChannelAddressSpace->release();
155        fBroadcastChannelAddressSpace = NULL;
156    }
157
158	OSObject::free();
159}
160
161// isIRMActive
162//
163//
164
165bool IOFireWireIRM::isIRMActive( void )
166{
167	// this irm should be active if we're the IRM node and we're
168	// not the only node on the bus
169
170	return (fOurNodeID == fIRMNodeID && (fOurNodeID & 0x3f) != 0);
171}
172
173// processBusReset
174//
175//
176
177void IOFireWireIRM::processBusReset( UInt16 ourNodeID, UInt16 irmNodeID, UInt32 generation )
178{
179	FWLOCALKLOG(( "IOFireWireIRM::processBusReset() - bus reset occurred\n" ));
180
181	FWLOCALKLOG(( "IOFireWireIRM::processBusReset() - ourNodeID = 0x%04x, irmNodeID = 0x%04x, generation = %d\n", ourNodeID, irmNodeID, generation ));
182
183	// node id's and generation
184
185	fIRMNodeID = irmNodeID;
186	fOurNodeID = ourNodeID;
187	fGeneration = generation;
188
189	if( isIRMActive() )
190	{
191
192		// stop any command in progress. any inflight commands should already
193		// have been canceled by the bus reset before this point.
194		// this is just an extra precaution
195
196		// lockComplete will run with status = kIOFireWireBusReset before we
197		// return from cancel.  fLockCmdInUse is cleared by lockComplete.
198
199		// calling cancel on commands that are not busy will still call
200		// complete, so we must make sure a command is in use before cancelling it.
201
202		// commands do not have a reliable API for tracking usage, so we
203		// use fLockCmdInUse instead
204
205		if( fLockCmdInUse )
206		{
207			fLockCmd->cancel( kIOFireWireBusReset );
208		}
209
210		// initialize fOldChannelsAvailable31_0 and fLockRetries
211		fLockRetries = 8;
212		fOldChannelsAvailable31_0 = OSSwapHostToBigInt32( 0xffffffff );  // don't really need to swap of course
213
214		allocateBroadcastChannel();
215	}
216	else
217	{
218		FWLOCALKLOG(( "IOFireWireIRM::processBusReset() - clear valid bit in BROADCAST_CHANNEL register\n" ));
219		fBroadcastChannelBuffer = OSSwapHostToBigInt32(kBroadcastChannelInitialValues);
220	}
221
222}
223
224// allocateBroadcastChannel
225//
226//
227
228void IOFireWireIRM::allocateBroadcastChannel( void )
229{
230	IOReturn status = kIOReturnSuccess;
231
232	FWLOCALKLOG(( "IOFireWireIRM::allocateBroadcastChannel() - attempting to allocate broadcast channel\n" ));
233
234	FWAddress address( kCSRRegisterSpaceBaseAddressHi, kCSRChannelsAvailable31_0 );
235	address.nodeID = fIRMNodeID;
236
237	UInt32 host_channels_available = OSSwapBigToHostInt32( fOldChannelsAvailable31_0 );
238	host_channels_available &= ~kChannel31Mask;
239	fNewChannelsAvailable31_0 = OSSwapHostToBigInt32( host_channels_available );
240
241	fLockCmd->reinit( fGeneration, address, &fOldChannelsAvailable31_0, &fNewChannelsAvailable31_0, 1, IOFireWireIRM::lockCompleteStatic, this );
242
243	// the standard async commands call complete with an error before
244	// returning an error from submit.
245
246	fLockCmdInUse = true;
247	status = fLockCmd->submit();
248}
249
250// lockComplete
251//
252//
253
254void IOFireWireIRM::lockCompleteStatic( void *refcon, IOReturn status, IOFireWireNub *device, IOFWCommand *fwCmd )
255{
256	IOFireWireIRM * me = (IOFireWireIRM*)refcon;
257	me->lockComplete( status );
258}
259
260void IOFireWireIRM::lockComplete( IOReturn status )
261{
262	bool done = true;
263
264	fLockCmdInUse = false;
265
266	if( status == kIOReturnSuccess )
267	{
268		// update fOldChannelsAvailable31_0 and fLockRetries
269		bool tryAgain = !fLockCmd->locked( &fOldChannelsAvailable31_0 );
270		if( tryAgain && fLockRetries-- )
271		{
272			FWLOCALKLOG(( "IOFireWireIRM::lockComplete() - allocation attempt failed, will retry\n" ));
273			allocateBroadcastChannel();
274
275			done = false;
276		}
277	}
278
279	// done means we did not resubmit this command
280	if( done )
281	{
282		// if this command was completed because of a bus reset,
283		// we will retry the channel allocation in a moment when
284		// processBusReset is called, therefore don't set the
285		// channel as valid just yet.
286		//
287		// otherwise, if no bus reset processing is pending we
288		// pretend we've allocated the channel even if we failed to do so.
289
290#if FWLOCALLOGGING
291		if( status == kIOReturnSuccess )
292		{
293			FWLOCALKLOG(( "IOFireWireIRM::lockComplete() - successfully allocated broadcast channel\n" ));
294		}
295		else
296		{
297			FWLOCALKLOG(( "IOFireWireIRM::lockComplete() - failed to allocate broadcast channel\n" ));
298		}
299#endif
300
301		if( status != kIOFireWireBusReset )
302		{
303			FWLOCALKLOG(( "IOFireWireIRM::lockComplete() - set valid bit in BROADCAST_CHANNEL register\n" ));
304
305			fBroadcastChannelBuffer = OSSwapHostToBigInt32( kBroadcastChannelInitialValues | kBroadcastChannelValidMask );
306		}
307	}
308}
309
310#pragma mark -
311
312OSDefineMetaClassAndStructors( IOFireWireIRMAllocation, OSObject );
313OSMetaClassDefineReservedUnused(IOFireWireIRMAllocation, 0);
314OSMetaClassDefineReservedUnused(IOFireWireIRMAllocation, 1);
315OSMetaClassDefineReservedUnused(IOFireWireIRMAllocation, 2);
316OSMetaClassDefineReservedUnused(IOFireWireIRMAllocation, 3);
317OSMetaClassDefineReservedUnused(IOFireWireIRMAllocation, 4);
318OSMetaClassDefineReservedUnused(IOFireWireIRMAllocation, 5);
319OSMetaClassDefineReservedUnused(IOFireWireIRMAllocation, 6);
320OSMetaClassDefineReservedUnused(IOFireWireIRMAllocation, 7);
321
322// IRMAllocationThreadInfo
323//
324// A little struct for keeping track of our this pointer and generation
325// when transitioning to a second thread during bandwidth reallocation.
326
327struct IRMAllocationThreadInfo
328{
329    IOFireWireIRMAllocation * fIRMAllocation;
330    UInt32 fGeneration;
331	IOFireWireController * fControl;
332	IORecursiveLock * fLock;
333	UInt8 fIsochChannel;
334	UInt32 fBandwidthUnits;
335};
336
337// IOFireWireIRMAllocation::init
338//
339//
340bool IOFireWireIRMAllocation::init( IOFireWireController * control,
341									Boolean releaseIRMResourcesOnFree,
342									AllocationLostNotificationProc allocationLostProc,
343									void *pLostProcRefCon)
344{
345	if (!OSObject::init())
346		return false ;
347
348	// Allocate a lock
349	fLock = IORecursiveLockAlloc () ;
350	if ( ! fLock )
351		return false ;
352
353	// Initialize some class members
354	fControl = control;
355	fAllocationGeneration = 0xFFFFFFFF;
356	fAllocationLostProc = allocationLostProc;
357	fLostProcRefCon = pLostProcRefCon;
358	fReleaseIRMResourcesOnFree = releaseIRMResourcesOnFree;
359	fBandwidthUnits = 0;
360	fIsochChannel = 64;
361
362	fControl->addToIRMAllocationSet(this);
363
364	isAllocated = false;
365	return true;
366}
367
368// IOFireWireIRMAllocation::release
369//
370//
371void IOFireWireIRMAllocation::release() const
372{
373	DebugLog( "IOFireWireIRMAllocation::release, retain count before release = %d\n",getRetainCount() ) ;
374
375	// Take the lock
376	IORecursiveLockLock(fLock);
377
378	int retainCnt = getRetainCount();
379
380	if ( retainCnt == 2 )
381	{
382		if( isAllocated == false )
383		{
384			fControl->removeFromIRMAllocationSet((IOFireWireIRMAllocation*)this);
385		}
386		else
387		{
388			// The controller has an extra retain on the IOFireWireIRMAllocation object
389			// because it's in the array used to restore allocations after a bus-reset.
390			// We now need to remove it from the controller's array, so it's no longer
391			// auto-restored after bus-reset!
392			fControl->removeIRMAllocation((IOFireWireIRMAllocation*)this);
393		}
394	}
395
396	OSObject::release();
397
398	// Bypass unlock if we just did the last release!
399	if (retainCnt != 1)
400		IORecursiveLockUnlock(fLock);
401}
402
403// IOFireWireIRMAllocation::free
404//
405//
406void IOFireWireIRMAllocation::free( void )
407{
408	DebugLog( "IOFireWireIRMAllocation::free\n") ;
409
410	// Take the lock
411	IORecursiveLockLock(fLock);
412
413
414	// If we need to release the isoch resources, do so now!
415	if (isAllocated)
416	{
417		if (fReleaseIRMResourcesOnFree)
418		{
419			if (fBandwidthUnits > 0)
420				fControl->releaseIRMBandwidthInGeneration(fBandwidthUnits,fAllocationGeneration);
421			if (fIsochChannel < 64)
422				fControl->releaseIRMChannelInGeneration(fIsochChannel,fAllocationGeneration);
423		}
424		// Note: we already removed this allocation from the controller's array! Don't need to do it here!
425	}
426
427	// Free the lock
428	if ( fLock )
429		IORecursiveLockFree( fLock ) ;
430
431	OSObject::free();
432}
433
434// IOFireWireIRMAllocation::allocateIsochResources
435//
436//
437IOReturn IOFireWireIRMAllocation::allocateIsochResources(UInt8 isochChannel, UInt32 bandwidthUnits)
438{
439	IOReturn res = kIOReturnError;
440	UInt32 irmGeneration;
441	UInt16 irmNodeID;
442
443	// Take the lock
444	IORecursiveLockLock(fLock);
445
446	if (!isAllocated)
447	{
448		// Initialize some class members
449		fAllocationGeneration = 0xFFFFFFFF;
450
451		// Get the current generation
452		fControl->getIRMNodeID(irmGeneration, irmNodeID);
453
454		res = kIOReturnSuccess;
455
456		if (isochChannel < 64)
457		{
458			// Attempt to allocate isoch channel
459			res = fControl->allocateIRMChannelInGeneration(isochChannel,irmGeneration);
460		}
461
462		if ((res == kIOReturnSuccess) && (bandwidthUnits > 0))
463		{
464			// Attempt to allocate isoch bandwidth
465			res = fControl->allocateIRMBandwidthInGeneration(bandwidthUnits,irmGeneration);
466			if (res != kIOReturnSuccess)
467			{
468				// Need to free the isoch channel (note: will fail if generation has changed)
469				fControl->releaseIRMChannelInGeneration(isochChannel,irmGeneration);
470			}
471		}
472
473		if (res == kIOReturnSuccess)
474		{
475			fIsochChannel = isochChannel;
476			fBandwidthUnits = bandwidthUnits;
477			fAllocationGeneration = irmGeneration;
478			isAllocated = true;
479
480			// Register this object with the controller
481			fControl->addIRMAllocation(this);
482		}
483	}
484
485	// Unlock the lock
486	IORecursiveLockUnlock(fLock);
487
488	FWTrace( kFWTIsoch, kTPIsochIRMAllocateIsochResources, (uintptr_t)(fControl->getLink()), fIsochChannel, fBandwidthUnits, res );
489
490	return res;
491}
492
493// IOFireWireIRMAllocation::deallocateIsochResources
494//
495//
496IOReturn IOFireWireIRMAllocation::deallocateIsochResources(void)
497{
498	IOReturn res = kIOReturnError;
499
500	// Take the lock
501	IORecursiveLockLock(fLock);
502
503	if (isAllocated)
504	{
505		if (fBandwidthUnits > 0)
506			fControl->releaseIRMBandwidthInGeneration(fBandwidthUnits,fAllocationGeneration);
507		if (fIsochChannel < 64)
508			fControl->releaseIRMChannelInGeneration(fIsochChannel,fAllocationGeneration);
509
510		// Unregister this object with the controller
511		fControl->removeIRMAllocation(this);
512
513		isAllocated = false;
514		fBandwidthUnits = 0;
515		fIsochChannel = 64;
516		fAllocationGeneration = 0xFFFFFFFF;
517	}
518
519	// Unlock the lock
520	IORecursiveLockUnlock(fLock);
521
522	return res;
523}
524
525// IOFireWireIRMAllocation::areIsochResourcesAllocated
526//
527//
528Boolean IOFireWireIRMAllocation::areIsochResourcesAllocated(UInt8 *pAllocatedIsochChannel, UInt32 *pAllocatedBandwidthUnits)
529{
530
531	*pAllocatedIsochChannel = fIsochChannel;
532	*pAllocatedBandwidthUnits = fBandwidthUnits;
533	return isAllocated;
534}
535
536// IOFireWireIRMAllocation::GetRefCon
537//
538//
539void * IOFireWireIRMAllocation::GetRefCon(void)
540{
541	return fLostProcRefCon;
542}
543
544// IOFireWireIRMAllocation::SetRefCon
545//
546//
547void IOFireWireIRMAllocation::SetRefCon(void* refCon)
548{
549	fLostProcRefCon = refCon;
550}
551
552// IOFireWireIRMAllocation::handleBusReset
553//
554//
555void IOFireWireIRMAllocation::handleBusReset(UInt32 generation)
556{
557	// Take the lock
558	IORecursiveLockLock(fLock);
559
560	if (!isAllocated)
561	{
562		IORecursiveLockUnlock(fLock);
563		return;
564	}
565
566	if (fAllocationGeneration == generation)
567	{
568		IORecursiveLockUnlock(fLock);
569		return;
570	}
571
572	// Spawn a thread to do the reallocation
573	IRMAllocationThreadInfo * threadInfo = (IRMAllocationThreadInfo *)IOMalloc( sizeof(IRMAllocationThreadInfo) );
574	if( threadInfo )
575	{
576		threadInfo->fGeneration = generation;
577		threadInfo->fIRMAllocation = this;
578		threadInfo->fControl = fControl;
579		threadInfo->fLock = fLock;
580		threadInfo->fIsochChannel = fIsochChannel;
581		threadInfo->fBandwidthUnits = fBandwidthUnits;
582
583		retain();	// retain ourself for the thread to use
584
585		thread_t		thread;
586		if( kernel_thread_start((thread_continue_t)threadFunc, threadInfo, &thread ) == KERN_SUCCESS )
587		{
588			thread_deallocate(thread);
589		}
590	}
591
592	// Unlock the lock
593	IORecursiveLockUnlock(fLock);
594}
595
596// IOFireWireIRMAllocation::setReleaseIRMResourcesOnFree
597//
598//
599void IOFireWireIRMAllocation::setReleaseIRMResourcesOnFree(Boolean doRelease)
600{
601	fReleaseIRMResourcesOnFree = doRelease;
602}
603
604// IOFireWireIRMAllocation::getAllocationGeneration
605//
606//
607UInt32 IOFireWireIRMAllocation::getAllocationGeneration(void)
608{
609	return fAllocationGeneration;
610}
611
612// IOFireWireIRMAllocation::failedToRealloc
613//
614//
615void IOFireWireIRMAllocation::failedToRealloc(void)
616{
617	// Notify client, and mark as to not reallocate in the future!
618
619	if (fAllocationLostProc)
620		fAllocationLostProc(fLostProcRefCon,this);
621
622	// Unregister this object with the controller
623	fControl->removeIRMAllocation(this);
624
625	isAllocated = false;
626	fAllocationGeneration = 0xFFFFFFFF;
627}
628
629// IOFireWireIRMAllocation::threadFunc
630//
631//
632void IOFireWireIRMAllocation::threadFunc( void * arg )
633{
634	IOReturn res = kIOReturnSuccess;
635    IRMAllocationThreadInfo * threadInfo = (IRMAllocationThreadInfo *)arg;
636    IOFireWireIRMAllocation *pIRMAllocation = threadInfo->fIRMAllocation;
637	IORecursiveLock * fLock = threadInfo->fLock;
638	UInt32 generation = threadInfo->fGeneration;
639	UInt32 irmGeneration;
640	UInt16 irmNodeID;
641
642	// Take the lock
643	IORecursiveLockLock(fLock);
644
645	// Get the current generation
646	threadInfo->fControl->getIRMNodeID(irmGeneration, irmNodeID);
647
648	if ((irmGeneration == generation) && (pIRMAllocation->getAllocationGeneration() != 0xFFFFFFFF))
649	{
650		if (threadInfo->fIsochChannel < 64)
651		{
652			// Attempt to reallocate isoch channel
653			res = threadInfo->fControl->allocateIRMChannelInGeneration(threadInfo->fIsochChannel,generation);
654		}
655
656		if ((res == kIOReturnSuccess) && (threadInfo->fBandwidthUnits > 0))
657		{
658			// Attempt to reallocate isoch bandwidth
659			res = threadInfo->fControl->allocateIRMBandwidthInGeneration(threadInfo->fBandwidthUnits,generation);
660			if (res != kIOReturnSuccess)
661			{
662				// Need to free the isoch channel (note: will fail if generation has changed)
663				threadInfo->fControl->releaseIRMChannelInGeneration(threadInfo->fIsochChannel,generation);
664			}
665		}
666
667		if ((res != kIOReturnSuccess) && (res != kIOFireWireBusReset))
668		{
669			// We failed to reallocate (and not due to a bus-reset).
670			pIRMAllocation->failedToRealloc();
671		}
672	}
673
674	// Unlock the lock
675	IORecursiveLockUnlock(fLock);
676
677	// clean up thread info
678	IOFree( threadInfo, sizeof(threadInfo) );
679    pIRMAllocation->release();		// retain occurred in handleBusReset
680
681	FWTrace( kFWTIsoch, kTPIsochIRMThreadFunc, (uintptr_t)(threadInfo->fControl->getLink()), threadInfo->fIsochChannel, threadInfo->fBandwidthUnits, res );
682}
683