i82557Private.cpp   [plain text]


/*
 * Copyright (c) 1998-2000 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@
 */
/*
 * Copyright (c) 1996 NeXT Software, Inc.  All rights reserved. 
 *
 * i82557Private.cpp
 *
 */

#include "i82557.h"

extern "C" {
#include <sys/param.h>
#include <sys/mbuf.h>
#include <string.h>
}

//---------------------------------------------------------------------------
// Function: _intrACK
//
// Purpose:
//   Acknowledge all of the pending interrupt sources.
//
// Returns:
//   Return the interrupt status.
//
// CSR usage:
//   Read/Write: SCB status

static inline scb_status_t _intrACK(CSR_t * CSR_p)
{
    UInt32 cmdstatus = ReadLE32(&CSR_p->status);
    UInt16 status;

    if (cmdstatus == C_CSR_BAD) cmdstatus = 0;

    status = cmdstatus & SCB_STATUS_INT_MASK;
    if (status)
        WriteLE16(&CSR_p->status, status);   // ack pending interrupts.

    return status;
}

//---------------------------------------------------------------------------
// Function: _waitSCBCommandClear
//
// Purpose:
//   Wait for the SCB Command field to clear. This ensures that we don't
//   overrun the NIC's command unit.
//
// Returns:
//   true  if the SCB command field was cleared.
//   false if the SCB command field was not cleared.
//
// CSR usage:
//   Read: SCB command

static inline bool
_waitSCBCommandClear(CSR_t * CSR_p)
{
    for (int i = 0; i < SPIN_TIMEOUT; i++) {
        if (ReadLE32(&CSR_p->status) == C_CSR_BAD) break;
		if (!ReadLE8(&CSR_p->command)) return true;
		IODelay(SPIN_COUNT);
    }
    return false;	// hardware is not responding.
}

//---------------------------------------------------------------------------
// Function: _waitCUNonActive
//
// Purpose:
//   Waits for the Command Unit to become inactive.
//
// Returns:
//   true  if the CU has become inactive.
//   false if the CU remains active.
//
// CSR usage:
//   Read: SCB status

static inline bool
_waitCUNonActive(CSR_t * CSR_p)
{
    UInt32 cmdstatus;

    for (int i = 0; i < SPIN_TIMEOUT; i++) {
        cmdstatus = ReadLE32(&CSR_p->status);
        if (cmdstatus == C_CSR_BAD) break;
		if (CSR_VALUE(SCB_STATUS_CUS, cmdstatus) != SCB_CUS_ACTIVE)
			return true;
		IODelay(SPIN_COUNT);
	}
	return false;
}

//---------------------------------------------------------------------------
// Function: isCSRValid

bool Intel82557::isCSRValid()
{
    // Returns false to indicate that the CSR is no longer valid by
    // checking for all 1's in the CSR status and command word.
    // Certain bits are defined to never return zero. There shouldn't
    // be any side effects from reading this register.

    return ( ReadLE32(&CSR_p->status) != C_CSR_BAD );
}

//---------------------------------------------------------------------------
// Function: _polledCommand:WithAddress
//
// Purpose:
//   Issue a polled command to the NIC.

bool Intel82557::_polledCommand(cbHeader_t * hdr_p, IOPhysicalAddress paddr)
{
    if (!_waitSCBCommandClear(CSR_p)) {
		VPRINT("%s: _polledCommand:(%s): _waitSCBCommandClear failed\n",
               CUCommandString(CSR_VALUE(CB_CMD, ReadLE16(&hdr_p->command))),
			   getName());
		return false;
    }

    if (!_waitCUNonActive(CSR_p)) {
		VPRINT("%s: _polledCommand:(%s): _waitCUNonActive failed\n",
               CUCommandString(CSR_VALUE(CB_CMD, ReadLE16(&hdr_p->command))),
			   getName());
		return false;
    }

	// Set the physical address of the command block, and issue a
	// command unit start.

	WriteLE32(&CSR_p->pointer, paddr);
	WriteLE8(&CSR_p->command, CSR_FIELD(SCB_COMMAND_CUC, SCB_CUC_START));

	prevCUCommand = SCB_CUC_START;

    for (int i = 0; (i < SPIN_TIMEOUT) && isCSRValid(); i++) {
		if (ReadLE16(&hdr_p->status) & CB_STATUS_C) {
            
            // If EL bit is set, then assume the CU engine is now in idle
            // state.

            if ( ReadLE16(&hdr_p->command) & CB_EL )
                cuIsIdle = true;
            
            return true;
        }
		IODelay(SPIN_COUNT);
    }
    return false;   // timeout occurred
}

//---------------------------------------------------------------------------
// Function: _abortReceive
//
// Purpose:
//   Abort the receive unit.

bool Intel82557::_abortReceive()
{
    if (!_waitSCBCommandClear(CSR_p)) {
		VPRINT("%s: _abortReceive: _waitSCBCommandClear failed\n", getName());
		return false;
    }

	WriteLE8(&CSR_p->command, CSR_FIELD(SCB_COMMAND_RUC, SCB_RUC_ABORT));

    for (int i = 0; (i < SPIN_TIMEOUT) && isCSRValid(); i++) {
		if (CSR_VALUE(SCB_STATUS_RUS, ReadLE16(&CSR_p->status)) == SCB_RUS_IDLE)
			return true;
		IODelay(SPIN_COUNT);
    }

	VPRINT("%s: _abortReceive: timeout\n", getName());	
    return false;
}

//---------------------------------------------------------------------------
// Function: _startReceive
//
// Purpose:
//   Start the receive unit

bool Intel82557::_startReceive()
{
    if (!_waitSCBCommandClear(CSR_p)) {
		VPRINT("%s: _startReceive: _waitSCBCommandClear failed\n", getName());
		return false;
    }

	// Make sure the initial RFD has a link to its RBD
	WriteLE32(&headRfd->rbdAddr, headRfd->_rbd._paddr);

	WriteLE32(&CSR_p->pointer, headRfd->_paddr);
	WriteLE8(&CSR_p->command, CSR_FIELD(SCB_COMMAND_RUC, SCB_RUC_START));

    for (int i = 0; (i < SPIN_TIMEOUT) && isCSRValid(); i++) {
		if (CSR_VALUE(SCB_STATUS_RUS, ReadLE16(&CSR_p->status)) == 
			SCB_RUS_READY) return true;
		IODelay(SPIN_COUNT);
    }

	VPRINT("%s: _startReceive: timeout\n", getName());		
    return false;
}

//---------------------------------------------------------------------------
// Function: _resetChip
//
// Purpose:
//   Issue a selective reset then a full reset.
//   This is done to avoid a PCI bus hang if the 82557 is in the midst of
//   a PCI bus cycle. The selective reset pauses the transmit and receive
//   engines.
//
void Intel82557::_resetChip()
{
    int i = 0;

	sendPortCommand(portSelectiveReset_e, 0);
    do {
		IOSleep(1);
    } while (isCSRValid() && ReadLE32(&CSR_p->port) && (++i < 100));

	sendPortCommand(portReset_e, 0);
    IOSleep(1);
    return;
}

//---------------------------------------------------------------------------
// Function: issueReset
//
// Purpose:
//   Shut down the chip, and issue a reset.

void Intel82557::issueReset()
{
    VPRINT("%s: resetting adapter\n", getName());

	etherStats->dot3RxExtraEntry.resets++;

	setActivationLevel(kActivationLevel0);
	if (!setActivationLevel(currentLevel)) {
		VPRINT("%s: Reset attempt unsuccessful\n", getName());
	}
}

