IOFireWireIP.cpp   [plain text]


/*
 * Copyright (c) 1998-2001 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * Copyright (c) 1999-2003 Apple Computer, Inc.  All Rights Reserved.
 * 
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this
 * file.
 * 
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 * 
 * @APPLE_LICENSE_HEADER_END@
 */
#include "IOFireWireIP.h"
#include "IOFireWireIPUnit.h"
#include "IOFireWireIPCommand.h"
#include "IOFWAsyncStreamRxCommand.h"

extern "C"
{
struct mbuf * m_getpackets(int num_needed, int num_with_pkthdrs, int how);
void _logMbuf(struct mbuf * m);
void _logPkt(void *pkt, UInt16 len);
void moveMbufWithOffset(SInt32 tempOffset, struct mbuf **srcm, vm_address_t *src, SInt32 *srcLen);
}

#define super IOFWController

struct {
   UCHAR specifierID[3];      // 24-bit RID
   UCHAR version[3];          // 24-bit version
} gaspVal = { {0x00, 0x00, 0x5E}, {0x00, 0x00, 0x01} };

static u_char multicast[3] = {0x01, 0x00, 0x5e};
static u_char fwbroadcastaddr[8] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };

// FireWire bus has two power states, off and on
#define kNumOfPowerStates 2

#define BCOPY(s, d, l) do { bcopy((void *) s, (void *) d, l); } while(0)

// Note: This defines two states. off and on.
static IOPMPowerState ourPowerStates[kNumOfPowerStates] = {
  {1,0,0,0,0,0,0,0,0,0,0,0},
  {1,IOPMDeviceUsable,IOPMPowerOn,IOPMPowerOn,0,0,0,0,0,0,0,0}
};

extern funnel_t * network_flock;

#pragma mark -
#pragma mark еее copy & queue flags еее

// Set this flag to true if you need to copy the payload
bool gDoCopy = false;

// Set this flag to false if you don't need to queue the block write packets 
bool gDoQueue = false; 

// Low water mark for commands in the pre-allocated pool
UInt16 gLowWaterMark = 48;

OSDefineMetaClassAndStructors(IOFireWireIP, IOFWController);

#pragma mark -
#pragma mark еее IOService methods еее

bool IOFireWireIP::start(IOService *provider)
{    
    IOReturn	ioStat = kIOReturnSuccess;
	
    fDevice = OSDynamicCast(IOFireWireNub, provider);

    if(!fDevice)
        return false;

    fControl = fDevice->getController(); 

	if(!fControl)
		return false;

	// Does calculate the fMaxTxAsyncDoubleBuffer & fAsyncRxIsocPacketSize;
    builtIn();
	
    // Initialize the LCB
    fLcb = (LCB*)IOMalloc(sizeof(LCB));
    if(fLcb == NULL)
        return false;
    
    memset(fLcb, 0, sizeof(LCB));

	// Initialize the control blocks memory area
    if(!initializeCBlk(CBLK_MEMORY_SIZE))
        return false;

	fDiagnostics_Symbol = OSSymbol::withCStringNoCopy("Diagnostics");
	
	fDiagnostics = IOFireWireIPDiagnostics::createDiagnostics(this);
	if( fDiagnostics )
	{
		if(fDiagnostics_Symbol)
			setProperty( fDiagnostics_Symbol, fDiagnostics );
		fDiagnostics->release();
	}

	if(ioStat == kIOReturnSuccess) {
	    
		CSRNodeUniqueID	fwuid = fDevice->getUniqueID();
		
		// Construct the ethernet address
		makeEthernetAddress(&fwuid, macAddr, GUID_TYPE);
		// getBytesFromGUID(&fwuid, macAddr, GUID_TYPE);
	}

	// IONetworkingFamily attachments
    if (!super::start(provider))
        return false;
	
    if (getHardwareAddress(&myAddress) != kIOReturnSuccess)
    {	
        return false;
    }

    if(!createMediumState())
	{
		IOLog( "IOFireWireIP::start - Couldn't allocate IONetworkMedium\n" );
		return false;
	}
	
    if (!attachInterface((IONetworkInterface**)&networkInterface, false ))
    {	
        return false;
    }

	// Try setting the dynamic MTU
	if(fDevice != NULL)
	{
		if(bOnLynx == false)
		{
			networkInterface->setIfnetMTU( 1 << fDevice->maxPackLog(true) );
		}
		else
		{
			// If on Lynx, we assume to do only 512 as the MTU
			networkInterface->setIfnetMTU( 1 << 9 );
		}
	}
	
	transmitQueue = (IOGatedOutputQueue*)getOutputQueue();
    if ( !transmitQueue ) 
    {
        IOLog( "IOFireWireIP::start - Output queue initialization failed\n" );
        return false;
    }
    transmitQueue->retain();

	// Create the lock
	if(ioStat == kIOReturnSuccess) {
		// Allocate lock
		ipLock = IOLockAlloc();
		
		if(ipLock == NULL)
			ioStat = kIOReturnNoMemory;
		else
			IOLockInitWithState(ipLock, kIOLockStateUnlocked);
	}
	
	if(ioStat == kIOReturnSuccess) {
		// Allocate Timer event source
		timerSource = IOTimerEventSource::timerEventSource(
														this,
														(IOTimerEventSource::Action)&IOFireWireIP::watchdog);
		if (timerSource == NULL)
		{
			IOLog( "IOFireWireIP::start - Couldn't allocate timer event source\n" );
			return false;
		}
		if (getWorkLoop()->addEventSource(timerSource) != kIOReturnSuccess )
		{
			IOLog( "IOFireWireIP::start - Couldn't add timer event source\n" );        
			return false;
		}
		
#ifdef FIREWIRETODO
		// Allocate a IP receive event source
		ipRxEventSource = new IOFWIPEventSource;
		if(ipRxEventSource == NULL)
		{
			IOLog( "IOFireWireIP::start - Couldn't allocate ipRx event source\n" );
			return false;
		}
		if(ipRxEventSource->init(this) == false)
		{
			IOLog( "IOFireWireIP::start - Couldn't init ipRx event source\n" );
			return false;
		}
		if (getWorkLoop()->addEventSource(ipRxEventSource) != kIOReturnSuccess )
		{
			IOLog( "IOFireWireIP::start - Couldn't add ipRx event source\n" );        
			return false;
		}
#endif
	}

	if(ioStat == kIOReturnSuccess) {
		// Add unit notification for units disappearing
		fIPUnitNotifier = IOService::addNotification(gIOPublishNotification, 
													serviceMatching("IOFireWireIPUnit"), 
													&deviceAttach, this, (void*)IP1394_VERSION, 0);
	}

	if(ioStat == kIOReturnSuccess) {
		// Add unit notification for units disappearing
		fIPv6UnitNotifier = IOService::addNotification(gIOPublishNotification, 
													serviceMatching("IOFireWireIPUnit"), 
													&deviceAttach, this, (void*)IP1394v6_VERSION, 0);
	}

	// Asyncstream hook up to recieve the broadcast packets
	if(ioStat == kIOReturnSuccess) {
		fBroadcastReceiveClient = new IOFWAsyncStreamRxCommand;
		if(fBroadcastReceiveClient == NULL) {
			ioStat = kIOReturnNoMemory;
		}
		else
		{
			if(fBroadcastReceiveClient->initAll(0x1f, rxAsyncStream, fControl, fMaxRxIsocPacketSize, this) == false) {
				ioStat = kIOReturnNoMemory;
			}
		}
	}

	// Create pseudo address space
	if(ioStat == kIOReturnSuccess)
		ioStat = createIPFifoAddress(MAX_FIFO_SIZE);

	// Create config rom entry
	if(ioStat == kIOReturnSuccess)
		ioStat = createIPConfigRomEntry();

	if(ioStat == kIOReturnSuccess)
		initIsocMgmtCmds();

	if(ioStat == kIOReturnSuccess) {
		// Fill information about the IOFireWireLocalNode
		fLcb->ownHandle.deviceID = (UInt32)fDevice;
		fLcb->ownHandle.maxRec = fDevice->maxPackLog(true);       // Maximum asynchronous payload
		fLcb->ownHandle.spd = fDevice->FWSpeed();                 // Maximum speed 
		fLcb->ownHandle.unicastFifoHi = fIP1394Address.addressHi; // Upper 16 bits of unicast FIFO address
		fLcb->ownHandle.unicastFifoLo = fIP1394Address.addressLo; // Lower 32 bits of unicast FIFO address
  
		// fix to enable the arp/dhcp support from network pref pane
		setProperty(kIOFWHWAddr,  (void *) &fLcb->ownHardwareAddress, sizeof(IP1394_HDW_ADDR));
						  
		fDevice->getNodeIDGeneration(fLcb->busGeneration, fLcb->ownNodeID); 
		fLcb->ownMaxSpeed = fDevice->FWSpeed();
		fLcb->maxBroadcastPayload = fDevice->maxPackLog(true);
		fLcb->maxBroadcastSpeed = fDevice->FWSpeed();
		fLcb->ownMaxPayload = fDevice->maxPackLog(true);
	}

	if(ioStat != kIOReturnSuccess)
	{
		IOLog( "IOFireWireIP::start - failed\n" );
		return false;
	}

	// might eventually start the timer
	timerSource->setTimeoutMS(WATCHDOG_TIMER_MS);

	fStarted = true;

	// Set the ifnet's softc to this pointer, useful in if_firewire.cpp
	networkInterface->setIfnetSoftc(this);
	
    networkInterface->registerService();

    return true;
} // end start

bool IOFireWireIP::finalize(IOOptionBits options)
{
	
	return super::finalize(options);
}

void IOFireWireIP::stop(IOService *provider)
{
	if(fDiagnostics_Symbol != NULL)
	{
		fDiagnostics_Symbol->release();		
		fDiagnostics_Symbol = 0;
	}

	if(fPolicyMaker != NULL)
		fPolicyMaker->deRegisterInterestedDriver(this);

    if (ipLock != NULL) 
	{
        IOLockFree(ipLock);
    }

	freeIsocMgmtCmds();

    // Free the firewire stuff
    if (fLocalIP1394v6ConfigDirectory != NULL){
        // clear the unit directory in config rom
        fDevice->getBus()->RemoveUnitDirectory(fLocalIP1394v6ConfigDirectory) ;
		fLocalIP1394v6ConfigDirectory->release();
	} 

    // Free the firewire stuff
    if (fLocalIP1394ConfigDirectory != NULL){
        // clear the unit directory in config rom
        fDevice->getBus()->RemoveUnitDirectory(fLocalIP1394ConfigDirectory) ;
		fLocalIP1394ConfigDirectory->release();
	} 

	if(fwOwnAddr != NULL)
		fwOwnAddr->release();
	
    if (fIP1394AddressSpace != NULL){
        fIP1394AddressSpace->deactivate();
        fIP1394AddressSpace->release();
		fIP1394AddressSpace = NULL;
    }

	// Release the Asyncstream receive broadcast client
	if(fBroadcastReceiveClient != NULL)
	{
		fBroadcastReceiveClient->release();
		fBroadcastReceiveClient = NULL;
	}
	
    freeIPCmdPool();


	if (transmitQueue != NULL)
	{	
		transmitQueue->release();
	}
		
	if(timerSource != NULL) 
	{
		if (workLoop != NULL)
		{
			workLoop->removeEventSource(timerSource);
		}
		timerSource->release();
	}

#ifdef FIREWIRETODO
	if(ipRxEventSource != NULL)
	{
		ipRxEventSource->release();
	}
#endif

	// Remove IOFireWireIPUnit notification
	if(fIPv6UnitNotifier != NULL)
	{
		fIPv6UnitNotifier->remove();
		fIPv6UnitNotifier = NULL;
	}
	
	// Remove IOFireWireIPUnit notification
	if(fIPUnitNotifier != NULL)
	{
		fIPUnitNotifier->remove();
		fIPUnitNotifier = NULL;
	}
	
    if(fLcb != NULL)
        IOFree(fLcb, sizeof(LCB));

    // Clear the memory area for the CBLK's
    if(fMemoryPool != NULL)
        IOFree(fMemoryPool, CBLK_MEMORY_SIZE);

	if (networkInterface != NULL)
	{
        //detachInterface(networkInterface,true);
		networkInterface->release();
	}

	super::stop(provider);
}

void IOFireWireIP::free(void)
{
	return super::free();
}

IOReturn IOFireWireIP::message(UInt32 type, IOService *provider, void *argument)
{
    IOReturn res = kIOReturnUnsupported;

    if( kIOReturnUnsupported == res )
    {
        switch (type)
        {                
            case kIOMessageServiceIsTerminated:
                res = kIOReturnSuccess;
				//IOLog(" IOFireWireIP: terminated\n");
                break;

            case kIOMessageServiceIsSuspended:
                res = kIOReturnSuccess;
                if(fStarted == true)
                {
                    stopAsyncStreamReceiveClients();
                }
				//IOLog(" IOFireWireIP: suspended\n");
                break;

            case kIOMessageServiceIsResumed:
                res = kIOReturnSuccess;
                // Busreset function here for handling busreset
                if(fStarted == true)
                {
					busReset(fLcb, 0);
                    
                    startAsyncStreamReceiveClients();
				}
				//IOLog(" IOFireWireIP: resumed\n");
				break;
				
			case kIOMessageServiceIsRequestingClose:
				res = kIOReturnSuccess;
				//IOLog(" IOFireWireIP: kIOMessageServiceIsRequestingClose\n");
				break;
				
            default: // default the action to return kIOReturnUnsupported
				//IOLog(" IOFireWireIP: default %lX\n", type);
                break;
        }
    }
	
	messageClients(type);

    return res;
}

#pragma mark -
#pragma mark еее IOFWController methods еее

IOReturn IOFireWireIP::setMaxPacketSize(UInt32 maxSize)
{

	if (maxSize > kIOFWMaxPacketSize)
	{
		return kIOReturnError;
	}
	
//  IOLog("FireWire: setMaxPacketSize size called\n");

    return kIOReturnSuccess;
}

IOReturn IOFireWireIP::getMaxPacketSize(UInt32 * maxSize) const
{
//	IOLog("IOFireWireIP: getmaxpacket size called\n");
	*maxSize = kIOFWMaxPacketSize;

	return kIOReturnSuccess;
}

IOReturn IOFireWireIP::getHardwareAddress(IOFWAddress *ea)
{
	ea->bytes[0] = macAddr[0];
	ea->bytes[1] = macAddr[1];
	ea->bytes[2] = macAddr[2];
	ea->bytes[3] = macAddr[3];
	ea->bytes[4] = macAddr[4];
	ea->bytes[5] = macAddr[5];
	ea->bytes[6] = macAddr[6];
	ea->bytes[7] = macAddr[7];

    return kIOReturnSuccess;
} // end getHardwareAddress


bool IOFireWireIP::configureInterface( IONetworkInterface *netif )
{
    IONetworkData	 *nd;

	//IOLog("IOFireWireIP: configureInterface - start\n");

    if ( super::configureInterface( netif ) == false )
        return false;

	/* Grab a pointer to the statistics structure in the interface:	*/
	nd = netif->getNetworkData( kIONetworkStatsKey );
    if (!nd || !(fpNetStats = (IONetworkStats *) nd->getBuffer()))
    {
        IOLog("IOFireWireIP: invalid network statistics\n");
        return false;
    }

	// Get the Ethernet statistics structure:
	nd = netif->getParameter( kIOFWStatsKey );
	if ( !nd || !(fpEtherStats = (IOFWStats*)nd->getBuffer()) )
	{
		IOLog( "IOFireWireIP::configureInterface - invalid ethernet statistics\n" );
        return false;
	}

	//IOLog("IOFireWireIP: configureInterface - end\n");

    /*
     * Set the driver/stack reentrancy flag. This is meant to reduce
     * context switches. May become irrelevant in the future.
     */
    return true;
	
} // end configureInterface

void IOFireWireIP::receivePackets(void * pkt, UInt32 pkt_len, UInt32 options)
{
	if(options == true)
		fPacketsQueued = true;

    IOTakeLock(ipLock);
		
	networkInterface->inputPacket((struct mbuf*)pkt, pkt_len, options);
	networkStatAdd(&fpNetStats->inputPackets);
	
    IOUnlock(ipLock);
}

IOOutputQueue* IOFireWireIP::createOutputQueue()
{
	return IOGatedOutputQueue::withTarget(this, getWorkLoop(), TRANSMIT_QUEUE_SIZE);
}

/*-------------------------------------------------------------------------
 * Override IONetworkController::createWorkLoop() method and create
 * a workloop.
 *-------------------------------------------------------------------------*/
bool IOFireWireIP::createWorkLoop()
{
	workLoop = fDevice->getController()->getWorkLoop();
	
    return ( workLoop != 0 );
} // end createWorkLoop


// Override IOService::getWorkLoop() method to return our workloop.
IOWorkLoop* IOFireWireIP::getWorkLoop() const
{
	//IOLog("IOFireWireIP: getWorkLoop - end %p \n", workLoop);

    return workLoop;
} // end getWorkLoop 

UInt32 IOFireWireIP::outputPacket(struct mbuf * pkt, void * param)
{
	// Do the branching between ARP/IPV4/IPV6
	register struct firewire_header *fwh;
	int	status = kIOReturnSuccess;
	UInt16	type;
	BOOLEAN	bRet = false;

	fwh = mtod(pkt, struct firewire_header*);
	type = htons(fwh->ether_type);
	
	switch(type)
	{
		case ETHERTYPE_IPV6:
			bRet = addNDPOptions(pkt);
			if(bRet == true)
			{
				// IOLog("options updated\n"); 
			}
		case ETHERTYPE_IP:
			status = txIP(ifp, pkt, type);
			break;

		case ETHERTYPE_ARP:
			txARP(ifp, pkt);
		
		default :
			freePacket(pkt);
			break;
	}

	if(status == kIOFireWireOutOfTLabels)
	{
		// IOLog("IOFWIP: kIOFireWireOutOfTLabels\n");
		fTxStatus = kIOReturnOutputStall;
		status = kIOReturnOutputStall;
	}
	else
	{
		status = kIOReturnOutputSuccess;
	}

    return status;
} // end outputPacket

/*-------------------------------------------------------------------------
 * Called by IOEthernetInterface client to enable the controller.
 * This method is always called while running on the default workloop
 * thread.
 *-------------------------------------------------------------------------*/
IOReturn IOFireWireIP::enable(IONetworkInterface * netif)
{
    /*
     * If an interface client has previously enabled us,
     * and we know there can only be one interface client
     * for this driver, then simply return true.
     */
    if (netifEnabled)
    {
        IOLog("IOFireWireIP: already enabled\n");
        return kIOReturnSuccess;
    }

	// IOLog("IOFireWireIP: enable \n");
    /*
     * Mark the controller as enabled by the interface.
     */
    netifEnabled = true;

	/*
     * Initialize the IP command pool.
     */
//	initIPCmdPool();
	
	
	/*
     * Start our IOOutputQueue object.
     */
    transmitQueue->setCapacity( TRANSMIT_QUEUE_SIZE );
    transmitQueue->start();
	
    return kIOReturnSuccess;
	
}// end enable netif


/*-------------------------------------------------------------------------
 * Called by IOEthernetInterface client to disable the controller.
 * This method is always called while running on the default workloop
 * thread.
 *-------------------------------------------------------------------------*/
IOReturn IOFireWireIP::disable(IONetworkInterface * /*netif*/)
{
    netifEnabled = false;

	// IOLog("IOFireWireIP: disable \n");

	/*
     * Free the IP command pool.
     */
	freeIPCmdPool();
	
	/*
     * Disable our IOOutputQueue object. This will prevent the
     * outputPacket() method from being called.
     */
    transmitQueue->stop();

    /*
     * Flush all packets currently in the output queue.
     */
    transmitQueue->setCapacity( 0 );
    transmitQueue->flush();

    return kIOReturnSuccess;

}// end disable netif


IOReturn IOFireWireIP::getPacketFilters( const OSSymbol	*group, UInt32 *filters ) const
{
	//IOLog("IOFireWireIP: getPacketFilters \n");

	return super::getPacketFilters( group, filters );

}// end getPacketFilters


IOReturn IOFireWireIP::setWakeOnMagicPacket( bool active )
{
	//IOLog("IOFireWireIP: setWakeOnMagicPacket \n");

	return kIOReturnSuccess;

}// end setWakeOnMagicPacket

const OSString * IOFireWireIP::newVendorString() const
{
	//IOLog("IOFireWireIP: newVendorString \n");
	
    return OSString::withCString("Apple");
}

const OSString * IOFireWireIP::newModelString() const
{
	//IOLog("IOFireWireIP: newModelString \n");

    return OSString::withCString("fw+");
}

