Source to iokit/Drivers/network/drvPPCUniN/UniNEnetPrivate.cpp


Enter a symbol's name here to quickly find it.

/*
 * Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * The contents of this file constitute Original Code as defined in and
 * are subject to the Apple Public Source License Version 1.1 (the
 * "License").  You may not use this file except in compliance with the
 * License.  Please obtain a copy of the License at
 * http://www.apple.com/publicsource and read it before using this file.
 * 
 * This 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 OR NON-INFRINGEMENT.  Please see the
 * License for the specific language governing rights and limitations
 * under the License.
 * 
 * @APPLE_LICENSE_HEADER_END@
 */
/*
 * Copyright (c) 1998-1999 Apple Computer
 *
 * Implementation for hardware dependent (relatively) code 
 * for the Sun GEM Ethernet controller. 
 *
 * HISTORY
 *
 * 10-Sept-97        
 *  Created.
 *
 */
#include "UniNEnetPrivate.h"

extern void             *kernel_pmap;

/*
 * Private functions
 */
bool UniNEnet::allocateMemory()
{
    UInt32              rxRingSize, txRingSize;
    UInt32              i, n;
    UInt8               *virtAddr;
    UInt32              physBase;
    UInt32              physAddr;
    TxQueueElement      *txElement;
 
    /* 
     * Calculate total space for DMA channel commands
     */
    txRingSize = (TX_RING_LENGTH * sizeof(enet_txdma_cmd_t) + 2048 - 1) & ~(2048-1);
    rxRingSize = (RX_RING_LENGTH * sizeof(enet_dma_cmd_t)   + 2048 - 1) & ~(2048-1);
     
    dmaCommandsSize = round_page( txRingSize + rxRingSize ); 
    /*
     * Allocate required memory
     */
    if ( !dmaCommands )
    {
      dmaCommands = (UInt8 *)IOMallocContiguous( dmaCommandsSize, PAGE_SIZE, 0 );

      if ( dmaCommands == 0  )
      {
          IOLog( "Ethernet(UniN): Cant allocate channel dma commands\n\r" );
          return false;
      }
    }

    /*
     * If we needed more than one page, then make sure we received contiguous memory.
     */
    n = (dmaCommandsSize - PAGE_SIZE) / PAGE_SIZE;
    physBase = pmap_extract(kernel_pmap, (vm_address_t) dmaCommands);

    virtAddr = (UInt8 *) dmaCommands;
    for( i=0; i < n; i++, virtAddr += PAGE_SIZE )
    {
        physAddr =  pmap_extract(kernel_pmap, (vm_address_t) virtAddr);      
        if (physAddr != (physBase + i * PAGE_SIZE) )
        {
            IOLog( "Ethernet(UniN): Cant allocate contiguous memory for dma commands\n\r" );
            return false;
        }
    }           

    /* 
     * Setup the receive ring pointers
     */
    rxDMACommands = (enet_dma_cmd_t *)dmaCommands;
    rxMaxCommand  = RX_RING_LENGTH;

    /*
     * Setup the transmit ring pointers
     */
    txDMACommands = (enet_txdma_cmd_t *)(dmaCommands + rxRingSize);
    txMaxCommand  = TX_RING_LENGTH;
    
    
    queue_init( &txActiveQueue );
    queue_init( &txFreeQueue );
    
    for ( i=0; i < TX_MAX_MBUFS; i++ )
    {
        txElement = (TxQueueElement *)IOMalloc( sizeof(TxQueueElement) );        
        if ( txElement == 0 )
        {
            return false;
        }
            
        bzero( txElement, sizeof(TxQueueElement) );
         
        releaseTxElement( txElement );
    }     
 
    return true;
}

/*-------------------------------------------------------------------------
 *
 * Setup the Transmit Ring
 * -----------------------
 * Each transmit ring entry consists of two words to transmit data from buffer
 * segments (possibly) spanning a page boundary. This is followed by two DMA commands 
 * which read transmit frame status and interrupt status from the UniN chip. The last
 * DMA command in each transmit ring entry generates a host interrupt.
 * The last entry in the ring is followed by a DMA branch to the first
 * entry.
 *-------------------------------------------------------------------------*/

bool UniNEnet::initTxRing()
{
    TxQueueElement      *txElement;
    UInt32		i;

    /*
     * Clear the transmit DMA command memory
     */  
    bzero( (void *)txDMACommands, sizeof(enet_txdma_cmd_t) * txMaxCommand);
    txCommandHead = 0;
    txCommandTail = 0;
    
    
    txDMACommandsPhys = pmap_extract(kernel_pmap, (vm_address_t) txDMACommands);

    if ( txDMACommandsPhys == 0 )
    {
        IOLog( "Ethernet(UniN): Bad dma command buf - %08x\n\r", (int)txDMACommands );
    }
 
    for ( i=0; i < TX_RING_LENGTH; i++ )
    {  
        txElement = txElementPtrs[i];

        if ( (txElement != 0) && (txElement->list != &txFreeQueue) )
        {
            if ( txElement->mbuf != 0 )
            {
                freePacket( txElement->mbuf );
            }
            bzero( txElement, sizeof(TxQueueElement) );
            releaseTxElement( txElement );
        }

        txElementPtrs[i] = 0;
    }

    txCommandsAvail = txMaxCommand - 1; 

    return true;
}

/*-------------------------------------------------------------------------
 *
 * Setup the Receive ring
 * ----------------------
 * Each receive ring entry consists of two DMA commands to receive data
 * into a network buffer (possibly) spanning a page boundary. The second
 * DMA command in each entry generates a host interrupt.
 * The last entry in the ring is followed by a DMA branch to the first
 * entry. 
 *
 *-------------------------------------------------------------------------*/

bool UniNEnet::initRxRing()
{
    UInt32      i;
    bool        status;
    
    /*
     * Clear the receive DMA command memory
     */
    bzero( (void *)rxDMACommands, sizeof(enet_dma_cmd_t) * rxMaxCommand);

    rxDMACommandsPhys = pmap_extract(kernel_pmap, (vm_address_t) rxDMACommands);
    if ( rxDMACommandsPhys == 0 )
    {
        IOLog( "Ethernet(UniN): Bad dma command buf - %08x\n\r",  (int)rxDMACommands );
        return false;
    }

    /*
     * Allocate a receive buffer for each entry in the Receive ring
     */
    for (i = 0; i < rxMaxCommand; i++) 
    {
        if (rxMbuf[i] == NULL)    
        {
            rxMbuf[i] = allocatePacket(NETWORK_BUFSIZE);
            if (rxMbuf[i] == NULL)    
        {
                IOLog("Ethernet(UniN): allocateMbuf returned NULL in _initRxRing\n\r");
                return false;
        }
      }
      
      /* 
       * Set the DMA commands for the ring entry to transfer data to the Mbuf.
       */
        status = updateDescriptorFromMbuf(rxMbuf[i], &rxDMACommands[i], true);
        if (status == false)
        {    
            IOLog("Ethernet(UniN): cant map mbuf to physical memory in initRxRing\n\r");
            return false;
        }
    }

    /*
     * Set the receive queue head to point to the first entry in the ring. Set the
     * receive queue tail to point to a DMA Stop command after the last ring entry
     */    
    i-=4;
    rxCommandHead = 0;
    rxCommandTail = i;

    return true;
}

/*-------------------------------------------------------------------------
 *
 *
 *
 *-------------------------------------------------------------------------*/