//---------------------------------------------------------------------------
// Function: updateRFDFromMbuf
//
// Purpose:
//   Updated a RFD/RBD in order to attach it to a cluster mbuf.
//   XXX - assume cluster will never cross page boundary.

bool Intel82557::updateRFDFromMbuf(rfd_t * rfd_p, struct mbuf * m)
{
	struct IOPhysicalSegment vector;
	UInt count;

	count = rxMbufCursor->getPhysicalSegments(m, &vector, 1);
	if (!count)
		return false;
	
	// Start modifying RFD
	//
	rfd_p->_rbd.buffer = vector.location;	// cursor is little-endian
//	WriteLE32(&rfd_p->_rbd.size, CSR_FIELD(RBD_SIZE, vector.length));

	rfd_p->_rbd._mbuf = m;
	
    return true;
}

//---------------------------------------------------------------------------
// Function: _initTcbQ
//
// Purpose:
//   Initialize the transmit control block queue.  Create a circularly
//   linked list of tcbs.

bool Intel82557::_initTcbQ( bool enable )
{
    int i;

    tcbQ.numFree = tcbQ.numTcbs = NUM_TRANSMIT_FRAMES;
    tcbQ.activeHead_p = tcbQ.activeTail_p = tcbQ.freeHead_p = tcbList_p;

    for (i = 0; i < tcbQ.numTcbs; i++) { /* free up buffers */
		if (tcbList_p[i]._mbuf) {
			freePacket(tcbList_p[i]._mbuf);
			tcbList_p[i]._mbuf = 0;
		}
    }
    bzero(tcbList_p, sizeof(tcb_t) * tcbQ.numTcbs);

	if (!enable)
		return true;

    for (i = 0; i < tcbQ.numTcbs; i++) {
		IOPhysicalAddress paddr;

        if (!getPhysAddress(&txRing, &tcbList_p[i], &tcbList_p[i]._paddr))
        {
            IOLog("i82557(tcbQ): Invalid TCB address\n");
            return false;
        }

        if (!getPhysAddress(&txRing, &tcbList_p[i]._tbds, &paddr))
        {
            IOLog("i82557(tcbQ): Invalid TBD address\n");
            return false;
        }
        WriteLE32(&tcbList_p[i].tbdAddr, paddr);
    
        if (i == (tcbQ.numTcbs - 1))
            tcbList_p[i]._next = &tcbList_p[0];
        else
            tcbList_p[i]._next = &tcbList_p[i + 1];
    }
    for (i = 0; i < tcbQ.numTcbs; i++) /* make physical links */
		WriteLE32(&tcbList_p[i].link, tcbList_p[i]._next->_paddr);
	
	return true;
}

//---------------------------------------------------------------------------
// Function: _setupRfd

static void _setupRfd(rfd_t * rfdList_p)
{
    for (int i = 0; i < NUM_RECEIVE_FRAMES; i++) {
		if (i == (NUM_RECEIVE_FRAMES - 1)) {
			/* mark tails and link the lists circularly */
			SetLE16(&rfdList_p[i].command, RFD_COMMAND_EL);
			rfdList_p[i]._next = &rfdList_p[0];
			SetLE32(&rfdList_p[i]._rbd.size, RBD_SIZE_EL);
			rfdList_p[i]._rbd._next = &rfdList_p[0]._rbd;
		}
		else {
			rfdList_p[i]._next = &rfdList_p[i + 1];
			rfdList_p[i]._rbd._next = &rfdList_p[i + 1]._rbd;
		}

		WriteLE32(&rfdList_p[i].link, rfdList_p[i]._next->_paddr);
		WriteLE32(&rfdList_p[i].rbdAddr,
                  (i == 0) ? rfdList_p[0]._rbd._paddr : C_NULL);
		
		WriteLE32(&rfdList_p[i]._rbd.link, rfdList_p[i]._rbd._next->_paddr);
		SetLE32(&rfdList_p[i]._rbd.size, CSR_FIELD(RBD_SIZE, MAX_BUF_SIZE));
	}
}

//---------------------------------------------------------------------------
// Function: _initRfdList
//
// Purpose:
//   Create a circularly linked list of receive frame descriptors, and 
//   populate them with receive buffers allocated from our special pool.

bool Intel82557::_initRfdList( bool enable )
{
    int  i;

    /* free allocated packet buffers */
	for (i = 0; i < NUM_RECEIVE_FRAMES; i++) {
		if (rfdList_p[i]._rbd._mbuf) {
			freePacket(rfdList_p[i]._rbd._mbuf);
		}
	}

    /* zero out the entire structure, and re-create it */
    bzero(rfdList_p, sizeof(rfd_t) * NUM_RECEIVE_FRAMES);

	if (!enable)
		return true;

	for (i = 0; i < NUM_RECEIVE_FRAMES; i++) {
        SetLE16(&rfdList_p[i].command, RFD_COMMAND_SF);
        
        if (!getPhysAddress(&rxRing, &rfdList_p[i], &rfdList_p[i]._paddr))
        {
            IOLog("%s: Invalid RFD address\n", getName());
            return false;
        }
        if (!getPhysAddress(&rxRing, &rfdList_p[i]._rbd,
                            &rfdList_p[i]._rbd._paddr))
        {
            IOLog("%s: Invalid RBD address\n", getName());
            return false;
        }
    }

	_setupRfd(rfdList_p);

    for (i = 0; i < NUM_RECEIVE_FRAMES; i++) {
		// Pre-load the receive ring with max size mbuf packets.
		//		
		struct mbuf * m = allocatePacket(MAX_BUF_SIZE);
		if (!m)
			return false;
		
		if (updateRFDFromMbuf(&rfdList_p[i], m) == false) {
			IOLog("%s: updateRFDFromMbuf() error\n", getName());
			freePacket(m);
			return false;
		}
    }

    headRfd = rfdList_p;
    tailRfd = rfdList_p + NUM_RECEIVE_FRAMES - 1;

    return true;
}

//---------------------------------------------------------------------------
// Function: _resetRfdList
//
// Purpose:
//   Reset the RFD list before the receiver engine is restarted after
//   a resource shortage.

bool Intel82557::_resetRfdList()
{
    int                i;

	struct _cache {
		IOPhysicalAddress rbd_buffer;
		struct mbuf *     rbd_mbuf;
		IOPhysicalAddress rfd_paddr;
		IOPhysicalAddress rbd_paddr;
	} * cache_p = (struct _cache *) KDB_buf_p;

	if ((sizeof(struct _cache) * NUM_RECEIVE_FRAMES) > kIOEthernetMaxPacketSize) {
		IOLog("%s: no space for cache data\n", getName());
		return false;
	}

    /* cache allocated packet buffers */
	for (i = 0; i < NUM_RECEIVE_FRAMES; i++) {
		cache_p[i].rbd_mbuf   = rfdList_p[i]._rbd._mbuf;
		cache_p[i].rbd_buffer = rfdList_p[i]._rbd.buffer;
		cache_p[i].rfd_paddr  = rfdList_p[i]._paddr;
		cache_p[i].rbd_paddr  = rfdList_p[i]._rbd._paddr;
	}

    /* zero out the entire structure, and re-create it */
    bzero(rfdList_p, sizeof(rfd_t) * NUM_RECEIVE_FRAMES);

	for (i = 0; i < NUM_RECEIVE_FRAMES; i++) {
		SetLE16(&rfdList_p[i].command, RFD_COMMAND_SF);
		rfdList_p[i]._paddr = cache_p[i].rfd_paddr;
		rfdList_p[i]._rbd._paddr = cache_p[i].rbd_paddr;
    }

	_setupRfd(rfdList_p);

    for (i = 0; i < NUM_RECEIVE_FRAMES; i++) {
		rfdList_p[i]._rbd.buffer = cache_p[i].rbd_buffer;
		rfdList_p[i]._rbd._mbuf  = cache_p[i].rbd_mbuf;
	}

    headRfd = rfdList_p;
    tailRfd = rfdList_p + NUM_RECEIVE_FRAMES - 1;

    return true;
}

