📄 ctpforwardingenginep.nc
字号:
/* $Id: CtpForwardingEngineP.nc,v 1.13 2008/06/04 04:30:41 regehr Exp $ *//* * Copyright (c) 2006 Stanford University. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the * distribution. * - Neither the name of the Stanford University nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL STANFORD * UNIVERSITY OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. *//** * The ForwardingEngine is responsible for queueing and scheduling outgoing * packets in a collection protocol. It maintains a pool of forwarding messages * and a packet send * queue. A ForwardingEngine with a forwarding message pool of size <i>F</i> * and <i>C</i> CollectionSenderC clients has a send queue of size * <i>F + C</i>. This implementation has a large number of configuration * constants, which can be found in <code>ForwardingEngine.h</code>. * * <p>Packets in the send queue are sent in FIFO order, with head-of-line * blocking. Because this is a tree collection protocol, all packets are going * to the same destination, and so the ForwardingEngine does not distinguish * packets from one another: packets from CollectionSenderC clients are * treated identically to forwarded packets.</p> * * <p>If ForwardingEngine is on top of a link layer that supports * synchronous acknowledgments, it enables them and retransmits packets * when they are not acked. It transmits a packet up to MAX_RETRIES times * before giving up and dropping the packet.</p> * * <p>The ForwardingEngine detects routing loops and tries to correct * them. It assumes that the collection tree is based on a gradient, * such as hop count or estimated transmissions. When the ForwardingEngine * sends a packet to the next hop, it puts the local gradient value in * the packet header. If a node receives a packet to forward whose * gradient value is less than its own, then the gradient is not monotonically * decreasing and there may be a routing loop. When the ForwardingEngine * receives such a packet, it tells the RoutingEngine to advertise its * gradient value soon, with the hope that the advertisement will update * the node who just sent a packet and break the loop. * * <p>ForwardingEngine times its packet transmissions. It differentiates * between four transmission cases: forwarding, success, ack failure, * and loop detection. In each case, the * ForwardingEngine waits a randomized period of time before sending the next * packet. This approach assumes that the network is operating at low * utilization; its goal is to prevent correlated traffic -- such as * nodes along a route forwarding packets -- from interfering with itself. * * <table> * <tr> * <td><b>Case</b></td> * <td><b>CC2420 Wait (ms)</b></td> * <td><b>Other Wait (ms)</b></td> * <td><b>Description</b></td> * </tr> * <tr> * <td>Forwarding</td> * <td>Immediate</td> * <td>Immediate</td> * <td>When the ForwardingEngine receives a packet to forward and it is not * already sending a packet (queue is empty). In this case, it immediately * forwards the packet.</td> * </tr> * <tr> * <td>Success</td> * <td>16-31</td> * <td>128-255</td> * <td>When the ForwardingEngine successfully sends a packet to the next * hop, it waits this long before sending the next packet in the queue. * </td> * </tr> * <tr> * <td>Ack Failure</td> * <td>8-15</td> * <td>128-255</td> * <td>If the link layer supports acks and the ForwardingEngine did not * receive an acknowledgment from the next hop, it waits this long before * trying a retransmission. If the packet has exceeded the retransmission * count, ForwardingEngine drops the packet and uses the Success timer instead. </td> * </tr> * <tr> * <td>Loop Detection</td> * <td>32-63</td> * <td>512-1023</td> * <td>If the ForwardingEngine is asked to forward a packet from a node that * believes it is closer to the root, the ForwardingEngine pauses its * transmissions for this interval and triggers the RoutingEngine to * send an update. The goal is to let the gradient become consistent before * sending packets, in order to prevent routing loops from consuming * bandwidth and energy.</td> * </tr> * </table> * * <p>The times above are all for CC2420-based platforms. The timings for * other platforms depend on their bit rates, as they are based on packet * transmission times.</p> * @author Philip Levis * @author Kyle Jamieson * @date $Date: 2008/06/04 04:30:41 $ */#include <CtpForwardingEngine.h>#include <CtpDebugMsg.h> generic module CtpForwardingEngineP() { provides { interface Init; interface StdControl; interface Send[uint8_t client]; interface Receive[collection_id_t id]; interface Receive as Snoop[collection_id_t id]; interface Intercept[collection_id_t id]; interface Packet; interface CollectionPacket; interface CtpPacket; interface CtpCongestion; } uses { interface AMSend as SubSend; interface Receive as SubReceive; interface Receive as SubSnoop; interface Packet as SubPacket; interface UnicastNameFreeRouting; interface SplitControl as RadioControl; interface Queue<fe_queue_entry_t*> as SendQueue; interface Pool<fe_queue_entry_t> as QEntryPool; interface Pool<message_t> as MessagePool; interface Timer<TMilli> as RetxmitTimer; interface LinkEstimator; // Counts down from the last time we heard from our parent; used // to expire local state about parent congestion. interface Timer<TMilli> as CongestionTimer; interface Cache<message_t*> as SentCache; interface CtpInfo; interface PacketAcknowledgements; interface Random; interface RootControl; interface CollectionId[uint8_t client]; interface AMPacket; interface CollectionDebug; interface Leds; }}implementation { /* Helper functions to start the given timer with a random number * masked by the given mask and added to the given offset. */ static void startRetxmitTimer(uint16_t mask, uint16_t offset); static void startCongestionTimer(uint16_t mask, uint16_t offset); /* Indicates whether our client is congested */ bool clientCongested = FALSE; /* Tracks our parent's congestion state. */ bool parentCongested = FALSE; /* Threshold for congestion */ uint8_t congestionThreshold; /* Keeps track of whether the routing layer is running; if not, * it will not send packets. */ bool running = FALSE; /* Keeps track of whether the radio is on; no sense sending packets * if the radio is off. */ bool radioOn = FALSE; /* Keeps track of whether an ack is pending on an outgoing packet, * so that the engine can work unreliably when the data-link layer * does not support acks. */ bool ackPending = FALSE; /* Keeps track of whether the packet on the head of the queue * is being used, and control access to the data-link layer.*/ bool sending = FALSE; /* Keep track of the last parent address we sent to, so that unacked packets to an old parent are not incorrectly attributed to a new parent. */ am_addr_t lastParent; /* Network-level sequence number, so that receivers * can distinguish retransmissions from different packets. */ uint8_t seqno; enum { CLIENT_COUNT = uniqueCount(UQ_CTP_CLIENT) }; /* Each sending client has its own reserved queue entry. If the client has a packet pending, its queue entry is in the queue, and its clientPtr is NULL. If the client is idle, its queue entry is pointed to by clientPtrs. */ fe_queue_entry_t clientEntries[CLIENT_COUNT]; fe_queue_entry_t* ONE_NOK clientPtrs[CLIENT_COUNT]; /* The loopback message is for when a collection roots calls Send.send. Since Send passes a pointer but Receive allows buffer swaps, the forwarder copies the sent packet into the loopbackMsgPtr and performs a buffer swap with it. See sendTask(). */ message_t loopbackMsg; message_t* ONE_NOK loopbackMsgPtr; command error_t Init.init() { int i; for (i = 0; i < CLIENT_COUNT; i++) { clientPtrs[i] = clientEntries + i; dbg("Forwarder", "clientPtrs[%hhu] = %p\n", i, clientPtrs[i]); } congestionThreshold = (call SendQueue.maxSize()) >> 1; loopbackMsgPtr = &loopbackMsg; lastParent = call AMPacket.address(); seqno = 0; return SUCCESS; } command error_t StdControl.start() { running = TRUE; return SUCCESS; } command error_t StdControl.stop() { running = FALSE; return SUCCESS; } /* sendTask is where the first phase of all send logic * exists (the second phase is in SubSend.sendDone()). */ task void sendTask(); /* ForwardingEngine keeps track of whether the underlying radio is powered on. If not, it enqueues packets; when it turns on, it then starts sending packets. */ event void RadioControl.startDone(error_t err) { if (err == SUCCESS) { radioOn = TRUE; if (!call SendQueue.empty()) { post sendTask(); } } } /* * If the ForwardingEngine has stopped sending packets because * these has been no route, then as soon as one is found, start * sending packets. */ event void UnicastNameFreeRouting.routeFound() { post sendTask(); } event void UnicastNameFreeRouting.noRoute() { // Depend on the sendTask to take care of this case; // if there is no route the component will just resume // operation on the routeFound event } event void RadioControl.stopDone(error_t err) { if (err == SUCCESS) { radioOn = FALSE; } } ctp_data_header_t* getHeader(message_t* m) { return (ctp_data_header_t*)call SubPacket.getPayload(m, sizeof(ctp_data_header_t)); } /* * The send call from a client. Return EBUSY if the client is busy * (clientPtrs is NULL), otherwise configure its queue entry * and put it in the send queue. If the ForwardingEngine is not * already sending packets (the RetxmitTimer isn't running), post * sendTask. It could be that the engine is running and sendTask * has already been posted, but the post-once semantics make this * not matter. */ command error_t Send.send[uint8_t client](message_t* msg, uint8_t len) { ctp_data_header_t* hdr; fe_queue_entry_t *qe; dbg("Forwarder", "%s: sending packet from client %hhu: %x, len %hhu\n", __FUNCTION__, client, msg, len); if (!running) {return EOFF;} if (len > call Send.maxPayloadLength[client]()) {return ESIZE;} call Packet.setPayloadLength(msg, len); hdr = getHeader(msg); hdr->origin = TOS_NODE_ID; hdr->originSeqNo = seqno++; hdr->type = call CollectionId.fetch[client](); hdr->thl = 0; if (clientPtrs[client] == NULL) { dbg("Forwarder", "%s: send failed as client is busy.\n", __FUNCTION__); return EBUSY; } qe = clientPtrs[client]; qe->msg = msg; qe->client = client; qe->retries = MAX_RETRIES; dbg("Forwarder", "%s: queue entry for %hhu is %hhu deep\n", __FUNCTION__, client, call SendQueue.size()); if (call SendQueue.enqueue(qe) == SUCCESS) { if (radioOn && !call RetxmitTimer.isRunning()) { post sendTask(); } clientPtrs[client] = NULL;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -