Source to bsd/dev/ppc/drvBMacEnet/BMacEnet.m


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

/*
 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * "Portions Copyright (c) 1999 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 1.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.apple.com/publicsource 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 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 by Apple Computer, Inc., All rights reserved.
 *
 * Hardware independent (relatively) code for the BMac Ethernet Controller 
 *
 * HISTORY
 *
 * dd-mmm-yy	 
 *	Created.
 *
 */

#import "BMacEnetPrivate.h"

@implementation BMacEnet

/*
 * Public Factory Methods
 */
 
+ (BOOL)probe:(IOTreeDevice *)devDesc
{
    BMacEnet		*BMacEnetInstance;

    BMacEnetInstance = [self alloc];
    return [BMacEnetInstance initFromDeviceDescription:devDesc] != nil;
}

/*
 * Public Instance Methods
 */

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

- initFromDeviceDescription:(IOTreeDevice *)devDesc
{
    IOTreeDevice	*devDescHeathrow;
    IORange		*ioRangeBMac;
    IORange		*ioRangeHeathrow;
    
    if ([super initFromDeviceDescription:devDesc] == nil)     
    {
      IOLog( "Ethernet(BMac): [super initFromDeviceDescription] failed\n");
      return nil;
    }

    if ( [devDesc numMemoryRanges] < 3 )
    {
      IOLog( "Ethernet(BMac): Incorrect deviceDescription - 1\n\r");
      return nil;
    }

    ioRangeBMac = [devDesc memoryRangeList];

    ioBaseEnet      = (IOPPCAddress) ioRangeBMac[0].start;
    ioBaseEnetTxDMA = (IOPPCAddress) ioRangeBMac[1].start;
    ioBaseEnetRxDMA = (IOPPCAddress) ioRangeBMac[2].start;

    devDescHeathrow = [[devDesc parent] deviceDescription];
  
    if ( [devDescHeathrow numMemoryRanges] < 1 )
    {  
      IOLog( "Ethernet(BMac): Incorrect deviceDescription - 2\n\r");
      return nil;
    } 

    ioRangeHeathrow = [devDescHeathrow memoryRangeList];

    ioBaseHeathrow  = (IOPPCAddress) ioRangeHeathrow[0].start;

    sromAddressBits   = 6;
    enetAddressOffset = 20;
    phyId             = -1;
    phyMIIDelay	      = MII_DEFAULT_DELAY;

    if ( ![self resetAndEnable:NO] ) 
    {
      [self free];
      return nil;
    }

    [self _getStationAddress:&myAddress]; 

    if ([self _allocateMemory] == NO) 
    {
      [self free];
      return nil;
    }

    if ( ![self resetAndEnable:YES] ) 
    {
      [self free];
      return nil;
    }

    isPromiscuous = NO;
    multicastEnabled = NO;

    networkInterface = [super attachToNetworkWithAddress:myAddress];

    [networkInterface getIONetworkIfnet]->if_eflags |= IFEF_DVR_REENTRY_OK;

    return self;
}

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

- free
{
    int		i;
    
    [self clearTimeout];
    
    [self _resetChip];
    
    if (networkInterface)
	[networkInterface free];

    for (i = 0; i < rxMaxCommand; i++)
    	if (rxNetbuf[i])  nb_free(rxNetbuf[i]);

    for (i = 0; i < txMaxCommand; i++)
    	if (txNetbuf[i]) nb_free(txNetbuf[i]);
    
    return [super free];
}

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