//---------------------------------------------------------------------------
// Function: _mdiReadPHY:Register:Data
//
// Purpose:
//   Read the specified MDI register and return the results.

bool
Intel82557::_mdiReadPHY(UInt8 phyAddress, UInt8 regAddress, UInt16 * data_p)
{
    mdi_control_t mdi;

	mdi = CSR_FIELD(MDI_CONTROL_PHYADDR, phyAddress) |
	      CSR_FIELD(MDI_CONTROL_REGADDR, regAddress) |
		  CSR_FIELD(MDI_CONTROL_OPCODE,  MDI_CONTROL_OP_READ);
	
	WriteLE32(&CSR_p->mdiControl, mdi);
	IODelay(1);

	bool ready = false;
	for (int i = 0; (i < SPIN_TIMEOUT) && isCSRValid(); i++) {
		if (ReadLE32(&CSR_p->mdiControl) & MDI_CONTROL_READY) {
			ready = true;
            break;
		}
	    IODelay(1);
	}
	if (ready == false) {
	    VPRINT("%s: _mdiReadPHYRegisterSuccess timeout\n", getName());
	    return false;
	}

	*data_p = CSR_VALUE(MDI_CONTROL_DATA, ReadLE32(&CSR_p->mdiControl));
    return true;
}

//---------------------------------------------------------------------------
// Function: _mdiWritePHY:Register:Data
//
// Purpose:
//   Write the specified MDI register with the given data.

bool Intel82557::_mdiWritePHY(UInt8 phyAddress, UInt8 regAddress, UInt16 data)
{
    mdi_control_t mdi;

	mdi = CSR_FIELD(MDI_CONTROL_PHYADDR, phyAddress) |
	      CSR_FIELD(MDI_CONTROL_REGADDR, regAddress) |
		  CSR_FIELD(MDI_CONTROL_OPCODE,  MDI_CONTROL_OP_WRITE) |
		  CSR_FIELD(MDI_CONTROL_DATA,    data);

    WriteLE32(&CSR_p->mdiControl, mdi);
    IODelay(1);

	bool ready = false;
	for (int i = 0; (i < SPIN_TIMEOUT) && isCSRValid(); i++) {
		if (ReadLE32(&CSR_p->mdiControl) & MDI_CONTROL_READY) {
			ready = true;
			break;
		}
	    IODelay(1);
	}
	if (ready == false) {
	    VPRINT("%s: _mdiWritePHYRegisterData timeout\n", getName());
	    return false;
	}
    return true;
}

//---------------------------------------------------------------------------
// Function: nop
//
// Purpose:
//   Issue a polled NOP command to the NIC.

bool Intel82557::nop()
{
	cbHeader_t * nop_p = &overlay_p->nop;

    bzero(nop_p, sizeof(*nop_p));
	WriteLE16(&nop_p->command, CSR_FIELD(CB_CMD, CB_CMD_NOP) | CB_EL);
	WriteLE32(&nop_p->link, C_NULL);

    return _polledCommand(nop_p, overlay_paddr);
}

//---------------------------------------------------------------------------
// Function: config
//
// Purpose:
//   Issue a polled CONFIGURE command to the NIC.

bool Intel82557::config()
{
    UInt8 *	cb_p;    
	cb_configure_t * cfg_p = &overlay_p->configure;
	
    /*
     * Fill the configure command block
     */
    bzero(cfg_p, sizeof(*cfg_p));
	
	WriteLE16(&cfg_p->header.command, CSR_FIELD(CB_CMD, CB_CMD_CONFIGURE) |
              CB_EL);
	WriteLE32(&cfg_p->header.link, C_NULL);
	
	cb_p = cfg_p->byte;
	cb_p[0] = CSR_FIELD(CB_CB0_BYTE_COUNT, CB_CONFIG_BYTE_COUNT);
	
	cb_p[1] = CSR_FIELD(CB_CB1_TX_FIFO_LIMIT, CB_CB1_TX_FIFO_0) |
              CSR_FIELD(CB_CB1_RX_FIFO_LIMIT, CB_CB1_RX_FIFO_64);

	cb_p[3] = CB_CB3_MWI_ENABLE;	// enable PCI-MWI on 82558 devices
	
	cb_p[4] = 0;					// disable PCI transfer limits
	cb_p[5] = 0;
	
	cb_p[6] = CB_CB6_NON_DIRECT_DMA | CB_CB6_STD_TCB | CB_CB6_STD_STATS;

	cb_p[7] = CSR_FIELD(CB_CB7_UNDERRUN_RETRY, CB_CB7_UNDERRUN_RETRY_1) |
              CB_CB7_DISC_SHORT_FRAMES;

    if ((eeprom->getContents()->controllerType != I82558_CONTROLLER_TYPE) &&
		(phyAddr != PHY_ADDRESS_I82503))
		cb_p[8] = CB_CB8_CSMA_EN;

	cb_p[10] = CSR_FIELD(CB_CB10_PREAMBLE, CB_CB10_PREAMBLE_7_BYTES) |
			   CB_CB10_NSAI;

	cb_p[12] = CSR_FIELD(CB_CB12_IFS, CB_CB12_IFS_96_BIT_TIMES);

	cb_p[13] = CSR_FIELD(CB_CB13_FC_TYPE_LSB, CB_CB13_FC_TYPE_LSB_DEF);
	cb_p[14] = CSR_FIELD(CB_CB14_FC_TYPE_MSB, CB_CB14_FC_TYPE_MSB_DEF);

	cb_p[15] = ((cb_p[8] & CB_CB8_CSMA_EN) ? 0 : CB_CB15_CRS_CDT) |
                (promiscuousEnabled ? CB_CB15_PROMISCUOUS : 0);
	
	cb_p[16] = CSR_FIELD(CB_CB16_FC_DELAY_LSB, CB_CB16_FC_DELAY_LSB_DEF);
	cb_p[17] = CSR_FIELD(CB_CB17_FC_DELAY_MSB, CB_CB17_FC_DELAY_MSB_DEF);
	
	cb_p[18] = CB_CB18_PADDING;

#if 0	// XXX - need to fix this
    /* 
     * Force full duplex if there is a user override, or we are using Phy 0
     * and full duplex mode is enabled.  The FDX# pin is wired to Phy 1, 
     * which means that the 82557 can't autodetect the setting correctly.
     */
    if (forceFullDuplex || (phyAddr == PHY_ADDRESS_0 && fullDuplexMode))
		cb_p[19] = CB_CB19_FORCE_FDX;
#endif

	cb_p[19] = CB_CB19_AUTO_FDX;
	if (flowControl) {
		cb_p[19] |= ( CB_CB19_TX_FC         |
                      CB_CB19_RX_FC_RESTOP  |
				      CB_CB19_RX_FC_RESTART |
				      CB_CB19_REJECT_FC );
	}

	cb_p[20] = CSR_FIELD(CB_CB20_FC_ADDR_LSB, CB_CB20_FC_ADDR_LSB_DEF);

    return _polledCommand((cbHeader_t *) cfg_p, overlay_paddr);
}