void UniNEnet::startChip()
{
    UInt32  gemReg;
  
//    dumpRegisters();

    gemReg  = ReadUniNRegister( ioBaseEnet, kGEMTxConfig );
    gemReg |= kGEMTxConfig_TxDMAEnable;
    WriteUniNRegister( ioBaseEnet, kGEMTxConfig, gemReg );

    IOSleep(20);

    gemReg  = ReadUniNRegister( ioBaseEnet, kGEMRxConfig );
    gemReg |= kGEMRxConfig_RxDMAEnable;
    WriteUniNRegister( ioBaseEnet, kGEMRxConfig, gemReg  );

    IOSleep(20);

    gemReg  = ReadUniNRegister( ioBaseEnet, kGEMMacTxMacConfig );
    gemReg |= kGEMMacTxMacConfig_TxMacEnable;    
    WriteUniNRegister( ioBaseEnet, kGEMMacTxMacConfig, gemReg  );

    IOSleep(20);

    gemReg  = ReadUniNRegister( ioBaseEnet, kGEMMacRxMacConfig );
    gemReg |= kGEMMacRxMacConfig_RxMacEnable;    
    WriteUniNRegister( ioBaseEnet, kGEMMacRxMacConfig, gemReg  );

//  dumpRegisters();

}

/*-------------------------------------------------------------------------
 *
 *
 *
 *-------------------------------------------------------------------------*/

void UniNEnet::stopChip()
{
    UInt32	gemReg;
  
    gemReg  = ReadUniNRegister( ioBaseEnet, kGEMTxConfig );
    gemReg &= ~kGEMTxConfig_TxDMAEnable;
    WriteUniNRegister( ioBaseEnet, kGEMTxConfig, gemReg );

    IOSleep(20);

    gemReg  = ReadUniNRegister( ioBaseEnet, kGEMRxConfig );
    gemReg &= ~kGEMRxConfig_RxDMAEnable;
    WriteUniNRegister( ioBaseEnet, kGEMRxConfig, gemReg  );

    IOSleep(20);

    gemReg  = ReadUniNRegister( ioBaseEnet, kGEMMacTxMacConfig );
    gemReg &= ~kGEMMacTxMacConfig_TxMacEnable;    
    WriteUniNRegister( ioBaseEnet, kGEMMacTxMacConfig, gemReg  );

    IOSleep(20);

    gemReg  = ReadUniNRegister( ioBaseEnet, kGEMMacRxMacConfig );
    gemReg &= ~kGEMMacRxMacConfig_RxMacEnable;    
    WriteUniNRegister( ioBaseEnet, kGEMMacRxMacConfig, gemReg  );
}



/*-------------------------------------------------------------------------
 *
 *
 *
 *-------------------------------------------------------------------------*/

bool UniNEnet::resetChip()
{
    UInt8       resetReg;
    UInt16      *pPhyType;
    UInt16      phyWord;


    WriteUniNRegister( ioBaseEnet, kGEMSoftwareReset, kGEMSoftwareReset_TxReset | kGEMSoftwareReset_RxReset );

    do
    {
        resetReg = ReadUniNRegister( ioBaseEnet, kGEMSoftwareReset );
    } 
    while( resetReg & (kGEMSoftwareReset_TxReset | kGEMSoftwareReset_RxReset) );

    /*
     * Determine if PHY chip is configured. Reset and enable it (if present).
     */
    if ( phyId == 0xff )
    {
        if ( miiFindPHY(&phyId) == true )
        {
            miiResetPHY( phyId );

            pPhyType = (UInt16 *)&phyType;
            miiReadWord( pPhyType,   MII_ID0, phyId );
            miiReadWord( pPhyType+1, MII_ID1, phyId );

            if ( (phyType & MII_BCM5400_MASK) == MII_BCM5400_ID )
            {
                miiReadWord( &phyWord, MII_BCM5400_AUXCONTROL, phyId );
                phyWord |= MII_BCM5400_AUXCONTROL_PWR10BASET;
                miiWriteWord( phyWord, MII_BCM5400_AUXCONTROL, phyId );
              
                miiReadWord( &phyWord, MII_BCM5400_1000BASETCONTROL, phyId );
                phyWord |= MII_BCM5400_1000BASETCONTROL_FULLDUPLEXCAP;
                miiWriteWord( phyWord, MII_BCM5400_1000BASETCONTROL, phyId );

                IODelay(100);   
                            
                miiResetPHY( 0x1F );

                miiReadWord( &phyWord, MII_BCM5201_MULTIPHY, 0x1F );
                phyWord |= MII_BCM5201_MULTIPHY_SERIALMODE;
                miiWriteWord( phyWord, MII_BCM5201_MULTIPHY, 0x1F );

                miiReadWord( &phyWord, MII_BCM5400_AUXCONTROL, phyId );
                phyWord &= ~MII_BCM5400_AUXCONTROL_PWR10BASET;
                miiWriteWord( phyWord, MII_BCM5400_AUXCONTROL, phyId );

            }              
        }
    }
 
    return true;
}    
    
/*-------------------------------------------------------------------------
 *
 *
 *
 *-------------------------------------------------------------------------*/

