Source to iokit/Drivers/network/drvPPCBMac/BMacEnet.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 by Apple Computer, Inc., All rights reserved.
 *
 * Hardware independent (relatively) code for the BMac Ethernet Controller 
 *
 * HISTORY
 *
 * dd-mmm-yy	 
 *	Created.
 *
 * Dec 10, 1998		jliu
 *  Converted to IOKit/C++.
 */

#include "BMacEnet.h"
#include "BMacEnetPrivate.h"

#include <IOKit/IORegistryEntry.h>
#include <IOKit/IODeviceTreeSupport.h>
#include <IOKit/platform/AppleMacIODevice.h>
#include <IOKit/assert.h>

// #define DEBUG_JOE	1

//------------------------------------------------------------------------

#define super IOEthernetController

OSDefineMetaClassAndStructors( BMacEnet, IOEthernetController )

//------------------------------------------------------------------------

#define PROVIDER_DEV	0
#define	PROVIDER_DMA_TX	1
#define	PROVIDER_DMA_RX	2

/*
 * Public Instance Methods
 */

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

bool BMacEnet::init(OSDictionary * properties = 0)
{
	/*
	 * Initialize my ivars.
	 */
	networkInterface  = 0;
	transmitQueue     = 0;
	debugQueue        = 0;
	debugger          = 0;
	rxIntSrc          = 0;
	txDebuggerPkt     = 0;
	phyId             = 0xff;
	phyMIIDelay	      = MII_DEFAULT_DELAY;
    sromAddressBits   = 6;
    enetAddressOffset = 20;
	phyStatusPrev     = 0;
	ready             = false;
	debugClient       = false;
	debugTxPoll       = false;
	netifClient       = false;
    isPromiscuous	  = false;
    multicastEnabled  = false;

	if (!super::init(properties))
		return false;

	/*
	 * Clear the mbuf rings.
	 */
	for (int i = 0; i < TX_RING_LENGTH; i++)
		txMbuf[i] = 0;
	for (int i = 0; i < RX_RING_LENGTH; i++)
		rxMbuf[i] = 0;

	/*
	 * Clear the memory map objects.
	 */
	for (int i = 0; i < MEMORY_MAP_COUNT; i++)
		maps[i] = 0;

	return true;
}

