Source to iokit/Families/IONetworking/IONetworkController.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) 1999 Apple Computer, Inc.  All rights reserved. 
 *
 * IONetworkController.cpp
 *
 * HISTORY
 * 9-Dec-1998       Joe Liu (jliu) created.
 *
 */

#include <IOKit/assert.h>
#include <IOKit/IOCommandGate.h>
#include <IOKit/network/IONetworkController.h>
#include <IOKit/network/IOOutputQueue.h>
#include <IOKit/network/IONetworkMedium.h>

// IONetworkController needs to know about mbufs, but it shall have no
// other dependencies on BSD networking.
//
extern "C" {
#include <sys/param.h>  // mbuf limits defined here.
#include <sys/mbuf.h>
//
// osfmk/kern/spl.h - Need splimp for mbuf macros.
//
typedef unsigned spl_t;
extern spl_t    (splimp)(void);
}

//-------------------------------------------------------------------------
// Macros.

#define super IOService

OSDefineMetaClass(         IONetworkController, IOService )
OSDefineAbstractStructors( IONetworkController, IOService )

// Define SYNC_REQ macro to simplify syncRequest() calls.
//
#define SYNC_REQ(sender, action, ret, args...)  \
        syncRequest(sender, this, (IONetworkAction) action, (UInt32 *) ret, \
                    ## args)

#define DO_SYNC_REQ(action, args...)                                 \
{                                                                    \
    IOReturn  ret = kIOReturnNotReady;                               \
    syncRequest(client, this,                                        \
                (RequestAction) &IONetworkController:: ## action,    \
                (UInt32 *) &ret, ## args);                           \
    return ret;                                                      \
} 

static bool isPowerOfTwo(UInt num)
{
    return (num == (num & ~(num - 1)));
}

#ifndef MAX
#define MAX(a, b)   (((a)>(b))?(a):(b))
#endif

#define MEDIUM_LOCK     IOTakeLock(_mediumLock);
#define MEDIUM_UNLOCK   IOUnlock(_mediumLock);

#ifdef  DEBUG
#define DLOG(fmt, args...)  IOLog(fmt, ## args)
#else
#define DLOG
#endif

// OSSymbols for frequently used keys.
//
static const OSSymbol * gIOActiveMediumKey;
static const OSSymbol * gIOCurrentMediumKey;

//---------------------------------------------------------------------------
// Class initializer for IONetworkController. Create OSSymbol objects
// ahead of time that are used as keys. This method is called explicitly
// by a line in IOStartIOKit.cpp and not by the OSDefineMetaClassAndInit()
// mechanism, since the OSSymbol class is not guaranteed to be initialized
// first, thus its OSSymbol pool may not be setup.

void IONetworkController::initialize()
{
    gIOActiveMediumKey  = OSSymbol::withCStringNoCopy(kIOActiveMedium); 
    gIOCurrentMediumKey = OSSymbol::withCStringNoCopy(kIOCurrentMedium);
    
    assert(gIOActiveMediumKey && gIOCurrentMediumKey);

    IONetworkData::initialize();
}

//---------------------------------------------------------------------------
// Initialize the IONetworkController instance. Instance variables are 
// initialized to their default values, and the init() method in superclass
// is called.
//
// properties: A property table.
//
// Returns true on success, false otherwise.

bool IONetworkController::init(OSDictionary * properties)
{
    // Initialize instance variables.
    //
    _workLoop           = 0;
    _cmdGate            = 0;
    _outputQueue        = 0;
    _clientSet          = 0;
    _reqClient          = 0;
    _requestEnabled     = true;
    _handleReady        = true;
    _mediumLock         = 0;
    _linkData           = 0;

    if (!super::init(properties))
    {
        DLOG("IONetworkController: super::init() failed\n");
        return false;
    }
    return true;
}

//-------------------------------------------------------------------------
// Called when the controller was matched to a provider and
// has been selected to start running. IONetworkController will allocate
// resources and gather the controller's properties. No I/O will be
// triggered until the subclass attaches a client object from its own
// start method. Subclasses must override this method and call
// super::start() at the beginning of its implementation. They should
// also check the return value to make sure their superclass was
// started successfully. The resources allocated
// by IONetworkController are:
//
// - An IOWorkLoop object.
// - An IOCommandGate object to handle synchronous requests.
// - An OSSet to track our clients.
// - An optional IOOutputQueue object for output queueing.
//
// Tasks that are usually performed by a typical network driver in start
// include:
//
// - Resource allocation
// - Hardware initialization
// - Allocation of IOEventSources and attaching them to an IOWorkLoop object.
// - Publishing a medium dictionary.
// - And finally, attaching an interface object when the driver is ready
//   to handle client requests.
//
// provider: The provider that the controller was matched
//           (and attached) to.
//
// Returns true on success, false otherwise.

bool IONetworkController::start(IOService * provider)
{
    // Most drivers will probably want to wait for BSD due to their
    // dependency on mbufs, which is not available until BSD is
    // initialized.
    //
    if ((getFeatureSet() & kIONetworkFeatureNoBSDWait) == 0)
        waitForService(resourceMatching( "IOBSD" ));

    // Start our superclass.
    //
    if (!super::start(provider))
        return false;

    // Create an OSSet to store our clients.
    //
    _clientSet = OSSet::withCapacity(2);
    if (!_clientSet)
        return false;

    // Initialize link status properties.
    //
    setMediumProperty(gIOActiveMediumKey,  0, &_activeMedium,  true);
    setMediumProperty(gIOCurrentMediumKey, 0, &_currentMedium, true);
    setLink32Property(kIOLinkStatus,       0, &_linkStatus,    true);
    setLink64Property(kIOLinkSpeed,        0, &_linkSpeed,     true);
            
    // Allocate a mutex lock to serialize access to the medium dictionary.
    //
    _mediumLock = IOLockAlloc();
    if (!_mediumLock)
        return false;
    IOLockInitWithState(_mediumLock, kIOLockStateUnlocked);

    // Allocate and initialize the default IOWorkLoop object.
    //
    _workLoop = IOWorkLoop::workLoop();
    if (!_workLoop)
    {
        DLOG("%s: IOWorkLoop allocation failed\n", getName());
        return false;
    }

    // Create a 'private' IOCommandGate object and attach it to
    // our workloop created above. This is used by the syncRequest()
    // function.
    //
    _cmdGate = IOCommandGate::commandGate(this);
    if (!_cmdGate ||
        (_workLoop->addEventSource(_cmdGate) != kIOReturnSuccess))
    {
        DLOG("%s: IOCommandGate initialization failed\n", getName());
        return false;
    }

    // Try to allocate an IOOutputQueue instance. This is optional and
    // _outputQueue may be 0.
    //
    _outputQueue = createOutputQueue();

    // Remove this when allocateOutputQueue is deleted.
    if (!_outputQueue) 
        _outputQueue = allocateOutputQueue();

    // Query the controller's mbuf buffer restrictions.
    //
    IOPacketBufferConstraints constraints;
    getPacketBufferConstraints(&constraints);
    if ((constraints.alignStart  > kIOPacketBufferAlign32) ||
        (constraints.alignLength > kIOPacketBufferAlign32) ||
        !isPowerOfTwo(constraints.alignStart) ||
        !isPowerOfTwo(constraints.alignLength))
    {
        IOLog("%s: Invalid alignment: start:%ld, length:%ld\n",
            getName(),
            constraints.alignStart,
            constraints.alignLength);
        return false;
    }

    // Make it easier to satisfy both constraints.
    //
    if (constraints.alignStart < constraints.alignLength)
        constraints.alignStart = constraints.alignLength;
    
    // Convert to alignment masks.
    //
    _alignStart  = (constraints.alignStart) ? constraints.alignStart - 1 : 0;
    _alignLength = (constraints.alignLength) ? constraints.alignLength - 1 : 0;
    _alignPadding = _alignStart + _alignLength;

    // Starts the power manager
    //
    PMinit();           // initialize superclass power management variables
    provider->joinPMtree(this); // attach into the power management hierarchy

    return true;
}

//---------------------------------------------------------------------------
// The opposite of start(). The controller has been instructed to stop running.
// This method should release resources and undo actions performed by start().
// Subclasses must override this method and call super::stop() at the end of 
// its implementation.
//
// provider: The provider that the controller was matched
//           (and attached) to.

void IONetworkController::stop(IOService * provider)
{
    // Free the memory allocated by PMInit:
    PMstop();

    super::stop(provider);
}   

//---------------------------------------------------------------------------
// Get the IOWorkLoop object created by IONetworkController.
// An IOWorkLoop object is created by the start() method. Drivers can call 
// getWorkLoop() to obtain a reference to the IOWorkLoop object, and attach
// their event sources, such as IOTimerEventSource or IOInterruptEventSource.
// See IOWorkLoop.
//
// Returns the IOWorkLoop object created by IONetworkController.

IOWorkLoop * IONetworkController::getWorkLoop() const
{
    return _workLoop;
}

//---------------------------------------------------------------------------
// Get the IOCommandGate object created by IONetworkController.
// An IOCommandGate is created and attached to an IOWorkLoop by the start().
// method This IOCommandGate object is used to handle client requests issued
// through the syncRequest() method. Subclasses that need an IOCommandGate 
// should try to use the object returned by this method, rather than creating
// a new instance.
// See IOCommandGate.
//
// Returns the IOCommandGate object created by IONetworkController.
    
IOCommandGate * IONetworkController::getCommandGate() const
{
    return _cmdGate;
}

//---------------------------------------------------------------------------
// Get the address of the method designated to handle output packets.
//
// Returns the address of the outputPacket() method.

IOOutputAction IONetworkController::getOutputHandler() const
{
    return (IOOutputAction) &IONetworkController::outputPacket;
}

//---------------------------------------------------------------------------
// Create a new interface client object and attach it
// to the controller. The createInterface() method is called to
// perform the allocation and initialization, followed by a call to 
// configureInterface() to configure the interface. Both of these
// methods can be overridden by subclasses to customize the
// interface client attached. Drivers must call attachInterface()
// from its start() method, after it is ready to service client requests.
//
// interfaceP: If successful (return value is true), then the interface
//             object will be written to the handle provided.
//
// doRegister: If true, then registerService() is called to register
//             the interface, which will trigger the matching process,
//             and cause the interface to become registered with the network
//             layer. For drivers that wish to delay the registration, and
//             hold off servicing requests and data packets from the network
//             layer, set doRegister to false and call registerService() on
//             the interface client when the controller becomes ready.
//
// Returns true on success, false otherwise.

bool
IONetworkController::attachInterface(IONetworkInterface ** interfaceP,
                                     bool  doRegister = true)
{
    IONetworkInterface * netif;
    bool                 initOk = false;

    *interfaceP = 0;

    // We delay some initialization until the first time that
    // attachInterface() is called by the subclass.
    //
    SYNC_REQ(this, &IONetworkController::_controllerIsReady, &initOk);
    if (!initOk)
        return false;

    do {
        // Allocate a concrete subclass of IONetworkInterface
        // by calling createInterface().
        //
        netif = createInterface();
        if (!netif)
            break;

        // Configure the interface instance by calling 
        // configureInterface(), then attach it as our client.
        //
        if (!configureInterface(netif) ||
            !configureNetworkInterface(netif) ||
            !netif->attach(this))
        {
            netif->release();
            break;
        }

        *interfaceP = netif;

        // Register the interface nub. Spawns a matching thread.
        //
        if (doRegister)
            netif->registerService();

        return true;
    }
    while (0);

    return false;   // failed
}

//---------------------------------------------------------------------------
// Detach the interface object. This method will check that
// the object provided is indeed an IONetworkInterface, and if so its 
// terminate() method is called. The interface object will close and detach 
// from its controller only after the network layer has removed all references
// to the data structures exposed by the interface.
//
// interface: An interface object to be detached.
// sync:      If true, the interface is terminated synchronously.
//            Note that this may cause detachInterface() to block
//            for an indefinite period of time.

void
IONetworkController::detachInterface(IONetworkInterface * interface,
                                     bool                 sync = false)
{
    IOOptionBits options = kIOServiceRequired;

    if (OSDynamicCast(IONetworkInterface, interface) == 0)
        return;

    if (sync)
        options |= kIOServiceSynchronous;

    interface->terminate(options);
}

//---------------------------------------------------------------------------
// This method is called the first time that a controller driver calls 
// attachInterface() or attachDebuggerClient(), which is an indication that
// the driver has been started and is ready to service client requests.
// IONetworkController  uses this method to complete its initialization
// before any client objects are attached.
//
// provider: The controller's provider.
//
// Returns true on success, false otherwise.

bool IONetworkController::ready(IOService * provider)
{
    return publishCapabilities();
}

//---------------------------------------------------------------------------
// Called when the controller is ready to handle client requests.
// Returns true to indicate success, false otherwise.

bool IONetworkController::_controllerIsReady()
{
    if (!_handleReady)
        return true;

    if (!ready(getProvider()))    // Call ready().
        return false;

    _handleReady = false;
    return true;
}

//---------------------------------------------------------------------------
// Handle a client open on the controller object. IOService calls this method 
// with the arbitration lock held. Subclasses are not expected to override
// this method.
//
//   client: The client that is trying to open the controller.
//  options: See IOService.
// argument: See IOService.
//
// Returns true to accept the client open, false to refuse the open.

bool IONetworkController::handleOpen(IOService *  client,
                                     IOOptionBits options,
                                     void *       argument)
{
    bool ok;

    assert(client);

    ok = _clientSet->setObject(client);
    if (ok && OSDynamicCast(IOKernelDebugger, client))
    {
        ok = (doEnable(client) == kIOReturnSuccess);
        if (!ok)
            _clientSet->removeObject(client);
    }
    return ok;
}

//---------------------------------------------------------------------------
// Handle a close from one of our client objects. IOService calls this method
// with the arbitration lock held. Subclasses are not expected to override this 
// method.
//
//  client: The client that has closed the controller.
// options: See IOService.

void IONetworkController::handleClose(IOService * client, IOOptionBits options)
{
    _clientSet->removeObject(client);

    if (OSDynamicCast(IOKernelDebugger, client))
        doDisable(client);
}

//---------------------------------------------------------------------------
// This method is always called by IOService with the arbitration lock held. 
// Subclasses should not override this method.
//
// Returns true if the specified client, or any client if none is
// specified, presently has an open on this object.

bool IONetworkController::handleIsOpen(const IOService * client) const
{
    if (client)
        return _clientSet->containsObject(client);
    else
        return (_clientSet->getCount() > 0);
}

//---------------------------------------------------------------------------
// Free the IONetworkController instance and all allocated resources,
// then call super::free().

void IONetworkController::free()
{
    if (_outputQueue)
        _outputQueue->release();

    if (_cmdGate)
        _cmdGate->release();

    if (_workLoop)
        _workLoop->release();

    if (_clientSet)
    {
        // We should have no clients at this point. If we do,
        // then something is very wrong! It means that a client
        // has an open on us, and yet we are going away.
        //
        assert(_clientSet->getCount() == 0);
        _clientSet->release();
    }

    if (_mediumLock)
        IOLockFree(_mediumLock);

    super::free();
}

//---------------------------------------------------------------------------
// Handle an enable request from a client. The client's class
// is typecasted using OSDynamicCast, and if the client is an
// IOKernelDebugger or an IONetworkInterface, an overloaded enable
// method that takes a more specific argument is called. If the client
// matches neither type, a kIOReturnBadArgument is returned.
// A driver has the option of override this generic enable method,
// or the derived version.
//
// client: The client object requesting the enable.
//
// The return value from the derived enable method, or
// kIOReturnBadArgument if the client's type is unknown.

IOReturn IONetworkController::enable(IOService * client)
{
    if (OSDynamicCast(IONetworkInterface, client))
        return enable((IONetworkInterface *) client);
    
    if (OSDynamicCast(IOKernelDebugger, client))
        return enable((IOKernelDebugger *) client);

    IOLog("%s::%s Unknown client type\n", getName(), __FUNCTION__);
    return kIOReturnBadArgument;
}

//---------------------------------------------------------------------------
// Handle an enable request from a client. The client
// object is typecasted using OSDynamicCast, and if the client is an
// IOKernelDebugger or an IONetworkInterface, then an overloaded disable
// method that takes a more specific argument is called. If the client
// matches neither type, a kIOReturnBadArgument is returned.
// A driver has the option of override this generic enable method,
// or the derived version.
//
// client: The client object requesting the disable.
//
// The return from the derived disable method, or
// kIOReturnBadArgument if the client's type is unknown

IOReturn IONetworkController::disable(IOService * client)
{
    if (OSDynamicCast(IONetworkInterface, client))
        return disable((IONetworkInterface *) client);
    
    if (OSDynamicCast(IOKernelDebugger, client))
        return disable((IOKernelDebugger *) client);

    IOLog("%s::%s Unknown client type\n", getName(), __FUNCTION__);
    return kIOReturnBadArgument;
}

//---------------------------------------------------------------------------
// Called by an interface client to enable the controller.
// This method must bring up the hardware and enable event sources
// to prepare for packet transmission and reception. A driver should
// delay the allocation of most runtime resources until this method is
// called to conserve shared system resources.
//
// interface: The interface object that requested the enable.
//
// Returns kIOReturnUnsupported. Driver may override this method and
// return kIOReturnSuccess on success, or an error code otherwise.

IOReturn IONetworkController::enable(IONetworkInterface * interface)
{
    IOLog("IONetworkController::%s called\n", __FUNCTION__);
    return kIOReturnUnsupported;
}

//---------------------------------------------------------------------------
// Called by an interface object to disable the controller.
// This method should quiesce the hardware and disable event sources.
// Any resources allocated in enable() should also be deallocated.
//
// interface: The interface object that requested the disable.
//
// Returns kIOReturnUnsupported. Driver may override this method and
// return kIOReturnSuccess on success, or an error code otherwise.

IOReturn IONetworkController::disable(IONetworkInterface * interface)
{
    IOLog("IONetworkController::%s called\n", __FUNCTION__);
    return kIOReturnUnsupported;
}

//---------------------------------------------------------------------------
// With the various overloaded forms of enable() and disable(), what does it
// mean when one does &IONetworkController::enable, to get the address of the 
// member function?
// Instead, these private functions are registered which then calls the
// correct functions.

IOReturn IONetworkController::_enable(IOService * client)
{
    return enable(client);
};

IOReturn IONetworkController::_disable(IOService * client)
{
    return disable(client);
};

//---------------------------------------------------------------------------
// Discover and publish controller capabilities to the property table.
// This method is called by ready().
//
// Returns true if all capabilities were discovered and published
// successfully, false otherwise. Returning false will prevent client
// objects from attaching to the controller since a property that
// a client depends on may be missing

bool IONetworkController::publishCapabilities()
{
    bool        ret = true;
    OSString *  string;
    UInt32      index;

    string = OSString::withCString(getVendorString());
    if (string) {
        ret = setProperty(kIOVendor, string) && ret;
        string->release();
    }

    string = OSString::withCString(getModelString());
    if (string) {
        ret = setProperty(kIOModel, string) && ret;
        string->release();
    }

    string = OSString::withCString(getRevisionString());
    if (string) {
        ret = setProperty(kIORevision, string) && ret;
        string->release();
    }

    string = OSString::withCString(getInfoString());
    if (string) {
        ret = setProperty(kIOInfo, string) && ret;
        string->release();
    }

    // Set OSNumber properties.
    //  
    ret = (getControllerIndex(&index) == kIOReturnSuccess) && ret;
    ret = setProperty(kIOControllerIndex,  index, 32) && ret;
    ret = setProperty(kIOFeatureSet,       getFeatureSet(), 32) && ret;
    ret = setProperty(kIOFamilyFeatureSet, getFamilyFeatureSet(), 32) && ret;

    if (getPacketFilters(&index) == kIOReturnSuccess) {
        ret = setProperty(kIOPacketFilters, index, 32) && ret; 
    }
    else {
        ret = false;
    }

    if (!ret)
        DLOG("IONetworkController::%s error\n", __FUNCTION__);

    return ret;
}

//---------------------------------------------------------------------------
// Broadcast a network event to all attached interface objects.
// This is equivalent to calling the IONetworkInterface::inputEvent()
// function for each interface client.
//
// IONetworkController uses this method to broadcast link and media
// events. Drivers will usually call the inputEvent() method directly
// since it is more efficient, and most drivers will only attach a
// single interface client.
//
// type: Event type.
//  arg: Event argument.
//
// Returns true if the event was delivered, false if an error occurred
// (unable to perform object allocation) and the event was not delivered

bool IONetworkController::broadcastEvent(UInt32 type, void * arg = 0)
{
    IONetworkInterface *   netif;
    OSCollectionIterator * iter = 0;
    OSSet *                clientSet;

    lockForArbitration();   // locks open/close/state changes.
    
    if (_clientSet->getCount()) {
        clientSet = OSSet::withSet(_clientSet, _clientSet->getCount());
        if (clientSet) {
            iter = OSCollectionIterator::withCollection(clientSet);
            clientSet->release();
        }
    }
    
    unlockForArbitration();

    if (!iter)
        return false;

    // Send the event to all attached interface objects.
    //
    while ((netif = (IONetworkInterface *) iter->getNextObject())) {
        if (OSDynamicCast(IONetworkInterface, netif) == 0)
            continue;   // only send events to IONetworkInterface subclasses.
        netif->inputEvent(type, arg);
    }

    iter->release();

    return true;
}

//---------------------------------------------------------------------------
// A client request for the controller to switch to a new MTU size.
//
// mtu: The new MTU size requested.
//
// Returns kIOReturnUnsupported. Drivers may override this method
// and return either kIOReturnSuccess to indicate that the new MTU size
// was accepted and is in effect, or an error to indicate failure.

IOReturn IONetworkController::setMaxTransferUnit(UInt32 mtu)
{
    return kIOReturnUnsupported;
}

//---------------------------------------------------------------------------
// A client request for the driver to perform hardware 
// diagnostics and return the test result after completion.
//
// resultCodeP: In addition to the return code, drivers may return
//               a hardware specific failure code.
//
// Return kIOReturnSuccess if the hardware passed all test, or an
// appropriate error code otherwise. The default return is always
// kIOReturnUnsupported.

IOReturn IONetworkController::performDiagnostics(UInt32 * resultCodeP)
{
    return kIOReturnUnsupported;
}

//---------------------------------------------------------------------------
// Transmit a packet mbuf. If an IOOutputQueue was allocated and returned by 
// createOutputQueue(), then this method will be called by the queue object.
// Otherwise, an interface object will call this method directly upon 
// receiving an output packet from the network layer. When a queue object
// is not present, this method must be safe from multiple client threads,
// each pushing a packet to be sent on the wire.
//
// There is no upper limit on the number of mbufs, hence the number of
// memory fragments, in the mbuf chain provided. Drivers must be able to
// handle cases when the chain might exceed the limit supported by their
// DMA engines, and perform coalescing to copy the various memory fragments
// into a lesser number of fragments. This complexity can be hidden from
// a driver when an IOMBufMemoryCursor is used, which is able to convert
// a mbuf chain into a physical address scatter-gather list that will not
// exceed a specified number of physically contiguous memory segments.
// See IOMBufMemoryCursor.
//
// Packets may also be chained to form a packet chain. Although the
// network layer, through the interface object, will currently only
// send a single mbuf packet to the controller for each outputPacket()
// call, it is possible for this to change. When a queue object is used,
// the queue will automatically accept a single packet or a packet chain,
// but it will call outputPacket() for each packet removed from the queue.
//
// The implementation in IONetworkController performs no useful action
// and will drop all packets. A driver must always override this method.
//
// m: The packet mbuf.
//
// Returns a return code defined by the caller.

UInt32 IONetworkController::outputPacket(struct mbuf * m)
{
    // The implementation here is simply a sink-hole, all packets are
    // dropped.
    //
    if (m) freePacket(m);
    return 0;
}

//---------------------------------------------------------------------------
// A client request to adjust the capacity of the driver's output queue
// (number of packets the queue can hold). If a driver does not override
// this method, then the default action is to forward the request to
// an IOOutputQueue object if it was created. Otherwise return
// kIOReturnUnsupported.
//
// capacity: The new capacity of the output queue.
//
// Returns kIOReturnSuccess on success, kIOReturnBadArgument if the
// specified capacity is invalid, or kIOReturnUnsupported if the
// function is not supported.

IOReturn IONetworkController::setOutputQueueCapacity(UInt32 capacity)
{
    if (_outputQueue)
        return _outputQueue->setCapacity(capacity) ?
               kIOReturnSuccess : kIOReturnBadArgument;
    else
        return kIOReturnUnsupported;
}

//---------------------------------------------------------------------------
// A client request to get the capacity of the output queue. If a driver
// does not override this method, then the default action is to forward the
// request to an IOOutputQueue object if it was created. Otherwise return
// kIOReturnUnsupported.
//
// capacityP: Address of an integer where the capacity
//            shall be written to.
//
// Returns kIOReturnSuccess on success, or kIOReturnUnsupported if an
// IOOutputQueue object was not created.

IOReturn IONetworkController::getOutputQueueCapacity(UInt32 * capacityP) const
{
    if (_outputQueue) {
        *capacityP = _outputQueue->getCapacity();
        return kIOReturnSuccess;
    }
    return kIOReturnUnsupported;
}

//---------------------------------------------------------------------------
// A client request to fetch the number of packets currently held by the
// queue. If a driver does not override this method, then the default action
// is to forward the request to an IOOutputQueue object if it was created. 
// Otherwise return kIOReturnUnsupported.
//
// sizeP: Address of an integer where the size shall be written to.
//
// Returns kIOReturnSuccess on success, or kIOReturnUnsupported if an
// IOOutputQueue object was not created.

IOReturn IONetworkController::getOutputQueueSize(UInt32 * sizeP) const
{
    if (_outputQueue) {
        *sizeP = _outputQueue->getSize();
        return kIOReturnSuccess;
    }
    return kIOReturnUnsupported;
}

//---------------------------------------------------------------------------
// A client request to flush all packets currently held by the queue,
// and return the number of packets discarded. If a driver does not
// override this method, then the default action is to forward the
// request to an IOOutputQueue object if it was created. 
// Otherwise return kIOReturnUnsupported.
//
// flushCountP: Address of an integer where the number of packets
//              discarded shall be written to.
//
// Returns kIOReturnSuccess on success, or kIOReturnUnsupported if an
// IOOutputQueue object was not created.

IOReturn IONetworkController::flushOutputQueue(UInt32 * flushCountP)
{
    if (_outputQueue) {
        *flushCountP = _outputQueue->flush();
        return kIOReturnSuccess;
    }
    return kIOReturnUnsupported;
}

//---------------------------------------------------------------------------
// Report features supported by the controller.
//
// Returns 0. Drivers may override this method and return a mask
// containing all supported feature bits.

UInt32 IONetworkController::getFeatureSet() const
{
    return 0;
}

//---------------------------------------------------------------------------
// Return a string describing the revision level of the controller.

const char * IONetworkController::getRevisionString() const
{
    return 0;
}

//---------------------------------------------------------------------------
// Return a string containing any additional information about the controller
// and/or driver.

const char * IONetworkController::getInfoString() const
{
    return 0;
}

//---------------------------------------------------------------------------
// Return an ordinal number for multiport network adapters.
// The implementation in IONetworkController will work for PCI controllers
// behind a PCI-PCI bridge. This method exists solely to support the
// current interface naming scheme, and is likely to
// undergo changes or may disappear in the future.
//
// indexP: The oridinal number should be written to the
//         integer at this address.
//
// Returns kIOReturnSuccess.

IOReturn IONetworkController::getControllerIndex(UInt32 * index) const
{
    IOService * provider = getProvider();

    *index = 0;

    if (provider) {
        OSNumber * offset = OSDynamicCast(OSNumber,
                            provider->getProperty("IOChildIndex"));
        if (offset)
            *index = offset->unsigned32BitValue();
    }
    return kIOReturnSuccess;
}

//---------------------------------------------------------------------------
// Encodes a request received by syncRequest. This command structure is then
// submitted to commandHandler() through the IOCommandGate::runAction().

struct cmdStruct {
    OSObject *                          client;
    OSObject *                          target;
    IONetworkController::RequestAction  action;
    void *                              arg0;
    void *                              arg1;
    void *                              arg2;
    void *                              arg3;
};

//---------------------------------------------------------------------------
// Execute the request received by syncRequest() and encoded onto
// a cmdStruct structure. This method is called by IOCommandGate's
// runAction() method.

void IONetworkController::commandHandler(void * arg0,
                                         void * arg1,
                                         void * arg2,
                                         void * /*arg3*/)
{
    cmdStruct * cmd       = (cmdStruct *) arg0;  // cmdStruct
    UInt32 *    actRetP   = (UInt32 *)    arg1;  // action return
    IOReturn *  cmdRetP   = (IOReturn *)  arg2;  // commandHandler return
    OSObject *  oldClient = _reqClient;
    bool        accept    = true;

    assert(cmd && actRetP && cmdRetP);

    if (cmd->client != this)
    {
        // Filter the client request.

        accept = syncRequestFilter(cmd->client,
                                   cmd->target,
                                   cmd->action,
                                   cmd->arg0,
                                   cmd->arg1,
                                   cmd->arg2,
                                   cmd->arg3);
    }

    if (accept) {
        _reqClient = cmd->client;

        *actRetP = ((cmd->target)->*(cmd->action))(cmd->arg0,
                                                   cmd->arg1,
                                                   cmd->arg2,
                                                   cmd->arg3);

        _reqClient = oldClient;

        *cmdRetP = kIOReturnSuccess;
    }
    else {
        // actRetP is not set if the request was rejected by the filter.
        *cmdRetP = kIOReturnNotReady;
    }
}

//---------------------------------------------------------------------------
// Perform a request action synchronously. Used both internally and also by
// clients to execute an arbitrary request action using the IOCommandGate's 
// runAction() method. For client requests, where the client field is not
// equal to 'this', the request is filtered by calling syncRequestFilter()
// to qualify the client request. This filter function must return true
// in order for the request to be accepted and the request action called.
//
// client: The client (caller) of the synchronous request.
// target: The target object that implements the request action.
// action: The action to perform.
// ret:    The return value from the action is written to the
//         integer with the provided address. The result is
//         not written if the request was rejected by the request filter.
// arg0:   Optional action argument.
// arg1:   Optional action argument.
// arg2:   Optional action argument.
// arg3:   Optional action argument.
//
// kIOReturnNotReady if the client request was rejected by the filter.
// Otherwise kIOReturnSuccess is returned.

IOReturn IONetworkController::syncRequest(OSObject *     sender,
                                          OSObject *     target,
                                          RequestAction  action,
                                          UInt32 *       ret  = 0,
                                          void *         arg0 = 0,
                                          void *         arg1 = 0,
                                          void *         arg2 = 0,
                                          void *         arg3 = 0)
{
    cmdStruct cmd;
    IOReturn  rc;
    UInt32    ra;

    // bzero(&cmd, sizeof(cmd));

    cmd.client  = sender;   // request client.
    cmd.target  = target;   // target object.
    cmd.action  = action;   // target action.
    cmd.arg0    = arg0;     // action arguments.
    cmd.arg1    = arg1;
    cmd.arg2    = arg2;
    cmd.arg3    = arg3;

    _cmdGate->runAction( (IOCommandGate::Action)
                         &IONetworkController::commandHandler,     /* Action */
                         (void *) &cmd,                 /* arg0 -  cmdStruct */
                         (void *) &ra,               /* arg1 - action return */
                         (void *) &rc );    /* arg2 -  commandHandler return */

    if ((rc == kIOReturnSuccess) && ret)
        *ret = ra;

    return rc;
}

//---------------------------------------------------------------------------
// This method is called to qualify all client requests
// sent to syncRequest(). This implementation will either allow or
// refuse all requests, and this behavior is set by calling the 
// enableSyncRequest() or disableSyncRequest() methods. By default,
// all requests are allowed.
//
//  client: The client of the synchronous request.
//  target: The target object that implements the request action.
//  action: The action to perform.
//  arg0:   Action argument.
//  arg1:   Action argument.
//  arg2:   Action argument.
//  arg3:   Action argument.
//
// Returns true to accept the request and allow the request action to be
// called, or false to refuse it.

bool IONetworkController::syncRequestFilter(OSObject *     client,
                                            OSObject *     target,
                                            RequestAction  action,
                                            void *         arg0,
                                            void *         arg1,
                                            void *         arg2,
                                            void *         arg3)
{
    // If _requestEnabled ifs true, allow the request, otherwise refuse it.

    return _requestEnabled;
}

//---------------------------------------------------------------------------
// A static member function to control the default syncRequestFilter()
// filter function.

void  IONetworkController::_enableSyncRequestFilter(IONetworkController * ctlr,
                                                    bool enabled)
{
    ctlr->_requestEnabled = enabled;
}

//---------------------------------------------------------------------------
// Enable all client requests sent to the syncRequest() method.
// Don't use this method if the driver overrides syncRequestFilter().

void IONetworkController::enableSyncRequest()
{
    _cmdGate->runAction( (IOCommandGate::Action)
                         &IONetworkController::_enableSyncRequestFilter,
                         (void *) true );
}

//---------------------------------------------------------------------------
// Disable all client requests sent to the syncRequest() method.
// Don't use this method if the driver overrides syncRequestFilter().

void IONetworkController::disableSyncRequest()
{
    _cmdGate->runAction( (IOCommandGate::Action)
                         &IONetworkController::_enableSyncRequestFilter,
                         (void *) false );
}

//---------------------------------------------------------------------------
// Get request client. Methods that are called by syncRequest() can call this
// to get the client object which initiated the request.
//
// Returns the request client. If the caller's context does not indicate
// that it is running through syncRequest(), then 0 is returned.

OSObject * IONetworkController::getSyncRequestClient() const
{
    return ( _workLoop->inGate() ? _reqClient : 0 );
}

//---------------------------------------------------------------------------
// Configure an interface object created through
// createInterface(). IONetworkController will register
// its output handler with the interface object provided.
// Subclasses may override this method to customize the interface object.
// Once the interface is registered and opened by a client, it will
// refuse changes to its properties. And since this method is called
// before the interface has become registered, this is a final
// opportunity for the controller to configure the interface.
//
// interface: The interface object to be configured.
//
// Returns true if configuration was successful, false otherwise (this
// will cause attachInterface() to fail).

bool IONetworkController::configureInterface(IONetworkInterface * interface)
{
    IOOutputAction  handler;
    OSObject *      target;
    bool            ret;
    IONetworkData * stats;

    if (!OSDynamicCast(IONetworkInterface, interface))
        return false;

    IOOutputQueue * outQueue = getOutputQueue();

    // Must register an output handler with the interface object.
    // The interface will send output packets, and requests (commands)
    // to its registered output handler. If we allocated an output
    // queue, then we register the queue as the output handler,
    // otherwise, we become the output handler.

    if (outQueue)
    {
        target  = outQueue;
        handler = outQueue->getOutputHandler();
    
        stats   = outQueue->getStatisticsData();
        interface->addNetworkData(stats);
    }
    else
    {
        target  = this;
        handler = getOutputHandler();
    }
    ret = interface->registerOutputHandler(target, handler);

    return ret;
}

bool
IONetworkController::configureNetworkInterface(IONetworkInterface * interface)
{
    return true;
}

//---------------------------------------------------------------------------
// Called by start() to create an optional IOOutputQueue instance to handle
// output queueing. The default implementation will always return 0, hence
// no output queue will be created. A driver may override this method and 
// return a subclass of IOOutputQueue. IONetworkController will keep a 
// reference to the queue created, and will release the object when 
// IONetworkController is freed. Also see getOutputQueue().
//
// Returns a newly allocated and initialized IOOutputQueue instance.

IOOutputQueue * IONetworkController::createOutputQueue()
{
    return 0;
}

IOOutputQueue * IONetworkController::allocateOutputQueue()
{
    return 0;
}

//---------------------------------------------------------------------------
// Return the output queue allocated though createOutputQueue().

IOOutputQueue * IONetworkController::getOutputQueue() const
{
    return _outputQueue;
}

//---------------------------------------------------------------------------
// Called by start() to obtain the constraints on the memory buffer
// associated with each mbuf allocated through allocatePacket().
// Drivers can override this method to specify their buffer constraints
// imposed by their bus master hardware. Note that outbound packets,
// those that originate from the network stack, are not subject
// to the constraints reported here.
//
// constraintsP: A pointer to an IOPacketBufferConstraints structure
//               that that this method is expected to initialize.
//               See IOPacketBufferConstraints structure definition.

void IONetworkController::getPacketBufferConstraints(
                          IOPacketBufferConstraints * constraintsP) const
{
    assert(constraintsP);
    constraintsP->alignStart  = kIOPacketBufferAlign1;
    constraintsP->alignLength = kIOPacketBufferAlign1;
}

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

static inline UInt IO_ALIGN_MBUF(
    struct mbuf * m,
    UInt          size,
    UInt          alignStart,
    UInt          alignLength)
{
    assert(m);
    UInt not_aligned = mtod(m, UInt) & alignStart;
    
    // Align starting address.
    //
    if (not_aligned) {
        not_aligned = alignStart - not_aligned + 1;
        m->m_data += not_aligned;
        size      -= not_aligned;
    }
    
    // Align buffer length.
    //
    if ((not_aligned = size & alignLength))
        size -= not_aligned;

    return size;
}

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

struct mbuf * IONetworkController::allocatePacket(UInt size)
{
    struct mbuf * head = 0;
    struct mbuf * tail = 0;

    while (size) {
        UInt mbufSize;
        struct mbuf * m;
    
        // Allocate a mbuf, for the initial mbuf segment, allocate a
        // mbuf header, otherwise a 'normal' mbuf will do.
        
        if (head) {
            MGET(m, M_DONTWAIT, MT_DATA);
            mbufSize = MLEN;
        }
        else {
            MGETHDR(m, M_DONTWAIT, MT_DATA);
            mbufSize = MHLEN;
        }

        if (!m) goto error;

        // Append the new mbuf to our chain.
        
        IO_APPEND_MBUF(head, tail, m);

        // If the remaining size exceed the buffer size of a normal mbuf,
        // then attach a cluster to our mbuf. Currently, the cluster size
        // is fixed, and the allocated memory is always MCLBYTES in size.

        if ((size + _alignPadding) > mbufSize) {
            MCLGET(m, M_DONTWAIT);
            if ((m->m_flags & M_EXT) == 0) goto error;
            mbufSize = MCLBYTES;
        }
        
        // mbufSize shall contain the (reduced) capacity of the mbuf after 
        // alignment.
        //
        mbufSize = IO_ALIGN_MBUF(m, mbufSize, _alignStart, _alignLength);

        if (mbufSize > size) {
            // If we wanted to force all mbufs in the chain to be aligned,
            // including the last one, then do the following.
            //
            // mbufSize -= (mbufSize - size) & ~_alignLength;

            mbufSize = size;
            size = 0;
        }
        else
            size -= mbufSize;           // decrement the remaining size         

        m->m_len = mbufSize;            // update the length for this mbuf
        head->m_pkthdr.len += mbufSize; // increment the total length
    }

    return head;

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

//---------------------------------------------------------------------------
// Release the mbuf back to the free pool.

void IONetworkController::freePacket(struct mbuf * m)
{
    assert(m);
    while (m) m = m_free(m);
}

static inline bool IO_COPY_MBUF(
    const struct mbuf * src,
    struct mbuf *       dst,
    int                 length)
{
    caddr_t src_dat, dst_dat;
    int dst_len, src_len;

    assert(src && dst);

    dst_len = dst->m_len;
    dst_dat = dst->m_data;

    while (src) {

        src_len = src->m_len;
        src_dat = src->m_data;

        if (src_len > length)
            src_len = length;

        while (src_len) {
        
            if (dst_len >= src_len) {
                // copy entire src mbuf to dst mbuf.

                bcopy(src_dat, dst_dat, src_len);               
                length -= src_len;
                dst_len -= src_len;
                dst_dat += src_len;
                src_len = 0;
            }
            else {
                // fill up dst mbuf with some portion of the data in
                // the src mbuf.

                bcopy(src_dat, dst_dat, dst_len);       // dst_len = 0?             
                length -= dst_len;
                dst_len = 0;
                src_len -= dst_len;         
            }
            
            // Go to the next destination mbuf segment.
            
            if (dst_len == 0) {
                if (!(dst = dst->m_next))
                    return (length == 0);
                dst_len = dst->m_len;
                dst_dat = dst->m_data;
            }

        } /* while (src_len) */

        src = src->m_next;

    } /* while (src) */
    
    return (length == 0);   // returns true on success.
}

//---------------------------------------------------------------------------
// Replace the mbuf pointed by the given pointer with another mbuf.
// Drivers can call this method to replace a mbuf before passing the
// original mbuf, which contains a received frame, to the network layer.
//
// mp:   A pointer to the original mbuf that shall be updated by this
//       method to point to the new mbuf.
// size: If size is 0, then the new mbuf shall have the same size
//       as the original mbuf that is being replaced. Otherwise, the new
//       mbuf shall have the size specified here.
//
// If mbuf allocation was successful, then the replacement will
// take place and the original mbuf will be returned. Otherwise,
// a NULL is returned.

struct mbuf * IONetworkController::replacePacket(struct mbuf ** mp,
                                                 UInt size = 0)
{   
    assert((mp != NULL) && (*mp != NULL));

    struct mbuf * m = *mp;

    // If size is zero, then size is taken from the source mbuf.
    //  
    if (size == 0)
        size = m->m_pkthdr.len;
    
    // Allocate a new packet to replace the current packet.
    //
    if (!(*mp = allocatePacket(size)))
    {
        *mp = m; m = 0;
    }

    return m;
}

//---------------------------------------------------------------------------
// Make a copy of a mbuf, and return the copy. The source mbuf is not modified.
//
// m:    The source mbuf.
// size: The number of bytes to copy. If set to 0, then the entire
//       source mbuf is copied.
//
// Returns a new mbuf created from the source packet.

struct mbuf * IONetworkController::copyPacket(const struct mbuf * m,
                                              UInt size = 0)
{
    struct mbuf * mn;

    assert(m != NULL);

    // If size is zero, then size is taken from the source mbuf.
    //  
    if (size == 0)
        size = m->m_pkthdr.len;

    // Copy the current mbuf to the new mbuf, and return the new mbuf.
    // The input mbuf is left intact.
    //
    if (!(mn = allocatePacket(size)))
        return 0;

    if (!IO_COPY_MBUF(m, mn, size))
    {
        IOLog("IONetworkController: copyPacket failure\n");
        freePacket(mn);
        mn = 0;
    }

    return mn;
}

//---------------------------------------------------------------------------
// Either replace or copy the source mbuf given depending on the amount of
// data in the source mbuf. This method will either perform a copy or replace 
// the source mbuf, whichever is more time efficient. If replaced, then the
// original mbuf is returned, and a new mbuf is allocated to take its place.
// If copied, the source mbuf is left intact, while a copy is returned that
// is just big enough to hold all the data from the source mbuf.
//
// mp:        A pointer to the source mbuf that may be updated by this
//            method to point to the new mbuf if replaced.
// rcvlen:    The number of data bytes in the source mbuf.
// replacedP: Pointer to a bool that is set to true if the
//            source mbuf was replaced, or set to false if the
//            source mbuf was copied.
//
// Returns a replacement or a copy of the source mbuf, 0 if mbuf
// allocation failed.

struct mbuf * IONetworkController::replaceOrCopyPacket(struct mbuf ** mp,
                                                       UInt   rcvlen,
                                                       bool * replacedP)
{
    struct mbuf * m;

    if ((rcvlen + _alignPadding) > MHLEN)
    {
        // Large packet, it is more efficient to allocate a new mbuf
        // to replace the original mbuf than to make a copy. The new
        // packet shall have the same size as the original mbuf being
        // replaced.
        // 
        m = replacePacket(mp);
        *replacedP = true;
    }
    else {
        // The copy will fit within a header mbuf. Fine, make a copy
        // of the original mbuf instead of replacing it. We only copy
        // the rcvlen bytes, not the entire source mbuf.
        //
        assert((mp != NULL) && (*mp != NULL));
        m = copyPacket(*mp, rcvlen);        
        *replacedP = false;
    }

    return m;
}

//---------------------------------------------------------------------------
// Used for debugging only. Log the mbuf header.

void IONetworkController::_logMbuf(struct mbuf * m)
{
    if (!m) {
        IOLog("logMbuf: NULL mbuf\n");
        return;
    }
    
    while (m) {
        IOLog("m_next   : %08x\n", (UInt) m->m_next);
        IOLog("m_nextpkt: %08x\n", (UInt) m->m_nextpkt);
        IOLog("m_len    : %d\n",   (UInt) m->m_len);
        IOLog("m_data   : %08x\n", (UInt) m->m_data);
        IOLog("m_type   : %08x\n", (UInt) m->m_type);
        IOLog("m_flags  : %08x\n", (UInt) m->m_flags);
        
        if (m->m_flags & M_PKTHDR)
            IOLog("m_pkthdr.len  : %d\n", (UInt) m->m_pkthdr.len);

        if (m->m_flags & M_EXT) {
            IOLog("m_ext.ext_buf : %08x\n", (UInt) m->m_ext.ext_buf);
            IOLog("m_ext.ext_size: %d\n", (UInt) m->m_ext.ext_size);
        }
        
        m = m->m_next;
    }
    IOLog("\n");
}

//---------------------------------------------------------------------------
// Allocate and attache a new IOKernelDebugger client object.
//
// debuggerP: An IOKernelDebugger handle that is updated by this method
//            to contain the allocated IOKernelDebugger instance.
//
// Returns true on success, false otherwise.

bool IONetworkController::attachDebuggerClient(IOKernelDebugger ** debugger)
{
    IOKernelDebugger *  client;
    OSString *          debuggerString;
    bool                ret = false;
    bool                initOk = false;

    // We delay some initialization until the first time that
    // attachInterface() is called by the subclass.
    //
    SYNC_REQ(this, &IONetworkController::_controllerIsReady, &initOk);
    if (!initOk)
        return false;

    // Refuse to attach a debugger client if the controller's kIOEnableDebugger
    // property set to No (first letter starts with 'n' or 'N').

    debuggerString = OSDynamicCast(OSString, getProperty(kIOEnableDebugger));
    if (!debuggerString ||
        (debuggerString->getChar(0) == 'N') ||
        (debuggerString->getChar(0) == 'n'))
        return false;

    // Create a debugger client nub and register the static
    // member functions as the polled-mode handlers.
    //
    client = IOKernelDebugger::debugger(this,
                                        &debugTxHandler,
                                        &debugRxHandler);

    *debugger = client;

    if (client && !client->attach(this))
    {
        // Unable to attach the client object.
        *debugger = 0;
        client->detach(this);
        client->release();
    }

    if (*debugger)
    {
        IOLog("%s: Debugger client attached\n", getName());
        publishResource("kdp");
        ret = true;
    }

    return ret;
}

//---------------------------------------------------------------------------
// Detach and terminate the IOKernelDebugger client object provided.
// A synchronous termination is issued, and this method returns after
// the client has been terminated.
//
// debugger: The IOKernelDebugger instance to be detached and terminated.
//           If the argument provided is NULL or is not an IOKernelDebugger,
//           this method will return immediately.

void IONetworkController::detachDebuggerClient(IOKernelDebugger * debugger)
{
    if (OSDynamicCast(IOKernelDebugger, debugger) == 0)
        return;

    // Terminate the debugger client and return after the client has
    // been terminated. Since the debugger has no IOKit clients of
    // its own, this should be fast.

    debugger->terminate(kIOServiceRequired | kIOServiceSynchronous);
}

//---------------------------------------------------------------------------
// An enable request from an IOKernelDebugger client. This method is called 
// when an open is received from an IOKernelDebugger client. Drivers that
// wish to provide debugging services must override this method and setup
// the hardware to support the polled-mode send and receive methods;
// receivePacket() and sendPacket(). Debug capable drivers may also override
// the more generic enable/disable calls that take an IOService argument.
//
// debugger: The IOKernelDebugger client that issued the open.
//
// Returns kIOReturnSuccess. The driver method must return kIOReturnSuccess
// to allow the debugger open, anything else will cause the debugger open
// to fail and the attachDebuggerClient() method will return false.

IOReturn IONetworkController::enable(IOKernelDebugger * debugger)
{
    return handleDebuggerOpen(debugger);
}

IOReturn IONetworkController::handleDebuggerOpen(IOKernelDebugger * debugger)
{
    return kIOReturnSuccess;
}

//---------------------------------------------------------------------------
// A disable request from an IOKernelDebugger client. This method is called
// when a close is received from an IOKernelDebugger client. A driver which
// implements enable(IOKernelDebugger *) should also implement this method
// to disable hardware support for the polled-mode send and receive methods.
//
// debugger: The IOKernelDebugger client that issued the close.
//
// Returns kIOReturnSuccess. The driver method should return a status
// from the disable operation.

IOReturn IONetworkController::disable(IOKernelDebugger * debugger)
{
    return handleDebuggerClose(debugger);
}

IOReturn IONetworkController::handleDebuggerClose(IOKernelDebugger * debugger)
{
    return kIOReturnSuccess;
}

//---------------------------------------------------------------------------
// Take and release the debugger lock.

void IONetworkController::reserveDebuggerLock()
{
    _debugLockState = IODebuggerLock(this);
}

void IONetworkController::releaseDebuggerLock()
{
    IODebuggerUnlock(_debugLockState);
}

//---------------------------------------------------------------------------
// This static C++ member function is registered by attachDebuggerClient()
// as the debugger receive handler. IOKernelDebugger will call this
// function when KDP is polling for a received packet. This function will
// in turn will call the receivePacket() member function implemented by
// a driver with debugger support.

void IONetworkController::debugRxHandler(IOService * handler,
                                         void *      buffer,
                                         UInt *      length,
                                         UInt        timeout)
{
    ((IONetworkController *) handler)->receivePacket(buffer,
                                                     length,
                                                     timeout);   
}

//---------------------------------------------------------------------------
// This static C++ member function is registered by attachDebuggerClient()
// as the debugger transmit handler. IOKernelDebugger will call this
// function when KDP sends an outgoing packet. This function will in turn
// call the sendPacket() member function implemented by a driver with
// debugger support.

void IONetworkController::debugTxHandler(IOService * handler,
                                         void *      buffer,
                                         UInt        length)
{
    ((IONetworkController *) handler)->sendPacket(buffer, length);
}

//---------------------------------------------------------------------------
// Debugger polled-mode receive handler. This method must be implemented
// by a driver that supports kernel debugging. After a debugger client is 
// attached through attachDebuggerClient(), this method will be called by
// the debugger object to poll for a incoming packet when the debugger is
// active. This method can be called from an interrupt context, and the
// driver must never block or perform any memory allocation. The
// receivePacket() method in IONetworkController is used as a placeholder
// and should never be called. A driver that attaches a debugger client
// must override this method.
//
// pkt:     Pointer to a receive buffer where the received packet should
//          be stored to. The buffer has room for 1518 bytes.
// pkt_len: The length of the received packet must be written to the
//          integer pointed by pkt_len.
// timeout: The maximum amount of time in milliseconds to poll for
//          a packet to arrive before this method must return.

void IONetworkController::receivePacket(void * /*pkt*/,
                                        UInt * /*pkt_len*/,
                                        UInt /*timeout*/)
{
    IOLog("IONetworkController::%s()\n", __FUNCTION__);
}

//---------------------------------------------------------------------------
// Debugger polled-mode transmit handler. This method must be implemented
// by a driver that supports kernel debugging. After a debugger client is 
// attached through attachDebuggerClient(), this method will be called by
// the debugger object to send an outbound packet generated by the debugger.
// This method can be called from an interrupt context, and the
// driver must never block or perform any memory allocation. The
// sendPacket() method in IONetworkController is used as a placeholder
// and should never be called. A driver that attaches a debugger client
// must override this method.
//
// pkt:     Pointer to a transmit buffer containing the packet to be sent.
// pkt_len: The amount of data in the transmit buffer.

void IONetworkController::sendPacket(void * /*pkt*/, UInt /*pkt_len*/)
{
    IOLog("IONetworkController::%s()\n", __FUNCTION__);
}

//---------------------------------------------------------------------------
// Report the link status and the active medium. Update the link status 
// parameters published by the controller. Drivers should call this method 
// whenever the link status changes. Never call this method from interrupt 
// context since this method may block. An event will be sent to all attached
// interface objects when a change is detected.
//
// status: Link status bits. See IONetworkMedium.h for defined link
//         status bits.
// speed:  Link speed in units of bits per second.
// activeMedium: A medium entry in the published medium dictionary
//               where the link was established. This may not be the
//               same as the current medium.
// data:   An OSData containing any additional link information.
//
// Returns true if all link properties were successfully updated,
// false otherwise.

bool IONetworkController::setLinkStatus(UInt32                  status,
                                        UInt64                  speed,
                                        const IONetworkMedium * activeMedium,
                                        OSData *                data = 0)
{
    bool   ret;
    bool   changed = false;

    MEDIUM_LOCK;

    ret = setMediumProperty(gIOActiveMediumKey,
                            activeMedium,
                            &_activeMedium,
                            false,
                            &changed);

    ret = setLink32Property(kIOLinkStatus,
                            status,
                            &_linkStatus,
                            false,
                            &changed) && ret;

    ret = setLink64Property(kIOLinkSpeed,
                            speed,
                            &_linkSpeed,
                            false,
                            &changed) && ret;

    if (data) {
        if (_linkData != data) {
            ret = setProperty(kIOLinkData, data) && ret;
            _linkData = data;
            changed = true;
        }
    }
    else if (_linkData) {
        _linkData = 0;
        removeProperty(kIOLinkData);
        changed = true;
    }

    MEDIUM_UNLOCK;

    if (changed)
        broadcastEvent(kIONetworkEventLinkChange);

    return ret;
}

//---------------------------------------------------------------------------
// Returns the medium dictionary published by the driver through
// publishMediumDictionary(). Use copyMediumDictionary() to get a copy
// of the medium dictionary.
//
// Returns the published medium dictionary, or 0 if the driver has not
// yet published a medium dictionary using publishMediumDictionary().

const OSDictionary * IONetworkController::getMediumDictionary() const
{
    return OSDynamicCast(OSDictionary, getProperty(kIOMediumDictionary));
}

//---------------------------------------------------------------------------
// Returns a.copy of the medium dictionary published by the driver.
// The caller is responsible for releasing the dictionary object returned.
// Use getMediumDictionary() to get a reference to the published medium
// dictionary instead of creating a copy.
//
// Returns a copy of the medium dictionary, or 0 if the driver has not
// published a medium dictionary using publishMediumDictionary().

OSDictionary * IONetworkController::copyMediumDictionary() const
{
    OSDictionary * newMediumDict = 0;

    MEDIUM_LOCK;

    if (getMediumDictionary()) {
        newMediumDict = OSDictionary::withDictionary(getMediumDictionary(),
                                      getMediumDictionary()->getCount());
    }

    MEDIUM_UNLOCK;

    return newMediumDict;
}

//---------------------------------------------------------------------------
// A client request for the controller to change the selected medium.
// Drivers may override this method and provide an implementation
// appropriate for its hardware, then call setCurrentMedium() to update
// the current medium property if a change occurred.
//
// medium: An entry in the published medium dictionary.
//
// Return kIOReturnUnsupported. Drivers may override this method and
// return kIOReturnSuccess if the selected medium was activated,
// or an error code otherwise.

IOReturn IONetworkController::selectMedium(const IONetworkMedium * medium)
{
    return kIOReturnUnsupported;
}

//---------------------------------------------------------------------------
// Private function to lookup a key in the medium dictionary and call 
// setMedium() if a match is found. This function is called by our
// clients to change the medium by passing a name for the desired medium.

IOReturn IONetworkController::selectMediumWithName(const OSSymbol * mediumName)
{
    OSSymbol *        currentMediumName;
    IONetworkMedium * newMedium = 0;
    bool              doChange  = true;
    IOReturn          ret       = kIOReturnSuccess;

    if (OSDynamicCast(OSSymbol, mediumName) == 0)
        return kIOReturnBadArgument;

    MEDIUM_LOCK;

    do {
        const OSDictionary * mediumDict = getMediumDictionary();
        if (!mediumDict) {  // no medium dictionary, bail out.
            ret = kIOReturnUnsupported;
            break;
        }

        // Lookup the new medium in the dictionary.
        //
        newMedium = OSDynamicCast(IONetworkMedium, 
                                  mediumDict->getObject(mediumName));
        if (!newMedium) {
            ret = kIOReturnBadArgument;
            break;          // not found, invalid mediumName.
        }

        newMedium->retain();

        // Lookup the current medium key to avoid unnecessary
        // medium changes.
        //
        currentMediumName = (OSSymbol *) getProperty(gIOCurrentMediumKey);

        // Is change necessary?
        //
        if (currentMediumName && mediumName->isEqualTo(currentMediumName))
            doChange = false;
    }
    while (0);

    MEDIUM_UNLOCK;

    if (newMedium)
    {
        // Call the driver's selectMedium() without holding the medium lock.
    
        if (doChange)
            ret = selectMedium(newMedium);

        // offset the earlier retain count increment.
        //
        newMedium->release();
    }

    return ret;
}

//---------------------------------------------------------------------------
// Private function to add/replace/remove a medium property in the 
// property table. Returns true if the medium property was successfully
// added to the property table. The medium lock should be held before
// calling this function.

bool IONetworkController::setMediumProperty(const OSSymbol *         key,
                                            const IONetworkMedium *  medium,
                                            const IONetworkMedium ** cache,
                                            bool                     force,
                                            bool *                   changed)
{
    bool  ret = false;

    do {
        if (!force && (*cache == medium))
        {
            ret = true;
            break;
        }

        if (medium == 0)
        {
            removeProperty(key);
            if (changed) *changed = true;
            *cache = 0;
            ret = true;
            break;
        }

        const OSDictionary * mediumDict = getMediumDictionary();
        if (!mediumDict)
            break;    // no medium dictionary, bail out.

        if (OSDynamicCast(IONetworkMedium, medium) == 0)
            break;    // not a valid IONetworkMedium.

        // Make sure the medium given is an entry in the medium dictionary.
        //
        if (mediumDict->getObject(medium->getName()) != (OSObject *) medium)
            break;    // not a member of dictionary.

        // Update property table.
        //
        if ((ret = setProperty(key, (OSSymbol *) medium->getName())))
        {
            if (changed) *changed = true;
            *cache = medium;
        }
    }
    while (0);

    return ret;
}

bool IONetworkController::setLink64Property(const char *   key,
                                            UInt64         value,
                                            UInt64 *       cache,
                                            bool           force = false,
                                            bool *         changed = 0)
{
    if (!force && (*cache == value))
        return true;

    if (setProperty(key, value, 64))
    {
        if (changed) *changed = true;
        *cache = value;
        return true;
    }

    return false;
}

bool IONetworkController::setLink32Property(const char *   key,
                                            UInt32         value,
                                            UInt32 *       cache,
                                            bool           force = false,
                                            bool *         changed = 0)
{
    if (!force && (*cache == value))
        return true;

    if (setProperty(key, value, 32))
    {
        if (changed) *changed = true;
        *cache = value;
        return true;
    }

    return false;
}

//---------------------------------------------------------------------------
// From the set of medium objects in the medium dictionary published by the 
// driver, one of them can be designated as the currently selected medium. 
// Drivers should call this method whenever their media selection changes.
// An entry in the driver's property table is updated to advertise the 
// current medium.
//
// A media change event will be broadcasted to all attached interface
// clients when the current medium property changes.
//
// medium: A medium object to promote as the current medium.
//
// Returns true if the medium dictionary exists, the medium object
// provided matches an entry in this dictionary, and the property
// table update was successful, false otherwise.

bool IONetworkController::setCurrentMedium(const IONetworkMedium * medium)
{
    bool ret, changed;

    MEDIUM_LOCK;
    ret = setMediumProperty(gIOCurrentMediumKey,
                            medium, 
                            &_currentMedium,
                            false,
                            &changed);
    MEDIUM_UNLOCK;

    if (changed)
        broadcastEvent(kIONetworkEventMediumChange);
    
    return ret;
}

//---------------------------------------------------------------------------
// Returns the currently selected medium object. If the driver has yet to
// assign an entry in its medium dictionary as the current medium using the
// setCurrentMedium() method, then the driver's property table is consulted
// and a default medium property (can be set by the user), is looked
// up and the corresponding entry in the medium dictionary is returned.
// Therefore, drivers can always call getCurrentMedium() to either get
// the current medium selected by the driver, or the default
// medium chosen by the user.
//
// Returns the current medium entry from the medium dictionary, or 0
// if a matching entry was not found.

const IONetworkMedium * IONetworkController::getCurrentMedium() const
{
    IONetworkMedium * currentMedium     = 0;
    OSString *        currentMediumName = 0;

    MEDIUM_LOCK;

    do {
        const OSDictionary * mediumDict = getMediumDictionary();
        if (!mediumDict)    // no medium dictionary, bail out.
            break;

        // Fetch the current medium name from the property table.
        //
        currentMediumName = OSDynamicCast(OSString,
                            getProperty(gIOCurrentMediumKey));

        // Make sure the current medium name points to an entry in
        // the medium dictionary.
        //
        if (currentMediumName && !mediumDict->getObject(currentMediumName))
            currentMediumName = 0;

        if (currentMediumName == 0) {
            OSString * defaultMediumName;

            // No (valid) current medium name, try the default medium name.
            //
            defaultMediumName = OSDynamicCast(OSString, 
                                getProperty(kIODefaultMedium));

            // If there is a default medium name, and it points to a
            // valid entry in the medium dictionary, then make it
            // current.
            //
            if (defaultMediumName &&
                mediumDict->getObject(defaultMediumName))
            {
                // setProperty(kIOCurrentMedium, defaultMediumKey);
                currentMediumName = defaultMediumName;
            }
        }

        if (currentMediumName)
            currentMedium = OSDynamicCast(IONetworkMedium, 
                            mediumDict->getObject(currentMediumName));
    }
    while (0);

    MEDIUM_UNLOCK;

    return currentMedium;
}

//---------------------------------------------------------------------------
// A private function to verify a medium dictionary. Returns true if the
// dictionary is OK.

bool IONetworkController::verifyMediumDictionary(
                                const OSDictionary * mediumDict)
{
    OSCollectionIterator * iter;
    bool                   verifyOk = true;
    OSSymbol *             key;

    if (!OSDynamicCast(OSDictionary, mediumDict))
        return false;   // invalid argument

    if (mediumDict->getCount() == 0)
        return false;   // empty dictionary

    iter = OSCollectionIterator::withCollection((OSDictionary *) mediumDict);
    if (!iter)
        return false;   // cannot allocate iterator

    while ((key = (OSSymbol *) iter->getNextObject()))
    {
        if ( !OSDynamicCast(IONetworkMedium, mediumDict->getObject(key)) )
        {
            verifyOk = false;   // non-medium object in dictionary
            break;
        }
    }

    iter->release();
    
    return verifyOk;
}

//---------------------------------------------------------------------------
// Called by drivers to publish their medium dictionary.
// The dictionary consist of IONetworkMedium entries that represent
// the entire media selection supported by the hardware. This method
// will make a copy of the provided dictionary, then add the copy to
// the driver's property table. The dictionary provided can be
// released after this call returns. It is permissible to call
// this method multiple times, which may be necessary if the hardware's
// media capability changes dynamically. However, if this were not
// so, then drivers will typically call this method from its start() 
// implementation, after the hardware capability is discovered.
//
// Several methods depend on the presence of a medium dictionary.
// They should be called after the dictionary has been published.
// Those are:
//   selectMedium()
//   setCurrentMedium()
//   getCurrentMedium()
//   getMediumDictionary()
//   copyMediumDictionary()
//
// Calling publishMediumDictionary() will cause a media change event
// to be delivered to all attached interface clients.
//
// Returns true if the dictionary is valid, and was successfully
// added to the property table, false otherwise.

bool
IONetworkController::publishMediumDictionary(const OSDictionary * mediumDict)
{
    OSDictionary *   cloneDict;
    bool             ret = false;

    if (!verifyMediumDictionary(mediumDict))
        return false;   // invalid dictionary

    // Create a clone of the source dictionary. This prevents the driver
    // from adding/removing entries after the medium dictionary is added
    // to the property table.
    //
    cloneDict = OSDictionary::withDictionary(mediumDict,
                                             mediumDict->getCount());
    if (!cloneDict)
        return false;  // unable to create a copy

    MEDIUM_LOCK;                              

    // Add the dictionary to the property table.
    //
    if (setProperty(kIOMediumDictionary, cloneDict))
    {
        const OSSymbol *   mediumName;
        IONetworkMedium *  medium;

        mediumName = (OSSymbol *) getProperty(gIOCurrentMediumKey);
        medium     = mediumName ?
                     (IONetworkMedium *) cloneDict->getObject(mediumName) : 0;
        setMediumProperty(gIOCurrentMediumKey, medium, &_currentMedium, true);
        
        mediumName = (OSSymbol *) getProperty(gIOActiveMediumKey);
        medium     = mediumName ?
                     (IONetworkMedium *) cloneDict->getObject(mediumName) : 0;
        setMediumProperty(gIOActiveMediumKey, medium, &_activeMedium, true);

        ret = true;
    }

    MEDIUM_UNLOCK;

    // Retained by the property table. drop our retain count.
    //
    cloneDict->release();

    // Broadcast a medium change event.
    //
    broadcastEvent(kIONetworkEventMediumChange);

    return ret;
}

//---------------------------------------------------------------------------
// Call enablePacketFilters() through syncRequest(). See enablePacketFilters().

IOReturn IONetworkController::doEnablePacketFilters(IOService * client,
                                                    UInt32      newFilters,
                                                    UInt32 *    activeFiltersP)
{
    IOReturn  ret = kIOReturnNotReady;
    UInt32    dummy;
    UInt32 *  activeP = activeFiltersP ? activeFiltersP : &dummy;

    *activeP = 0;

    if (SYNC_REQ(client, &IONetworkController::enablePacketFilters,
                 &ret,
                 (void *) newFilters, (void *) activeP) == kIOReturnSuccess)
    {
        // Update the registry.
        //
        setProperty(kIOActivePacketFilters, *activeP, 32);
    }

    return ret;
}

//---------------------------------------------------------------------------
// Calls getPacketFilters() through syncRequest(). See getPacketFilters().

IOReturn IONetworkController::doGetPacketFilters(IOService * client,
                                                 UInt32 *    filtersP)
{
    DO_SYNC_REQ(getPacketFilters, (void *) filtersP)
}

//---------------------------------------------------------------------------
// Call enable(IOService *) through syncRequest(). See enable().

IOReturn IONetworkController:: doEnable(IOService * client)
{
    DO_SYNC_REQ(_enable, (void *) client)
}

//---------------------------------------------------------------------------
// Call disable(IOService *) through syncRequest(). See disable().

IOReturn IONetworkController:: doDisable(IOService * client)
{
    DO_SYNC_REQ(_disable, (void *) client)
}

//---------------------------------------------------------------------------
// Call getControllerIndex() through syncRequest(). See getControllerIndex().

IOReturn IONetworkController::doGetControllerIndex(IOService * client,
                                                   UInt32 *    index)
{
    DO_SYNC_REQ(getControllerIndex, (void *) index)
}

//---------------------------------------------------------------------------
// Call setMaxTransferUnit() through syncRequest(). See setMaxTransferUnit().

IOReturn IONetworkController::doSetMaxTransferUnit(IOService * client,
                                                   UInt32      mtu)
{
    DO_SYNC_REQ(setMaxTransferUnit, (void *) mtu)
}

//---------------------------------------------------------------------------
// Call selectMedium() through syncRequest(). See selectMedium().

IOReturn IONetworkController::doSelectMedium(IOService *      client,
                                             const OSSymbol * mediumName)
{
    DO_SYNC_REQ(selectMediumWithName, (void *) mediumName)
}

//---------------------------------------------------------------------------
// Call setOutputQueueCapacity() through syncRequest().
// See setOutputQueueCapacity().

IOReturn IONetworkController::doSetOutputQueueCapacity(IOService * client,
                                                       UInt32      capacity)
{
    DO_SYNC_REQ(setOutputQueueCapacity, (void *) capacity)
}

//---------------------------------------------------------------------------
// Call getOutputQueueCapacity() through syncRequest().
// See getOutputQueueCapacity().

IOReturn
IONetworkController::doGetOutputQueueCapacity(IOService * client,
                                              UInt32 *    capacityP)
{
    DO_SYNC_REQ(getOutputQueueCapacity, (void *) capacityP)
}

//---------------------------------------------------------------------------
// Call getOutputQueueSize() through syncRequest().
// See getOutputQueueSize().

IOReturn
IONetworkController::doGetOutputQueueSize(IOService * client,
                                          UInt32 *    sizeP)
{
    DO_SYNC_REQ(getOutputQueueSize, (void *) sizeP)
}

//---------------------------------------------------------------------------
// Call flushOutputQueue() through syncRequest(). See flushOutputQueue().

IOReturn
IONetworkController::doFlushOutputQueue(IOService * client,
                                        UInt32 *    flushCountP)
{
    DO_SYNC_REQ(flushOutputQueue, (void *) flushCountP)
}

//---------------------------------------------------------------------------
// Call performDiagnostics() through syncRequest().
// See performDiagnostics().

IOReturn
IONetworkController::doPerformDiagnostics(IOService * client,
                                          UInt32 *    failureCode)
{
    DO_SYNC_REQ(performDiagnostics, (void *) failureCode)
}