bool UniNEnet::initChip()
{
    UInt32          	i, j;
    mach_timespec_t	timeStamp;
    UInt32          	rxFifoSize;
    UInt32          	rxOff;
    UInt32          	rxOn;
    u_int16_t       	*p16;

    if ( phyId == 0xff )
    {
        WriteUniNRegister( ioBaseEnet, kGEMPCSDatapathMode, kGEMPCSDatapathMode_ExtSERDESMode );

        WriteUniNRegister( ioBaseEnet, kGEMPCSSerdesControl, kGEMPCSSerdesControl_DisableLoopback | 
                                                             kGEMPCSSerdesControl_EnableSyncDet     );

        WriteUniNRegister( ioBaseEnet, kGEMPCSAdvert, kGEMPCSAdvert_FullDuplexCap |
                                                      kGEMPCSAdvert_SymPauseCap     );    

        WriteUniNRegister( ioBaseEnet, kGEMPCSControl, kGEMPCSControl_EnableAutoNegot |
                                                       kGEMPCSControl_RestartAutoNegot    );    

        WriteUniNRegister( ioBaseEnet, kGEMPCSConfig, kGEMPCSConfig_Enable );  

        WriteUniNRegister( ioBaseEnet, kGEMMacXIFConfig, kGEMMacXIFConfig_TxMIIOutputEnable 	|
                                                         kGEMMacXIFConfig_GMIIMode		|
                                                         kGEMMacXIFConfig_FullDuplexLed );
    }
    else
    {
        WriteUniNRegister( ioBaseEnet, kGEMPCSDatapathMode, kGEMPCSDatapathMode_GMIIMode );

        WriteUniNRegister( ioBaseEnet, kGEMMacXIFConfig, kGEMMacXIFConfig_TxMIIOutputEnable 	|
                                                         kGEMMacXIFConfig_FullDuplexLed );
   }

    WriteUniNRegister( ioBaseEnet, kGEMMacSendPauseCmd, kGEMMacSendPauseCmd_Bit * kGEMMacSendPauseCmd_Default );

    WriteUniNRegister( ioBaseEnet, kGEMMacControlConfig, kGEMMacControlConfig_ReceivePauseEnable );

    WriteUniNRegister( ioBaseEnet, kGEMInterruptMask,  0xffffffff );

    WriteUniNRegister( ioBaseEnet, kGEMMacTxMask, kGEMMacTxMaskDefault );

    WriteUniNRegister( ioBaseEnet, kGEMMacRxMask, kGEMMacRxMaskDefault );  

    WriteUniNRegister( ioBaseEnet, kGEMMacControlMask, kGEMMacControlMaskDefault );

    WriteUniNRegister( ioBaseEnet, kGEMConfig,  31*kGEMConfig_TxDMALimitBit | 
						31*kGEMConfig_RxDMALimitBit | 
      						kGEMConfig_InfiniteBurst);

    WriteUniNRegister( ioBaseEnet, kGEMMacInterPktGap0, kGEMMacInterPktGap0_Default );
    WriteUniNRegister( ioBaseEnet, kGEMMacInterPktGap1, kGEMMacInterPktGap1_Default );
    WriteUniNRegister( ioBaseEnet, kGEMMacInterPktGap2, kGEMMacInterPktGap2_Default );

    WriteUniNRegister( ioBaseEnet, kGEMMacSlotTime, kGEMMacSlotTime_Default );

    WriteUniNRegister( ioBaseEnet, kGEMMacMinFrameSize, kGEMMacMinFrameSize_Default );
    WriteUniNRegister( ioBaseEnet, kGEMMacMaxFrameSize, kGEMMacMaxFrameSize_Default );

    WriteUniNRegister( ioBaseEnet, kGEMMacPASize, kGEMMacPASize_Default );

    WriteUniNRegister( ioBaseEnet, kGEMMacJamSize, kGEMMacJamSize_Default );

    WriteUniNRegister( ioBaseEnet, kGEMMacAttemptLimit, kGEMMacAttemptLimit_Default );

    WriteUniNRegister( ioBaseEnet, kGEMMacTypeControl, kGEMMacTypeControl_Default );

    /*
     *
     */
    p16 = (u_int16_t *) myAddress.ea_byte;
    for ( i=0; i < sizeof(enet_addr_t) / 2; i++ )
    {
        WriteUniNRegister( ioBaseEnet, kGEMMacAddr + i * 4, p16[2-i] );
    }

    for ( i=0; i < 3; i ++ )
    {
        WriteUniNRegister( ioBaseEnet, kGEMMacAddr       + (i+3) * 4, 0 );
        WriteUniNRegister( ioBaseEnet, kGEMMacAddrFilter + (i+0) * 4, 0 );
    }

    WriteUniNRegister( ioBaseEnet, kGEMMacAddr + (6*4), kGEMMacAddr6_Default );
    WriteUniNRegister( ioBaseEnet, kGEMMacAddr + (7*4), kGEMMacAddr7_Default );
    WriteUniNRegister( ioBaseEnet, kGEMMacAddr + (8*4), kGEMMacAddr8_Default );

    WriteUniNRegister( ioBaseEnet, kGEMMacAddrFilterMask1, 0 );
    WriteUniNRegister( ioBaseEnet, kGEMMacAddrFilterMask0, 0 );

    for ( i=0; i < 16; i++ )
    {
        WriteUniNRegister( ioBaseEnet, kGEMMacHashTable + i * 4, 0 );
    }

    /*
     *
     */
    for ( i = kGEMMacCollisions; i <= kGEMMacRxCodeErrors; i+=4 );
    {
        WriteUniNRegister( ioBaseEnet, i, 0 );
    }

    IOGetTime(&timeStamp); 
    WriteUniNRegister( ioBaseEnet, kGEMMacRandomSeed, (u_int16_t) timeStamp.tv_nsec );      

    /*
     *
     */
    WriteUniNRegister( ioBaseEnet, kGEMTxDescBaseLo, txDMACommandsPhys );
    WriteUniNRegister( ioBaseEnet, kGEMTxDescBaseHi, 0 );

    for ( i=0, j=TX_RING_LENGTH / kGEMTxConfig_TxRingSizeMin - 1; 
         (i < 13) && j; 
         i++, j >>= 1 )
            ;

    WriteUniNRegister( ioBaseEnet, kGEMTxConfig, (i                                   * kGEMTxConfig_TxDescRingSizeBit) | 
                                                 (kGEMTxConfig_TxFIFOThresholdDefault * kGEMTxConfig_TxFIFOThresholdBit) );

    WriteUniNRegister( ioBaseEnet, kGEMMacTxMacConfig, 0 );

    setDuplexMode( (phyId == 0xff) ? true : false );
   
    /*
     *
     */
    WriteUniNRegister( ioBaseEnet, kGEMRxDescBaseLo, rxDMACommandsPhys );
    WriteUniNRegister( ioBaseEnet, kGEMRxDescBaseHi, 0 );

    WriteUniNRegister( ioBaseEnet, kGEMRxKick, RX_RING_LENGTH-4 );

    for ( i=0, j=RX_RING_LENGTH / kGEMRxConfig_RxRingSizeMin - 1;
          (i < 13) && j; 
          i++, j >>= 1 )
             ;

    WriteUniNRegister( ioBaseEnet, kGEMRxConfig, (i                                  * kGEMRxConfig_RxDescRingSizeBit) | 
                                                 (kGEMRxConfig_RxDMAThresholdDefault * kGEMRxConfig_RxDMAThresholdBit) 
                                                        );

    WriteUniNRegister( ioBaseEnet, kGEMMacRxMacConfig,  0 );

    rxFifoSize  = ReadUniNRegister( ioBaseEnet, kGEMRxFIFOSize );

    rxOff  = rxFifoSize - ((kGEMMacMaxFrameSize_Aligned + 8) * 2 / kGEMRxPauseThresholds_Units);
    rxOn   = rxFifoSize - ((kGEMMacMaxFrameSize_Aligned + 8) * 3 / kGEMRxPauseThresholds_Units);

    WriteUniNRegister( ioBaseEnet, kGEMRxPauseThresholds, (rxOff * kGEMRxPauseThresholds_OffBit) | 
                                                          (rxOn  * kGEMRxPauseThresholds_OnBit) );

    i = (ReadUniNRegister(ioBaseEnet, kGEMBIFConfig) & kGEMBIFConfig_66Mhz) ? PCI_PERIOD_66MHz : PCI_PERIOD_33MHz;
    i = (RX_INT_LATENCY_uS * 1000) / (2048 * i);
    
    WriteUniNRegister( ioBaseEnet, kGEMRxIntBlanking, i*kGEMRxIntBlanking_TimeBit | 5*kGEMRxIntBlanking_NumPktBit );

    return true;
}

/*-------------------------------------------------------------------------
 *
 *
 *
 *-------------------------------------------------------------------------*/

void UniNEnet::disableAdapterInterrupts()
{

    WriteUniNRegister( ioBaseEnet, kGEMInterruptMask, 0xffffffff );
}

/*-------------------------------------------------------------------------
 *
 *
 *
 *-------------------------------------------------------------------------*/

void UniNEnet::enableAdapterInterrupts()
{
    UInt32             gemReg;

    gemReg = ReadUniNRegister( ioBaseEnet, kGEMInterruptMask );
    gemReg &= ~(kGEMInterruptMask_TxInt | kGEMInterruptMask_RxDone |  kGEMInterruptStatus_RxMacInt);
    WriteUniNRegister( ioBaseEnet, kGEMInterruptMask, gemReg );
}

/*-------------------------------------------------------------------------
 *
 *
 *
 *-------------------------------------------------------------------------*/

void UniNEnet::setDuplexMode( bool duplexMode )
{
    UInt16      txMacConfig;
    UInt16      xifConfig;

    isFullDuplex = duplexMode;

    txMacConfig = ReadUniNRegister( ioBaseEnet, kGEMMacTxMacConfig );

    WriteUniNRegister( ioBaseEnet, kGEMMacTxMacConfig, txMacConfig & ~kGEMMacTxMacConfig_TxMacEnable );
    while( ReadUniNRegister(ioBaseEnet, kGEMMacTxMacConfig) & kGEMMacTxMacConfig_TxMacEnable )
      ;

    xifConfig = ReadUniNRegister( ioBaseEnet, kGEMMacXIFConfig );

    if ( isFullDuplex )
    {
        txMacConfig |= (kGEMMacTxMacConfig_IgnoreCollsn | kGEMMacTxMacConfig_IgnoreCarrierSense);
        xifConfig   &= ~kGEMMacXIFConfig_DisableEcho;
    }
    else
    {
        txMacConfig &= ~(kGEMMacTxMacConfig_IgnoreCollsn | kGEMMacTxMacConfig_IgnoreCarrierSense);
        xifConfig   |= kGEMMacXIFConfig_DisableEcho;
    }

    WriteUniNRegister( ioBaseEnet, kGEMMacTxMacConfig, txMacConfig );
    WriteUniNRegister( ioBaseEnet, kGEMMacXIFConfig,   xifConfig );
}    


