IONetworkInterface.cpp [plain text]
extern "C" {
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <net/bpf.h>
#include <net/if.h>
#include <net/if_media.h>
#include <net/dlil.h>
#include <sys/kern_event.h>
}
#include <IOKit/assert.h>
#include <IOKit/IOLib.h>
#include <IOKit/IOBSD.h>
#include <IOKit/network/IONetworkInterface.h>
#include <IOKit/network/IONetworkController.h>
#include "IONetworkUserClient.h"
#include "IONetworkStack.h"
#define super IOService
OSDefineMetaClassAndAbstractStructors( IONetworkInterface, IOService )
OSMetaClassDefineReservedUnused( IONetworkInterface, 2);
OSMetaClassDefineReservedUnused( IONetworkInterface, 3);
OSMetaClassDefineReservedUnused( IONetworkInterface, 4);
OSMetaClassDefineReservedUnused( IONetworkInterface, 5);
OSMetaClassDefineReservedUnused( IONetworkInterface, 6);
OSMetaClassDefineReservedUnused( IONetworkInterface, 7);
OSMetaClassDefineReservedUnused( IONetworkInterface, 8);
OSMetaClassDefineReservedUnused( IONetworkInterface, 9);
OSMetaClassDefineReservedUnused( IONetworkInterface, 10);
OSMetaClassDefineReservedUnused( IONetworkInterface, 11);
OSMetaClassDefineReservedUnused( IONetworkInterface, 12);
OSMetaClassDefineReservedUnused( IONetworkInterface, 13);
OSMetaClassDefineReservedUnused( IONetworkInterface, 14);
OSMetaClassDefineReservedUnused( IONetworkInterface, 15);
#ifdef DEBUG
#define DLOG(fmt, args...) IOLog(fmt, ## args)
#else
#define DLOG(fmt, args...)
#endif
#define _powerChangeThreadCall _reserved->powerChangeThreadCall
#define _powerChangeNoticeLock _reserved->powerChangeNoticeLock
#define _powerChangeNoticeList _reserved->powerChangeNoticeList
bool IONetworkInterface::init(IONetworkController * controller)
{
bool ret = false;
if ( super::init() == false )
return false;
if ( OSDynamicCast(IONetworkController, controller) == 0 )
return false;
_controller = controller;
_reserved = IONew( ExpansionData, 1 );
if ( _reserved == 0 )
return false;
bzero( _reserved, sizeof(ExpansionData) );
queue_init( &_powerChangeNoticeList );
_powerChangeThreadCall = thread_call_allocate(
(thread_call_func_t) powerChangeHandler,
(thread_call_param_t) this );
if ( !_powerChangeThreadCall )
return false;
_powerChangeNoticeLock = IOSimpleLockAlloc();
if ( _powerChangeNoticeLock == 0 )
return false;
_ifLock = IORecursiveLockAlloc();
if ( _ifLock == 0 )
return false;
_stateBits = OSNumber::withNumber((UInt64) 0, 32);
if ( _stateBits == 0 )
return false;
setProperty( kIOInterfaceState, _stateBits );
_clientSet = OSSet::withCapacity(2);
if ( _clientSet == 0 )
return false;
_ifp = getIfnet();
if ( _ifp == 0 )
{
DLOG("%s: getIfnet() returned NULL\n", getName());
return false;
}
if ( (_dataDict = OSDictionary::withCapacity(5)) == 0 )
return false;
IONetworkData * data = IONetworkData::withExternalBuffer(
kIONetworkStatsKey,
sizeof(IONetworkStats),
(UInt8 *) &(_ifp->if_data.ifi_ipackets));
if ( data )
{
addNetworkData(data);
data->release();
}
if ( registerOutputHandler( controller,
controller->getOutputHandler() ) == false )
{
return false;
}
boolean_t funnel_state = thread_funnel_set( network_flock, TRUE );
ret = initIfnet(_ifp);
thread_funnel_set( network_flock, funnel_state );
setProperty( kIOInterfaceNamePrefix, getNamePrefix() );
setProperty( kIOPrimaryInterface, isPrimaryInterface() );
return ret;
}
void IONetworkInterface::free()
{
DLOG("IONetworkInterface::free()\n");
if ( _clientSet )
{
assert(_clientSet->getCount() == 0);
_clientSet->release();
_clientSet = 0;
}
if ( _dataDict ) { _dataDict->release(); _dataDict = 0; }
if ( _stateBits ) { _stateBits->release(); _stateBits = 0; }
if ( _ifLock )
{
IORecursiveLockFree(_ifLock);
_ifLock = 0;
}
if ( _reserved )
{
if ( _powerChangeThreadCall )
thread_call_free( _powerChangeThreadCall );
if ( _powerChangeNoticeLock )
IOSimpleLockFree( _powerChangeNoticeLock );
IODelete( _reserved, ExpansionData, 1 );
_reserved = 0;
}
clearInputQueue();
super::free();
}
bool IONetworkInterface::isPrimaryInterface() const
{
#if defined(__ppc__)
IOService * provider = getController();
bool isPrimary = false;
if ( provider ) provider = provider->getProvider();
if ( provider && provider->getProperty( "built-in" ) )
{
isPrimary = true;
}
return isPrimary;
#else
return false;
#endif
}
IONetworkController * IONetworkInterface::getController() const
{
return _controller;
}
static UInt32 getIfnetHardwareAssistValue( IONetworkController * ctr )
{
UInt32 input;
UInt32 output;
UInt32 hwassist = 0;
do {
if ( ctr->getChecksumSupport(
&input,
IONetworkController::kChecksumFamilyTCPIP,
false ) != kIOReturnSuccess ) break;
if ( ctr->getChecksumSupport(
&output,
IONetworkController::kChecksumFamilyTCPIP,
true ) != kIOReturnSuccess ) break;
if ( input & output & IONetworkController::kChecksumIP )
{
hwassist |= CSUM_IP;
}
if ( ( input & ( IONetworkController::kChecksumTCP |
IONetworkController::kChecksumTCPNoPseudoHeader ) )
&& ( output & ( IONetworkController::kChecksumTCP ) ) )
{
hwassist |= CSUM_TCP;
}
if ( ( input & ( IONetworkController::kChecksumUDP |
IONetworkController::kChecksumUDPNoPseudoHeader ) )
&& ( output & ( IONetworkController::kChecksumUDP ) ) )
{
hwassist |= CSUM_UDP;
}
if ( input & output & IONetworkController::kChecksumTCPSum16 )
{
hwassist |= ( CSUM_TCP_SUM16 | CSUM_TCP | CSUM_UDP );
}
}
while ( false );
return hwassist;
}
bool IONetworkInterface::initIfnet(struct ifnet * ifp)
{
UInt32 hwassist = getIfnetHardwareAssistValue( getController() );
ifp->if_private = this;
ifp->if_output = output_shim;
ifp->if_set_bpf_tap = set_bpf_tap_shim;
ifp->if_name = (char *) getNamePrefix();
ifp->if_hwassist = hwassist;
return true;
}
bool IONetworkInterface::matchPropertyTable(OSDictionary * table,
SInt32 * score)
{
return super::matchPropertyTable(table, score);
}
void IONetworkInterface::lock()
{
IORecursiveLockLock(_ifLock);
}
void IONetworkInterface::unlock()
{
IORecursiveLockUnlock(_ifLock);
}
bool IONetworkInterface::controllerDidOpen(IONetworkController * controller)
{
return true; }
void IONetworkInterface::controllerWillClose(IONetworkController * controller)
{
}
bool IONetworkInterface::handleOpen(IOService * client,
IOOptionBits options,
void * argument)
{
bool accept = false;
bool controllerOpen = false;
do {
if ( _clientSet->containsObject(client) )
{
DLOG("%s: multiple opens from client %lx\n",
getName(), (UInt32) client);
accept = true;
break;
}
if ( ( getInterfaceState() & kIONetworkInterfaceOpenedState ) == 0 )
{
if ( ( (controllerOpen = _controller->open(this)) == false ) ||
( controllerDidOpen(_controller) == false ) )
break;
}
if ( handleClientOpen(client, options, argument) == false )
break;
if ( _clientSet->setObject(client) == false )
{
handleClientClose(client, 0);
break;
}
accept = true;
}
while (false);
if ( controllerOpen )
{
if (accept)
{
setInterfaceState( kIONetworkInterfaceOpenedState );
_controller->registerInterestedDriver( this );
}
else {
controllerWillClose(_controller);
_controller->close(this);
}
}
return accept;
}
void IONetworkInterface::handleClose(IOService * client, IOOptionBits options)
{
if ( _clientSet->containsObject(client) )
{
handleClientClose( client, options );
if ( _clientSet->getCount() == 1 )
{
_controller->deRegisterInterestedDriver( this );
controllerWillClose( _controller );
_controller->close( this );
setInterfaceState( 0, kIONetworkInterfaceOpenedState );
}
_clientSet->removeObject(client);
}
}
bool IONetworkInterface::handleIsOpen(const IOService * client) const
{
if (client)
return _clientSet->containsObject(client);
else
return (_clientSet->getCount() > 0);
}
bool IONetworkInterface::handleClientOpen(IOService * client,
IOOptionBits options,
void * argument)
{
return true;
}
void IONetworkInterface::handleClientClose(IOService * client,
IOOptionBits options)
{
}
bool IONetworkInterface::registerOutputHandler(OSObject * target,
IOOutputAction action)
{
lock();
if ( (getInterfaceState() & kIONetworkInterfaceOpenedState) ||
!target || !action )
{
unlock();
return false;
}
_outTarget = target;
_outAction = action;
unlock();
return true;
}
static inline void _feedPacketTap(struct ifnet * ifp,
struct mbuf * m,
BPF_FUNC func,
int mode)
{
if (func) func(ifp, m);
}
#define IN_Q_ENQUEUE(m) \
{ \
if (_inputQHead == 0) { \
_inputQHead = _inputQTail = (m); \
} \
else { \
_inputQTail->m_nextpkt = (m) ; \
_inputQTail = (m); \
} \
_inputQCount++; \
}
#define DLIL_INPUT(m_head, m_tail) \
{ \
if ( m_head ) { \
dlil_input(_ifp, (m_head), (m_tail)); \
} \
}
UInt32 IONetworkInterface::flushInputQueue()
{
UInt32 count = _inputQCount;
DLIL_INPUT(_inputQHead, _inputQTail);
_inputQHead = _inputQTail = 0;
_inputQCount = 0;
return count;
}
UInt32 IONetworkInterface::clearInputQueue()
{
UInt32 count = _inputQCount;
m_freem_list( _inputQHead );
_inputQHead = _inputQTail = 0;
_inputQCount = 0;
return count;
}
UInt32 IONetworkInterface::inputPacket(struct mbuf * pkt,
UInt32 length,
IOOptionBits options,
void * param)
{
UInt32 count;
assert(pkt);
if ( length )
{
if ( pkt->m_next == 0 )
{
pkt->m_pkthdr.len = pkt->m_len = length;
}
else
{
struct mbuf * m = pkt;
pkt->m_pkthdr.len = length;
do {
if (length < (UInt32) m->m_len)
m->m_len = length;
length -= m->m_len;
} while (( m = m->m_next ));
assert(length == 0);
}
}
pkt->m_pkthdr.rcvif = _ifp;
_ifp->if_ibytes += pkt->m_pkthdr.len;
_feedPacketTap(_ifp, pkt, _inputFilterFunc, BPF_TAP_INPUT);
pkt->m_pkthdr.header = pkt->m_data;
pkt->m_pkthdr.len -= _ifp->if_hdrlen;
pkt->m_len -= _ifp->if_hdrlen;
pkt->m_data += _ifp->if_hdrlen;
if ( options & kInputOptionQueuePacket )
{
IN_Q_ENQUEUE(pkt);
count = 0;
}
else
{
if ( _inputQHead ) {
IN_Q_ENQUEUE(pkt);
count = _inputQCount;
DLIL_INPUT(_inputQHead, _inputQTail);
_inputQHead = _inputQTail = 0;
_inputQCount = 0;
}
else
{
DLIL_INPUT(pkt, pkt);
count = 1;
}
}
return count;
}
bool IONetworkInterface::inputEvent(UInt32 type, void * data)
{
bool success = true;
boolean_t funnel_state;
struct {
kern_event_msg header;
u_long unit;
char if_name[IFNAMSIZ];
} event;
switch (type)
{
case kIONetworkEventTypeLinkUp:
case kIONetworkEventTypeLinkDown:
bzero((void *) &event, sizeof(event));
funnel_state = thread_funnel_set( network_flock, TRUE );
if ( _ifp->refcnt )
{
event.header.total_size = sizeof(event);
event.header.vendor_code = KEV_VENDOR_APPLE;
event.header.kev_class = KEV_NETWORK_CLASS;
event.header.kev_subclass = KEV_DL_SUBCLASS;
event.header.event_code = (type == kIONetworkEventTypeLinkUp) ?
KEV_DL_LINK_ON : KEV_DL_LINK_OFF;
event.header.event_data[0] = _ifp->if_family;
event.unit = (u_long) _ifp->if_unit;
strncpy(&event.if_name[0], _ifp->if_name, IFNAMSIZ);
dlil_event(_ifp, &event.header);
}
thread_funnel_set( network_flock, funnel_state );
break;
case kIONetworkEventTypeDLIL:
dlil_event(_ifp, (struct kern_event_msg *) data);
break;
default:
IOLog("IONetworkInterface: unknown event type %lx\n", type);
success = false;
break;
}
return success;
}
SInt32 IONetworkInterface::syncSIOCSIFMTU(IONetworkController * ctr,
struct ifreq * ifr)
{
SInt32 error;
UInt32 newMTU = ifr->ifr_mtu;
if ( getMaxTransferUnit() == newMTU )
return 0;
error = errnoFromReturn( ctr->setMaxPacketSize(newMTU) );
if ( error == 0 )
{
setMaxTransferUnit(newMTU);
}
return error;
}
SInt32 IONetworkInterface::syncSIOCSIFMEDIA(IONetworkController * ctr,
struct ifreq * ifr)
{
OSDictionary * mediumDict;
IONetworkMedium * medium;
SInt32 error;
mediumDict = ctr->copyMediumDictionary(); if ( mediumDict == 0 )
{
return EOPNOTSUPP;
}
do {
medium = IONetworkMedium::getMediumWithType(
mediumDict,
ifr->ifr_media );
if ( medium ) break;
OSSymbol * selMediumName = (OSSymbol *)
ctr->copyProperty( kIOSelectedMedium );
if ( selMediumName )
{
UInt32 modMediumBits;
IONetworkMedium * selMedium = (IONetworkMedium *)
mediumDict->getObject( selMediumName );
if ( selMedium &&
(modMediumBits = selMedium->getType() ^ ifr->ifr_media) )
{
medium = IONetworkMedium::getMediumWithType(
mediumDict,
ifr->ifr_media,
~( IFM_TMASK | IFM_NMASK | modMediumBits));
}
selMediumName->release();
}
if ( medium ) break;
medium = IONetworkMedium::getMediumWithType(
mediumDict,
ifr->ifr_media,
~( IFM_TMASK | IFM_NMASK ));
} while ( false );
if ( medium )
error = errnoFromReturn(
ctr->selectMediumWithName(medium->getName()) );
else
error = EINVAL;
mediumDict->release();
return error;
}
SInt32 IONetworkInterface::syncSIOCGIFMEDIA(IONetworkController * ctr,
struct ifreq * ifr)
{
OSDictionary * mediumDict = 0;
UInt mediumCount = 0;
UInt maxCount;
OSCollectionIterator * iter = 0;
UInt32 * typeList;
UInt typeListSize;
OSSymbol * keyObject;
SInt32 error = 0;
struct ifmediareq * ifmr = (struct ifmediareq *) ifr;
maxCount = ifmr->ifm_count;
do {
mediumDict = ctr->copyMediumDictionary(); if (mediumDict == 0)
{
break; }
if ((mediumCount = mediumDict->getCount()) == 0)
break;
if (maxCount == 0)
break;
if (maxCount < mediumCount)
{
error = E2BIG;
}
iter = OSCollectionIterator::withCollection(mediumDict);
if (!iter)
{
error = ENOMEM;
break;
}
typeListSize = maxCount * sizeof(UInt32);
typeList = (UInt32 *) IOMalloc(typeListSize);
if (!typeList)
{
error = ENOMEM;
break;
}
bzero(typeList, typeListSize);
mediumCount = 0;
while ( (keyObject = (OSSymbol *) iter->getNextObject()) &&
(mediumCount < maxCount) )
{
IONetworkMedium * medium = (IONetworkMedium *)
mediumDict->getObject(keyObject);
if (!medium)
continue;
typeList[mediumCount++] = medium->getType();
}
if (mediumCount)
{
error = copyout((caddr_t) typeList,
(caddr_t) ifmr->ifm_ulist,
typeListSize);
}
IOFree(typeList, typeListSize);
}
while (0);
ifmr->ifm_active = ifmr->ifm_current = IFM_NONE;
ifmr->ifm_status = 0;
ifmr->ifm_count = mediumCount;
OSDictionary * pTable = ctr->dictionaryWithProperties();
if (pTable)
{
OSNumber * linkStatus = (OSNumber *)
pTable->getObject(kIOLinkStatus);
if (linkStatus)
ifmr->ifm_status = linkStatus->unsigned32BitValue();
if (mediumDict)
{
IONetworkMedium * medium;
OSSymbol * mediumName;
if ((mediumName = (OSSymbol *) pTable->getObject(kIOSelectedMedium))
&& (medium = (IONetworkMedium *)
mediumDict->getObject(mediumName)))
{
ifmr->ifm_current = medium->getType();
}
if ((mediumName = (OSSymbol *) pTable->getObject(kIOActiveMedium))
&& (medium = (IONetworkMedium *)
mediumDict->getObject(mediumName)))
{
ifmr->ifm_active = medium->getType();
}
}
pTable->release();
}
if (iter)
iter->release();
if (mediumDict)
mediumDict->release();
return error;
}
SInt32 IONetworkInterface::performCommand(IONetworkController * ctr,
UInt32 cmd,
void * arg0,
void * arg1)
{
struct ifreq * ifr = (struct ifreq *) arg1;
SInt32 ret = EOPNOTSUPP;
if ( (ifr == 0) || (ctr == 0) )
return EINVAL;
switch ( cmd )
{
case SIOCGIFMTU:
ifr->ifr_mtu = getMaxTransferUnit();
ret = 0; break;
case SIOCGIFMEDIA:
ret = syncSIOCGIFMEDIA(ctr, ifr);
break;
case SIOCSIFMTU:
case SIOCSIFMEDIA:
case SIOCSIFLLADDR:
ret = (int) ctr->executeCommand(
this,
(IONetworkController::Action)
&IONetworkInterface::performGatedCommand,
this,
ctr,
(void *) cmd,
arg0,
arg1 );
break;
default:
break;
}
return ret;
}
int IONetworkInterface::performGatedCommand(void * target,
void * arg1_ctr,
void * arg2_cmd,
void * arg3_0,
void * arg4_1)
{
IONetworkInterface * self = (IONetworkInterface *) target;
IONetworkController * ctr = (IONetworkController *) arg1_ctr;
struct ifreq * ifr = (struct ifreq *) arg4_1;
SInt32 ret = EOPNOTSUPP;
if ( self->getInterfaceState() & kIONetworkInterfaceDisabledState )
return EPWROFF;
switch ( (UInt32) arg2_cmd )
{
case SIOCSIFMTU:
ret = self->syncSIOCSIFMTU(ctr, ifr);
break;
case SIOCSIFMEDIA:
ret = self->syncSIOCSIFMEDIA(ctr, ifr);
break;
case SIOCSIFLLADDR:
ret = ctr->errnoFromReturn(
ctr->setHardwareAddress( ifr->ifr_addr.sa_data,
ifr->ifr_addr.sa_len ) );
break;
}
return ret;
}
int
IONetworkInterface::ioctl_shim(struct ifnet * ifp, u_long cmd, void * data)
{
assert(ifp && ifp->if_private);
IONetworkInterface * self = (IONetworkInterface *) ifp->if_private;
assert(ifp == self->_ifp);
return self->performCommand( self->_controller,
cmd,
(void *) ifp,
data );
}
int IONetworkInterface::output_shim(struct ifnet * ifp, struct mbuf * m)
{
assert(ifp && ifp->if_private);
IONetworkInterface * self = (IONetworkInterface *) ifp->if_private;
assert(ifp == self->_ifp);
if ( m == 0 )
{
DLOG("IONetworkInterface: NULL output mbuf\n");
return EINVAL;
}
if ( (m->m_flags & M_PKTHDR) == 0 )
{
DLOG("IONetworkInterface: M_PKTHDR bit not set\n");
m_freem(m);
return EINVAL;
}
ifp->if_obytes += m->m_pkthdr.len;
_feedPacketTap(ifp, m, self->_outputFilterFunc, BPF_TAP_OUTPUT);
return ((self->_outTarget)->*(self->_outAction))(m, 0);
}
int IONetworkInterface::set_bpf_tap_shim(struct ifnet * ifp,
int mode,
BPF_FUNC func)
{
assert(ifp && ifp->if_private);
IONetworkInterface * self = (IONetworkInterface *) ifp->if_private;
assert(ifp == self->_ifp);
switch ( mode )
{
case BPF_TAP_DISABLE:
self->_inputFilterFunc = self->_outputFilterFunc = 0;
break;
case BPF_TAP_INPUT:
assert(func);
self->_inputFilterFunc = func;
break;
case BPF_TAP_OUTPUT:
assert(func);
self->_outputFilterFunc = func;
break;
case BPF_TAP_INPUT_OUTPUT:
assert(func);
self->_inputFilterFunc = self->_outputFilterFunc = func;
break;
default:
DLOG("IONetworkInterface: Unknown BPF tap mode %d\n", mode);
break;
}
return 0;
}
void IONetworkInterface::null_shim(struct ifnet * )
{
IOLog("IONetworkInterface::null_shim called!\n");
}
bool IONetworkInterface::_setInterfaceProperty(UInt32 value,
UInt32 mask,
UInt32 bytes,
void * addr,
char * key)
{
bool updateOk = true;
UInt32 newValue;
switch (bytes)
{
case 1:
newValue = (*((UInt8 *) addr) & mask) | value;
*((UInt8 *) addr) = (UInt8) newValue;
break;
case 2:
newValue = (*((UInt16 *) addr) & mask) | value;
*((UInt16 *) addr) = (UInt16) newValue;
break;
case 4:
newValue = (*((UInt32 *) addr) & mask) | value;
*((UInt32 *) addr) = (UInt32) newValue;
break;
default:
updateOk = false;
break;
}
return updateOk;
}
#define IO_IFNET_GET(func, type, field) \
type IONetworkInterface::func() const \
{ \
return ( _ifp ? _ifp->field : 0 ); \
}
#define IO_IFNET_SET(func, type, field, propName) \
bool IONetworkInterface::func(type value) \
{ \
return _setInterfaceProperty( \
(UInt32) value, \
0, \
sizeof(type), \
(void *) &_ifp->field, \
propName); \
}
#define IO_IFNET_RMW(func, type, field, propName) \
bool IONetworkInterface::func(type value, type clear) \
{ \
return _setInterfaceProperty( \
(UInt32) value, \
(UInt32) ~clear, \
sizeof(type), \
(void *) &_ifp->field, \
propName); \
}
IO_IFNET_SET(setInterfaceType, UInt8, if_type, kIOInterfaceType)
IO_IFNET_GET(getInterfaceType, UInt8, if_type)
IO_IFNET_SET(setMaxTransferUnit, UInt32, if_mtu, kIOMaxTransferUnit)
IO_IFNET_GET(getMaxTransferUnit, UInt32, if_mtu)
IO_IFNET_RMW(setFlags, UInt16, if_flags, kIOInterfaceFlags)
IO_IFNET_GET(getFlags, UInt16, if_flags)
IO_IFNET_RMW(setExtraFlags, UInt32, if_eflags, kIOInterfaceExtraFlags)
IO_IFNET_GET(getExtraFlags, UInt32, if_eflags)
IO_IFNET_SET(setMediaAddressLength, UInt8, if_addrlen, kIOMediaAddressLength)
IO_IFNET_GET(getMediaAddressLength, UInt8, if_addrlen)
IO_IFNET_SET(setMediaHeaderLength, UInt8, if_hdrlen, kIOMediaHeaderLength)
IO_IFNET_GET(getMediaHeaderLength, UInt8, if_hdrlen)
bool IONetworkInterface::setUnitNumber( UInt16 value )
{
if ( setProperty( kIOInterfaceUnit, value, 16 ) )
{
_ifp->if_unit = value;
return true;
}
else
return false;
}
IO_IFNET_GET(getUnitNumber, UInt16, if_unit)
bool IONetworkInterface::isRegistered() const
{
return (bool)(getInterfaceState() & kIONetworkInterfaceRegisteredState);
}
bool IONetworkInterface::serializeProperties( OSSerialize * s ) const
{
IONetworkInterface * self = (IONetworkInterface *) this;
self->setProperty( kIOInterfaceType, getInterfaceType(), 8 );
self->setProperty( kIOMaxTransferUnit, getMaxTransferUnit(), 32 );
self->setProperty( kIOInterfaceFlags, getFlags(), 16 );
self->setProperty( kIOInterfaceExtraFlags, getExtraFlags(), 32 );
self->setProperty( kIOMediaAddressLength, getMediaAddressLength(), 8 );
self->setProperty( kIOMediaHeaderLength, getMediaHeaderLength(), 8 );
return super::serializeProperties( s );
}
UInt32 IONetworkInterface::getInterfaceState() const
{
return _stateBits->unsigned32BitValue();
}
UInt32 IONetworkInterface::setInterfaceState( UInt32 set,
UInt32 clear )
{
UInt32 val;
assert( _stateBits );
lock();
val = ( _stateBits->unsigned32BitValue() | set ) & ~clear;
_stateBits->setValue( val );
unlock();
return val;
}
IONetworkData * IONetworkInterface::getNetworkData(const OSSymbol * key) const
{
return OSDynamicCast(IONetworkData, _dataDict->getObject(key));
}
IONetworkData * IONetworkInterface::getNetworkData(const char * key) const
{
return OSDynamicCast(IONetworkData, _dataDict->getObject(key));
}
bool IONetworkInterface::_syncNetworkDataDict()
{
OSDictionary * aCopy = OSDictionary::withDictionary(_dataDict);
bool ret = false;
if (aCopy) {
ret = setProperty(kIONetworkData, aCopy);
aCopy->release();
}
return ret;
}
bool IONetworkInterface::removeNetworkData(const OSSymbol * aKey)
{
bool ret = false;
lock();
do {
if ( getInterfaceState() & kIONetworkInterfaceOpenedState )
break;
_dataDict->removeObject(aKey);
ret = _syncNetworkDataDict();
}
while (0);
unlock();
return ret;
}
bool IONetworkInterface::removeNetworkData(const char * aKey)
{
bool ret = false;
lock();
do {
if ( getInterfaceState() & kIONetworkInterfaceOpenedState )
break;
_dataDict->removeObject(aKey);
ret = _syncNetworkDataDict();
}
while (0);
unlock();
return ret;
}
bool IONetworkInterface::addNetworkData(IONetworkData * aData)
{
bool ret = false;
if (OSDynamicCast(IONetworkData, aData) == 0)
return false;
lock();
if (( getInterfaceState() & kIONetworkInterfaceOpenedState ) == 0)
{
if ((ret = _dataDict->setObject(aData->getKey(), aData)))
ret = _syncNetworkDataDict();
}
unlock();
return ret;
}
IOReturn IONetworkInterface::newUserClient(task_t owningTask,
void * ,
UInt32 type,
IOUserClient ** handler)
{
IOReturn err = kIOReturnSuccess;
IONetworkUserClient * client;
if (type != kIONUCType)
return kIOReturnBadArgument;
client = IONetworkUserClient::withTask(owningTask);
if (!client || !client->attach(this) || !client->start(this))
{
if (client)
{
client->detach(this);
client->release();
client = 0;
}
err = kIOReturnNoMemory;
}
*handler = client;
return err;
}
struct IONetworkPowerChangeNotice {
queue_chain_t link;
IOPMPowerFlags powerFlags;
UInt32 stateNumber;
IOService * policyMaker;
UInt32 phase;
};
enum {
kPhasePowerStateWillChange = 1,
kPhasePowerStateDidChange
};
#define kMaxAckDelayUS (10 * 1000 * 1000)
IOReturn
IONetworkInterface::controllerWillChangePowerState(
IONetworkController * controller,
IOPMPowerFlags flags,
UInt32 stateNumber,
IOService * policyMaker )
{
if ( ( flags & IOPMDeviceUsable ) == 0 )
{
setInterfaceState( kIONetworkInterfaceDisabledState );
}
return kIOReturnSuccess;
}
IOReturn
IONetworkInterface::controllerDidChangePowerState(
IONetworkController * controller,
IOPMPowerFlags flags,
UInt32 stateNumber,
IOService * policyMaker )
{
if ( flags & IOPMDeviceUsable )
{
setInterfaceState( 0, kIONetworkInterfaceDisabledState );
}
return kIOReturnSuccess;
}
void
IONetworkInterface::powerChangeHandler( void * param0,
void * param1,
void * param2,
void * param3,
void * param4 )
{
IONetworkInterface * self = (IONetworkInterface *) param0;
IONetworkPowerChangeNotice * notice;
IOService * policyMaker = 0;
assert( self );
if ( param1 == 0 )
{
self->getController()->executeCommand(
self,
(IONetworkController::Action) powerChangeHandler,
self,
(void *) 1 );
return;
}
do {
notice = 0;
IOSimpleLockLock( self->_powerChangeNoticeLock );
if ( queue_empty( &self->_powerChangeNoticeList ) == false )
{
queue_remove_first( &self->_powerChangeNoticeList,
notice,
IONetworkPowerChangeNotice *,
link );
}
IOSimpleLockUnlock( self->_powerChangeNoticeLock );
if ( notice )
{
DLOG("%s: power change fl:%08lx, st:%ld, pm:%p, ph:%ld\n",
self->getName(), notice->powerFlags, notice->stateNumber,
notice->policyMaker, notice->phase );
if ( notice->phase == kPhasePowerStateWillChange )
{
self->controllerWillChangePowerState(
self->getController(),
notice->powerFlags,
notice->stateNumber,
notice->policyMaker );
}
else if ( notice->phase == kPhasePowerStateDidChange )
{
self->controllerDidChangePowerState(
self->getController(),
notice->powerFlags,
notice->stateNumber,
notice->policyMaker );
}
else
{
IOPanic("Invalid power change phase\n");
}
policyMaker = notice->policyMaker;
IODelete( notice, IONetworkPowerChangeNotice, 1 );
}
}
while ( notice );
if ( policyMaker )
{
policyMaker->acknowledgePowerChange( self );
}
assert( self->getRetainCount() > 0 );
self->release();
}
IOReturn
IONetworkInterface::powerStateWillChangeTo( IOPMPowerFlags powerFlags,
UInt32 stateNumber,
IOService * policyMaker )
{
IONetworkPowerChangeNotice * notice;
notice = IONew( IONetworkPowerChangeNotice, 1 );
if ( notice == 0 )
return kIOReturnNoMemory;
notice->powerFlags = powerFlags;
notice->stateNumber = stateNumber;
notice->policyMaker = policyMaker;
notice->phase = kPhasePowerStateWillChange;
IOSimpleLockLock( _powerChangeNoticeLock );
queue_enter( &_powerChangeNoticeList, notice, IONetworkPowerChangeNotice *, link );
IOSimpleLockUnlock( _powerChangeNoticeLock );
retain();
if ( thread_call_enter( _powerChangeThreadCall ) == TRUE )
{
release();
}
return kMaxAckDelayUS;
}
IOReturn
IONetworkInterface::powerStateDidChangeTo( IOPMPowerFlags powerFlags,
UInt32 stateNumber,
IOService * policyMaker )
{
IONetworkPowerChangeNotice * notice;
notice = IONew( IONetworkPowerChangeNotice, 1 );
if ( notice == 0 )
return kIOReturnNoMemory;
notice->powerFlags = powerFlags;
notice->stateNumber = stateNumber;
notice->policyMaker = policyMaker;
notice->phase = kPhasePowerStateDidChange;
IOSimpleLockLock( _powerChangeNoticeLock );
queue_enter( &_powerChangeNoticeList, notice, IONetworkPowerChangeNotice *, link );
IOSimpleLockUnlock( _powerChangeNoticeLock );
retain();
if ( thread_call_enter( _powerChangeThreadCall ) == TRUE )
{
release();
}
return kMaxAckDelayUS;
}
#define kIONetworkControllerProperties "IONetworkControllerProperties"
IOReturn IONetworkInterface::setProperties( OSObject * properties )
{
IOReturn ret;
OSDictionary * dict;
OSObject * obj;
if (( dict = OSDynamicCast( OSDictionary, properties ) ) &&
( obj = dict->getObject( kIONetworkControllerProperties ) ))
{
ret = _controller->setProperties( obj );
}
else
{
ret = super::setProperties(properties);
}
return ret;
}
bool IONetworkInterface::willTerminate( IOService * provider,
IOOptionBits options )
{
DLOG("%s::%s\n", getName(), __FUNCTION__);
setInterfaceState( kIONetworkInterfaceDisabledState );
return super::willTerminate( provider, options );
}
IONetworkData * IONetworkInterface::getParameter(const char * aKey) const
{ return getNetworkData(aKey); }
bool IONetworkInterface::setExtendedFlags(UInt32 flags, UInt32 clear)
{ return true; }
OSMetaClassDefineReservedUsed(IONetworkInterface, 0);
IOReturn IONetworkInterface::attachToDataLinkLayer( IOOptionBits options,
void * parameter )
{
dlil_if_attach( getIfnet() );
return kIOReturnSuccess;
}
OSMetaClassDefineReservedUsed(IONetworkInterface, 1);
void IONetworkInterface::detachFromDataLinkLayer( IOOptionBits options,
void * parameter )
{
dlil_if_detach( getIfnet() );
}