- (void)interruptOccurredAt:(int)irqNum
{    

    [self reserveDebuggerLock];

    switch ( irqNum )
    {
      /*
       * On the transmit side, we use the chipset interrupt. Using the transmit
       * DMA interrupt (or having multiple transmit DMA entries) would allows us to 
       * send the next frame to the chipset prior the transmit fifo going empty. 
       * However, this aggrevates a BMac chipset bug where the next frame going out
       * gets corrupted (first two bytes lost) if the chipset had to retry the previous
       * frame.
       */ 
      case kIRQEnetDev:
      case kIRQEnetTxDMA:
        txWDInterrupts++;
        KERNEL_DEBUG(DBG_BMAC_TXIRQ | DBG_FUNC_START, 0, 0, 0, 0, 0 );
        [self _transmitInterruptOccurred];
        [self serviceTransmitQueue];
        KERNEL_DEBUG(DBG_BMAC_TXIRQ | DBG_FUNC_END,   0, 0, 0, 0, 0 );
        break;
      case kIRQEnetRxDMA:
        KERNEL_DEBUG(DBG_BMAC_RXIRQ | DBG_FUNC_START, 0, 0, 0, 0, 0 );
        [self _receiveInterruptOccurred];
        KERNEL_DEBUG(DBG_BMAC_RXIRQ | DBG_FUNC_END,   0, 0, 0, 0, 0 );
        break;
    }

    [self enableAllInterrupts];

    [self releaseDebuggerLock];
}

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

- (void) serviceTransmitQueue
{
    netbuf_t			packet;
    u_int32_t			i;

    while ( 1 )
    {
      if ( ![transmitQueue count] )
      {
        break;
      }

      i = txCommandTail + 1;
      if ( i >= txMaxCommand ) i = 0;
      if ( i == txCommandHead )
      {
        break;
      }
      packet = [transmitQueue dequeue];
      [self _transmitPacket:packet];
    }

}

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

- (void)transmit:(netbuf_t)pkt
{
    u_int32_t 		i;
    u_int32_t           n;

    KERNEL_DEBUG(DBG_BMAC_TXQUEUE | DBG_FUNC_NONE, (int) pkt, (int) nb_size(pkt), 0, 0, 0 );

    /*
     * Hold the debugger lock so the debugger can't interrupt us
     */
    [self reserveDebuggerLock];
 
    do
    {
      /* 
       * Preliminary sanity checks
       */
      if (!pkt) 
      {
        IOLog("EtherNet(BMac): transmit received NULL netbuf\n");
        continue;  
      }
      if ( ![self isRunning] ) 
      {
        nb_free(pkt);
        continue;
      }

#if 0
      /*
       * Remove any completed packets from the Tx ring 
       */
      if ( chipId >= kCHIPID_PaddingtonXmitStreaming )
      {
        [self _transmitInterruptOccurred]; 
      }
#endif
	
      /*
       * Refill the Tx ring from the Transmit waiting list
       */
      [self serviceTransmitQueue];

      /*
       * If the Transmit waiting list is not empty or the Tx ring is
       * full, add the new packet to the waiting list.
       */
      n = [transmitQueue count];
      if ( n > 0 )
      {
        if ( n >= [transmitQueue maxCount] )
        {
          IOLog("Ethernet(BMac): Transmit queue overflow\n");
        }

        [transmitQueue enqueue:pkt];
        continue;
      }
      i = txCommandTail + 1;
      if ( i >= txMaxCommand ) i = 0;
      if ( i == txCommandHead )
      {
        [transmitQueue enqueue:pkt];
        continue;
      }

      /*
       * If there is space on the Tx ring, add the packet directly to the
       * ring
       */
      [self _transmitPacket:pkt];
    }
    while( 0 );

    [self releaseDebuggerLock];
}

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

- (int) transmitQueueSize
{
    return (TRANSMIT_QUEUE_SIZE);
}

- (int) transmitQueueCount
{
    return ([transmitQueue count]);
}
/*-------------------------------------------------------------------------
 *
 *
 *
 *-------------------------------------------------------------------------*/

- (BOOL)resetAndEnable:(BOOL)enable
{
    resetAndEnabled = NO;
    [self clearTimeout];
    [self disableAllInterrupts];
    [self _resetChip];
    
    if (enable) 
    {
      if (![self _initRxRing] || ![self _initTxRing])	
      {
        return NO;
      }

      if ( phyId != 0xff )
      {
          [self miiInitializePHY:phyId];
      }

      if ([self _initChip] == NO) 
      {
	[self setRunning:NO];
	return NO;
      }

      [self _startChip];
	
      [self setRelativeTimeout: WATCHDOG_TIMER_MS];

      [self enableAllInterrupts]; 
      [self _enableAdapterInterrupts];

      resetAndEnabled = YES;

      [self _sendDummyPacket];      
    }

    [self setRunning:enable];

    return YES;
}

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