/*-------------------------------------------------------------------------
 *
 *
 *
 *-------------------------------------------------------------------------*/

void UniNEnet::restartTransmitter()
{
}

/*-------------------------------------------------------------------------
 *
 *
 *
 *-------------------------------------------------------------------------*/

void UniNEnet::restartReceiver()
{
}

/*-------------------------------------------------------------------------
 *
 * Orderly stop of receive DMA.
 *
 *
 *-------------------------------------------------------------------------*/

void UniNEnet::stopReceiveDMA()
{
}    

/*-------------------------------------------------------------------------
 *
 *
 *
 *-------------------------------------------------------------------------*/

void UniNEnet::stopTransmitDMA()
{
}

/*-------------------------------------------------------------------------
 *
 *
 *
 *-------------------------------------------------------------------------*/

bool UniNEnet::transmitPacket(struct mbuf *packet)
{
    UInt32              i,j,k;
    struct mbuf         *m;
    TxQueueElement      *txElement;
    UInt32              dataPhys;
    
    
    for ( m = packet, i=1; m->m_next; m=m->m_next, i++ )
      ;
      
    
    if ( i > txCommandsAvail )  
    {
        transmitInterruptOccurred();
        
        if ( i > txCommandsAvail )
        {
            return false;
        } 
    }    

    
    if ( (txElement=getTxElement()) == 0 )
    {
        transmitInterruptOccurred();
        
        if ( (txElement=getTxElement()) == 0 )
        {
            return false;
        } 
    }    
    
    j = txCommandTail;
        
    txElement->mbuf      = packet;
    txElement->slot      = j;
    txElement->count     = i;
    
    txCommandsAvail     -= i;    

    m = packet;

    do
    {        
        k = j; 
    
        txElementPtrs[j] = txElement;
        
        dataPhys = (UInt32)mcl_to_paddr( mtod(m, char *) );
        if ( dataPhys == 0 )
        {
            dataPhys = pmap_extract( kernel_pmap, mtod(m, vm_offset_t));
        }    
        
        txDMACommands[j].desc_seg[0].bufferAddrLo  = EndianSwap32(dataPhys);
        txDMACommands[j].desc_seg[0].flags0        = EndianSwap32(kGEMTxDescFlags0_BufferSizeBit * m->m_len);
               
        j++;   
        if ( j >= txMaxCommand ) j = 0;               
    }
    while ( (m=m->m_next) != 0 );

    txDMACommands[k].desc_seg[0].flags0             |= EndianSwap32(kGEMTxDescFlags0_EndOfFrame);
    txDMACommands[txCommandTail].desc_seg[0].flags0 |= EndianSwap32(kGEMTxDescFlags0_StartOfFrame);
     
    txCommandTail = j;
          
    WriteUniNRegister( ioBaseEnet, kGEMTxKick, j );
     
    return true;          
}          


/*-------------------------------------------------------------------------
 * _receivePacket
 * --------------
 * This routine runs the receiver in polled-mode (yuk!) for the kernel debugger.
 * Don't mess with the interrupt source here that can deadlock in the debugger
 *
 * The _receivePackets allocate MBufs and pass them up the stack. The kernel
 * debugger interface passes a buffer into us. To reconsile the two interfaces,
 * we allow the receive routine to continue to allocate its own buffers and
 * transfer any received data to the passed-in buffer. This is handled by 
 * _receivePacket calling _packetToDebugger.
 *-------------------------------------------------------------------------*/

void UniNEnet::receivePacket( void *pkt, unsigned int *pkt_len, unsigned int timeout )
{
    mach_timespec_t	startTime;
    mach_timespec_t	currentTime;
    UInt32          	elapsedTimeMS;

    *pkt_len = 0;

    if (ready == false)
      return;

    debuggerPkt     = pkt;
    debuggerPktSize = 0;

    IOGetTime(&startTime);
    do
    {
      receivePackets( true );
      IOGetTime( &currentTime );
      elapsedTimeMS = (currentTime.tv_nsec - startTime.tv_nsec) / (1000*1000);
    } 
    while ( (debuggerPktSize == 0) && (elapsedTimeMS < timeout) );

    *pkt_len = debuggerPktSize;

    return;
}

/*-------------------------------------------------------------------------
 * _packetToDebugger
 * -----------------
 * This is called by _receivePackets when we are polling for kernel debugger
 * packets. It copies the MBuf contents to the buffer passed by the debugger.
 * It also sets the var debuggerPktSize which will break the polling loop.
 *-------------------------------------------------------------------------*/

void UniNEnet::packetToDebugger( struct mbuf * packet, u_int size )
{
    debuggerPktSize = size;
    bcopy( mtod(packet, char *), debuggerPkt, size );
}

/*-------------------------------------------------------------------------
 * _sendPacket
 * -----------
 *
 * This routine runs the transmitter in polled-mode (yuk!) for the kernel debugger.
 * Don't mess with the interrupt source here that can deadlock in the debugger
 *
 *-------------------------------------------------------------------------*/

void UniNEnet::sendPacket( void *pkt, unsigned int pkt_len )
{
    mach_timespec_t	startTime;
    mach_timespec_t	currentTime;
    UInt32		elapsedTimeMS;

    if (!ready || !pkt || (pkt_len > ETHERMAXPACKET))
    {
        return;
    }

    /*
     * Wait for the transmit ring to empty
     */
    IOGetTime(&startTime); 
    do
    {   
      debugTransmitInterruptOccurred();
      IOGetTime(&currentTime);
      elapsedTimeMS = (currentTime.tv_nsec - startTime.tv_nsec) / (1000*1000);
    }
    while ( (txCommandHead != txCommandTail) && (elapsedTimeMS < TX_KDB_TIMEOUT) ); 
    
    if ( txCommandHead != txCommandTail )
    {
      IOLog( "Ethernet(UniN): Polled tranmit timeout - 1\n\r");
      return;
    }

    /*
     * Allocate a MBuf and copy the debugger transmit data into it.
     *
     * jliu - no allocation, just recycle the same buffer dedicated to
     * KDB transmit.
     */
    txDebuggerPkt->m_next = 0;
    txDebuggerPkt->m_data = (caddr_t) pkt;
    txDebuggerPkt->m_pkthdr.len = txDebuggerPkt->m_len = pkt_len;

    /*
     * Send the debugger packet. txDebuggerPkt must not be freed by
     * the transmit routine.
     */
    transmitPacket(txDebuggerPkt);

    /*
     * Poll waiting for the transmit ring to empty again
     */
    do 
    {
        debugTransmitInterruptOccurred();
        IOGetTime(&currentTime);
        elapsedTimeMS = (currentTime.tv_nsec - startTime.tv_nsec) / (1000*1000);
    }
    while ( (txCommandHead != txCommandTail) &&
            (elapsedTimeMS < TX_KDB_TIMEOUT) ); 

    if ( txCommandHead != txCommandTail )
    {
        IOLog( "Ethernet(UniN): Polled tranmit timeout - 2\n\r");
    }

    return;
}

/*-------------------------------------------------------------------------
 * _sendDummyPacket
 * ----------------
 * The UniN receiver seems to be locked until we send our first packet.
 *
 *-------------------------------------------------------------------------*/
void UniNEnet::sendDummyPacket()
{
    union
    {
        UInt8           bytes[64];
        enet_addr_t     enet_addr[2];
    } dummyPacket;

    bzero( &dummyPacket, sizeof(dummyPacket) );


    dummyPacket.enet_addr[0] = myAddress;   
    dummyPacket.enet_addr[1] = myAddress;

    sendPacket((void *)dummyPacket.bytes, (unsigned int)sizeof(dummyPacket));
}