//---------------------------------------------------------------------------
// Function: iaSetup
//
// Purpose:
//   Issue a polled IndividualAddressSETUP command to the NIC.
//
bool Intel82557::iaSetup()
{
	cb_iasetup_t * iaSetup_p = &overlay_p->iasetup;

    /*
     * Fill the IA-setup command block
     */
    bzero(iaSetup_p, sizeof(*iaSetup_p));

    WriteLE16(&iaSetup_p->header.command, CSR_FIELD(CB_CMD, CB_CMD_IASETUP) | 
                                          CB_EL);
	WriteLE32(&iaSetup_p->header.link, C_NULL);
    iaSetup_p->addr = myAddress;

    return _polledCommand((cbHeader_t *) iaSetup_p, overlay_paddr);
}

//---------------------------------------------------------------------------
// Function: mcSetup
//
// Purpose:
//   Issue a polled MultiCastSETUP command to the NIC. If 'fromData' is
//   true, then we ignore the addrs/count arguments and instead use the
//   multicast address list property in our interface client object.

bool Intel82557::mcSetup(IOEthernetAddress * addrs,
                         UInt32              count,
                         bool                update)
{
    bool                       cmdResult;
    cb_mcsetup_t *             mcSetup_ptr;
    IOPhysicalAddress          mcSetup_paddr;
    IOByteCount                mcSetup_len;
    IOBufferMemoryDescriptor * mcSetup_mem;

	if ( update )
    {
        //
        // mcSetup() was not called by the setMulticastList() function.
        // We should get the multicast list stored in the interface
        // object's property table.
        //
        // mcSetup() is always executed by the default workloop thread,
        // thus we don't have to worry about the address list being
        // changed while we go through it.
        //

        addrs = 0;
        count = 0;

        if (netif) {
            OSData * mcData = OSDynamicCast(OSData, 
                              netif->getProperty(kIOMulticastFilterData));
            if (mcData) {
                addrs = (IOEthernetAddress *) mcData->getBytesNoCopy();
                count = mcData->getLength() / sizeof(IOEthernetAddress);
                assert(addrs && count);
            }
        }
    }

    mcSetup_mem = IOBufferMemoryDescriptor::withOptions(
                                                kIOMemoryUnshared,
                                                PAGE_SIZE, PAGE_SIZE);
    if (!mcSetup_mem) {
		IOLog("%s: mcSetup memory allocation error\n", getName());
		return false;
    }

    mcSetup_ptr   = (cb_mcsetup_t *) mcSetup_mem->getBytesNoCopy();
    mcSetup_paddr = mcSetup_mem->getPhysicalSegment(0, &mcSetup_len);
    count         = min(count, (PAGE_SIZE - sizeof(cb_mcsetup_t)) /
                                sizeof(IOEthernetAddress));

	reserveDebuggerLock();

	do {
		cmdResult = false;

		WriteLE16(&mcSetup_ptr->header.status, 0);
		WriteLE16(&mcSetup_ptr->header.command,
                  CSR_FIELD(CB_CMD, CB_CMD_MCSETUP) | CB_EL);
		WriteLE32(&mcSetup_ptr->header.link, C_NULL);

		/* fill in the addresses (count may be zero) */
		for (UInt i = 0; i < count; i++)
			mcSetup_ptr->addrs[i] = addrs[i];

		/* Set the number of bytes in the MC list, if the count is zero,
		 * it is equivalent to disabling the multicast filtering mechanism.
		 */
		WriteLE16(&mcSetup_ptr->count, count * sizeof(IOEthernetAddress));

		if (!_polledCommand((cbHeader_t *) mcSetup_ptr, mcSetup_paddr)) {
			VPRINT("%s: MC-setup command failed 0x%x\n", getName(),
                   ReadLE16(&mcSetup_ptr->header.status));
			break;
		}

		cmdResult = (ReadLE16(&mcSetup_ptr->header.status) & CB_STATUS_OK) ?
                    true : false;
	} while (0);
	
	releaseDebuggerLock();

    mcSetup_mem->release();

    return cmdResult;
}

//---------------------------------------------------------------------------
// Function: _selfTest
//
// Purpose:
//   Issue a PORT self test command to the NIC and verify the results.

bool Intel82557::_selfTest()
{
    port_selftest_t * test_p = (port_selftest_t *) overlay_p;
	UInt32	results;

    WriteLE32(&test_p->signature, 0);
    WriteLE32(&test_p->results, ~0);
	sendPortCommand(portSelfTest_e, overlay_paddr);
    IOSleep(20);
    if (ReadLE32(&test_p->signature) == 0) {
		IOLog("%s: Self test timed out\n", getName());
		return false;
    }

	results = ReadLE32(&test_p->results);
    if (results) {		/* report errors from self test */
    	if (results & PORT_SELFTEST_ROM)
	    	IOLog("%s: Self test reports invalid ROM contents\n",
			getName());
    	if (results & PORT_SELFTEST_REGISTER)
			IOLog("%s: Self test reports internal register failure\n", 
			getName());
		if (results & PORT_SELFTEST_DIAGNOSE)
			IOLog("%s: Self test reports serial subsystem failure\n", 
			getName());
		if (results & PORT_SELFTEST_GENERAL)
			IOLog("%s: Self test failed\n", getName());
			return false;
	}
    return true;
}

//---------------------------------------------------------------------------
// Function: sendPortCommand
//
// Purpose:
//   Issue an 82557 PORT command.
//
void Intel82557::sendPortCommand(port_command_t command, UInt arg)
{
	WriteLE32(&CSR_p->port, (arg & PORT_ADDRESS_MASK) |
                            CSR_FIELD(PORT_FUNCTION, command));
    return;
}

//---------------------------------------------------------------------------
// Function: enableAdapterInterrupts, disableAdapterInterrupts
//
// Purpose:
//   Turn on/off interrupts at the adapter.

void Intel82557::enableAdapterInterrupts()
{
	/*
	 * For 82558, mask (disable) the ER and FCP interrupts.
	 */
	UInt8	interruptByte;
	interruptByte = SCB_INTERRUPT_ER | SCB_INTERRUPT_FCP;
    WriteLE8(&CSR_p->interrupt, interruptByte);
	interruptEnabled = true;
    return;
}

void Intel82557::disableAdapterInterrupts()
{
	UInt8	interruptByte;
	interruptByte = SCB_INTERRUPT_M;
	WriteLE8(&CSR_p->interrupt, interruptByte);
	interruptEnabled = false;
    return;
}

//---------------------------------------------------------------------------
// Function: _logCounters
//
// Purpose:
//   If Verbose is defined as yes, log extra information about errors that
//   have occurred.