const OSString * IOFireWireIP::newRevisionString() const
{
	//IOLog("IOFireWireIP: newRevisionString \n");
	
    return OSString::withCString("");

}

IOReturn IOFireWireIP::setPromiscuousMode( bool active )
{

	//IOLog("IOFireWireIP: setPromiscuousMode \n");

	isPromiscuous	= active;

	return kIOReturnSuccess;

} // end setPromiscuousMode

IOReturn IOFireWireIP::setMulticastMode( bool active )
{
	//IOLog("IOFireWireIP: setMulticastMode \n");
	
	multicastEnabled = active;

	return kIOReturnSuccess;
}/* end setMulticastMode */

// FIREWIRETODO
IOReturn IOFireWireIP::setMulticastList(IOFWAddress *addrs, UInt32 count)
{
#ifdef FIREWIRETODO	
	IOLog("IOFireWireIP: +setMulticastList count = %lx \n", count);

    for (UInt32 i = 0; i < count; i++) 
    {
		if(addrs != NULL)
		{
			IOLog(" mca.addr[%lx] %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", 
							i, addrs->bytes[0], addrs->bytes[1], addrs->bytes[2], addrs->bytes[3], 
							addrs->bytes[4], addrs->bytes[5], addrs->bytes[6], addrs->bytes[7]);
			addrs++;
		}
    }

	IOLog("IOFireWireIP: -setMulticastList \n");
#endif
	
    return kIOReturnSuccess;
}

/*!
	@function registerWithPolicyMaker
	@abstract Initialize the driver for power management and register
			  ourselves with policy-maker.
	@param none.
	@result Returns 0 or 1.
*/
IOReturn IOFireWireIP::registerWithPolicyMaker(IOService *policyMaker)
{
	IOReturn	rc;
	
	fPolicyMaker = policyMaker;
//  May be can uncomment this portion when we have native Asyncstream receive support
//	if (fBuiltin)
		rc = policyMaker->registerPowerDriver( this, ourPowerStates, kNumOfPowerStates );
//	else 
//		rc = super::registerWithPolicyMaker( policyMaker );	// return unsupported

	return rc;
}

/*!
	@function maxCapabilityForDomainState
	@abstract returns the maximum state of card power, which would be
			  power on without any attempt to power manager.
	@param none.
	@result Returns 0 or 1.
*/
unsigned long IOFireWireIP::maxCapabilityForDomainState( IOPMPowerFlags domainState )
{
	if (domainState & IOPMPowerOn)
		return kNumOfPowerStates - 1;

	return 0;
}

/*!
	@function initialPowerStateForDomainState
	@abstract The power domain may be changing state.	If power is on in the new
			  state, that will not affect our state at all.  If domain power is off,
			  we can attain only our lowest state, which is off.
	@param none.
	@result Returns 0 or 1.
*/
unsigned long IOFireWireIP::initialPowerStateForDomainState( IOPMPowerFlags domainState )
{
	if (domainState & IOPMPowerOn)
	   return kNumOfPowerStates - 1;

	return 0;
}

/*!
	@function powerStateForDomainState
	@abstract The power domain may be changing state.	If power is on in the new
			  state, that will not affect our state at all.  If domain power is off,
			  we can attain only our lowest state, which is off.
	@param none.
	@result Returns 0 or 1.
*/
unsigned long IOFireWireIP::powerStateForDomainState(IOPMPowerFlags domainState )
{
	if (domainState & IOPMPowerOn)
		return 1;
							
	return 0;
}


/*!
	@function setPowerState
	@abstract 
	@param none.
	@result Returns IOPMAckImplied or IOPMNoSuchState.
*/
IOReturn IOFireWireIP::setPowerState(unsigned long	powerStateOrdinal,
									IOService		*whatDevice)
{
	IOReturn status = kIOReturnSuccess;

	// Do nothing if state invalid
	if (powerStateOrdinal >= kNumOfPowerStates)
		return IOPMNoSuchState;						

	//IOLog("IOFireWireIP: setPowerState \n");
	
	// Fix to TiPBG4 sleep as soon as booted
	if(unitCount == 0)
		return IOPMAckImplied;
	
	if ( powerStateOrdinal == 0 )
    {
		//IOLog(" IOFireWireIP: powering off\n");
		status = stopAsyncStreamReceiveClients();
	}
	else if ( powerStateOrdinal == 1 )
    {
		//IOLog(" IOFireWireIP: powering on\n");
		status = startAsyncStreamReceiveClients();
	}
	else
		IOLog("IOFireWireIP: unknown powerStateOrdinal \n");

	return IOPMAckImplied;
}

/*!
	@function createMediumState
	@abstract 
	@param none.
	@result create a supported medium information and 
			attach to the IONetworkingFamily.
*/
bool IOFireWireIP::createMediumState()
{
	OSDictionary * mediumDict = OSDictionary::withCapacity(5);
	if (!mediumDict) 
	{
		return false;
	}
	
	IONetworkMedium * medium = IONetworkMedium::medium(
								(IOMediumType)kIOMediumEthernetAuto | 
								kIOMediumOptionFullDuplex, fLcb->maxBroadcastSpeed);
	if (medium) 
	{
		mediumDict->setObject(medium->getKey(), medium);
		setCurrentMedium(medium);
		medium->release();
	}
			
	if (!publishMediumDictionary(mediumDict)) 
	{
		return false;
	}
	
	if( mediumDict )
	{
		mediumDict->release();
	}

	// Link status is Valid and inactive
	return setLinkStatus( kIONetworkLinkValid, getCurrentMedium(), 0 ); 
}

#pragma mark -
#pragma mark еее IOFirewireIP methods еее

/*!
	@function builtIn
	@abstract checks whether the FWIM is for builtin H/W, if not 
			  sets appropriate performance related params
	@param none.
	@result Returns void.
*/
void IOFireWireIP::builtIn()
{
    IORegistryEntry* parent = fControl->getParentEntry(gIOServicePlane); 
//	IOLog("controllers parent %s\n", parent->getName(gIOServicePlane));

	if(strcmp(parent->getName(gIOServicePlane), "AppleLynx") == 0)
	{
		bOnLynx = true;
		fMaxRxIsocPacketSize = 2048;
		fMaxTxAsyncDoubleBuffer =  1 << 9;
	} 
	else
	{
		bOnLynx = false;
		fMaxRxIsocPacketSize = 4096;
		fMaxTxAsyncDoubleBuffer = 1 << fDevice->maxPackLog(true);
	}

    parent = parent->getParentEntry(gIOServicePlane);
//	IOLog("FWIM's parent %s\n", parent->getName(gIOServicePlane));
//	fBuiltin = true;
}

/*!
	@function initAsyncCmdPool
	@abstract constructs Asynchronous command objects and queues them in the pool
	@param none.
	@result Returns kIOReturnSuccess if it was successful, else kIOReturnNoMemory.
*/
UInt32 IOFireWireIP::initAsyncCmdPool()
{
    IOFWIPAsyncWriteCommand		*cmd1 = NULL;
    IOReturn status = kIOReturnSuccess;
    int		i = 0;

    // IOLog("IOFireWireIP: initAsyncCmdPool+\n");

	if(fAsyncCmdPool == NULL)
	{
		// Create a command pool
		fAsyncCmdPool = IOCommandPool::withWorkLoop(getWorkLoop());
	}
		
    for(i=0; i<=MAX_ASYNC_WRITE_CMDS; i++){
        
        FWAddress addr;
        // setup block write
        addr.addressHi   = 0xdead;
        addr.addressLo   = 0xbabeface;
        
        // Create a IP Async write command 
        cmd1 = new IOFWIPAsyncWriteCommand;
        if(!cmd1) {
            status = kIOReturnNoMemory;
            break;
        }

        // Initialize the write command
        if(!cmd1->initAll(fDevice, fMaxTxAsyncDoubleBuffer, addr, txCompleteBlockWrite, this, false)) {
            status = kIOReturnNoMemory;
			cmd1->release();
            break;
        }

        // Queue the command in the command pool
        fAsyncCmdPool->returnCommand(cmd1);
    }
	
    // IOLog("IOFireWireIP: initAsyncCmdPool-\n");
	
    return status;
}

/*!
	@function initAsyncStreamCmdPool
	@abstract constructs AsyncStreamcommand objects and queues them in the pool
	@param none.
	@result Returns kIOReturnSuccess if it was successful, else kIOReturnNoMemory.
*/
UInt32 IOFireWireIP::initAsyncStreamCmdPool()
{
    IOReturn status = kIOReturnSuccess;
    int		i = 0;

    // IOLog("IOFireWireIP: initAsyncStreamCmdPool+\n");
	if(fAsyncStreamTxCmdPool == NULL)
	{
		fAsyncStreamTxCmdPool = IOCommandPool::withWorkLoop(getWorkLoop());
	}
    
	IOFWIPAsyncStreamTxCommand *cmd2 = NULL;
	
	for(i=0; i<=MAX_ASYNCSTREAM_TX_CMDS; i++){
        
        // Create a IP Async write command 
        cmd2 = new IOFWIPAsyncStreamTxCommand;
        if(!cmd2) {
            status = kIOReturnNoMemory;
            break;
        }

        // Initialize the write command
        if(!cmd2->initAll(fControl, fLcb->busGeneration, 0, 0, GASP_TAG, fMaxTxAsyncDoubleBuffer, 
						fLcb->maxBroadcastSpeed, txCompleteAsyncStream, this)) {
            status = kIOReturnNoMemory;
			cmd2->release();
            break;
        }

        // Queue the command in the command pool
        fAsyncStreamTxCmdPool->returnCommand(cmd2);
    }
	
    // IOLog("IOFireWireIP: initAsyncStreamCmdPool-\n");
	
    return status;
}


										
/*!
	@function freeIPCmdPool
	@abstract frees both Async and AsyncStream command objects from the pool
	@param none.
	@result void.
*/
void IOFireWireIP::freeIPCmdPool()
{
    IOFWIPAsyncWriteCommand	*cmd1 = NULL;
	UInt32 freeCount = 0;

    // IOLog("IOFireWireIP: freeIPCmdPool+\n");
    
    if(fAsyncCmdPool == NULL)
        return;
	
	// Should block till all outstanding commands are freed
    do
	{
        cmd1 = (IOFWIPAsyncWriteCommand*)fAsyncCmdPool->getCommand(false);
        if(cmd1 != NULL)
		{
			freeCount++;
            // release the command
            cmd1->release();
        }
    }while(cmd1 != NULL);
    
	fAsyncCmdPool->release();
	fAsyncCmdPool = NULL;

	if(fAsyncStreamTxCmdPool == NULL)
		return;

	IOFWIPAsyncStreamTxCommand *cmd2 = NULL;
	freeCount = 0;
	
	// Should block till all outstanding commands are freed
	do{
        cmd2 = (IOFWIPAsyncStreamTxCommand*)fAsyncStreamTxCmdPool->getCommand(false);
        if(cmd2 != NULL)
		{
			freeCount++;
            // release the command
            cmd2->release();
        }
    }while(cmd2 != NULL);
    
	fAsyncStreamTxCmdPool->release();
	fAsyncStreamTxCmdPool = NULL;

    // IOLog("IOFireWireIP: freeIPCmdPool-\n");
	
    return;
}



IOReturn IOFireWireIP::stopAsyncStreamReceiveClients()
{
	if(!fBroadcastReceiveClient)
		return kIOReturnSuccess;
		
	return fBroadcastReceiveClient->stop();
}

IOReturn IOFireWireIP::startAsyncStreamReceiveClients()
{
	if(!fBroadcastReceiveClient)
		return kIOReturnSuccess;

	return fBroadcastReceiveClient->start(fLcb->maxBroadcastSpeed);
}

/*!
	@function createIPConfigRomEntry
	@abstract creates the config rom entry for IP over Firewire.
	@param 	none
	@result IOReturn - kIOReturnSuccess or error if failure.
*/
IOReturn IOFireWireIP::createIPConfigRomEntry()
{
	IOReturn ioStat = kIOReturnSuccess;
    IP1394_HDW_ADDR	hwAddr;
//	UInt8 *a;
	
    // Create entries for UnitSpecID and UnitSwVersion
    fLocalIP1394ConfigDirectory = IOLocalConfigDirectory::create();

    if (!fLocalIP1394ConfigDirectory){
        ioStat =  kIOReturnError;
    } 
	
    if(ioStat == kIOReturnSuccess)
        ioStat = fLocalIP1394ConfigDirectory->addEntry(kConfigUnitSpecIdKey, IP1394_SPEC_ID) ;

    if(ioStat == kIOReturnSuccess)
        ioStat = fLocalIP1394ConfigDirectory->addEntry(kConfigUnitSwVersionKey, IP1394_VERSION) ;

    if(ioStat == kIOReturnSuccess){
		CSRNodeUniqueID	fwuid = fDevice->getUniqueID();

        hwAddr.eui64.hi = (UInt32)(fwuid >> 32);
        hwAddr.eui64.lo = (UInt32)(fwuid & 0xffffffff);
		//bcopy(macAddr, &hwAddr.eui64, FIREWIRE_ADDR_LEN);
#ifdef FIREWIRETODO         
        // Add Mac specific info in the unit directory
		a = (UInt8*)&hwAddr.eui64.hi;
		a[0] = macAddr[0]; 
		a[1] = macAddr[1]; 
		a[2] = macAddr[2]; 
		a[3] = macAddr[3];

		a = (UInt8*)&hwAddr.eui64.lo;
		a[0] = macAddr[4]; 
		a[1] = macAddr[5]; 
		a[2] = macAddr[6]; 
		a[3] = macAddr[7];
#endif
		
        hwAddr.maxRec	= fDevice->maxPackLog(true);              
        hwAddr.spd		= fDevice->FWSpeed();
                         
        hwAddr.unicastFifoHi = fIP1394Address.addressHi;      
        hwAddr.unicastFifoLo = fIP1394Address.addressLo;
        
        // IOLog("IOFireWireIP GUID = 0x%lx:0x%lx \nmaxRec  = %d \nspeed   = %d  \nFifo Hi = 0x%x\nFifo Lo = 0x%lx\n",
        //      hwAddr.eui64.hi, hwAddr.eui64.lo, hwAddr.maxRec,
        //      hwAddr.spd, hwAddr.unicastFifoHi, hwAddr.unicastFifoLo);
        
        // Get a OSData created for storing the whole structure in the config rom .:)
        fwOwnAddr = OSData::withBytes(&hwAddr, sizeof(IP1394_HDW_ADDR));
        
        if(fwOwnAddr != NULL){
            // Create the mac specific data key in the config rom
            ioStat = fLocalIP1394ConfigDirectory->addEntry(kConfigUnitDependentInfoKey, 
                                                           fwOwnAddr, NULL);
        }
    }
    // lets publish it
    if(ioStat == kIOReturnSuccess)
        ioStat = fControl->AddUnitDirectory(fLocalIP1394ConfigDirectory) ;

    // Create entries for IPv6 UnitSpecID and UnitSwVersion
    fLocalIP1394v6ConfigDirectory = IOLocalConfigDirectory::create();

    if (!fLocalIP1394v6ConfigDirectory){
        ioStat =  kIOReturnError;
    } 

	if(ioStat == kIOReturnSuccess)
		ioStat = fLocalIP1394v6ConfigDirectory->addEntry(kConfigUnitSpecIdKey, IP1394_SPEC_ID) ;
	
	if(ioStat == kIOReturnSuccess)
		ioStat = fLocalIP1394v6ConfigDirectory->addEntry(kConfigUnitSwVersionKey, IP1394v6_VERSION) ;

	if(fwOwnAddr != NULL)
	{
		// Create the mac specific data key in the config rom
		ioStat = fLocalIP1394v6ConfigDirectory->addEntry(kConfigUnitDependentInfoKey, 
																		fwOwnAddr, NULL);
	}

    if(ioStat == kIOReturnSuccess)
        ioStat = fControl->AddUnitDirectory(fLocalIP1394v6ConfigDirectory) ;

	memcpy(&fLcb->ownHardwareAddress, &hwAddr, sizeof(IP1394_HDW_ADDR)); 
    // IOLog("IOFireWireIP start status %d, specId %x swversion %x\n",
    //                               ioStat, IP1394_SPEC_ID, IP1394_VERSION );
    
    // end of unit directory addition
	return ioStat;
}

/*!
	@function createDrbWithDevice
	@abstract create device reference block for a device object.
	@param lcb - the firewire link control block for this interface.
	@param eui64 - global unique id of a device on the bus.
	@param fDevObj - IOFireWireNub that has to be linked with the device reference block.
	@param itsMac - Indicates whether the destination is Macintosh or not.
	@result DRB* - pointer to the device reference block.
*/
DRB *IOFireWireIP::createDrbWithDevice(LCB *lcb, UWIDE eui64, IOFireWireNub *fDevObj, bool itsMac)
{
	DRB   *drb = NULL;
	CSRNodeUniqueID fwuid;
	
    // Returns DRB if EUI-64 matches
    drb = getDrbFromEui64(lcb, eui64);   

    // Device reference ID already created
    if (drb != NULL)
	{    
		// IOLog("   DRB Exists --- clear the timer --- \n");
		drb->deviceID = (UInt32)fDevObj;
		
		// get the 64 bit address
		fwuid = fDevObj->getUniqueID();
		if(itsMac)
			makeEthernetAddress(&fwuid, drb->fwaddr, GUID_TYPE);
		else
			getBytesFromGUID((void*)(&fwuid), drb->fwaddr, GUID_TYPE);

        drb->timer = 0;          
		drb->itsMac = itsMac;
		
        // Just return it to caller
        return drb;        
    }
	else if ((drb = (DRB*)allocateCBlk(lcb)) == NULL)
	{    // Get an empty DRB
    	IOLog("   No DRB's - failure: \n");
        return NULL;
	}
	else
	{                         // Success! Initialixe the DRB...
        drb->deviceID = (UInt32)fDevObj;

		// get the 64 bit address
		fwuid = fDevObj->getUniqueID();
		if(itsMac)
			makeEthernetAddress(&fwuid, drb->fwaddr, GUID_TYPE);
		else
			getBytesFromGUID((void*)(&fwuid), drb->fwaddr, GUID_TYPE);
			
		
        drb->timer	= 0;
        drb->lcb	= lcb;
        drb->eui64	= eui64;
		drb->itsMac = itsMac;

        // In case of failure below
        drb->maxSpeed = kFWSpeed100MBit;      
        drb->maxSpeed = fDevObj->FWSpeed();
        //
        // When the packet is transmitted, it will pick the smaller of this value or the 
        // maxRec from the device's ARP.	
	    //
		drb->maxPayload = fDevObj->maxPackLog(true);
		
		linkCBlk(&lcb->activeDrb, drb);
	}
	
    return drb;
}

/*!
	@function createIPFifoAddress
	@abstract creates the pseudo address space for IP over Firewire.
	@param 	UInt32 fifosize - size of the pseudo address space
	@result IOReturn - kIOReturnSuccess or error if failure.
*/
IOReturn IOFireWireIP::createIPFifoAddress(UInt32 fifosize)
{
    IOReturn		ioStat = kIOReturnSuccess;
	/// UInt32			ptr = (UInt32)&fIP1394Address;
	
	// IOLog("fIP1394Address %lX %d\n", ptr, __LINE__);
										
	// add  csr address space
    fIP1394AddressSpace = fControl->createPseudoAddressSpace(&fIP1394Address, fifosize,
                                                            NULL,
                                                            &rxUnicast,
                                                            this);
    if (fIP1394AddressSpace == NULL){
        IOLog("IOFireWireIP PseudoAddressSpace failure status %d\n", ioStat);
        ioStat = kIOReturnNoMemory;
    }

    if(ioStat == kIOReturnSuccess ){
		// change for performance, coalescing incoming writes
		fIP1394AddressSpace->setARxReqIntCompleteHandler(this, &rxUnicastComplete);
	}

    if(ioStat == kIOReturnSuccess ){
        ioStat = fIP1394AddressSpace->activate();
    }
    
    if(ioStat != kIOReturnSuccess){
        IOLog("IOFireWireIP PseudoAddressSpace Activate failure status %d\n", ioStat);
    }
    // end of csr address space
	
	return ioStat;
}

 
/*!
	@function getMTU
	@abstract returns the MTU (Max Transmission Unit) supported by the IOFireWireIP.
	@param None.
	@result UInt32 - MTU value.
*/
UInt32 IOFireWireIP::getMTU()
{
    UInt32	mtu = 0;
    
	mtu = FIREWIRE_MTU;

    return mtu;
}

/*!
	@function getMacAddress
	@abstract returns the mac address of size "len".
	@param srcBuf - source buffer that can hold the macaddress.
	@param len - source buffer size.
	@result UInt32 - size copied.
*/
void *IOFireWireIP::getMacAddress(char	*srcBuf, UInt32 len)
{
	return memcpy(srcBuf, macAddr, FIREWIRE_ADDR_LEN);
}