- (void)timeoutOccurred
{
    u_int32_t		dmaStatus;
    u_int16_t		phyStatus;
    u_int16_t		linkStatus;
    u_int16_t		phyStatusChange;
    BOOL		fullDuplex = NO;

    if ( ![self isRunning] ) 
    {    
      return;
    }

    [self reserveDebuggerLock];

    /*
     * Check for DMA shutdown on receive channel
     */
    dmaStatus = IOGetDBDMAChannelStatus( ioBaseEnetRxDMA );
    if ( !(dmaStatus & kdbdmaActive) )
    {
      IOLog( "Ethernet(BMac): Checking for timeout - RxHead = %d RxTail = %d\n\r", rxCommandHead, rxCommandTail); 
#if 0
      IOLog( "Ethernet(BMac): Rx Commands = %08x(p) Rx DMA Ptr = %08x(p)\n\r", rxDMACommandsPhys, IOGetDBDMACommandPtr(ioBaseEnetRxDMA) ); 
      [self _dumpDesc:(void *)rxDMACommands Size:rxMaxCommand * sizeof(enet_dma_cmd_t)];
#endif 
      [self _receiveInterruptOccurred];
    } 

    /*
     * If there are pending entries on the Tx ring
     */
    if ( txCommandHead != txCommandTail )
    {
      /* 
       * If we did not service the Tx ring during the last timeout interval,
       * then force servicing of the Tx ring
       */
      if ( txWDInterrupts == 0 )
      {      
        if ( txWDCount++ > 0 )
        {
          if ( [self _transmitInterruptOccurred] == NO )
          {
              IOLog( "Ethernet(BMac): Checking for timeout - TxHead = %d TxTail = %d\n\r", txCommandHead, txCommandTail); 
              [self _restartTransmitter];
          }
        }
      }
      else
      {
        txWDInterrupts   = 0;
        txWDCount        = 0;
      }
    }
    else
    {      
      txWDInterrupts   = 0;
      txWDCount        = 0;
    }

    if ( phyId != 0xff )
    {
        if ( [self miiReadWord:&phyStatus reg:MII_STATUS phy:phyId] == YES )
        {
            phyStatusChange = (phyStatusPrev ^ phyStatus) & (MII_STATUS_LINK_STATUS | MII_STATUS_NEGOTIATION_COMPLETE);
            if ( phyStatusChange )
            {
                if ( (phyStatus & MII_STATUS_LINK_STATUS) && (phyStatus & MII_STATUS_NEGOTIATION_COMPLETE ) )
                {
                    if ( (phyType & MII_ST10040_MASK) == MII_ST10040_ID )
                    {
                        [self miiReadWord:&linkStatus reg:MII_ST10040_CHIPST phy:phyId];

                        fullDuplex =  (linkStatus & MII_ST10040_CHIPST_DUPLEX) ? YES : NO;

                        IOLog( "Ethernet(BMac): Link is up at %sMb - %s Duplex\n\r",
                              (linkStatus & MII_ST10040_CHIPST_SPEED)  ? "100" : "10",
                              (fullDuplex)                             ? "Full" : "Half" );                        
                    }
                    else if ( (phyType & MII_DP83843_MASK) == MII_DP83843_ID )
                    {
                        [self miiReadWord:&linkStatus reg:MII_DP83843_PHYSTS phy:phyId];

                        fullDuplex =  (linkStatus & MII_DP83843_PHYSTS_DUPLEX) ? YES : NO;

                        IOLog( "Ethernet(BMac): Link is up at %sMb - %s Duplex\n\r",
                              (linkStatus & MII_DP83843_PHYSTS_SPEED10) ? "10" : "100",
                              (fullDuplex)                              ? "Full" : "Half" );           
                     }  
                     else
                     {
                        fullDuplex = NO ;
                        IOLog( "Ethernet(BMac): Link is up\n\r" );
                     }

                     if ( fullDuplex != isFullDuplex )
                     {
                         [self _setDuplexMode: fullDuplex];    
                     }
                }
                else
                {
                    IOLog( "Ethernet(BMac): Link is down.\n\r" );
                }
                phyStatusPrev = phyStatus;
            }
        }
    }          

    /*
     * Restart the watchdog timer
     */
    [self setRelativeTimeout: WATCHDOG_TIMER_MS];

    [self releaseDebuggerLock];

}

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

