IONetworkController.cpp [plain text]
#include <IOKit/assert.h>
#include <IOKit/IOCommandGate.h>
#include <IOKit/network/IONetworkController.h>
#include <IOKit/network/IOOutputQueue.h>
#include <IOKit/network/IONetworkMedium.h>
#include <IOKit/IOMessage.h>
#include "IONetworkControllerPrivate.h"
extern "C" {
#include <sys/param.h> // mbuf limits defined here.
#include <sys/mbuf.h>
#include <sys/kdebug.h>
#include <machine/machine_routines.h>
}
#define super IOService
OSDefineMetaClassAndAbstractStructors( IONetworkController, IOService )
OSMetaClassDefineReservedUnused( IONetworkController, 0);
OSMetaClassDefineReservedUnused( IONetworkController, 1);
OSMetaClassDefineReservedUnused( IONetworkController, 2);
OSMetaClassDefineReservedUnused( IONetworkController, 3);
OSMetaClassDefineReservedUnused( IONetworkController, 4);
OSMetaClassDefineReservedUnused( IONetworkController, 5);
OSMetaClassDefineReservedUnused( IONetworkController, 6);
OSMetaClassDefineReservedUnused( IONetworkController, 7);
OSMetaClassDefineReservedUnused( IONetworkController, 8);
OSMetaClassDefineReservedUnused( IONetworkController, 9);
OSMetaClassDefineReservedUnused( IONetworkController, 10);
OSMetaClassDefineReservedUnused( IONetworkController, 11);
OSMetaClassDefineReservedUnused( IONetworkController, 12);
OSMetaClassDefineReservedUnused( IONetworkController, 13);
OSMetaClassDefineReservedUnused( IONetworkController, 14);
OSMetaClassDefineReservedUnused( IONetworkController, 15);
OSMetaClassDefineReservedUnused( IONetworkController, 16);
OSMetaClassDefineReservedUnused( IONetworkController, 17);
OSMetaClassDefineReservedUnused( IONetworkController, 18);
OSMetaClassDefineReservedUnused( IONetworkController, 19);
OSMetaClassDefineReservedUnused( IONetworkController, 20);
OSMetaClassDefineReservedUnused( IONetworkController, 21);
OSMetaClassDefineReservedUnused( IONetworkController, 22);
OSMetaClassDefineReservedUnused( IONetworkController, 23);
OSMetaClassDefineReservedUnused( IONetworkController, 24);
OSMetaClassDefineReservedUnused( IONetworkController, 25);
OSMetaClassDefineReservedUnused( IONetworkController, 26);
OSMetaClassDefineReservedUnused( IONetworkController, 27);
OSMetaClassDefineReservedUnused( IONetworkController, 28);
OSMetaClassDefineReservedUnused( IONetworkController, 29);
OSMetaClassDefineReservedUnused( IONetworkController, 30);
OSMetaClassDefineReservedUnused( IONetworkController, 31);
static bool isPowerOfTwo(UInt32 num)
{
return (num == (num & ~(num - 1)));
}
#define MEDIUM_LOCK IOTakeLock(_mediumLock);
#define MEDIUM_UNLOCK IOUnlock(_mediumLock);
#ifdef DEBUG
#define DLOG(fmt, args...) IOLog(fmt, ## args)
#else
#define DLOG(fmt, args...)
#endif
#define RELEASE(x) do { if (x) { (x)->release(); (x) = 0; } } while (0)
static const OSSymbol * gIOActiveMediumKey;
static const OSSymbol * gIOCurrentMediumKey;
static const OSSymbol * gIODefaultMediumKey;
static const OSSymbol * gIONullMediumName;
static const OSSymbol * gIOLinkDataKey;
static const OSSymbol * gIOControllerEnabledKey;
static const OSData * gIONullLinkData;
const OSSymbol * gIONetworkFilterGroup;
const OSSymbol * gIOEthernetWakeOnLANFilterGroup;
enum {
kCommandEnable = 1,
kCommandDisable = 2,
kCommandPrepare = 3,
kCommandInitDebugger = 4
};
class IONetworkControllerGlobals
{
public:
IONetworkControllerGlobals();
~IONetworkControllerGlobals();
inline bool isValid() const;
};
static IONetworkControllerGlobals gIONetworkControllerGlobals;
IONetworkControllerGlobals::IONetworkControllerGlobals()
{
gIOActiveMediumKey = OSSymbol::withCStringNoCopy(kIOActiveMedium);
gIOCurrentMediumKey = OSSymbol::withCStringNoCopy(kIOSelectedMedium);
gIODefaultMediumKey = OSSymbol::withCStringNoCopy(kIODefaultMedium);
gIONullMediumName = OSSymbol::withCStringNoCopy("");
gIOLinkDataKey = OSSymbol::withCStringNoCopy(kIOLinkData);
gIONullLinkData = OSData::withCapacity(0);
gIONetworkFilterGroup =
OSSymbol::withCStringNoCopy(kIONetworkFilterGroup);
gIOEthernetWakeOnLANFilterGroup =
OSSymbol::withCStringNoCopy("IOEthernetWakeOnLANFilterGroup");
gIOControllerEnabledKey =
OSSymbol::withCStringNoCopy("IOControllerEnabled");
}
IONetworkControllerGlobals::~IONetworkControllerGlobals()
{
RELEASE( gIOActiveMediumKey );
RELEASE( gIOCurrentMediumKey );
RELEASE( gIODefaultMediumKey );
RELEASE( gIONullMediumName );
RELEASE( gIOLinkDataKey );
RELEASE( gIONullLinkData );
RELEASE( gIONetworkFilterGroup );
RELEASE( gIOEthernetWakeOnLANFilterGroup );
RELEASE( gIOControllerEnabledKey );
}
bool IONetworkControllerGlobals::isValid() const
{
return ( gIOActiveMediumKey &&
gIOCurrentMediumKey &&
gIODefaultMediumKey &&
gIONullMediumName &&
gIOLinkDataKey &&
gIONullLinkData &&
gIONetworkFilterGroup &&
gIOEthernetWakeOnLANFilterGroup &&
gIOControllerEnabledKey );
}
bool IONetworkController::init(OSDictionary * properties)
{
_workLoop = 0;
_cmdGate = 0;
_outputQueue = 0;
_clientSet = 0;
_clientSetIter = 0;
_cmdClient = 0;
_propertiesPublished = false;
_mediumLock = 0;
_lastLinkData = gIONullLinkData;
_lastActiveMediumName = gIONullMediumName;
_lastCurrentMediumName = gIONullMediumName;
if ( super::init(properties) == false )
{
DLOG("IONetworkController: super::init() failed\n");
return false;
}
if ( gIONetworkControllerGlobals.isValid() == false )
{
return false;
}
return true;
}
bool IONetworkController::start(IOService * provider)
{
if ((getFeatures() & kIONetworkFeatureNoBSDWait) == 0)
waitForService(resourceMatching( "IOBSD" ));
if (!super::start(provider))
return false;
_clientSet = OSSet::withCapacity(2);
if (_clientSet == 0)
return false;
_clientSetIter = OSCollectionIterator::withCollection(_clientSet);
if (_clientSetIter == 0)
return false;
if (!setProperty(gIOActiveMediumKey, (OSSymbol *) gIONullMediumName) ||
!setProperty(gIOCurrentMediumKey, (OSSymbol *) gIONullMediumName))
return false;
_linkStatus = OSNumber::withNumber((UInt64) 0, 32);
if (!_linkStatus || !setProperty(kIOLinkStatus, _linkStatus))
{
return false;
}
_linkSpeed = OSNumber::withNumber((UInt64) 0, 64);
if (!_linkSpeed || !setProperty(kIOLinkSpeed, _linkSpeed))
{
return false;
}
_mediumLock = IOLockAlloc();
if (!_mediumLock)
return false;
IOLockInitWithState(_mediumLock, kIOLockStateUnlocked);
if ( createWorkLoop() != true )
{
DLOG("%s: createWorkLoop() error\n", getName());
return false;
}
_workLoop = getWorkLoop();
if ( _workLoop == 0 )
{
DLOG("%s: IOWorkLoop allocation failed\n", getName());
return false;
}
_workLoop->retain();
if (_workLoop != provider->getWorkLoop())
{
ml_thread_policy( _workLoop->getThread(), MACHINE_GROUP,
(MACHINE_NETWORK_GROUP|MACHINE_NETWORK_WORKLOOP) );
}
_cmdGate = IOCommandGate::commandGate(this);
if (!_cmdGate ||
(_workLoop->addEventSource(_cmdGate) != kIOReturnSuccess))
{
DLOG("%s: IOCommandGate initialization failed\n", getName());
return false;
}
_outputQueue = createOutputQueue();
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;
}
if (constraints.alignStart < constraints.alignLength)
constraints.alignStart = constraints.alignLength;
_alignStart = (constraints.alignStart) ? constraints.alignStart - 1 : 0;
_alignLength = (constraints.alignLength) ? constraints.alignLength - 1 : 0;
_alignPadding = _alignStart + _alignLength;
PMinit();
provider->joinPMtree(this);
return true;
}
void IONetworkController::stop(IOService * provider)
{
PMstop();
super::stop(provider);
}
IOReturn IONetworkController::registerWithPolicyMaker(IOService * policyMaker)
{
return kIOReturnUnsupported;
}
bool IONetworkController::createWorkLoop()
{
return true;
}
IOCommandGate * IONetworkController::getCommandGate() const
{
return _cmdGate;
}
IOOutputAction IONetworkController::getOutputHandler() const
{
return (IOOutputAction) &IONetworkController::outputPacket;
}
bool
IONetworkController::attachInterface(IONetworkInterface ** interfaceP,
bool doRegister)
{
IONetworkInterface * netif;
*interfaceP = 0;
if (executeCommand(this, &IONetworkController::handleCommand,
this, (void *) kCommandPrepare) != kIOReturnSuccess)
{
return false;
}
do {
netif = createInterface();
if (!netif)
break;
if ( !configureInterface(netif) || !netif->attach(this) )
{
netif->release();
break;
}
*interfaceP = netif;
if (doRegister)
netif->registerService();
return true; }
while (0);
return false; }
void
IONetworkController::detachInterface(IONetworkInterface * interface,
bool sync)
{
IOOptionBits options = kIOServiceRequired;
if (OSDynamicCast(IONetworkInterface, interface) == 0)
return;
if (sync)
options |= kIOServiceSynchronous;
interface->terminate(options);
}
IOReturn IONetworkController::prepare()
{
IOReturn ret = kIOReturnSuccess;
if ( _propertiesPublished == false )
{
if ( publishProperties() == true )
{
_propertiesPublished = true;
if (pm_vars != 0)
{
if ( registerWithPolicyMaker( this ) != kIOReturnSuccess )
{
PMstop();
}
}
}
else
{
ret = kIOReturnError;
}
}
return ret;
}
bool IONetworkController::handleOpen(IOService * client,
IOOptionBits options,
void * argument)
{
assert(client);
return _clientSet->setObject(client);
}
void IONetworkController::handleClose(IOService * client, IOOptionBits options)
{
_clientSet->removeObject(client);
}
bool IONetworkController::handleIsOpen(const IOService * client) const
{
if (client)
return _clientSet->containsObject(client);
else
return (_clientSet->getCount() > 0);
}
void IONetworkController::free()
{
if (_clientSet) assert(_clientSet->getCount() == 0);
RELEASE( _outputQueue );
if( _cmdGate )
{
if(_workLoop) _workLoop->removeEventSource(_cmdGate);
_cmdGate->release();
_cmdGate = 0;
}
RELEASE( _workLoop );
RELEASE( _clientSetIter );
RELEASE( _clientSet );
RELEASE( _linkStatus );
RELEASE( _linkSpeed );
if (_mediumLock) { IOLockFree(_mediumLock); _mediumLock = 0; }
super::free();
}
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;
}
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;
}
IOReturn IONetworkController::enable(IONetworkInterface * interface)
{
IOLog("IONetworkController::%s\n", __FUNCTION__);
return kIOReturnUnsupported;
}
IOReturn IONetworkController::disable(IONetworkInterface * interface)
{
IOLog("IONetworkController::%s\n", __FUNCTION__);
return kIOReturnUnsupported;
}
bool IONetworkController::publishProperties()
{
bool ret = false;
const OSString * string;
UInt32 num;
OSDictionary * dict = 0;
OSNumber * numObj = 0;
do {
bool status;
string = newVendorString();
if (string) {
status = setProperty(kIOVendor, (OSObject *) string);
string->release();
if (status != true) break;
}
string = newModelString();
if (string) {
status = setProperty(kIOModel, (OSObject *) string);
string->release();
if (status != true) break;
}
string = newRevisionString();
if (string) {
status = setProperty(kIORevision, (OSObject *) string);
string->release();
if (status != true) break;
}
num = getFeatures();
if ( !setProperty(kIOFeatures, num, sizeof(num) * 8) )
break;
if ( ( getMaxPacketSize(&num) != kIOReturnSuccess ) ||
( !setProperty(kIOMaxPacketSize, num, sizeof(num) * 8) ) )
break;
if ( ( getMinPacketSize(&num) != kIOReturnSuccess ) ||
( !setProperty(kIOMinPacketSize, num, sizeof(num) * 8) ) )
break;
if (getPacketFilters(gIONetworkFilterGroup, &num) != kIOReturnSuccess)
break;
dict = OSDictionary::withCapacity(4);
numObj = OSNumber::withNumber(num, sizeof(num) * 8);
if ( (dict == 0) || (numObj == 0) ) break;
if ( !dict->setObject(gIONetworkFilterGroup, numObj) ||
!setProperty(kIOPacketFilters, dict) )
break;
ret = true;
}
while (false);
if (ret == false) {
DLOG("IONetworkController::%s error\n", __FUNCTION__);
}
if ( dict ) dict->release();
if ( numObj ) numObj->release();
return ret;
}
bool IONetworkController::_broadcastEvent(UInt32 type, void * data)
{
IONetworkInterface * netif;
lockForArbitration();
if (_clientSet->getCount())
{
_clientSetIter->reset();
while ((netif = (IONetworkInterface *)_clientSetIter->getNextObject()))
{
if (OSDynamicCast(IONetworkInterface, netif) == 0)
continue; netif->inputEvent(type, data);
}
}
unlockForArbitration();
return true;
}
IOReturn IONetworkController::setMaxPacketSize(UInt32 maxSize)
{
return kIOReturnUnsupported;
}
UInt32 IONetworkController::outputPacket(mbuf_t m, void * param)
{
if (m) freePacket(m);
return 0;
}
UInt32 IONetworkController::getFeatures() const
{
return 0;
}
const OSString * IONetworkController::newVendorString() const
{
return 0;
}
const OSString * IONetworkController::newModelString() const
{
return 0;
}
const OSString * IONetworkController::newRevisionString() const
{
return 0;
}
struct cmdStruct {
OSObject * client;
void * target;
IONetworkController::Action action;
void * param0;
void * param1;
void * param2;
void * param3;
IOReturn ret;
};
OSObject * IONetworkController::getCommandClient() const
{
return ( _workLoop->inGate() ? _cmdClient : 0 );
}
bool IONetworkController::configureInterface(IONetworkInterface * interface)
{
IOOutputAction handler;
OSObject * target;
bool ret;
IONetworkData * stats;
if (!OSDynamicCast(IONetworkInterface, interface))
return false;
IOOutputQueue * outQueue = getOutputQueue();
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;
}
IOOutputQueue * IONetworkController::createOutputQueue()
{
return 0;
}
IOOutputQueue * IONetworkController::getOutputQueue() const
{
return _outputQueue;
}
void IONetworkController::getPacketBufferConstraints(
IOPacketBufferConstraints * constraintsP) const
{
assert(constraintsP);
constraintsP->alignStart = kIOPacketBufferAlign1;
constraintsP->alignLength = kIOPacketBufferAlign1;
}
static mbuf_t getPacket( UInt32 size,
UInt32 how,
UInt32 smask,
UInt32 lmask )
{
mbuf_t packet;
UInt32 reqSize = size + smask + lmask;
if(reqSize > MHLEN && reqSize <= MINCLSIZE) reqSize = MINCLSIZE + 1;
if( 0 == mbuf_allocpacket(how, reqSize, NULL, &packet))
{
mbuf_t m = packet;
mbuf_pkthdr_setlen(packet, size);
while(size && m)
{
vm_address_t alignedStart, originalStart;
originalStart = (vm_address_t)mbuf_data(m);
alignedStart = (originalStart + smask) & ~smask;
mbuf_setdata(m, (caddr_t)alignedStart, (mbuf_maxlen(m) - (alignedStart - originalStart)) & ~lmask);
if(mbuf_len(m) > size)
mbuf_setlen(m, size);
size -= mbuf_len(m);
m = mbuf_next(m);
}
return packet;
}
else
return NULL;
}
mbuf_t IONetworkController::allocatePacket( UInt32 size )
{
return getPacket( size, M_WAIT, _alignStart, _alignLength );
}
void IONetworkController::freePacket(mbuf_t m, IOOptionBits options)
{
assert(m);
if ( options & kDelayFree )
{
mbuf_setnextpkt(m, _freeList);
_freeList = m;
}
else
{
mbuf_freem_list(m);
}
}
UInt32 IONetworkController::releaseFreePackets()
{
UInt32 count = 0;
mbuf_t tempmb = _freeList;
while(tempmb)
{
tempmb = mbuf_nextpkt(tempmb);
count++;
}
if ( count )
{
mbuf_freem_list( _freeList );
_freeList = 0;
}
return count;
}
static inline bool IO_COPY_MBUF(
mbuf_t src,
mbuf_t dst,
int length)
{
caddr_t src_dat, dst_dat;
int dst_len, src_len;
assert(src && dst);
dst_len = mbuf_len(dst);
dst_dat = (caddr_t)mbuf_data(dst);
while (src) {
src_len = mbuf_len( src );
src_dat = (caddr_t)mbuf_data( src );
if (src_len > length)
src_len = length;
while (src_len) {
if (dst_len >= src_len) {
bcopy(src_dat, dst_dat, src_len);
length -= src_len;
dst_len -= src_len;
dst_dat += src_len;
src_len = 0;
}
else {
bcopy(src_dat, dst_dat, dst_len); length -= dst_len;
dst_len = 0;
src_len -= dst_len;
}
if (dst_len == 0) {
if (!(dst = mbuf_next(dst)))
return (length == 0);
dst_len = mbuf_len(dst);
dst_dat = (caddr_t)mbuf_data(dst);
}
}
src = mbuf_next(src);
}
return (length == 0); }
mbuf_t IONetworkController::replacePacket(mbuf_t * mp,
UInt32 size)
{
assert((mp != NULL) && (*mp != NULL));
mbuf_t m = *mp;
if (size == 0) size = mbuf_pkthdr_len(m);
if ( (*mp = getPacket(size, M_DONTWAIT, _alignStart, _alignLength)) == 0 )
{
*mp = m; m = 0;
}
return m;
}
mbuf_t IONetworkController::copyPacket(mbuf_t m,
UInt32 size)
{
mbuf_t mn;
assert(m != NULL);
if (size == 0) size = mbuf_pkthdr_len(m);
if ( (mn = getPacket(size, M_DONTWAIT, _alignStart, _alignLength)) == 0 )
return 0;
if (!IO_COPY_MBUF(m, mn, size))
{
freePacket(mn); mn = 0;
}
return mn;
}
mbuf_t IONetworkController::replaceOrCopyPacket(mbuf_t *mp,
UInt32 rcvlen,
bool * replacedP)
{
mbuf_t m;
assert((mp != NULL) && (*mp != NULL));
if ( (rcvlen + _alignPadding) > MHLEN )
{
m = *mp;
if ( (*mp = getPacket( mbuf_pkthdr_len(m), M_DONTWAIT,
_alignStart, _alignLength)) == 0 )
{
*mp = m; m = 0; }
*replacedP = true;
}
else
{
if ( (m = getPacket( rcvlen, M_DONTWAIT,
_alignStart, _alignLength )) == 0 ) return 0;
if (!IO_COPY_MBUF(*mp, m, rcvlen))
{
freePacket(m); m = 0;
}
*replacedP = false;
}
return m;
}
IOReturn
IONetworkController::getChecksumSupport( UInt32 * checksumMask,
UInt32 checksumFamily,
bool isOutput )
{
return kIOReturnUnsupported;
}
#define kTransportLayerPartialChecksums \
( kChecksumTCPNoPseudoHeader | \
kChecksumUDPNoPseudoHeader | \
kChecksumTCPSum16 )
#define kTransportLayerFullChecksums \
( kChecksumTCP | kChecksumUDP )
void
IONetworkController::getChecksumDemand( const mbuf_t mt,
UInt32 checksumFamily,
UInt32 * demandMask,
void * param0,
void * param1 )
{
mbuf_csum_request_flags_t request;
u_int32_t value;
*demandMask = 0;
if ( checksumFamily != kChecksumFamilyTCPIP )
{
return;
}
mbuf_get_csum_requested(mt, &request, &value);
*demandMask = request & ( kChecksumIP |
kChecksumTCP |
kChecksumUDP |
kChecksumTCPSum16 );
if ( request & kChecksumTCPSum16 )
{
if (param0)
*((UInt16 *) param0) = (UInt16) (value);
if (param1)
*((UInt16 *) param1) = (UInt16) (value >> 16);
}
}
bool
IONetworkController::setChecksumResult( mbuf_t mt,
UInt32 family,
UInt32 result,
UInt32 valid,
UInt32 param0,
UInt32 param1 )
{
mbuf_csum_performed_flags_t performed;
u_int32_t value;
valid &= result;
performed = value = 0;
if ( family != kChecksumFamilyTCPIP )
{
return false;
}
if ( result & kChecksumIP )
{
performed |= MBUF_CSUM_DID_IP;
if ( valid & kChecksumIP )
performed |= MBUF_CSUM_IP_GOOD;
}
if ( valid & kTransportLayerFullChecksums )
{
performed |= ( MBUF_CSUM_DID_DATA | MBUF_CSUM_PSEUDO_HDR );
value = 0xffff; }
else if ( result & kTransportLayerPartialChecksums )
{
performed |= MBUF_CSUM_DID_DATA;
value = (UInt16) param0;
if ( result & kChecksumTCPSum16 )
{
performed |= MBUF_CSUM_TCP_SUM16;
value |= (((UInt16) param1) << 16);
}
}
mbuf_set_csum_performed(mt, performed, value);
return true;
}
#if 0
static void _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");
}
#endif
bool IONetworkController::attachDebuggerClient(IOKernelDebugger ** debugger)
{
IOKernelDebugger * client;
bool ret = false;
UInt32 debugArg=0;
PE_parse_boot_arg( "debug", &debugArg );
if(debugArg == 0)
{
*debugger = 0;
return false;
}
if (executeCommand(this, &IONetworkController::handleCommand,
this, (void *) kCommandPrepare) != kIOReturnSuccess)
{
return false;
}
client = IOKernelDebugger::debugger( this,
&debugTxHandler,
&debugRxHandler );
if ( client && !client->attach(this) )
{
client->terminate( kIOServiceRequired | kIOServiceSynchronous );
client->release();
client = 0;
}
*debugger = client;
if ( client )
{
executeCommand( this, &IONetworkController::handleCommand,
this, (void *) kCommandInitDebugger,
(void *) client );
client->registerService();
ret = true;
}
return ret;
}
void IONetworkController::detachDebuggerClient(IOKernelDebugger * debugger)
{
if (OSDynamicCast(IOKernelDebugger, debugger) == 0)
return;
debugger->terminate(kIOServiceRequired | kIOServiceSynchronous);
}
IOReturn IONetworkController::enable(IOKernelDebugger * debugger)
{
return kIOReturnSuccess;
}
IOReturn IONetworkController::disable(IOKernelDebugger * debugger)
{
return kIOReturnSuccess;
}
void IONetworkController::reserveDebuggerLock()
{
if ( _debugLockCount++ == 0 )
{
_debugLockState = IODebuggerLock( this );
}
}
void IONetworkController::releaseDebuggerLock()
{
if ( --_debugLockCount == 0 )
{
IODebuggerUnlock( _debugLockState );
}
assert( _debugLockCount >= 0 );
}
void IONetworkController::debugRxHandler(IOService * handler,
void * buffer,
UInt32 * length,
UInt32 timeout)
{
((IONetworkController *) handler)->receivePacket(buffer,
length,
timeout);
}
void IONetworkController::debugTxHandler(IOService * handler,
void * buffer,
UInt32 length)
{
((IONetworkController *) handler)->sendPacket(buffer, length);
}
void IONetworkController::receivePacket(void * ,
UInt32 * ,
UInt32 )
{
IOLog("IONetworkController::%s()\n", __FUNCTION__);
}
void IONetworkController::sendPacket(void * , UInt32 )
{
IOLog("IONetworkController::%s()\n", __FUNCTION__);
}
bool IONetworkController::setLinkStatus(
UInt32 status,
const IONetworkMedium * activeMedium,
UInt64 speed,
OSData * data)
{
bool success = true;
bool changed = false;
UInt32 linkEvent = 0;
const OSSymbol * name = activeMedium ? activeMedium->getName() :
gIONullMediumName;
if (data == 0)
data = (OSData *) gIONullLinkData;
if ((speed == 0) && activeMedium)
speed = activeMedium->getSpeed();
MEDIUM_LOCK;
if (name != _lastActiveMediumName)
{
if ( setProperty(gIOActiveMediumKey, (OSSymbol *) name) )
{
changed = true;
_lastActiveMediumName = name;
}
else
success = false;
}
if (data != _lastLinkData)
{
if ( setProperty(gIOLinkDataKey, data) )
{
changed = true;
_lastLinkData = data;
}
else
success = false;
}
if (status != _linkStatus->unsigned32BitValue())
{
if (status & kIONetworkLinkValid)
{
linkEvent = (status & kIONetworkLinkActive) ?
kIONetworkEventTypeLinkUp :
kIONetworkEventTypeLinkDown;
}
_linkStatus->setValue(status);
changed = true;
}
if (speed != _linkSpeed->unsigned64BitValue())
{
_linkSpeed->setValue(speed);
changed = true;
}
MEDIUM_UNLOCK;
if (linkEvent)
_broadcastEvent(linkEvent);
return success;
}
const OSDictionary * IONetworkController::getMediumDictionary() const
{
return (OSDictionary *) getProperty(kIOMediumDictionary);
}
OSDictionary * IONetworkController::copyMediumDictionary() const
{
const OSDictionary * mediumDict;
OSDictionary * copy = 0;
MEDIUM_LOCK;
mediumDict = getMediumDictionary();
if (mediumDict)
{
copy = OSDictionary::withDictionary(mediumDict,
mediumDict->getCount());
}
MEDIUM_UNLOCK;
return copy;
}
IOReturn IONetworkController::selectMedium(const IONetworkMedium * medium)
{
return kIOReturnUnsupported;
}
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)
{
ret = kIOReturnUnsupported;
break;
}
newMedium = (IONetworkMedium *) mediumDict->getObject(mediumName);
if (!newMedium)
{
ret = kIOReturnBadArgument;
break; }
newMedium->retain();
currentMediumName = (OSSymbol *) getProperty(gIOCurrentMediumKey);
if (currentMediumName && mediumName->isEqualTo(currentMediumName))
doChange = false;
}
while (0);
MEDIUM_UNLOCK;
if (newMedium)
{
if (doChange)
ret = selectMedium(newMedium);
newMedium->release();
}
return ret;
}
bool IONetworkController::setSelectedMedium(const IONetworkMedium * medium)
{
bool success = true;
bool changed = false;
const OSSymbol * name = medium ? medium->getName() : gIONullMediumName;
MEDIUM_LOCK;
if (name != _lastCurrentMediumName)
{
if ( setProperty(gIOCurrentMediumKey, (OSSymbol *) name) )
{
changed = true;
_lastCurrentMediumName = name;
}
else
success = false;
}
MEDIUM_UNLOCK;
#if 0
if (changed)
_broadcastEvent(kIONetworkEventTypeLinkChange);
#endif
return success;
}
const IONetworkMedium * IONetworkController::getSelectedMedium() const
{
IONetworkMedium * medium = 0;
OSSymbol * mediumName;
MEDIUM_LOCK;
do {
const OSDictionary * mediumDict = getMediumDictionary();
if (!mediumDict) break;
mediumName = (OSSymbol *) getProperty(gIOCurrentMediumKey);
medium = (IONetworkMedium *) mediumDict->getObject(mediumName);
if ( medium == 0 )
{
OSString * aString;
aString = OSDynamicCast( OSString,
getProperty(gIODefaultMediumKey) );
if ( aString )
medium = (IONetworkMedium *) mediumDict->getObject(aString);
}
}
while (0);
MEDIUM_UNLOCK;
return medium;
}
static bool verifyMediumDictionary(const OSDictionary * mediumDict)
{
OSCollectionIterator * iter;
bool verifyOk = true;
OSSymbol * key;
if (!OSDynamicCast(OSDictionary, mediumDict))
return false;
if (mediumDict->getCount() == 0)
return false;
iter = OSCollectionIterator::withCollection((OSDictionary *) mediumDict);
if (!iter)
return false;
while ((key = (OSSymbol *) iter->getNextObject()))
{
if ( !OSDynamicCast(IONetworkMedium, mediumDict->getObject(key)) )
{
verifyOk = false; break;
}
}
iter->release();
return verifyOk;
}
bool
IONetworkController::publishMediumDictionary(const OSDictionary * mediumDict)
{
OSDictionary * cloneDict;
bool ret = false;
if (!verifyMediumDictionary(mediumDict))
return false;
cloneDict = OSDictionary::withDictionary(mediumDict,
mediumDict->getCount());
if (!cloneDict)
return false;
MEDIUM_LOCK;
if (setProperty(kIOMediumDictionary, cloneDict))
{
const OSSymbol * mediumName;
mediumName = (OSSymbol *) getProperty(gIOCurrentMediumKey);
if (cloneDict->getObject(mediumName) == 0)
{
mediumName = gIONullMediumName;
}
setProperty(gIOCurrentMediumKey, (OSSymbol *) mediumName);
_lastCurrentMediumName = mediumName;
mediumName = (OSSymbol *) getProperty(gIOActiveMediumKey);
if (cloneDict->getObject(mediumName) == 0)
{
mediumName = gIONullMediumName;
}
setProperty(gIOActiveMediumKey, (OSSymbol *) mediumName);
_lastActiveMediumName = mediumName;
ret = true;
}
MEDIUM_UNLOCK;
cloneDict->release();
#if 0
_broadcastEvent(kIONetworkEventTypeLinkChange);
#endif
return ret;
}
IOReturn IONetworkController::executeCommandAction(OSObject * owner,
void * arg0,
void * ,
void * ,
void * )
{
IONetworkController * self = (IONetworkController *) owner;
cmdStruct * cmdP = (cmdStruct *) arg0;
IOReturn ret = kIOReturnSuccess;
OSObject * oldClient;
assert(cmdP && self);
oldClient = self->_cmdClient;
self->_cmdClient = cmdP->client;
cmdP->ret = (*cmdP->action)( cmdP->target,
cmdP->param0,
cmdP->param1,
cmdP->param2,
cmdP->param3 );
self->_cmdClient = oldClient;
return ret;
}
IOReturn IONetworkController::executeCommand(OSObject * client,
Action action,
void * target,
void * param0,
void * param1,
void * param2,
void * param3)
{
cmdStruct cmd;
IOReturn ret;
cmd.client = client;
cmd.action = action;
cmd.target = target;
cmd.param0 = param0;
cmd.param1 = param1;
cmd.param2 = param2;
cmd.param3 = param3;
ret = _cmdGate->runAction( (IOCommandGate::Action)
&IONetworkController::executeCommandAction,
(void *) &cmd );
if (ret == kIOReturnSuccess)
ret = cmd.ret;
return ret;
}
void countBSDEnablesApplier( IOService * client, void * context )
{
if ( OSDynamicCast( IONetworkInterface, client ) &&
client->getProperty( gIOControllerEnabledKey ) == kOSBooleanTrue )
(*(UInt32 *)context)++;
}
IOReturn IONetworkController::handleCommand(void * target,
void * param0,
void * param1,
void * param2,
void * param3)
{
IONetworkController * self = (IONetworkController *) target;
UInt32 command = (UInt32) param0;
IOService * client = (IOService *) param1;
IOReturn ret = kIOReturnSuccess;
UInt32 count = 0;
switch ( command )
{
case kCommandEnable:
if (( ret = self->enable(client) ) == kIOReturnSuccess )
{
client->setProperty( gIOControllerEnabledKey, kOSBooleanTrue );
if ( OSDynamicCast( IONetworkInterface, client ) )
{
self->applyToClients( countBSDEnablesApplier, &count );
if ( count == 1 )
self->messageClients(kMessageControllerWasEnabledForBSD);
}
self->messageClients(kMessageControllerWasEnabled, client);
}
break;
case kCommandDisable:
if (( ret = self->disable(client) ) == kIOReturnSuccess )
{
client->setProperty( gIOControllerEnabledKey, kOSBooleanFalse );
if ( OSDynamicCast( IONetworkInterface, client ) )
{
self->applyToClients( countBSDEnablesApplier, &count );
if ( count == 0 )
self->messageClients(kMessageControllerWasDisabledForBSD);
}
self->messageClients(kMessageControllerWasDisabled, client);
}
break;
case kCommandPrepare:
ret = self->prepare();
break;
case kCommandInitDebugger:
self->applyToClients( countBSDEnablesApplier, &count );
if ( count )
client->message( kMessageControllerWasEnabledForBSD, self );
break;
default:
ret = kIOReturnUnsupported;
break;
}
return ret;
}
IOReturn IONetworkController::doEnable(IOService * client)
{
return executeCommand( client,
&IONetworkController::handleCommand,
this,
(void *) kCommandEnable,
(void *) client);
}
IOReturn IONetworkController::doDisable(IOService * client)
{
return executeCommand( client,
&IONetworkController::handleCommand,
this,
(void *) kCommandDisable,
(void *) client);
}
const IONetworkMedium * IONetworkController::getCurrentMedium() const
{
return getSelectedMedium();
}
bool IONetworkController::setCurrentMedium(const IONetworkMedium * medium)
{
return setSelectedMedium(medium);
}