/*!
	@function setIPAddress
	@abstract sets the ipaddress for the link control block.
	@param in_addr *sip - ipaddress contained in the in_addr structure.
	@result void.
*/
void IOFireWireIP::setIPAddress(register struct in_addr *sip)
{
	fLcb->ownIpAddress = sip->s_addr;
}

/*!
	@function getBytesFromGUID
	@abstract constructs byte array from the GUID.
	@param fwuid - GUID of the node.
	@param bufAddr - pointer to the buffer.
	@result void.
*/
void IOFireWireIP::getBytesFromGUID(void *guid, u_char *bufAddr, UInt8 type)
{
	u_long lo=0, hi=0;

	if(type == GUID_TYPE)
	{
		CSRNodeUniqueID	*fwuid = (CSRNodeUniqueID*)guid;
	    hi	=	(u_long)(*fwuid >> 32);
		lo	=	(u_long)(*fwuid & 0xffffffff);
	}
	else
	{
		UWIDE *eui64 = (UWIDE*)guid;
		hi	=	eui64->hi;
		lo	=	eui64->lo;
	}

	bufAddr[0] = (unsigned char)((hi >> 24) & 0x000000ff);
	bufAddr[1] = (unsigned char)((hi >> 16) & 0x000000ff);
    bufAddr[2] = (unsigned char)((hi >> 8) & 0x000000ff);
    bufAddr[3] = (unsigned char)((hi) & 0x000000ff);
	
	bufAddr[4] = (unsigned char)((lo >> 24) & 0x000000ff);
	bufAddr[5] = (unsigned char)((lo >> 16) & 0x000000ff);
	bufAddr[6] = (unsigned char)((lo >> 8) & 0x000000ff);
    bufAddr[7] = (unsigned char)((lo) & 0x000000ff);
}

/*!
	@function makeEthernetAddress
	@abstract constructs mac address from the GUID.
	@param fwuid - GUID of the node.
	@param bufAddr - pointer to the buffer.
	@param vendorID - vendorID.
	@result void.
*/
void IOFireWireIP::makeEthernetAddress(CSRNodeUniqueID	*fwuid, u_char *bufAddr, UInt32 vendorID)
{
#ifdef FIREWIRETODO
	//
	// Update the guid array for every addition of new Vendor ID 
	//
	UInt8 guids[][6] = {{0x00,0x30,0x65},
						{0x00,0x03,0x93},
						{0x00,0x0a,0x27},
						{0x00,0x05,0x02},
						{0x00,0x50,0xE4},
						{0x00,0x0A,0x95}};
	bool bFound = false;
#endif
        
	getBytesFromGUID(fwuid, bufAddr, GUID_TYPE);

#ifdef FIREWIRETODO
	// Work around the sharing of GUID across ethernet and firewire
	for (UInt32 i = 0; i <= LAST(guids); i++)
		if (memcmp(bufAddr, guids[i], 3) == 0) 
		{  
			// if last one roll back
			(LAST(guids) == i)?i = 0:i++;
				
			// reset to next possible address
			memcpy(bufAddr, guids[i], 3);
			bFound = true;
			break;
		}
	
	if(!bFound)
		memcpy(bufAddr, guids[2], 3);
#endif		
		
}

/*!
	@function deviceAttach
	@abstract Callback for a Unit attached of type IP1394
	@param target - callback data.
	@param refcon - callback data.
	@param newService - handle to the new IP1394 unit created.
	@result bool.
*/
bool IOFireWireIP::deviceAttach(void * target, void * refCon, IOService * newService)
{
    IOFireWireIP *fwIPObject;
    IOFireWireIPUnit *fwIPunit;
    IOFireWireNub	*fDevice = NULL;
    UWIDE eui64;
    ARB *arb;
	DRB *drb;
//	UInt32 unitType = (UInt32)refCon;
	u_char	lladdr[FIREWIRE_ADDR_LEN];
    
	if(target == NULL || newService == NULL)
		return false;

    // IOLog(" IOFireWireIP:: device attach\n");
	fwIPObject = OSDynamicCast(IOFireWireIP, (IOService *)target);
    fwIPunit = OSDynamicCast(IOFireWireIPUnit, (IOService *)newService);
	    
    if(!fwIPObject)
        return false;

    // Create the device reference block for this IP device
    fDevice = fwIPunit->getDevice();
    if(!fDevice){
        IOLog("Colonel ! we have problem !! Null device for legal unit %p\n", fwIPunit);
        return false;
    }

	//
	// If controller does not match the localnode controller then ignore the attach
	// Usefull for multiple firewire link interfaces 
	//
	if(fwIPObject->fControl != fDevice->getController())
		return false;

	// if(unitType == IP1394_VERSION)
	//	IOLog("IPv4 Unit\n");

	// if(unitType == IP1394v6_VERSION)
	//	IOLog("IPv6 Unit\n");
    
	fwIPunit->setLocalNode(fwIPObject);
	
    CSRNodeUniqueID	fwuid = fDevice->getUniqueID();

	// If other unit is Macitosh, then do the OUI rotation, else don't touch  
	if(fwIPunit->isSpecial())
	{
		fwIPObject->makeEthernetAddress(&fwuid, lladdr, GUID_TYPE);
		bcopy((void*)lladdr, &eui64, FIREWIRE_ADDR_LEN);
	}
	else
	{
		eui64.hi = (UInt32)(fwuid >> 32);
		eui64.lo = (UInt32)(fwuid & 0xffffffff);
	}
	
	
    drb = fwIPObject->createDrbWithDevice(fwIPObject->fLcb, eui64, fDevice, fwIPunit->isSpecial());
    
    if(drb == NULL){
        IOLog("No memory !, Can't create Device reference block\n\r");
		return false;
    }
	
	// Update the device reference block in IP Unit
	fwIPunit->setDrb(drb);

    // Create the arb if we recognise a IP unit.
    arb = fwIPObject->getArbFromEui64(fwIPObject->fLcb,  eui64);

	// Update the device object in the address resolution block used in the ARP resolve routine
    if(arb != NULL)
	{
		arb->handle.unicast.deviceID	= (UInt32)fDevice;
		arb->handle.unicast.maxRec		= fDevice->maxPackLog(true); 
		arb->handle.unicast.spd			= fDevice->FWSpeed();
		arb->timer	= 0;
		arb->itsMac = fwIPunit->isSpecial();
    }
	
	if(!fwIPunit->getUnitState())
	{
		fwIPObject->unitCount++;
		fwIPunit->setUnitState(true);
	}
		
	fwIPObject->updateBroadcastValues(false);

	// IOLog("attach unit count %d\n", fwIPObject->unitCount);

	//	IOLog(" DRB for 0x%08lX%08lX and linked for controller %p !!!\n\r", 
	//	eui64.hi, eui64.lo,	
	//	fDevice->getController());
	fwIPObject->updateLinkStatus();

    return true;
}


/*!
	@function deviceDetach
	@abstract Callback for a Unit detach of type IP1394 - Invoked from 
			  IOFireWireIPunit::message(....)
	@param target - callback data.
	@result void.
*/	
void IOFireWireIP::deviceDetach(void *target)
{
    IOFireWireIPUnit	*fwIPUnit = (IOFireWireIPUnit *)target;
    DRB					*drb = NULL; 
	ARB					*arb = NULL;
	int					arpt_keep = (30*60); // lets wait for 30 mins safe !

	drb = fwIPUnit->getDrb();
	
	//IOLog("IOFireWireIP: device detach called + \n");

    if(drb != NULL)
	{
        drb->timer = arpt_keep;
		arb = getArbFromFwAddr(fLcb, drb->fwaddr);
		if(arb != NULL)
			arb->timer = arpt_keep;
    }
	
	if(unitCount > 0)
	{
		unitCount--;
		//IOLog("detach unit count %d\n", unitCount);
	}
	
	if(unitCount == 0)
	{
		// Link status is Valid and inactive
		setLinkStatus( kIONetworkLinkValid, getCurrentMedium(), 0 ); 
		//IOLog("...fw media inactive\n");
	}

	//IOLog("IOFireWireIP: device detach called \n");
	
	updateBroadcastValues(true);
		
	updateLinkStatus();
}

/*!
	@function updateBroadcastValues
	@abstract Updates the max broadcast payload and speed  
	@param reset - useful to know whether to start from beginning.
	@result void.
*/	
void IOFireWireIP::updateBroadcastValues(bool reset)
{
    DRB	*drb = NULL;
    CBLK *cBlk = NULL;
	IOFireWireDevice *remoteDevice = NULL;
	
	if(reset)
	{
		fLcb->maxBroadcastPayload = fDevice->maxPackLog(true);
		fLcb->maxBroadcastSpeed = fDevice->FWSpeed();
	}
		
	IOTakeLock(ipLock);
	
	// Display the active DRB
	if (fLcb->activeDrb == NULL)
	{
		//IOLog(" No active devices\n\r");
	}
	else 
	{
		// IOLog(" Active devices \n\r");
		cBlk = (CBLK*)&fLcb->activeDrb;
		
		while ((cBlk = cBlk->next) != NULL)
		{ 
			// IOLog("  %p\n\r", cBlk);
			drb = (DRB*)cBlk;
			
			// Disappeared device, so lets skip
			if(drb->timer > 0)
				continue;
			
			remoteDevice = (IOFireWireDevice*)drb->deviceID;
			// drb->maxSpeed = fDevice->FWSpeed(remoteDevice);
			// drb->maxPayload = 1 << fDevice->maxPackLog(true, remoteDevice);
			
			// IOLog("maxSpeed = %d & maxPayload = %d \n", drb->maxSpeed, drb->maxPayload);
				
			if(fLcb->maxBroadcastSpeed > drb->maxSpeed)
				fLcb->maxBroadcastSpeed = drb->maxSpeed;
			
			if(fLcb->maxBroadcastPayload > drb->maxPayload)
				fLcb->maxBroadcastPayload = drb->maxPayload;
		}
	}
   
    IOUnlock(ipLock);
}

/*!
	@function updateLinkStatus
	@abstract Updates the link status based on maxbroadcast speed & payload.  
	@param None.
	@result void.
*/	
void IOFireWireIP::updateLinkStatus()
{

	// lets update the link status
	if(getUnitCount() == 0)
	{
		// IOLog("setLinkStatus inactive \n");

		// set medium inactive
		setLinkStatus(kIONetworkLinkValid, getCurrentMedium(), 0); 
	}
	else
	{
		// IOLog("setLinkStatus %d \n", (1 << fLcb->maxBroadcastSpeed) * 100 * 1000000);

		// <fix> 
		// set medium inactive, before setting it to active for radar 3300357
		setLinkStatus(kIONetworkLinkValid, getCurrentMedium(), 0); 
		// </fix>

		// set medium active
		setLinkStatus (kIONetworkLinkActive | kIONetworkLinkValid,
						getCurrentMedium(), 
						(1 << fLcb->maxBroadcastSpeed) * 100 * 1000000);
						
	}	
	fLcb->ownHardwareAddress.spd = fLcb->maxBroadcastSpeed;
	// fix to enable the arp/dhcp support from network pref pane
	setProperty(kIOFWHWAddr,  (void *) &fLcb->ownHardwareAddress, sizeof(IP1394_HDW_ADDR));

}
/*!
	@function txComplete
	@abstract Callback for the Async write complete 
	@param refcon - callback data.
    @param status - status of the command.
    @param device - device that the command was send to.
    @param fwCmd - command object which generated the transaction.
	@result void.
*/
void IOFireWireIP::txCompleteBlockWrite(void *refcon, IOReturn status, IOFireWireNub *device, IOFWCommand *fwCmd)
{
    IOFireWireIP			*fwIPObject = (IOFireWireIP *)refcon;
    IOFWIPAsyncWriteCommand *cmd		= (IOFWIPAsyncWriteCommand *)fwCmd;
    struct mbuf * pkt = NULL;
	UInt32	type = UNFRAGMENTED;
	
	//
	// Make sure that we start the queue again,
	// only after we have stalled, not for every 
	// successful completion
	//
	if(fwIPObject->fTxStatus == kIOReturnOutputStall)
	{
		fwIPObject->fStalls++;
		fwIPObject->fTxStatus = kIOReturnOutputSuccess;
	}
	
	//
	// Only in case of kIOFireWireOutOfTLabels, we ignore 
	// freeing of Mbuf
	//
	if(status == kIOReturnSuccess)
	{
		// We get callback 1 packet at a time, so we can increment by 1
		fwIPObject->networkStatAdd(&(fwIPObject->getNetStats())->outputPackets);
		fwIPObject->fTxUni++;
	}
	else 
	{
		// IOLog("IOFireWireIP: txCompleteBlockWrite error %x\n", status);
		// Increment error output packets
		fwIPObject->networkStatAdd(&(fwIPObject->getNetStats())->outputErrors);
		fwIPObject->fCallErrs++;
	}
	
	type = cmd->getLinkFragmentType();
	pkt = cmd->getMbuf();
	if(pkt != NULL)
	{
		if(type == LAST_FRAGMENT || type == UNFRAGMENTED)
		{
			/////
			// If we stalled, don't free Mbuf, the Queue will try retransmit
			/////
			if(status != kIOFireWireOutOfTLabels)
			{
				// free the packet
				fwIPObject->freePacket(pkt);
			}
		}
	}

	cmd->resetDescriptor();

    // Queue the command back into the command pool
	if(fwIPObject->fAsyncCmdPool != NULL)
	{
		fwIPObject->fAsyncCmdPool->returnCommand(cmd);
	}
	else
	{
		IOLog("IOFWIPAsyncWriteCommand got completed after the interface is disabled !!\n");
		cmd->release();
	}

	IOTakeLock(fwIPObject->ipLock);
	fwIPObject->fUsedCmds--;
    IOUnlock(fwIPObject->ipLock);

	//////
	// Fix to over kill servicing the queue 
	//////
	if(status != kIOFireWireOutOfTLabels && status != kIOReturnNoResources && (fwIPObject->fUsedCmds  <= (gLowWaterMark+10)))
	{
		fwIPObject->transmitQueue->service(0x01);
	}
	
    return;
}

/*!
	@function txAsyncStreamComplete
	@abstract Callback for the Async stream transmit complete 
	@param refcon - callback 
	@param status - status of the command.
	@param bus information.
	@param fwCmd - command object which generated the transaction.
	@result void.
*/
void IOFireWireIP::txCompleteAsyncStream(void *refcon, IOReturn status, 
										IOFireWireBus *bus, IOFWAsyncStreamCommand *fwCmd)
{
    IOFireWireIP			*fwIPObject = (IOFireWireIP *)refcon;
    IOFWIPAsyncStreamTxCommand *cmd		= (IOFWIPAsyncStreamTxCommand *)fwCmd;
    struct mbuf * pkt = NULL;
	
	//IOLog("IOFireWireIP: txCompleteAsyncStream called\n");

	if(status == kIOReturnSuccess)
	{
		// We send 1 packet at a time, so we can increment by 1
		fwIPObject->networkStatAdd(&(fwIPObject->getNetStats())->outputPackets);
		fwIPObject->fTxBcast++;
	}
	else
	{
		// Error, so we touch the error output packets
		fwIPObject->networkStatAdd(&(fwIPObject->getNetStats())->outputErrors);
	}
    
	pkt = cmd->getMbuf();
	if(pkt != NULL)
		fwIPObject->freePacket(pkt);
	
	cmd->setMbuf(NULL);

	if(fwIPObject->fAsyncStreamTxCmdPool != NULL)
	{
		// Queue the command back into the command pool
		fwIPObject->fAsyncStreamTxCmdPool->returnCommand(cmd);
	}
	else
	{
		IOLog("IOFWIPAsyncStreamTxCommand got completed after the interface is disabled !!\n");
		cmd->release();
	}
	
    return;
}



/*!
	@function txARP
	@abstract Transmit ARP request or response.
	@param ifp - ifnet pointer.
	@param m - mbuf containing the ARP packet.
	@result void.
*/
void IOFireWireIP::txARP(struct ifnet *ifp, struct mbuf *m){
	struct firewire_header *fwh;
	struct mbuf *n;
	UInt8 *buf;
	UInt32	offset = 0;
	IOFWIPAsyncStreamTxCommand	*cmd = NULL;
	UInt32 cmdLen = 0;
	UInt32 dstBufLen = 0;
	UInt32 ret = 0;
	struct arp_packet *fwa_pkt;

	//IOLog("IOFireWireIP: txARP called\n");
	
	fwh = mtod(m, struct firewire_header *);
        
	n = m;

	if(fAsyncStreamTxCmdPool == NULL)
	{
		initAsyncStreamCmdPool();
	}
	
	// Get an async command from the command pool
	cmd = (IOFWIPAsyncStreamTxCommand*)fAsyncStreamTxCmdPool->getCommand(false);
		
	// Lets not block to get a command, IP may retry soon ..:)
	if(cmd == NULL)
	{
		// Error, so we touch the error output packets
		networkStatAdd(&(getNetStats())->outputErrors);
		goto endtxARP;
	}
		
	// Get the buffer pointer from the command pool
	buf = (UInt8*)cmd->getBufferFromDesc();
	dstBufLen = cmd->getMaxBufLen();
	
	offset = sizeof(struct firewire_header);
	cmdLen = m->m_pkthdr.len - sizeof(struct firewire_header);
	
	// Construct the GASP_HDR and Unfragment header
	fwa_pkt = (struct arp_packet*)(buf);
    bzero((caddr_t)fwa_pkt, sizeof(*fwa_pkt));
    
	// Fill the GASP fields 
    fwa_pkt->gaspHdr.sourceID = htons(fLcb->ownNodeID);
    memcpy(&fwa_pkt->gaspHdr.gaspID, &gaspVal, sizeof(GASP_ID));
	
	// Set the unfragmented header information
    fwa_pkt->ip1394Hdr.etherType = htons(ETHERTYPE_ARP);
	// Modify the buffer pointer
	buf += (sizeof(GASP_HDR) + sizeof(IP1394_UNFRAG_HDR));
	// Copy the arp packet into the buffer
	// n = mbufTobuffer(n, &offset, buf, dstBufLen, cmdLen);
	mbufTobuffer(n, &offset, buf, dstBufLen, cmdLen);
	// Update the length to have the GASP and IP1394 Header
	cmdLen += (sizeof(GASP_HDR) + sizeof(IP1394_UNFRAG_HDR));
	
	// IOLog("  CmdLen %lX BcastSpd %d\n\r", cmdLen, fLcb->maxBroadcastSpeed);
	
	// Initialize the command with new values of device object
	ret = cmd->reinit(fLcb->busGeneration, 
					  DEFAULT_BROADCAST_CHANNEL, 
					  cmdLen,
					  fLcb->maxBroadcastSpeed,
					  txCompleteAsyncStream, 
					  this);
					
	if (ret == kIOReturnSuccess)			
		cmd->submit(true);
			
endtxARP:

	return;
}