bool BMacEnet::start(IOService * provider)
{
	AppleMacIODevice *nub = OSDynamicCast(AppleMacIODevice, provider);
	IOInterruptEventSource *intES;

	if (!nub || !super::start(provider))
		return false;

	transmitQueue = OSDynamicCast(IOOQGateFIFOQueue, getOutputQueue());
	if (!transmitQueue) {
		IOLog("BMac: output queue initialization failed\n");
		return false;
	}
	transmitQueue->retain();

	// Allocate debug queue. This stores packets retired from the TX ring
	// by the polling routine. We cannot call freePacket() or m_free() within
	// the debugger context.
	//
	// The capacity of the queue is set at maximum to prevent the queue from
	// calling m_free() due to over-capacity. But we don't expect the size
	// of the queue to grow too large.
	//
	debugQueue = IOPacketQueue::withCapacity((UInt) -1);
	if (!debugQueue)
		return false;

	// Allocate a IOMBufDBDMAMemoryCursor instance. Currently, the maximum
	// number of segments is set to 2. The maximum length for each segment
	// is set to the maximum ethernet frame size (plus padding).

	mbufCursor = IOMBufBigMemoryCursor::withSpecification(NETWORK_BUFSIZE, 2);
	if (!mbufCursor) {
		IOLog("Ethernet(BMac): IOMBufDBDMAMemoryCursor allocation failure\n");
		return false;
	}

	//
	// Our provider is the nub representing the BMacEnet hardware
	// controller. We will query it for our resource information.
	//

	for (int i = 0; i < MEMORY_MAP_HEATHROW_INDEX; i++) {
		IOMemoryMap *		map;
		
		map = provider->mapDeviceMemoryWithIndex(i);
		if (!map)
			return false;
#ifdef DEBUG_JOE
		IOLog("map %d: Phys:%08x Virt:%08x len:%d\n",
			i,
			(unsigned) map->getPhysicalAddress(),
			(unsigned) map->getVirtualAddress(),
			(unsigned) map->getLength());
#endif DEBUG_JOE

		switch (i) {
			case MEMORY_MAP_ENET_INDEX:
				ioBaseEnet = (IOPPCAddress) map->getVirtualAddress();
				break;
			
			case MEMORY_MAP_TXDMA_INDEX:
				ioBaseEnetTxDMA = (IODBDMAChannelRegisters *)
					map->getVirtualAddress();
				break;
			
			case MEMORY_MAP_RXDMA_INDEX:
				ioBaseEnetRxDMA = (IODBDMAChannelRegisters *)
					map->getVirtualAddress();
				break;
		}
		
		maps[i] = map;
	}

#ifdef DEBUG_JOE
	IOLog("ioBaseEnet:      0x%08x\n", ioBaseEnet);
	IOLog("ioBaseEnetTxDMA: 0x%08x\n", ioBaseEnetTxDMA);
	IOLog("ioBaseEnetRxDMA: 0x%08x\n", ioBaseEnetRxDMA);
#endif DEBUG_JOE

	//
	// We need to get the I/O address for the Heathrow controller.
	// We ask the provider (bmac) for its device tree parent.
	//
	IOService *heathrow;
	if (!(heathrow = OSDynamicCast(IOService,
                            provider->getParentEntry( gIODTPlane ))))
		return false;

	// Check whether the hardware is susceptible to the broken unicast
	// filter problem.
	//
	OSData * devIDData;
	devIDData = OSDynamicCast(OSData, heathrow->getProperty("device-id"));

	if (devIDData) {
		useUnicastFilter = ( *((UInt32 *) devIDData->getBytesNoCopy()) ==
                             0x10 );
		if (useUnicastFilter)
			IOLog("%s: Enabling workaround for broken unicast filter\n", 
				getName());
	}

	IOMemoryMap *		map = heathrow->mapDeviceMemoryWithIndex(0);
	if (map)
	{
#ifdef DEBUG_JOE
		IOLog("Heathrow: Phys:%08x Virt:%08x len:%d\n",
			(unsigned) map->getPhysicalAddress(),
			(unsigned) map->getVirtualAddress(),
			(unsigned) map->getLength());
#endif DEBUG_JOE
		ioBaseHeathrow = (IOPPCAddress) map->getVirtualAddress();
		
		maps[MEMORY_MAP_HEATHROW_INDEX] = map;
	}
	else {
		return false;
	}

	//
	// Get a reference to the IOWorkLoop in our superclass.
	//
	IOWorkLoop * myWorkLoop = getWorkLoop();
	assert(myWorkLoop);

	//
	// Allocate three IOInterruptEventSources.
	//
	rxIntSrc = IOInterruptEventSource::interruptEventSource
            (this,
             (IOInterruptEventAction) &BMacEnet::interruptOccurredForSource,
             provider, PROVIDER_DMA_RX);
        if (!rxIntSrc
        || (myWorkLoop->addEventSource(rxIntSrc) != kIOReturnSuccess)) {
		IOLog("Ethernet(BMac): rxIntSrc init failure\n");
		return false;
	}

	intES = IOInterruptEventSource::interruptEventSource
            (this,
             (IOInterruptEventAction) &BMacEnet::interruptOccurredForSource,
             provider, PROVIDER_DMA_TX);
        if (intES) {
            bool res = (myWorkLoop->addEventSource(intES) != kIOReturnSuccess);
            intES->release();
            if (res) {
		IOLog("Ethernet(BMac): PROVIDER_DMA_TX add failure\n");
                return false;
            }
        }
        else {
		IOLog("Mace: PROVIDER_DMA_TX init failure\n");
		return false;
	}
	
	intES = IOInterruptEventSource::interruptEventSource
            (this,
             (IOInterruptEventAction) &BMacEnet::interruptOccurredForSource,	
	     provider, PROVIDER_DEV);	
	if (intES) {	
	    bool res = (myWorkLoop->addEventSource(intES) != kIOReturnSuccess);	
	    intES->release();	
	    if (res) {
		IOLog("Ethernet(BMac): PROVIDER_DEV add failure\n");	
	        return false;	
	    }	
	}	
	else {
		IOLog("Ethernet(BMac): PROVIDER_DEV init failure\n");
		return false;
	}
	
	timerSrc = IOTimerEventSource::timerEventSource
            (this, (IOTimerEventSource::Action) &BMacEnet::timeoutOccurred);
	if (!timerSrc
	|| (myWorkLoop->addEventSource(timerSrc) != kIOReturnSuccess)) {
		IOLog("Ethernet(BMac): timerSrc init failure\n");
		return false;
	}

	MGETHDR(txDebuggerPkt, M_DONTWAIT, MT_DATA);
	if (!txDebuggerPkt) {
		IOLog("Ethernet(BMac): Couldn't allocate KDB buffer\n");
		return false;
	}

#if 0
	// Enable the interrupt event sources. The hardware interrupts
	// sources remain disabled until _resetAndEnable(true) is called.
	//
	// myWorkLoop->enableAllInterrupts();
#endif

	//
	// Perform a hardware reset.
	//
    if ( !_resetAndEnable(false) ) 
    {
		return false;
    }

	//
	// Cache my MAC address.
	//
	getHardwareAddress(&myAddress);

	//
	// Allocate memory for ring buffers.
	//
    if (_allocateMemory() == false) 
    {
		return false;
    }

	//
	// Attach a kernel debugger client.
	//
	attachDebuggerClient(&debugger);

	//
	// Attach an IOEthernetInterface client.
	//
	if (!attachNetworkInterface((IONetworkInterface **) &networkInterface))
		return false;

	return true;
}

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