static inline void
_logCounters(errorCounters_t * errorCounters_p)
{
    if (errorCounters_p->tx_good_frames)
		IOLog("tx_good_frames %ld\n",
			ReadLE32(&errorCounters_p->tx_good_frames));
    if (errorCounters_p->tx_maxcol_errors)
		IOLog("tx_maxcol_errors %ld\n",
			ReadLE32(&errorCounters_p->tx_maxcol_errors));
    if (errorCounters_p->tx_late_collision_errors)
		IOLog("tx_late_collision_errors %ld\n", 
			ReadLE32(&errorCounters_p->tx_late_collision_errors));
    if (errorCounters_p->tx_underrun_errors)
		IOLog("tx_underrun_errors %ld\n",
			ReadLE32(&errorCounters_p->tx_underrun_errors));
    if (errorCounters_p->tx_lost_carrier_sense_errors)
		IOLog("tx_lost_carrier_sense_errors %ld\n", 
			ReadLE32(&errorCounters_p->tx_lost_carrier_sense_errors));
    if (errorCounters_p->tx_deferred)
		IOLog("tx_deferred %ld\n", ReadLE32(&errorCounters_p->tx_deferred));
    if (errorCounters_p->tx_single_collisions)
		IOLog("tx_single_collisions %ld\n", 
			ReadLE32(&errorCounters_p->tx_single_collisions));
    if (errorCounters_p->tx_multiple_collisions)
		IOLog("tx_multiple_collisions %ld\n", 
			ReadLE32(&errorCounters_p->tx_multiple_collisions));
    if (errorCounters_p->tx_total_collisions)
		IOLog("tx_total_collisions %ld\n", 
			ReadLE32(&errorCounters_p->tx_total_collisions));
	if (errorCounters_p->rx_good_frames)
		IOLog("rx_good_frames %ld\n", 
			ReadLE32(&errorCounters_p->rx_good_frames));
	if (errorCounters_p->rx_crc_errors)
		IOLog("rx_crc_errors %ld\n",
			ReadLE32(&errorCounters_p->rx_crc_errors));
	if (errorCounters_p->rx_alignment_errors)
		IOLog("rx_alignment_errors %ld\n", 
			ReadLE32(&errorCounters_p->rx_alignment_errors));
	if (errorCounters_p->rx_resource_errors)
		IOLog("rx_resource_errors %ld\n",
			ReadLE32(&errorCounters_p->rx_resource_errors));
	if (errorCounters_p->rx_overrun_errors)
		IOLog("rx_overrun_errors %ld\n",
			ReadLE32(&errorCounters_p->rx_overrun_errors));
	if (errorCounters_p->rx_collision_detect_errors)
		IOLog("rx_collision_detect_errors %ld\n", 
			ReadLE32(&errorCounters_p->rx_collision_detect_errors));
	if (errorCounters_p->rx_short_frame_errors)
		IOLog("rx_short_frame_errors %ld\n", 
			ReadLE32(&errorCounters_p->rx_short_frame_errors));
    return;
}

//---------------------------------------------------------------------------
// Function: _dumpStatistics
//
// Purpose:
//   _dumpStatistics issues a new statistics dump command.  Every few seconds,
//   _updateStatistics is called from timeoutOccurred to check for updated
//   statistics.  If complete, update our counters, and issue a new dump
//   command.

bool Intel82557::_dumpStatistics()
{
	reserveDebuggerLock();

    if (!_waitSCBCommandClear(CSR_p)) {
		VPRINT("%s: _dumpStatistics: _waitSCBCommandClear failed\n", getName());
		return false;
    }

	WriteLE8(&CSR_p->command, CSR_FIELD(SCB_COMMAND_CUC, SCB_CUC_DUMP_RESET_STAT));

	prevCUCommand = SCB_CUC_DUMP_RESET_STAT;

	releaseDebuggerLock();

    return true;
}

//---------------------------------------------------------------------------
// Function: _updateStatistics
//
// Purpose:
//   Gather statistics information from the adapter at regular intervals.

void Intel82557::_updateStatistics()
{
    if (ReadLE32(&errorCounters_p->_status) != DUMP_STATUS) {
		if (verbose)
			_logCounters(errorCounters_p);

		// Ethernet transmitter stats.
		//
		etherStats->dot3StatsEntry.singleCollisionFrames += 
			ReadLE32(&errorCounters_p->tx_single_collisions);
		
		etherStats->dot3StatsEntry.multipleCollisionFrames += 
			ReadLE32(&errorCounters_p->tx_multiple_collisions);
		
		etherStats->dot3StatsEntry.lateCollisions += 
			ReadLE32(&errorCounters_p->tx_late_collision_errors);
		
		etherStats->dot3StatsEntry.excessiveCollisions += 
			ReadLE32(&errorCounters_p->tx_maxcol_errors);

		etherStats->dot3StatsEntry.deferredTransmissions += 
			ReadLE32(&errorCounters_p->tx_deferred);

		etherStats->dot3StatsEntry.carrierSenseErrors += 
			ReadLE32(&errorCounters_p->tx_lost_carrier_sense_errors);

		etherStats->dot3TxExtraEntry.underruns += 
			ReadLE32(&errorCounters_p->tx_underrun_errors);

		// Ethernet receiver stats.
		//
		etherStats->dot3StatsEntry.alignmentErrors += 
			ReadLE32(&errorCounters_p->rx_alignment_errors);

		etherStats->dot3StatsEntry.fcsErrors += 
			ReadLE32(&errorCounters_p->rx_crc_errors);

		etherStats->dot3RxExtraEntry.resourceErrors += 
			ReadLE32(&errorCounters_p->rx_resource_errors);

		etherStats->dot3RxExtraEntry.overruns += 
			ReadLE32(&errorCounters_p->rx_overrun_errors);

		etherStats->dot3RxExtraEntry.collisionErrors += 
			ReadLE32(&errorCounters_p->rx_collision_detect_errors);			

		etherStats->dot3RxExtraEntry.frameTooShorts += 
			ReadLE32(&errorCounters_p->rx_short_frame_errors);			
		
		// Generic network stats. For the error counters, we assume
		// the Ethernet stats will never be cleared. Thus we derive the
		// error counters by summing the appropriate Ethernet error fields.
		//
		netStats->outputErrors =
			( etherStats->dot3StatsEntry.lateCollisions
			+ etherStats->dot3StatsEntry.excessiveCollisions
			+ etherStats->dot3StatsEntry.carrierSenseErrors
			+ etherStats->dot3TxExtraEntry.underruns
			+ etherStats->dot3TxExtraEntry.resourceErrors);

		netStats->inputErrors =
			( etherStats->dot3StatsEntry.fcsErrors
			+ etherStats->dot3StatsEntry.alignmentErrors
			+ etherStats->dot3RxExtraEntry.resourceErrors
			+ etherStats->dot3RxExtraEntry.overruns
			+ etherStats->dot3RxExtraEntry.collisionErrors
			+ etherStats->dot3RxExtraEntry.frameTooShorts);

		netStats->collisions += 
			ReadLE32(&errorCounters_p->tx_total_collisions);

		WriteLE32(&errorCounters_p->_status, DUMP_STATUS);
		_dumpStatistics();
    }
}

//---------------------------------------------------------------------------
// Function: allocatePageBlock
//
// Purpose:
//   Allocate a page of memory.

bool Intel82557::allocatePageBlock( pageBlock_t * p )
{
    UInt32 size = PAGE_SIZE;

    p->memory = IOBufferMemoryDescriptor::withOptions(kIOMemoryUnshared,
                                                      size, PAGE_SIZE);

    if (p->memory == 0) return false;

    if (p->memory->prepare() != kIOReturnSuccess)
    {
        p->memory->release();
        p->memory = 0;
        return false;
    }

	p->freeStart = p->memory->getBytesNoCopy();
	p->freeBytes = p->memory->getCapacity();
	bzero(p->freeStart, p->freeBytes);

	return true;
}

//---------------------------------------------------------------------------
// Function: freePageBlock
//
// Purpose:
//   Deallocate a page of memory.
//
void Intel82557::freePageBlock( pageBlock_t * p )
{
    if ( p->memory )
    {
        p->memory->complete();
        p->memory->release();
        p->memory = 0;
    }
}

//---------------------------------------------------------------------------
// Function: allocateMemoryFrom
//
// Purpose:
//   Allocate the next aligned chunk of memory in a page block.

void * Intel82557::allocateMemoryFrom( pageBlock_t *       p,
                                       UInt32              size,
                                       UInt32              align,
                                       IOPhysicalAddress * paddr )
{
    void *      allocPtr;
    IOByteCount segLength;

	if (align == 0)
		return 0;

	// Locate next alignment boundary.

	allocPtr = (void *)(((UInt32)p->freeStart + (align - 1)) & ~(align - 1));

	// Add alignment padding to the allocation size.

    size += (UInt32) allocPtr - (UInt32) p->freeStart;

    if (size > p->freeBytes)
		return 0;

    p->freeStart  = (void *)((UInt32) p->freeStart + size);
    p->freeBytes -= size;

    if ( paddr )
    {
        *paddr = p->memory->getPhysicalSegment(
                 (UInt32) allocPtr - (UInt32) p->memory->getBytesNoCopy(),
                 &segLength);
    }

    return allocPtr;
}