/*!
	@function txIP
	@abstract Transmit IP packet.
	@param ifp - ifnet pointer.
	@param m - mbuf containing the IP packet.
	@param type - type of the packet (IPv6 or IPv4).
    @result void.
*/
SInt32 IOFireWireIP::txIP(struct ifnet *ifp, struct mbuf *m, UInt16 type)
{
	struct firewire_header *fwh;
	GASP_HDR *gaspHdr;
	IP1394_ENCAP_HDR *ip1394Hdr;
	struct mbuf *n;
	TNF_HANDLE	*handle;
	UInt8 *datagram;
	UInt8 *buf;
    FWAddress addr;
	UInt16	datagramSize = 0;
	ARB *arb = NULL;
	IOFWIPAsyncWriteCommand	*cmd = NULL;
	IOFWIPAsyncStreamTxCommand *asyncStreamCmd = NULL;
 	UInt32 offset = 0;
	UInt32 dstBufLen = 0; 
	UInt32 maxPayload = 0;
	UInt16 residual = 0;
	UInt16 drbMaxPayload = 0;
	UInt16 dgl = 0;
	BOOLEAN unfragmented;
	UInt16 headerSize = 0;
	UInt16 fragmentOffset = 0;
	UInt16 fragmentSize = 0;
	UInt32 cmdLen = 0;
	UInt32 channel = 0;
	SInt32 status = 0;
	IOFireWireNub *device;
	bool broadcast = false;
	bool fDeferNotify = true;
	bool bTxError = false;	
	
	// IOLog("IOFireWireIP: txIP called\n");
	
	fwh = mtod(m, struct firewire_header *);

	if(bcmp(fwh->ether_dhost, fwbroadcastaddr, FIREWIRE_ADDR_LEN) == 0 ||
	   bcmp(fwh->ether_dhost, multicast, 3) == 0)
	{
		// broadcast/multicast
		broadcast = true;
	}
	else
	{
		// unicast
		arb = getArbFromFwAddr(fLcb, fwh->ether_dhost);
	}
	
	
	// If its not a packet header
	if(!(m->m_flags & M_PKTHDR))
	{
		status = kIOReturnError;
		goto endtxIP;
	}
		
	n = m;
	
	datagram = (UInt8*)n->m_data;
	datagramSize = m->m_pkthdr.len - sizeof(struct firewire_header);

	if(datagramSize > fMaxPktSize)
	{
		fMaxPktSize = datagramSize; 
	} 
	
	// If multicast/broadcast, lets send through via asyncstream packet 
	if(broadcast)
	{
		channel = DEFAULT_BROADCAST_CHANNEL;
		// IOLog("IOFireWireIP: MCAST/BCAST size = %d, type = %x \n", datagramSize, type);
		
		maxPayload = 1 << fLcb->ownMaxPayload;
        maxPayload = MIN((UInt32)1 << fLcb->maxBroadcastPayload, maxPayload);

		// Asynchronous stream datagrams are never fragmented!
		if (datagramSize + sizeof(IP1394_UNFRAG_HDR) > maxPayload)
		{
			status = ENOBUFS;
			goto endtxIP;
		}

		if(fAsyncStreamTxCmdPool == NULL)
		{
			// create a command pool on demand
			initAsyncStreamCmdPool();
		}
		
		// Get an async command from the command pool
		asyncStreamCmd = (IOFWIPAsyncStreamTxCommand*)fAsyncStreamTxCmdPool->getCommand(false);
		
		// Lets not block to get a command, IP may retry soon ..:)
		if(asyncStreamCmd == NULL)
		{
			status = ENOBUFS;
			goto endtxIP;
		}
		
		asyncStreamCmd->setMbuf(m);
		
		// Get the buffer pointer from the command pool
		buf = (UInt8*)asyncStreamCmd->getBufferFromDesc();
		dstBufLen = asyncStreamCmd->getMaxBufLen();
		
		// Get it assigned to the header
		gaspHdr = (GASP_HDR *)buf;
        gaspHdr->sourceID = htons(fLcb->ownNodeID);	
		memcpy(&gaspHdr->gaspID, &gaspVal, sizeof(GASP_ID));
        ip1394Hdr = (IP1394_ENCAP_HDR*)((UInt8*)buf + sizeof(GASP_HDR));
        ip1394Hdr->singleFragment.etherType = htons(type);
		ip1394Hdr->singleFragment.reserved = htons(UNFRAGMENTED);
		
		cmdLen = datagramSize;
		offset = sizeof(struct firewire_header);
		headerSize = sizeof(GASP_HDR) + sizeof(IP1394_UNFRAG_HDR);
		
		// Increment the buffer pointer for the unfrag or frag header
		buf = buf + headerSize;
		
		mbufTobuffer(n, &offset, buf, dstBufLen, cmdLen);

        cmdLen += headerSize;

//		IOLog("IOFireWireIP: pkt size = %d maxbroadcastspeed %d \n", cmdLen, fLcb->maxBroadcastSpeed);

		// Initialize the command with new values of device object
		status = asyncStreamCmd->reinit(fLcb->busGeneration, 
										channel, 
										cmdLen,
										fLcb->maxBroadcastSpeed,
										txCompleteAsyncStream, 
										this);
					  
		if(status == kIOReturnSuccess)
			asyncStreamCmd->submit(true);
		
	}
	else
	{
		// IOLog("IOFireWireIP: UNICAST size = %d, type = %x\n", datagramSize, type);
	
		if(arb == NULL)
		{
			status = EHOSTUNREACH;
			/*
			IOLog("IOFireWireIP: Host unreachabele %d\n", __LINE__);
			IOLog("tgtaddr = %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", 
									fwh->ether_dhost[0], fwh->ether_dhost[1],
									fwh->ether_dhost[2], fwh->ether_dhost[3],
									fwh->ether_dhost[4], fwh->ether_dhost[5],
									fwh->ether_dhost[6], fwh->ether_dhost[7]);
			*/
			goto endtxIP;
		}
	
		// Node had disappeared, but entry exists for specified timer value
		if(arb != NULL && arb->timer > 1) 
		{
			status = EHOSTUNREACH;
			// IOLog("IOFireWireIP: Host unreachabele %d\n", __LINE__);
			goto endtxIP;
		}
	
		handle = &arb->handle; 
	
		if(handle->unicast.deviceID == 0)
		{
			status = EHOSTUNREACH;
			// IOLog("IOFireWireIP: Host unreachabele %d\n", __LINE__);
			goto endtxIP;
		}
			
	
		//
		// Get the actual length of the packet from the mbuf
		//
		residual = datagramSize;
		
		// setup block write
		addr.addressHi   = handle->unicast.unicastFifoHi;
		addr.addressLo   = handle->unicast.unicastFifoLo;
		device = (IOFireWireNub*)handle->unicast.deviceID;
		
		// Calculate the payload and further down will decide the fragmentation based on that
		drbMaxPayload = 1 << device->maxPackLog(true, addr);
		// to test fragmentation ;
		// drbMaxPayload = 1 << 10;
		maxPayload = 1 << fLcb->ownMaxPayload;
		maxPayload = MIN((UInt32)1 << handle->unicast.maxRec, maxPayload);
		maxPayload = MIN(drbMaxPayload, maxPayload);

		// Only fragments use datagram label
		if (!(unfragmented = ((datagramSize + sizeof(IP1394_UNFRAG_HDR)) <= maxPayload)))
		{
			dgl = fLcb->datagramLabel++; 
		}
	  
		// IOLog("txIP: maxpayload %d drbMaxPayload %d\n", maxPayload, drbMaxPayload);

		offset = sizeof(struct firewire_header);
		
		while (residual) 
		{
			if(fAsyncCmdPool == NULL)
			{
				// Create a command pool on demand
				initAsyncCmdPool();
			}
		
			// Get an async command from the command pool
			cmd = (IOFWIPAsyncWriteCommand	*)fAsyncCmdPool->getCommand(false);
		
			// Lets not block to get a command, IP may retry soon ..:)
			if(cmd == NULL) 
			{
				status = ENOBUFS;
				// IOLog("IOFireWireIP: CMD not available %d\n", __LINE__);
				goto endtxIP;
			}
		

			//if(cmd->getRetainCount() > 1)
			//	IOLog("DeQ RTC %X fUsedCmds %d\n", cmd->getRetainCount(), fUsedCmds);

			// Count the successfully dequeued commands
			IOTakeLock(ipLock);
			fUsedCmds++;
		    IOUnlock(ipLock);

			if(fUsedCmds >= gLowWaterMark)
			{
				fDeferNotify = false;
			}
					
			// false - don't copy , if true - copy the packets
			cmd->setMbuf(m, gDoCopy);
			
			// begin fragmentation
			if (unfragmented)
			{
				//
				// Lets point to the m->m_data
				// 
				headerSize = sizeof(IP1394_UNFRAG_HDR);
				cmd->setOffset(offset, true);
				cmd->setHeaderSize(headerSize);
				cmd->setLinkFragmentType(UNFRAGMENTED);

				// All done in one gulp!
				cmdLen = residual;               
				ip1394Hdr = (IP1394_ENCAP_HDR*)cmd->getDescriptorHeader(unfragmented);
				ip1394Hdr->fragment.datagramSize = htons(UNFRAGMENTED);
				ip1394Hdr->singleFragment.etherType = htons(type);
				ip1394Hdr->singleFragment.reserved = 0;
			} 
			else 
			{   
				fTxFragmentPkts++;

				// Get it assigned to the header
				headerSize = sizeof(IP1394_FRAG_HDR);
				cmd->setHeaderSize(headerSize);
				ip1394Hdr = (IP1394_ENCAP_HDR*)cmd->getDescriptorHeader(unfragmented);

				// Distinguish first, interior and last fragments
				cmdLen = MIN(residual, maxPayload - sizeof(IP1394_FRAG_HDR));
				fragmentSize = cmdLen;
				ip1394Hdr->fragment.datagramSize = htons(datagramSize - 1);
				
				if (fragmentOffset == 0) 
				{
					ip1394Hdr->fragment.datagramSize |= htons(FIRST_FRAGMENT << 14);
					ip1394Hdr->singleFragment.etherType = htons(type);
					//
					// since its first fragment we can use the first 14 bytes of the
					// area in Mbuf, incase we don't copy
					// 
					cmd->setOffset(offset, true);
					cmd->setLinkFragmentType(FIRST_FRAGMENT);
				} 
				else 
				{
					if (fragmentSize < residual)
					{
						ip1394Hdr->fragment.datagramSize |= htons(INTERIOR_FRAGMENT << 14);
						cmd->setLinkFragmentType(INTERIOR_FRAGMENT);
					}
					else
					{
						ip1394Hdr->fragment.datagramSize |= htons(LAST_FRAGMENT << 14);
						cmd->setLinkFragmentType(LAST_FRAGMENT);
					}
					
					ip1394Hdr->fragment.fragmentOffset = htons(fragmentOffset);
					cmd->setOffset(offset, false);
				}
				// Get your datagram labels correct 
				ip1394Hdr->fragment.dgl = htons(dgl);
				ip1394Hdr->fragment.reserved = 0;
			}
			// end fragmentation


        
			if(handle->unicast.deviceID != 0) 
			{ 
			
				status = cmd->initDescriptor(unfragmented, cmdLen);
				if(status != kIOReturnSuccess)
					goto endtxIP;

				// Initialize the command with new values of device object
				status = cmd->reinit((IOFireWireDevice*)handle->unicast.deviceID, 
									cmdLen+headerSize, 
									addr, 
									txCompleteBlockWrite, 
									this, 
									true);

				cmd->setDeferredNotify(fDeferNotify);
				
				if(status == kIOReturnSuccess)
					cmd->submit(gDoQueue);
				
				status = cmd->getStatus();
				
				//
				// FWFamily command overrun, return imm and indicate
				// outputStalled
				//
				if(status == kIOFireWireOutOfTLabels || status == kIOReturnNoResources)
				{
					fSubmitErrs++;
					return status;
				}
				else
				{
					//
					// its a timeout or some unknown error
					//
					bTxError = true;
				}
			}

			fragmentOffset += cmdLen;  // Account for the position and...
			offset += cmdLen;
			residual -= cmdLen;        // ...size of the fragment just sent
			
		} // end next fragment
	}

endtxIP:
	if(status != kIOReturnSuccess)
	{
		//
		// If not a transmit or submit error then we go ahead and free the Mbuf
		// and return the command
		//
		if(bTxError == false)
		{
			// IOLog("IOFireWireIP: freed in txIP %X\n", status);
			if(cmd != NULL)
			{
				cmd->resetDescriptor();
				// Queue the command back into the command pool
				if(fAsyncCmdPool)
					fAsyncCmdPool->returnCommand(cmd);
				IOTakeLock(ipLock);
				fUsedCmds--;	
			    IOUnlock(ipLock);
			}
			freePacket(m);

			// Error, so we touch the error output packets
			networkStatAdd(&(getNetStats())->outputErrors);
		}
	}
	
	return status;
}

/*!
	@function txMCAP
	@abstract This procedure transmits either an MCAP solicitation or advertisement on the
			  default broadcast channel, dependent upon whether or not an MCB is supplied.
			  Note that if more than one multicast address group is associated with a
			  particular channel that multiple MCAP group descriptors are created.
 	@param lcb - link control block of the local node.
	@param mcb - multicast control block.
	@param ipAddress - IP address.
	@result void.
*/
void IOFireWireIP::txMCAP(LCB *lcb, MCB *mcb, UInt32 ipAddress){
	ARB *arb;
	MCAST_DESCR *groupDescriptor;
	IOFWIPAsyncStreamTxCommand *asyncStreamCmd = NULL;
	struct mcap_packet *packet;
	SInt32 status;
	UInt32 cmdLen = 0;
	UInt8 *buf;

    //IOLog("IOFireWireIP: txMCAP called\n");
	if(fAsyncStreamTxCmdPool == NULL)
	{
		initAsyncStreamCmdPool();
	}
	
	// Get an async command from the command pool
	asyncStreamCmd = (IOFWIPAsyncStreamTxCommand*)fAsyncStreamTxCmdPool->getCommand(false);
		
	// Lets not block to get a command, IP may retry soon ..:)
	if(asyncStreamCmd == NULL)
		return;
			
	// Get the buffer pointer from the command pool
	buf = (UInt8*)asyncStreamCmd->getBufferFromDesc();
	// dstBufLen = asyncStreamCmd->getMaxBufLen();

	packet = (struct mcap_packet*)buf;
	memset(packet, 0, sizeof(*packet));
	packet->gaspHdr.sourceID = htons(fLcb->ownNodeID);
	memcpy(&packet->gaspHdr.gaspID, &gaspVal, sizeof(GASP_ID));
	packet->ip1394Hdr.etherType = htons(ETHER_TYPE_MCAP);
	packet->mcap.length = sizeof(*packet);          /* Fix endian-ness later */
	groupDescriptor = packet->mcap.groupDescr;
	
	if (mcb != NULL) {
		packet->mcap.opcode = MCAP_ADVERTISE;
		arb = (ARB*)&lcb->multicastArb;
      
		while ((arb = arb->next) != NULL) {
			if (arb->handle.multicast.channel == mcb->channel) {
				memcpy(&ipAddress, &arb->ipAddress, sizeof(ipAddress));
				IOLog("   Advertise %u.%u.%u.%u channel %u expires %u\n\r",
                      ((UCHAR *) &ipAddress)[0], ((UCHAR *) &ipAddress)[1],
                      ((UCHAR *) &ipAddress)[2], ((UCHAR *) &ipAddress)[3],
                      mcb->channel, mcb->expiration);
				memset(groupDescriptor, 0, sizeof(MCAST_DESCR));
				groupDescriptor->length = sizeof(MCAST_DESCR);
				groupDescriptor->type = MCAST_TYPE;
				groupDescriptor->expiration = mcb->expiration;
				groupDescriptor->channel = mcb->channel;
				groupDescriptor->speed = arb->handle.multicast.spd;
				groupDescriptor->groupAddress = arb->ipAddress;
				groupDescriptor = (MCAST_DESCR*)((UInt32) groupDescriptor + sizeof(MCAST_DESCR));
				packet->mcap.length += sizeof(MCAST_DESCR);
			}
		}
	} else {
		packet->mcap.opcode = MCAP_SOLICIT;
#ifdef FIREWIRETODO		
		IOLog("   Solicit %u.%u.%u.%u\n\r", ((UCHAR *) &ipAddress)[0],
                ((UCHAR *) &ipAddress)[1], ((UCHAR *) &ipAddress)[2],
                ((UCHAR *) &ipAddress)[3]);
#endif			
		memset(groupDescriptor, 0, sizeof(MCAST_DESCR));
		groupDescriptor->length = sizeof(MCAST_DESCR);
		groupDescriptor->type = MCAST_TYPE;
		groupDescriptor->groupAddress = ipAddress;
		packet->mcap.length += sizeof(MCAST_DESCR);
	}

   cmdLen = packet->mcap.length;   // In CPU byte order 
   packet->mcap.length = htons(packet->mcap.length); // Serial Bus order

	asyncStreamCmd->setMbuf(NULL);

	// Initialize the command with new values of device object
	status = asyncStreamCmd->reinit(fLcb->busGeneration, 
						DEFAULT_BROADCAST_CHANNEL, 
						cmdLen,
						fLcb->maxBroadcastSpeed,
						txCompleteAsyncStream, 
						this);
					  
	if(status == kIOReturnSuccess)
		asyncStreamCmd->submit(true);
}

/*!
	@function rxUnicastFlush
	@abstract Starts the batch processing of the packets, its
	          already on its own workloop.
*/
void IOFireWireIP::rxUnicastFlush()
{
	UInt32 count = 0;

    IOTakeLock(ipLock);
	
	if(fPacketsQueued = true)
	{
		count = networkInterface->flushInputQueue();
        if(count > fMaxInputCount)
        {
            fMaxInputCount = count; 
        } 
		fPacketsQueued = false;
	}

    IOUnlock(ipLock);

	return;
}

/*!
	@function rxUnicastComplete
	@abstract triggers the indication workloop to do batch processing
				of incoming packets.
*/
void IOFireWireIP::rxUnicastComplete(void *refcon)
{
	IOFireWireIP *fwIPObject = (IOFireWireIP *)refcon;

	fwIPObject->rxUnicastFlush();

	return;
}

/*!
	@function rxUnicast
	@abstract block write handler. Handles both ARP and IP packet.
*/
UInt32 IOFireWireIP::rxUnicast( void		*refcon,
								UInt16		nodeID,
                                IOFWSpeed	&speed,
                                FWAddress	addr,
                                UInt32		len,
								const void	*buf,
								IOFWRequestRefCon requestRefcon)
{
	void *datagram, *fragment;
	UInt16 datagramSize;
	IP1394_UNFRAG_HDR *ip1394Hdr;
	IP1394_FRAG_HDR *fragmentHdr;
	RCB *currRcb;
	UInt16 fragmentSize;
	UInt16 type;
	UInt8 lf;
	RCB *rcb;
	CBLK *cBlk;
	struct mbuf *rxMBuf;
	IOFireWireIP *fwIPObject = (IOFireWireIP *)refcon;
	struct firewire_header *fwh = NULL;
	ip1394Hdr = (IP1394_UNFRAG_HDR *)buf;

//	IOLog("IOFireWireIP: rxUnicast+\n");

	if(fwIPObject->netifEnabled != true)
		return kIOReturnSuccess;
   
	// Handle the unfragmented packet
	if (ip1394Hdr->reserved == htons(UNFRAGMENTED)) 
	{
		datagram = (void *) ((ULONG) ip1394Hdr + sizeof(IP1394_UNFRAG_HDR));
		datagramSize = len - sizeof(IP1394_UNFRAG_HDR);
		type = ntohs(ip1394Hdr->etherType);
		switch (type) 
		{
			case ETHERTYPE_IPV6:
				// Update the NDP cache
				if (datagramSize >= IPV6_HDR_SIZE)
					fwIPObject->updateNDPCache(datagram, &datagramSize);
				else
					break;
			case ETHERTYPE_IP:
				if (datagramSize >= IPV4_HDR_SIZE)
					fwIPObject->rxIP(fwIPObject,  datagram, datagramSize, FW_M_UCAST, type);
				break;

			case ETHERTYPE_ARP:
				if (datagramSize >= sizeof(IP1394_ARP))
				{
					fwIPObject->rxARP(fwIPObject, (IP1394_ARP*)datagram, FW_M_UCAST);
				}
				break;
			
			default :
				// Unknown packet type
				fwIPObject->networkStatAdd(&(fwIPObject->getNetStats())->inputErrors);
				break;
		}
	}
	else
	{       
		fwIPObject->fRxFragmentPkts++;
	
		// Sigh! Have to collect and reassemble the fragments
		fragmentHdr = (IP1394_FRAG_HDR*) ip1394Hdr;  // Different header layout
		fragment = (void *) ((UInt32) fragmentHdr + sizeof(IP1394_FRAG_HDR));
		fragmentSize = len - sizeof(IP1394_FRAG_HDR);
		lf = htons(fragmentHdr->datagramSize) >> 14;
		if ((rcb = fwIPObject->getRcb(fwIPObject->fLcb, nodeID, htons(fragmentHdr->dgl))) == NULL) 
		{

			if ((rxMBuf = fwIPObject->getMBuf((htons(fragmentHdr->datagramSize) & 0x3FFF)
												+ 1 + sizeof(firewire_header))) == NULL)
			{
				IOLog("  mbuf not available\n");
				return kIOReturnSuccess;
			}
				
			if ((rcb = (RCB*)fwIPObject->allocateCBlk(fwIPObject->fLcb)) == NULL) 
			{
				IOTakeLock(fwIPObject->ipLock);
				// IOLog("  Cleaning up RCB\n");
				cBlk = (CBLK*)&(fwIPObject->fLcb->activeRcb);
				while ((cBlk = cBlk->next) != NULL) 
				{
					currRcb	= (RCB*)cBlk;
					if (currRcb->timer == 1)
					{
						fwIPObject->networkStatAdd(&(fwIPObject->getNetStats())->inputErrors);
						if(currRcb->mBuf != NULL)
							fwIPObject->freePacket(currRcb->mBuf, kDelayFree);
						currRcb->sourceID = 0;
						currRcb->dgl = 0;
						currRcb->mBuf = NULL;
						currRcb->residual = 0;
						fwIPObject->unlinkCBlk(&(fwIPObject->fLcb->activeRcb), cBlk);
						fwIPObject->deallocateCBlk(fwIPObject->fLcb, cBlk);
					}
				}
				IOUnlock(fwIPObject->ipLock);
					
				fwIPObject->freePacket(rxMBuf, kDelayFree);
				fwIPObject->releaseFreePackets();				
				//fwIPObject->freeMBuf(rxMBuf);
				return kIOReturnSuccess;
			}
         
			rcb->sourceID = nodeID;
			rcb->dgl = htons(fragmentHdr->dgl);
			rcb->mBuf = rxMBuf;
			rcb->timer = 1;

			// Make space for the firewire header to be helpfull in firewire_demux
			fwh = (struct firewire_header *)rxMBuf->m_data;
			bzero(fwh, sizeof(struct firewire_header));
			rcb->datagram = rxMBuf->m_data  + sizeof(struct firewire_header);
			// when indicating to the top layer
			if (lf == FIRST_FRAGMENT)
			{
				fwh->ether_type = htons(fragmentHdr->fragmentOffset);
			}
			else
			{
			    // TODO : May be we should do something better
				fwh->ether_type = ETHERTYPE_IP;
			}
			rcb->datagramSize = (htons(fragmentHdr->datagramSize) & 0x3FFF) + 1;
			rcb->residual = rcb->datagramSize;
			fwIPObject->linkCBlk(&(fwIPObject->fLcb->activeRcb), rcb);
		}
      
		if (rcb->mBuf == NULL || rcb->datagram == NULL)
		{
			fwIPObject->networkStatAdd(&(fwIPObject->getNetStats())->inputErrors);
			fwIPObject->showRcb(rcb);
			return kIOReturnError;
		}
		 
		if (lf == FIRST_FRAGMENT) 
		{
			//IOLog("   datagram size %d fragmentSize %d\n\r", rcb->datagramSize, fragmentSize);
			// Actually etherType
			rcb->etherType = htons(fragmentHdr->fragmentOffset);
			// memcpy(rcb->datagram, fragment, MIN(fragmentSize, rcb->datagramSize));
			fwIPObject->bufferToMbuf(rcb->mBuf, 
									sizeof(struct firewire_header), 
									(UInt8*)fragment, 
									MIN(fragmentSize, rcb->datagramSize));

		}
		else 
		{
			fwIPObject->bufferToMbuf(rcb->mBuf, 
									sizeof(struct firewire_header)+htons(fragmentHdr->fragmentOffset), 
									(UInt8*)fragment, 
									MIN(fragmentSize, rcb->datagramSize - htons(fragmentHdr->fragmentOffset)));
		}
      
		// Don't reduce below zero
		rcb->residual -= MIN(fragmentSize, rcb->residual); 
		// Reassembly (probably) complete ?
		if (rcb->residual == 0) 
		{           
			// Legitimate etherType ?
			if (rcb->etherType == ETHERTYPE_IP || rcb->etherType == ETHERTYPE_IPV6) 
			{  
				fwIPObject->receivePackets (rcb->mBuf, 
											rcb->mBuf->m_pkthdr.len,
											true);
			} 
			else
			{          
				// Unknown packet type
				fwIPObject->networkStatAdd(&(fwIPObject->getNetStats())->inputErrors);
				// IOLog("   All that reassembly work for nothing!\n\r");
				// All that reassembly work for nothing!           
				fwIPObject->freeMBuf(rcb->mBuf);
			}
			rcb->mBuf = NULL;
			rcb->residual = 0;
			rcb->timer = 0;
			// Either case, release the RCB
			fwIPObject->unlinkCBlk(&(fwIPObject->fLcb->activeRcb), rcb);   
			fwIPObject->deallocateCBlk(fwIPObject->fLcb, rcb);
		}
   }

   fwIPObject->fRxUni++;
   
   return kIOReturnSuccess;
}