/*-------------------------------------------------------------------------
 *
 *
 *
 *-------------------------------------------------------------------------*/

bool UniNEnet::receiveInterruptOccurred()
{
    return receivePackets(false);
}

/*-------------------------------------------------------------------------
 *
 *
 *
 *-------------------------------------------------------------------------*/

bool UniNEnet::receivePackets( bool fDebugger )
{
    struct mbuf     *packet;
    UInt32          i,last;
    int             receivedFrameSize = 0;
    UInt16          dmaFlags;
    UInt32          rxPktStatus = 0;
    UInt32          badFrameCount;
    bool            passPacketUp;
    bool            reusePkt;
    bool            status;
    bool	    useNetif = !fDebugger && netifClient;
    bool            packetsQueued = false;

   
    last      = (UInt32)-1;  
    i         = rxCommandHead;

    while ( 1 )
    {
        passPacketUp = false;
        reusePkt     = false;

        dmaFlags = EndianSwap16(rxDMACommands[i].desc_seg[0].frameDataSize);

        /* 
         * If the current entry has not been written, then stop at this entry
         */
        if ( dmaFlags & kGEMRxDescFrameSize_Own )
        {
            break;
        }


        receivedFrameSize = dmaFlags & kGEMRxDescFrameSize_Mask;
        rxPktStatus       = EndianSwap32(rxDMACommands[i].desc_seg[0].flags);


        /*
         * Reject packets that are runts or that have other mutations.
         */
        if ( receivedFrameSize < (ETHERMINPACKET - ETHERCRC) || 
                     receivedFrameSize > (ETHERMAXPACKET + ETHERCRC) ||
                         rxPktStatus & kGEMRxDescFlags_BadCRC )
        {
            if (useNetif) netStats->inputErrors++;
            reusePkt = true;
        }
        else if ( useNetif == false )
        {
            /*
             * Always reuse packets in debugger mode. We also refuse to
             * pass anything up the stack unless the driver is open. The
             * hardware is enabled before the stack has opened us, to
             * allow earlier debug interface registration. But we must
             * not pass any packets up.
             */
            reusePkt = true;
            if (fDebugger)
            {
                packetToDebugger(rxMbuf[i], receivedFrameSize);
            }
        }
        
 
        /*
         * Before we pass this packet up the networking stack. Make sure we
         * can get a replacement. Otherwise, hold on to the current packet and
         * increment the input error count.
         * Thanks Justin!
         */

        packet = 0;

        if ( reusePkt == false )
        {
            bool replaced;
        
            packet = replaceOrCopyPacket(&rxMbuf[i], receivedFrameSize, &replaced);

            reusePkt = true;

            if (packet && replaced)
            {
                status = updateDescriptorFromMbuf(rxMbuf[i], &rxDMACommands[i], true);

                if (status)
                {
                    reusePkt = false;
                }
                else
                {
                    // Assume descriptor has not been corrupted.
                    freePacket(rxMbuf[i]);  // release new packet.
                    rxMbuf[i] = packet;     // get the old packet back.
                    packet = 0;             // pass up nothing.
                    IOLog("Ethernet(UniN): updateDescriptorFromMbuf error\n");
                }
            }
            
            if (packet == 0)
                netStats->inputErrors++;
        }

        /*
         * Install the new MBuf for the one we're about to pass to the network stack
         */

        if ( reusePkt == true )
        {
            rxDMACommands[i].desc_seg[0].frameDataSize = EndianSwap16(NETWORK_BUFSIZE | kGEMRxDescFrameSize_Own);
            rxDMACommands[i].desc_seg[0].flags         = 0;
        }

        /*
         * Keep track of the last receive descriptor processed
         */            
        last = i;

        /*
         * Implement ring wrap-around
         */
        if (++i >= rxMaxCommand) i = 0;

        if (fDebugger)
        {
            break;
        }

        /*
         * Transfer received packet to network
         */
        if (packet)
        {
            KERNEL_DEBUG(DBG_UniN_RXCOMPLETE | DBG_FUNC_NONE, (int) packet, 
                (int)receivedFrameSize, 0, 0, 0 );

            networkInterface->inputPacket(packet, receivedFrameSize, true);
            netStats->inputPackets++;
			packetsQueued = true;
        }
    }

    /*
     *
     *
     */
#if 0
    IOLog( "Ethernet(UniN): Prev - Rx Head = %2d Rx Tail = %2d Rx Last = %2d\n\r", rxCommandHead, rxCommandTail, last );
#endif

    if ( last != (UInt32)-1 )
    {
        rxCommandTail = last;
        rxCommandHead = i;
    }

    /*
     * Tap the DMA to wake it up
     */
    WriteUniNRegister(ioBaseEnet, kGEMRxKick, rxCommandTail & ~(4-1));


    /*
     * Update receive error statistics
     */

    badFrameCount =    ReadUniNRegister(ioBaseEnet, kGEMMacRxLengthErrors)
                     + ReadUniNRegister(ioBaseEnet, kGEMMacRxFCSErrors);

    /*
     * Clear Hardware counters
     */
    WriteUniNRegister(ioBaseEnet, kGEMMacRxLengthErrors,   0);
    WriteUniNRegister(ioBaseEnet, kGEMMacRxFCSErrors,      0);
    
    if (useNetif) netStats->inputErrors += badFrameCount;


    return packetsQueued;
}
 
/*-------------------------------------------------------------------------
 *
 *
 *
 *-------------------------------------------------------------------------*/

bool UniNEnet::transmitInterruptOccurred()
{
    UInt32          	i;
    bool        	fServiced = false;
    UInt32		txHeadOrig, txCompOrig;
    TxQueueElement      *txElement;

    i = ReadUniNRegister( ioBaseEnet, kGEMTxCompletion );

    txHeadOrig = txCommandHead;
    txCompOrig = i;
    
    while ( i != txCommandHead )
    {
        /*
         * Free the MBuf we just transmitted
         */
        txElement = txElementPtrs[txCommandHead];         
         
        KERNEL_DEBUG(DBG_UniN_TXCOMPLETE | DBG_FUNC_NONE, (int) txElement->mbuf, 0, 0, 0, 0 );

        txElementPtrs[txCommandHead] = 0;
        txCommandsAvail++;

        if ( --txElement->count == 0 )
        {
            freePacket( txElement->mbuf );
            releaseTxElement( txElement );   
            if (netifClient) netStats->outputPackets++;     
        }    
        
        if ( ++txCommandHead >= txMaxCommand ) txCommandHead = 0;

        i = ReadUniNRegister( ioBaseEnet, kGEMTxCompletion );

        fServiced = true;
    }

    return fServiced;
}

/*-------------------------------------------------------------------------
 *
 *
 *
 *-------------------------------------------------------------------------*/

bool UniNEnet::debugTransmitInterruptOccurred()
{
    bool        	fServiced = false;
    UInt32		i;
    TxQueueElement      *txElement;

    // Set the debugTxPoll flag to indicate the debugger was active
    // and some cleanup may be needed when the driver returns to
    // normal operation.
    //
    debugTxPoll = true;

    i = ReadUniNRegister( ioBaseEnet, kGEMTxCompletion );

    while ( i != txCommandHead )
    {
        fServiced = true;

        /*
         * Free the mbuf we just transmitted.
         *
         * If it is the debugger packet, just remove it from the ring.
         * and reuse the same packet for the next sendPacket() request.
         */
         
        /*
         * While in debugger mode, do not touch the mbuf pool.
         * Queue any used mbufs to a local queue. This queue
         * will get flushed after we exit from debugger mode.
         *
         * During continuous debugger transmission and
         * interrupt polling, we expect only the txDebuggerPkt
         * to show up on the transmit mbuf ring.
         */
        txElement = txElementPtrs[txCommandHead];
        txElementPtrs[txCommandHead] = 0;
        txCommandsAvail++;
        
        KERNEL_DEBUG(DBG_UniN_TXCOMPLETE | DBG_FUNC_NONE,
            (int)txElement->mbuf,
            (int)txElement->mbuf->m_pkthdr.len, 0, 0, 0 );

        if ( --txElement->count == 0 )
        {
            if (txElement->mbuf != txDebuggerPkt) 
            {
                debugQueue->enqueue( txElement->mbuf );
            }    
            releaseTxElement( txElement );            
        }                 

        if ( ++(txCommandHead) >= txMaxCommand )
            txCommandHead = 0;
    }

    return fServiced;
}