- (BOOL)enablePromiscuousMode
{
    u_int16_t		rxCFGVal;
    
    isPromiscuous = YES;
    
    [self reserveDebuggerLock];

    /*
     * Turn off the receiver and wait for the chipset to acknowledge
     */
    rxCFGVal = ReadBigMacRegister(ioBaseEnet, kRXCFG);
    WriteBigMacRegister(ioBaseEnet, kRXCFG, rxCFGVal & ~kRxMACEnable );
    while( ReadBigMacRegister(ioBaseEnet, kRXCFG) & kRxMACEnable )
      ;

    /*
     * Set promiscuous mode and restore receiver state
     */
    rxCFGVal |= kRxPromiscEnable;	
    WriteBigMacRegister( ioBaseEnet, kRXCFG, rxCFGVal );		

    [self releaseDebuggerLock];
    
    return YES;
}

- (void)disablePromiscuousMode
{
    u_int16_t		rxCFGVal;
    
    isPromiscuous = NO;
    
    [self reserveDebuggerLock];

    /*
     * Turn off the receiver and wait for the chipset to acknowledge
     */
    rxCFGVal = ReadBigMacRegister(ioBaseEnet, kRXCFG);
    WriteBigMacRegister(ioBaseEnet, kRXCFG, rxCFGVal & ~kRxMACEnable );
    while( ReadBigMacRegister(ioBaseEnet, kRXCFG) & kRxMACEnable )
      ;

    /*
     * Reset promiscuous mode and restore receiver state
     */
    rxCFGVal &= ~kRxPromiscEnable;	
    WriteBigMacRegister( ioBaseEnet, kRXCFG, rxCFGVal );		

    [self releaseDebuggerLock];
}

- (BOOL)enableMulticastMode
{
    multicastEnabled = YES;
    return YES;
}
	 
- (void)disableMulticastMode
{
    multicastEnabled = NO;
}

- (void)addMulticastAddress:(enet_addr_t *)addr
{
    multicastEnabled = YES;
    [self reserveDebuggerLock];
    [self _addToHashTableMask:addr->ea_byte];
    [self _updateBMacHashTableMask];
    [self releaseDebuggerLock];
}

- (void)removeMulticastAddress:(enet_addr_t *)addr
{
    [self reserveDebuggerLock];
    [self _removeFromHashTableMask:addr->ea_byte];
    [self _updateBMacHashTableMask];
    [self releaseDebuggerLock];
}

/*
 * Kernel Debugger Support 
 */
- (void)sendPacket:(void *)pkt length:(unsigned int)pkt_len
{
    [self _sendPacket:(void *)pkt length:(unsigned int)pkt_len];
}

- (void)receivePacket:(void *)pkt length:(unsigned int *)pkt_len  timeout:(unsigned int)timeout
{
    [self _receivePacket:(void *)pkt length:(unsigned int *)pkt_len  timeout:(unsigned int)timeout];
}

/*
 * Power management methods. 
 */
- (IOReturn)getPowerState:(PMPowerState *)state_p
{
    return IO_R_UNSUPPORTED;
}

- (IOReturn)setPowerState:(PMPowerState)state
{
    if (state == PM_OFF) {
	resetAndEnabled = NO;
        [self _resetChip];
	return IO_R_SUCCESS;
    }
    return IO_R_UNSUPPORTED;
}

- (IOReturn)getPowerManagement:(PMPowerManagementState *)state_p
{
    return IO_R_UNSUPPORTED;
}

- (IOReturn)setPowerManagement:(PMPowerManagementState)state
{
    return IO_R_UNSUPPORTED;
}

- (void)reserveDebuggerLock
{
    if ( debuggerLockCount++ == 0 )
    {
        [super reserveDebuggerLock];
    }
}   

- (void)releaseDebuggerLock
{
    if ( --debuggerLockCount == 0 )
    {
        [super releaseDebuggerLock];
    }
}   


@end