/* * Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * The contents of this file constitute Original Code as defined in and * are subject to the Apple Public Source License Version 1.1 (the * "License"). You may not use this file except in compliance with the * License. Please obtain a copy of the License at * http://www.apple.com/publicsource and read it before using this file. * * This Original Code and all software distributed under the License are * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the * License for the specific language governing rights and limitations * under the License. * * @APPLE_LICENSE_HEADER_END@ */ /* * Copyright (c) 1999 Apple Computer, Inc. All rights reserved. * * IOFWInterface.cpp * */ #include #include #include #include "IOFWInterface.h" #include "IOFWController.h" //#include #include // publishFeature() extern "C" { #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include } #include "firewire.h" #include "if_firewire.h" #ifndef DLT_APPLE_IP_OVER_IEEE1394 #define DLT_APPLE_IP_OVER_IEEE1394 138 #endif //--------------------------------------------------------------------------- #define super IONetworkInterface OSDefineMetaClassAndStructors( IOFWInterface, IONetworkInterface ) OSMetaClassDefineReservedUnused( IOFWInterface, 0); OSMetaClassDefineReservedUnused( IOFWInterface, 1); OSMetaClassDefineReservedUnused( IOFWInterface, 2); OSMetaClassDefineReservedUnused( IOFWInterface, 3); OSMetaClassDefineReservedUnused( IOFWInterface, 4); OSMetaClassDefineReservedUnused( IOFWInterface, 5); OSMetaClassDefineReservedUnused( IOFWInterface, 6); OSMetaClassDefineReservedUnused( IOFWInterface, 7); OSMetaClassDefineReservedUnused( IOFWInterface, 8); OSMetaClassDefineReservedUnused( IOFWInterface, 9); OSMetaClassDefineReservedUnused( IOFWInterface, 10); OSMetaClassDefineReservedUnused( IOFWInterface, 11); OSMetaClassDefineReservedUnused( IOFWInterface, 12); OSMetaClassDefineReservedUnused( IOFWInterface, 13); OSMetaClassDefineReservedUnused( IOFWInterface, 14); OSMetaClassDefineReservedUnused( IOFWInterface, 15); // The name prefix for all BSD FireWire interfaces. // #define kIOFWInterfaceNamePrefix "fw" // Options used for enableFilter(), disableFilter(). enum { kFilterOptionDeferIO = 0x0001, kFilterOptionNotInsideGate = 0x0002, kFilterOptionNoStateChange = 0x0004, kFilterOptionDisableZeroBits = 0x0008, kFilterOptionSyncPendingIO = 0x0010 }; static u_long ivedonethis = 0; //--------------------------------------------------------------------------- // Macros #ifdef DEBUG #define DLOG(fmt, args...) IOLog(fmt, ## args) #else #define DLOG(fmt, args...) #endif UInt32 IOFWInterface::getFilters(const OSDictionary *dict, const OSSymbol *group) { OSNumber * num; UInt32 filters = 0; assert( dict && group ); if (( num = (OSNumber *) dict->getObject(group) )) { filters = num->unsigned32BitValue(); } return filters; } bool IOFWInterface::setFilters(OSDictionary *dict, const OSSymbol *group, UInt32 filters) { OSNumber * num; bool ret = false; assert( dict && group ); num = (OSNumber *) dict->getObject(group); if ( num == 0 ) { if (( num = OSNumber::withNumber(filters, 32) )) { ret = dict->setObject(group, num); num->release(); } } else { num->setValue(filters); ret = true; } return ret; } #define GET_REQUIRED_FILTERS(g) getFilters(_requiredFilters, (g)) #define GET_ACTIVE_FILTERS(g) getFilters(_activeFilters, (g)) #define GET_SUPPORTED_FILTERS(g) getFilters(_supportedFilters, (g)) #define SET_REQUIRED_FILTERS(g, v) setFilters(_requiredFilters, (g), (v)) #define SET_ACTIVE_FILTERS(g, v) setFilters(_activeFilters, (g), (v)) //--------------------------------------------------------------------------- // Initialize an IOFWInterface instance. Instance variables are // initialized bool IOFWInterface::init(IONetworkController *controller) { // Pass the init() call to our superclass. if ( super::init(controller) == false ) return false; // initialize firewire specific fields. setInterfaceType( IFT_IEEE1394 ); setMaxTransferUnit( FIREWIREMTU ); setMediaAddressLength( FIREWIRE_ADDR_LEN ); setMediaHeaderLength( FIREWIRE_HDR_LEN ); setFlags( IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS, IFF_RUNNING | IFF_MULTICAST ); // Add an IONetworkData with room to hold an IOFWStats structure. // This class does not reference the data object created, and no harm // is done if the data object is released or replaced. IONetworkData * data = IONetworkData::withInternalBuffer( kIOFWStatsKey, sizeof(IOFWStats)); if (data) { addNetworkData(data); data->release(); } // Create and initialize the filter dictionaries. _requiredFilters = OSDictionary::withCapacity(4); _activeFilters = OSDictionary::withCapacity(4); if ( (_requiredFilters == 0) || (_activeFilters == 0) ) return false; _supportedFilters = OSDynamicCast(OSDictionary, controller->getProperty(kIOPacketFilters)); if ( _supportedFilters == 0 ) return false; _supportedFilters->retain(); // Controller's Unicast (directed) and Broadcast filters should always // be enabled. Those bits should never be cleared. if ( !SET_REQUIRED_FILTERS( gIONetworkFilterGroup, kIOPacketFilterUnicast | kIOPacketFilterBroadcast ) || !SET_REQUIRED_FILTERS( gIOEthernetWakeOnLANFilterGroup, 0 ) || !SET_ACTIVE_FILTERS( gIONetworkFilterGroup, 0 ) || !SET_ACTIVE_FILTERS( gIOEthernetWakeOnLANFilterGroup, 0 ) ) { return false; } // Publish filter dictionaries to property table. setProperty( kIORequiredPacketFilters, _requiredFilters ); setProperty( kIOActivePacketFilters, _activeFilters ); _controller = controller; return true; } //--------------------------------------------------------------------------- // Initialize the given ifnet structure. The argument specified is a pointer // to an ifnet structure obtained through getIfnet(). IOFWInterface // will initialize this structure in a manner that is appropriate for most // FireWire interfaces, then call super::initIfnet() to allow the superclass // to perform generic interface initialization. // // ifp: Pointer to the ifnet structure to be initialized. // // Returns true on success, false otherwise. int firewire_frameout(ifnet_t ifp, mbuf_t *m, const struct sockaddr *ndest, const char *edst, const char *fw_type); int firewire_demux(ifnet_t ifp, mbuf_t m,char *frame_header,protocol_family_t *protocol_family); int firewire_add_proto(ifnet_t ifp, protocol_family_t protocol, const struct ifnet_demux_desc *demux_list, u_int32_t demux_count); int firewire_del_proto(ifnet_t ifp, protocol_family_t protocol_family); int firewire_add_if(ifnet_t ifp); bool IOFWInterface::initIfnetParams(struct ifnet_init_params *params) { //get the default values super::initIfnetParams( params ); _uniqueID = OSDynamicCast(OSData, getProvider()->getProperty(kIOMACAddress)); if ( (_uniqueID == 0) || (_uniqueID->getLength() != FIREWIRE_ADDR_LEN) ) { DLOG("%s: kIOMACAddress property access error (len %d)\n", getName(), _uniqueID ? _uniqueID->getLength() : 0); return false; } // fill in firewire specific values params->uniqueid = _uniqueID->getBytesNoCopy(); params->uniqueid_len = _uniqueID->getLength(); params->family = APPLE_IF_FAM_FIREWIRE; params->demux = firewire_demux; params->add_proto = firewire_add_proto; params->del_proto = firewire_del_proto; params->framer = firewire_frameout; params->broadcast_addr = fwbroadcastaddr; params->broadcast_len = sizeof(fwbroadcastaddr); return true; } //--------------------------------------------------------------------------- // Free the IOFWInterface instance. int firewire_del_if(IOFWInterface *fwIf); void IOFWInterface::free() { struct ifnet *interface = (struct ifnet*)getIfnet(); if ( interface ) { DLOG("%s%d: release ifnet %p\n", getNamePrefix(), getUnitNumber(), interface); firewire_del_if( this ); dlil_if_release(interface ); } if ( _requiredFilters ) { _requiredFilters->release(); _requiredFilters = 0; } if ( _activeFilters ) { _activeFilters->release(); _activeFilters = 0; } if ( _supportedFilters ) { _supportedFilters->release(); _supportedFilters = 0; } super::free(); } //--------------------------------------------------------------------------- // The name of the interface advertised to the network layer // is generated by concatenating the string returned by this method, // and an unit number. // // Returns a pointer to a constant string "fw". Thus FireWire interfaces // will be registered as fw0, fw1, etc. const char * IOFWInterface::getNamePrefix() const { return kIOFWInterfaceNamePrefix; } //--------------------------------------------------------------------------- // Prepare the 'FireWire' controller after it has been opened. This is called // by IONetworkInterface after a controller has accepted an open from this // interface. IOFWInterface uses this method to inspect the controller, // and to cache certain controller properties, such as its hardware address. // This method is called with the arbitration lock held. // // controller: The controller object that was opened. // // Returns true on success, false otherwise // (which will cause the controller to be closed). bool IOFWInterface::controllerDidOpen(IONetworkController * ctr) { bool ret = false; OSData * addrData; do { // Call the controllerDidOpen() in superclass first. if ( (ctr == 0) || (super::controllerDidOpen(ctr) == false) ) break; // If the controller supports some form of multicast filtering, // then set the ifnet IFF_MULTICAST flag. if ( GET_SUPPORTED_FILTERS(gIONetworkFilterGroup) & (kIOPacketFilterMulticast | kIOPacketFilterMulticastAll) ) { setFlags(IFF_MULTICAST); } // Advertise Wake on Magic Packet feature if supported. if ( GET_SUPPORTED_FILTERS( gIOEthernetWakeOnLANFilterGroup ) & kIOFWWakeOnMagicPacket ) { IOPMrootDomain * root = getPMRootDomain(); if ( root ) root->publishFeature( "WakeOnMagicPacket" ); } // Get the controller's MAC/FireWire address. addrData = OSDynamicCast(OSData, ctr->getProperty(kIOMACAddress)); if ((addrData == 0) || (addrData->getLength() != FIREWIRE_ADDR_LEN)) { DLOG("%s: kIOMACAddress property access error (len %d)\n", getName(), addrData ? addrData->getLength() : 0); break; } ret = true; } while (0); return ret; } //--------------------------------------------------------------------------- // When a close from our last client is received, the interface will // close the controller. But before the controller is closed, this method // will be called by our superclass to perform any final cleanup. This // method is called with the arbitration lock held. // // IOFWInterface will ensure that the controller is disabled. // // controller: The currently opened controller object. void IOFWInterface::controllerWillClose(IONetworkController * ctr) { super::controllerWillClose(ctr); } //--------------------------------------------------------------------------- // Handle ioctl commands originated from the network layer. // Commands not handled by this method are passed to our superclass. // // Argument convention is: // // arg0 - (ifnet_t ) // arg1 - (void *) // // The commands handled by IOFWInterface are: // // SIOCSIFADDR // SIOCSIFFLAGS // SIOCADDMULTI // SIOCDELMULTI // // Returns an error code defined in errno.h (BSD). SInt32 IOFWInterface::performCommand( IONetworkController * ctr, UInt32 cmd, void * arg0, void * arg1 ) { SInt32 ret; if ( ctr == 0 ) return EINVAL; switch ( cmd ) { case SIOCSIFFLAGS: case SIOCADDMULTI: case SIOCDELMULTI: case SIOCSIFADDR: case SIOCSIFMTU: case SIOCSIFLLADDR: ret = (int) ctr->executeCommand( this, /* client */ (IONetworkController::Action) &IOFWInterface::performGatedCommand, this, /* target */ ctr, /* param0 */ (void *) cmd, /* param1 */ arg0, /* param2 */ arg1 ); /* param3 */ break; default: // Unknown command, let our superclass deal with it. ret = super::performCommand(ctr, cmd, arg0, arg1); break; } return ret; } //--------------------------------------------------------------------------- // Handle an ioctl command on the controller's workloop context. int IOFWInterface::performGatedCommand(void * target, void * arg1_ctr, void * arg2_cmd, void * arg3_0, void * arg4_1) { IOFWInterface *self = (IOFWInterface *) target; IONetworkController *ctr = (IONetworkController *) arg1_ctr; struct ifreq *ifr = (struct ifreq*) arg4_1; SInt ret = EOPNOTSUPP; // Refuse to perform controller I/O if the controller is in a // low-power state that makes it "unusable". if ( self->_controllerLostPower || ( self->getInterfaceState() & kIONetworkInterfaceDisabledState ) ) return EPWROFF; switch ( (UInt32) arg2_cmd ) { case SIOCSIFADDR: ret = self->syncSIOCSIFADDR(ctr); break; case SIOCSIFFLAGS: ret = self->syncSIOCSIFFLAGS(ctr); break; case SIOCADDMULTI: ret = self->syncSIOCADDMULTI(ctr); break; case SIOCDELMULTI: ret = self->syncSIOCDELMULTI(ctr); break; case SIOCSIFMTU: ret = self->syncSIOCSIFMTU( ctr, ifr ); break; case SIOCSIFLLADDR: ret = self->syncSIOCSIFLLADDR( ctr, ifr->ifr_addr.sa_data, ifr->ifr_addr.sa_len ); break; } return ret; } //--------------------------------------------------------------------------- // enableController() is reponsible for calling the controller's enable() // method and restoring the state of the controller. We assume that // controllers can completely reset its state upon receiving a disable() // method call. And when it is brought back up, the interface should // assist in restoring the previous state of the FireWire controller. IOReturn IOFWInterface::enableController(IONetworkController * ctr) { IOReturn ret = kIOReturnSuccess; bool enabled = false; assert(ctr); do { // Is controller already enabled? If so, exit and return success. if ( _ctrEnabled ) break; // Send the controller an enable command. if ( (ret = ctr->doEnable(this)) != kIOReturnSuccess ) break; // unable to enable the controller. enabled = true; // Disable all Wake-On-LAN filters. disableFilter(ctr, gIOEthernetWakeOnLANFilterGroup, ~0, kFilterOptionNoStateChange); // Restore current filter selection. SET_ACTIVE_FILTERS(gIONetworkFilterGroup, 0); ret = enableFilter(ctr, gIONetworkFilterGroup, 0, kFilterOptionSyncPendingIO); if ( ret != kIOReturnSuccess ) break; // Restore multicast filter settings. syncSIOCADDMULTI(ctr); // Re-apply the user supplied link-level address. OSData * lladdr = OSDynamicCast(OSData, getProperty(kIOMACAddress)); if ( lladdr && lladdr->getLength() == FIREWIRE_ADDR_LEN ) { ctr->setHardwareAddress( lladdr->getBytesNoCopy(), lladdr->getLength() ); } _ctrEnabled = true; } while (false); // Disable the controller if a serious error has occurred after the // controller has been enabled. if ( enabled && (ret != kIOReturnSuccess) ) { ctr->doDisable(this); } return ret; } //--------------------------------------------------------------------------- // Handles SIOCSIFFLAGS ioctl command for FireWire interfaces. The network // stack has changed the if_flags field in ifnet. Our job is to go // through if_flags and see what has changed, and act accordingly. // // The fact that if_flags contains both generic and FireWire specific bits // means that we cannot move some of the default flag processing to the // superclass. int IOFWInterface::syncSIOCSIFFLAGS(IONetworkController * ctr) { UInt16 flags = getFlags(); IOReturn ret = kIOReturnSuccess; if ( ( ((flags & IFF_UP) == 0) || _controllerLostPower ) && ( flags & IFF_RUNNING ) ) { // If interface is marked down and it is currently running, // then stop it. ctr->doDisable(this); flags &= ~IFF_RUNNING; _ctrEnabled = false; } else if ( ( flags & IFF_UP ) && ( _controllerLostPower == false ) && ((flags & IFF_RUNNING) == 0) ) { // If interface is marked up and it is currently stopped, // then start it. if ( (ret = enableController(ctr)) == kIOReturnSuccess ) flags |= IFF_RUNNING; } if ( flags & IFF_RUNNING ) { IOReturn rc; // We don't expect multiple flags to be changed for a given // SIOCSIFFLAGS call. // Promiscuous mode rc = (flags & IFF_PROMISC) ? enableFilter(ctr, gIONetworkFilterGroup, kIOPacketFilterPromiscuous) : disableFilter(ctr, gIONetworkFilterGroup, kIOPacketFilterPromiscuous); if (ret == kIOReturnSuccess) ret = rc; // Multicast-All mode rc = (flags & IFF_ALLMULTI) ? enableFilter(ctr, gIONetworkFilterGroup, kIOPacketFilterMulticastAll) : disableFilter(ctr, gIONetworkFilterGroup, kIOPacketFilterMulticastAll); if (ret == kIOReturnSuccess) ret = rc; } // Update the flags field to pick up any modifications. Also update the // property table to reflect any flag changes. setFlags(flags, ~flags); return errnoFromReturn(ret); } //--------------------------------------------------------------------------- // Handles SIOCSIFADDR ioctl. SInt IOFWInterface::syncSIOCSIFADDR(IONetworkController * ctr) { ifnet_t ifp = getIfnet(); IOReturn ret = kIOReturnSuccess; char lladdr[8]; if(ifp == NULL) return (EINVAL); IOFWAddress *addr = (IOFWAddress *) _uniqueID->getBytesNoCopy(); bcopy(addr->bytes, lladdr, kIOFWAddressSize); if(ifnet_set_lladdr(ifp, lladdr, kIOFWAddressSize) != 0) DLOG("ifnet_set_lladdr failure in %s:%d", __FILE__, __LINE__); // Interface is implicitly brought up by an SIOCSIFADDR ioctl. setFlags(IFF_UP); if ( (getFlags() & IFF_RUNNING) == 0 ) { if ( (ret = enableController(ctr)) == kIOReturnSuccess ) { setFlags(IFF_RUNNING); ifaddr_t *addresses; /* * Also send gratuitous ARPs to notify other nodes about * the address change. */ if (ifnet_get_address_list_family(ifp, &addresses, AF_INET) == 0) { int i; for (i = 0; addresses[i] != NULL; i++) inet_arp_init_ifaddr(ifp, addresses[i]); ifnet_free_address_list(addresses); } } } return errnoFromReturn(ret); } //--------------------------------------------------------------------------- // Handle SIOCADDMULTI ioctl command. SInt IOFWInterface::syncSIOCADDMULTI(IONetworkController * ctr) { IOReturn ret; // Make sure multicast filter is active. ret = enableFilter(ctr, gIONetworkFilterGroup, kIOPacketFilterMulticast); if ( ret == kIOReturnSuccess ) { // Load multicast addresses only if the filter was activated. ret = setupMulticastFilter(ctr); // If the list is now empty, then deactivate the multicast filter. if ( _mcAddrCount == 0 ) { IOReturn dret = disableFilter(ctr, gIONetworkFilterGroup, kIOPacketFilterMulticast); if (ret == kIOReturnSuccess) ret = dret; } } return errnoFromReturn(ret); } //--------------------------------------------------------------------------- // Handle SIOCDELMULTI ioctl command. SInt IOFWInterface::syncSIOCDELMULTI(IONetworkController * ctr) { return syncSIOCADDMULTI(ctr); } //--------------------------------------------------------------------------- // Handle SIOCSIFMTU ioctl. int IOFWInterface::syncSIOCSIFMTU( IONetworkController * ctr, struct ifreq * ifr ) { #define MTU_TO_FRAMESIZE(x) \ ((x) + kIOFWCRCSize + sizeof(struct firewire_header)) SInt32 error = 0; UInt32 size; UInt32 maxSize = kIOFWMaxPacketSize; // 1518 UInt32 ifrSize = MTU_TO_FRAMESIZE( ifr->ifr_mtu ); UInt32 ctrSize = MTU_TO_FRAMESIZE( getMaxTransferUnit() ); // If change is not necessary, return success without getting the // controller involved. if ( ctrSize == ifrSize ) return 0; // no change required if ( ctr->getMaxPacketSize( &size ) == kIOReturnSuccess ) maxSize = max( size, kIOFWMaxPacketSize ); if ( ifrSize > maxSize ) return EINVAL; // MTU is too large for the controller. // Message the controller if the new MTU requires a non standard // frame size, or if the controller is currently programmed to // support an extended frame size which is no longer required. if ( max( ifrSize, ctrSize ) > kIOFWMaxPacketSize ) { IOReturn ret; ret = ctr->setMaxPacketSize( max(ifrSize, kIOFWMaxPacketSize) ); error = errnoFromReturn( ret ); } if ( error == 0 ) { // Success, update the MTU in ifnet. setMaxTransferUnit( ifr->ifr_mtu ); } return error; } //--------------------------------------------------------------------------- int IOFWInterface::syncSIOCSIFLLADDR( IONetworkController * ctr, const char * lladdr, int len ) { unsigned char tempaddr[kIOFWAddressSize]; OSData *hardAddr; if(len != kIOFWAddressSize) return EINVAL; if (_ctrEnabled != true) /* reject if interface is down */ return (ENETDOWN); // keep a backup in case stack refuses our change hardAddr = OSDynamicCast(OSData, getProperty(kIOMACAddress)); if(hardAddr && hardAddr->getLength() == kIOFWAddressSize) bcopy(hardAddr->getBytesNoCopy(), tempaddr, kIOFWAddressSize); // change the hardware- we do it before the stack, in case the stack // needs to generate traffic as a result. if ( ctr->setHardwareAddress( lladdr, len ) == kIOReturnSuccess ) { if( ifnet_set_lladdr(getIfnet(), lladdr, len) ) //uh-oh, stack didn't like this { // restore previous address if(hardAddr) ctr->setHardwareAddress(tempaddr, sizeof(tempaddr)); return EINVAL; } setProperty(kIOMACAddress, (void *)lladdr, len); DLOG("%s: SIOCSIFLLADDR %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", ctr->getName(), lladdr[0], lladdr[1], lladdr[2], lladdr[3], lladdr[4], lladdr[5], lladdr[6], lladdr[7]); } return 0; } //--------------------------------------------------------------------------- // Enable a packet filter. #define getOneFilter(x) ((x) & (~((x) - 1))) IOReturn IOFWInterface::enableFilter(IONetworkController * ctr, const OSSymbol * group, UInt32 filters, IOOptionBits options) { IOReturn ret = kIOReturnSuccess; if ( options & kFilterOptionNotInsideGate ) { options &= ~kFilterOptionNotInsideGate; return ctr->executeCommand( this, /* client */ (IONetworkController::Action) &IOFWInterface::enableFilter, this, /* target */ (void *) ctr, /* param0 */ (void *) group, /* param1 */ (void *) filters, /* param2 */ (void *) options ); /* param3 */ } if ( options & kFilterOptionDisableZeroBits ) { ret = disableFilter(ctr, group, ~filters, options); if ( ret != kIOReturnSuccess) return ret; } // If the controller does not support the packet filter, // there's no need to proceed. if (( GET_SUPPORTED_FILTERS(group) & filters ) != filters) return kIOReturnUnsupported; do { // Add new filter to the set of required filters. UInt32 reqFilters = GET_REQUIRED_FILTERS(group) | filters; UInt32 actFilters = GET_ACTIVE_FILTERS(group); UInt32 resFilters = ( actFilters ^ reqFilters ); if ( (options & kFilterOptionSyncPendingIO) == 0 ) { // Restrict filter update by using 'filters' as a mask. resFilters &= filters; } while ( resFilters && ((options & kFilterOptionDeferIO) == 0) ) { UInt32 oneFilter = getOneFilter(resFilters); // Send a command to the controller driver. ret = ctr->enablePacketFilter(group, oneFilter, actFilters, options); if ( ret != kIOReturnSuccess ) break; resFilters &= ~oneFilter; actFilters |= oneFilter; } if ( (options & kFilterOptionNoStateChange) == 0 ) SET_REQUIRED_FILTERS(group, reqFilters); SET_ACTIVE_FILTERS(group, actFilters); } while (false); return ret; } //--------------------------------------------------------------------------- // Disable a packet filter. IOReturn IOFWInterface::disableFilter(IONetworkController * ctr, const OSSymbol * group, UInt32 filters, IOOptionBits options) { IOReturn ret = kIOReturnSuccess; do { // Remove specified filter from the set of required filters. UInt32 reqFilters = GET_REQUIRED_FILTERS(group) & ~filters; UInt32 actFilters = GET_ACTIVE_FILTERS(group); UInt32 resFilters = ( actFilters ^ reqFilters ) & filters; while ( resFilters && ((options & kFilterOptionDeferIO) == 0) ) { UInt32 oneFilter = getOneFilter(resFilters); // Send a command to the controller driver. ret = ctr->disablePacketFilter(group, oneFilter, actFilters, options); if ( ret != kIOReturnSuccess ) break; resFilters &= ~oneFilter; actFilters &= ~oneFilter; } if ( (options & kFilterOptionNoStateChange) == 0 ) SET_REQUIRED_FILTERS(group, reqFilters); SET_ACTIVE_FILTERS(group, actFilters); } while (false); return ret; } //--------------------------------------------------------------------------- // Cache the list of multicast addresses and send a command to the // controller to update the multicast list. IOReturn IOFWInterface::setupMulticastFilter(IONetworkController * ctr) { void * multiAddrs = 0; UInt mcount; OSData * mcData = 0; ifnet_t interface; struct sockaddr dlAddress; IOReturn ret = kIOReturnSuccess; bool ok; ifmultiaddr_t *addressList; interface = getIfnet(); assert(interface); // get the list and count how many mcast link addresses there are if(ifnet_get_multicast_list(interface, &addressList)) return kIOReturnNoMemory; mcount = 0; for(int i=0; addressList[i]; i++) { ifmaddr_address(addressList[i], &dlAddress, sizeof(dlAddress)); if (dlAddress.sa_family == AF_UNSPEC || dlAddress.sa_family == AF_LINK) mcount++; } _mcAddrCount = mcount; // now rewalk the list and copy the addresses to a format suitable to give to the controller if ( mcount ) { char * addrp; mcData = OSData::withCapacity(mcount * FIREWIRE_ADDR_LEN); if (!mcData) { DLOG("%s: no memory for multicast address list\n", getName()); return kIOReturnNoMemory; } // Loop through the list and copy the link multicast // address to the OSData. for(int i = 0; addressList[i]; i++) { //retrieve the datalink mcast address ifmaddr_address(addressList[i], &dlAddress, sizeof(dlAddress)); if (dlAddress.sa_family == AF_UNSPEC) addrp = &dlAddress.sa_data[0]; else if (dlAddress.sa_family == AF_LINK) addrp = LLADDR((struct sockaddr_dl *)&dlAddress); else continue; ok = mcData->appendBytes((const void *) addrp, FIREWIRE_ADDR_LEN); assert(ok); } multiAddrs = (void *) mcData->getBytesNoCopy(); assert(multiAddrs); } // Issue a controller command to setup the multicast filter. ret = ((IOFWController *)ctr)->setMulticastList((IOFWAddress *) multiAddrs, mcount); if (mcData) { if (ret == kIOReturnSuccess) setProperty(kIOMulticastAddressList, mcData); mcData->release(); } else { removeProperty(kIOMulticastAddressList); } ifnet_free_multicast_list(addressList); return ret; } //--------------------------------------------------------------------------- // Power management support. // // Handlers called, with the controller's gate closed, in response to a // controller power state change. IOReturn IOFWInterface::controllerWillChangePowerState( IONetworkController * ctr, IOPMPowerFlags flags, UInt32 stateNumber, IOService * policyMaker ) { if ( ( (flags & IOPMDeviceUsable ) == 0) && ( _controllerLostPower == false ) && _ctrEnabled ) { UInt32 filters; _controllerLostPower = true; // Get the "aggressiveness" factor from the policy maker. ctr->getAggressiveness( kPMEthernetWakeOnLANSettings, &filters ); filters &= GET_SUPPORTED_FILTERS( gIOEthernetWakeOnLANFilterGroup ); // Is the link up? If it is, leave the WOL filters intact, // otherwise mask out the WOL filters that would not function // without a proper link. This will reduce power consumption // for cases when a machine is put to sleep and there is no // network connection. OSNumber * linkStatusNumber = (OSNumber *) ctr->getProperty( kIOLinkStatus ); if ( ( linkStatusNumber == 0 ) || ( ( linkStatusNumber->unsigned32BitValue() & (kIONetworkLinkValid | kIONetworkLinkActive) ) == kIONetworkLinkValid ) ) { filters &= ~( kIOFWWakeOnMagicPacket | kIOFWWakeOnPacketAddressMatch ); } // Before controller is disabled, program its wake up filters. // The WOL setting is formed by a bitwise OR between the WOL filter // settings, and the aggresiveness factor from the policy maker. enableFilter( ctr, gIOEthernetWakeOnLANFilterGroup, filters, kFilterOptionNoStateChange | kFilterOptionSyncPendingIO ); // Call SIOCSIFFLAGS handler to disable the controller, // and mark the interface as Not Running. syncSIOCSIFFLAGS(ctr); } return super::controllerWillChangePowerState( ctr, flags, stateNumber, policyMaker ); } IOReturn IOFWInterface::controllerDidChangePowerState( IONetworkController * ctr, IOPMPowerFlags flags, UInt32 stateNumber, IOService * policyMaker ) { IOReturn ret = super::controllerDidChangePowerState( ctr, flags, stateNumber, policyMaker ); if ( ( flags & IOPMDeviceUsable ) && ( _controllerLostPower == true ) ) { _controllerLostPower = false; // Clear _controllerLostPower, then call the SIOCSIFFLAGS handler to // perhaps enable the controller, restore all FireWire controller // state, then mark the interface as Running. syncSIOCSIFFLAGS(ctr); } return ret; } #define kIONetworkInterfaceProperties "IONetworkInterfaceProperties" //--------------------------------------------------------------------------- // Handle a request to set properties from kernel or non-kernel clients. // For non-kernel clients, the preferred mechanism is through an user // client. IOReturn IOFWInterface::setProperties( OSObject * properties ) { IOReturn ret; OSDictionary * dict = (OSDictionary *) properties; OSNumber * num; // Call IONetworkInterface::setProperties() first. ret = super::setProperties(properties); if ( (ret == kIOReturnSuccess) || (ret == kIOReturnUnsupported) ) { dict = OSDynamicCast( OSDictionary, dict->getObject(kIONetworkInterfaceProperties) ); if ( dict ) { dict = OSDynamicCast( OSDictionary, dict->getObject(kIORequiredPacketFilters) ); if ( dict ) { num = OSDynamicCast( OSNumber, dict->getObject(kIOFWWakeOnLANFilterGroup) ); if ( num ) { ret = enableFilter( getController(), gIOEthernetWakeOnLANFilterGroup, num->unsigned32BitValue(), kFilterOptionDeferIO | kFilterOptionNotInsideGate | kFilterOptionDisableZeroBits ); } } } } return ret; } //--------------------------------------------------------------------------- // willTerminate bool IOFWInterface::willTerminate( IOService * provider, IOOptionBits options ) { bool ret = super::willTerminate( provider, options ); // We assume that willTerminate() is always called from the // provider's work loop context. // Once the gated ioctl path has been blocked, disable the controller. // The hardware may have already been removed from the system. if ( _ctrEnabled && getController() ) { DLOG("IOFWInterface::willTerminate disabling controller\n"); getController()->doDisable( this ); _ctrEnabled = false; } return ret; } //--------------------------------------------------------------------------- int firewire_init_if(ifnet_t ifp); IOReturn IOFWInterface::attachToDataLinkLayer( IOOptionBits options, void * parameter ) { IOReturn ret = 0; unsigned char lladdr[kIOFWAddressSize]; ret = super::attachToDataLinkLayer( options, parameter ); if (ret == kIOReturnSuccess) { ifnet_set_baudrate(getIfnet(), 10000000); bpfattach(getIfnet(), DLT_APPLE_IP_OVER_IEEE1394, sizeof(struct firewire_header) ); IOFWAddress *addr = (IOFWAddress *) _uniqueID->getBytesNoCopy(); bcopy(addr->bytes, lladdr, kIOFWAddressSize); if(ifnet_set_lladdr(getIfnet(), lladdr, kIOFWAddressSize) != 0) DLOG("ifnet_set_lladdr failure in %s:%d", __FILE__, __LINE__); if (ivedonethis) { ivedonethis++; return kIOReturnSuccess; } ivedonethis++; // IPv4 proto register ret = dlil_reg_proto_module(PF_INET, APPLE_IF_FAM_FIREWIRE, firewire_attach_inet, dlil_detach_protocol); if(ret == EEXIST || ret == 0) ret = kIOReturnSuccess; else DLOG("ERROR: dlil_reg_proto_module for IPv4 over FIREWIRE %x", ret); // IPv6 proto register ret = dlil_reg_proto_module(PF_INET6, APPLE_IF_FAM_FIREWIRE, firewire_attach_inet6, dlil_detach_protocol); if(ret == EEXIST || ret == 0) ret = kIOReturnSuccess; else DLOG("ERROR: dlil_reg_proto_module for IPv6 over FIREWIRE %x", ret); } return ret; } void IOFWInterface::detachFromDataLinkLayer( IOOptionBits options, void * parameter ) { int ret = 0; int *detach_ret = (int*)parameter; if (ivedonethis == 1) { ret = dlil_dereg_proto_module(PF_INET, APPLE_IF_FAM_FIREWIRE); if(ret == ENOENT) DLOG("no protocol module for IPv4 to deregister for APPLE_IF_FAM_FIREWIRE"); ret = dlil_dereg_proto_module(PF_INET6, APPLE_IF_FAM_FIREWIRE); if(ret == ENOENT) DLOG("no protocol module for IPv6 to deregister for APPLE_IF_FAM_FIREWIRE"); dlil_dereg_if_modules(APPLE_IF_FAM_FIREWIRE); } ivedonethis--; ret = dlil_if_detach((struct ifnet*)getIfnet()); if(detach_ret) *detach_ret = ret; } void IOFWInterface::setIfnetMTU(UInt32 mtu) { setMaxTransferUnit(mtu-FIREWIRE_HDR_LEN); } void IOFWInterface::setFamilyCookie(void *data) { _familyCookie = data; }