/*-------------------------------------------------------------------------
 *
 *
 *
 *-------------------------------------------------------------------------*/

void UniNEnet::debugTransmitCleanup()
{
    // Debugger was active, clear all packets in the debugQueue, and
    // issue a start(), just in case the debugger became active while the
    // ring was full and the output queue stopped. Since the debugger
    // does not restart the output queue, to avoid calling
    // semaphore_signal() which may reenable interrupts, we need to
    // make sure the output queue is not stalled after the debugger has
    // flushed the ring.
    
    debugQueue->flush();

    transmitQueue->start();
}


/*-------------------------------------------------------------------------
 *
 *
 *
 *-------------------------------------------------------------------------*/

static UInt32 txCnt = 1;

bool UniNEnet::updateDescriptorFromMbuf(struct mbuf * m,  enet_dma_cmd_t *desc, bool isReceive)
{
    struct IOPhysicalSegment    	segVector[1];
    UInt32			segments;

    segments = mbufCursor->getPhysicalSegmentsWithCoalesce(m, segVector);
    
    if ( segments == 0 || segments > 1 )
    {
        IOLog("Ethernet(UniN): updateDescriptorFromMbuf error, %d segments\n", (int)segments);
        return false;
    }    
    
    if ( isReceive )
    {
        enet_dma_cmd_t      *rxCmd = (enet_dma_cmd_t *)desc;

        rxCmd->desc_seg[0].bufferAddrLo   = EndianSwap32(segVector[0].location);
        rxCmd->desc_seg[0].frameDataSize  = EndianSwap16(segVector[0].length | kGEMRxDescFrameSize_Own); 
        rxCmd->desc_seg[0].flags          = 0; 
    }
    else
    {
        enet_txdma_cmd_t    *txCmd = (enet_txdma_cmd_t *)desc;

        txCmd->desc_seg[0].bufferAddrLo  = EndianSwap32(segVector[0].location);
        txCmd->desc_seg[0].flags0        = EndianSwap32( kGEMTxDescFlags0_BufferSizeBit * segVector[0].length   |
                                                         kGEMTxDescFlags0_StartOfFrame      |
                                     kGEMTxDescFlags0_EndOfFrame );
            
        txCmd->desc_seg[0].flags1        = ((txCnt++ % 64) == 0) ? EndianSwap32(kGEMTxDescFlags1_Int) : 0;
    }                                          

    return true;
}

/*-------------------------------------------------------------------------
 *
 *
 *
 *-------------------------------------------------------------------------*/

TxQueueElement *UniNEnet::getTxElement()
{
    TxQueueElement              *txElement = 0;

    if ( queue_empty( &txFreeQueue ) == false )
    {
        queue_remove_first( &txFreeQueue, txElement, TxQueueElement *, next );
        
        txElement->list = &txActiveQueue;
        
        queue_enter( txElement->list, txElement, TxQueueElement *, next );
    }
    
    return txElement;
}

/*-------------------------------------------------------------------------
 *
 *
 *
 *-------------------------------------------------------------------------*/

void UniNEnet::releaseTxElement(TxQueueElement *txElement)
{
    if ( txElement->list != 0 )
    {
        queue_remove( txElement->list, txElement, TxQueueElement *, next );
    }
    
    txElement->list = &txFreeQueue;   

    queue_enter(  txElement->list, txElement, TxQueueElement *, next);
}

/*-------------------------------------------------------------------------
 *
 *
 *
 *-------------------------------------------------------------------------*/

void UniNEnet::monitorLinkStatus()
{
    UInt16          phyStatus;
    UInt16          linkStatus;
    UInt16          linkMode;
    UInt16          lpAbility;
    UInt16          gemReg;
    UInt16          phyStatusChange;
    bool            fullDuplex = false;
    UInt8           *linkSpeedTxt;

    if ( phyId == 0xff )
    {
        phyStatus = ReadUniNRegister( ioBaseEnet, kGEMPCSStatus );
        lpAbility =  ReadUniNRegister( ioBaseEnet, kGEMPCSLPAbility );
    }
    else 
    {
        if ( miiReadWord( &phyStatus, MII_STATUS, phyId) != true )
        {
            return;
        }
       miiReadWord( &lpAbility, MII_STATUS, phyId);
    }

    phyStatusChange = (phyStatusPrev ^ phyStatus) & (MII_STATUS_LINK_STATUS | MII_STATUS_NEGOTIATION_COMPLETE);
    if ( phyStatusChange )
    {
        gemReg = ReadUniNRegister( ioBaseEnet, kGEMMacControlConfig );
        if ( lpAbility & MII_LPAR_PAUSE )
        {
            gemReg |= kGEMMacControlConfig_SendPauseEnable;
        }
        else
        {
            gemReg &= ~kGEMMacControlConfig_SendPauseEnable;
        }
        WriteUniNRegister( ioBaseEnet, kGEMMacControlConfig, gemReg );
    
        if ( (phyStatus & MII_STATUS_LINK_STATUS) && (phyStatus & MII_STATUS_NEGOTIATION_COMPLETE ) )
        {
            if ( phyId == 0xff )
            {
                IOLog( "Ethernet(UniN): Link is up at 1Gb - Full Duplex\n\r" );
                fullDuplex = true;
            }
            else if ( (phyType & MII_BCM5201_MASK) == MII_BCM5201_ID )
            {
                miiReadWord( &linkStatus, MII_BCM5201_AUXSTATUS, phyId );

                fullDuplex =  (linkStatus & MII_BCM5201_AUXSTATUS_DUPLEX) ? true : false;

                IOLog( "Ethernet(UniN): Link is up at %sMb - %s Duplex\n\r",
                       (linkStatus & MII_BCM5201_AUXSTATUS_SPEED)  ? "100" : "10",
                       (fullDuplex)                                ? "Full" : "Half" );                        

            }  
            else if ( (phyType & MII_BCM5400_MASK) == MII_BCM5400_ID )
            {
                miiReadWord( &linkStatus, MII_BCM5400_AUXSTATUS, phyId );

                linkMode = (linkStatus & MII_BCM5400_AUXSTATUS_LINKMODE_MASK) / MII_BCM5400_AUXSTATUS_LINKMODE_BIT;

                gemReg = ReadUniNRegister( ioBaseEnet, kGEMMacXIFConfig );
                if ( linkMode < 6 )
                {
                    gemReg &= ~kGEMMacXIFConfig_GMIIMode;
                }
                else
                {
                    gemReg |= kGEMMacXIFConfig_GMIIMode;
                }
                WriteUniNRegister( ioBaseEnet, kGEMMacXIFConfig, gemReg );

                if ( linkMode == 0 )
                {
                    linkSpeedTxt = NULL;
                }
                else if ( linkMode < 3 )
                {
                    linkSpeedTxt = (UInt8 *)"10Mb";
                    fullDuplex =  ( linkMode < 2 ) ? false : true;                    

                }
                else if ( linkMode < 6 )
                {
                    linkSpeedTxt = (UInt8 *)"100Mb";
                    fullDuplex =  ( linkMode < 5 ) ? false : true;                                        
                }
                else
                {
                    linkSpeedTxt = (UInt8 *)"1Gb";
                    fullDuplex = true;
                }                    

                if ( linkSpeedTxt )
                {
                    IOLog( "Ethernet(UniN): Link is up at %s - %s Duplex\n\r",
                            linkSpeedTxt,
                           (fullDuplex) ? "Full" : "Half" );                        
                }
                else
                {
                    IOLog( "Ethernet(UniN): Link is up\n\r" );
                }

            }

            if ( fullDuplex != isFullDuplex )
            {
                setDuplexMode( fullDuplex );    
            }

            if ( ready == true )
            {
                startChip();
            }

            linkStatusPrev = true;
        }
        else
        {
            if ( linkStatusPrev == true )
            {
                stopChip();
                IOLog( "Ethernet(UniN): Link is down.\n\r" );
            }
            linkStatusPrev = false;
        }
        phyStatusPrev = phyStatus;
    }
}