void BMacEnet::free()
{
    UInt i;

	_resetAndEnable(false);

	if (debugger)
		debugger->release();

	if (getWorkLoop())
		getWorkLoop()->disableAllEventSources();

	if (timerSrc)
		timerSrc->release();

	if (rxIntSrc)
		rxIntSrc->release();

	if (txDebuggerPkt)
		freePacket(txDebuggerPkt);

	if (transmitQueue)
		transmitQueue->release();

	if (debugQueue)
		debugQueue->release();

    if (networkInterface)
		networkInterface->release();

	if (mbufCursor)
		mbufCursor->release();

    for (i = 0; i < rxMaxCommand; i++)
    	if (rxMbuf[i]) freePacket(rxMbuf[i]);

    for (i = 0; i < txMaxCommand; i++)
    	if (txMbuf[i]) freePacket(txMbuf[i]);

	for (i = 0; i < MEMORY_MAP_COUNT; i++)
		if (maps[i]) maps[i]->release();

	if (dmaMemory.ptr)
	{
		IOFree(dmaMemory.ptrReal, dmaMemory.sizeReal);
		dmaMemory.ptr = 0;
	}

	super::free();
}

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

void BMacEnet::interruptOccurredForSource(IOInterruptEventSource *src,
		 int /*count*/)
{
	bool doFlushQueue = false;
	bool doService    = false;

	reserveDebuggerLock();

    statReg = ReadBigMacRegister( ioBaseEnet, kSTAT );

	if (src == rxIntSrc) {	
		KERNEL_DEBUG(DBG_BMAC_RXIRQ | DBG_FUNC_START, 0, 0, 0, 0, 0 );		
		doFlushQueue = _receiveInterruptOccurred();	
		KERNEL_DEBUG(DBG_BMAC_RXIRQ | DBG_FUNC_END,   0, 0, 0, 0, 0 );	
	}
	else {			
      /*
       * 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.
       */ 
        txWDInterrupts++;
        KERNEL_DEBUG(DBG_BMAC_TXIRQ | DBG_FUNC_START, 0, 0, 0, 0, 0 );
		doService = _transmitInterruptOccurred();
        KERNEL_DEBUG(DBG_BMAC_TXIRQ | DBG_FUNC_END,   0, 0, 0, 0, 0 );
	}

	releaseDebuggerLock();

	/*
	 * Submit all received packets queued up by _receiveInterruptOccurred()
	 * to the network stack. The up call is performed without holding the
	 * debugger lock.
	 */
	if (doFlushQueue)
		networkInterface->flushInputQueue();

	/*
	 * Unstall the output queue if some space was made available.
	 */
	if (doService && netifClient)
		transmitQueue->service();
}

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