/*!
	@function rxAsyncStream
	@abstract callback for an Asyncstream packet, can be both IP or ARP packet.
			This procedure receives an indication when an asynchronous stream
			packet arrives on the default broadcast channel. The packet "should" be GASP,
			but we perform a few checks to make sure. Once we know these are OK, we check
			the etherType field in the unfragmented encapsulation header. This is necessary
			to dispatch the three types of packet that RFC 2734 permits on the default
			broadcast channel: an IPv4 datagram, and ARP request or response or a multi-
			channel allocation protocol (MCAP) message. The only remaining check, for each
			of these three cases, is to make sure that the packet is large enough to hold
			meaningful data. If so, send the packet to another procedure for further
			processing.  
	@param DCLCommandStruct *callProc.
	@result void.
*/
void IOFireWireIP::rxAsyncStream(DCLCommandStruct *callProc){
    
	DCLCallProc 	*ptr = (DCLCallProc*)callProc;
	RXProcData		*proc = (RXProcData*)ptr->procData;
	IOFireWireIP	*fwIPObject = (IOFireWireIP*)proc->obj;
    IOFWAsyncStreamRxCommand	*fwRxAsyncStream;
	UInt8			*buffer = proc->buffer;
	void			*datagram;
	UInt16			datagramSize;
	GASP			*gasp = (GASP*)buffer;
	LCB 			*lcb = fwIPObject->fLcb;
	ISOC_DATA_PKT	*pkt = (ISOC_DATA_PKT*)buffer; 
	UInt16 			type = 0;

    fwRxAsyncStream = (IOFWAsyncStreamRxCommand*)proc->thisObj;

    fwRxAsyncStream->modifyDCLJumps(callProc);
    
	if(fwIPObject->netifEnabled != true)
    {
		return;
    }
    
 // IOLog("IOFireWireIP: rxAsyncStream start called\n");

	if(pkt->tag != GASP_TAG){
		// Error, so we touch the error output packets
		fwIPObject->networkStatAdd(&(fwIPObject->getNetStats())->inputErrors);
		IOLog("   GASP tag error\n\r");
		return;
    }
	
	// Minimum size requirement
	if (gasp->dataLength < sizeof(GASP_HDR) + sizeof(IP1394_UNFRAG_HDR)) {
		fwIPObject->networkStatAdd(&(fwIPObject->getNetStats())->inputErrors);
		IOLog("   GASP header error\n\r");
		return;
    }

	// Ignore GASP if not specified by RFC 2734
	if (memcmp(&gasp->gaspHdr.gaspID, &gaspVal, sizeof(GASP_ID)) != 0) {
		fwIPObject->networkStatAdd(&(fwIPObject->getNetStats())->inputErrors);
		IOLog("   Non-RFC 2734 GASP\n\r");
		return;
    }

	// Also ignore GASP if not from the local bus
	if ((htons(gasp->gaspHdr.sourceID) >> 6) != LOCAL_BUS_ID) {
		fwIPObject->networkStatAdd(&(fwIPObject->getNetStats())->inputErrors);
		IOLog("   Remote GASP error\n\r");
		return;
    }
   
	// Broadcast fragmentation not supported
	if (gasp->ip1394Hdr.reserved != htons(UNFRAGMENTED)) {
		fwIPObject->networkStatAdd(&(fwIPObject->getNetStats())->inputErrors);
		IOLog("   Encapsulation header error\n\r");
		return;
    }
   
   datagram = (void *) ((UInt32) buffer + sizeof(GASP));
   datagramSize = gasp->dataLength - (sizeof(GASP_HDR) + sizeof(IP1394_UNFRAG_HDR));
   type = ntohs(gasp->ip1394Hdr.etherType);
//   IOLog("   Ether type 0x%04X (data length %d)\n\r",htons(gasp->ip1394Hdr.etherType), datagramSize);
   
	switch (type) {
		case ETHERTYPE_IPV6:
			// Update the NDP cache
			if (datagramSize >= IPV6_HDR_SIZE)
				fwIPObject->updateNDPCache(datagram, &datagramSize);
			else
				break;
		case ETHERTYPE_IP:
			if (datagramSize >= IPV4_HDR_SIZE)
				fwIPObject->rxIP(fwIPObject,  datagram, datagramSize, FW_M_BCAST, type);
			break;

		case ETHERTYPE_ARP:
			if (datagramSize >= sizeof(IP1394_ARP))
			{
				fwIPObject->rxARP(fwIPObject, (IP1394_ARP*)datagram, FW_M_BCAST);
			}
			break;
			
		case ETHER_TYPE_MCAP:
			if (datagramSize >= sizeof(IP1394_MCAP))
				fwIPObject->rxMCAP(lcb, htons(gasp->gaspHdr.sourceID), 
									(IP1394_MCAP*)datagram, datagramSize - sizeof(IP1394_MCAP));
			break;
	}

 //   IOLog("IOFireWireIP: rxAsyncStream end called\n");
	fwIPObject->fRxBcast++;

	return;
}

/*!
	@function rxMCAP
	@abstract called from rxAsyncstream for processing MCAP advertisement.
			When an MCAP advertisement is received, parse all of its descriptors 
			looking for any that match group addreses in our MCAP cache. For those that 
			match, update  the channel number (it may have changed from the default
			broadcast channel or since the last advertisement), update the speed 
			(the MCAP owner may have changed the speed requirements as nodes joined or 
			left the group) and refresh the expiration timer so that the MCAP 
			channel is valid for another number of seconds into the future. 
			Th-th-th-that's all, folks!
	@param lcb - the firewire link control block for this interface.
    @param mcapSourceID - source nodeid which generated the multicast advertisement packet.
    @param mcap - mulitcast advertisment packet without the GASP header.
	@param dataSize - size of the packet.
	@result void.
*/
void IOFireWireIP::rxMCAP(LCB *lcb, UInt16 mcapSourceID, IP1394_MCAP *mcap, UInt32 dataSize){

	ARB *arb;
	UInt32 currentChannel;
	MCAST_DESCR *groupDescr = mcap->groupDescr;
	MCB		*mcb,	*priorMcb;
	IOFWAsyncStreamRxCommand *asyncStreamRxClient;
	IOReturn ioStat = kIOReturnSuccess;

	//IOLog("rxMCAP\n\r");
   
	if ((mcap->opcode != MCAP_ADVERTISE) && (mcap->opcode != MCAP_SOLICIT))
		return;        // Ignore reserved MCAP opcodes
	  
	dataSize = MIN(dataSize, htons(mcap->length) - sizeof(IP1394_MCAP));
   
	while (dataSize >= sizeof(MCAST_DESCR)) 
	{
	
		if (groupDescr->length != sizeof(MCAST_DESCR))
			;           // Skip over malformed MCAP group address descriptors
		else if (groupDescr->type != MCAST_TYPE)
			;           // Skip over unrecognized descriptor types
		else if ((arb = getMulticastArb(lcb, groupDescr->groupAddress)) == NULL)
			;           // Ignore if not in our multicast cache */
		else if (mcap->opcode == MCAP_SOLICIT) {
#ifdef FIREWIRETODO			
			IOLog("   Solicit %u.%u.%u.%u\n\r",
											((UCHAR *) &groupDescr->groupAddress)[0],
											((UCHAR *) &groupDescr->groupAddress)[1],
											((UCHAR *) &groupDescr->groupAddress)[2],
											((UCHAR *) &groupDescr->groupAddress)[3]);
#endif											
			mcb = &lcb->mcapState[arb->handle.multicast.channel];
			if (mcb->ownerNodeID == lcb->ownNodeID)   // Do we own the channel?
				txMCAP(lcb, mcb, 0);             // OK, respond to solicitation
				
		} 
		else if ((groupDescr->channel != DEFAULT_BROADCAST_CHANNEL) 
				&& (groupDescr->channel <= LAST(lcb->mcapState))) 
		{
			IOLog("   Advertise %u.%u.%u.%u channel %u expires %u\n\r",
												((UCHAR *) &groupDescr->groupAddress)[0],
												((UCHAR *) &groupDescr->groupAddress)[1],
												((UCHAR *) &groupDescr->groupAddress)[2],
												((UCHAR *) &groupDescr->groupAddress)[3],
												groupDescr->channel, groupDescr->expiration);
         
			mcb = &lcb->mcapState[groupDescr->channel];
			if (groupDescr->expiration < 60) 
			{
			
				if (mcb->ownerNodeID == mcapSourceID) 
				{
					currentChannel = groupDescr->channel;
				//	acquireChannel(&currentChannel, TRUE, kDoNotAllocate | kNotifyOnSuccess);
					mcb->ownerNodeID = lcb->ownNodeID;  // Take channel ownership
					mcb->nextTransmit = 1;        // Transmit advertisement ASAP
					
				}
			
			} 
			else if (mcb->ownerNodeID == mcapSourceID) 
			{
				mcb->expiration = groupDescr->expiration;
			}
			else if (mcb->ownerNodeID < mcapSourceID || mcb->expiration < 60) 
			{
            	if (mcb->ownerNodeID == lcb->ownNodeID)   // Are we the owner?
					// releaseChannel(groupDescr->channel, kDoNotDeallocate);
					// TNFReleaseChannel(lcb->unspecifiedDeviceID, groupDescr->channel, kDoNotDeallocate);
				
				mcb->ownerNodeID = mcapSourceID;
				mcb->expiration = groupDescr->expiration;
			}
			currentChannel = arb->handle.multicast.channel;
         
			if (currentChannel == DEFAULT_BROADCAST_CHANNEL) 
			{
				if (mcb->asyncStreamID == kInvalidAsyncStreamRefID) 
				{
					if(groupDescr->channel != DEFAULT_BROADCAST_CHANNEL)
					{
						asyncStreamRxClient = new IOFWAsyncStreamRxCommand;
						if(asyncStreamRxClient == NULL) 
						{
							ioStat = kIOReturnNoMemory;
						}
				
						if(asyncStreamRxClient->initAll(groupDescr->channel, rxAsyncStream, fControl, 
																	fMaxRxIsocPacketSize, this) == false) {
							ioStat = kIOReturnNoMemory;
						}
						if(ioStat == kIOReturnSuccess)
							mcb->asyncStreamID = (UInt32)asyncStreamRxClient;
					}
					else
					{
						if(fBroadcastReceiveClient != NULL)
						{
							fBroadcastReceiveClient->retain();
							mcb->asyncStreamID = (UInt32)fBroadcastReceiveClient;
						}
					}
				}
				
				arb->handle.multicast.channel = groupDescr->channel;
				mcb->groupCount++;
				
			} else if (currentChannel != groupDescr->channel) {
				
				priorMcb = &lcb->mcapState[currentChannel];
            
				if (priorMcb->groupCount == 1)
				{   
					// Are we the last user?
					asyncStreamRxClient = (IOFWAsyncStreamRxCommand *)mcb->asyncStreamID;
					if(asyncStreamRxClient != NULL)
						asyncStreamRxClient->release();
					//TNFRemoveAsyncStreamClient(lcb->clientID, mcb->asyncStreamID);
					priorMcb->asyncStreamID = kInvalidAsyncStreamRefID;
					priorMcb->groupCount = 0;
				} else if (priorMcb->groupCount > 0)
					priorMcb->groupCount--;
					
				if (mcb->asyncStreamID == kInvalidAsyncStreamRefID) 
				{
					if(groupDescr->channel != DEFAULT_BROADCAST_CHANNEL)
					{				
						asyncStreamRxClient = new IOFWAsyncStreamRxCommand;
						if(asyncStreamRxClient == NULL) {
							ioStat = kIOReturnNoMemory;
						}
				
						if(asyncStreamRxClient->initAll(groupDescr->channel, rxAsyncStream, fControl, 
																			fMaxRxIsocPacketSize, this) == false) {
							ioStat = kIOReturnNoMemory;
						}
						if(ioStat == kIOReturnSuccess)
							mcb->asyncStreamID = (UInt32)asyncStreamRxClient;
					}
					else
					{
						if(fBroadcastReceiveClient != NULL)
						{
							fBroadcastReceiveClient->retain();
							mcb->asyncStreamID = (UInt32)fBroadcastReceiveClient;
						}
					}
				}
				
				arb->handle.multicast.channel = groupDescr->channel;
				mcb->groupCount++;
			}
			
			if (arb->deletionPending && (mcb->ownerNodeID != lcb->ownNodeID)) {
				unlinkCBlk(&lcb->multicastArb, arb);
				deallocateCBlk(lcb, arb);
			}
		}
		dataSize -= MIN(groupDescr->length, dataSize);
		groupDescr = (MCAST_DESCR*)((ULONG) groupDescr + groupDescr->length);
	}


}

/*!
	@function rxIP
	@abstract Receive IP packet.
	@param fwIPObj - IOFireWireIP object.
	@param pkt - points to the IP packet without the header.
	@param len - length of the packet.
	@params flags - indicates broadcast or unicast	
	@params type - indicates type of the packet IPv4 or IPv6	
	@result IOReturn.
*/
IOReturn IOFireWireIP::rxIP(IOFireWireIP *fwIPObj, void *pkt, UInt32 len, UInt32 flags, UInt16 type)
{

	struct mbuf *rxMBuf;
	struct firewire_header *fwh = NULL;
	void	*datagram = NULL;
	bool	queuePkt = false; 

	
	if ((rxMBuf = getMBuf(len + sizeof(struct firewire_header))) != NULL) 
	{
        IOTakeLock(ipLock);
		
        // Make space for the firewire header to be helpfull in firewire_demux
		fwh = (struct firewire_header *)rxMBuf->m_data;
		datagram = rxMBuf->m_data + sizeof(struct firewire_header);
		bzero(fwh, sizeof(struct firewire_header));
		if (flags == FW_M_UCAST)
		{
			bcopy(macAddr, fwh->ether_dhost, FIREWIRE_ADDR_LEN);
			queuePkt = true;
		}
		else
		{
			bcopy(fwbroadcastaddr, fwh->ether_dhost, FIREWIRE_ADDR_LEN);
			queuePkt = false;
		}
		
		// IOLog("rxIP %d, type %x\n", len, type);
		
		fwh->ether_type = type;

        IOUnlock(ipLock);
	
        bufferToMbuf(rxMBuf, sizeof(struct firewire_header), (UInt8*)pkt, len); 			
	
		receivePackets(rxMBuf, rxMBuf->m_pkthdr.len, queuePkt);
	}


	return kIOReturnSuccess;
}

/*!
	@function rxARP
	@abstract ARP processing routine called from both Asynstream path and Async path.
	@param fwIPObj - IOFireWireIP object.
	@param arp - 1394 arp packet without the GASP or Async header.
	@params flags - indicates broadcast or unicast	
	@result IOReturn.
*/
IOReturn IOFireWireIP::rxARP(IOFireWireIP *fwIPObj, IP1394_ARP *arp, UInt32 flags){

	struct mbuf *rxMBuf;
	struct firewire_header *fwh = NULL;
	void	*datagram = NULL;
	
//  IOLog("IOFireWireIP: rxARP called\n");

	if (arp->hardwareType != htons(ARP_HDW_TYPE)
		|| arp->protocolType != htons(ETHERTYPE_IP)
		|| arp->hwAddrLen != sizeof(IP1394_HDW_ADDR)
		|| arp->ipAddrLen != IPV4_ADDR_SIZE)
	{
		IOLog("IOFireWireIP: rxARP ERROR in packet header\n");
		return kIOReturnError;
	}

	if ((rxMBuf = getMBuf(sizeof(*arp) + sizeof(struct firewire_header))) != NULL) 
	{
        IOTakeLock(ipLock);
    
		fwh = (struct firewire_header *)rxMBuf->m_data;
		datagram = rxMBuf->m_data + sizeof(struct firewire_header);
		bzero(fwh, sizeof(struct firewire_header));
		fwh->ether_type = ETHERTYPE_ARP;
		// Copy the data
		memcpy(datagram, arp, sizeof(*arp));
		
        IOUnlock(ipLock);
	
        receivePackets(rxMBuf, rxMBuf->m_pkthdr.len, NULL);
	}
   
 	return kIOReturnSuccess;
}


