#include "../../KernelHeaders/IOKit/IOFWIPBusInterface.h" #include #define BCOPY(s, d, l) do { bcopy((void *) s, (void *) d, l); } while(0) struct { UCHAR specifierID[3]; // 24-bit RID UCHAR version[3]; // 24-bit version } gaspVal = { {0x00, 0x00, 0x5E}, {0x00, 0x00, 0x01} }; extern "C" { /*! @function watchdog @abstract watchdog timer - cleans the Link control block's stale arb's, drb's and rcb's. @param timer - IOTimerEventsource. @result void. */ void watchdog(OSObject *, IOTimerEventSource *); } #define super IOService OSDefineMetaClassAndStructors(IOFWIPBusInterface, IOService) OSDefineMetaClassAndStructors(ARB, OSObject) OSDefineMetaClassAndStructors(DRB, OSObject) OSDefineMetaClassAndStructors(RCB, OSObject) bool IOFWIPBusInterface::init(IOFireWireIP *primaryInterface) { fIPLocalNode = OSDynamicCast(IOFireWireIP, primaryInterface); if(not fIPLocalNode) return false; fControl = fIPLocalNode->getController(); if( not fControl) return false; if ( not super::init() ) return false; fMemoryPool = 0; fIP1394AddressSpace = 0; fAsyncCmdPool = 0; fAsyncStreamTxCmdPool = 0; fAsyncTransitSet = 0; fAsyncStreamTransitSet = 0; // set the secondary interface handlers with IOFireWireIP if( not attachIOFireWireIP ( fIPLocalNode ) ) return false; fControl->retain(); fIPLocalNode->retain(); fControl->resetBus(); fStarted = true; return true; } bool IOFWIPBusInterface::finalize(IOOptionBits options) { IORecursiveLockLock(fIPLock); fStarted = false; fIPLocalNode->deRegisterFWIPPrivateHandlers(); IORecursiveLockUnlock(fIPLock); IOFWIPAsyncWriteCommand *cmd1 = NULL; OSCollectionIterator * iterator = OSCollectionIterator::withCollection( fAsyncTransitSet ); while( NULL != (cmd1 = OSDynamicCast(IOFWIPAsyncWriteCommand, iterator->getNextObject())) ) { if(cmd1->Busy()) cmd1->wait(); } iterator->release(); fAsyncTransitSet->flushCollection(); fAsyncTransitSet->free(); fAsyncTransitSet = NULL; IOFWIPAsyncStreamTxCommand *cmd2 = NULL; iterator = OSCollectionIterator::withCollection( fAsyncStreamTransitSet ); while( NULL != (cmd2 = OSDynamicCast(IOFWIPAsyncStreamTxCommand, iterator->getNextObject())) ) { if(cmd2->Busy()) cmd2->wait(); } iterator->release(); fAsyncStreamTransitSet->flushCollection(); fAsyncStreamTransitSet->free(); fAsyncStreamTransitSet = NULL; return super::finalize(options); } void IOFWIPBusInterface::free() { detachIOFireWireIP(); super::free(); } IOReturn IOFWIPBusInterface::message(UInt32 type, IOService *provider, void *argument) { IOReturn res = kIOReturnSuccess; switch (type) { case kIOMessageServiceIsTerminated: break; case kIOMessageServiceIsSuspended: res = kIOReturnSuccess; if(fStarted == true) stopReceivingBroadcast(); break; case kIOMessageServiceIsResumed: res = kIOReturnSuccess; if(fStarted == true) { resetARBCache(); resetRCBCache(); resetMcapState(); updateBroadcastValues(true); startReceivingBroadcast(fLcb->maxBroadcastSpeed); } break; case kIOMessageServiceIsRequestingClose: res = kIOReturnSuccess; break; default: res = kIOReturnUnsupported; break; } return res; } bool IOFWIPBusInterface::attachIOFireWireIP(IOFireWireIP *provider) { fIPLocalNode = provider; fLcb = fIPLocalNode->getLcb(); fIPLock = fIPLocalNode->getIPLock(); workLoop = fIPLocalNode->getWorkLoop(); unicastArb = OSSet::withCapacity(kUnicastArbs); if(unicastArb == 0) return false; multicastArb = OSSet::withCapacity(kMulticastArbs); if(multicastArb == 0) return false; activeDrb = OSSet::withCapacity(kActiveDrbs); if(activeDrb == 0) return false; activeRcb = OSSet::withCapacity(kActiveRcbs); if(activeRcb == 0) return false; mcapState = OSDictionary::withCapacity(kMaxChannels); if(mcapState == 0) return false; fAsyncStreamTransitSet = OSSet::withCapacity(kMaxAsyncStreamCommands); if(fAsyncStreamTransitSet == 0) return false; fAsyncTransitSet = OSSet::withCapacity(kMaxAsyncCommands); if(fAsyncTransitSet == 0) return false; // Does calculate the fMaxTxAsyncDoubleBuffer & fAsyncRxIsocPacketSize; isAppleLynx(); // Allocate Timer event source timerSource = IOTimerEventSource::timerEventSource ( ( OSObject* ) this, ( IOTimerEventSource::Action ) &watchdog); if ( timerSource == NULL ) { IOLog( "IOFireWireIP::start - Couldn't allocate timer event source\n" ); return false; } if ( workLoop->addEventSource ( timerSource ) != kIOReturnSuccess ) { IOLog( "IOFireWireIP::start - Couldn't add timer event source\n" ); return false; } // Asyncstream hook up to recieve the broadcast packets fBroadcastReceiveClient = new IOFWAsyncStreamRxCommand; if ( not fBroadcastReceiveClient ) return false; if ( not fBroadcastReceiveClient->initAll(0x1f, rxAsyncStream, fControl, fMaxRxIsocPacketSize, this ) ) return false; fIPLocalNode->updateMTU(bOnLynx); // Create pseudo address space if ( createIPFifoAddress (MAX_FIFO_SIZE) != kIOReturnSuccess) return false; // might eventually start the timer timerSource->setTimeoutMS ( WATCHDOG_TIMER_MS ); IOFireWireIPPrivateHandlers privateHandlers; privateHandlers.newService = this; privateHandlers.transmitPacket = getOutputHandler(); privateHandlers.updateARPCache = getARPCacheHandler(); fIPLocalNode->registerFWIPPrivateHandlers(&privateHandlers); attach(fIPLocalNode); // IOLog("attachIOFireWireIP- \n"); return true; } void IOFWIPBusInterface::detachIOFireWireIP() { IORecursiveLockLock(fIPLock); // Release the Asyncstream receive broadcast client if(fBroadcastReceiveClient != NULL) fBroadcastReceiveClient->release(); fBroadcastReceiveClient = NULL; if (fIP1394AddressSpace != NULL){ fIP1394AddressSpace->deactivate(); fIP1394AddressSpace->release(); fIP1394AddressSpace = NULL; } if(timerSource != NULL) { if (workLoop != NULL) workLoop->removeEventSource(timerSource); timerSource->release(); } freeAsyncCmdPool(); freeAsyncStreamCmdPool(); if(unicastArb != NULL) { unicastArb->flushCollection(); unicastArb->free(); } if(multicastArb != NULL) { multicastArb->flushCollection(); multicastArb->free(); } if(activeDrb != NULL) { activeDrb->flushCollection(); activeDrb->free(); } if(activeRcb != NULL) { activeRcb->flushCollection(); activeRcb->free(); } if(mcapState != NULL) { mcapState->flushCollection(); mcapState->free(); } if(fControl) fControl->release(); fControl = NULL; if(fIPLocalNode) fIPLocalNode->release(); fIPLocalNode = NULL; IORecursiveLockUnlock(fIPLock); } void IOFWIPBusInterface::decrementUnitCount() { recursiveScopeLock lock(fIPLock); if(fUnitCount > 0) fUnitCount--; } void IOFWIPBusInterface::incrementUnitCount() { recursiveScopeLock lock(fIPLock); fUnitCount++; } UInt16 IOFWIPBusInterface::getUnitCount() { recursiveScopeLock lock(fIPLock); return fUnitCount; } /*! @function fwIPUnitAttach @abstract Callback for a Unit Attach of type IPv4 or IPv6 @result void. */ void IOFWIPBusInterface::fwIPUnitAttach() { incrementUnitCount(); updateBroadcastValues(true); } /*! @function fwIPUnitTerminate @abstract Callback for a Unit detach of type IP1394 @result void. */ void IOFWIPBusInterface::fwIPUnitTerminate() { decrementUnitCount(); updateBroadcastValues(true); } /*! @function updateBroadcastValues @abstract Updates the max broadcast payload and speed @param reset - useful to know whether to start from beginning. @result void. */ void IOFWIPBusInterface::updateBroadcastValues(bool reset) { IOFireWireDevice *remoteDevice = NULL; IORecursiveLockLock(fIPLock); if(reset) { IOFireWireDevice *localDevice = (IOFireWireDevice*)(fIPLocalNode->getDevice()); fLcb->maxBroadcastPayload = localDevice->maxPackLog(true); fLcb->maxBroadcastSpeed = localDevice->FWSpeed(); // Update our own max payload fLcb->ownMaxPayload = localDevice->maxPackLog(true); // Update the nodeID localDevice->getNodeIDGeneration(fLcb->busGeneration, fLcb->ownNodeID); // Update the speed fLcb->ownMaxSpeed = localDevice->FWSpeed(); } // Display the active DRB DRB *drb = NULL; OSCollectionIterator * iterator = OSCollectionIterator::withCollection( activeDrb ); while( NULL != (drb = OSDynamicCast(DRB, iterator->getNextObject())) ) { // Disappeared device, so lets skip if(drb->timer > 0) continue; remoteDevice = (IOFireWireDevice*)drb->deviceID; if(fLcb->maxBroadcastSpeed > drb->maxSpeed) fLcb->maxBroadcastSpeed = drb->maxSpeed; if(fLcb->maxBroadcastPayload > drb->maxPayload) fLcb->maxBroadcastPayload = drb->maxPayload; } iterator->release(); updateLinkStatus(); IORecursiveLockUnlock(fIPLock); } /*! @function updateLinkStatus @abstract Updates the link status based on maxbroadcast speed & payload. @param None. @result void. */ void IOFWIPBusInterface::updateLinkStatus() { IORecursiveLockLock(fIPLock); // set medium inactive, before setting it to active for radar 3300357 fIPLocalNode->setLinkStatus(kIONetworkLinkValid, fIPLocalNode->getCurrentMedium(), 0); // lets update the link status as active, if only units are greater than 0 if(fUnitCount > 0) fIPLocalNode->setLinkStatus (kIONetworkLinkActive | kIONetworkLinkValid, fIPLocalNode->getCurrentMedium(), (1 << fLcb->maxBroadcastSpeed) * 100 * 1000000); fLcb->ownHardwareAddress.spd = fLcb->maxBroadcastSpeed; // fix to enable the arp/dhcp support from network pref pane fIPLocalNode->setProperty(kIOFWHWAddr, (void *)&fLcb->ownHardwareAddress, sizeof(IP1394_HDW_ADDR)); IORecursiveLockUnlock(fIPLock); } /*! @function createIPFifoAddress @abstract creates the pseudo address space for IP over Firewire. @param UInt32 fifosize - size of the pseudo address space @result IOReturn - kIOReturnSuccess or error if failure. */ IOReturn IOFWIPBusInterface::createIPFifoAddress(UInt32 fifosize) { IOReturn ioStat = kIOReturnSuccess; // add csr address space fIP1394AddressSpace = fControl->createPseudoAddressSpace(&fIP1394Address, fifosize, NULL, &rxUnicast, this); if (fIP1394AddressSpace == NULL) ioStat = kIOReturnNoMemory; if(ioStat == kIOReturnSuccess ) // change for performance, coalescing incoming writes fIP1394AddressSpace->setARxReqIntCompleteHandler(this, &rxUnicastComplete); fLcb->ownHardwareAddress.unicastFifoHi = fIP1394Address.addressHi; fLcb->ownHardwareAddress.unicastFifoLo = fIP1394Address.addressLo; // fix to enable the arp/dhcp support from network pref pane fIPLocalNode->setProperty(kIOFWHWAddr, (void *)&fLcb->ownHardwareAddress, sizeof(IP1394_HDW_ADDR)); if(ioStat == kIOReturnSuccess ) ioStat = fIP1394AddressSpace->activate(); if(ioStat != kIOReturnSuccess) IOLog("IOFireWireIP PseudoAddressSpace Activate failure status %d\n", ioStat); // end of csr address space return ioStat; } /*! @function isAppleLynx @abstract checks whether the FWIM is for isAppleLynx H/W, if not sets appropriate performance related params @param none. @result Returns void. */ void IOFWIPBusInterface::isAppleLynx() { IORegistryEntry *parent = fControl->getParentEntry(gIOServicePlane); if(strcmp(parent->getName(gIOServicePlane), "AppleLynx") == 0) { bOnLynx = true; fMaxRxIsocPacketSize = 2048; fMaxTxAsyncDoubleBuffer = 1 << 9; } else { bOnLynx = false; fMaxRxIsocPacketSize = 4096; fMaxTxAsyncDoubleBuffer = 1 << ((IOFireWireNub*)(fIPLocalNode->getDevice()))->maxPackLog(true); } parent = parent->getParentEntry(gIOServicePlane); } /*! @function initAsyncCmdPool @abstract constructs Asynchronous command objects and queues them in the pool @param none. @result Returns kIOReturnSuccess if it was successful, else kIOReturnNoMemory. */ UInt32 IOFWIPBusInterface::initAsyncCmdPool() { IOFWIPAsyncWriteCommand *cmd1 = NULL; IOReturn status = kIOReturnSuccess; int i = 0; if(fAsyncCmdPool == NULL) fAsyncCmdPool = IOCommandPool::withWorkLoop(workLoop); for(i=0; i<=kMaxAsyncCommands; i++){ FWAddress addr; // setup block write addr.addressHi = 0xdead; addr.addressLo = 0xbabeface; // Create a IP Async write command cmd1 = new IOFWIPAsyncWriteCommand; if(!cmd1) { status = kIOReturnNoMemory; break; } // Initialize the write command if(!cmd1->initAll(fIPLocalNode, fMaxTxAsyncDoubleBuffer, addr, txCompleteBlockWrite, this, false)) { status = kIOReturnNoMemory; cmd1->release(); break; } // Queue the command in the command pool fAsyncCmdPool->returnCommand(cmd1); fAsyncTransitSet->setObject(cmd1); } return status; } IOFWIPAsyncWriteCommand *IOFWIPBusInterface::getAsyncCommand(const mbuf_t m, bool block, bool *deferNotify) { IOFWIPAsyncWriteCommand * cmd = (IOFWIPAsyncWriteCommand *)fAsyncCmdPool->getCommand(block); if(cmd == NULL) { fIPLocalNode->freePacket((struct mbuf*)m); fIPLocalNode->networkStatAdd(&(fIPLocalNode->getNetStats())->outputErrors); fIPLocalNode->fNoCommands++; return NULL; } fIPLocalNode->fActiveCmds++; if((fIPLocalNode->fActiveCmds % fIPLocalNode->fInActiveCmds) >= kLowWaterMark) *deferNotify = false; return cmd; } void IOFWIPBusInterface::returnAsyncCommand(IOFWIPAsyncWriteCommand *cmd) { if(cmd->notDoubleComplete()) { if(fAsyncCmdPool != NULL) fAsyncCmdPool->returnCommand(cmd); else cmd->release(); fIPLocalNode->fInActiveCmds++; } else fIPLocalNode->fDoubleCompletes++; } /*! @function initAsyncStreamCmdPool @abstract constructs AsyncStreamcommand objects and queues them in the pool @param none. @result Returns kIOReturnSuccess if it was successful, else kIOReturnNoMemory. */ UInt32 IOFWIPBusInterface::initAsyncStreamCmdPool() { IOReturn status = kIOReturnSuccess; int i = 0; if(fAsyncStreamTxCmdPool == NULL) fAsyncStreamTxCmdPool = IOCommandPool::withWorkLoop(workLoop); IOFWIPAsyncStreamTxCommand *cmd2 = NULL; for(i=0; i<=kMaxAsyncStreamCommands; i++){ // Create a IP Async write command cmd2 = new IOFWIPAsyncStreamTxCommand; if(!cmd2) { status = kIOReturnNoMemory; break; } // Initialize the write command if(!cmd2->initAll(fIPLocalNode, fControl, 0, 0, 0, GASP_TAG, fMaxTxAsyncDoubleBuffer, kFWSpeed100MBit, txCompleteAsyncStream, this)) { status = kIOReturnNoMemory; cmd2->release(); break; } // Queue the command in the command pool fAsyncStreamTxCmdPool->returnCommand(cmd2); fAsyncStreamTransitSet->setObject(cmd2); } return status; } /*! @function freeIPCmdPool @abstract frees both Async and AsyncStream command objects from the pool @param none. @result void. */ void IOFWIPBusInterface::freeAsyncCmdPool() { IOFWIPAsyncWriteCommand *cmd1 = NULL; UInt32 freeCount = 0; if(fAsyncCmdPool == NULL) return; // Should block till all outstanding commands are freed do { cmd1 = (IOFWIPAsyncWriteCommand*)fAsyncCmdPool->getCommand(false); if(cmd1 != NULL) { freeCount++; // release the command cmd1->release(); } }while(cmd1 != NULL); fAsyncCmdPool->release(); fAsyncCmdPool = NULL; } /*! @function freeIPCmdPool @abstract frees both Async and AsyncStream command objects from the pool @param none. @result void. */ void IOFWIPBusInterface::freeAsyncStreamCmdPool() { if(fAsyncStreamTxCmdPool == NULL) return; IOFWIPAsyncStreamTxCommand *cmd2 = NULL; UInt32 freeCount = 0; // Should block till all outstanding commands are freed do{ cmd2 = (IOFWIPAsyncStreamTxCommand*)fAsyncStreamTxCmdPool->getCommand(false); if(cmd2 != NULL) { freeCount++; // release the command cmd2->release(); } }while(cmd2 != NULL); fAsyncStreamTxCmdPool->release(); fAsyncStreamTxCmdPool = NULL; return; } IOReturn IOFWIPBusInterface::stopReceivingBroadcast() { return fBroadcastReceiveClient->stop(); } IOReturn IOFWIPBusInterface::startReceivingBroadcast(IOFWSpeed speed) { return fBroadcastReceiveClient->start(speed); } IOUpdateARPCache IOFWIPBusInterface::getARPCacheHandler() const { return (IOUpdateARPCache) &IOFWIPBusInterface::staticUpdateARPCache; } IOTransmitPacket IOFWIPBusInterface::getOutputHandler() const { return (IOTransmitPacket) &IOFWIPBusInterface::staticOutputPacket; } /*! @function initDRBwithDevice @abstract create device reference block for a device object. @param lcb - the firewire link control block for this interface. @param eui64 - global unique id of a device on the bus. @param fDevObj - IOFireWireNub that has to be linked with the device reference block. @param itsMac - Indicates whether the destination is Macintosh or not. @result DRB* - pointer to the device reference block. */ DRB *IOFWIPBusInterface::initDRBwithDevice(UWIDE eui64, IOFireWireNub *device, bool itsMac) { DRB *drb = getDrbFromEui64(eui64); if(not drb) { if ((drb = new DRB) == NULL) return NULL; } CSRNodeUniqueID fwuid = device->getUniqueID(); if(itsMac) fIPLocalNode->makeEthernetAddress(&fwuid, drb->fwaddr, GUID_TYPE); else fIPLocalNode->getBytesFromGUID((void*)(&fwuid), drb->fwaddr, GUID_TYPE); drb->deviceID = (UInt32)device; drb->timer = 0; drb->eui64 = eui64; drb->itsMac = itsMac; drb->maxSpeed = kFWSpeed100MBit; drb->maxSpeed = device->FWSpeed(); drb->maxPayload = device->maxPackLog(true); activeDrb->setObject(drb); return drb; } /*! @function getMTU @abstract returns the MTU (Max Transmission Unit) supported by the IOFireWireIP. @param None. @result UInt32 - MTU value. */ UInt32 IOFWIPBusInterface::getMTU() { UInt32 mtu = 0; mtu = FIREWIRE_MTU; return mtu; } UInt32 IOFWIPBusInterface::outputPacket(mbuf_t pkt, void * param) { register struct firewire_header *fwh; int status = kIOReturnError; fwh = (struct firewire_header*)mbuf_data(pkt); switch(htons(fwh->fw_type)) { case FWTYPE_IPV6: addNDPOptions(pkt); status = txIP(pkt, fLcb->ownNodeID, fLcb->busGeneration, fLcb->ownMaxPayload, fLcb->maxBroadcastPayload, fLcb->maxBroadcastSpeed, FWTYPE_IPV6); break; case FWTYPE_IP: status = txIP(pkt, fLcb->ownNodeID, fLcb->busGeneration, fLcb->ownMaxPayload, fLcb->maxBroadcastPayload, fLcb->maxBroadcastSpeed, FWTYPE_IP); break; case FWTYPE_ARP: status = txARP(pkt, fLcb->ownNodeID, fLcb->busGeneration, fLcb->maxBroadcastSpeed); break; default : fIPLocalNode->freePacket((struct mbuf*)pkt); break; } if(status == kIOFireWireOutOfTLabels || status == kIOReturnNoResources) { fTxStatus = kIOReturnOutputStall; status = kIOReturnOutputStall; } else status = kIOReturnOutputSuccess; return status; } /*! @function txComplete @abstract Callback for the Async write complete @param refcon - callback data. @param status - status of the command. @param device - device that the command was send to. @param fwCmd - command object which generated the transaction. @result void. */ void IOFWIPBusInterface::txCompleteBlockWrite(void *refcon, IOReturn status, IOFireWireNub *device, IOFWCommand *fwCmd) { IOFWIPBusInterface *fwIPPriv = (IOFWIPBusInterface*)refcon; if(not fwIPPriv) return; IOFireWireIP *fwIPObject = OSDynamicCast(IOFireWireIP, fwIPPriv->fIPLocalNode); if(not fwIPObject) return; IOFWIPAsyncWriteCommand *cmd = OSDynamicCast(IOFWIPAsyncWriteCommand, fwCmd); if(not cmd) return; fwIPObject->closeIPGate(); // Make sure that we start the queue again, only after we have stalled, not for every successful completion if(fwIPPriv->fTxStatus == kIOReturnOutputStall) { fwIPObject->fStalls++; fwIPPriv->fTxStatus = kIOReturnOutputSuccess; } // Only in case of kIOFireWireOutOfTLabels, we ignore freeing of Mbuf if(status == kIOReturnSuccess) { // We get callback 1 packet at a time, so we can increment by 1 fwIPObject->networkStatAdd(&(fwIPObject->getNetStats())->outputPackets); fwIPObject->fTxUni++; } else { // Increment error output packets fwIPObject->networkStatAdd(&(fwIPObject->getNetStats())->outputErrors); fwIPObject->fCallErrs++; } cmd->resetDescriptor(status); fwIPPriv->returnAsyncCommand(cmd); // Fix to over kill servicing the queue if ((status != kIOFireWireOutOfTLabels) && (status != kIOReturnNoResources) && ((fwIPObject->fActiveCmds % fwIPObject->fInActiveCmds) <= (kLowWaterMark+10))) fwIPObject->transmitQueue->service(kTransmitQueueRestart); else fwIPObject->fMissedQRestarts++; fwIPObject->openIPGate(); return; } /*! @function txAsyncStreamComplete @abstract Callback for the Async stream transmit complete @param refcon - callback @param status - status of the command. @param bus information. @param fwCmd - command object which generated the transaction. @result void. */ void IOFWIPBusInterface::txCompleteAsyncStream(void *refcon, IOReturn status, IOFireWireBus *bus, IOFWAsyncStreamCommand *fwCmd) { IOFWIPBusInterface *fwIPPriv = (IOFWIPBusInterface*)refcon; if(not fwIPPriv) return; IOFireWireIP *fwIPObject = OSDynamicCast(IOFireWireIP, fwIPPriv->fIPLocalNode); if(not fwIPObject) return; IOFWIPAsyncStreamTxCommand *cmd = OSDynamicCast(IOFWIPAsyncStreamTxCommand, fwCmd); if(not cmd) return; fwIPObject->closeIPGate(); if(status == kIOReturnSuccess) { fwIPObject->networkStatAdd(&(fwIPObject->getNetStats())->outputPackets); fwIPObject->fTxBcast++; } else fwIPObject->networkStatAdd(&(fwIPObject->getNetStats())->outputErrors); if(fwIPPriv->fAsyncStreamTxCmdPool != NULL) // Queue the command back into the command pool fwIPPriv->fAsyncStreamTxCmdPool->returnCommand(cmd); fwIPObject->openIPGate(); return; } /*! @function txARP @abstract Transmit ARP request or response. @param m - mbuf containing the ARP packet. @result void. */ SInt32 IOFWIPBusInterface::txARP(mbuf_t m, UInt16 nodeID, UInt32 busGeneration, IOFWSpeed speed) { IOReturn status = kIOReturnSuccess; if(fAsyncStreamTxCmdPool == NULL) status = initAsyncStreamCmdPool(); // Get an async command from the command pool IOFWIPAsyncStreamTxCommand *cmd = (IOFWIPAsyncStreamTxCommand*)fAsyncStreamTxCmdPool->getCommand(false); // Lets not block to get a command, IP may retry soon ..:) if(cmd == NULL) { // Error, so we touch the error output packets fIPLocalNode->networkStatAdd(&(fIPLocalNode->getNetStats())->outputErrors); fIPLocalNode->fNoBCastCommands++; return status; } IORecursiveLockLock(fIPLock); mbuf_t n = m; // Get the buffer pointer from the command pool UInt8 *buf = (UInt8*)cmd->getBufferFromDesc(); UInt32 dstBufLen = cmd->getMaxBufLen(); UInt32 offset = sizeof(struct firewire_header); UInt32 cmdLen = mbuf_pkthdr_len(m) - offset; // Construct the GASP_HDR and Unfragment header struct arp_packet *fwa_pkt = (struct arp_packet*)(buf); bzero((caddr_t)fwa_pkt, sizeof(*fwa_pkt)); // Fill the GASP fields fwa_pkt->gaspHdr.sourceID = htons(nodeID); memcpy(&fwa_pkt->gaspHdr.gaspID, &gaspVal, sizeof(GASP_ID)); // Set the unfragmented header information fwa_pkt->ip1394Hdr.etherType = htons(FWTYPE_ARP); // Modify the buffer pointer buf += (sizeof(GASP_HDR) + sizeof(IP1394_UNFRAG_HDR)); // Copy the arp packet into the buffer mbufTobuffer(n, &offset, buf, dstBufLen, cmdLen); // Update the length to have the GASP and IP1394 Header cmdLen += (sizeof(GASP_HDR) + sizeof(IP1394_UNFRAG_HDR)); // Initialize the command with new values of device object status = cmd->reinit( busGeneration, DEFAULT_BROADCAST_CHANNEL, cmdLen, speed, txCompleteAsyncStream, this); if(status == kIOReturnSuccess) status = cmd->submit(); else fAsyncStreamTxCmdPool->returnCommand(cmd); fIPLocalNode->freePacket((struct mbuf*)m); IORecursiveLockUnlock(fIPLock); return status; } SInt32 IOFWIPBusInterface::txBroadcastIP(const mbuf_t m, UInt16 nodeID, UInt32 busGeneration, UInt16 ownMaxPayload, UInt16 maxBroadcastPayload, IOFWSpeed speed, const UInt16 type) { UInt16 datagramSize = mbuf_pkthdr_len(m) - sizeof(struct firewire_header); fIPLocalNode->fMaxPktSize = max(datagramSize,fIPLocalNode->fMaxPktSize); UInt16 maxPayload = MIN((UInt16)1 << maxBroadcastPayload, (UInt16)1 << ownMaxPayload); SInt32 status = ENOBUFS; // Asynchronous stream datagrams are never fragmented! if (datagramSize + sizeof(IP1394_UNFRAG_HDR) > maxPayload) { fIPLocalNode->freePacket((struct mbuf*)m); return status; } // create a command pool on demand if(fAsyncStreamTxCmdPool == NULL) initAsyncStreamCmdPool(); // Get an async command from the command pool IOFWIPAsyncStreamTxCommand *asyncStreamCmd = (IOFWIPAsyncStreamTxCommand*)fAsyncStreamTxCmdPool->getCommand(false); // Lets not block to get a command, IP may retry soon ..:) if(asyncStreamCmd == NULL) { fIPLocalNode->freePacket((struct mbuf*)m); fIPLocalNode->fNoBCastCommands++; return status; } IORecursiveLockLock(fIPLock); // Get the buffer pointer from the command pool UInt8 *buf = (UInt8*)asyncStreamCmd->getBufferFromDesc(); UInt32 dstBufLen = asyncStreamCmd->getMaxBufLen(); // Get it assigned to the header GASP_HDR *gaspHdr = (GASP_HDR *)buf; gaspHdr->sourceID = htons(nodeID); memcpy(&gaspHdr->gaspID, &gaspVal, sizeof(GASP_ID)); IP1394_ENCAP_HDR *ip1394Hdr = (IP1394_ENCAP_HDR*)((UInt8*)buf + sizeof(GASP_HDR)); ip1394Hdr->singleFragment.etherType = htons(type); ip1394Hdr->singleFragment.reserved = htons(UNFRAGMENTED); UInt32 cmdLen = datagramSize; UInt32 offset = sizeof(struct firewire_header); UInt16 headerSize = sizeof(GASP_HDR) + sizeof(IP1394_UNFRAG_HDR); // Increment the buffer pointer for the unfrag or frag header buf += headerSize; mbufTobuffer(m, &offset, buf, dstBufLen, cmdLen); cmdLen += headerSize; // Initialize the command with new values of device object status = asyncStreamCmd->reinit(busGeneration, DEFAULT_BROADCAST_CHANNEL, cmdLen, speed, txCompleteAsyncStream, this); if(status == kIOReturnSuccess) status = asyncStreamCmd->submit(); else fAsyncStreamTxCmdPool->returnCommand(asyncStreamCmd); fIPLocalNode->freePacket((struct mbuf*)m); IORecursiveLockUnlock(fIPLock); return status; } SInt32 IOFWIPBusInterface::txUnicastUnFragmented(IOFireWireNub *device, const FWAddress addr, const mbuf_t m, const UInt16 pktSize, const UInt16 type) { SInt32 status = EHOSTUNREACH; bool deferNotify = true; IOFWIPAsyncWriteCommand *cmd = getAsyncCommand(m, false, &deferNotify); // Get an async command from the command pool // Lets not block to get a command, IP may retry soon ..:) if(not cmd) return status; // All done in one gulp! IP1394_ENCAP_HDR *ip1394Hdr = (IP1394_ENCAP_HDR*)cmd->getDescriptorHeader(m, kCopyBuffers, UNFRAGMENTED, sizeof(IP1394_UNFRAG_HDR), sizeof(struct firewire_header)); ip1394Hdr->fragment.datagramSize = htons(UNFRAGMENTED); ip1394Hdr->singleFragment.etherType = htons(type); ip1394Hdr->singleFragment.reserved = 0; status = cmd->transmit (device, pktSize, addr, txCompleteBlockWrite, this, true, deferNotify, kQueueCommands); return status; } SInt32 IOFWIPBusInterface::txUnicastFragmented(IOFireWireNub *device, const FWAddress addr, const mbuf_t m, const UInt16 pktSize, const UInt16 type, UInt16 maxPayload, UInt16 dgl) { UInt32 residual = pktSize; UInt32 fragmentOffset = 0; UInt32 offset = sizeof(struct firewire_header); SInt32 status = EHOSTUNREACH; bool deferNotify = true; while (residual) { deferNotify = true; status = EHOSTUNREACH; IOFWIPAsyncWriteCommand *cmd = getAsyncCommand(m, false, &deferNotify); // Get an async command from the command pool // Lets not block to get a command, IP may retry soon ..:) if(not cmd) break; // false - don't copy , if true - copy the packets fIPLocalNode->fTxFragmentPkts++; FragmentType fragmentType = FIRST_FRAGMENT; IP1394_ENCAP_HDR *ip1394Hdr = (IP1394_ENCAP_HDR*)cmd->getDescriptorHeader(m, kCopyBuffers, fragmentType, sizeof(IP1394_FRAG_HDR), offset); // Distinguish first, interior and last fragments UInt32 cmdLen = MIN(residual, maxPayload - sizeof(IP1394_FRAG_HDR)); ip1394Hdr->fragment.datagramSize = htons(pktSize - 1); if (fragmentOffset == 0) ip1394Hdr->singleFragment.etherType = htons(type); else { ip1394Hdr->fragment.fragmentOffset = htons(fragmentOffset); fragmentType = (cmdLen < residual) ? INTERIOR_FRAGMENT : LAST_FRAGMENT; } // Get your datagram labels correct ip1394Hdr->fragment.datagramSize |= htons(fragmentType << 14); ip1394Hdr->fragment.dgl = htons(dgl); ip1394Hdr->fragment.reserved = 0; status = cmd->transmit (device, cmdLen, addr, txCompleteBlockWrite, this, true, deferNotify, kQueueCommands, fragmentType); if(status != kIOReturnSuccess) break; fragmentOffset += cmdLen; // Account for the position and... offset += cmdLen; residual -= cmdLen; // ...size of the fragment just sent } return status; } SInt32 IOFWIPBusInterface::txUnicastIP(mbuf_t m, UInt16 nodeID, UInt32 busGeneration, UInt16 ownMaxPayload, IOFWSpeed speed, const UInt16 type) { struct firewire_header *fwh = (struct firewire_header *)mbuf_data(m); IORecursiveLockLock(fIPLock); ARB *arb = getArbFromFwAddr(fwh->fw_dhost); SInt32 status = EHOSTUNREACH; if(arb == NULL) { fIPLocalNode->freePacket((struct mbuf*)m); fIPLocalNode->networkStatAdd(&(fIPLocalNode->getNetStats())->outputErrors); IORecursiveLockUnlock(fIPLock); return status; } TNF_HANDLE *handle = &arb->handle; // Node had disappeared, but entry exists for specified timer value if(arb->timer > 1 or (handle->unicast.deviceID == kInvalidIPDeviceRefID)) { fIPLocalNode->freePacket((struct mbuf*)m); fIPLocalNode->networkStatAdd(&(fIPLocalNode->getNetStats())->outputErrors); IORecursiveLockUnlock(fIPLock); return status; } // Get the actual length of the packet from the mbuf UInt16 datagramSize = mbuf_pkthdr_len(m) - sizeof(struct firewire_header); UInt16 residual = datagramSize; // setup block write FWAddress addr; addr.addressHi = handle->unicast.unicastFifoHi; addr.addressLo = handle->unicast.unicastFifoLo; IOFireWireNub *device = (IOFireWireNub*)handle->unicast.deviceID; // Calculate the payload and further down will decide the fragmentation based on that UInt32 drbMaxPayload = 1 << device->maxPackLog(true, addr); UInt32 maxPayload = MIN((UInt32)1 << (handle->unicast.maxRec+1), (UInt32)1 << fLcb->ownMaxPayload); maxPayload = MIN(drbMaxPayload, maxPayload); UInt16 dgl = 0; bool unfragmented = false; // Only fragments use datagram label if (!(unfragmented = ((datagramSize + sizeof(IP1394_UNFRAG_HDR)) <= maxPayload))) dgl = fLcb->datagramLabel++; if(fAsyncCmdPool == NULL) initAsyncCmdPool(); // Create a command pool on demand if (unfragmented) status = txUnicastUnFragmented(device, addr, m, residual, type); else status = txUnicastFragmented(device, addr, m, residual, type, maxPayload, dgl); IORecursiveLockUnlock(fIPLock); return status; } /*! @function txIP @abstract Transmit IP packet. @param m - mbuf containing the IP packet. @param type - type of the packet (IPv6 or IPv4). @result void. */ SInt32 IOFWIPBusInterface::txIP(mbuf_t m, UInt16 nodeID, UInt32 busGeneration, UInt16 ownMaxPayload, UInt16 maxBroadcastPayload, IOFWSpeed speed, UInt16 type) { // If its not a packet header if(not (mbuf_flags(m) & M_PKTHDR)) { fIPLocalNode->freePacket((struct mbuf*)m); fIPLocalNode->networkStatAdd(&(fIPLocalNode->getNetStats())->outputErrors); return kIOReturnError; } struct firewire_header *fwh = (struct firewire_header *)mbuf_data(m); SInt32 status = kIOReturnSuccess; if(bcmp(fwh->fw_dhost, fwbroadcastaddr, FIREWIRE_ADDR_LEN) == 0 || bcmp(fwh->fw_dhost, ipv4multicast, FIREWIREMCAST_V4_LEN) == 0 || bcmp(fwh->fw_dhost, ipv6multicast, FIREWIREMCAST_V6_LEN) == 0) status = txBroadcastIP(m, nodeID, busGeneration, ownMaxPayload, maxBroadcastPayload, speed, type); else status = txUnicastIP(m, nodeID, busGeneration, ownMaxPayload, speed, type); return status; } /*! @function txMCAP @abstract This procedure transmits either an MCAP solicitation or advertisement on the default broadcast channel, dependent upon whether or not an MCB is supplied. Note that if more than one multicast address group is associated with a particular channel that multiple MCAP group descriptors are created. @param lcb - link control block of the local node. @param mcb - multicast control block. @param ipAddress - IP address. @result void. */ void IOFWIPBusInterface::txMCAP(LCB *lcb, MCB *mcb, UInt32 ipAddress){ ARB *arb; MCAST_DESCR *groupDescriptor; IOFWIPAsyncStreamTxCommand *asyncStreamCmd = NULL; struct mcap_packet *packet; SInt32 status; UInt32 cmdLen = 0; UInt8 *buf; if(fAsyncStreamTxCmdPool == NULL) initAsyncStreamCmdPool(); // Get an async command from the command pool asyncStreamCmd = (IOFWIPAsyncStreamTxCommand*)fAsyncStreamTxCmdPool->getCommand(false); // Lets not block to get a command, IP may retry soon ..:) if(asyncStreamCmd == NULL) return; // Get the buffer pointer from the command pool buf = (UInt8*)asyncStreamCmd->getBufferFromDesc(); // dstBufLen = asyncStreamCmd->getMaxBufLen(); packet = (struct mcap_packet*)buf; memset(packet, 0, sizeof(*packet)); packet->gaspHdr.sourceID = htons(fLcb->ownNodeID); memcpy(&packet->gaspHdr.gaspID, &gaspVal, sizeof(GASP_ID)); packet->ip1394Hdr.etherType = htons(ETHER_TYPE_MCAP); packet->mcap.length = sizeof(*packet); /* Fix endian-ness later */ groupDescriptor = packet->mcap.groupDescr; if (mcb != NULL) { packet->mcap.opcode = MCAP_ADVERTISE; OSCollectionIterator * iterator = OSCollectionIterator::withCollection( multicastArb ); while( NULL != (arb = OSDynamicCast(ARB, iterator->getNextObject())) ) { if (arb->handle.multicast.channel == mcb->channel) { memcpy(&ipAddress, &arb->ipAddress, sizeof(ipAddress)); memset(groupDescriptor, 0, sizeof(MCAST_DESCR)); groupDescriptor->length = sizeof(MCAST_DESCR); groupDescriptor->type = MCAST_TYPE; groupDescriptor->expiration = mcb->expiration; groupDescriptor->channel = mcb->channel; groupDescriptor->speed = arb->handle.multicast.spd; groupDescriptor->groupAddress = arb->ipAddress; groupDescriptor = (MCAST_DESCR*)((UInt32) groupDescriptor + sizeof(MCAST_DESCR)); packet->mcap.length += sizeof(MCAST_DESCR); } } iterator->release(); } else { packet->mcap.opcode = MCAP_SOLICIT; memset(groupDescriptor, 0, sizeof(MCAST_DESCR)); groupDescriptor->length = sizeof(MCAST_DESCR); groupDescriptor->type = MCAST_TYPE; groupDescriptor->groupAddress = ipAddress; packet->mcap.length += sizeof(MCAST_DESCR); } cmdLen = packet->mcap.length; // In CPU byte order packet->mcap.length = htons(packet->mcap.length); // Serial Bus order // Initialize the command with new values of device object status = asyncStreamCmd->reinit(fLcb->busGeneration, DEFAULT_BROADCAST_CHANNEL, cmdLen, fLcb->maxBroadcastSpeed, txCompleteAsyncStream, this); if(status == kIOReturnSuccess) status = asyncStreamCmd->submit(); else fAsyncStreamTxCmdPool->returnCommand(asyncStreamCmd); } /*! @function rxUnicastFlush @abstract Starts the batch processing of the packets, its already on its own workloop. */ void IOFWIPBusInterface::rxUnicastFlush() { UInt32 count = 0; IORecursiveLockLock(fIPLock); if(fIPLocalNode->fPacketsQueued = true) { count = fIPLocalNode->networkInterface->flushInputQueue(); if(count > fIPLocalNode->fMaxInputCount) fIPLocalNode->fMaxInputCount = count; fIPLocalNode->fPacketsQueued = false; } IORecursiveLockUnlock(fIPLock); return; } /*! @function rxUnicastComplete @abstract triggers the indication workloop to do batch processing of incoming packets. */ void IOFWIPBusInterface::rxUnicastComplete(void *refcon) { IOFWIPBusInterface *fwIPPriv = (IOFWIPBusInterface*)refcon; fwIPPriv->rxUnicastFlush(); return; } /*! @function rxUnicast @abstract block write handler. Handles both ARP and IP packet. */ UInt32 IOFWIPBusInterface::rxUnicast( void *refcon, UInt16 nodeID, IOFWSpeed &speed, FWAddress addr, UInt32 len, const void *buf, IOFWRequestRefCon requestRefcon) { IOFWIPBusInterface *fwIPPriv = (IOFWIPBusInterface*)refcon; IOFireWireIP *fwIPObject = OSDynamicCast(IOFireWireIP, fwIPPriv->fIPLocalNode); IP1394_UNFRAG_HDR *ip1394Hdr = (IP1394_UNFRAG_HDR *)buf; if(not fwIPObject->netifEnabled) return kIOReturnSuccess; // Handle the unfragmented packet if (ip1394Hdr->reserved == htons(UNFRAGMENTED)) { void *datagram = (void *) ((ULONG) ip1394Hdr + sizeof(IP1394_UNFRAG_HDR)); UInt16 datagramSize = len - sizeof(IP1394_UNFRAG_HDR); UInt16 type = ntohs(ip1394Hdr->etherType); switch (type) { case FWTYPE_IPV6: case FWTYPE_IP: if (datagramSize >= IPV4_HDR_SIZE && datagramSize <= FIREWIRE_MTU) fwIPPriv->rxIP(datagram, datagramSize, FW_M_UCAST, type); break; case FWTYPE_ARP: if (datagramSize >= sizeof(IP1394_ARP) && datagramSize <= FIREWIRE_MTU) fwIPPriv->rxARP((IP1394_ARP*)datagram, FW_M_UCAST); break; default : // Unknown packet type fwIPObject->networkStatAdd(&(fwIPObject->getNetStats())->inputErrors); break; } } else { fwIPObject->fRxFragmentPkts++; if(fwIPPriv->rxFragmentedUnicast(nodeID, (IP1394_FRAG_HDR*)ip1394Hdr, len) == kIOReturnError) fwIPObject->networkStatAdd(&(fwIPObject->getNetStats())->inputErrors); } fwIPObject->fRxUni++; return kIOReturnSuccess; } IOReturn IOFWIPBusInterface::rxFragmentedUnicast(UInt16 nodeID, IP1394_FRAG_HDR *pkt, UInt32 len) { IP1394_FRAG_HDR *fragmentHdr = (IP1394_FRAG_HDR*) pkt; // Different header layout void *fragment = (void *) ((UInt32) fragmentHdr + sizeof(IP1394_FRAG_HDR)); UInt16 fragmentSize = len - sizeof(IP1394_FRAG_HDR); UInt8 lf = htons(fragmentHdr->datagramSize) >> 14; UInt16 datagramSize = (htons(fragmentHdr->datagramSize) & 0x3FFF) + 1; if(datagramSize > FIREWIRE_MTU) return kIOReturnError; IORecursiveLockLock(fIPLock); RCB *rcb = getRcb(nodeID, htons(fragmentHdr->dgl)); if (rcb == NULL) { mbuf_t rxMBuf = (mbuf_t)fIPLocalNode->allocatePacket(datagramSize + sizeof(firewire_header)); if (rxMBuf == NULL) { IORecursiveLockUnlock(fIPLock); return kIOReturnError; } if ((rcb = new RCB) == NULL) { cleanRCBCache(); fIPLocalNode->freePacket((struct mbuf*)rxMBuf, 0); IORecursiveLockUnlock(fIPLock); return kIOReturnError; } rcb->sourceID = nodeID; rcb->dgl = htons(fragmentHdr->dgl); rcb->mBuf = rxMBuf; rcb->timer = rcbexpirationtime; // Make space for the firewire header to be helpfull in firewire_demux struct firewire_header *fwh = (struct firewire_header *)mbuf_data(rxMBuf); bzero(fwh, sizeof(struct firewire_header)); rcb->datagram = ((UInt8*)mbuf_data(rxMBuf)) + sizeof(struct firewire_header); // when indicating to the top layer fwh->fw_type = (lf == FIRST_FRAGMENT) ? htons(fragmentHdr->fragmentOffset): FWTYPE_IP; rcb->datagramSize = datagramSize; rcb->residual = rcb->datagramSize; activeRcb->setObject(rcb); } if (rcb->mBuf == NULL || rcb->datagram == NULL) { IORecursiveLockUnlock(fIPLock); return kIOReturnError; } if (lf == FIRST_FRAGMENT) { // Actually etherType rcb->etherType = htons(fragmentHdr->fragmentOffset); bufferToMbuf(rcb->mBuf, sizeof(struct firewire_header), (UInt8*)fragment, MIN(fragmentSize, rcb->datagramSize)); } else { bufferToMbuf(rcb->mBuf, sizeof(struct firewire_header)+htons(fragmentHdr->fragmentOffset), (UInt8*)fragment, MIN(fragmentSize, rcb->datagramSize - htons(fragmentHdr->fragmentOffset))); } IOReturn result = kIOReturnError; // Don't reduce below zero rcb->residual -= MIN(fragmentSize, rcb->residual); // Reassembly (probably) complete ? if (rcb->residual == 0) { // Legitimate etherType ? if (rcb->etherType == FWTYPE_IP || rcb->etherType == FWTYPE_IPV6) { fIPLocalNode->receivePackets (rcb->mBuf, mbuf_pkthdr_len(rcb->mBuf), false); result = kIOReturnSuccess; } else fIPLocalNode->freePacket((struct mbuf*)rcb->mBuf, 0); // All that reassembly work for nothing! releaseRCB(rcb, false); } IORecursiveLockUnlock(fIPLock); return result; } /*! @function rxAsyncStream @abstract callback for an Asyncstream packet, can be both IP or ARP packet. This procedure receives an indication when an asynchronous stream packet arrives on the default broadcast channel. The packet "should" be GASP, but we perform a few checks to make sure. Once we know these are OK, we check the etherType field in the unfragmented encapsulation header. This is necessary to dispatch the three types of packet that RFC 2734 permits on the default broadcast channel: an IPv4 datagram, and ARP request or response or a multi- channel allocation protocol (MCAP) message. The only remaining check, for each of these three cases, is to make sure that the packet is large enough to hold meaningful data. If so, send the packet to another procedure for further processing. @param DCLCommandStruct *callProc. @result void. */ void IOFWIPBusInterface::rxAsyncStream(DCLCommandStruct *callProc){ DCLCallProc *ptr = (DCLCallProc*)callProc; RXProcData *proc = (RXProcData*)ptr->procData; IOFWIPBusInterface *fwIPPriv = (IOFWIPBusInterface*)proc->obj; IOFireWireIP *fwIPObject = OSDynamicCast(IOFireWireIP, fwIPPriv->fIPLocalNode); IOFWAsyncStreamRxCommand *fwRxAsyncStream; UInt8 *buffer = proc->buffer; void *datagram; UInt16 datagramSize; GASP *gasp = (GASP*)buffer; LCB *lcb = fwIPPriv->fLcb; ISOC_DATA_PKT *pkt = (ISOC_DATA_PKT*)buffer; UInt16 type = 0; fwRxAsyncStream = (IOFWAsyncStreamRxCommand*)proc->thisObj; fwRxAsyncStream->modifyDCLJumps(callProc); if(not fwIPObject->netifEnabled) return; if(pkt->tag != GASP_TAG){ // Error, so we touch the error output packets fwIPObject->networkStatAdd(&(fwIPObject->getNetStats())->inputErrors); IOLog(" GASP tag error\n\r"); return; } // Minimum size requirement if (gasp->dataLength < sizeof(GASP_HDR) + sizeof(IP1394_UNFRAG_HDR)) { fwIPObject->networkStatAdd(&(fwIPObject->getNetStats())->inputErrors); IOLog(" GASP header error\n\r"); return; } // Ignore GASP if not specified by RFC 2734 if (memcmp(&gasp->gaspHdr.gaspID, &gaspVal, sizeof(GASP_ID)) != 0) { fwIPObject->networkStatAdd(&(fwIPObject->getNetStats())->inputErrors); IOLog(" Non-RFC 2734 GASP\n\r"); return; } // Also ignore GASP if not from the local bus if ((htons(gasp->gaspHdr.sourceID) >> 6) != LOCAL_BUS_ID) { fwIPObject->networkStatAdd(&(fwIPObject->getNetStats())->inputErrors); IOLog(" Remote GASP error\n\r"); return; } // Broadcast fragmentation not supported if (gasp->ip1394Hdr.reserved != htons(UNFRAGMENTED)) { fwIPObject->networkStatAdd(&(fwIPObject->getNetStats())->inputErrors); IOLog(" Encapsulation header error\n\r"); return; } datagram = (void *) ((UInt32) buffer + sizeof(GASP)); datagramSize = gasp->dataLength - (sizeof(GASP_HDR) + sizeof(IP1394_UNFRAG_HDR)); type = ntohs(gasp->ip1394Hdr.etherType); // IOLog(" Ether type 0x%04X (data length %d)\n\r",htons(gasp->ip1394Hdr.etherType), datagramSize); switch (type) { case FWTYPE_IPV6: case FWTYPE_IP: if (datagramSize >= IPV4_HDR_SIZE && datagramSize <= FIREWIRE_MTU) fwIPPriv->rxIP(datagram, datagramSize, FW_M_BCAST, type); break; case FWTYPE_ARP: if (datagramSize >= sizeof(IP1394_ARP) && datagramSize <= FIREWIRE_MTU) fwIPPriv->rxARP((IP1394_ARP*)datagram, FW_M_BCAST); break; case ETHER_TYPE_MCAP: if (datagramSize >= sizeof(IP1394_MCAP) && datagramSize <= FIREWIRE_MTU) fwIPPriv->rxMCAP(lcb, htons(gasp->gaspHdr.sourceID), (IP1394_MCAP*)datagram, datagramSize - sizeof(IP1394_MCAP)); break; } fwIPObject->fRxBcast++; return; } /*! @function rxMCAP @abstract called from rxAsyncstream for processing MCAP advertisement. When an MCAP advertisement is received, parse all of its descriptors looking for any that match group addreses in our MCAP cache. For those that match, update the channel number (it may have changed from the default broadcast channel or since the last advertisement), update the speed (the MCAP owner may have changed the speed requirements as nodes joined or left the group) and refresh the expiration timer so that the MCAP channel is valid for another number of seconds into the future. Th-th-th-that's all, folks! @param lcb - the firewire link control block for this interface. @param mcapSourceID - source nodeid which generated the multicast advertisement packet. @param mcap - mulitcast advertisment packet without the GASP header. @param dataSize - size of the packet. @result void. */ void IOFWIPBusInterface::rxMCAP(LCB *lcb, UInt16 mcapSourceID, IP1394_MCAP *mcap, UInt32 dataSize){ ARB *arb; UInt32 currentChannel; MCAST_DESCR *groupDescr = mcap->groupDescr; MCB *mcb, *priorMcb; IOFWAsyncStreamRxCommand *asyncStreamRxClient; IOReturn ioStat = kIOReturnSuccess; if ((mcap->opcode != MCAP_ADVERTISE) && (mcap->opcode != MCAP_SOLICIT)) return; // Ignore reserved MCAP opcodes dataSize = MIN(dataSize, htons(mcap->length) - sizeof(IP1394_MCAP)); while (dataSize >= sizeof(MCAST_DESCR)) { if (groupDescr->length != sizeof(MCAST_DESCR)) ; // Skip over malformed MCAP group address descriptors else if (groupDescr->type != MCAST_TYPE) ; // Skip over unrecognized descriptor types else if ((arb = getMulticastArb(groupDescr->groupAddress)) == NULL) ; // Ignore if not in our multicast cache */ else if (mcap->opcode == MCAP_SOLICIT) { mcb = 0; OSString *channel = OSString::withCString((const char*)&arb->handle.multicast.channel); mcb = OSDynamicCast(MCB, mcapState->getObject(channel)); channel->free(); if(mcb) { if (mcb->ownerNodeID == lcb->ownNodeID) // Do we own the channel? txMCAP(lcb, mcb, 0); // OK, respond to solicitation } } else if ((groupDescr->channel != DEFAULT_BROADCAST_CHANNEL) && (groupDescr->channel <= kMaxChannels)) { mcb = 0; OSString *channel = OSString::withCString((const char*)&groupDescr->channel); mcb = OSDynamicCast(MCB, mcapState->getObject(channel)); channel->free(); if(not mcb) break; if (groupDescr->expiration < 60) { if (mcb->ownerNodeID == mcapSourceID) { currentChannel = groupDescr->channel; // acquireChannel(¤tChannel, TRUE, kDoNotAllocate | kNotifyOnSuccess); mcb->ownerNodeID = lcb->ownNodeID; // Take channel ownership mcb->nextTransmit = 1; // Transmit advertisement ASAP } } else if (mcb->ownerNodeID == mcapSourceID) { mcb->expiration = groupDescr->expiration; } else if (mcb->ownerNodeID < mcapSourceID || mcb->expiration < 60) { if (mcb->ownerNodeID == lcb->ownNodeID) // Are we the owner? { // releaseChannel(groupDescr->channel, kDoNotDeallocate); // TNFReleaseChannel(lcb->unspecifiedDeviceID, groupDescr->channel, kDoNotDeallocate); } mcb->ownerNodeID = mcapSourceID; mcb->expiration = groupDescr->expiration; } currentChannel = arb->handle.multicast.channel; if (currentChannel == DEFAULT_BROADCAST_CHANNEL) { if (mcb->asyncStreamID == kInvalidAsyncStreamRefID) { if(groupDescr->channel != DEFAULT_BROADCAST_CHANNEL) { asyncStreamRxClient = new IOFWAsyncStreamRxCommand; if(asyncStreamRxClient == NULL) { ioStat = kIOReturnNoMemory; } if(asyncStreamRxClient->initAll(groupDescr->channel, rxAsyncStream, fControl, fMaxRxIsocPacketSize, this) == false) { ioStat = kIOReturnNoMemory; } if(ioStat == kIOReturnSuccess) mcb->asyncStreamID = (UInt32)asyncStreamRxClient; } else { if(fBroadcastReceiveClient != NULL) { fBroadcastReceiveClient->retain(); mcb->asyncStreamID = (UInt32)fBroadcastReceiveClient; } } } arb->handle.multicast.channel = groupDescr->channel; mcb->groupCount++; } else if (currentChannel != groupDescr->channel) { priorMcb = 0; OSString *channel = OSString::withCString((const char*)¤tChannel); priorMcb = OSDynamicCast(MCB, mcapState->getObject(channel)); channel->free(); if(not priorMcb) break; if (priorMcb->groupCount == 1) { // Are we the last user? asyncStreamRxClient = (IOFWAsyncStreamRxCommand *)mcb->asyncStreamID; if(asyncStreamRxClient != NULL) asyncStreamRxClient->release(); //TNFRemoveAsyncStreamClient(lcb->clientID, mcb->asyncStreamID); priorMcb->asyncStreamID = kInvalidAsyncStreamRefID; priorMcb->groupCount = 0; } else if (priorMcb->groupCount > 0) priorMcb->groupCount--; if (mcb->asyncStreamID == kInvalidAsyncStreamRefID) { if(groupDescr->channel != DEFAULT_BROADCAST_CHANNEL) { asyncStreamRxClient = new IOFWAsyncStreamRxCommand; if(asyncStreamRxClient == NULL) ioStat = kIOReturnNoMemory; if(asyncStreamRxClient->initAll(groupDescr->channel, rxAsyncStream, fControl, fMaxRxIsocPacketSize, this) == false) ioStat = kIOReturnNoMemory; if(ioStat == kIOReturnSuccess) mcb->asyncStreamID = (UInt32)asyncStreamRxClient; } else { if(fBroadcastReceiveClient != NULL) { fBroadcastReceiveClient->retain(); mcb->asyncStreamID = (UInt32)fBroadcastReceiveClient; } } } arb->handle.multicast.channel = groupDescr->channel; mcb->groupCount++; } if (mcb->ownerNodeID != lcb->ownNodeID) { multicastArb->removeObject(arb); arb->release(); } } dataSize -= MIN(groupDescr->length, dataSize); groupDescr = (MCAST_DESCR*)((ULONG) groupDescr + groupDescr->length); } } /*! @function rxIP @abstract Receive IP packet. @param pkt - points to the IP packet without the header. @param len - length of the packet. @params flags - indicates broadcast or unicast @params type - indicates type of the packet IPv4 or IPv6 @result IOReturn. */ IOReturn IOFWIPBusInterface::rxIP(void *pkt, UInt32 len, UInt32 flags, UInt16 type) { mbuf_t rxMBuf = NULL; struct firewire_header *fwh = NULL; bool queuePkt = false; IOReturn ret = kIOReturnSuccess; IORecursiveLockLock(fIPLock); if ((rxMBuf = (mbuf_t)fIPLocalNode->allocatePacket(len)) != NULL) { bufferToMbuf(rxMBuf, 0, (UInt8*)pkt, len); mbuf_prepend(&rxMBuf, sizeof(struct firewire_header), M_DONTWAIT); if (rxMBuf != NULL) { fwh = (struct firewire_header *)mbuf_data(rxMBuf); bzero(fwh, sizeof(struct firewire_header)); fwh->fw_type = type; queuePkt = (flags == FW_M_UCAST); if(queuePkt) bcopy(fIPLocalNode->macAddr, fwh->fw_dhost, FIREWIRE_ADDR_LEN); else bcopy(fwbroadcastaddr, fwh->fw_dhost, FIREWIRE_ADDR_LEN); if(FWTYPE_IPV6 == type) updateNDPCache(rxMBuf); } else ret = kIOReturnNoMemory; if(ret == kIOReturnSuccess) { fIPLocalNode->receivePackets(rxMBuf, mbuf_pkthdr_len(rxMBuf), queuePkt); } else { if(rxMBuf != NULL) fIPLocalNode->freePacket((struct mbuf*)rxMBuf, 0); fIPLocalNode->networkStatAdd(&(fIPLocalNode->getNetStats())->inputErrors); } } IORecursiveLockUnlock(fIPLock); return ret; } /*! @function rxARP @abstract ARP processing routine called from both Asynstream path and Async path. @param fwIPObj - IOFireWireIP object. @param arp - 1394 arp packet without the GASP or Async header. @params flags - indicates broadcast or unicast @result IOReturn. */ IOReturn IOFWIPBusInterface::rxARP(IP1394_ARP *arp, UInt32 flags){ mbuf_t rxMBuf; struct firewire_header *fwh = NULL; void *datagram = NULL; if (arp->hardwareType != htons(ARP_HDW_TYPE) || arp->protocolType != htons(FWTYPE_IP) || arp->hwAddrLen != sizeof(IP1394_HDW_ADDR) || arp->ipAddrLen != IPV4_ADDR_SIZE) { IOLog("IOFireWireIP: rxARP ERROR in packet header\n"); return kIOReturnError; } if ((rxMBuf = (mbuf_t)fIPLocalNode->allocatePacket(sizeof(*arp) + sizeof(struct firewire_header))) != NULL) { IORecursiveLockLock(fIPLock); fwh = (struct firewire_header *)mbuf_data(rxMBuf); datagram = ((UInt8*)mbuf_data(rxMBuf)) + sizeof(struct firewire_header); bzero(fwh, sizeof(struct firewire_header)); fwh->fw_type = FWTYPE_ARP; // Copy the data memcpy(datagram, arp, sizeof(*arp)); IORecursiveLockUnlock(fIPLock); fIPLocalNode->receivePackets(rxMBuf, mbuf_pkthdr_len(rxMBuf), 0); } return kIOReturnSuccess; } /*! @function watchdog @abstract cleans the Link control block's stale drb's and rcb's. The cleanCache's job is to age (and eventually discard) device objects for FireWireIP devices that have come unplugged. If they do reappear after they have been discarded from the caches, all that is required is a new ARP. The IP network stack handles that automatically @param lcb - the firewire link control block for this interface. @result void. */ void watchdog(OSObject *obj, IOTimerEventSource *src) { IOFWIPBusInterface *FWIPPriv = (IOFWIPBusInterface*)obj; FWIPPriv->processWatchDogTimeout(); } void IOFWIPBusInterface::processWatchDogTimeout() { recursiveScopeLock lock(fIPLock); cleanDRBCache(); updateMcapState(); cleanARBCache(); cleanRCBCache(); // Restart the watchdog timer timerSource->setTimeoutMS(WATCHDOG_TIMER_MS); } #pragma mark - #pragma mark еее IPv6 NDP routines еее const int ipv6fwoffset = 8; bool IOFWIPBusInterface::addNDPOptions(mbuf_t m) { if(not (mbuf_flags(m) & M_PKTHDR)) return false; vm_address_t src = (vm_offset_t)mbuf_data(m); if(src == 0) return false; UInt32 fwhdrlen = sizeof(firewire_header); mbuf_t ipv6Mbuf = m; int pkthdrlen = 0; // check whether len equals ether header if(mbuf_len(m) == sizeof(firewire_header)) { ipv6Mbuf = mbuf_next(m); if(ipv6Mbuf == NULL) return false; src = (vm_offset_t)mbuf_data(ipv6Mbuf); fwhdrlen = 0; pkthdrlen = mbuf_pkthdr_len(ipv6Mbuf); } if(mbuf_len(ipv6Mbuf) < (fwhdrlen + sizeof(struct ip6_hdr))) return false; // no space in mbuf if(mbuf_trailingspace(ipv6Mbuf) < (int)sizeof(IP1394_NDP)) return false; UInt8 *bufPtr = (UInt8*)(src + fwhdrlen); // show type of ICMPV6 packets being sent struct ip6_hdr *ip6 = (struct ip6_hdr*)bufPtr; struct icmp6_hdr *icp = (struct icmp6_hdr*)(ip6 + 1); struct nd_neighbor_advert *nd_na = (struct nd_neighbor_advert*)icp; struct nd_neighbor_solicit *nd_ns = (struct nd_neighbor_solicit*)icp; int offset = sizeof(*ip6) + fwhdrlen; bool modify = false; IP1394_NDP *fwndp = NULL; u_int16_t *icmp6_cksum = NULL; if(nd_ns->nd_ns_type == ND_NEIGHBOR_SOLICIT) { // neighbor solicitation fwndp = (IP1394_NDP*)((UInt8*)nd_ns + sizeof(struct nd_neighbor_solicit)); if(fwndp->type == 1) { modify = true; icmp6_cksum = &nd_ns->nd_ns_cksum; /* IOLog("+type = %d | +len = %d | +srclladdr = %02x:%02x:%02x:%02x:%02x:%02x:%02x::%02x\n", fwndp->type, fwndp->len, fwndp->lladdr[0], fwndp->lladdr[1], fwndp->lladdr[2], fwndp->lladdr[3], fwndp->lladdr[4], fwndp->lladdr[5], fwndp->lladdr[6], fwndp->lladdr[7]); */ } } if(nd_na->nd_na_type == ND_NEIGHBOR_ADVERT) { // neighbor advertisment fwndp = (IP1394_NDP*)((UInt8*)nd_na + sizeof(struct nd_neighbor_advert)); if(fwndp->type == 2) { modify = true; icmp6_cksum = &nd_na->nd_na_cksum; /* IOLog("+type = %d | +len = %d | +tgtlladdr = %02x:%02x:%02x:%02x:%02x:%02x:%02x::%02x\n", fwndp->type, fwndp->len, fwndp->lladdr[0], fwndp->lladdr[1], fwndp->lladdr[2], fwndp->lladdr[3], fwndp->lladdr[4], fwndp->lladdr[5], fwndp->lladdr[6], fwndp->lladdr[7]); */ } } if(modify) { fwndp->len = 3; // len in units of 8 octets bzero(fwndp->reserved, 6); // reserved by the RFC 3146 fwndp->senderMaxRec = fLcb->ownHardwareAddress.maxRec; // Maximum payload (2 ** senderMaxRec) fwndp->sspd = fLcb->ownHardwareAddress.spd; // Maximum speed fwndp->senderUnicastFifoHi = htons(fLcb->ownHardwareAddress.unicastFifoHi); // Most significant 16 bits of FIFO address fwndp->senderUnicastFifoLo = htonl(fLcb->ownHardwareAddress.unicastFifoLo); // Least significant 32 bits of FIFO address // current mbuf length IPv6+ICMP6 mbuf_setlen(ipv6Mbuf, mbuf_len(ipv6Mbuf)+ipv6fwoffset); // main mbuf header length of FW+IPv6+ICMP6 mbuf_pkthdr_setlen(m, mbuf_pkthdr_len(m)+ipv6fwoffset); // fix for : Developer: FireWire IPv6 header payload legnth value 0x28 may be incorrect // mbuf header length of IPv6+ICMP6 if(pkthdrlen != 0) mbuf_pkthdr_setlen(ipv6Mbuf, pkthdrlen+ipv6fwoffset); int icmp6len = ntohs(ip6->ip6_plen) + ipv6fwoffset; ip6->ip6_plen = htons(icmp6len); *icmp6_cksum = in6_cksum((struct mbuf*)ipv6Mbuf, IPPROTO_ICMPV6, offset, icmp6len); // IOLog("ANO: +len = %d ip6_plen %d | csum: %x ip6Size : %d\n", mbuf_len(ipv6Mbuf), ntohs(ip6->ip6_plen), *icmp6_cksum, sizeof(*ip6)); return true; } return false; } void IOFWIPBusInterface::updateNDPCache(mbuf_t m) { if(not (mbuf_flags(m) & M_PKTHDR)) return ; mbuf_t ipv6Mbuf = m; int fwhdrlen = sizeof(firewire_header); int pkthdrlen = 0; // check whether len equals ether header if(mbuf_len(m) == sizeof(firewire_header)) { ipv6Mbuf = mbuf_next(m); if(ipv6Mbuf == NULL) return; fwhdrlen = 0; pkthdrlen = mbuf_pkthdr_len(ipv6Mbuf); } vm_address_t src = (vm_offset_t)mbuf_data(ipv6Mbuf); if(src == 0) return; if(mbuf_len(ipv6Mbuf) < (sizeof(struct ip6_hdr) + fwhdrlen)) return; // no space in mbuf if(mbuf_trailingspace(ipv6Mbuf) < (int)sizeof(IP1394_NDP)) return; // show type of ICMPV6 packets being sent struct ip6_hdr *ip6 = (struct ip6_hdr*)((UInt8*)src + fwhdrlen); struct icmp6_hdr *icp = (struct icmp6_hdr*)(ip6 + 1); struct nd_neighbor_advert *nd_na = (struct nd_neighbor_advert*)icp; struct nd_neighbor_solicit *nd_ns = (struct nd_neighbor_solicit*)icp; int offset = sizeof(*ip6) + fwhdrlen; IP1394_NDP *fwndp = NULL; bool modify = false; u_int16_t *icmp6_cksum = NULL; if(nd_ns->nd_ns_type == ND_NEIGHBOR_SOLICIT) { // neighbor solicitation fwndp = (IP1394_NDP*)((UInt8*)nd_ns + sizeof(struct nd_neighbor_solicit)); if(fwndp->type == 1) { modify = true; icmp6_cksum = &nd_ns->nd_ns_cksum; /* IOLog("+type = %d | +len = %d | +srclladdr = %02x:%02x:%02x:%02x:%02x:%02x:%02x::%02x\n", fwndp->type, fwndp->len, fwndp->lladdr[0], fwndp->lladdr[1], fwndp->lladdr[2], fwndp->lladdr[3], fwndp->lladdr[4], fwndp->lladdr[5], fwndp->lladdr[6], fwndp->lladdr[7]); */ } } if(nd_na->nd_na_type == ND_NEIGHBOR_ADVERT) { // neighbor advertisment fwndp = (IP1394_NDP*)((UInt8*)nd_na + sizeof(struct nd_neighbor_advert)); if(fwndp->type == 2) { modify = true; icmp6_cksum = &nd_na->nd_na_cksum; /* IOLog("+type = %d | +len = %d | +tgtlladdr = %02x:%02x:%02x:%02x:%02x:%02x:%02x::%02x\n", fwndp->type, fwndp->len, fwndp->lladdr[0], fwndp->lladdr[1], fwndp->lladdr[2], fwndp->lladdr[3], fwndp->lladdr[4], fwndp->lladdr[5], fwndp->lladdr[6], fwndp->lladdr[7]); */ } } ARB *arb = NULL; if(modify && fwndp != NULL && fwndp->len > 2) { arb = getArbFromFwAddr(fwndp->lladdr); if(arb == NULL) { if((arb = new ARB) == NULL) return; unicastArb->setObject(arb); } if(arb != NULL) { bcopy(fwndp->lladdr, &arb->eui64, FIREWIRE_ADDR_LEN); bcopy(fwndp->lladdr, arb->fwaddr, FIREWIRE_ADDR_LEN); arb->handle.unicast.maxRec = fwndp->senderMaxRec; arb->handle.unicast.spd = fwndp->sspd; arb->handle.unicast.unicastFifoHi = htons(fwndp->senderUnicastFifoHi); arb->handle.unicast.unicastFifoLo = htonl(fwndp->senderUnicastFifoLo); arb->timer = 0; arb->handle.unicast.deviceID = getDeviceID(arb->eui64, &arb->itsMac); // Reset the packet fwndp->len = 2; // len in units of 8 octets fwndp->senderMaxRec = 0; fwndp->sspd = 0; fwndp->senderUnicastFifoHi = 0; fwndp->senderUnicastFifoLo = 0; // current mbuf length IPv6+ICMP6 mbuf_setlen(ipv6Mbuf, mbuf_len(ipv6Mbuf)-8); // main mbuf header length of FW+IPv6+ICMP6 mbuf_pkthdr_setlen(m, mbuf_pkthdr_len(m)-8); // fix for : Developer: FireWire IPv6 header payload legnth value 0x28 may be incorrect // mbuf header length of IPv6+ICMP6 if(pkthdrlen != 0) mbuf_pkthdr_setlen(ipv6Mbuf, mbuf_pkthdr_len(ipv6Mbuf)-8); int icmp6len = ntohs(ip6->ip6_plen) - 8; ip6->ip6_plen = htons(icmp6len); *icmp6_cksum = 0; *icmp6_cksum = in6_cksum((struct mbuf*)ipv6Mbuf, IPPROTO_ICMPV6, offset, icmp6len); } } } void IOFWIPBusInterface::updateNDPCache(void *buf, UInt16 *len) { struct icmp6_hdr *icp = NULL; struct ip6_hdr *ip6; struct nd_neighbor_advert *nd_na = NULL; struct nd_neighbor_solicit *nd_ns = NULL; ARB *arb = NULL; IP1394_NDP *fwndp = NULL; BOOLEAN update = false; ip6 = (struct ip6_hdr*)buf; icp = (struct icmp6_hdr*)(ip6 + 1); nd_na = (struct nd_neighbor_advert*)icp; nd_ns = (struct nd_neighbor_solicit*)icp; if(nd_ns->nd_ns_type == ND_NEIGHBOR_SOLICIT) { // neighbor solicitation fwndp = (IP1394_NDP*)((UInt8*)nd_ns + sizeof(struct nd_neighbor_solicit)); if(fwndp->type == 1) { update = true; /* IOLog("+type = %d | +len = %d | +srclladdr = %02x:%02x:%02x:%02x:%02x:%02x:%02x::%02x\n", fwndp->type, fwndp->len, fwndp->lladdr[0], fwndp->lladdr[1], fwndp->lladdr[2], fwndp->lladdr[3], fwndp->lladdr[4], fwndp->lladdr[5], fwndp->lladdr[6], fwndp->lladdr[7]); */ } } if(nd_na->nd_na_type == ND_NEIGHBOR_ADVERT) { // neighbor advertisment fwndp = (IP1394_NDP*)((UInt8*)nd_na + sizeof(struct nd_neighbor_advert)); if(fwndp->type == 2) { update = true; /* IOLog("+type = %d | +len = %d | +tgtlladdr = %02x:%02x:%02x:%02x:%02x:%02x:%02x::%02x\n", fwndp->type, fwndp->len, fwndp->lladdr[0], fwndp->lladdr[1], fwndp->lladdr[2], fwndp->lladdr[3], fwndp->lladdr[4], fwndp->lladdr[5], fwndp->lladdr[6], fwndp->lladdr[7]); */ } } if(update && fwndp != NULL && fwndp->len > 2) { arb = getArbFromFwAddr(fwndp->lladdr); if(arb == NULL) { if((arb = new ARB) == NULL) return; unicastArb->setObject(arb); } if(arb != NULL) { bcopy(fwndp->lladdr, &arb->eui64, FIREWIRE_ADDR_LEN); bcopy(fwndp->lladdr, arb->fwaddr, FIREWIRE_ADDR_LEN); arb->handle.unicast.maxRec = fwndp->senderMaxRec; arb->handle.unicast.spd = fwndp->sspd; arb->handle.unicast.unicastFifoHi = htons(fwndp->senderUnicastFifoHi); arb->handle.unicast.unicastFifoLo = htonl(fwndp->senderUnicastFifoLo); arb->timer = 0; arb->handle.unicast.deviceID = getDeviceID(arb->eui64, &arb->itsMac); // Reset the packet *len -= 8; fwndp->len = 2; // len in units of 8 octets fwndp->senderMaxRec = 0; fwndp->sspd = 0; fwndp->senderUnicastFifoHi = 0; fwndp->senderUnicastFifoLo = 0; } } return; } #pragma mark - #pragma mark еее IOFWIPBusInterface utility routines еее bool IOFWIPBusInterface::staticUpdateARPCache(void *refcon, IP1394_ARP *fwa) { return ((IOFWIPBusInterface*)refcon)->updateARPCache(fwa); } ARB *IOFWIPBusInterface::staticGetArbFromFwAddr(void *refcon, u_char *fwaddr) { return ((IOFWIPBusInterface*)refcon)->getArbFromFwAddr(fwaddr); } ARB *IOFWIPBusInterface::staticGetARBFromEui64(void *refcon, UWIDE eui64) { return ((IOFWIPBusInterface*)refcon)->getARBFromEui64(eui64); } DRB *IOFWIPBusInterface::staticInitDRBwithDevice(void *refcon, UWIDE eui64, IOFireWireNub *fDevObj, bool itsMac) { return ((IOFWIPBusInterface*)refcon)->initDRBwithDevice(eui64,fDevObj,itsMac); } UInt32 IOFWIPBusInterface::staticOutputPacket(mbuf_t pkt, void * param) { return ((IOFWIPBusInterface*)param)->outputPacket(pkt,param); } /*! @function updateARPCache @abstract updates IPv4 ARP cache from the incoming ARP packet @param fwa - firewire ARP packet. @result void. */ bool IOFWIPBusInterface::updateARPCache(IP1394_ARP *fwa) { ARB *fwarb = NULL; IORecursiveLockLock(fIPLock); // Get the arb pointer from sdl->data fwarb = getUnicastArb(fwa->senderIpAddress); if(fwarb == NULL) { // Create a new entry if it does not exist if((fwarb = new ARB) == NULL) { IOLog("arp: arb allocation failed !\n"); IORecursiveLockUnlock(fIPLock); return false; } // Fill in the details for the arb cache fwarb->ipAddress = fwa->senderIpAddress; // Initialize some parts unicastArb->setObject(fwarb); } // Update the hardware information for the existing arb entry if (ntohs(fwa->opcode) != ARPOP_REQUEST) fwarb->timer = 0; fwarb->handle.unicast.maxRec = fwa->senderMaxRec; // Volatile fields fwarb->handle.unicast.spd = fwa->sspd; fwarb->handle.unicast.unicastFifoHi = htons(fwa->senderUnicastFifoHi); fwarb->handle.unicast.unicastFifoLo = htonl(fwa->senderUnicastFifoLo); fwarb->eui64.hi = htonl(fwa->senderUniqueID.hi); fwarb->eui64.lo = htonl(fwa->senderUniqueID.lo); fwarb->handle.unicast.deviceID = getDeviceID(fwarb->eui64, &fwarb->itsMac); fIPLocalNode->getBytesFromGUID(&fwarb->eui64, fwarb->fwaddr, 0); IORecursiveLockUnlock(fIPLock); return true; } ARB *IOFWIPBusInterface::updateARBwithDevice(IOFireWireNub *device, UWIDE eui64) { // Create the arb if we recognise a IP unit. ARB *arb = getARBFromEui64(eui64); // Update the device object in the address resolution block used in the ARP resolve routine if(arb != NULL) { arb->handle.unicast.deviceID = (UInt32)device; arb->handle.unicast.maxRec = device->maxPackLog(true); arb->handle.unicast.spd = device->FWSpeed(); arb->timer = 0; arb->itsMac = false; } return arb; } /*! @function cleanARBCache @abstract cleans the Link control block's stale arb's. Invoked from the ArpTimer to clean up FW specific ARB cleanup. Unresolved ARB's are returned to the free CBLKs @param none. @result void. */ void IOFWIPBusInterface::cleanARBCache() { IORecursiveLockLock(fIPLock); ARB *arb = NULL; OSCollectionIterator * iterator = OSCollectionIterator::withCollection( unicastArb ); while( NULL != (arb = OSDynamicCast(ARB, iterator->getNextObject())) ) { if(arb->timer > 1) arb->timer--; else if (arb->timer == 1) { arb->handle.unicast.deviceID = kInvalidIPDeviceRefID; unicastArb->removeObject(arb); arb->release(); } } iterator->release(); IORecursiveLockUnlock(fIPLock); } void IOFWIPBusInterface::cleanDRBCache() { IORecursiveLockLock(fIPLock); DRB *drb = NULL; OSCollectionIterator * iterator = OSCollectionIterator::withCollection( activeDrb ); while( NULL != (drb = OSDynamicCast(DRB, iterator->getNextObject())) ) { if (drb->timer > 1) // Still has time remaining? drb->timer--; // If so, just decrement time else if (drb->timer == 1) // Expiring on this clock tick? { releaseARB(drb->deviceID); drb->deviceID = kInvalidIPDeviceRefID; // Don't notify in future activeDrb->removeObject(drb); // time to clean up drb->release(); } } iterator->release(); IORecursiveLockUnlock(fIPLock); } /*! @function cleanFWRcbCache @abstract cleans the Link control block's stale rcb's. UnAssembled RCB's are returned to the free CBLKs @param none. @result void. */ void IOFWIPBusInterface::cleanRCBCache() { IORecursiveLockLock(fIPLock); RCB *rcb = 0; OSCollectionIterator * iterator = OSCollectionIterator::withCollection( activeRcb ); while( NULL != (rcb = OSDynamicCast(RCB, iterator->getNextObject())) ) { if(rcb->timer > 1) rcb->timer--; // still reassembling packets else if (rcb->timer == 1) releaseRCB(rcb); } iterator->release(); IORecursiveLockUnlock(fIPLock); } /*! @function getDeviceID @abstract returns a fireWire device object for the GUID @param lcb - the firewire link control block for this interface. @param eui64 - global unique id of a device on the bus. @param itsMac - destination is Mac or not. @result Returns IOFireWireNub if successfull else 0. */ UInt32 IOFWIPBusInterface::getDeviceID(UWIDE eui64, BOOLEAN *itsMac) { // Returns DRB if EUI-64 matches DRB *drb = getDrbFromEui64(eui64); // Device reference ID already created if (drb != NULL) { *itsMac = drb->itsMac; // Just return it to caller return(drb->deviceID); } else { *itsMac = false; // Get an empty DRB return(kInvalidIPDeviceRefID); } } void IOFWIPBusInterface::releaseARB(ULONG deviceID) { IORecursiveLockLock(fIPLock); ARB *arb = NULL; OSCollectionIterator * iterator = OSCollectionIterator::withCollection( unicastArb ); while( NULL != (arb = OSDynamicCast(ARB, iterator->getNextObject())) ) { if (arb->handle.unicast.deviceID == deviceID) { arb->handle.unicast.deviceID = kInvalidIPDeviceRefID; unicastArb->removeObject(arb); arb->release(); break; // Only one ARB will match } } iterator->release(); IORecursiveLockUnlock(fIPLock); } void IOFWIPBusInterface::releaseRCB(RCB *rcb, bool freeMbuf) { IORecursiveLockLock(fIPLock); if(freeMbuf && rcb->mBuf != NULL) { fIPLocalNode->freePacket((struct mbuf*)rcb->mBuf, 0); rcb->mBuf = NULL; } activeRcb->removeObject(rcb); rcb->release(); IORecursiveLockUnlock(fIPLock); } void IOFWIPBusInterface::updateMcapState() { IOFWAsyncStreamRxCommand *asyncStreamRxClient; IORecursiveLockLock(fIPLock); MCB *mcb = NULL; OSCollectionIterator *iterator = OSCollectionIterator::withCollection( mcapState ); while( NULL != (mcb = OSDynamicCast(MCB, iterator->getNextObject())) ) { if (mcb->expiration > 1) // Life in this channel allocation yet? mcb->expiration--; // Yes, but the clock is ticking... else if (mcb->expiration == 1) // Dead in the water? { mcb->expiration = 0; // Yes, mark it expired asyncStreamRxClient = (IOFWAsyncStreamRxCommand *)mcb->asyncStreamID; if(asyncStreamRxClient != NULL) asyncStreamRxClient->release(); mcb->asyncStreamID = kInvalidAsyncStreamRefID; if (mcb->ownerNodeID == fLcb->ownNodeID) // We own the channel? { mcb->finalWarning = 4; // Yes, four final advertisements mcb->nextTransmit = 1; // Starting right now... } } if (mcb->ownerNodeID != fLcb->ownNodeID) continue; // Cycle to next array entry else if (mcb->nextTransmit > 1) // Time left before next transmit? mcb->nextTransmit--; // Keep on ticking... else if (mcb->nextTransmit == 1) { // Due to expire now? if (mcb->groupCount > 0) // Still in use at this machine? mcb->expiration = 60; // Renew this channel's lease txMCAP(fLcb, mcb, 0); // Broadcast the MCAP advertisement if (mcb->expiration > 0) mcb->nextTransmit = 10; // Send MCAP again in ten seconds else if (--mcb->finalWarning > 0) mcb->nextTransmit = 10; // Channel deallocation warning else { mcb->ownerNodeID = MCAP_UNOWNED; // Reliquish our ownership mcb->nextTransmit = 0; // We're really, really done! releaseMulticastARB(mcb); } } } iterator->release(); IORecursiveLockUnlock(fIPLock); } void IOFWIPBusInterface::releaseMulticastARB(MCB *mcb) { IORecursiveLockLock(fIPLock); ARB *arb = NULL; OSCollectionIterator * iterator = OSCollectionIterator::withCollection( multicastArb ); while( NULL != (arb = OSDynamicCast(ARB, iterator->getNextObject())) ) { if (arb->handle.multicast.channel == mcb->channel) { multicastArb->removeObject(arb); arb->release(); continue; } } iterator->release(); IORecursiveLockUnlock(fIPLock); } void IOFWIPBusInterface::resetRCBCache() { IORecursiveLockLock(fIPLock); RCB *rcb = NULL; OSCollectionIterator * iterator = OSCollectionIterator::withCollection( activeRcb ); while( NULL != (rcb = OSDynamicCast(RCB, iterator->getNextObject())) ) releaseRCB(rcb); iterator->release(); IORecursiveLockUnlock(fIPLock); } void IOFWIPBusInterface::resetARBCache() { IORecursiveLockLock(fIPLock); ARB *arb = NULL; OSCollectionIterator * iterator = OSCollectionIterator::withCollection( unicastArb ); while( NULL != (arb = OSDynamicCast(ARB, iterator->getNextObject())) ) arb->handle.unicast.deviceID = kInvalidIPDeviceRefID; iterator->release(); IORecursiveLockUnlock(fIPLock); } void IOFWIPBusInterface::resetMcapState() { IORecursiveLockLock(fIPLock); MCB *mcb = NULL; OSCollectionIterator * iterator = OSCollectionIterator::withCollection( mcapState ); while( NULL != (mcb = OSDynamicCast(MCB, iterator->getNextObject())) ) mcb->nextTransmit = 0; iterator->release(); IORecursiveLockUnlock(fIPLock); } /*! @function getArbFromFwAddr @abstract Locates the corresponding Unicast ARB (Address resolution block) for GUID @param lcb - the firewire link control block for this interface. @param FwAddr - global unique id of a device on the bus. @result Returns ARB if successfull else NULL. */ ARB *IOFWIPBusInterface::getArbFromFwAddr(u_char *fwaddr) { IORecursiveLockLock(fIPLock); ARB *arb = 0; OSCollectionIterator * iterator = OSCollectionIterator::withCollection( unicastArb ); while( NULL != (arb = OSDynamicCast(ARB, iterator->getNextObject())) ) if (bcmp(fwaddr, arb->fwaddr, FIREWIRE_ADDR_LEN) == 0) break; iterator->release(); IORecursiveLockUnlock(fIPLock); return(arb); } /*! @function getDrbFromEui64 @abstract Locates the corresponding DRB (device reference block) for GUID @param lcb - the firewire link control block for this interface. @param eui64 - global unique id of a device on the bus. @result Returns DRB if successfull else NULL. */ DRB *IOFWIPBusInterface::getDrbFromEui64(UWIDE eui64) { IORecursiveLockLock(fIPLock); DRB *drb = 0; OSCollectionIterator * iterator = OSCollectionIterator::withCollection( activeDrb ); while( NULL != (drb = OSDynamicCast(DRB, iterator->getNextObject())) ) if (drb->eui64.hi == eui64.hi && drb->eui64.lo == eui64.lo) break; iterator->release(); IORecursiveLockUnlock(fIPLock); return(drb); } /*! @function getDrbFromFwAddr @abstract Locates the corresponding DRB (device reference block) for GUID @param lcb - the firewire link control block for this interface. @param fwaddr - global unique id of a device on the bus. @result Returns DRB if successfull else NULL. */ DRB *IOFWIPBusInterface::getDrbFromFwAddr(u_char *fwaddr) { IORecursiveLockLock(fIPLock); DRB *drb = 0; OSCollectionIterator *iterator = OSCollectionIterator::withCollection( activeDrb ); while( NULL != (drb = OSDynamicCast(DRB, iterator->getNextObject())) ) if (bcmp(fwaddr, drb->fwaddr, FIREWIRE_ADDR_LEN) == 0) break; iterator->release(); IORecursiveLockUnlock(fIPLock); return(drb); } /*! @function getARBFromEui64 @abstract Locates the corresponding Unicast ARB (Address resolution block) for GUID @param lcb - the firewire link control block for this interface. @param eui64 - global unique id of a device on the bus. @result Returns ARB if successfull else NULL. */ ARB *IOFWIPBusInterface::getARBFromEui64(UWIDE eui64) { IORecursiveLockLock(fIPLock); ARB *arb = 0; OSCollectionIterator *iterator = OSCollectionIterator::withCollection( unicastArb ); while( NULL != (arb = OSDynamicCast(ARB, iterator->getNextObject())) ) if (arb->eui64.hi == eui64.hi && arb->eui64.lo == eui64.lo) break; iterator->release(); IORecursiveLockUnlock(fIPLock); return(arb); } /*! @function getDrbFromDeviceID @abstract Locates the corresponding DRB (Address resolution block) for IOFireWireNub @param deviceID - IOFireWireNub to look for. @result Returns DRB if successfull else NULL. */ DRB *IOFWIPBusInterface::getDrbFromDeviceID(void *deviceID) { IORecursiveLockLock(fIPLock); DRB *drb = 0; OSCollectionIterator *iterator = OSCollectionIterator::withCollection( activeDrb ); while( NULL != (drb = OSDynamicCast(DRB, iterator->getNextObject())) ) if (drb->deviceID == (UInt32)deviceID) break; iterator->release(); IORecursiveLockUnlock(fIPLock); return drb; } /*! @function getMulticastArb @abstract Locates the corresponding multicast ARB (Address resolution block) for ipaddress @param lcb - the firewire link control block for this interface. @param ipAddress - destination ipaddress to send the multicast packet. @result Returns ARB if successfull else NULL. */ ARB *IOFWIPBusInterface::getMulticastArb(UInt32 ipAddress) { IORecursiveLockLock(fIPLock); ARB *arb = 0; OSCollectionIterator *iterator = OSCollectionIterator::withCollection( multicastArb ); while( NULL != (arb = OSDynamicCast(ARB, iterator->getNextObject())) ) if (arb->ipAddress == ipAddress) break; iterator->release(); IORecursiveLockUnlock(fIPLock); return arb; } /*! @function getUnicastArb @abstract Locates the corresponding unicast ARB (Address resolution block) for ipaddress @param lcb - the firewire link control block for this interface. @param ipAddress - destination ipaddress to send the unicast packet. @result Returns ARB if successfull else NULL. */ ARB *IOFWIPBusInterface::getUnicastArb(UInt32 ipAddress) { IORecursiveLockLock(fIPLock); ARB *arb = 0; OSCollectionIterator *iterator = OSCollectionIterator::withCollection( unicastArb ); while( NULL != (arb = OSDynamicCast(ARB, iterator->getNextObject())) ) if (arb->ipAddress == ipAddress) break; iterator->release(); IORecursiveLockUnlock(fIPLock); return(arb); } /*! @function getRcb @abstract Locates a reassembly control block. @param lcb - the firewire link control block for this interface. @param sourceID - source nodeid which generated the fragmented packet. @param dgl - datagram label for the fragmented packet. @result Returns RCB if successfull else NULL. */ RCB *IOFWIPBusInterface::getRcb(UInt16 sourceID, UInt16 dgl) { IORecursiveLockLock(fIPLock); RCB *rcb = 0; OSCollectionIterator *iterator = OSCollectionIterator::withCollection( activeRcb ); while( NULL != (rcb = OSDynamicCast(RCB, iterator->getNextObject())) ) if (rcb->sourceID == sourceID && rcb->dgl == dgl) break; iterator->release(); IORecursiveLockUnlock(fIPLock); return(rcb); } #pragma mark - #pragma mark еее mbuf utility routines еее void IOFWIPBusInterface::moveMbufWithOffset(SInt32 tempOffset, mbuf_t *srcm, vm_address_t *src, SInt32 *srcLen) { mbuf_t temp = NULL; for(;;) { if(tempOffset == 0) break; if(*srcm == NULL) break; if(*srcLen < tempOffset) { tempOffset = tempOffset - *srcLen; temp = mbuf_next(*srcm); *srcm = temp; if(*srcm != NULL) *srcLen = mbuf_len(*srcm); continue; } else if (*srcLen > tempOffset) { *srcLen = mbuf_len(*srcm); *src = (vm_offset_t)mbuf_data(*srcm); *src += tempOffset; *srcLen -= tempOffset; break; } else if (*srcLen == tempOffset) { temp = mbuf_next(*srcm); *srcm = temp; if(*srcm != NULL) { *srcLen = mbuf_len(*srcm); *src = (vm_offset_t)mbuf_data(*srcm); } break; } } } /*! @function bufferToMbuf @abstract Copies buffer to Mbuf. @param m - destination mbuf. @param offset - offset into the mbuf data pointer. @param srcbuf - source buf. @param srcbufLen - source buffer length. @result bool - true if success else false. */ bool IOFWIPBusInterface::bufferToMbuf(mbuf_t m, UInt32 offset, UInt8 *srcbuf, UInt32 srcbufLen) { IORecursiveLockLock(fIPLock); // Get the source mbuf_t srcm = m; SInt32 srcLen = mbuf_len(srcm); vm_address_t src = (vm_offset_t)mbuf_data(srcm); // Mbuf manipulated to point at the correct offset SInt32 tempOffset = offset; moveMbufWithOffset(tempOffset, &srcm, &src, &srcLen); // Modify according to our fragmentation SInt32 dstLen = srcbufLen; SInt32 copylen = dstLen; vm_address_t dst = (vm_address_t)srcbuf; mbuf_t temp = NULL; for (;;) { if (srcLen < dstLen) { // Copy remainder of buffer to current mbuf upto m_len. BCOPY(dst, src, srcLen); dst += srcLen; dstLen -= srcLen; copylen -= srcLen; // set the offset if(copylen == 0){ // set the new mbuf to point to the new chain temp = mbuf_next(srcm); srcm = temp; break; } // Move on to the next source mbuf. temp = mbuf_next(srcm); assert(temp); srcm = temp; srcLen = mbuf_len(srcm); src = (vm_offset_t)mbuf_data(srcm); } else if (srcLen > dstLen) { // // Copy some of buffer to src mbuf, since mbuf // has more space. // BCOPY(dst, src, dstLen); src += dstLen; srcLen -= dstLen; copylen -= dstLen; if(copylen == 0) break; } else { /* (srcLen == dstLen) */ // copy remainder of src into remaining space of current mbuffer BCOPY(dst, src, srcLen); copylen -= srcLen; if(copylen == 0){ // set the new mbuf to point to the new chain temp = mbuf_next(srcm); srcm = temp; break; } // Free current mbuf and move the current onto the next srcm = mbuf_next(srcm); // Do we have any data left to copy? if (dstLen == 0) break; srcLen = mbuf_len(srcm); src = (vm_offset_t)mbuf_data(srcm); } } IORecursiveLockUnlock(fIPLock); return true; } /*! @function mbufTobuffer @abstract Copies mbuf data into the buffer pointed by IOMemoryDescriptor. @param src - source mbuf. @param offset - offset into the mbuf data pointer. @param dstbuf - destination buf. @param dstbufLen - destination buffer length. @param length - length to copy. @result NULL if copied else should be invoked again till the residual is copied into the buffer. */ mbuf_t IOFWIPBusInterface::mbufTobuffer(const mbuf_t m, UInt32 *offset, UInt8 *dstbuf, UInt32 dstbufLen, UInt32 length) { // Get the source mbuf_t srcm = m; SInt32 srcLen = mbuf_len(srcm); vm_address_t src = (vm_offset_t)mbuf_data(srcm); // Mbuf manipulated to point at the correct offset SInt32 tempOffset = *offset; moveMbufWithOffset(tempOffset, &srcm, &src, &srcLen); // Modify according to our fragmentation SInt32 dstLen = length; SInt32 copylen = dstLen; vm_address_t dst = (vm_address_t)dstbuf; mbuf_t temp = NULL; for (;;) { if (srcLen < dstLen) { // Copy remainder of src mbuf to current dst. BCOPY(src, dst, srcLen); dst += srcLen; dstLen -= srcLen; copylen -= srcLen; // set the offset *offset = *offset + srcLen; if(copylen == 0){ // set the new mbuf to point to the new chain temp = mbuf_next(srcm); srcm = temp; break; } // Move on to the next source mbuf. temp = mbuf_next(srcm); assert(temp); srcm = temp; srcLen = mbuf_len(srcm); src = (vm_offset_t)mbuf_data(srcm); } else if (srcLen > dstLen) { // Copy some of src mbuf to remaining space in dst mbuf. BCOPY(src, dst, dstLen); src += dstLen; srcLen -= dstLen; copylen -= dstLen; // set the offset *offset = *offset + dstLen; // Move on to the next destination mbuf. if(copylen == 0) break; } else { /* (srcLen == dstLen) */ // copy remainder of src into remaining space of current dst BCOPY(src, dst, srcLen); copylen -= srcLen; if(copylen == 0){ // set the offset *offset = 0; // set the new mbuf to point to the new chain temp = mbuf_next(srcm); srcm = temp; break; } // Free current mbuf and move the current onto the next srcm = mbuf_next(srcm); // Do we have any data left to copy? if (dstLen == 0) break; srcLen = mbuf_len(srcm); src = (vm_offset_t)mbuf_data(srcm); } } return temp; }