UInt32 BMacEnet::outputPacket(struct mbuf * pkt)
{
	u_int32_t	i;
	UInt32      ret = kIOOQReturnSuccess;

    KERNEL_DEBUG(DBG_BMAC_TXQUEUE | DBG_FUNC_NONE, (int) pkt,
		(int) pkt->m_pkthdr.len, 0, 0, 0 );

    /*
     * Hold the debugger lock so the debugger can't interrupt us
     */
	reserveDebuggerLock();
 
    do
    {
		/* 
		 * Preliminary sanity checks
		 */
		assert(pkt && netifClient);

#if 0
		/*
		 * Remove any completed packets from the Tx ring 
		 */
		if ( chipId >= kCHIPID_PaddingtonXmitStreaming )
		{
			_transmitInterruptOccurred(); 
		}
#endif

		i = txCommandTail + 1;
		if ( i >= txMaxCommand ) i = 0;
		if ( i == txCommandHead )
		{
			/*
			 * Ring buffer is full. Disable the dequeueing process.
			 * We reenable it when an entry is made available by the
			 * transmit interrupt handler, or if a timeout occurs.
			 */
			ret = kIOOQReturnStall;
			continue;
		}

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

	releaseDebuggerLock();

	return ret;
}

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

bool BMacEnet::_resetAndEnable(bool enable)
{
	bool ret = true;

//	reserveDebuggerLock();

	ready = false;

	if (timerSrc) timerSrc->cancelTimeout();

	_disableAdapterInterrupts();
	if (getWorkLoop()) getWorkLoop()->disableAllInterrupts();

	_resetChip();
    
	while (enable)
	{
		if (!_initRxRing() || !_initTxRing())	
		{
			ret = false;
			break;
		}

		if ( phyId != 0xff )
		{
			miiInitializePHY(phyId);
		}

		if (_initChip() == false)
		{
			ret = false;
			break;
		}

		_startChip();

		timerSrc->setTimeoutMS(WATCHDOG_TIMER_MS);

		if (getWorkLoop()) getWorkLoop()->enableAllInterrupts();
		_enableAdapterInterrupts();

		ready = true;

		_sendDummyPacket();

		break;
	}
	
//	releaseDebuggerLock();

    return ret;
}

/*-------------------------------------------------------------------------
 * Called by IOEthernetInterface client to enable the controller.
 * This method is always called while running on the default workloop
 * thread.
 *-------------------------------------------------------------------------*/

IOReturn BMacEnet::enable(IONetworkInterface * netif)
{
	IONetworkParameter * param;

	// If an interface client has previously enabled us,
	// and we know there can only be one interface client
	// for this driver, then simply return true.
	//
	if (netifClient) {
		IOLog("EtherNet(BMac): already enabled\n");
		return kIOReturnSuccess;
	}

	// Grab a pointer to the statistics structure in the interface.
	//
	param = netif->getParameter(kIONetworkStatsKey);
	if (!param || !(netStats = (IONetworkStats *) param->getBuffer()))
	{
		IOLog("EtherNet(BMac): invalid network statistics\n");
		return kIOReturnError;
	}

	if ((ready == false) && !_resetAndEnable(true))
		return kIOReturnIOError;

	// Record the interface as an active client.
	//
	netifClient = true;

	// Start our IOOutputQueue object.
	//
	transmitQueue->setCapacity(TRANSMIT_QUEUE_SIZE);
	transmitQueue->start();

	return kIOReturnSuccess;
}

/*-------------------------------------------------------------------------
 * Called by IOEthernetInterface client to disable the controller.
 * This method is always called while running on the default workloop
 * thread.
 *-------------------------------------------------------------------------*/
 
IOReturn BMacEnet::disable(IONetworkInterface * /*netif*/)
{
	// If we have no active clients, then disable the controller.
	//
	if (debugClient == false)
		_resetAndEnable(false);

	// Disable our IOOutputQueue object. This will prevent the
	// outputPacket() method from being called.
	//
	transmitQueue->stop();

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

	return kIOReturnSuccess;
}

/*-------------------------------------------------------------------------
 * This method is called by our debugger client to bring up the controller
 * just before the controller is registered as the debugger device. The
 * debugger client is attached in response to the attachDebuggerClient()
 * call.
 *
 * This method is always called while running on the default workloop
 * thread.
 *-------------------------------------------------------------------------*/

IOReturn BMacEnet::handleDebuggerOpen(IOKernelDebugger * /*debugger*/)
{
	// Enable hardware and make it ready to support the debugger client.
	//
	if ((ready == false) && !_resetAndEnable(true))
		return kIOReturnIOError;

	// Record the debugger as an active client of ours.
	//
	debugClient = true;

	// Returning true will allow the kdp registration to continue.
	// If we return false, then we will not be registered as the
	// debugger device, and the attachDebuggerClient() call will
	// return NULL.
	//
	return kIOReturnSuccess;
}

/*-------------------------------------------------------------------------
 * This method is called by our debugger client to stop the controller.
 * The debugger will call this method when we issue a detachDebuggerClient().
 *
 * This method is always called while running on the default workloop
 * thread.
 *-------------------------------------------------------------------------*/

IOReturn BMacEnet::handleDebuggerClose(IOKernelDebugger * /*debugger*/)
{
	debugClient = false;

	// If we have no active clients, then disable the controller.
	//
	if (netifClient == false)
		_resetAndEnable(false);

	return kIOReturnSuccess;
}

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

void BMacEnet::timeoutOccurred(IOTimerEventSource * /*timer*/)
{
    u_int32_t		dmaStatus;
    u_int16_t		phyStatus;
    u_int16_t		linkStatus;
    u_int16_t		phyStatusChange;
	bool			fullDuplex   = false;
	bool			doFlushQueue = false;
	bool			doService    = false;

	// IOLog("Ethernet(BMac): watchdog timer\n");

	reserveDebuggerLock();
	
    /*
     * Check for DMA shutdown on receive channel
     */
    dmaStatus = IOGetDBDMAChannelStatus( ioBaseEnetRxDMA );
    if ( !(dmaStatus & kdbdmaActive) )
    {
#if 0
		IOLog( "Ethernet(BMac): Timeout check - RxHead = %d RxTail = %d\n", 
			rxCommandHead, rxCommandTail);

      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

		doFlushQueue = _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 (_transmitInterruptOccurred() == false)
				{
#if 0
					IOLog( "Ethernet(BMac): Timeout check - TxHead = %d TxTail = %d\n", 
						txCommandHead, txCommandTail);
#endif
					_restartTransmitter();
				}
				doService = true;
			}
		}
		else
		{
			txWDInterrupts   = 0;
			txWDCount        = 0;
		}
	}
    else
	{
		txWDInterrupts   = 0;
		txWDCount        = 0;
	}

    if ( phyId != 0xff )
    {
        if ( miiReadWord(&phyStatus, MII_STATUS, phyId) == true )
        {
            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 )
					{
						miiReadWord(&linkStatus, MII_ST10040_CHIPST, phyId);
	
						fullDuplex =  (linkStatus & MII_ST10040_CHIPST_DUPLEX) ? true : false;
	
						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 )
					{
						miiReadWord(&linkStatus, MII_DP83843_PHYSTS, phyId);
	
						fullDuplex =  (linkStatus & MII_DP83843_PHYSTS_DUPLEX) ? true : false;
	
						IOLog( "Ethernet(BMac): Link is up at %sMb - %s Duplex\n\r",
								(linkStatus & MII_DP83843_PHYSTS_SPEED10) ? "10" : "100",
								(fullDuplex)                              ? "Full" : "Half" );           
					}  
					else
					{
						fullDuplex = false ;
						IOLog( "Ethernet(BMac): Link is up\n\r" );
					}
	
					if ( fullDuplex != isFullDuplex )
					{							
							_setDuplexMode(fullDuplex);
					}
				}
                else
                {
                    IOLog( "Ethernet(BMac): Link is down.\n\r" );
                }
                phyStatusPrev = phyStatus;
            }
        }
    }

	// Clean-up after the debugger if the debugger was active.
	//
	if (debugTxPoll)
	{
		debugQueue->flush();
		debugTxPoll = false;
		releaseDebuggerLock();
		doService = true;
	}
	else
	{
		releaseDebuggerLock();
	}

	/*
	 * Submit all received packets queued up by _receiveInterruptOccurred()
	 * to the network stack. The up call is performed without holding the
	 * debugger lock.
	 */
	if (doFlushQueue)
	{
		networkInterface->flushInputQueue();
	}

	/*
	 * Make sure the output queue is not stalled.
	 */
	if (doService && netifClient)
	{
		transmitQueue->service();
	}

    /*
     * Restart the watchdog timer
     */
	timerSrc->setTimeoutMS(WATCHDOG_TIMER_MS);
}

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