/*!
	@function watchdog
	@abstract cleans the Link control block's stale drb's and rcb's.
			The cleanCache's job is to age (and eventually discard) device objects 
			for FireWireIP devices that have come unplugged. If they do reappear after  
			they have been discarded from the caches, all that is required is a new ARP. 
			The IP network stack handles that automatically
	@param lcb - the firewire link control block for this interface.
	@result void.
*/
void IOFireWireIP::watchdog(IOTimerEventSource *) 
{
	ARB *arb, *priorArb;
	DRB *drb, *priorDrb;
	UNSIGNED i;
	MCB *mcb;
	IOFireWireIP *fwIpObj = (IOFireWireIP*)this;
	LCB *lcb = fwIpObj->getLcb();
	IOFWAsyncStreamRxCommand *asyncStreamRxClient;

	priorDrb = (DRB *) &lcb->activeDrb;

	while ((drb = priorDrb->next) != NULL) 
	{
		if (drb->timer > 1)                    // Still has time remaining?
			drb->timer--;                      // If so, just decrement time
		else if (drb->timer == 1) 
		{            // Expiring on this clock tick?
			priorDrb->next = drb->next;        // Remove from active list
			arb = (ARB *) &lcb->unicastArb;     // Flush ARP cache for this device
			while ((arb = arb->next) != NULL) 
			{
				if (arb->handle.unicast.deviceID == drb->deviceID) 
				{
					arb->handle.unicast.deviceID = kInvalidIPDeviceRefID;
					fwIpObj->unlinkCBlk(&lcb->unicastArb, arb);
					fwIpObj->deallocateCBlk(lcb, arb);
					break;                        // Only one ARB will match
				}
			}
			drb->deviceID = kInvalidIPDeviceRefID;  // Don't notify in future
			//IOLog("IOFireWireIP: Host 0x%lx:0x%lx cleaned \n", (UInt32)drb->eui64.hi, (UInt32)drb->eui64.lo);
			fwIpObj->deallocateCBlk(lcb, drb);
			continue;         // Important to recheck ->next after unlinkCBlk!
		}
		priorDrb = drb;		  // Continue to next DRB in linked list
	}
	
	for (i = 0; i <= LAST(lcb->mcapState); i++) 
	{
		mcb = &lcb->mcapState[i];
		if (mcb->expiration > 1)      // Life in this channel allocation yet?
		{
			mcb->expiration--;         // Yes, but the clock is ticking...
		}
		else if (mcb->expiration == 1) // Dead in the water?
		{ 
			mcb->expiration = 0;          // Yes, mark it expired
			asyncStreamRxClient = (IOFWAsyncStreamRxCommand *)mcb->asyncStreamID;
			if(asyncStreamRxClient != NULL)
				asyncStreamRxClient->release();
			mcb->asyncStreamID = kInvalidAsyncStreamRefID;
			if (mcb->ownerNodeID == lcb->ownNodeID) // We own the channel?
			{  
				mcb->finalWarning = 4;  // Yes, four final advertisements
				mcb->nextTransmit = 1;  // Starting right now... 
			}
		}
		if (mcb->ownerNodeID != lcb->ownNodeID)
			continue;                     // Cycle to next array entry 
		else if (mcb->nextTransmit > 1)  // Time left before next transmit? 
			mcb->nextTransmit--;                         // Keep on ticking... 
		else if (mcb->nextTransmit == 1) 
		{              // Due to expire now? 
			if (mcb->groupCount > 0)      // Still in use at this machine? 
				mcb->expiration = 60;      // Renew this channel's lease
				
			fwIpObj->txMCAP(lcb, mcb, 0);          // Broadcast the MCAP advertisement
			
			if (mcb->expiration > 0)
				mcb->nextTransmit = 10;    // Send MCAP again in ten seconds 
			else if (--mcb->finalWarning > 0)
				mcb->nextTransmit = 10;    // Channel deallocation warning 
			else 
			{
				mcb->ownerNodeID = MCAP_UNOWNED; // Reliquish our ownership 
				mcb->nextTransmit = 0;           // We're really, really done! 
				//TNFReleaseChannel(lcb->unspecifiedDeviceID, mcb->channel, 0);
				//fwIpObj->releaseChannel(mcb->channel, 0);
				priorArb = (ARB *) &lcb->multicastArb;
				while ((arb = priorArb->next) != NULL) 
				{
					if (arb->handle.multicast.channel == mcb->channel)
					{
						if (arb->deletionPending) 
						{
							priorArb->next = arb->next;   // Unlink the ARB 
							fwIpObj->deallocateCBlk(lcb, arb);     // And release it 
							continue;      // Important to recheck priorArb->next //
						} 
						else            // Revert to default channel //
							arb->handle.multicast.channel = DEFAULT_BROADCAST_CHANNEL;
					}
					priorArb = arb;      // Advance to next ARB in linked list 
				}
			}
		}
	}
	fwIpObj->cleanFWArbCache(lcb);
	
	//
	// Restart the watchdog timer
	//
	timerSource->setTimeoutMS(WATCHDOG_TIMER_MS);
}

/*!
	@function busReset
	@abstract Does busreset cleanup of the Link control block
	@param lcb - the firewire link control block for this interface.
	@param flags - ignored.
	@result void
*/	
void IOFireWireIP::busReset(LCB *lcb, UInt32 flags){
    CBLK	*cBlk;
    UInt32	i;
	RCB		*rcb;
	
    // Update our own max payload
    lcb->ownMaxPayload = fDevice->maxPackLog(true);
    
    // Update the nodeID
    fDevice->getNodeIDGeneration(lcb->busGeneration, lcb->ownNodeID);
	
    // Update the speed
    lcb->ownMaxSpeed = fDevice->FWSpeed();

    IOTakeLock(ipLock);
	cBlk = (CBLK*)&lcb->activeRcb;
	while ((cBlk = cBlk->next) != NULL) 
	{
		rcb		= (RCB*)cBlk;
        unlinkCBlk(&lcb->activeRcb, cBlk);
		networkStatAdd(&(getNetStats())->inputErrors);
		if(rcb->mBuf != NULL)
			freePacket(rcb->mBuf, kDelayFree);
		rcb->sourceID = 0;
		rcb->dgl = 0;
		rcb->mBuf = NULL;
		rcb->residual = 0;
        deallocateCBlk(lcb, cBlk);
	}
    IOUnlock(ipLock);
	releaseFreePackets();
   
    // Suspend MCAP for now
    for (i = 0; i <= LAST(lcb->mcapState); i++)
        lcb->mcapState[i].nextTransmit = 0;     

	updateBroadcastValues(true);

	updateLinkStatus();

	return;
}

#pragma mark -
#pragma mark еее IPv6 NDP routines  еее

bool IOFireWireIP::addNDPOptions(struct mbuf *m)
{
	struct icmp6_hdr			*icp	= NULL;
	struct ip6_hdr				*ip6;
	struct nd_neighbor_advert	*nd_na	= NULL;
	struct nd_neighbor_solicit	*nd_ns	= NULL;
 	
	IP1394_NDP	*fwndp	= NULL;
	BOOLEAN		modify  = false;
	
	UInt8			*buf = NULL;
	vm_address_t 	src = 0;
	struct mbuf		*temp = NULL;
	
	int		*pktLen = NULL;
	long	*length = NULL;
	
	if (m->m_flags & M_PKTHDR)
	{
		// IOLog("m_pkthdr.len  : %d\n", (UInt) m->m_pkthdr.len);
		pktLen = &m->m_pkthdr.len;
	}
	else
		return false;
			
	src = mtod(m, vm_offset_t);
	if(src == NULL)
		return false;

	// check whether len equals ether header
	if(m->m_len == sizeof(firewire_header))
	{
		temp = m->m_next;
		if(temp == NULL)
			return false;

		src =  mtod(temp, vm_offset_t);
		
		if(temp->m_len < (int)(sizeof(struct ip6_hdr)))
		{
			// IOLog("pkt too small %d\n", __LINE__);
			return false;
		}
		
		if(m_trailingspace(temp) < (int)sizeof(IP1394_NDP))
		{
			// IOLog("no space in mbuf %d\n", __LINE__);
			return false;
		}
		
		buf = (UInt8*)(src);
		length = &temp->m_len;
	}
	else
	{
		if(m->m_len < (int)(sizeof(firewire_header) + sizeof(struct ip6_hdr)))
		{
			// IOLog("pkt too small %d\n", __LINE__);
			return false;
		}	

		if(m_trailingspace(m) < (int)sizeof(IP1394_NDP))
		{
			// IOLog("no space in mbuf %d\n", __LINE__);
			return false;
		}

		buf = (UInt8*)(src + sizeof(firewire_header));
		length = &m->m_len;
	}

	

	// IOLog("IOFireWireIP: addNDPOptions+\n");
	// show type of ICMPV6 packets being sent
	ip6		= (struct ip6_hdr*)buf;
	icp		= (struct icmp6_hdr*)(ip6 + 1);
	nd_na	= (struct nd_neighbor_advert*)icp;
	nd_ns	= (struct nd_neighbor_solicit*)icp;

	if(nd_ns->nd_ns_type == ND_NEIGHBOR_SOLICIT)
	{		
		// neighbor solicitation
		// IOLog("IOFireWireIP: neighbor solicitation+\n");
		fwndp = (IP1394_NDP*)((UInt8*)nd_ns + sizeof(struct nd_neighbor_solicit));
		if(fwndp->type == 1)
		{
			modify = true;
			/*
			IOLog("+type = %d | +len = %d | +srclladdr = %02x:%02x:%02x:%02x:%02x:%02x:%02x::%02x\n", 
				fwndp->type, fwndp->len, fwndp->lladdr[0], fwndp->lladdr[1], fwndp->lladdr[2],
				fwndp->lladdr[3], fwndp->lladdr[4], fwndp->lladdr[5], fwndp->lladdr[6], fwndp->lladdr[7]);
			*/
		}
		if(fwndp->type == 2)
		{
			/*
			IOLog("+type = %d | +len = %d | +tgtlladdr = %02x:%02x:%02x:%02x:%02x:%02x:%02x::%02x\n", 
				fwndp->type, fwndp->len, fwndp->lladdr[0], fwndp->lladdr[1], fwndp->lladdr[2],
				fwndp->lladdr[3], fwndp->lladdr[4], fwndp->lladdr[5], fwndp->lladdr[6], fwndp->lladdr[7]);
			*/
		}
		// IOLog("IOFireWireIP: neighbor solicitation-\n");
	}
	
	if(nd_na->nd_na_type == ND_NEIGHBOR_ADVERT)
	{
		// neighbor advertisment
		// IOLog("IOFireWireIP: neighbor advertisment+\n");
		fwndp =  (IP1394_NDP*)((UInt8*)nd_na + sizeof(struct nd_neighbor_advert));
		
		// we don't touch this option
		if(fwndp->type == 1)
		{
			/*
			IOLog("+type = %d | +len = %d | +srclladdr = %02x:%02x:%02x:%02x:%02x:%02x:%02x::%02x\n", 
				fwndp->type, fwndp->len, fwndp->lladdr[0], fwndp->lladdr[1], fwndp->lladdr[2],
				fwndp->lladdr[3], fwndp->lladdr[4], fwndp->lladdr[5], fwndp->lladdr[6], fwndp->lladdr[7]);
			*/
		}
		
		if(fwndp->type == 2)
		{
			modify = true;
			/*
			IOLog("+type = %d | +len = %d | +tgtlladdr = %02x:%02x:%02x:%02x:%02x:%02x:%02x::%02x\n", 
				fwndp->type, fwndp->len, fwndp->lladdr[0], fwndp->lladdr[1], fwndp->lladdr[2],
				fwndp->lladdr[3], fwndp->lladdr[4], fwndp->lladdr[5], fwndp->lladdr[6], fwndp->lladdr[7]);
			*/
		}
	}
	
	if(modify)
	{
		fwndp->len = 3;       									// len in units of 8 octets
		bzero(fwndp->reserved, 6);								// reserved by the RFC 3146
		fwndp->senderMaxRec = fLcb->ownHardwareAddress.maxRec;	// Maximum payload (2 ** senderMaxRec)
		fwndp->sspd = fLcb->ownHardwareAddress.spd;				// Maximum speed
		fwndp->senderUnicastFifoHi = htons(fLcb->ownHardwareAddress.unicastFifoHi);	// Most significant 16 bits of FIFO address
		fwndp->senderUnicastFifoLo = htonl(fLcb->ownHardwareAddress.unicastFifoLo);	// Least significant 32 bits of FIFO address
		
		// IOLog("+len = %d \n", *length);
		 		
		*length += 8;
		*pktLen += 8;
		
		// IOLog("+len = %d | sizeof(fwndp) = %d\n", *length , sizeof(IP1394_NDP)); 

		return true;
	}

	return false;
}

void IOFireWireIP::updateNDPCache(void *buf, UInt16	*len)
{
	struct icmp6_hdr			*icp	= NULL;
	struct ip6_hdr				*ip6;
	struct nd_neighbor_advert	*nd_na	= NULL;
	struct nd_neighbor_solicit	*nd_ns	= NULL;
	
	ARB			*arb	= NULL;
	IP1394_NDP	*fwndp	= NULL;
	BOOLEAN		update  = false;
	
	// IOLog("IOFireWireIP: updateNDPCache+\n");
	ip6		= (struct ip6_hdr*)buf;
	icp		= (struct icmp6_hdr*)(ip6 + 1);
	nd_na	= (struct nd_neighbor_advert*)icp;
	nd_ns	= (struct nd_neighbor_solicit*)icp;

	if(nd_ns->nd_ns_type == ND_NEIGHBOR_SOLICIT)
	{		
		// neighbor solicitation
		// IOLog("IOFireWireIP: neighbor solicitation+\n");
		fwndp = (IP1394_NDP*)((UInt8*)nd_ns + sizeof(struct nd_neighbor_solicit));
		if(fwndp->type == 1)
		{
			update = true;
			/*
			IOLog("+type = %d | +len = %d | +srclladdr = %02x:%02x:%02x:%02x:%02x:%02x:%02x::%02x\n", 
				fwndp->type, fwndp->len, fwndp->lladdr[0], fwndp->lladdr[1], fwndp->lladdr[2],
				fwndp->lladdr[3], fwndp->lladdr[4], fwndp->lladdr[5], fwndp->lladdr[6], fwndp->lladdr[7]);
			*/
		}
		if(fwndp->type == 2)
		{
			/*
			IOLog("+type = %d | +len = %d | +tgtlladdr = %02x:%02x:%02x:%02x:%02x:%02x:%02x::%02x\n", 
				fwndp->type, fwndp->len, fwndp->lladdr[0], fwndp->lladdr[1], fwndp->lladdr[2],
				fwndp->lladdr[3], fwndp->lladdr[4], fwndp->lladdr[5], fwndp->lladdr[6], fwndp->lladdr[7]);
			*/
		}
		// IOLog("IOFireWireIP: neighbor solicitation-\n");
	}
	
	if(nd_na->nd_na_type == ND_NEIGHBOR_ADVERT)
	{
		// neighbor advertisment
		// IOLog("IOFireWireIP: neighbor advertisment+\n");
		fwndp =  (IP1394_NDP*)((UInt8*)nd_na + sizeof(struct nd_neighbor_advert));
		
		// we don't touch this option
		if(fwndp->type == 1)
		{
			/*
			IOLog("+type = %d | +len = %d | +srclladdr = %02x:%02x:%02x:%02x:%02x:%02x:%02x::%02x\n", 
				fwndp->type, fwndp->len, fwndp->lladdr[0], fwndp->lladdr[1], fwndp->lladdr[2],
				fwndp->lladdr[3], fwndp->lladdr[4], fwndp->lladdr[5], fwndp->lladdr[6], fwndp->lladdr[7]);
			*/
		}
		
		if(fwndp->type == 2)
		{
			update = true;
			/*
			IOLog("+type = %d | +len = %d | +tgtlladdr = %02x:%02x:%02x:%02x:%02x:%02x:%02x::%02x\n", 
				fwndp->type, fwndp->len, fwndp->lladdr[0], fwndp->lladdr[1], fwndp->lladdr[2],
				fwndp->lladdr[3], fwndp->lladdr[4], fwndp->lladdr[5], fwndp->lladdr[6], fwndp->lladdr[7]);
			*/
		}
	}
	
	
	if(update && fwndp != NULL && fwndp->len > 2)
	{
		arb = getArbFromFwAddr(fLcb, fwndp->lladdr);
		if(arb == NULL)
		{
			arb = (ARB*)allocateCBlk(fLcb);
			if(arb == NULL)
			{
				// IOLog("IOFireWireIP: No ARB's !!\n");
				return;
			}
			linkCBlk(&fLcb->unicastArb, arb);  
		}
		
		if(arb != NULL)
		{
			bcopy(fwndp->lladdr, &arb->eui64, FIREWIRE_ADDR_LEN);
			bcopy(fwndp->lladdr, arb->fwaddr, FIREWIRE_ADDR_LEN);
			arb->handle.unicast.maxRec = fwndp->senderMaxRec;
			arb->handle.unicast.spd = fwndp->sspd;
			arb->handle.unicast.unicastFifoHi = htons(fwndp->senderUnicastFifoHi);
			arb->handle.unicast.unicastFifoLo = htonl(fwndp->senderUnicastFifoLo); 
			arb->timer = 0;
			arb->datagramPending = FALSE;
			arb->handle.unicast.deviceID = getDeviceID(fLcb, arb->eui64, &arb->itsMac);
			
			// Reset the packet
			*len -= 8;
			fwndp->len = 2;       	// len in units of 8 octets
			fwndp->senderMaxRec = 0;
			fwndp->sspd = 0;
			fwndp->senderUnicastFifoHi = 0;
			fwndp->senderUnicastFifoLo = 0;
		}
	}
	// IOLog("IOFireWireIP: updateNDPCache-\n");

	return;
}

#pragma mark -
#pragma mark еее IOFireWireIP utility routines  еее

/*!
	@function initializeCBlk
	@abstract Initializes the memory for control blocks.
	@param  memorySize size of the control block.
	@result Returns pointer to the control block if successfull else NULL.
*/
void* IOFireWireIP::initializeCBlk(UInt32	memorySize)
{
    UInt32	*memoryPool = 0;
	UInt32	i	= 0;
	UInt32	*ptr;

    // memorySize = CBLK_MEMORY_SIZE;
	fMemoryPool = memoryPool = (UInt32*)IOMalloc(memorySize);

    if(memoryPool == NULL){
        IOLog("IOFireWireIP:: memory pool for CBLKS not allocated \n");
        return NULL;
    }
    
    // Set all of the area to 0
    memset(memoryPool, 0, sizeof(memorySize));

	for (i = 0; i < N_CBLK; i++) 
	{
		*((void **) memoryPool) = fLcb->freeCBlk;
		fLcb->freeCBlk = (struct cblk*)memoryPool;
		memoryPool = (UInt32 *) ((ULONG) memoryPool + MAX_CBLK_SIZE);
		memorySize -= MAX_CBLK_SIZE;
	}
	
    fLcb->cFreeCBlk = fLcb->minFreeCBlk = fLcb->nCBlk = N_CBLK;

	ptr = (UInt32*)&fLcb->mcapState;
	memset(ptr, 0, sizeof(MCB)*MAX_CHANNEL_DES);
    
    return memoryPool;
}

/*!
	@function allocateCBlk
	@abstract allocates a generic control block
	@param lcb - the firewire link control block for this interface.
	@result returns a preallocated control block
*/	
void *IOFireWireIP::allocateCBlk(LCB *lcb) {   /* Grab a free CBLK and return it */

    CBLK *cBlk;

    IOTakeLock(ipLock);

    if ((cBlk = lcb->freeCBlk) != NULL) {
        lcb->freeCBlk = cBlk->next;
        lcb->cFreeCBlk--;
        memset(cBlk, 0, MAX_CBLK_SIZE);
    }
   
    if ((lcb->freeCBlk == NULL) != (lcb->cFreeCBlk == 0))
        IOLog("allocateCBlk error: lcb->freeCBlk %08lX lcb->cFreeCBlk %04X\n\r",
                    (ULONG) lcb->freeCBlk, lcb->cFreeCBlk);
   
   IOUnlock(ipLock);
   
   lcb->minFreeCBlk = MIN(lcb->minFreeCBlk, lcb->cFreeCBlk);
   
   if (cBlk == NULL)
      IOLog("ERROR: No CBLK available!\n\r");
      
   return(cBlk);

}


/*!
	@function deallocateCBlk
	@abstract deallocates a generic control block
	@param lcb - the firewire link control block for this interface.
	@param cBlk - a control block i.e: arb, rcb or drb.
	@result returns a preallocated control block
*/	
void IOFireWireIP::deallocateCBlk(LCB *lcb, void *cBlk) {  

    IOTakeLock(ipLock);
   
    if (cBlk != NULL) {
        ((CBLK *) cBlk)->next = lcb->freeCBlk;
        lcb->freeCBlk = (CBLK *)cBlk;
        lcb->cFreeCBlk++;
    }
    
    if ((lcb->freeCBlk == NULL) != (lcb->cFreeCBlk == 0))
	{
        IOLog("deallocateCBlk error: lcb->freeCBlk %08lX lcb->cFreeCBlk %04X\n\r",
                                    (ULONG) lcb->freeCBlk, lcb->cFreeCBlk);
	}
    
    IOUnlock(ipLock);
}

