]> oss.titaniummirror.com Git - tinyos-2.x.git/blobdiff - tos/lib/net/tymo/dymo/DymoTableM.nc
Initial tymo commit
[tinyos-2.x.git] / tos / lib / net / tymo / dymo / DymoTableM.nc
diff --git a/tos/lib/net/tymo/dymo/DymoTableM.nc b/tos/lib/net/tymo/dymo/DymoTableM.nc
new file mode 100644 (file)
index 0000000..d0e00dd
--- /dev/null
@@ -0,0 +1,353 @@
+/*
+ * Copyright (c) 2007 Romain Thouvenin <romain.thouvenin@gmail.com>
+ * Published under the terms of the GNU General Public License (GPLv2).
+ */
+
+#include "dymo_table.h"
+
+/**
+ * DymoTableM - Implements a routing table with DYMO routing information.
+ * @param maxsize maximum number of entries in the table, cannot be higher than 51
+ *
+ * @author Romain Thouvenin
+ */
+
+generic module DymoTableM(uint8_t maxsize) {
+  provides {
+    interface StdControl;
+    interface RoutingTable;
+    interface DymoTable;
+  }
+  uses {
+    interface Timer<TMilli>[uint8_t id];
+    interface LinkMonitor;
+  }
+#ifdef DYMO_MONITORING
+  provides interface RoutingTableInfo;
+#endif
+}
+
+implementation {
+
+  rt_entry_t table[maxsize];
+  rt_info_t buf_info;
+  uint8_t size; 
+  uint8_t num_entries;
+  uint8_t replace;
+
+  /* declared at the end */
+  void replace_info(uint8_t entry_id, const rt_info_t * route_info);
+  int8_t get_route(addr_t address);
+  void delete_route(uint8_t entry_id, reason_t r);
+  bool is_superior(const rt_info_t * info1, const rt_entry_t * entry, dymo_msg_t msg_type);
+  void set_timer(uint8_t entry_id, rt_timer_t timer_id);
+  void cancel_timer(uint8_t entry_id, rt_timer_t timer);
+  void cancel_timers(uint8_t entry_id);
+
+  command error_t StdControl.start(){
+    num_entries = 0;
+    size = 0;
+    replace = 0;
+    return SUCCESS;
+  }
+
+  command error_t StdControl.stop(){
+    uint8_t i;
+    for(i=0; i<num_entries; i++){
+      if( !(table[i].flags & FLAG_DELETED) ){
+       cancel_timers(i);
+      }
+    }
+    return SUCCESS;
+  }
+
+  command error_t RoutingTable.getForwardingRoute(addr_t address, rt_info_t * info){
+    int8_t i = get_route(address);
+    dbg("dt", "DT: Someone wants a forwarding route for %u.\n", address);
+    if(i == -1){
+      dbg("dt", "DT: But I don't have it. => brokenRouteNeeded\n");
+      buf_info.address = address;
+      buf_info.seqnum = 0;
+      buf_info.has_hopcnt = 0;
+      signal DymoTable.brokenRouteNeeded(&buf_info);
+      return FAIL;
+    }
+
+    //The caller may want to know what is in the table even if it is broken
+    if(info && !(table[i].flags & FLAG_DELETED)){
+      *info = table[i].info;
+    }
+
+    if(table[i].flags & (FLAG_BROKEN | FLAG_DELETED)){
+      dbg("dt", "DT: But it is deleted. => brokenRouteNeeded\n");
+      signal DymoTable.brokenRouteNeeded(&table[i].info); //TODO not if not used recently (for other signals too)
+      return FAIL;
+    }
+
+    cancel_timer(i, ROUTE_NEW);
+    table[i].flags &= ~FLAG_NEW;
+    cancel_timer(i, ROUTE_DELETE);
+    set_timer(i, ROUTE_USED);
+    table[i].flags |= FLAG_USED;
+    dbg("dt", "DT: Here it is: %u.\n", table[i].info.nexthop);
+    return SUCCESS;
+  }
+
+  command error_t RoutingTable.getRoute(addr_t address, rt_info_t * info){
+    int i = get_route(address);
+    dbg("dt", "DT: Someone wants a sending route for %u.\n", address);
+    if(i == -1){
+      dbg("dt", "DT: But I don't have it. => routeNeeded\n");
+      signal DymoTable.routeNeeded(address);
+      return EBUSY;
+    }
+
+    //The caller may want to know what is in the table even if it is broken
+    if(info){
+      *info = table[i].info;
+    }
+
+    if(table[i].flags & (FLAG_DELETED | FLAG_BROKEN)){
+      dbg("dt", "DT: But it is deleted or broken. => routeNeeded\n");
+      signal DymoTable.routeNeeded(address);
+      return EBUSY;
+    }
+    
+    //We assume the route is going to be used
+    cancel_timer(i, ROUTE_NEW);
+    table[i].flags &= ~FLAG_NEW;
+    cancel_timer(i, ROUTE_DELETE);
+    set_timer(i, ROUTE_USED);
+    table[i].flags |= FLAG_USED;
+    dbg("dt", "DT: Here it is: %u-%u-%hhu.\n", table[i].info.nexthop, table[i].info.seqnum, table[i].info.hopcnt);
+    return SUCCESS;
+  }
+
+  command error_t DymoTable.update(const rt_info_t * route_info, dymo_msg_t msg_type){
+    int8_t i = get_route(route_info->address);
+
+    if(msg_type == DYMO_RERR){
+
+      if(i != -1){
+       if( (table[i].info.nexthop == route_info->nexthop) 
+           && ((table[i].info.seqnum == 0)
+               || (route_info->seqnum == 0)
+               || (route_info->seqnum >= table[i].info.seqnum)) ){
+         table[i].flags |= FLAG_BROKEN;
+         dbg("dt", "DT: Route for %u evicted because of a RERR.\n", route_info->address);
+         signal RoutingTable.evicted(&table[i].info, REASON_UNREACHABLE);
+         return SUCCESS;
+       } else {
+         return EINVAL;
+       }
+      } else {
+       return EINVAL;
+      }
+
+    } else {
+
+      if(i == -1){
+       
+       if(num_entries < maxsize){ //We have room to add a new route
+         
+         replace_info(num_entries, route_info);
+         num_entries++;
+         size++;
+         dbg("dt", "DT: Updated route for %u in entry %hhu.\n", route_info->address, num_entries-1); //TODO debug below too
+         return SUCCESS;
+
+       } else { //We have to find a route to replace
+         //TODO possible optimization : caching the last deleted and broken route
+         int8_t j = -1; //will be set to a non-new route if found
+         
+         //We look for a deleted route
+         for(i=0; i<num_entries; i++){
+           if(table[i].flags & FLAG_DELETED){
+             replace_info(i, route_info);
+             return SUCCESS;
+           }
+         }
+
+         //the table is full, we try to replace an existing route
+         for(i=0; i<num_entries; i++){
+           if(table[i].flags & FLAG_BROKEN){
+             replace_info(i, route_info);
+             return SUCCESS;
+           } else if( !(table[i].flags & FLAG_NEW) ){
+             j = i;
+           }
+         }
+
+         //no broken route found, we a take a non-new route
+         //TODO rather take a non-used route
+         if(j != -1){
+           delete_route(j, REASON_FULL);
+           replace_info(j, route_info);
+           return SUCCESS;
+         }
+
+         /* No room found. We delete a random route */
+         delete_route(replace, REASON_FULL);
+         replace_info(replace++, route_info);
+         if (replace == maxsize)
+           replace = 0;
+         return SUCCESS;
+
+       }
+
+      } else { //if(i == -1)
+
+       if(is_superior(route_info, table + i, msg_type)){
+         replace_info(i, route_info);
+         return SUCCESS;
+       } else {
+         return EINVAL;
+       }
+
+      }
+
+    }
+  }
+
+  command bool DymoTable.isSuperior(const rt_info_t * info, dymo_msg_t t){
+    int8_t i = get_route(info->address);
+    return ((i == -1) || is_superior(info, table + i, t));
+  }
+
+  event void Timer.fired[uint8_t timer_id](){
+    uint8_t e = timer_id / NB_ROUTE_TIMERS;
+    switch(timer_id % NB_ROUTE_TIMERS){
+    case ROUTE_AGE_MIN:
+      table[e].flags &= ~FLAG_NEW;
+      break;
+    case ROUTE_AGE_MAX:
+      dbg("dt", "DT: Route for %u is really old, I delete it.\n", table[e].info.address);
+      delete_route(e, REASON_OLD);
+      break;
+    case ROUTE_NEW:
+      table[e].flags &= ~FLAG_NEW;
+      set_timer(e, ROUTE_DELETE);
+      break;
+    case ROUTE_USED:
+      table[e].flags &= ~FLAG_USED;
+      set_timer(e, ROUTE_DELETE);
+      break;
+    case ROUTE_DELETE:
+      dbg("dt", "DT: Route for %u is unused, I delete it.\n", table[e].info.address);
+      delete_route(e, REASON_OLD);
+      break;
+    }
+  }
+
+  event void LinkMonitor.brokenLink(addr_t neighbor){
+    int8_t i = get_route(neighbor);
+    if (i != -1) {
+      table[i].flags |= FLAG_BROKEN;
+      signal RoutingTable.evicted(&table[i].info, REASON_UNREACHABLE);
+      if (table[i].flags & (FLAG_NEW | FLAG_USED)) {
+       cancel_timer(i, ROUTE_NEW);
+       cancel_timer(i, ROUTE_USED);
+       set_timer(i, ROUTE_DELETE);
+      }
+    }
+  }
+
+  void replace_info(uint8_t pos, const rt_info_t * route_info){
+    table[pos].info = *route_info;
+    table[pos].flags = FLAG_NEW;
+    cancel_timers(pos);
+    set_timer(pos, ROUTE_AGE_MIN);
+    set_timer(pos, ROUTE_AGE_MAX);
+    set_timer(pos, ROUTE_NEW);
+  }
+
+  /* Return the index of the route toward address if it exists, -1 otherwise */
+  int8_t get_route(addr_t address){
+    uint8_t i = 0;
+    for(i=0;i<num_entries;i++){
+      if(table[i].info.address == address){
+       return i;
+      }
+    }
+    return -1;
+  }
+
+  /* Remove a route from the table */
+  void delete_route(uint8_t entry_id, reason_t r){
+    table[entry_id].flags = FLAG_DELETED;
+    cancel_timers(entry_id);
+    dbg("dt", "DT: I'm deleting route number %hhu (for node %u).\n", entry_id, table[entry_id].info.address);
+    signal RoutingTable.evicted(&table[entry_id].info, r);
+  }
+
+  /* compare two pieces of routing information
+   * returns true if info1 > entry->info */
+  bool is_superior(const rt_info_t * info1, const rt_entry_t * entry, dymo_msg_t msg_type){
+    //a copy of the superior test in the specifications
+    //with nil values discarded
+    return ((info1->seqnum > entry->info.seqnum)
+           || ((info1->seqnum == entry->info.seqnum)
+               && info1->has_hopcnt
+               && entry->info.has_hopcnt
+               && ((info1->hopcnt < entry->info.has_hopcnt)
+                   || ((info1->hopcnt == entry->info.has_hopcnt)
+                       && ((msg_type == DYMO_RREP)
+                           || (entry->flags & FLAG_BROKEN))))));
+  }
+
+  /* Start a timer for a route */
+  void set_timer(uint8_t entry_id, rt_timer_t timer_id){
+    call Timer.startOneShot[entry_id * NB_ROUTE_TIMERS + timer_id](timer_values[timer_id]);
+  }
+
+  /* Cancel a timer for a route */
+  void cancel_timer(uint8_t entry_id, rt_timer_t timer_id){
+    call Timer.stop[entry_id * NB_ROUTE_TIMERS + timer_id]();
+  }
+
+  /* Cancel all the timers of an entry */
+  void cancel_timers(uint8_t entry_id){
+    uint8_t i = entry_id * NB_ROUTE_TIMERS;
+    for(i=0; i<NB_ROUTE_TIMERS; i++){
+      call Timer.stop[i]();
+    }
+  }
+
+#ifdef DYMO_MONITORING
+
+  command uint8_t RoutingTableInfo.size(){
+    return size;
+  }
+
+  command uint8_t RoutingTableInfo.maxSize(){
+    return maxsize;
+  }
+
+  command uint8_t  RoutingTableInfo.getTableContent(rt_info_t * buf){
+    uint8_t i=0, j=0;
+    for(i=0; i<num_entries; i++){
+      if( !(table[i].flags & (FLAG_DELETED | FLAG_BROKEN)) ){
+       buf[j++] = table[i].info;
+      }
+    }
+    return j;
+  }
+
+  command uint8_t RoutingTableInfo.getLinks(rt_link_t * buf){
+    uint8_t i=0, j=0;
+    for(i=0; i<num_entries; i++){
+      if( !(table[i].flags & (FLAG_DELETED | FLAG_BROKEN)) ){
+       buf[j].target = table[i].info.address;
+       buf[j].nexthop = table[i].info.nexthop;
+       j++;
+      }
+    }
+    return j;
+  }
+
+#endif
+
+ default event void RoutingTable.evicted(const rt_info_t * route_info, reason_t r){ }
+
+}
+