const char * BMacEnet::getVendorString() const
{
	return ("Apple");
}

const char * BMacEnet::getModelString() const
{
	return ("BMac");
}

const char * BMacEnet::getRevisionString() const
{
	return ("");
}

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

IOReturn BMacEnet::setPromiscuousMode(IOEnetPromiscuousMode mode)
{
    u_int16_t		rxCFGVal;
    
	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 or reset promiscuous mode and restore receiver state
     */
	if (mode == kIOEnetPromiscuousModeOff) {
		rxCFGVal &= ~kRxPromiscEnable;
		isPromiscuous = false;
	}
	else {
		rxCFGVal |= kRxPromiscEnable;
		isPromiscuous = true;
	}

    WriteBigMacRegister( ioBaseEnet, kRXCFG, rxCFGVal );		

	releaseDebuggerLock();
    
    return kIOReturnSuccess;
}

IOReturn BMacEnet::setMulticastMode(IOEnetMulticastMode mode)
{
	multicastEnabled = (mode == kIOEnetMulticastModeOff) ? false : true;

	return kIOReturnSuccess;
}

IOReturn BMacEnet::setMulticastList(enet_addr_t *addrs, UInt count)
{
	reserveDebuggerLock();
	_resetHashTableMask();
	for (UInt i = 0; i < count; i++) {
		_addToHashTableMask(addrs->ea_byte);
		addrs++;
	}
	_updateBMacHashTableMask();
	releaseDebuggerLock();
	return kIOReturnSuccess;
}

/*
 * Kernel Debugger Support 
 */
void BMacEnet::sendPacket(void *pkt, unsigned int pkt_len)
{
	_sendPacket(pkt, pkt_len);
}

void BMacEnet::receivePacket(void *pkt, unsigned int *pkt_len,
	unsigned int timeout)
{
    _receivePacket(pkt, pkt_len, timeout);
}

/*
 * Allocate a WorkLoop serialized output queue object.
 */
IOOutputQueue * BMacEnet::allocateOutputQueue()
{
	return IOOQGateFIFOQueue::withTarget( this,
	                                      getWorkLoop(), 
	                                      TRANSMIT_QUEUE_SIZE );
}

#ifdef PM_SUPPORT
/*
 * Power management methods. 
 */
- (IOReturn)getPowerState:(PMPowerState *)state_p
{
    return kIOReturnUnsupported;
}

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

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

- (IOReturn)setPowerManagement:(PMPowerManagementState)state
{
    return kIOReturnUnsupported;
}
#endif PM_SUPPORT