//---------------------------------------------------------------------------
// Function: getPhysAddress
//
// Purpose:
//   Get the physical address for a location described by a pageBlock.
//
bool Intel82557::getPhysAddress( pageBlock_t *       pageBlock,
                                 void *              virtAddress,
                                 IOPhysicalAddress * physAddress )
{
    IOPhysicalAddress paddr = 0;
    IOByteCount       segLength;

    if ( pageBlock && pageBlock->memory )
    {
        paddr = pageBlock->memory->getPhysicalSegment(
                    (UInt32) virtAddress -
                    (UInt32) pageBlock->memory->getBytesNoCopy(),
                    &segLength);
    }

    *physAddress = paddr;

    return (paddr != 0);
}

//---------------------------------------------------------------------------
// Function: hwInit
//
// Purpose:
//   Reset/configure the chip, detect the PHY.

bool Intel82557::hwInit( bool resetOnly )
{
	disableAdapterInterrupts();
	_resetChip();
	disableAdapterInterrupts();

    /* Command unit is idle after a chip reset */
    cuIsIdle = true;

    /* If only a chip reset is requested, then we're done. */
    if ( resetOnly )
    {
        return true;
    }

	/* disable early RX interrupt */
	WriteLE8(&CSR_p->earlyRxInterrupt, 0);

    /* load command unit base address */
    if (!_waitSCBCommandClear(CSR_p)) {
		VPRINT("%s: hwInit: CU _waitSCBCommandClear failed\n", getName());
		return false;
    }
    WriteLE32(&CSR_p->pointer, 0);	
	WriteLE8(&CSR_p->command, CSR_FIELD(SCB_COMMAND_CUC, SCB_CUC_LOAD_BASE));
	prevCUCommand = SCB_CUC_LOAD_BASE;

    /* load receive unit base address */
    if (!_waitSCBCommandClear(CSR_p)) {
		VPRINT("%s: hwInit: RU _waitSCBCommandClear failed\n", getName());
		return false;
    }
    WriteLE32(&CSR_p->pointer, 0);	
	WriteLE8(&CSR_p->command, CSR_FIELD(SCB_COMMAND_RUC, SCB_RUC_LOAD_BASE));

    if (!_waitSCBCommandClear(CSR_p)) {
		VPRINT("%s: hwInit: LOAD_DUMP_ADDR _waitSCBCommandClear failed\n",
               getName());
		return false;
    }
    WriteLE32(&errorCounters_p->_status, DUMP_STATUS);
	WriteLE32(&CSR_p->pointer, errorCounters_paddr);	
	WriteLE8(&CSR_p->command, CSR_FIELD(SCB_COMMAND_CUC, SCB_CUC_LOAD_DUMP_ADDR));
	prevCUCommand = SCB_CUC_LOAD_DUMP_ADDR;

    if (!_waitSCBCommandClear(CSR_p)) {
		VPRINT("%s: hwInit: intrACK _waitSCBCommandClear failed\n", getName());
		return false;
    }

	/* Setup flow-control threshold */
	WriteLE8(&CSR_p->flowControlThreshold, CSR_FIELD(FC_THRESHOLD, FC_THRESHOLD_512));

	_intrACK(CSR_p); /* ack any pending interrupts */

	_phyProbe();

	phyID = _phyGetID();
    VPRINT("%s: PHY model id is 0x%08lx\n", getName(), phyID);
    phyID &= PHY_MODEL_MASK;

    if (!config())
		return false;
    IOSleep(500);

    if (!iaSetup())
		return false;

	_intrACK(CSR_p); /* ack any pending interrupts */

    return true;
}

//---------------------------------------------------------------------------
// Function: coldInit
//
// Purpose:
//   One-time initialization code. This is called by start(), before we
//   attach any client objects.

bool Intel82557::coldInit()
{
	IOPhysicalAddress paddr;

	disableAdapterInterrupts();

    /* allocate and initialize shared memory pointers */
	if (!allocatePageBlock(&shared)) {
		IOLog("%s: Can't allocate shared memory page\n", getName());
		goto failed;
    }
	if (!allocatePageBlock(&txRing)) {
		IOLog("%s: Can't allocate memory page for TX ring\n", getName());
		goto failed;
    }
	if (!allocatePageBlock(&rxRing)) {
		IOLog("%s: Can't allocate memory page for RX ring\n", getName());
		goto failed;
    }

    /* allocate memory for shared data structures
	 * self test needs to be
	 * 16 byte aligned
	 */	 
    overlay_p = (overlay_t *) allocateMemoryFrom( &shared,
                                                  sizeof(overlay_t), 
                                                  PARAGRAPH_ALIGNMENT,
                                                  &overlay_paddr );
    if (!overlay_p || !overlay_paddr)
        goto failed;

    tcbList_p = (tcb_t *) allocateMemoryFrom( &txRing, sizeof(tcb_t) *
                                              NUM_TRANSMIT_FRAMES,
                                              CACHE_ALIGNMENT );
	if (!tcbList_p)
        goto failed;

    KDB_tcb_p = (tcb_t *) allocateMemoryFrom( &shared, sizeof(tcb_t),
                                              CACHE_ALIGNMENT,
                                              &paddr );
    if (!KDB_tcb_p || !paddr)
        goto failed;
    KDB_tcb_p->_paddr = paddr;

    if (!getPhysAddress(&shared, &KDB_tcb_p->_tbds, &paddr))
        goto failed;
	WriteLE32(&KDB_tcb_p->tbdAddr, paddr);

    KDB_buf_p = allocateMemoryFrom( &shared, kIOEthernetMaxPacketSize,
                                    DWORD_ALIGNMENT, &KDB_buf_paddr );
	if (!KDB_buf_p || !KDB_buf_paddr)
        goto failed;

    errorCounters_p = (errorCounters_t *) allocateMemoryFrom(
                                          &shared, 
                                          sizeof(errorCounters_t),
                                          DWORD_ALIGNMENT,
                                          &errorCounters_paddr );

	if (!errorCounters_p || !errorCounters_paddr)
        goto failed;

    rfdList_p = (rfd_t *) allocateMemoryFrom( &rxRing, sizeof(rfd_t) *
                                              NUM_RECEIVE_FRAMES,
                                              CACHE_ALIGNMENT );
	if (!rfdList_p)
        goto failed;

    if (!_selfTest())
        goto failed;

    myAddress = eeprom->getContents()->addr;

    return true;

failed:
    return false;
}

//---------------------------------------------------------------------------
// Function: receiveInterruptOccurred
//
// Purpose:
//   Hand up rceived frames.