/*-------------------------------------------------------------------------
 *
 *
 *
 *-------------------------------------------------------------------------*/

void UniNEnet::dumpRegisters()
{
    UInt32      i;

    struct _regtable
    {
        UInt32      reg;
        char        *text;
    } 
    regtable[] =
    {
        { kGEMSEBState                  ,"SEBState"         },
        { kGEMConfig                    ,"Config"       },
        { kGEMInterruptStatus           ,"InterruptStatus"  },
        { kGEMInterruptMask             ,"InterruptMask"    },
        { kGEMInterruptAck              ,"InterruptAck"     },
        { kGEMInterruptAltStatus        ,"InterruptAltStatus"   },
        { kGEMPCIErrorStatus            ,"PCIErrorStatus"   },
        { kGEMPCIErrorMask              ,"PCIErrorMask"     },
        { kGEMBIFConfig                 ,"BIFConfig"        },
        { kGEMBIFDiagnostic             ,"BIFDiagnostic"    },
        { kGEMSoftwareReset             ,"SoftwareReset"    },
        { kGEMTxKick                    ,"TxKick"       },
        { kGEMTxConfig                  ,"TxConfig"         },
        { kGEMTxDescBaseLo              ,"TxDescBaseLo"     },
        { kGEMTxDescBaseHi              ,"TxDescBaseHi"     },
        { kGEMTxFIFOWritePtr            ,"TxFIFOWritePtr"   },
        { kGEMTxFIFOShadowWritePtr      ,"TxFIFOShadowWritePtr" },
        { kGEMTxFIFOReadPtr             ,"TxFIFOReadPtr"    },
        { kGEMTxFIFOShadowReadPtr       ,"TxFIFOShadowReadPtr"  },
        { kGEMTxFIFOPktCounter          ,"TxFIFOPktCounter"     },
        { kGEMTxStateMachine            ,"TxStateMachine"   },
        { kGEMTxDataPtrLo               ,"TxDataPtrLo"      },
        { kGEMTxDataPtrHi               ,"TxDataPtrHi"      },
        { kGEMTxCompletion              ,"TxCompletion"     },
        { kGEMTxFIFOAddr                ,"TxFIFOAddr"       },
        { kGEMTxFIFOTag                 ,"TxFIFOTag"        },
        { kGEMTxFIFODataLo              ,"TxFIFODataLo"     },
        { kGEMTxFIFODataHiTag1          ,"TxFIFODataHiTag1"     },
        { kGEMTxFIFODataHiTag0          ,"TxFIFODataHiTag0"     },
        { kGEMTxFIFOSize                ,"TxFIFOSize"       },
        { kGEMRxConfig                  ,"RxConfig"         },
        { kGEMRxDescBaseLo              ,"RxDescBaseLo"     },
        { kGEMRxDescBaseHi              ,"RxDescBaseHi"     },
        { kGEMRxFIFOWritePtr            ,"RxFIFOWritePtr"   },
        { kGEMRxFIFOShadowWritePtr      ,"RxFIFOShadowWritePtr" },
        { kGEMRxFIFOReadPtr             ,"RxFIFOReadPtr"    },
        { kGEMRxFIFOPktCounter          ,"RxFIFOPktCounter"     },
        { kGEMRxStateMachine            ,"RxStateMachine"   },
        { kGEMRxPauseThresholds         ,"RxPauseThresholds"    },
        { kGEMRxDataPtrLo               ,"RxDataPtrLo"      },
        { kGEMRxDataPtrHi               ,"RxDataPtrHi"      },
        { kGEMRxKick                    ,"RxKick"       },
        { kGEMRxCompletion              ,"RxCompletion"     },
        { kGEMRxIntBlanking             ,"RxIntBlanking"    },
        { kGEMRxFIFOAddr                ,"RxFIFOAddr"       },
        { kGEMRxFIFOTag                 ,"RxFIFOTag"        },
        { kGEMRxDataLo                  ,"RxDataLo"         },
        { kGEMRxDataHiTag0              ,"RxDataHiTag0"     },
        { kGEMRxDataHiTag1              ,"RxDataHiTag1"     },
        { kGEMRxFIFOSize                ,"RxFIFOSize"       },
        { kGEMMacTxResetCmd             ,"MacTxResetCmd"    },
        { kGEMMacRxResetCmd             ,"MacRxResetCmd"    },
        { kGEMMacSendPauseCmd           ,"MacSendPauseCmd"  },
        { kGEMMacTxStatus               ,"MacTxStatus"      },
        { kGEMMacRxStatus               ,"MacRxStatus"      },
        { kGEMMacControlStatus          ,"MacControlStatus"     },
        { kGEMMacTxMask                 ,"MacTxMask"        },
        { kGEMMacRxMask                 ,"MacRxMask"        },
        { kGEMMacControlMask            ,"MacControlMask"   },
        { kGEMMacTxMacConfig            ,"MacTxMacConfig"   },
        { kGEMMacRxMacConfig            ,"MacRxMacConfig"   },
        { kGEMMacControlConfig          ,"MacControlConfig"     },
        { kGEMMacXIFConfig              ,"MacXIFConfig"     },
        { kGEMMacInterPktGap0           ,"MacInterPktGap0"  },
        { kGEMMacInterPktGap1           ,"MacInterPktGap1"  },
        { kGEMMacInterPktGap2           ,"MacInterPktGap2"  },
        { kGEMMacSlotTime               ,"MacSlotTime"      },
        { kGEMMacMinFrameSize           ,"MacMinFrameSize"  },
        { kGEMMacMaxFrameSize           ,"MacMaxFrameSize"  },
        { kGEMMacPASize                 ,"MacPASize"        },
        { kGEMMacJamSize                ,"MacJamSize"       },
        { kGEMMacAttemptLimit           ,"MacAttemptLimit"  },
        { kGEMMacTypeControl            ,"MacTypeControl"   },
        { kGEMMacAddr                   ,"MacAddr"      },
        { kGEMMacAddrFilter             ,"MacAddrFilter"    },
        { kGEMMacAddrFilterMask1        ,"MacAddrFilterMask1"   },
        { kGEMMacAddrFilterMask0        ,"MacAddrFilterMask0"   },
        { kGEMMacHashTable              ,"MacHashTable"     },
        { kGEMMacCollisions             ,"MacCollisions"    },
        { kGEMMacSingleCollision        ,"MacSingleCollision"   },
        { kGEMMacExcessCollisions       ,"MacExcessCollisions"  },
        { kGEMMacLateCollisions         ,"MacLateCollisions"    },
        { kGEMMacDeferTimer             ,"MacDeferTimer"    },
        { kGEMMacPeakAttempts           ,"MacPeakAttempts"  },
        { kGEMMacRxFrameCounter         ,"MacRxFrameCounter"    },
        { kGEMMacRxLengthErrors         ,"MacRxLengthErrors"    },
        { kGEMMacRxAlignErrors          ,"MacRxAlignErrors"     },
        { kGEMMacRxFCSErrors            ,"MacRxFCSErrors"   },
        { kGEMMacRxCodeErrors           ,"MacRxCodeErrors"  },
        { kGEMMacRandomSeed             ,"MacRandomSeed"    },
        { kGEMMacStateMachine           ,"MacStateMachine"  },
        { kGEMMIFCLock                  ,"MIFCLock"         },
        { kGEMMIFData                   ,"MIFData"      },
        { kGEMMIFOutputEnable           ,"MIFOutputEnable"  },
        { kGEMMIFFrame                  ,"MIFFrame"         },
        { kGEMMIFConfig                 ,"MIFConfig"        },
        { kGEMMIFMask                   ,"MIFMask"      },  
        { kGEMMIFStatus                 ,"MIFStatus"        },
        { kGEMMIFStateMachine           ,"MIFStateMachine"  },
        { kGEMPCSControl                ,"PCSControl"       },
        { kGEMPCSStatus                 ,"PCSStatus"        },
        { kGEMPCSAdvert                 ,"PCSAdvert"        },
        { kGEMPCSLPAbility              ,"PCSLPAbility"     },
        { kGEMPCSConfig                 ,"PCSConfig"        },
        { kGEMPCSStateMachine           ,"PCSStateMachine"  },
        { kGEMPCSInterruptStatus        ,"PCSInterruptStatus"   },
        { kGEMPCSDatapathMode           ,"PCSDatapathMode"  },
        { kGEMPCSSerdesControl          ,"PCSSerdesControl"     },
        { kGEMPCSSerdesOutputSelect     ,"PCSSerdesOutputSelect"},
        { kGEMPCSSerdesState            ,"PCSSerdesState"   }
    };

    IOLog("\nEthernet(UniN): IO Address = %08x\n\r", (int)ioBaseEnet );

    for ( i=0; i < sizeof(regtable)/sizeof(regtable[0]); i++ )
    {
        switch ( regtable[i].reg >> 16 )
        {
            case 1:
                IOLog( "Ethernet(UniN): %04x: %s = %02x\n\r", 
                  (int)regtable[i].reg & 0xffff,
                       regtable[i].text,
                       ReadUniNRegister( ioBaseEnet, regtable[i].reg ) );
                break;
            case 2:
                IOLog( "Ethernet(UniN): %04x: %s = %04x\n\r", 
                  (int)regtable[i].reg & 0xffff,
                       regtable[i].text,
                       ReadUniNRegister( ioBaseEnet, regtable[i].reg ) );
                break;
            case 4:
                IOLog( "Ethernet(UniN): %04x: %s = %08x\n\r", 
                  (int)regtable[i].reg & 0xffff,
                       regtable[i].text,
                       ReadUniNRegister( ioBaseEnet, regtable[i].reg ) );
                break;
        }
    }
}