/*!
	@function cleanFWArbCache
	@abstract cleans the Link control block's stale arb's. Invoked from the 
				ArpTimer to clean up FW specific ARB cleanup. Unresolved ARB's
				are returned to the free CBLKs
	@param lcb - the firewire link control block for this interface.
	@result void.
*/
void IOFireWireIP::cleanFWArbCache(LCB *lcb)
{
    ARB *arb = (ARB *) &lcb->unicastArb;

    IOTakeLock(ipLock);
        
    while ((arb = arb->next) != NULL)
	{
        if(arb->datagramPending == TRUE)
		{
            if(arb->timer > 1)
			{
                // The IP Address never resolved, lets decrement the timer
                arb->timer--;
            }
			else if (arb->timer == 1) 
			{
				IOLog("IOFireWireIP: unresolved arb cleaned up of ipaddress 0x%lx \n", arb->ipAddress);
				arb->handle.unicast.deviceID = kInvalidIPDeviceRefID;
                // time to clean up
                unlinkCBlk(&lcb->unicastArb, arb);
                deallocateCBlk(lcb, arb);
            }
        }
    }
   
    IOUnlock(ipLock);
}

/*!
	@function getDeviceID
	@abstract returns a fireWire device object for the GUID
	@param lcb - the firewire link control block for this interface.
    @param eui64 - global unique id of a device on the bus.
    @param itsMac - destination is Mac or not.
	@result Returns IOFireWireNub if successfull else 0.
*/
UInt32 IOFireWireIP::getDeviceID(LCB *lcb, UWIDE eui64, BOOLEAN *itsMac) {  

    // Returns DRB if EUI-64 matches
    DRB *drb = getDrbFromEui64(lcb, eui64);   

    // IOLog(" getDeviceID for EUI-64 0x%08X%08X DRB %x \n\r", eui64.hi, eui64.lo, drb);

    // Device reference ID already created
    if (drb != NULL) 
	{              
		*itsMac = drb->itsMac;
        // Just return it to caller
        return(drb->deviceID);        
    }
    else 
	{
		*itsMac = false;
        // Get an empty DRB
        return(kInvalidIPDeviceRefID);
    }
}

/*! 
	@function getArbFromFwAddr
	@abstract Locates the corresponding Unicast ARB (Address resolution block) for GUID
	@param lcb - the firewire link control block for this interface.
	@param FwAddr - global unique id of a device on the bus.
	@result Returns ARB if successfull else NULL.
*/
ARB *IOFireWireIP::getArbFromFwAddr(LCB *lcb, u_char *fwaddr) 
{
	ARB *arb = (ARB *) &lcb->unicastArb;
	
	IOTakeLock(ipLock);
	
	while ((arb = arb->next) != NULL)
		if (bcmp(fwaddr, arb->fwaddr, FIREWIRE_ADDR_LEN) == 0)
					break;
				
	IOUnlock(ipLock);
	
	return(arb);
}

/*!
	@function getDrbFromEui64
	@abstract Locates the corresponding DRB (device reference block) for GUID
	@param lcb - the firewire link control block for this interface.
	@param eui64 - global unique id of a device on the bus.
	@result Returns DRB if successfull else NULL.
*/
DRB *IOFireWireIP::getDrbFromEui64(LCB *lcb, UWIDE eui64) {  

    DRB *drb = (DRB *) &lcb->activeDrb;
    
    IOTakeLock(ipLock);
   
    while ((drb = drb->next) != NULL)
        if (drb->eui64.hi == eui64.hi && drb->eui64.lo == eui64.lo)
            break;
   
    IOUnlock(ipLock);
   
    return(drb);
}

/*!
	@function getDrbFromFwAddr
	@abstract Locates the corresponding DRB (device reference block) for GUID
	@param lcb - the firewire link control block for this interface.
	@param fwaddr - global unique id of a device on the bus.
	@result Returns DRB if successfull else NULL.
*/
DRB *IOFireWireIP::getDrbFromFwAddr(LCB *lcb, u_char *fwaddr) 
{  
    DRB *drb = (DRB *) &lcb->activeDrb;
    
    IOTakeLock(ipLock);
   
    while ((drb = drb->next) != NULL)
        if (bcmp(fwaddr, drb->fwaddr, FIREWIRE_ADDR_LEN) == 0)
            break;
   
    IOUnlock(ipLock);
   
    return(drb);
}

/*! 
	@function getArbFromEui64
	@abstract Locates the corresponding Unicast ARB (Address resolution block) for GUID
	@param lcb - the firewire link control block for this interface.
	@param eui64 - global unique id of a device on the bus.
	@result Returns ARB if successfull else NULL.
*/
ARB *IOFireWireIP::getArbFromEui64(LCB *lcb, UWIDE eui64) {  

    ARB *arb = (ARB *) &lcb->unicastArb;

    IOTakeLock(ipLock);
   
    while ((arb = arb->next) != NULL)
        if (arb->eui64.hi == eui64.hi && arb->eui64.lo == eui64.lo)
            break;
   
    IOUnlock(ipLock);
   
    return(arb);
}


/*!
	@function getDrbFromDeviceID
	@abstract Locates the corresponding DRB (Address resolution block) for IOFireWireNub
	@param lcb - the firewire link control block for this interface.
    @param deviceID - IOFireWireNub to look for.
	@result Returns DRB if successfull else NULL.
*/
DRB *IOFireWireIP::getDrbFromDeviceID(LCB *lcb, void *deviceID){   

    DRB *drb = (DRB *) &lcb->activeDrb;

    IOTakeLock(ipLock);
   
    while ((drb = drb->next) != NULL)
        if (drb->deviceID == (UInt32)deviceID)
            break;
    
    IOUnlock(ipLock);
   
    return(drb);
}

/*!
	@function getMulticastArb
	@abstract Locates the corresponding multicast ARB (Address resolution block) for ipaddress
	@param lcb - the firewire link control block for this interface.
	@param ipAddress - destination ipaddress to send the multicast packet.
	@result Returns ARB if successfull else NULL.
*/
ARB *IOFireWireIP::getMulticastArb(LCB *lcb, UInt32 ipAddress){  

    ARB *arb = (ARB *) &lcb->multicastArb;

    while ((arb = arb->next) != NULL)
        if (arb->ipAddress == ipAddress)
            break;
         
    return(arb);
}

/*!
	@function getUnicastArb
	@abstract Locates the corresponding unicast ARB (Address resolution block) for ipaddress
	@param lcb - the firewire link control block for this interface.
    @param ipAddress - destination ipaddress to send the unicast packet.
	@result Returns ARB if successfull else NULL.
*/
ARB *IOFireWireIP::getUnicastArb(LCB *lcb, UInt32 ipAddress){
    
    ARB *arb = (ARB *) &lcb->unicastArb;
    
	// IOLog(" getUnicastArb called %x \n\r", ipAddress);
	
    IOTakeLock(ipLock);
    
    while ((arb = arb->next) != NULL)
        if (arb->ipAddress == ipAddress)
            break;
    
    IOUnlock(ipLock);
    
	// IOLog(" getUnicastArb exit %x \n\r", arb);
	
    return(arb);
}

/*!
	@function getRcb
	@abstract Locates a reassembly control block.
	@param lcb - the firewire link control block for this interface.
    @param sourceID - source nodeid which generated the fragmented packet.
    @param dgl - datagram label for the fragmented packet.
	@result Returns RCB if successfull else NULL.
*/
RCB *IOFireWireIP::getRcb(LCB *lcb, UInt16 sourceID, UInt16 dgl){

    RCB *rcb = (RCB *) &lcb->activeRcb;

    IOTakeLock(ipLock);
   
    while ((rcb = rcb->next) != NULL) {
        if (rcb->sourceID == sourceID && rcb->dgl == dgl)
            break;
    }
   
    IOUnlock(ipLock);

    return(rcb);
}

/*!
	@function linkCBlk
	@abstract generic function to queue a control block to its corresponding list.
	@param queueHead - queuehead of the rcb, arb or drb.
	@param cBlk - control block of type rcb, arb or drb .
	@result void.
*/
void IOFireWireIP::linkCBlk(void *queueHead, void *cBlk) { 

    IOTakeLock(ipLock);
    
    ((CBLK *) cBlk)->next = (CBLK*)(*((void **) queueHead));
    *((void **) queueHead) = cBlk;
   
    IOUnlock(ipLock);
}

/*!
	@function unlinkCBlk
	@abstract generic function to dequeue a control block from its corresponding list.
	@param queueHead - queuehead of the rcb, arb or drb.
	@param cBlk - control block of type rcb, arb or drb .
	@result void.
*/
void IOFireWireIP::unlinkCBlk(void *queueHead, void *cBlk) { 
    
    IOTakeLock(ipLock);
    while (*((void **) queueHead) != NULL)
        if (*((void **) queueHead) == cBlk) {
            *((void **) queueHead) = ((CBLK *) cBlk)->next;
            ((CBLK *) cBlk)->next = NULL;
            break;
        } else
            queueHead = *((void **) queueHead);
   
   IOUnlock(ipLock);
}

// Display the reassembly control block
void IOFireWireIP::showRcb(RCB *rcb) {
	if (rcb != NULL) {
      IOLog("RCB %p\n\r", rcb);
      IOLog(" sourceID %04X dgl %u etherType %04X mBlk %p\n\r", rcb->sourceID, rcb->dgl, rcb->etherType, rcb->mBuf);
      IOLog(" datagram %p datagramSize %u residual %u\n\r", rcb->datagram, rcb->datagramSize, rcb->residual);
	}
}

void IOFireWireIP::showArb(ARB *arb) {

   u_char ipAddress[4];

   IOLog("ARB %p\n\r", arb);
   memcpy(ipAddress, &arb->ipAddress, sizeof(ipAddress));
   IOLog(" IP address %u.%u.%u.%u EUI-64 %08lX %08lX\n\r", ipAddress[0],
          ipAddress[1], ipAddress[2], ipAddress[3], arb->eui64.hi,
          arb->eui64.lo);
   IOLog(" fwAddr  %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n\r", arb->fwaddr[0],
          arb->fwaddr[1], arb->fwaddr[2], arb->fwaddr[3], arb->fwaddr[4],
          arb->fwaddr[5], arb->fwaddr[6], arb->fwaddr[7]);
   IOLog(" Handle: %08lX %02X %02X %04X%08lX\n\r", arb->handle.unicast.deviceID,
          arb->handle.unicast.maxRec, arb->handle.unicast.spd,
          arb->handle.unicast.unicastFifoHi, arb->handle.unicast.unicastFifoLo);
   IOLog(" Timer %d datagramPending %d\n\r", arb->timer, arb->datagramPending);

}

void IOFireWireIP::showHandle(TNF_HANDLE *handle) {

   if (handle->unicast.deviceID != kInvalidIPDeviceRefID)
      IOLog("   Unicast handle: %08lX %02X %02X %04X%08lX\n\r",
             handle->unicast.deviceID, handle->unicast.maxRec,
             handle->unicast.spd, handle->unicast.unicastFifoHi,
             handle->unicast.unicastFifoLo);
   else
      IOLog("   Multicast handle: 00000000 %02X %02X %02X %08lX\n\r",
             handle->multicast.maxRec, handle->multicast.spd,
             handle->multicast.channel, htonl(handle->multicast.groupAddress));

}

void IOFireWireIP::showDrb(DRB *drb) 
{
   if (drb != NULL) {
      IOLog("DRB 0x%p (associated with LCB 0x%p)\n\r", drb, drb->lcb);
      IOLog(" Device ID %08lX EUI-64 %08lX %08lX\n\r", drb->deviceID, drb->eui64.hi, drb->eui64.lo);
      IOLog(" timer %08lX maxPayload %d maxSpeed %d\n\r", drb->timer, drb->maxPayload, drb->maxSpeed);
   }
}

void IOFireWireIP::showLcb() {

   CBLK *cBlk;
   UNSIGNED cCBlk = 0;

   IOLog("LCB at %p (driver object at %p)\n\r", fLcb,
          fLcb->driverObject);
   IOLog(" Node ID %04X maxPayload %u maxSpeed %u busGeneration 0x%08lX\n\r",
          fLcb->ownNodeID, fLcb->ownMaxPayload,
          fLcb->ownMaxSpeed, fLcb->busGeneration);
   IOLog(" Free CBLKs %u (of %u in pool)\n\r", fLcb->cFreeCBlk,
          fLcb->nCBlk);
   IOLog(" CBLK Low water mark %u\n\r", fLcb->minFreeCBlk);
   
   // Display the arb's
   if (fLcb->unicastArb == NULL)
      IOLog(" No unicast ARBs\n\r");
   else {
      IOLog(" Unicast ARBs\n\r");
      cBlk = (CBLK*)&fLcb->unicastArb;
      while ((cBlk = cBlk->next) != NULL) {
         cCBlk++;
         IOLog("  %p\n\r", cBlk);
		 showArb((ARB*)cBlk);
      }
   }
   
   // Display the multicast arb's
   if (fLcb->multicastArb == NULL)
      IOLog(" No multicast ARBs\n\r");
   else {
      IOLog(" Multicast ARBs\n\r");
      cBlk = (CBLK*)&fLcb->multicastArb;
      while ((cBlk = cBlk->next) != NULL) {
         cCBlk++;
         IOLog("  %p\n\r", cBlk);
      }
   }
   
   // Display the active DRB
   if (fLcb->activeDrb == NULL)
      IOLog(" No active DRBs\n\r");
   else {
      IOLog(" Active DRBs\n\r");
      cBlk = (CBLK*)&fLcb->activeDrb;
      while ((cBlk = cBlk->next) != NULL) {
         cCBlk++;
         IOLog("  %p\n\r", cBlk);
		 showDrb((DRB*)cBlk);
      }
   }
   
   // Display the active RCB
   if (fLcb->activeRcb == NULL)
      IOLog(" No active RCBs\n\r");
   else {
      IOLog(" Active RCBs\n\r");
      cBlk = (CBLK*)&fLcb->activeRcb;
      while ((cBlk = cBlk->next) != NULL) {
         cCBlk++;
         IOLog("  %p\n\r", cBlk);
		 showRcb((RCB*)cBlk);
      }
   }
   
   IOLog(" %u CBLKs in use\n\r", cCBlk);
   if (cCBlk + fLcb->cFreeCBlk != fLcb->nCBlk)
      IOLog(" CBLK accounting error!\n\r");

}

void IOFireWireIP::updateStatistics()
{
	if(fBroadcastReceiveClient != NULL)
	{
		fIsoRxOverrun = fBroadcastReceiveClient->getOverrunCounter();
	}
}

#pragma mark -
#pragma mark еее mbuf utility routines еее

//---------------------------------------------------------------------------
// Allocates a mbuf chain. Each mbuf in the chain is aligned according to
// the constraints that are currently ignored.
// The last mbuf in the chain will be guaranteed to be length aligned if 
// the 'size' argument is a multiple of the length alignment.
//
// The m->m_len and m->pkthdr.len fields are updated by this function.
// This allows the driver to pass the mbuf chain obtained through this
// function to the IOMbufMemoryCursor object directly.
//
// If (size + alignments) is smaller than MCLBYTES, then this function
// will always return a single mbuf header or cluster.
//
// The allocation is guaranteed not to block. If a packet cannot be
// allocated, this function will return NULL.

#define IO_APPEND_MBUF(head, tail, m) {   \
    if (tail) {                           \
        (tail)->m_next = (m);             \
        (tail) = (m);                     \
    }                                     \
    else {                                \
        (head) = (tail) = (m);            \
        (head)->m_pkthdr.len = 0;         \
    }                                     \
}

#define IO_ALIGN_MBUF_START(m, mask) {                                 \
    if ( (mask) & mtod((m), vm_address_t) ) {                          \
        (m)->m_data = (caddr_t) (( mtod((m), vm_address_t) + (mask) )  \
                                 & ~(mask));                           \
    }                                                                  \
}

#define IO_ALIGN_MBUF(m, size, smask, lmask) {   \
    IO_ALIGN_MBUF_START((m), (smask));           \
    (m)->m_len = ((size) - (smask)) & ~(lmask);  \
}

static struct mbuf * allocateMbuf( UInt32 size,
                                   UInt32 how,
                                   UInt32 smask,
                                   UInt32 lmask )
{
    struct mbuf * m;
    struct mbuf * head = 0;
    struct mbuf * tail = 0;
    UInt32        capacity;

    while ( size )
    {
        // Allocate a mbuf. For the initial mbuf segment, allocate a
        // mbuf header.

        if ( head == 0 )
        {
            MGETHDR( m, how, MT_DATA );
            capacity = MHLEN;
        }
        else
        {
            MGET( m, how, MT_DATA );
            capacity = MLEN;
        }

        if ( m == 0 ) goto error;  // mbuf allocation error

        // Append the new mbuf to the tail of the mbuf chain.

        IO_APPEND_MBUF( head, tail, m );

        // If the remaining size exceed the buffer size of a normal mbuf,
        // then promote it to a cluster. Currently, the cluster size is
        // fixed to MCLBYTES bytes.

        if ( ( size + smask + lmask ) > capacity )
        {
            MCLGET( m, how );
            if ( (m->m_flags & M_EXT) == 0 ) goto error;
            capacity = MCLBYTES;
        }

        // Align the mbuf per driver's specifications.

        IO_ALIGN_MBUF( m, capacity, smask, lmask );

        // Compute the number of bytes needed after accounting for the
        // current mbuf allocation.

        if ( (UInt) m->m_len > size )
            m->m_len = size;

        size -= m->m_len;

        // Update the total length in the packet header.

        head->m_pkthdr.len += m->m_len;
    }

    return head;

error:
    if ( head ) m_freem(head);
    return 0;
}

static struct mbuf * getPacket( UInt32 size,
                                UInt32 how,
                                UInt32 smask,
                                UInt32 lmask )
{
    struct mbuf * m = NULL;

    do {
        // Handle the simple case where the requested size is small
        // enough for a single mbuf. Otherwise, go to the more costly
        // route and call the generic mbuf allocation routine.

        if ( ( size + smask ) <= MCLBYTES )
        {
            if ( ( size + smask ) > MHLEN )
            {
                /* MGETHDR+MCLGET under one single lock */
                m = m_getpackets( 1, 1, how );
            }
            else
            {
                MGETHDR( m, how, MT_DATA );
            }
            if ( m == 0 ) break;

            // Align start of mbuf buffer.

            IO_ALIGN_MBUF_START( m, smask );

            // No length adjustment for single mbuf.
            // Driver gets what it asked for.

            m->m_pkthdr.len = m->m_len = size;
        }
        else
        {
            m = allocateMbuf( size, how, smask, lmask );
        }
    }
    while ( false );

    return m;
}

void moveMbufWithOffset(SInt32 tempOffset, struct mbuf **srcm, vm_address_t *src, SInt32 *srcLen)
{
    struct mbuf *temp = NULL;

	for(;;) 
	{

		if(tempOffset == 0)
			break;

		if(*srcm == NULL)
			break;

		if(*srcLen < tempOffset) 
		{
			tempOffset = tempOffset - *srcLen;
			temp = (*srcm)->m_next; 
			*srcm = temp;
			if(*srcm != NULL)
				*srcLen = (*srcm)->m_len;
			continue;
		} 
		else if (*srcLen > tempOffset) 
		{
			*srcLen = (*srcm)->m_len;
			*src = mtod(*srcm, vm_offset_t);
			*src += tempOffset;
			*srcLen -= tempOffset;
			break;
		} 
		else if (*srcLen == tempOffset) 
		{
			temp = (*srcm)->m_next; 
			*srcm = temp;
			if(*srcm != NULL) 
			{
				*srcLen = (*srcm)->m_len;
				*src = mtod(*srcm, vm_offset_t);
			}
			break;
		}
	}
}

/*!
	@function getMBuf
	@abstract Allocate Mbuf of required size.
	@param size - required size for the allocated mbuf.
	@result NULL if failed else a valid mbuf.
*/
struct mbuf * IOFireWireIP::getMBuf(UInt32 size)
{
	struct mbuf *m;

    IOTakeLock(ipLock);

	m = getPacket(size, M_DONTWAIT, 0, 0);

	if (m != NULL)
    {
		// Set the interface pointer to our ifnet pointer
		m->m_pkthdr.rcvif = ifp; 
	}

    IOUnlock(ipLock);

	return m;
}