bool Intel82557::receiveInterruptOccurred()
{
	bool packetsQueued = false;

    while (ReadLE16(&headRfd->status) & RFD_STATUS_C) {
		rbd_count_t rbd_count = ReadLE32(&headRfd->_rbd.count);

		// rxCount does NOT include the Ethernet CRC (FCS).
		//
		UInt32 rxCount = CSR_VALUE(RBD_COUNT, rbd_count);

#if 0
		// When the receive unit runs out of resources, it will
		// skip over RFD/RBD, making them as complete, but the RBD will
		// have zero bytes and the EOF bit will not be set.
		// We just skip over those and allow them to be recycled.
		//
		// In those cases, the RFD->status word will be 0x8220.

		/* should have exactly 1 rbd per rfd */
		if (!(rbd_count & RBD_COUNT_EOF)) {
	    	IOLog("%s: more than 1 rbd, frame size %d\n", getName(), rxCount);
			
			IOLog("%s: RFD status: %04x\n", getName(), 
				ReadLE16(&headRfd->status));
			
			issueReset();
			return;
		}
#endif

		if ((!(ReadLE16(&headRfd->status) & RFD_STATUS_OK)) ||
			(rxCount < (kIOEthernetMinPacketSize - kIOEthernetCRCSize)) ||
			!enabledForNetif) {
			; /* bad or unwanted packet */
		}
		else {
	    	struct mbuf * m = headRfd->_rbd._mbuf;
			struct mbuf * m_in = 0;	// packet to pass up to inputPacket()
			bool replaced;

			packetsReceived = true;

			m_in = replaceOrCopyPacket(&m, rxCount, &replaced);
			if (!m_in) {
				etherStats->dot3RxExtraEntry.resourceErrors++;
				goto RX_INTR_ABORT;
			}

			if (replaced && (updateRFDFromMbuf(headRfd, m) == false)) {
				freePacket(m);	// free the new replacement mbuf.
				m_in = 0;		// pass up nothing.
				etherStats->dot3RxExtraEntry.resourceErrors++;
				IOLog("%s: updateRFDFromMbuf() error\n", getName());
				goto RX_INTR_ABORT;
			}

            netif->inputPacket(m_in, rxCount,
                               IONetworkInterface::kInputOptionQueuePacket);

			packetsQueued = true;
			netStats->inputPackets++;
	    }

RX_INTR_ABORT:
		/* clear fields in rfd */
		WriteNoSyncLE16(&headRfd->status, 0);
		WriteNoSyncLE16(&headRfd->command, (RFD_COMMAND_SF | RFD_COMMAND_EL));

        WriteNoSyncLE32(&headRfd->rbdAddr, C_NULL);
		WriteNoSyncLE32(&headRfd->misc, 0);

		/* clear fields in rbd */
		WriteNoSyncLE32(&headRfd->_rbd.count, 0);
		WriteLE32(&headRfd->_rbd.size,
                  CSR_FIELD(RBD_SIZE, MAX_BUF_SIZE) | RBD_SIZE_EL);

		/* adjust tail markers */
		WriteNoSyncLE32(&tailRfd->_rbd.size, CSR_FIELD(RBD_SIZE, MAX_BUF_SIZE));
		WriteLE16(&tailRfd->command, RFD_COMMAND_SF);

		tailRfd = headRfd;			// new tail
		headRfd = headRfd->_next;	// new head
    } /* while */

    return packetsQueued;
}

//---------------------------------------------------------------------------
// Function: transmitInterruptOccurred
//
// Purpose:
//   Free up packets associated with any completed TCB's.

void Intel82557::transmitInterruptOccurred()
{
	tcbQ_t * tcbQ_p = &tcbQ;
	tcb_t *  head;

	head = tcbQ_p->activeHead_p;
    while (tcbQ_p->numFree < tcbQ_p->numTcbs &&
          (ReadLE16(&head->status) & TCB_STATUS_C))
	{
		WriteNoSyncLE16(&head->status, 0);
		if (head->_mbuf) {
	    	freePacket(head->_mbuf);
	    	head->_mbuf = 0;
		}
		head = tcbQ_p->activeHead_p = head->_next;
		tcbQ_p->numFree++;
    }

    return;
}

//---------------------------------------------------------------------------
// Function: interruptOccurred
//
// Purpose:
//   Field an interrupt.

void Intel82557::interruptOccurred(IOInterruptEventSource * src, int /*count*/)
{
	scb_status_t  status;
	bool          flushInputQ = false;
	bool          doService   = false;

	if (interruptEnabled == false)
    {
        // This can occur when interrupts are shared. Each interrupt event
        // connected to the same interrupt line will get called when there
        // is an interrupt. Use a filter interrupt event source to avoid
        // work loop scheduling cost.
		return;
	}

	reserveDebuggerLock();

	/*
	 * Loop until the interrupt line becomes deasserted.
	 */
	while (1) {
		if ((status = _intrACK(CSR_p)) == 0)
			break;

		/*
		 * RX interrupt.
		 */
		if (status & (SCB_STATUS_FR | SCB_STATUS_RNR)) {
		
			flushInputQ = receiveInterruptOccurred() || flushInputQ;

			etherStats->dot3RxExtraEntry.interrupts++;

			if (status & SCB_STATUS_RNR) {
				etherStats->dot3RxExtraEntry.resets++;

				_abortReceive();
				_resetRfdList();

				if (!_startReceive()) {
					VPRINT("%s: Unable to restart receiver\n", getName());
					// issueReset();	/* shouldn't need to do this. */
				}
			}
		}

		/*
		 * TX interrupt.
		 */
		if (status & (SCB_STATUS_CX | SCB_STATUS_CNA)) {
			transmitInterruptOccurred();
			etherStats->dot3TxExtraEntry.interrupts++;
			doService = true;
		}
	}

	releaseDebuggerLock();

	if (enabledForNetif) {
		// Flush all packets received and pass them to the network stack.
		//
		if (flushInputQ)
			netif->flushInputQueue();

		// Call service() without holding the debugger lock to prevent a
		// deadlock when service() calls our outputPacket() function.
		//
		if (doService)
			transmitQueue->service();
	}
}

//---------------------------------------------------------------------------
// Function: updateTCBForMbuf
//
// Update the TxCB pointed by tcb_p to point to the mbuf chain 'm'.
// Returns the mbuf encoded onto the TxCB.

struct mbuf *
Intel82557::updateTCBForMbuf(tcb_t * tcb_p, struct mbuf * m)
{
	// Set the invariant TCB fields.
	//
	WriteNoSyncLE16(&tcb_p->status, 0);

    if (++txCount == TRANSMIT_INT_DELAY) {
		WriteNoSyncLE16(&tcb_p->command, CSR_FIELD(TCB_COMMAND, CB_CMD_TRANSMIT) |
                        TCB_COMMAND_S  | TCB_COMMAND_SF | TCB_COMMAND_I);
		txCount = 0;
    }
	else
		WriteNoSyncLE16(&tcb_p->command, CSR_FIELD(TCB_COMMAND, CB_CMD_TRANSMIT) |
                        TCB_COMMAND_S | TCB_COMMAND_SF);

	WriteNoSyncLE8(&tcb_p->threshold, txThreshold8);
	WriteNoSyncLE16(&tcb_p->count, 0);	// all data are in the TBD's, none in TxCB

	// Since the format of a TBD closely matches the structure of an
	// 'struct IOPhysicalSegment', we shall have the cursor update the TBD list
	// directly.
	//
	UInt segments = txMbufCursor->getPhysicalSegmentsWithCoalesce(m,
                    (struct IOPhysicalSegment *) &tcb_p->_tbds[0],
                    TBDS_PER_TCB);

	if (!segments) {
		VPRINT("%s: getPhysicalSegments error, pkt len = %d\n", 
               getName(), m->m_pkthdr.len);
		return 0;
	}

	// Update the TBD array size count.
	//
	WriteLE8(&tcb_p->number, segments);

	return m;
}

