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 <net/if_dl.h>
#include <net/kpi_interface.h>
#include <net/ethernet.h>
#include <sys/kern_event.h>
#define _IP_VHL
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <sys/_types/_timeval32.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"
#include "IONetworkTypesPrivate.h"
#include "IONetworkControllerPrivate.h"
#include "IONetworkDebug.h"
#include "IOMbufQueue.h"
#include <TargetConditionals.h>
#define super IOService
OSDefineMetaClassAndAbstractStructors( IONetworkInterface, IOService )
OSMetaClassDefineReservedUsed( IONetworkInterface, 5);
OSMetaClassDefineReservedUsed( IONetworkInterface, 6);
OSMetaClassDefineReservedUsed( IONetworkInterface, 7);
OSMetaClassDefineReservedUsed( IONetworkInterface, 8);
OSMetaClassDefineReservedUsed( IONetworkInterface, 9);
OSMetaClassDefineReservedUsed( IONetworkInterface, 10);
OSMetaClassDefineReservedUnused( IONetworkInterface, 11);
OSMetaClassDefineReservedUnused( IONetworkInterface, 12);
OSMetaClassDefineReservedUnused( IONetworkInterface, 13);
OSMetaClassDefineReservedUnused( IONetworkInterface, 14);
OSMetaClassDefineReservedUnused( IONetworkInterface, 15);
#define IFNET_TO_THIS(x) ((IONetworkInterface *) ifnet_softc(ifp))
#define WAITING_FOR_DETACH(n) ((n)->_clientVar[1])
#define _unit _reserved->unit
#define _type _reserved->type
#define _mtu _reserved->mtu
#define _flags _reserved->flags
#define _eflags _reserved->eflags
#define _addrlen _reserved->addrlen
#define _hdrlen _reserved->hdrlen
#define _loggingLevel _reserved->loggingLevel
#define _outputQueueModel _reserved->outputQueueModel
#define _inputDeltas _reserved->inputDeltas
#define _driverStats _reserved->driverStats
#define _lastDriverStats _reserved->lastDriverStats
#define _publicLock _reserved->publicLock
#define _remote_NMI_pattern _reserved->remote_NMI_pattern
#define _remote_NMI_len _reserved->remote_NMI_len
#define _controller _reserved->controller
#define _configFlags _reserved->configFlags
#define _txRingSize _reserved->txRingSize
#define _txPullOptions _reserved->txPullOptions
#define _txQueueSize _reserved->txQueueSize
#define _txSchedulingModel _reserved->txSchedulingModel
#define _txTargetQdelay _reserved->txTargetQdelay
#define _txThreadState _reserved->txThreadState
#define _txThreadFlags _reserved->txThreadFlags
#define _txThreadSignal _reserved->txThreadSignal
#define _txThreadSignalLast _reserved->txThreadSignalLast
#define _txStartThread _reserved->txStartThread
#define _txStartAction _reserved->txStartAction
#define _txWorkLoop _reserved->txWorkLoop
#define _rxRingSize _reserved->rxRingSize
#define _rxPollOptions _reserved->rxPollOptions
#define _rxPollModel _reserved->rxPollModel
#define _rxPollAction _reserved->rxPollAction
#define _rxCtlAction _reserved->rxCtlAction
#define _rxPollEmpty _reserved->rxPollEmpty
#define _rxPollTotal _reserved->rxPollTotal
#define _peqHandler _reserved->peqHandler
#define _peqTarget _reserved->peqTarget
#define _peqRefcon _reserved->peqRefcon
#define _subType _reserved->subType
#define _txStartDelayQueueLength _reserved->txStartDelayQueueLength
#define _txStartDelayTimeout _reserved->txStartDelayTimeout
#define _clientBehavior _reserved->clientBehavior
#define _inputEventThreadCall _reserved->inputEventThreadCall
#define kRemoteNMI "remote_nmi"
#define REMOTE_NMI_PATTERN_LEN 32
#define kTxThreadStateInit 0x00000001 // initial state
#define kTxThreadStateStop 0x00000100 // temporarily disable
#define kTxThreadStateDetach 0x00000200 // permanently disable
#define kTxThreadStateHalted 0x00000800 // stop confirmation
#define kTxThreadStatePurge 0x00001000 // purge new packets
#define kTxThreadWakeupEnable 0x00000001 // enable ifnet_start call
#define kTxThreadWakeupSignal 0x00000002 // driver signaled
#define kTxThreadWakeupMask 0x00000003 // wakeup bits
enum {
kConfigFrozen = 0x01,
kConfigTxPull = 0x02,
kConfigRxPoll = 0x04,
kConfigDataRates = 0x08,
kConfigPreEnqueue = 0x10
};
bool IONetworkInterface::init( IONetworkController * controller )
{
IONetworkData * nd;
#if TARGET_OS_IPHONE
OSString * networkType;
#endif
if ( super::init() == false )
goto fail;
if (getIfnet())
{
IOLog("\33[00m\33[31m%s: IONetworkingFamily interface KPIs do not support IONetworkInterface subclasses that use ifnets\n\33[00m", getName());
goto fail;
}
if ( OSDynamicCast(IONetworkController, controller) == 0 )
goto fail;
_driver = controller;
_driver->retain();
_reserved = IONew( ExpansionData, 1 );
if ( _reserved == 0 )
goto fail;
bzero(_reserved, sizeof(ExpansionData));
_inputEventThreadCall = thread_call_allocate( handleNetworkInputEvent, this );
if ( !_inputEventThreadCall )
goto fail;
_privateLock = IOLockAlloc();
if ( _privateLock == 0)
goto fail;
_publicLock = IORecursiveLockAlloc();
if ( _publicLock == 0 )
goto fail;
_controller = controller;
_rxPollModel = IFNET_MODEL_INPUT_POLL_OFF;
_inputPushQueue = IONew(IOMbufQueue, 1);
bzero(_inputPushQueue, sizeof(*_inputPushQueue));
_txThreadState = kTxThreadStateInit | kTxThreadStateHalted;
_stateBits = OSNumber::withNumber((UInt64) 0, 32);
if ( _stateBits == 0 )
goto fail;
setProperty( kIOInterfaceState, _stateBits );
_clientSet = OSSet::withCapacity(2);
if ( _clientSet == 0 )
goto fail;
if ( (_dataDict = OSDictionary::withCapacity(5)) == 0 )
goto fail;
nd = IONetworkData::withExternalBuffer(
kIONetworkStatsKey,
sizeof(IONetworkStats),
&_driverStats);
if ( nd )
{
addNetworkData(nd);
nd->release();
}
if (!registerOutputHandler(controller, controller->getOutputHandler()))
goto fail;
setProperty( kIOInterfaceNamePrefix, getNamePrefix() );
#if TARGET_OS_IPHONE
networkType = OSDynamicCast(OSString, controller->getProperty( "IONetworkRootType" ));
if (networkType)
setProperty( "IONetworkRootType", networkType );
#endif
if (IOService *provider = controller->getProvider())
{
bool gotLocation = false;
OSData * locationAsData;
OSData * locationAsCstr;
OSString * locationAsString;
setProperty(kIOBuiltin, (bool)(provider->getProperty("built-in")));
if ((locationAsData = OSDynamicCast(OSData, provider->getProperty("location"))))
{
if ((locationAsCstr = OSData::withData(locationAsData)))
{
locationAsCstr->appendByte(0, 1);
if ((locationAsString = OSString::withCString(
(const char *) locationAsCstr->getBytesNoCopy())))
{
gotLocation = true;
setProperty(kIOLocation, locationAsString);
locationAsString->release(); }
locationAsCstr->release();
}
}
if (!gotLocation)
setProperty(kIOLocation, "");
}
DLOG("IONetworkInterface::init(%p, %s)\n", this, controller->getName());
return true;
fail:
LOG("IONetworkInterface::init(%p, %s) failed\n", this, controller->getName());
return false;
}
void IONetworkInterface::free( void )
{
DLOG("IONetworkInterface::free(%p)\n", this);
if (_driver)
{
_driver->release();
_driver = 0;
}
if ( _clientSet )
{
assert(_clientSet->getCount() == 0);
_clientSet->release();
_clientSet = 0;
}
if ( _dataDict )
{
_dataDict->release();
_dataDict = 0;
}
if ( _stateBits )
{
_stateBits->release();
_stateBits = 0;
}
if ( _inputPushQueue )
{
clearInputQueue();
IODelete(_inputPushQueue, IOMbufQueue, 1);
_inputPushQueue = 0;
}
if ( _privateLock )
{
IOLockFree(_privateLock);
_privateLock = 0;
}
if (_backingIfnet)
{
ifnet_release(_backingIfnet);
_backingIfnet = 0;
}
if ( _reserved )
{
if (_inputEventThreadCall)
{
thread_call_free(_inputEventThreadCall);
_inputEventThreadCall = 0;
}
if ( _publicLock )
{
IORecursiveLockFree(_publicLock);
_publicLock = 0;
}
if (_remote_NMI_pattern) {
IOFree(_remote_NMI_pattern, _remote_NMI_len);
}
if (_txWorkLoop)
_txWorkLoop->release();
memset(_reserved, 0, sizeof(ExpansionData));
IODelete(_reserved, ExpansionData, 1);
_reserved = 0;
}
super::free();
}
bool IONetworkInterface::isPrimaryInterface() const
{
IOService * provider = _driver;
bool isPrimary = false;
if ( provider ) provider = provider->getProvider();
if ( provider && provider->getProperty("built-in") && getUnitNumber() == 0)
{
isPrimary = true;
}
return isPrimary;
}
IONetworkController * IONetworkInterface::getController( void ) const
{
return _controller;
}
static UInt32 getIfnetHardwareAssistValue(
IONetworkController * driver )
{
UInt32 input;
UInt32 output;
UInt32 hwassist = 0;
UInt32 driverFeatures = driver->getFeatures();
do {
if ( driver->getChecksumSupport(
&input,
IONetworkController::kChecksumFamilyTCPIP,
false ) != kIOReturnSuccess ) break;
if ( driver->getChecksumSupport(
&output,
IONetworkController::kChecksumFamilyTCPIP,
true ) != kIOReturnSuccess ) break;
if ( input & output & IONetworkController::kChecksumIP )
{
hwassist |= IFNET_CSUM_IP;
}
if ( ( input & ( IONetworkController::kChecksumTCP |
IONetworkController::kChecksumTCPNoPseudoHeader ) )
&& ( output & ( IONetworkController::kChecksumTCP ) ) )
{
hwassist |= IFNET_CSUM_TCP;
}
if ( ( input & ( IONetworkController::kChecksumUDP |
IONetworkController::kChecksumUDPNoPseudoHeader ) )
&& ( output & ( IONetworkController::kChecksumUDP ) ) )
{
hwassist |= IFNET_CSUM_UDP;
}
if ( input & output & IONetworkController::kChecksumTCPSum16 )
{
hwassist |= ( IFNET_CSUM_SUM16 | IFNET_CSUM_TCP | IFNET_CSUM_UDP );
}
if ( input & output & IONetworkController::kChecksumTCPIPv6 )
{
hwassist |= IFNET_CSUM_TCPIPV6;
}
if ( input & output & IONetworkController::kChecksumUDPIPv6 )
{
hwassist |= IFNET_CSUM_UDPIPV6;
}
}
while ( false );
if( driverFeatures & kIONetworkFeatureHardwareVlan)
hwassist |= IFNET_VLAN_TAGGING;
if( driverFeatures & kIONetworkFeatureSoftwareVlan)
hwassist |= IFNET_VLAN_MTU;
if( driverFeatures & kIONetworkFeatureMultiPages)
hwassist |= IFNET_MULTIPAGES;
if( driverFeatures & kIONetworkFeatureTSOIPv4)
hwassist |= IFNET_TSO_IPV4;
if( driverFeatures & kIONetworkFeatureTSOIPv6)
hwassist |= IFNET_TSO_IPV6;
if (driverFeatures & kIONetworkFeatureTransmitCompletionStatus)
hwassist |= IFNET_TX_STATUS;
if (driverFeatures & kIONetworkFeatureHWTimeStamp)
hwassist |= IFNET_HW_TIMESTAMP;
if (driverFeatures & kIONetworkFeatureSWTimeStamp)
hwassist |= IFNET_SW_TIMESTAMP;
return hwassist;
}
bool IONetworkInterface::initIfnet( struct ifnet * ifp )
{
return false; }
OSMetaClassDefineReservedUsed(IONetworkInterface, 4);
bool IONetworkInterface::initIfnetParams( struct ifnet_init_params *params )
{
params->name = (char *) getNamePrefix();
params->type = _type;
params->unit = _unit;
params->output = if_output;
params->ioctl = if_ioctl;
params->set_bpf_tap = if_set_bpf_tap;
params->detach = if_detach;
params->softc = this;
return true;
}
IOReturn IONetworkInterface::configureOutputStartDelay(
uint16_t outputStartDelayQueueLength,
uint16_t outputStartDelayTimeout )
{
IOReturn ret = kIOReturnError;
IOLockLock(_privateLock);
if ((_configFlags & kConfigFrozen) == 0)
{
_txStartDelayQueueLength = outputStartDelayQueueLength;
_txStartDelayTimeout = outputStartDelayTimeout;
ret = kIOReturnSuccess;
}
IOLockUnlock(_privateLock);
return ret;
}
void IONetworkInterface::lock()
{
IORecursiveLockLock(_publicLock);
}
void IONetworkInterface::unlock()
{
IORecursiveLockUnlock(_publicLock);
}
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: rejected open from existing client %s\n",
getName(), client->getName());
accept = true;
break;
}
if (( getInterfaceState() & kIONetworkInterfaceOpenedState ) == 0)
{
if (!_driver ||
((controllerOpen = _driver->open(this)) == false) ||
(controllerDidOpen(_driver) == 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 );
_driver->registerInterestedDriver( this );
}
else
{
controllerWillClose(_driver);
_driver->close(this);
}
}
return accept;
}
void IONetworkInterface::handleClose( IOService * client, IOOptionBits options )
{
if ( _clientSet->containsObject(client) )
{
handleClientClose( client, options );
if ( _clientSet->getCount() == 1 )
{
_driver->deRegisterInterestedDriver( this );
controllerWillClose( _driver );
_driver->close( this );
setInterfaceState( 0, kIONetworkInterfaceOpenedState );
if (!isRegistered())
{
_driver->release();
_driver = 0;
}
}
_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)
{
IOLockLock(_privateLock);
if ( (getInterfaceState() & kIONetworkInterfaceOpenedState) ||
!target || !action )
{
IOLockUnlock(_privateLock);
return false;
}
_outTarget = target;
_outAction = action;
IOLockUnlock(_privateLock);
return true;
}
OSMetaClassDefineReservedUsed(IONetworkInterface, 2);
void IONetworkInterface::feedPacketInputTap(mbuf_t m)
{
bpf_packet_func inFilter = _inputFilterFunc;
if (inFilter)
inFilter(_backingIfnet, m);
}
OSMetaClassDefineReservedUsed(IONetworkInterface, 3);
void IONetworkInterface::feedPacketOutputTap(mbuf_t m)
{
bpf_packet_func outFilter = _outputFilterFunc;
if (outFilter)
outFilter(_backingIfnet, m);
}
#define ABS(a) ((a) < 0 ? -(a) : (a))
void IONetworkInterface::pushInputQueue( IOMbufQueue * queue )
{
uint32_t temp;
int delta;
_inputDeltas.packets_in = queue->count;
_inputDeltas.bytes_in = queue->bytes;
temp = _driverStats.inputErrors;
delta = temp - _lastDriverStats.inputErrors;
_inputDeltas.errors_in = ABS(delta);
_lastDriverStats.inputErrors = temp;
temp = _driverStats.collisions;
delta = temp - _lastDriverStats.collisions;
_inputDeltas.collisions = ABS(delta);
_lastDriverStats.collisions = temp;
ifnet_input_extended(_backingIfnet, queue->head, queue->tail, &_inputDeltas);
IOMbufQueueInit(queue);
}
void IONetworkInterface::pushInputPacket( mbuf_t packet, uint32_t length )
{
uint32_t temp;
int delta;
_inputDeltas.packets_in = 1;
_inputDeltas.bytes_in = length;
temp = _driverStats.inputErrors;
delta = temp - _lastDriverStats.inputErrors;
_inputDeltas.errors_in = ABS(delta);
_lastDriverStats.inputErrors = temp;
temp = _driverStats.collisions;
delta = temp - _lastDriverStats.collisions;
_inputDeltas.collisions = ABS(delta);
_lastDriverStats.collisions = temp;
ifnet_input_extended(_backingIfnet, packet, packet, &_inputDeltas);
}
UInt32 IONetworkInterface::flushInputQueue( void )
{
UInt32 count = _inputPushQueue->count;
if (count)
{
pushInputQueue(_inputPushQueue);
}
return count;
}
UInt32 IONetworkInterface::clearInputQueue( void )
{
UInt32 count = _inputPushQueue->count;
if (count)
mbuf_freem_list(_inputPushQueue->head);
IOMbufQueueInit(_inputPushQueue);
return count;
}
inline static void checkForRNMIPacket(mbuf_t pkt, int hdrlen, int datalen, const char* pattern)
{
if (sizeof(struct ether_header) != hdrlen)
return;
if (mbuf_len(pkt) < sizeof(struct ether_header))
return;
struct ether_header etherHeader;
if (mbuf_copydata(pkt, 0, sizeof(struct ether_header), ðerHeader) != 0)
return;
if (ntohs(etherHeader.ether_type) != ETHERTYPE_IP)
return;
if (mbuf_len(pkt) < sizeof(struct ether_header) + sizeof(struct ip))
return;
struct ip ipHeader;
if (mbuf_copydata(pkt, sizeof(struct ether_header), sizeof(struct ip), &ipHeader) != 0)
return;
if (IP_VHL_V(ipHeader.ip_vhl) != IPVERSION)
return;
int ipHeaderLen = IP_VHL_HL(ipHeader.ip_vhl) << 2;
if (ipHeaderLen != sizeof(struct ip))
return;
if (mbuf_len(pkt) < sizeof(struct ether_header) + ipHeaderLen + sizeof(struct icmp) + sizeof(struct timeval32) + datalen)
return;
struct icmp icmpHeader;
if (mbuf_copydata(pkt, sizeof(struct ether_header) + ipHeaderLen, sizeof(struct icmp), &icmpHeader) != 0)
return;
if (icmpHeader.icmp_type != ICMP_ECHO)
return;
if (datalen > (REMOTE_NMI_PATTERN_LEN >> 1))
return;
char icmpPayload[(REMOTE_NMI_PATTERN_LEN >> 1) + 1];
memset(icmpPayload, 0, sizeof(icmpPayload));
if (mbuf_copydata(pkt, sizeof(struct ether_header) + ipHeaderLen + sizeof(struct icmp) + sizeof(struct timeval32), datalen, icmpPayload) != 0)
return;
if (memcmp(icmpPayload, pattern, datalen) == 0)
IOKernelDebugger::signalDebugger();
}
UInt32 IONetworkInterface::inputPacket( mbuf_t packet,
UInt32 length,
IOOptionBits options,
void * param )
{
const UInt32 hdrlen = _hdrlen;
UInt32 count;
void * mdata;
assert(packet);
assert(_backingIfnet);
if (!packet || !_backingIfnet)
return 0;
assert((mbuf_flags(packet) & MBUF_PKTHDR));
assert((mbuf_nextpkt(packet) == 0));
mbuf_pkthdr_setrcvif(packet, _backingIfnet);
if (length)
{
if (mbuf_next(packet) == 0)
{
mbuf_pkthdr_setlen(packet, length);
mbuf_setlen(packet, length);
}
else
{
mbuf_t m = packet;
uint32_t remain = length;
mbuf_pkthdr_setlen(packet, remain);
do {
if (remain < (UInt32) mbuf_len(m))
mbuf_setlen(m, remain);
remain -= mbuf_len(m);
} while ((m = mbuf_next(m)));
assert(remain == 0);
}
}
else
{
length = (UInt32)mbuf_pkthdr_len(packet);
}
if (_remote_NMI_len)
checkForRNMIPacket(packet, hdrlen, _remote_NMI_len, _remote_NMI_pattern);
if (_inputFilterFunc)
feedPacketInputTap(packet);
mdata = mbuf_data(packet);
mbuf_pkthdr_setheader(packet, mdata);
mbuf_pkthdr_setlen(packet, length - hdrlen);
mbuf_setdata(packet, (char *)mdata + hdrlen, mbuf_len(packet) - hdrlen);
if ( options & kInputOptionQueuePacket )
{
IOMbufQueueTailAdd(_inputPushQueue, packet, length);
count = 0;
}
else
{
if (!IOMbufQueueIsEmpty(_inputPushQueue))
{
IOMbufQueueTailAdd(_inputPushQueue, packet, length);
count = _inputPushQueue->count;
pushInputQueue(_inputPushQueue);
}
else
{
pushInputPacket(packet, length);
count = 1;
}
}
return count;
}
bool IONetworkInterface::inputEvent(UInt32 type, void * data)
{
bool success = true;
struct {
kern_event_msg header;
uint32_t unit;
char if_name[IFNAMSIZ];
} event;
const IONetworkLinkEventData * linkData;
switch (type)
{
case kIONetworkEventTypeLinkUp:
case kIONetworkEventTypeLinkDown:
case kIONetworkEventTypeLinkSpeedChange:
linkData = (const IONetworkLinkEventData *) data;
if (!_backingIfnet || !linkData)
break;
if ((_configFlags & kConfigDataRates) == 0)
{
if_bandwidths_t bw;
bw.max_bw = linkData->linkSpeed;
bw.eff_bw = linkData->linkSpeed;
ifnet_set_bandwidths(_backingIfnet, &bw, &bw);
}
if ((type == kIONetworkEventTypeLinkUp) ||
(type == kIONetworkEventTypeLinkDown))
{
bzero(&event, sizeof(event));
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] = ifnet_family(_backingIfnet);
event.unit = ifnet_unit(_backingIfnet);
strncpy(&event.if_name[0], ifnet_name(_backingIfnet), IFNAMSIZ);
ifnet_event(_backingIfnet, &event.header);
retain();
if (thread_call_enter1(_inputEventThreadCall, (thread_call_param_t)(uintptr_t)type) == TRUE)
{
release();
}
}
break;
case kIONetworkEventTypeDLIL:
ifnet_event(_backingIfnet, (struct kern_event_msg *) data);
break;
case kIONetworkEventWakeOnLANSupportChanged:
break;
default:
IOLog("IONetworkInterface: unknown event type %x\n", (uint32_t) 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,
unsigned long cmd)
{
OSDictionary * mediumDict = 0;
UInt mediumCount = 0;
UInt maxCount;
OSCollectionIterator * iter = 0;
UInt32 * typeList;
OSSymbol * keyObject;
SInt32 error = 0;
struct ifmediareq * ifmr = (struct ifmediareq *) ifr;
if (ifmr->ifm_count < 0)
return EINVAL;
do {
mediumDict = ctr->copyMediumDictionary(); if (mediumDict == 0)
{
error = EOPNOTSUPP;
break; }
if ((mediumCount = mediumDict->getCount()) == 0)
{
error = EOPNOTSUPP;
break; }
maxCount = ifmr->ifm_count;
if (maxCount == 0)
break;
if (maxCount > mediumCount)
{
maxCount = mediumCount;
}
else if (maxCount < mediumCount)
{
error = E2BIG;
}
iter = OSCollectionIterator::withCollection(mediumDict);
if (!iter)
{
error = ENOMEM;
break;
}
typeList = IONew(UInt32, maxCount);
if (!typeList)
{
error = ENOMEM;
break;
}
bzero(typeList, maxCount * sizeof(UInt32));
mediumCount = 0;
while ( (keyObject = (OSSymbol *) iter->getNextObject()) &&
(mediumCount < maxCount) )
{
IONetworkMedium * medium = (IONetworkMedium *)
mediumDict->getObject(keyObject);
if (!medium)
continue;
typeList[mediumCount++] = medium->getType();
}
if (mediumCount)
{
user_addr_t srcaddr;
srcaddr = (cmd == SIOCGIFMEDIA64) ?
((struct ifmediareq64 *)ifmr)->ifmu_ulist :
CAST_USER_ADDR_T(((struct ifmediareq32 *)ifmr)->ifmu_ulist);
if (srcaddr != USER_ADDR_NULL) {
error = copyout((caddr_t) typeList, srcaddr, maxCount * sizeof(UInt32));
}
}
IODelete(typeList, UInt32, maxCount);
}
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,
unsigned long 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 SIOCGIFMEDIA32:
case SIOCGIFMEDIA64:
ret = syncSIOCGIFMEDIA(ctr, ifr, cmd);
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 ( (uintptr_t) 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;
}
errno_t
IONetworkInterface::if_ioctl( ifnet_t ifp, unsigned long cmd, void * data )
{
IONetworkInterface * self = IFNET_TO_THIS(ifp);
IONetworkController * driver;
errno_t err;
if (!self || self->isInactive())
{
return EOPNOTSUPP;
}
driver = self->_driver;
if (!driver)
{
return EINVAL;
}
assert(ifp == self->_backingIfnet);
err = self->performCommand( driver, cmd, (void *) ifp, data );
return err;
}
int IONetworkInterface::if_output( ifnet_t ifp, mbuf_t m )
{
UInt32 noraceTemp;
int delta;
u_int32_t outPackets, outErrors;
IONetworkInterface * self = IFNET_TO_THIS(ifp);
assert(ifp == self->_backingIfnet);
if ( m == 0 )
{
DLOG("%s: NULL output mbuf\n", self->getName());
return EINVAL;
}
if ( (mbuf_flags(m) & MBUF_PKTHDR) == 0 )
{
DLOG("%s: MBUF_PKTHDR bit not set\n", self->getName());
mbuf_freem(m);
return EINVAL;
}
noraceTemp = self->_driverStats.outputErrors;
delta = noraceTemp - self->_lastDriverStats.outputErrors;
outErrors = ABS(delta);
self->_lastDriverStats.outputErrors = noraceTemp;
noraceTemp = self->_driverStats.outputPackets;
delta = noraceTemp - self->_lastDriverStats.outputPackets;
outPackets = ABS(delta);
self->_lastDriverStats.outputPackets = noraceTemp;
ifnet_stat_increment_out(self->getIfnet(), outPackets, (u_int32_t)mbuf_pkthdr_len(m), outErrors);
if(self->_outputFilterFunc)
self->feedPacketOutputTap(m);
return ((self->_outTarget)->*(self->_outAction))(m, 0);
}
errno_t IONetworkInterface::if_set_bpf_tap(
ifnet_t ifp,
bpf_tap_mode mode,
bpf_packet_func func )
{
IONetworkInterface * self = IFNET_TO_THIS(ifp);
bool changed = false;
assert(ifp == self->_backingIfnet);
if (self->isInactive())
{
return 0;
}
IOLockLock(self->_privateLock);
switch ( mode )
{
case BPF_TAP_DISABLE:
changed = (self->_inputFilterFunc != 0);
self->_inputFilterFunc = self->_outputFilterFunc = 0;
break;
case BPF_TAP_INPUT:
assert(func);
changed = (self->_inputFilterFunc == 0);
self->_inputFilterFunc = func;
break;
case BPF_TAP_OUTPUT:
assert(func);
self->_outputFilterFunc = func;
break;
case BPF_TAP_INPUT_OUTPUT:
assert(func);
changed = (self->_inputFilterFunc == 0);
self->_inputFilterFunc = self->_outputFilterFunc = func;
break;
default:
DLOG("%s: Unknown BPF tap mode %d\n", self->getName(), mode);
break;
}
IOLockUnlock(self->_privateLock);
if (changed)
self->notifyDriver(kIONetworkNotificationBPFTapStateChange, 0);
return 0;
}
void IONetworkInterface::if_detach( ifnet_t ifp )
{
IONetworkInterface * self = IFNET_TO_THIS(ifp);
assert(WAITING_FOR_DETACH(self) == 1);
self->retain();
IOLockLock(self->_privateLock);
WAITING_FOR_DETACH(self) = 0;
thread_wakeup((void *) self);
IOLockUnlock(self->_privateLock);
self->release();
}
#define IO_IFNET_GET(func, type, field, kpi) \
type IONetworkInterface::func(void) const \
{ \
type val = (_backingIfnet != NULL) ? \
kpi(_backingIfnet) : _##field; \
return val; \
}
#define IO_IFNET_SET(func, type, field, kpi) \
bool IONetworkInterface::func(type value) \
{ \
if (_backingIfnet) \
kpi(_backingIfnet, value); \
else \
_##field = value; \
return true; \
}
#define IO_IFNET_RMW(func, type, field, kpi) \
bool IONetworkInterface::func(type set, type clear) \
{ \
if (_backingIfnet) \
kpi(_backingIfnet, set, set | clear); \
else \
_##field = (_##field & ~clear) | set; \
return true; \
}
bool IONetworkInterface::setInterfaceType(UInt8 type)
{
if (_backingIfnet)
return false;
_type = type;
setProperty(kIOInterfaceType, _type, 8);
return true;
}
UInt8 IONetworkInterface::getInterfaceType(void) const
{
return _type;
}
IO_IFNET_SET(setMaxTransferUnit, UInt32, mtu, ifnet_set_mtu)
IO_IFNET_GET(getMaxTransferUnit, UInt32, mtu, ifnet_mtu)
IO_IFNET_RMW(setFlags, UInt16, flags, ifnet_set_flags)
IO_IFNET_GET(getFlags, UInt16, flags, ifnet_flags)
bool IONetworkInterface::setExtraFlags( UInt32 set, UInt32 clear )
{
_eflags = (_eflags & ~clear) | set;
return true;
}
IO_IFNET_GET(getExtraFlags, UInt32, eflags, ifnet_eflags)
IO_IFNET_SET(setMediaAddressLength, UInt8, addrlen, ifnet_set_addrlen)
IO_IFNET_GET(getMediaAddressLength, UInt8, addrlen, ifnet_addrlen)
IO_IFNET_SET(setMediaHeaderLength, UInt8, hdrlen, ifnet_set_hdrlen)
IO_IFNET_GET(getMediaHeaderLength, UInt8, hdrlen, ifnet_hdrlen)
IO_IFNET_GET(getUnitNumber, UInt16, unit, ifnet_unit)
bool IONetworkInterface::setUnitNumber( UInt16 value )
{
if (_backingIfnet)
return false;
if (setProperty( kIOInterfaceUnit, value, 32 ))
{
char name[128];
_unit = value;
setProperty( kIOPrimaryInterface, isPrimaryInterface() );
snprintf(name, sizeof(name), "%s%u", getNamePrefix(), _unit);
setName(name);
return true;
}
else
return false;
}
bool IONetworkInterface::setInterfaceSubType( uint32_t subType )
{
if (_backingIfnet)
return false;
else
_subType = subType;
return true;
}
bool IONetworkInterface::isRegistered() const
{
return (bool)(getInterfaceState() & kIONetworkInterfaceRegisteredState);
}
bool IONetworkInterface::serializeProperties( OSSerialize * s ) const
{
IONetworkInterface * self = (IONetworkInterface *) this;
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 );
IOLockLock(_privateLock);
val = ( _stateBits->unsigned32BitValue() | set ) & ~clear;
_stateBits->setValue( val );
IOLockUnlock(_privateLock);
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;
IOLockLock(_privateLock);
_dataDict->removeObject(aKey);
ret = _syncNetworkDataDict();
IOLockUnlock(_privateLock);
return ret;
}
bool IONetworkInterface::removeNetworkData(const char * aKey)
{
bool ret;
IOLockLock(_privateLock);
_dataDict->removeObject(aKey);
ret = _syncNetworkDataDict();
IOLockUnlock(_privateLock);
return ret;
}
bool IONetworkInterface::addNetworkData(IONetworkData * aData)
{
bool ret = false;
if (OSDynamicCast(IONetworkData, aData) == 0)
return false;
IOLockLock(_privateLock);
if (_dataDict->setObject(aData->getKey(), aData))
ret = _syncNetworkDataDict();
IOLockUnlock(_privateLock);
return ret;
}
IOReturn IONetworkInterface::newUserClient(task_t owningTask,
void * ,
UInt32 type,
IOUserClient ** handler)
{
IOReturn err = kIOReturnSuccess;
IONetworkUserClient * client;
if (type != kIONetworkUserClientTypeID)
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 {
IOService * policyMaker;
IOPMPowerFlags powerFlags;
uint32_t stateNumber;
uint32_t phase;
};
enum {
kPhasePowerStateWillChange = 0x01,
kPhasePowerStateDidChange = 0x02
};
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 * target,
void * param0,
void * param1 )
{
IONetworkInterface * self = (IONetworkInterface *) target;
IONetworkPowerChangeNotice * notice;
assert( self );
if (!self->_driver)
return;
if ( param1 == 0 )
{
self->_driver->executeCommand(
self,
(IONetworkController::Action) &powerChangeHandler,
self,
param0,
(void *) true );
return;
}
notice = (IONetworkPowerChangeNotice *) param0;
assert( notice );
DLOG("%s: power change flags:%08x state:%d from:%p phase:%d\n",
self->getName(), (uint32_t) notice->powerFlags,
notice->stateNumber, notice->policyMaker, notice->phase );
if ( notice->phase == kPhasePowerStateWillChange )
{
self->controllerWillChangePowerState(
self->_driver,
notice->powerFlags,
notice->stateNumber,
notice->policyMaker );
}
else if ( notice->phase == kPhasePowerStateDidChange )
{
self->controllerDidChangePowerState(
self->_driver,
notice->powerFlags,
notice->stateNumber,
notice->policyMaker );
}
}
IOReturn
IONetworkInterface::powerStateWillChangeTo( IOPMPowerFlags powerFlags,
unsigned long stateNumber,
IOService * policyMaker )
{
IONetworkPowerChangeNotice notice;
notice.policyMaker = policyMaker;
notice.powerFlags = powerFlags;
notice.stateNumber = (uint32_t)stateNumber;
notice.phase = kPhasePowerStateWillChange;
powerChangeHandler( (void *) this, (void *) ¬ice,
(void *) false );
return kIOPMAckImplied;
}
IOReturn
IONetworkInterface::powerStateDidChangeTo( IOPMPowerFlags powerFlags,
unsigned long stateNumber,
IOService * policyMaker )
{
IONetworkPowerChangeNotice notice;
notice.policyMaker = policyMaker;
notice.powerFlags = powerFlags;
notice.stateNumber = (uint32_t)stateNumber;
notice.phase = kPhasePowerStateDidChange;
powerChangeHandler( (void *) this, (void *) ¬ice,
(void *) false );
return kIOPMAckImplied;
}
IOReturn IONetworkInterface::message( UInt32 type, IOService * provider,
void * argument )
{
if (kMessageControllerWillShutdown == type)
{
DLOG("%s: kMessageControllerWillShutdown\n", getName());
haltOutputThread( kTxThreadStateDetach );
powerStateWillChangeTo( 0, 0, NULL );
return kIOReturnSuccess;
}
return super::message( type, provider, argument );
}
bool IONetworkInterface::requestTerminate(
IOService * provider,
IOOptionBits options )
{
#if EVALUATE_FOR_LATER_REMOVAL
if (provider == _driver)
{
DLOG("%s::requestTerminate(%s)\n",
getName(), provider->getName());
haltOutputThread( kTxThreadStateDetach );
}
#endif
return super::requestTerminate(provider, options);
}
bool IONetworkInterface::willTerminate( IOService * provider,
IOOptionBits options )
{
DLOG("%s::%s(%s, 0x%x)\n", getName(), __FUNCTION__,
provider->getName(), (uint32_t) options);
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; }
ifnet_t IONetworkInterface::getIfnet( void ) const
{
return _backingIfnet;
}
OSMetaClassDefineReservedUsed(IONetworkInterface, 0);
IOReturn IONetworkInterface::attachToDataLinkLayer( IOOptionBits options,
void * parameter )
{
ifnet_init_params params;
ifnet_init_eparams eparams;
struct sockaddr_dl * ll_addr = 0;
char buffer[2 * sizeof(struct sockaddr_dl)];
errno_t error;
OSObject * prop;
OSNumber *link_prop;
uint32_t link_status;
IOReturn result = kIOReturnInternalError;
if (!_driver)
{
LOG("%s: BSD attach failed, no driver\n", getName());
goto fail;
}
memset(¶ms, 0, sizeof(params));
if (!initIfnetParams(¶ms))
{
LOG("%s: initIfnetParams failed\n", getName());
goto fail;
}
memset(&eparams, 0, sizeof(eparams));
IOLockLock(_privateLock);
_configFlags |= kConfigFrozen;
eparams.ver = IFNET_INIT_CURRENT_VERSION;
eparams.len = sizeof(eparams);
eparams.uniqueid = params.uniqueid;
eparams.uniqueid_len = params.uniqueid_len;
eparams.name = params.name;
eparams.unit = params.unit;
eparams.family = params.family;
eparams.type = params.type;
eparams.subfamily = _subType;
eparams.demux = params.demux;
eparams.add_proto = params.add_proto;
eparams.del_proto = params.del_proto;
eparams.check_multi = params.check_multi;
eparams.framer = params.framer;
eparams.softc = params.softc;
eparams.ioctl = params.ioctl;
eparams.set_bpf_tap = params.set_bpf_tap;
eparams.detach = params.detach;
eparams.event = params.event;
eparams.broadcast_addr = params.broadcast_addr;
eparams.broadcast_len = params.broadcast_len;
eparams.start_delay_qlen = _txStartDelayQueueLength;
eparams.start_delay_timeout = _txStartDelayTimeout;
if (!(_configFlags & kConfigRxPoll) &&
!(_configFlags & kConfigTxPull))
{
eparams.flags = IFNET_INIT_LEGACY;
}
if (_configFlags & kConfigRxPoll)
{
eparams.flags = IFNET_INIT_INPUT_POLL;
eparams.input_ctl = if_input_ctl;
eparams.input_poll = (_rxPollOptions & kIONetworkWorkLoopSynchronous) ?
if_input_poll_gated : if_input_poll;
_rxPollAction = (void *) OSMemberFunctionCast(
IONetworkController::Action,
_driver, &IONetworkController::pollInputPackets);
_rxCtlAction = (void *) OSMemberFunctionCast(
IONetworkController::Action,
this, &IONetworkInterface::actionInputCtl);
DLOG("%s: supports input polling\n", getName());
}
if (_configFlags & kConfigTxPull)
{
eparams.sndq_maxlen = _txQueueSize;
if (_txQueueSize)
DLOG("%s: sndq_maxlen = %u\n", getName(), _txQueueSize);
eparams.output_sched_model = _txSchedulingModel;
eparams.output_target_qdelay = _txTargetQdelay;
if (_txPullOptions & kIONetworkWorkLoopSynchronous)
{
eparams.start = if_start_gated;
_txWorkLoop = _driver->getWorkLoop();
if (!_txWorkLoop)
{
IOLockUnlock(_privateLock);
goto fail;
}
_txWorkLoop->retain();
_txStartAction = (void *) OSMemberFunctionCast(
IONetworkController::Action,
this, &IONetworkInterface::drainOutputQueue);
}
else
{
eparams.start = if_start;
}
if (_configFlags & kConfigPreEnqueue)
{
eparams.pre_enqueue = if_output_pre_enqueue;
}
DLOG("%s: supports %stransmit pull, pre_enqueue %d\n",
getName(), _txWorkLoop ? "gated " : "", (eparams.pre_enqueue != 0));
}
else
{
eparams.output = params.output;
}
eparams.output_ctl = if_output_ctl;
IOLockUnlock(_privateLock);
error = ifnet_allocate_extended(&eparams, &_backingIfnet);
if (error)
{
LOG("%s: ifnet_allocate_extended error %d\n", getName(), error);
goto fail;
}
error = ifnet_set_offload(_backingIfnet,
getIfnetHardwareAssistValue(_driver));
if (error)
LOG("%s: ifnet_set_offload error %d\n", getName(), error);
prop = _driver->copyProperty(kIOMACAddress);
if (prop)
{
OSData * macAddr = OSDynamicCast(OSData, prop);
uint32_t len;
memset(buffer, 0, sizeof(buffer));
len = sizeof(buffer) - offsetof(struct sockaddr_dl, sdl_data);
if (macAddr && macAddr->getLength() && (macAddr->getLength() <= len))
{
len = macAddr->getLength();
ll_addr = (struct sockaddr_dl *) buffer;
bcopy(macAddr->getBytesNoCopy(), ll_addr->sdl_data, len);
ll_addr->sdl_len = offsetof(struct sockaddr_dl, sdl_data) + len;
ll_addr->sdl_family = AF_LINK;
ll_addr->sdl_alen = len;
}
prop->release();
if (!ll_addr)
{
LOG("%s: BSD attach failed, bad MAC address\n", getName());
goto fail;
}
}
ifnet_set_mtu(_backingIfnet, _mtu);
ifnet_set_flags(_backingIfnet, _flags, 0xffff);
ifnet_set_addrlen(_backingIfnet, _addrlen);
ifnet_set_hdrlen(_backingIfnet, _hdrlen);
if (_configFlags & kConfigRxPoll)
ifnet_set_rcvq_maxlen(_backingIfnet, _rxRingSize);
error = ifnet_attach(_backingIfnet, ll_addr);
if (!error) {
link_prop = OSDynamicCast(OSNumber, _controller->getProperty(kIOLinkStatus));
if (link_prop != NULL) {
link_status = link_prop->unsigned32BitValue();
if (link_status & kIONetworkLinkValid) {
if (link_status & kIONetworkLinkActive)
ifnet_set_link_quality(_backingIfnet, IFNET_LQM_THRESH_GOOD);
else
ifnet_set_link_quality(_backingIfnet, IFNET_LQM_THRESH_OFF);
}
}
result = kIOReturnSuccess;
} else
LOG("%s: ifnet_attach error %d\n", getName(), error);
fail:
if ((result != kIOReturnSuccess) && _backingIfnet)
{
ifnet_release(_backingIfnet);
_backingIfnet = NULL;
}
return result;
}
OSMetaClassDefineReservedUsed(IONetworkInterface, 1);
void IONetworkInterface::detachFromDataLinkLayer(
IOOptionBits options, void * parameter )
{
DLOG("%s: detachFromDataLinkLayer\n", getName());
haltOutputThread( kTxThreadStateDetach );
WAITING_FOR_DETACH(this) = true;
ifnet_detach(_backingIfnet);
IOLockLock(_privateLock);
while (WAITING_FOR_DETACH(this)) {
IOLockSleep(_privateLock, this, THREAD_UNINT);
}
IOLockUnlock(_privateLock);
}
void IONetworkInterface::debuggerRegistered( void )
{
char buffer[REMOTE_NMI_PATTERN_LEN + 2];
unsigned int i;
if (_remote_NMI_len)
return;
memset(buffer, 0, sizeof(buffer));
if (!PE_parse_boot_argn(kRemoteNMI, buffer, sizeof(buffer)))
return;
for (i = 0; i < (REMOTE_NMI_PATTERN_LEN >> 1); i++) {
unsigned int val;
if (sscanf(buffer + (i << 1), "%02X", &val) != 1)
break;
buffer[i] = val;
}
_remote_NMI_pattern = (char *) IOMalloc(sizeof(char) * i + 1);
if (!_remote_NMI_pattern)
return;
_remote_NMI_pattern[i] = '\0';
memcpy(_remote_NMI_pattern, buffer, i);
_remote_NMI_len = i;
}
void IONetworkInterface::reportDataTransferRates(
uint64_t outputRateMax,
uint64_t inputRateMax,
uint64_t outputRateEffective,
uint64_t inputRateEffective )
{
if_bandwidths_t bw_out, bw_in;
if ((_configFlags & kConfigDataRates) == 0)
{
IOLockLock(_privateLock);
_configFlags |= kConfigDataRates;
IOLockUnlock(_privateLock);
}
if (!outputRateEffective)
outputRateEffective = outputRateMax;
if (!inputRateEffective)
inputRateEffective = inputRateMax;
bw_out.max_bw = outputRateMax;
bw_out.eff_bw = outputRateEffective;
bw_in.max_bw = inputRateMax;
bw_in.eff_bw = inputRateEffective;
if (_backingIfnet)
ifnet_set_bandwidths(_backingIfnet, &bw_out, &bw_in);
}
IOReturn IONetworkInterface::configureClientBehavior(
IOOptionBits options )
{
_clientBehavior = options;
return kIOReturnSuccess;
}
IOReturn IONetworkInterface::configureOutputPullModel(
uint32_t driverQueueSize,
IOOptionBits options,
uint32_t outputQueueSize,
uint32_t outputSchedulingModel,
uint32_t outputTargetQdelay )
{
IOReturn ret = kIOReturnError;
IOLockLock(_privateLock);
if ((_configFlags & kConfigFrozen) == 0)
{
_txRingSize = driverQueueSize;
_txPullOptions = options;
_txQueueSize = outputQueueSize;
_txSchedulingModel = outputSchedulingModel;
_txTargetQdelay = outputTargetQdelay;
_configFlags |= kConfigTxPull;
ret = kIOReturnSuccess;
}
IOLockUnlock(_privateLock);
return ret;
}
IOReturn IONetworkInterface::installOutputPreEnqueueHandler(
OutputPreEnqueueHandler handler,
void * target,
void * refCon )
{
IOReturn ret = kIOReturnError;
if (!handler)
return kIOReturnBadArgument;
IOLockLock(_privateLock);
if ((_configFlags & kConfigFrozen) == 0)
{
_peqHandler = handler;
_peqTarget = target;
_peqRefcon = refCon;
_configFlags |= kConfigPreEnqueue;
ret = kIOReturnSuccess;
}
IOLockUnlock(_privateLock);
return ret;
}
errno_t IONetworkInterface::if_output_pre_enqueue( ifnet_t ifp, mbuf_t packet )
{
IONetworkInterface * me = IFNET_TO_THIS(ifp);
errno_t ret;
assert(ifp == me->_backingIfnet);
assert(me->_peqHandler);
ret = me->_peqHandler(me->_peqTarget, me->_peqRefcon, packet);
return ret;
}
int IONetworkInterface::if_start_precheck( ifnet_t ifp )
{
int halted = 0;
if (__builtin_expect((_txThreadState != 0), 0))
{
IOLockLock(_privateLock);
if (_txThreadState & kTxThreadStateInit)
{
assert(_txStartThread == 0);
_txStartThread = current_thread();
_txThreadState &= ~kTxThreadStateInit;
}
if (_txThreadState & (kTxThreadStateStop | kTxThreadStateDetach))
{
_txThreadState &= ~kTxThreadStateStop;
_txThreadState |= kTxThreadStateHalted;
thread_wakeup(&_txThreadState);
}
if (_txThreadState & kTxThreadStateHalted)
{
halted = true;
}
IOLockUnlock(_privateLock);
if (halted)
{
if (_txThreadState & kTxThreadStatePurge)
ifnet_purge(ifp);
}
}
return halted;
}
void IONetworkInterface::if_start( ifnet_t ifp )
{
IONetworkInterface * me = IFNET_TO_THIS(ifp);
IONetworkController * driver;
assert(ifp == me->_backingIfnet);
if (me->if_start_precheck(ifp))
return;
driver = me->_driver;
assert(driver);
if (__builtin_expect(!driver, 0))
{
IOLockLock(me->_privateLock);
me->_txThreadState |= kTxThreadStateHalted;
IOLockUnlock(me->_privateLock);
return;
}
me->drainOutputQueue(ifp, driver);
}
void IONetworkInterface::if_start_gated( ifnet_t ifp )
{
IONetworkInterface * me = IFNET_TO_THIS(ifp);
IONetworkController * driver;
assert(ifp == me->_backingIfnet);
if (me->if_start_precheck(ifp))
return;
driver = me->_driver;
assert(driver);
if (__builtin_expect(!driver, 0))
{
IOLockLock(me->_privateLock);
me->_txThreadState |= kTxThreadStateHalted;
IOLockUnlock(me->_privateLock);
return;
}
driver->executeCommand(
me,
(IONetworkController::Action) me->_txStartAction,
me,
ifp,
driver,
0,
0 );
}
errno_t IONetworkInterface::enqueueOutputPacket( mbuf_t packet, IOOptionBits options )
{
return ifnet_enqueue(_backingIfnet, packet);
}
void IONetworkInterface::drainOutputQueue(
ifnet_t ifp,
IONetworkController * driver )
{
uint32_t count;
IOReturn status;
while (true)
{
if (ifnet_get_sndq_len(ifp, &count) || !count)
break;
_txThreadFlags = 0;
status = driver->outputStart(this, 0);
if (kIOReturnSuccess != status)
{
if (kIOReturnNoResources == status)
{
if (OSCompareAndSwap(0, kTxThreadWakeupEnable, &_txThreadFlags))
break;
}
else
{
break;
}
}
}
}
IOReturn IONetworkInterface::dequeueOutputPackets(
uint32_t maxCount,
mbuf_t * packetHead,
mbuf_t * packetTail,
uint32_t * packetCount,
uint64_t * packetBytes )
{
uint32_t txByteCount, temp, txPackets = 0, txErrors = 0;
int delta;
errno_t error;
if (!maxCount || !packetHead)
return kIOReturnBadArgument;
if (_txThreadState & (kTxThreadStateInit | kTxThreadStateDetach))
{
goto no_frames;
}
assert(_backingIfnet);
if (maxCount == 1)
{
error = ifnet_dequeue(_backingIfnet, packetHead);
if (!error)
{
if (packetTail)
*packetTail = *packetHead;
if (packetCount)
*packetCount = 1;
txByteCount = (uint32_t)mbuf_pkthdr_len(*packetHead);
}
}
else
{
error = ifnet_dequeue_multi(
_backingIfnet, maxCount,
packetHead, packetTail, packetCount, &txByteCount);
}
if (error)
goto no_frames;
if (_outputFilterFunc)
{
mbuf_t m, n;
m = *packetHead;
assert(m);
assert((mbuf_flags(m) & MBUF_PKTHDR));
for (n = m; n != 0; n = mbuf_nextpkt(n))
feedPacketOutputTap(n);
}
if (_txThreadSignal != _txThreadSignalLast)
{
temp = _driverStats.outputErrors;
delta = temp - _lastDriverStats.outputErrors;
if (delta)
{
txErrors = ABS(delta);
_lastDriverStats.outputErrors = temp;
}
temp = _driverStats.outputPackets;
delta = temp - _lastDriverStats.outputPackets;
txPackets = ABS(delta);
_lastDriverStats.outputPackets = temp;
_txThreadSignalLast = _txThreadSignal;
}
ifnet_stat_increment_out(_backingIfnet, txPackets, txByteCount, txErrors);
if (packetBytes)
*packetBytes = txByteCount;
return kIOReturnSuccess;
no_frames:
*packetHead = 0;
if (packetTail)
packetTail = 0;
if (packetCount)
packetCount = 0;
if (packetBytes)
packetBytes = 0;
return kIOReturnNoFrames;
}
IOReturn IONetworkInterface::dequeueOutputPacketsWithServiceClass(
uint32_t maxCount,
IOMbufServiceClass serviceClass,
mbuf_t * packetHead,
mbuf_t * packetTail,
uint32_t * packetCount,
uint64_t * packetBytes )
{
uint32_t txByteCount, temp, txPackets = 0, txErrors = 0;
int delta;
mbuf_svc_class_t mbufSC;
errno_t error;
if (!maxCount || !packetHead)
return kIOReturnBadArgument;
if (_txThreadState & (kTxThreadStateInit | kTxThreadStateDetach))
{
goto no_frames;
}
assert(_backingIfnet);
switch (serviceClass)
{
case kIOMbufServiceClassBE: mbufSC = MBUF_SC_BE; break;
case kIOMbufServiceClassBKSYS: mbufSC = MBUF_SC_BK_SYS; break;
case kIOMbufServiceClassBK: mbufSC = MBUF_SC_BK; break;
case kIOMbufServiceClassRD: mbufSC = MBUF_SC_RD; break;
case kIOMbufServiceClassOAM: mbufSC = MBUF_SC_OAM; break;
case kIOMbufServiceClassAV: mbufSC = MBUF_SC_AV; break;
case kIOMbufServiceClassRV: mbufSC = MBUF_SC_RV; break;
case kIOMbufServiceClassVI: mbufSC = MBUF_SC_VI; break;
case kIOMbufServiceClassVO: mbufSC = MBUF_SC_VO; break;
case kIOMbufServiceClassCTL: mbufSC = MBUF_SC_CTL; break;
default:
return kIOReturnBadArgument;
}
if (maxCount == 1)
{
error = ifnet_dequeue_service_class(
_backingIfnet, mbufSC, packetHead);
if (!error)
{
if (packetTail)
*packetTail = *packetHead;
if (packetCount)
*packetCount = 1;
txByteCount = (uint32_t)mbuf_pkthdr_len(*packetHead);
}
}
else
{
error = ifnet_dequeue_service_class_multi(
_backingIfnet, mbufSC, maxCount,
packetHead, packetTail, packetCount, &txByteCount);
}
if (error)
goto no_frames;
if (_outputFilterFunc)
{
mbuf_t m, n;
m = *packetHead;
assert(m);
assert((mbuf_flags(m) & MBUF_PKTHDR));
for (n = m; n != 0; n = mbuf_nextpkt(n))
feedPacketOutputTap(n);
}
if (_txThreadSignal != _txThreadSignalLast)
{
temp = _driverStats.outputErrors;
delta = temp - _lastDriverStats.outputErrors;
if (delta)
{
txErrors = ABS(delta);
_lastDriverStats.outputErrors = temp;
}
temp = _driverStats.outputPackets;
delta = temp - _lastDriverStats.outputPackets;
txPackets = ABS(delta);
_lastDriverStats.outputPackets = temp;
_txThreadSignalLast = _txThreadSignal;
}
ifnet_stat_increment_out(_backingIfnet, txPackets, txByteCount, txErrors);
if (packetBytes)
*packetBytes = txByteCount;
return kIOReturnSuccess;
no_frames:
*packetHead = 0;
if (packetTail)
packetTail = 0;
if (packetCount)
packetCount = 0;
if (packetBytes)
packetBytes = 0;
return kIOReturnNoFrames;
}
IOReturn IONetworkInterface::dequeueOutputPacketsWithMaxSize(
uint32_t maxSize,
mbuf_t * packetHead,
mbuf_t * packetTail,
uint32_t * packetCount,
uint64_t * packetBytes )
{
uint32_t txByteCount, temp, txPackets = 0, txErrors = 0;
int delta;
errno_t error;
if (!maxSize || !packetHead)
return kIOReturnBadArgument;
if (_txThreadState & (kTxThreadStateInit | kTxThreadStateDetach))
{
goto no_frames;
}
assert(_backingIfnet);
error = ifnet_dequeue_multi_bytes(
_backingIfnet, maxSize,
packetHead, packetTail, packetCount, &txByteCount);
if (error) goto no_frames;
if (_outputFilterFunc)
{
mbuf_t m, n;
m = *packetHead;
assert(m);
assert((mbuf_flags(m) & MBUF_PKTHDR));
for (n = m; n != 0; n = mbuf_nextpkt(n))
feedPacketOutputTap(n);
}
if (_txThreadSignal != _txThreadSignalLast)
{
temp = _driverStats.outputErrors;
delta = temp - _lastDriverStats.outputErrors;
if (delta)
{
txErrors = ABS(delta);
_lastDriverStats.outputErrors = temp;
}
temp = _driverStats.outputPackets;
delta = temp - _lastDriverStats.outputPackets;
txPackets = ABS(delta);
_lastDriverStats.outputPackets = temp;
_txThreadSignalLast = _txThreadSignal;
}
ifnet_stat_increment_out(_backingIfnet, txPackets, txByteCount, txErrors);
if (packetBytes)
*packetBytes = txByteCount;
return kIOReturnSuccess;
no_frames:
*packetHead = 0;
if (packetTail)
packetTail = 0;
if (packetCount)
packetCount = 0;
if (packetBytes)
packetBytes = 0;
return kIOReturnNoFrames;
}
void IONetworkInterface::signalOutputThread( IOOptionBits options )
{
UInt32 old = OSBitOrAtomic(kTxThreadWakeupSignal, &_txThreadFlags);
if (_txThreadState & (kTxThreadStateInit | kTxThreadStateDetach))
{
return;
}
if ((old & kTxThreadWakeupMask) == kTxThreadWakeupEnable)
{
assert(_backingIfnet);
ifnet_start(_backingIfnet);
}
_txThreadSignal++;
}
IOReturn IONetworkInterface::startOutputThread( IOOptionBits options )
{
const uint32_t mask = (kTxThreadStateHalted | kTxThreadStateStop);
IOReturn error = kIOReturnUnsupported;
bool purge = false;
DLOG("%s: %s(0x%x)\n",
getName(), __FUNCTION__, _txThreadState);
IOLockLock(_privateLock);
if (_txThreadState & kTxThreadStateDetach)
{
error = kIOReturnNotAttached;
}
else
{
error = kIOReturnSuccess;
purge = ((_txThreadState & kTxThreadStatePurge) != 0);
_txThreadState &= ~kTxThreadStatePurge;
if (_txThreadState & mask)
{
_txThreadState &= ~mask;
if ((_txThreadState & kTxThreadStateInit) == 0)
{
assert(_backingIfnet);
if (purge)
ifnet_purge(_backingIfnet);
ifnet_start(_backingIfnet);
}
}
}
IOLockUnlock(_privateLock);
return error;
}
IOReturn IONetworkInterface::haltOutputThread( uint32_t stateBit )
{
AbsoluteTime deadline;
uint32_t count = 0;
const uint32_t timeout = 100;
IOReturn error = kIOReturnSuccess;
DLOG("%s: %s(0x%x, 0x%x)\n",
getName(), __FUNCTION__, _txThreadState, stateBit);
IOLockLock(_privateLock);
do {
_txThreadState |= (stateBit & kTxThreadStateDetach);
_txThreadState &= ~kTxThreadStatePurge;
if (_txThreadState & kTxThreadStateHalted)
break;
if (!_txStartThread)
{
_txThreadState |= kTxThreadStateHalted;
break;
}
assert(_backingIfnet);
if (current_thread() == _txStartThread)
{
_txThreadState |= kTxThreadStateHalted;
break;
}
if (_txWorkLoop && _txWorkLoop->inGate())
{
_txThreadState |= kTxThreadStateHalted;
break;
}
while ((_txThreadState & kTxThreadStateHalted) == 0)
{
if (count)
{
LOG("%s: %s(0x%x, 0x%x) retry %u\n",
getName(), __FUNCTION__, _txThreadState, stateBit, count);
if (count >= timeout)
{
error = kIOReturnTimeout;
break;
}
}
_txThreadState |= stateBit;
clock_interval_to_deadline(100, kMillisecondScale, &deadline);
ifnet_start(_backingIfnet);
IOLockSleepDeadline(_privateLock, &_txThreadState,
deadline, THREAD_UNINT);
count++;
}
} while (false);
IOLockUnlock(_privateLock);
DLOG("%s: %s(0x%x, 0x%x) done after %u try\n",
getName(), __FUNCTION__, _txThreadState, stateBit, count);
return error;
}
IOReturn IONetworkInterface::stopOutputThread( IOOptionBits options )
{
return haltOutputThread( kTxThreadStateStop );
}
void IONetworkInterface::flushOutputQueue( IOOptionBits options )
{
DLOG("%s: %s(0x%x)\n",
getName(), __FUNCTION__, _txThreadState);
IOLockLock(_privateLock);
if (_backingIfnet && ((_txThreadState & kTxThreadStateDetach) == 0))
{
if (_txThreadState & kTxThreadStateHalted)
_txThreadState |= kTxThreadStatePurge;
ifnet_purge(_backingIfnet);
}
IOLockUnlock(_privateLock);
}
IOReturn IONetworkInterface::reportTransmitCompletionStatus(
mbuf_t packet,
IOReturn status,
uint32_t param1 __unused,
uint32_t param2 __unused,
IOOptionBits options __unused )
{
errno_t error = ifnet_tx_compl_status(getIfnet(), packet, status);
return (error) ? kIOReturnError : kIOReturnSuccess;
}
IOReturn IONetworkInterface::configureInputPacketPolling(
uint32_t driverQueueSize,
IOOptionBits options )
{
IOReturn ret = kIOReturnError;
IOLockLock(_privateLock);
if ((_configFlags & kConfigFrozen) == 0)
{
_rxRingSize = driverQueueSize;
_rxPollOptions = options;
_configFlags |= kConfigRxPoll;
ret = kIOReturnSuccess;
}
IOLockUnlock(_privateLock);
return ret;
}
void IONetworkInterface::if_input_poll(
ifnet_t ifp,
uint32_t flags,
uint32_t max_count,
mbuf_t * first_packet,
mbuf_t * last_packet,
uint32_t * cnt,
uint32_t * len )
{
IONetworkInterface * me = IFNET_TO_THIS(ifp);
IONetworkController * driver;
IOMbufQueue queue;
assert(ifp == me->_backingIfnet);
assert(max_count != 0);
driver = me->_driver;
assert(driver);
if (__builtin_expect(!driver, 0))
{
return;
}
me->_rxPollTotal++;
IOMbufQueueInit(&queue);
driver->pollInputPackets(me, max_count, &queue, 0);
if (!IOMbufQueueIsEmpty(&queue))
{
*first_packet = queue.head;
*last_packet = queue.tail;
*cnt = queue.count;
*len = queue.bytes;
}
else
{
me->_rxPollEmpty++;
*first_packet = 0;
*last_packet = 0;
*cnt = 0;
*len = 0;
}
}
void IONetworkInterface::if_input_poll_gated(
ifnet_t ifp,
uint32_t flags,
uint32_t max_count,
mbuf_t * first_packet,
mbuf_t * last_packet,
uint32_t * cnt,
uint32_t * len )
{
IONetworkInterface * me = IFNET_TO_THIS(ifp);
IONetworkController * driver;
IOMbufQueue queue;
assert(ifp == me->_backingIfnet);
assert(max_count != 0);
driver = me->_driver;
assert(driver);
if (__builtin_expect(!driver, 0))
{
return;
}
me->_rxPollTotal++;
IOMbufQueueInit(&queue);
driver->executeCommand(
me,
(IONetworkController::Action) me->_rxPollAction,
driver,
me,
(void *)(uintptr_t) max_count,
&queue,
0 );
if (!IOMbufQueueIsEmpty(&queue))
{
*first_packet = queue.head;
*last_packet = queue.tail;
*cnt = queue.count;
*len = queue.bytes;
}
else
{
me->_rxPollEmpty++;
*first_packet = 0;
*last_packet = 0;
*cnt = 0;
*len = 0;
}
}
IOReturn IONetworkInterface::setPacketPollingParameters(
const IONetworkPacketPollingParameters * params,
IOOptionBits options )
{
struct ifnet_poll_params ifParams;
errno_t error;
if (!params)
return kIOReturnBadArgument;
if (!_backingIfnet)
return kIOReturnNotAttached;
bzero(&ifParams, sizeof(ifParams));
ifParams.flags = options;
ifParams.packets_limit = params->maxPacketCount;
ifParams.packets_lowat = params->lowThresholdPackets;
ifParams.packets_hiwat = params->highThresholdPackets;
ifParams.bytes_lowat = params->lowThresholdBytes;
ifParams.bytes_hiwat = params->highThresholdBytes;
ifParams.interval_time = params->pollIntervalTime;
error = ifnet_set_poll_params(_backingIfnet, &ifParams);
return errnoToIOReturn(error);
}
IOReturn IONetworkInterface::enqueueInputPacket(
mbuf_t packet,
IOMbufQueue * queue,
IOOptionBits options )
{
const uint32_t hdrlen = _hdrlen;
void * mdata;
uint32_t length;
assert(packet);
assert(_backingIfnet);
if (!packet)
return kIOReturnBadArgument;
if (!_backingIfnet)
{
mbuf_freem(packet);
return kIOReturnNotAttached;
}
if (!queue)
{
queue = _inputPushQueue;
}
assert((mbuf_flags(packet) & MBUF_PKTHDR));
assert((mbuf_nextpkt(packet) == 0));
mbuf_pkthdr_setrcvif(packet, _backingIfnet);
length = (uint32_t)mbuf_pkthdr_len(packet);
assert(length != 0);
if (_remote_NMI_len)
checkForRNMIPacket(packet, hdrlen, _remote_NMI_len, _remote_NMI_pattern);
if (_inputFilterFunc)
feedPacketInputTap(packet);
mdata = mbuf_data(packet);
mbuf_pkthdr_setheader(packet, mdata);
mbuf_pkthdr_setlen(packet, length - hdrlen);
mbuf_setdata(packet, (char *)mdata + hdrlen, mbuf_len(packet) - hdrlen);
IOMbufQueueTailAdd(queue, packet, length);
return kIOReturnSuccess;
}
errno_t IONetworkInterface::if_input_ctl( ifnet_t ifp,
ifnet_ctl_cmd_t cmd,
u_int32_t arglen,
void * arg )
{
IONetworkInterface * me = IFNET_TO_THIS(ifp);
IONetworkController * driver;
assert(ifp == me->_backingIfnet);
driver = me->_driver;
assert(driver);
if (!driver)
{
return 0;
}
driver->executeCommand(
me,
(IONetworkController::Action) me->_rxCtlAction,
me,
driver,
(void *)(uintptr_t) cmd,
(void *)(uintptr_t) arglen,
arg );
return 0;
}
void IONetworkInterface::actionInputCtl( IONetworkController * driver,
ifnet_ctl_cmd_t cmd,
uint32_t arglen,
void * arg )
{
ifnet_model_params * params = (ifnet_model_params *) arg;
switch (cmd)
{
case IFNET_CTL_SET_INPUT_MODEL:
if (arglen != sizeof(ifnet_model_params))
{
LOG("%s: SET_INPUT_MODEL bad params size %u != %u\n",
getName(), arglen, (uint32_t) sizeof(ifnet_model_params));
}
else if ((params->model != IFNET_MODEL_INPUT_POLL_OFF) &&
(params->model != IFNET_MODEL_INPUT_POLL_ON))
{
LOG("%s: SET_INPUT_MODEL unknown model 0x%x\n",
getName(), params->model);
}
else if (params->model != _rxPollModel)
{
_rxPollModel = params->model;
DLOG("%s: SET_INPUT_MODEL 0x%x\n", getName(), _rxPollModel);
DLOG("%s: poll cnt %llu, empty %llu\n", getName(),
_rxPollTotal, _rxPollEmpty);
_rxPollEmpty = 0;
_rxPollTotal = 0;
driver->setInputPacketPollingEnable(
this, _rxPollModel == IFNET_MODEL_INPUT_POLL_ON );
}
break;
case IFNET_CTL_GET_INPUT_MODEL:
if (arglen != sizeof(ifnet_model_params))
{
LOG("%s: GET_INPUT_MODEL bad params size %u != %u\n",
getName(), arglen, (uint32_t) sizeof(ifnet_model_params));
}
else
{
params->model = _rxPollModel;
}
break;
default:
DLOG("%s: if_input_ctl unknown cmd 0x%x\n", getName(), cmd);
}
}
errno_t IONetworkInterface::if_output_ctl( ifnet_t ifp,
ifnet_ctl_cmd_t cmd,
u_int32_t arglen,
void * arg )
{
IONetworkInterface * me = IFNET_TO_THIS(ifp);
IONetworkController * driver;
errno_t error = ENOTSUP;
bool changed = false;
assert(ifp == me->_backingIfnet);
driver = me->_driver;
assert(driver);
if (!driver)
{
return ENODEV;
}
if (cmd == IFNET_CTL_SET_LOG)
{
if (arg && (arglen == sizeof(struct ifnet_log_params)))
{
const struct ifnet_log_params * lp =
(const struct ifnet_log_params *) arg;
IONetworkInterfaceLoggingParameters params;
IOLockLock(me->_privateLock);
if (me->_loggingLevel != lp->level)
changed = true;
me->_loggingLevel = lp->level;
IOLockUnlock(me->_privateLock);
error = 0;
bzero(¶ms, sizeof(params));
params.level = lp->level;
params.flags = lp->flags;
params.category = lp->category;
params.subCategory = lp->subcategory;
me->notifyDriver(kIONetworkNotificationLoggingParametersChange,
¶ms);
}
else
{
error = EINVAL;
}
if (changed)
me->notifyDriver(kIONetworkNotificationLoggingLevelChange, 0);
}
else if (cmd == IFNET_CTL_NOTIFY_ADDRESS)
{
if (arg && (arglen == sizeof(struct ifnet_notify_address_params)))
{
const struct ifnet_notify_address_params * ap =
(const struct ifnet_notify_address_params *) arg;
IONetworkInterfaceAddressChangeParameters params;
bzero(¶ms, sizeof(params));
params.addressFamily = ap->address_family;
me->notifyDriver(kIONetworkNotificationInterfaceAddressChange,
¶ms);
}
else
{
error = EINVAL;
}
}
return error;
}
bool IONetworkInterface::isBPFTapEnabled( IOOptionBits options __unused ) const
{
bool enabled;
IOLockLock(_privateLock);
enabled = (_inputFilterFunc != 0);
IOLockUnlock(_privateLock);
return enabled;
}
int32_t IONetworkInterface::getLoggingLevel( IOOptionBits options __unused ) const
{
int32_t level;
IOLockLock(_privateLock);
level = _loggingLevel;
IOLockUnlock(_privateLock);
return level;
}
void IONetworkInterface::notifyDriver( uint32_t notifyType, void * data )
{
if (_driver)
_driver->networkInterfaceNotification(this, notifyType, data);
}
void IONetworkInterface::handleNetworkInputEvent( thread_call_param_t param0, thread_call_param_t param1 )
{
IONetworkInterface *me = (IONetworkInterface *) param0;
UInt32 type = (UInt32)(size_t)param1;
if (me)
{
if ((me->_clientBehavior & kIONetworkClientBehaviorLinkStateActiveRegister) && (type == kIONetworkEventTypeLinkUp)){
me->registerService();
}
if (me->_clientBehavior & kIONetworkClientBehaviorLinkStateChangeMessage) {
me->messageClients((type == kIONetworkEventTypeLinkUp) ? kIONetworkLinkStateActive : kIONetworkLinkStateInactive);
}
me->release();
}
}
IOReturn IONetworkInterface::errnoToIOReturn( errno_t error )
{
switch (error)
{
case 0:
return kIOReturnSuccess;
case EINVAL:
return kIOReturnBadArgument;
case ENOMEM:
return kIOReturnNoMemory;
case EPERM:
return kIOReturnNotPermitted;
case EACCES:
return kIOReturnNotPrivileged;
case ENOTSUP:
return kIOReturnUnsupported;
case EBUSY:
return kIOReturnBusy;
case ETIMEDOUT:
return kIOReturnTimeout;
default:
return kIOReturnError;
}
}
IOReturn IONetworkInterface::reportDatapathIssue(
IOReturn issue,
void * data __unused,
IOByteCount length __unused )
{
errno_t error;
static const uint8_t modid[DLIL_MODIDLEN] = {
0xe3, 0x26, 0x1e, 0x7d, 0xf2, 0x15, 0xf1, 0x3d, 0x9d, 0x9d,
0xbc, 0x1f, 0x75, 0x4d, 0xa5, 0xa4, 0x1c, 0xac, 0x2a, 0xab };
union {
uint8_t errorData[ DLIL_MODARGLEN ];
IOReturn errorCode;
} issueInfo;
bzero(&issueInfo, sizeof(issueInfo));
issueInfo.errorCode = issue;
error = ifnet_report_issues(getIfnet(), (uint8_t *) modid, issueInfo.errorData);
return errnoToIOReturn(error);
}