/*!
	@function bufferToMbuf
	@abstract Copies buffer to Mbuf.
	@param m - destination mbuf.
	@param offset - offset into the mbuf data pointer.
	@param srcbuf - source buf.
	@param srcbufLen - source buffer length.
	@result bool - true if success else false.
*/
bool IOFireWireIP::bufferToMbuf(struct mbuf *m, 
								UInt32 offset, 
								UInt8  *srcbuf, 
								UInt32 srcbufLen)
{
	vm_address_t src, dst;
    SInt32 srcLen, dstLen, copylen, tempOffset;
    struct mbuf *temp;
    struct mbuf *srcm;

    IOTakeLock(ipLock);

    temp = NULL;
    srcm = NULL;

	// Get the source
	srcm = m; 
	srcLen = srcm->m_len;
    src = mtod(srcm, vm_offset_t);

	//IOLog("bufferToMbuf+\n\r");
	
	//
	// Mbuf manipulated to point at the correct offset
	//
	tempOffset = offset;

	moveMbufWithOffset(tempOffset, &srcm, &src, &srcLen);

	// Modify according to our fragmentation
	// src += *offset;
	// srcLen -= *offset;

	dstLen = srcbufLen;
    copylen = dstLen;
	dst = (vm_address_t)srcbuf;
	
	
    for (;;) {
	
		//IOLog("  offset %d mbuflen %d pktlen %d \n\r", offset, srcLen, dstLen);


        if (srcLen < dstLen) {

            // Copy remainder of buffer to current mbuf upto m_len.
            BCOPY(dst, src, srcLen);
            dst += srcLen;
            dstLen -= srcLen;
			copylen -= srcLen;
			// set the offset
			// offset = offset + srcLen; 
			
			//IOLog("srcLen < dstLen : copyLen = %d\n", copylen);

			if(copylen == 0){
				// set the new mbuf to point to the new chain
				temp = srcm->m_next; 
				srcm = temp;
				break;
			}
            // Move on to the next source mbuf.
            temp = srcm->m_next; assert(temp);
            srcm = temp;
            srcLen = srcm->m_len;
            src = mtod(srcm, vm_offset_t);
        }
        else if (srcLen > dstLen) {
			//
            // Copy some of buffer to src mbuf, since mbuf 
			// has more space.
			//
            BCOPY(dst, src, dstLen);
            src += dstLen;
            srcLen -= dstLen;
            copylen -= dstLen;
			// set the offset
			// offset = offset + dstLen; 
            // Move on to the next destination mbuf.
			// IOLog("srcLen > dstLen : copyLen = %d\n", copylen);
			
			if(copylen == 0){
				// set the new mbuf to point to the new chain
	//			temp = srcm; 
	//			srcm = temp;
				break;
			}
        }
        else {  /* (srcLen == dstLen) */
            // copy remainder of src into remaining space of current mbuffer
            BCOPY(dst, src, srcLen);
			copylen -= srcLen;

			// IOLog("srcLen == dstLen : copyLen = %d\n", copylen);
			
			if(copylen == 0){
				// set the offset
				// offset = 0; 
				// set the new mbuf to point to the new chain
				temp = srcm->m_next; 
				srcm = temp;
				break;
			}
            // Free current mbuf and move the current onto the next
            srcm = srcm->m_next;

            // Do we have any data left to copy?
            if (dstLen == 0)
				break;

            srcLen = srcm->m_len;
            src = mtod(srcm, vm_offset_t);
        }
    }
	// IOLog("bufferToMbuf-\n\r");
    IOUnlock(ipLock);
	
	return true;
}

/*!
	@function mbufTobuffer
	@abstract Copies mbuf data into the buffer pointed by IOMemoryDescriptor.
	@param src - source mbuf.
	@param offset - offset into the mbuf data pointer.
	@param dstbuf - destination buf.
	@param dstbufLen - destination buffer length.
	@param length - length to copy.
	@result NULL if copied else should be invoked again till 
			the residual is copied into the buffer.
*/
struct mbuf *IOFireWireIP::mbufTobuffer(struct mbuf *m, 
								UInt32 *offset, 
								UInt8  *dstbuf, 
								UInt32 dstbufLen, 
								UInt32 length)
{
	vm_address_t src, dst;
    SInt32 srcLen, dstLen, copylen, tempOffset;
    struct mbuf *temp = NULL;
    struct mbuf *srcm = NULL;

	// Get the source
	srcm = m; 
	srcLen = srcm->m_len;
    src = mtod(srcm, vm_offset_t);
	
	//
	// Mbuf manipulated to point at the correct offset
	//
	tempOffset = *offset;

	moveMbufWithOffset(tempOffset, &srcm, &src, &srcLen);

	// Modify according to our fragmentation
	dstLen = length;
    copylen = dstLen;
	dst = (vm_address_t)dstbuf;
	
	
    for (;;) {
	
		//IOLog("  mbuf->buf::offset %d srcLen %d dstLen %d \n\r", *offset, srcLen, dstLen);

        if (srcLen < dstLen) {

            // Copy remainder of src mbuf to current dst.
            BCOPY(src, dst, srcLen);
            dst += srcLen;
            dstLen -= srcLen;
			copylen -= srcLen;
			// set the offset
			*offset = *offset + srcLen; 
			
			if(copylen == 0){
				// set the new mbuf to point to the new chain
				temp = srcm->m_next; 
				srcm = temp;
				break;
			}
            // Move on to the next source mbuf.
            temp = srcm->m_next; assert(temp);
            srcm = temp;
            srcLen = srcm->m_len;
            src = mtod(srcm, vm_offset_t);
        }
        else if (srcLen > dstLen) {
            // Copy some of src mbuf to remaining space in dst mbuf.
            BCOPY(src, dst, dstLen);
            src += dstLen;
            srcLen -= dstLen;
            copylen -= dstLen;
			// set the offset
			*offset = *offset + dstLen; 

            // Move on to the next destination mbuf.
			if(copylen == 0){
				// set the new mbuf to point to the new chain
//				temp = srcm; 
//				srcm = temp;
				break;
			}
        }
        else {  /* (srcLen == dstLen) */
            // copy remainder of src into remaining space of current dst
            BCOPY(src, dst, srcLen);
			copylen -= srcLen;
			
			if(copylen == 0){
				// set the offset
				*offset = 0; 
				// set the new mbuf to point to the new chain
				temp = srcm->m_next; 
				srcm = temp;
				break;
			}
            // Free current mbuf and move the current onto the next
            srcm = srcm->m_next;

            // Do we have any data left to copy?
            if (dstLen == 0)
				break;

            srcLen = srcm->m_len;
            src = mtod(srcm, vm_offset_t);
        }
    }
	return temp;
}

/*!
	@function freeMBuf
	@abstract free the allocated mbuf.
	@param struct mbuf *m.
	@result void.
*/
void IOFireWireIP::freeMBuf(struct mbuf *m) {

    IOTakeLock(ipLock);

	//_logMbuf(m);
	m_freem(m);

    IOUnlock(ipLock);
}

//---------------------------------------------------------------------------
// Used for debugging only. Log the mbuf fields.
void _logMbuf(struct mbuf * m)
{
	UInt8	*bytePtr;
	
    if (!m) {
        IOLog("logMbuf: NULL mbuf\n");
        return;
    }
    
    while (m) {
        IOLog("m_next   : %08x\n", (UInt) m->m_next);
        IOLog("m_nextpkt: %08x\n", (UInt) m->m_nextpkt);
        IOLog("m_len    : %d\n",   (UInt) m->m_len);
        IOLog("m_data   : %08x\n", (UInt) m->m_data);
        IOLog("m_type   : %08x\n", (UInt) m->m_type);
        IOLog("m_flags  : %08x\n", (UInt) m->m_flags);
        
        if (m->m_flags & M_PKTHDR)
            IOLog("m_pkthdr.len  : %d\n", (UInt) m->m_pkthdr.len);

        if (m->m_flags & M_EXT) {
            IOLog("m_ext.ext_buf : %08x\n", (UInt) m->m_ext.ext_buf);
            IOLog("m_ext.ext_size: %d\n", (UInt) m->m_ext.ext_size);
        }
		
		IOLog("m_data -> \t\t") ;
		
		if(m->m_data != NULL){
		
			bytePtr = (UInt8*)m->m_data;
						
			for(SInt32 index=0; index < min(m->m_len, 12); index++)
			{
				if ((index & 0x3) == 0)
				{
					IOLog(" ") ;
					if ((index & 0x7) == 0)
					{
						IOLog("   ") ;
						if ((index & 0xF) == 0)
							IOLog("\n\t\t") ;
					} 
				}
				IOLog("%02X", (unsigned char)bytePtr[index]) ;
			}
			IOLog("\n\n") ;
		}
        
        m = m->m_next;
    }
    IOLog("\n");
}
void _logPkt(void *pkt, UInt16 len)
{
	UInt8 	*bytePtr;

	///
	// start log code
	///
	bytePtr = (UInt8*)pkt;
	
	IOLog("pkt {\n") ;

	for(SInt32 index=0; index<len; index++)
	{
		if ((index & 0x3) == 0)
		{
			IOLog(" ") ;
			if ((index & 0x7) == 0)
			{
				IOLog("   ") ;
				if ((index & 0xF) == 0)
					IOLog("\n\t\t") ;
			} 
		}
		IOLog("%02X", (unsigned char)bytePtr[index]) ;
	}
	IOLog("}\n\n") ;
	//////
	// end log code
	//////
}

#pragma mark -
#pragma mark еее firewire irm routines еее

UInt32 IOFireWireIP::initIsocMgmtCmds()
{
	// Should be moved to start and free methods !!
	fReadCmd = new IOFWReadQuadCommand;
    if(fReadCmd)
        fReadCmd->initAll(fControl, 0, FWAddress(), NULL, 0, NULL, NULL);

    fLockCmd = new IOFWCompareAndSwapCommand;
    if(fLockCmd)
        fLockCmd->initAll(fControl, 0, FWAddress(), NULL, NULL, 0, NULL, NULL);
	
	return fReadCmd != NULL && fLockCmd != NULL; 
}

void IOFireWireIP::freeIsocMgmtCmds()
{
	if(fReadCmd) 
	{
		//IOLog("%d fReadCmd retain count %p fReadCmd %p %d\n", fReadCmd->getRetainCount(),  fReadCmd, &fReadCmd, __LINE__);
        fReadCmd->release();
	}
    
	if(fLockCmd)
	{
		//IOLog("%d fLockCmd retain count %p fLockCmd %p %d\n", fLockCmd->getRetainCount(),  fLockCmd, &fLockCmd, __LINE__);
		fLockCmd->release();
	}
}

void IOFireWireIP::getChan31()
{
	UInt32 		newVal ;
	FWAddress 	addr(kCSRRegisterSpaceBaseAddressHi, kCSRBandwidthAvailable) ;
	UInt32 		oldIRM[3] ;
	UInt32 		channel ;
	IOReturn	err = kIOReturnSuccess;
	//UInt64 		inAllowedChans;
    UInt32 		generation;	
	
	// contact irm and allocate one of the available channel
	fControl->getIRMNodeID(generation, addr.nodeID);

	fReadCmd->reinit(generation, addr, oldIRM, 3);
	fReadCmd->setMaxPacket(4);		
	err = fReadCmd->submit();

	channel = 31;
	
	if (!err)
	{
		UInt32*		oldPtr;

		// Claim channel
		if(channel < 32)
		{
			addr.addressLo = kCSRChannelsAvailable31_0;
			oldPtr = &oldIRM[1];
			newVal = *oldPtr & ~(1<<(31 - channel));
		}
		else
		{
			addr.addressLo = kCSRChannelsAvailable63_32;
			oldPtr = &oldIRM[2];
			newVal = *oldPtr & ~( (UInt64)1 << (63 - channel) );
		}
	
		fLockCmd->reinit(generation, addr, oldPtr, &newVal, 1);
		err = fLockCmd->submit();
		
		if (!err && !fLockCmd->locked(oldPtr))
			err = kIOReturnCannotLock ;
	}
}

//
// Receive async stream packets
// Just set the required channel in channelRxMask variable  
//
UInt32 IOFireWireIP::acquireChannel(UInt32 *pChannel, Boolean autoReAllocate, UInt32	resourceAllocationFlags)
{
	UInt32 		newVal ;
	FWAddress 	addr(kCSRRegisterSpaceBaseAddressHi, kCSRBandwidthAvailable) ;
	UInt32 		oldIRM[3] ;
	UInt32 		channel = 0;
	IOReturn	err = kIOReturnSuccess;
	UInt64 		inAllowedChans;
    UInt32 		generation;	
	
	inAllowedChans = ~(UInt64)0;

	IOLog(" %s %d\n", __FILE__, __LINE__);
	
	if(autoReAllocate & kDoNotAllocate) // its for receive
	{
		// set the required channel bit in channelRxMask
		
		//
		// we are right now not worried about who allocated
		// the channel, just start listening
		//
	}
	else // its for send
	{
		// contact irm and allocate one of the available channel
		fControl->getIRMNodeID(generation, addr.nodeID);
	
		fReadCmd->reinit(generation, addr, oldIRM, 3);
		fReadCmd->setMaxPacket(4);		
		err = fReadCmd->submit();

		if (!err)
		{
			// mask inAllowedChans by channels IRM has available
			inAllowedChans &= (UInt64)(oldIRM[2]) | ((UInt64)oldIRM[1] << 32);
		}
#ifdef FIREWIRETODO // Should be moved to channels available function and can set the available channels
	
		// if we have an error here, the bandwidth wasn't allocated
		if (!err)
		{
			for(channel=0; channel<64; channel++)
			{
				if( inAllowedChans & ((UInt64)1 << ( 63 - channel )) )
					break;
			}

			if(channel == 64) {
				IOLog("IOFireWireIP: No resources, will use 0x1f chan");
				err = kIOReturnNoResources;
			}
		}
#endif
		if (!err)
		{
			// mask inAllowedChans by channels IRM has available
			inAllowedChans &= (UInt64)(oldIRM[2]) | ((UInt64)oldIRM[1] << 32);
		}

		// if we have an error here, the bandwidth wasn't allocated
		if (!err)
		{
			channel = *pChannel;
			if( inAllowedChans & ((UInt64)1 << ( 63 - channel )) != 0){
				IOLog("IOFireWireIP: No resources, will use 0x1f chan");
				err = kIOReturnNoResources;
			}
		}
	
		if (!err)
		{
			UInt32*		oldPtr;

			// Claim channel
			if(channel < 32)
			{
				addr.addressLo = kCSRChannelsAvailable31_0;
				oldPtr = &oldIRM[1];
				newVal = *oldPtr & ~(1<<(31 - channel));
			}
			else
			{
				addr.addressLo = kCSRChannelsAvailable63_32;
				oldPtr = &oldIRM[2];
				newVal = *oldPtr & ~( (UInt64)1 << (63 - channel) );
			}
		
			fLockCmd->reinit(generation, addr, oldPtr, &newVal, 1);
			err = fLockCmd->submit();
			if (!err && !fLockCmd->locked(oldPtr))
				err = kIOReturnCannotLock ;
		}

		if (!err)
		{ 
			// *pChannel = channel;
			if(channel >= 32)
				fReAllocateChannel.hi |= (kChannelPrime >> (channel - 32));
			else
				fReAllocateChannel.lo |= (kChannelPrime >> channel);
		}
	}
	
	return err;
}

void IOFireWireIP::releaseChannel(UInt32 channel,UInt32 releaseFlags)
{
	FWAddress addr(kCSRRegisterSpaceBaseAddressHi, kCSRBandwidthAvailable);
    UInt32 generation = 0;
    UInt32 newVal;
    UInt32 oldVal;
    IOReturn result = kIOReturnSuccess;
    bool tryAgain;
	bool claim = false;
	UInt32 fChannel = channel;

	IOLog(" %s %d\n", __FILE__, __LINE__);

	if(channel == DEFAULT_BROADCAST_CHANNEL)
		return;

	//
	// If we allocated with the IRM then we free it only
	// happens when we advertise and transmit packet in 
	// a specific channel 
	//
	if (!(releaseFlags & kDoNotDeallocate)) { 
		if(fChannel != 64) {
			UInt32 mask;
			if(fChannel <= 31) {
				addr.addressLo = kCSRChannelsAvailable31_0;
				mask = 1 << (31-fChannel);
			}
			else {
				addr.addressLo = kCSRChannelsAvailable63_32;
				mask = 1 << (63-fChannel);
			}
			fReadCmd->reinit(generation, addr, &oldVal, 1);
			result = fReadCmd->submit();
			if(kIOReturnSuccess != result) {
				return;
			}
			do {
				if(claim) {
					newVal = oldVal & ~mask;
					if(newVal == oldVal) {
						// Channel already allocated!
						result = kIOReturnNoSpace;
						break;
					}
				}
				else {
					newVal = oldVal | mask;
				}
				fLockCmd->reinit(generation, addr, &oldVal, &newVal, 1);
				result = fLockCmd->submit();
				if(kIOReturnSuccess != result) {
					IOLog("channel update result 0x%x\n", result);
					break;
				}
				tryAgain = !fLockCmd->locked(&oldVal);
			} while (tryAgain);
		}
		if(channel >= 32)
			fReAllocateChannel.hi &= ~(kChannelPrime >> (channel - 32));
		else
			fReAllocateChannel.lo &= ~(kChannelPrime >> channel);
	}
	else 
	{
		// Free the mask for the receive async stream client channels
		
	}
}

//
// Called from bus reset function to reallocate the previosuly allocated
// channels
//
void IOFireWireIP::reclaimChannels()
{
	UInt32 channel = 0;
	UInt32 chnlBit;
	UInt32 *pChannelReg;
	IOReturn err = kIOReturnSuccess ;
	
	pChannelReg = &fReAllocateChannel.lo;
	for(channel = 0; ;)
	{
		for (chnlBit = kChannelPrime; chnlBit != 0; chnlBit >>= 1, channel++) 
		{
			if ((*pChannelReg & chnlBit) != 0) 
			{
				IOLog(" %s %d\n", __FILE__, __LINE__);
				// Reallocate the channel
				err = acquireChannel(&channel, TRUE, 0);
				
				if(err != kIOReturnSuccess)
					channelNotification(channel, kDoNotNotifyOnFailure);
				else
					channelNotification(channel, kNotifyOnSuccess);
			}
		}
		if (pChannelReg == &fReAllocateChannel.hi)
			break;

		pChannelReg = &fReAllocateChannel.hi;
	}
}

/*-----------------------------------------------------------------------------
 This procedure is called, rain or shine, after a bus reset necessitates the
 reallocation of any channels for which we are the MCAP owner. Note that the
 channel parameter has a high-order bit that encodes the result, success or
 failure of the reallocation. First, see if we still own the channel. If we
 don't, we must cease our attempts to reallocate it in the future but nothing
 else need be done. Otherwise, if we are the owner and reallocation succeeded,
 announce it to the world with an MCAP advertisement as soon as we can. If
 reallocation failed, drastic steps are called for. Release the channel, mark
 the channel unowned, stop receiving asynchronous streams on the channel and
 suppress future MCAP transmission. Then sift through the multicast ARBs for
 any that use the channel we just lost; the channel number reverts to 31, the
 default broadcast channel. If an ARB had been marked for deletion, though,
 just get rid of it. */
void IOFireWireIP::channelNotification(UInt32 channel, UInt32 flags) {
	ARB *arb, *priorArb;
	MCB *mcb;
	IOFWAsyncStreamRxCommand *asyncStreamRxClient;
	
	IOLog(" %s %d\n", __FILE__, __LINE__);
	
	mcb = &fLcb->mcapState[channel];
	
	if (mcb->ownerNodeID != fLcb->ownNodeID)	// Are we no longer the owner?
		releaseChannel(mcb->channel, kDoNotDeallocate);
	else if (flags & kNotifyOnSuccess)			// Was the reallocation OK?
		mcb->nextTransmit = 1;					// Broadcast MCAP advertisement ASAP */
	else {										// Trouble in River City! Abandon ship! */
		releaseChannel(mcb->channel, kDoNotDeallocate);
		mcb->ownerNodeID = MCAP_UNOWNED;		// Relinquish our claim...
		
		asyncStreamRxClient = (IOFWAsyncStreamRxCommand *)mcb->asyncStreamID;
		if(asyncStreamRxClient != NULL)
			asyncStreamRxClient->release();
		
		//TNFRemoveAsyncStreamClient(fLcb->clientID, mcb->asyncStreamID);
		mcb->asyncStreamID = kInvalidAsyncStreamRefID;
		mcb->expiration = mcb->nextTransmit = mcb->finalWarning = 0;
		priorArb = (ARB *) &fLcb->multicastArb; /* Tidy up this channel's ARBs */
		while ((arb = priorArb->next) != NULL) {
			if (arb->handle.multicast.channel == mcb->channel)
				if (arb->deletionPending) {
					priorArb->next = arb->next;   /* Unlink the ARB */
					deallocateCBlk(fLcb, arb);     /* And release it */
					continue;      /* Important to recheck priorArb->next */
				} else            /* Revert to default channel */
					arb->handle.multicast.channel = DEFAULT_BROADCAST_CHANNEL;
			priorArb = arb;      /* Advance to next ARB in linked list */
		}
		mcb->groupCount = 0;    /* No ARBs left that reference the channel */
	}
}