//---------------------------------------------------------------------------
// Function: outputPacket <IONetworkController>
//
// Purpose:
//   Transmit the packet handed by our IOOutputQueue.
//   TCBs have the suspend bit set, so that the CU goes into the suspend
//   state when done.  We use the CU_RESUME optimization that allows us to
//   issue CU_RESUMES without waiting for SCB command to clear.
//
UInt32 Intel82557::outputPacket(struct mbuf * m, void * param)
{
	tcb_t * tcb_p;
	
    if (!enabledForNetif) {		// drop the packet.
		freePacket(m);
		return kIOReturnOutputDropped;
	}

	reserveDebuggerLock();

	if (tcbQ.numFree == 0) {	// retry when more space is available.
		releaseDebuggerLock();
		return kIOReturnOutputStall;
	}

    packetsTransmitted = true;	
	netStats->outputPackets++;

    tcb_p = tcbQ.freeHead_p;

	tcb_p->_mbuf = updateTCBForMbuf(tcb_p, m);
	if (tcb_p->_mbuf == 0) {
		etherStats->dot3TxExtraEntry.resourceErrors++;
		goto fail;
	}

    /* update the queue */
    tcbQ.numFree--;
    tcbQ.freeHead_p = tcbQ.freeHead_p->_next;

	/* The TCB is already setup and the suspend bit set. Now clear the
	 * suspend bit of the previous TCB.
	 */
    if (tcbQ.activeTail_p != tcb_p)
		ClearLE16(&tcbQ.activeTail_p->command, TCB_COMMAND_S);
    tcbQ.activeTail_p = tcb_p;

    /*
     * CUC_RESUME is optimized such that it is unnecessary to wait
     * for the CU to clear the SCB command word if the previous command
     * was a resume and the CU state is not idle.
     */
	if ( cuIsIdle ) 
	{
		if (!_waitSCBCommandClear(CSR_p)) {
	    	VPRINT("%s: outputPacket: _waitSCBCommandClear error\n", getName());
			etherStats->dot3TxExtraEntry.timeouts++;
	    	goto fail;
		}
		WriteLE32(&CSR_p->pointer, tcb_p->_paddr);
		WriteLE8(&CSR_p->command, CSR_FIELD(SCB_COMMAND_CUC, SCB_CUC_START));
		prevCUCommand = SCB_CUC_START;
        cuIsIdle = false;
    }
    else {
		if (prevCUCommand != SCB_CUC_RESUME) {
	    	if (!_waitSCBCommandClear(CSR_p)) {
				VPRINT("%s: outputPacket: _waitSCBCommandClear error\n", getName());
				etherStats->dot3TxExtraEntry.timeouts++;
				goto fail;
	    	}
		}
		WriteLE8(&CSR_p->command, CSR_FIELD(SCB_COMMAND_CUC, SCB_CUC_RESUME));
		prevCUCommand = SCB_CUC_RESUME;
    }
	releaseDebuggerLock();
    return kIOReturnOutputSuccess;

fail:
    freePacket(m);
    tcb_p->_mbuf = 0;
	releaseDebuggerLock();
    return kIOReturnOutputDropped;
}

//---------------------------------------------------------------------------
// Function: _receivePacket
//
// Purpose:
//   Part of kerneldebugger protocol.
//   Returns true if a packet was received successfully.
//
bool Intel82557::_receivePacket(void * pkt, UInt * len, UInt timeout)
{
	bool    processPacket = true;
	bool    ret           = false;
	UInt32  cmdstatus;

    timeout *= 1000;

    while ((ReadLE16(&headRfd->status) & RFD_STATUS_C) == 0) {
		if ((int) timeout <= 0) {
			processPacket = false;
			break;
		}
		IODelay(50);
		timeout -= 50;
    }

	if (processPacket) {
		if ((ReadLE16(&headRfd->status) & RFD_STATUS_OK) &&
		    (ReadLE32(&headRfd->_rbd.count) & RBD_COUNT_EOF))
	    {
			// Pass up good frames.
			//
			*len = CSR_VALUE(RBD_COUNT, ReadLE32(&headRfd->_rbd.count));
			*len = MIN(*len, kIOEthernetMaxPacketSize - kIOEthernetCRCSize);
			bcopy(mtod(headRfd->_rbd._mbuf, void *), pkt, *len);
			ret = true;
		}

		/* the head becomes the new tail */
		/* clear fields in rfd */
		WriteNoSyncLE16(&headRfd->status, 0);
		WriteNoSyncLE16(&headRfd->command, (RFD_COMMAND_SF | RFD_COMMAND_EL));
		WriteNoSyncLE32(&headRfd->rbdAddr, C_NULL);
		WriteNoSyncLE32(&headRfd->misc, 0);
	
		/* clear fields in rbd */
		WriteNoSyncLE32(&headRfd->_rbd.count, 0);
		WriteLE32(&headRfd->_rbd.size, CSR_FIELD(RBD_SIZE, MAX_BUF_SIZE) | 
                                       RBD_SIZE_EL);

		/* adjust tail markers */
		WriteNoSyncLE32(&tailRfd->_rbd.size, CSR_FIELD(RBD_SIZE, MAX_BUF_SIZE));
		WriteLE16(&tailRfd->command, RFD_COMMAND_SF);

		tailRfd = headRfd;			// new tail
		headRfd = headRfd->_next;	// new head
	}

    cmdstatus = ReadLE32(&CSR_p->status);

	if ( (cmdstatus != C_CSR_BAD) && (cmdstatus & SCB_STATUS_RNR) )
    {
		WriteLE16(&CSR_p->status, SCB_STATUS_RNR);	// ack RNR interrupt

#if DEBUG_KDP
		IOLog("%s::%s RUS:0x%x Index:%d\n", getName(), __FUNCTION__, 
			  CSR_VALUE(SCB_STATUS_RUS, ReadLE16(&CSR_p->status)),
              tailRfd - rfdList_p);
#endif

		_abortReceive();

#if DEBUG_KDP	// Display RFD/RBD fields
		for (int i = 0; i < NUM_RECEIVE_FRAMES; i++) {
			IOLog("   %02d: %04x %04x - %08x %08x\n", i,
				ReadLE16(&rfdList_p[i].command),
				ReadLE16(&rfdList_p[i].status),
				ReadLE32(&rfdList_p[i]._rbd.size),
				ReadLE32(&rfdList_p[i].misc));
		}
#endif

		_resetRfdList();
		_startReceive();
    }

    return ret;
}

//---------------------------------------------------------------------------
// Function: _sendPacket
//
// Purpose:
//   Part of kerneldebugger protocol.
//   Returns true if the packet was sent successfully.

bool Intel82557::_sendPacket(void * pkt, UInt len)
{
	tbd_t * tbd_p;

    // Set up the TCB and issue the command
	//
    WriteNoSyncLE16(&KDB_tcb_p->status, 0);
	WriteNoSyncLE32(&KDB_tcb_p->link, C_NULL);
	WriteNoSyncLE8( &KDB_tcb_p->threshold, txThreshold8);
	WriteNoSyncLE16(&KDB_tcb_p->command, CSR_FIELD(TCB_COMMAND, CB_CMD_TRANSMIT) |
                    TCB_COMMAND_EL | TCB_COMMAND_SF );
	WriteNoSyncLE16(&KDB_tcb_p->count, 0);	// all data are in the TBD's.
	WriteNoSyncLE8( &KDB_tcb_p->number, 1);	// 1 TBD only.

	// Copy the debugger packet to the pre-allocated buffer area.
	//
    len = MIN(len, kIOEthernetMaxPacketSize - kIOEthernetCRCSize);
    len = MAX(len, kIOEthernetMinPacketSize);
    bcopy(pkt, KDB_buf_p, len);

	// Update the TBD.
	//
	tbd_p = &KDB_tcb_p->_tbds[0];
	WriteNoSyncLE32(&tbd_p->addr, KDB_buf_paddr);
	WriteNoSyncLE32(&tbd_p->size, CSR_FIELD(TBD_SIZE, len));

	// Start up the command unit to send the packet.
	//
	return _polledCommand((cbHeader_t *) KDB_tcb_p, KDB_tcb_p->_paddr);
}