/*-------------------------------------------------------------------------
 *
 *
 *
 *-------------------------------------------------------------------------*/

IOReturn UniNEnet::getHardwareAddress(enet_addr_t *ea)
{
    UInt32      i;
    OSData	*macEntry;
    UInt8       *macAddress;
    UInt32      len;

    macEntry    = OSDynamicCast( OSData, nub->getProperty( "local-mac-address" ) );
    if ( macEntry == 0 )
    {
        return kIOReturnError;
    }

    macAddress  = (UInt8 *)macEntry->getBytesNoCopy();
    if ( macAddress == 0 )
    {
        return kIOReturnError;
    }

    len = macEntry->getLength();
    if ( len != 6 )
    {
        return kIOReturnError;
    }
   
    for (i = 0; i < sizeof(*ea); i++)   
    {
        ea->ea_byte[i] = macAddress[i];
    }
    return kIOReturnSuccess;
}

/*-------------------------------------------------------------------------
 *
 *
 *
 *-------------------------------------------------------------------------*/

#define ENET_CRCPOLY 0x04c11db7

static UInt32 crc416(UInt32 current, UInt16 nxtval )
{
    register UInt32 counter;
    register int highCRCBitSet, lowDataBitSet;

    /* Swap bytes */
    nxtval = ((nxtval & 0x00FF) << 8) | (nxtval >> 8);

    /* Compute bit-by-bit */
    for (counter = 0; counter != 16; ++counter)
    {   /* is high CRC bit set? */
      if ((current & 0x80000000) == 0)  
        highCRCBitSet = 0;
      else
        highCRCBitSet = 1;
        
      current = current << 1;
    
      if ((nxtval & 0x0001) == 0)
        lowDataBitSet = 0;
      else
    lowDataBitSet = 1;

      nxtval = nxtval >> 1;
    
      /* do the XOR */
      if (highCRCBitSet ^ lowDataBitSet)
        current = current ^ ENET_CRCPOLY;
    }
    return current;
}

/*-------------------------------------------------------------------------
 *
 *
 *
 *-------------------------------------------------------------------------*/

static UInt32 mace_crc(UInt16 *address)
{   
    register UInt32 newcrc;

    newcrc = crc416(0xffffffff, *address);  /* address bits 47 - 32 */
    newcrc = crc416(newcrc, address[1]);    /* address bits 31 - 16 */
    newcrc = crc416(newcrc, address[2]);    /* address bits 15 - 0  */

    return(newcrc);
}

/*-------------------------------------------------------------------------
 *
 *
 *
 *-------------------------------------------------------------------------*/

/*
 * Add requested mcast addr to UniN's hash table filter.  
 *  
 */
void UniNEnet::addToHashTableMask(UInt8 *addr)
{   
    UInt32   i,j;
    UInt32   crcBitIndex;
    UInt16   mask;

    j = mace_crc((UInt16 *)addr) & 0xFF; /* Big-endian alert! */
   
    for ( crcBitIndex = i = 0; i < 8; i++ )
    {
        crcBitIndex >>= 1;
        crcBitIndex  |= (j & 0x80);
        j           <<= 1;
    }
     
    crcBitIndex ^= 0xFF;
            
    if (hashTableUseCount[crcBitIndex]++)   
      return;           /* This bit is already set */
    mask = crcBitIndex % 16;
    mask = 1 << mask;
    hashTableMask[crcBitIndex/16] |= mask;
}

/*-------------------------------------------------------------------------
 *
 *
 *
 *-------------------------------------------------------------------------*/

void UniNEnet::resetHashTableMask()
{
    bzero(hashTableUseCount, sizeof(hashTableUseCount));
    bzero(hashTableMask, sizeof(hashTableMask));
}

/*-------------------------------------------------------------------------
 *
 *
 *
 *-------------------------------------------------------------------------*/

/*
 * Sync the adapter with the software copy of the multicast mask
 *  (logical address filter).
 */
void UniNEnet::updateHashTableMask()
{
    UInt32      i;
    UInt16      rxCFGReg;

    rxCFGReg = ReadUniNRegister(ioBaseEnet, kGEMMacRxMacConfig);
    WriteUniNRegister(ioBaseEnet, kGEMMacRxMacConfig, rxCFGReg & ~(kGEMMacRxMacConfig_RxMacEnable | 
                                                                  kGEMMacRxMacConfig_HashFilterEnable) );

    while ( ReadUniNRegister(ioBaseEnet, kGEMMacRxMacConfig) & (kGEMMacRxMacConfig_RxMacEnable | 
                                                               kGEMMacRxMacConfig_HashFilterEnable) )
      ;
    for (i=0; i<16; i++ )
    {
        WriteUniNRegister( ioBaseEnet, kGEMMacHashTable + i*4, hashTableMask[15-i] );
    }

    rxCFGReg |= kGEMMacRxMacConfig_HashFilterEnable;
    WriteUniNRegister(ioBaseEnet, kGEMMacRxMacConfig, rxCFGReg );
}