cxmac.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2007, Swedish Institute of Computer Science.
00003  * All rights reserved.
00004  *
00005  * Redistribution and use in source and binary forms, with or without
00006  * modification, are permitted provided that the following conditions
00007  * are met:
00008  * 1. Redistributions of source code must retain the above copyright
00009  *    notice, this list of conditions and the following disclaimer.
00010  * 2. Redistributions in binary form must reproduce the above copyright
00011  *    notice, this list of conditions and the following disclaimer in the
00012  *    documentation and/or other materials provided with the distribution.
00013  * 3. Neither the name of the Institute nor the names of its contributors
00014  *    may be used to endorse or promote products derived from this software
00015  *    without specific prior written permission.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
00018  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00019  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00020  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
00021  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
00022  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
00023  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
00024  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00025  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
00026  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00027  * SUCH DAMAGE.
00028  *
00029  * This file is part of the Contiki operating system.
00030  *
00031  * $Id: cxmac.c,v 1.15 2010/10/03 20:37:32 adamdunkels Exp $
00032  */
00033 
00034 /**
00035  * \file
00036  *         A simple power saving MAC protocol based on X-MAC [SenSys 2006]
00037  * \author
00038  *         Adam Dunkels <adam@sics.se>
00039  *         Niclas Finne <nfi@sics.se>
00040  *         Joakim Eriksson <joakime@sics.se>
00041  */
00042 
00043 #include "dev/leds.h"
00044 #include "dev/radio.h"
00045 #include "dev/watchdog.h"
00046 #include "net/netstack.h"
00047 #include "lib/random.h"
00048 #include "net/mac/cxmac.h"
00049 #include "net/rime.h"
00050 #include "net/rime/timesynch.h"
00051 #include "sys/compower.h"
00052 #include "sys/pt.h"
00053 #include "sys/rtimer.h"
00054 
00055 #include "contiki-conf.h"
00056 
00057 #ifdef EXPERIMENT_SETUP
00058 #include "experiment-setup.h"
00059 #endif
00060 
00061 #include <string.h>
00062 
00063 #ifndef WITH_ACK_OPTIMIZATION
00064 #define WITH_ACK_OPTIMIZATION        1
00065 #endif
00066 #ifndef WITH_ENCOUNTER_OPTIMIZATION
00067 #define WITH_ENCOUNTER_OPTIMIZATION  1
00068 #endif
00069 #ifndef WITH_STREAMING
00070 #define WITH_STREAMING               1
00071 #endif
00072 #ifndef WITH_STROBE_BROADCAST
00073 #define WITH_STROBE_BROADCAST        0
00074 #endif
00075 
00076 struct announcement_data {
00077   uint16_t id;
00078   uint16_t value;
00079 };
00080 
00081 /* The maximum number of announcements in a single announcement
00082    message - may need to be increased in the future. */
00083 #define ANNOUNCEMENT_MAX 10
00084 
00085 /* The structure of the announcement messages. */
00086 struct announcement_msg {
00087   uint16_t num;
00088   struct announcement_data data[ANNOUNCEMENT_MAX];
00089 };
00090 
00091 /* The length of the header of the announcement message, i.e., the
00092    "num" field in the struct. */
00093 #define ANNOUNCEMENT_MSG_HEADERLEN (sizeof (uint16_t))
00094 
00095 #define DISPATCH          0
00096 #define TYPE_STROBE       0x10
00097 /* #define TYPE_DATA         0x11 */
00098 #define TYPE_ANNOUNCEMENT 0x12
00099 #define TYPE_STROBE_ACK   0x13
00100 
00101 struct cxmac_hdr {
00102   uint8_t dispatch;
00103   uint8_t type;
00104 };
00105 
00106 #define MAX_STROBE_SIZE 50
00107 
00108 #ifdef CXMAC_CONF_ON_TIME
00109 #define DEFAULT_ON_TIME (CXMAC_CONF_ON_TIME)
00110 #else
00111 #define DEFAULT_ON_TIME (RTIMER_ARCH_SECOND / 160)
00112 #endif
00113 
00114 #ifdef CXMAC_CONF_OFF_TIME
00115 #define DEFAULT_OFF_TIME (CXMAC_CONF_OFF_TIME)
00116 #else
00117 #define DEFAULT_OFF_TIME (RTIMER_ARCH_SECOND / NETSTACK_RDC_CHANNEL_CHECK_RATE - DEFAULT_ON_TIME)
00118 #endif
00119 
00120 #define DEFAULT_PERIOD (DEFAULT_OFF_TIME + DEFAULT_ON_TIME)
00121 
00122 #define WAIT_TIME_BEFORE_STROBE_ACK RTIMER_ARCH_SECOND / 1000
00123 
00124 /* On some platforms, we may end up with a DEFAULT_PERIOD that is 0
00125    which will make compilation fail due to a modulo operation in the
00126    code. To ensure that DEFAULT_PERIOD is greater than zero, we use
00127    the construct below. */
00128 #if DEFAULT_PERIOD == 0
00129 #undef DEFAULT_PERIOD
00130 #define DEFAULT_PERIOD 1
00131 #endif
00132 
00133 /* The cycle time for announcements. */
00134 #define ANNOUNCEMENT_PERIOD 4 * CLOCK_SECOND
00135 
00136 /* The time before sending an announcement within one announcement
00137    cycle. */
00138 #define ANNOUNCEMENT_TIME (random_rand() % (ANNOUNCEMENT_PERIOD))
00139 
00140 #define DEFAULT_STROBE_WAIT_TIME (7 * DEFAULT_ON_TIME / 8)
00141 
00142 struct cxmac_config cxmac_config = {
00143   DEFAULT_ON_TIME,
00144   DEFAULT_OFF_TIME,
00145   4 * DEFAULT_ON_TIME + DEFAULT_OFF_TIME,
00146   DEFAULT_STROBE_WAIT_TIME
00147 };
00148 
00149 #include <stdio.h>
00150 
00151 static struct pt pt;
00152 
00153 static volatile uint8_t cxmac_is_on = 0;
00154 
00155 static volatile unsigned char waiting_for_packet = 0;
00156 static volatile unsigned char someone_is_sending = 0;
00157 static volatile unsigned char we_are_sending = 0;
00158 static volatile unsigned char radio_is_on = 0;
00159 
00160 #undef LEDS_ON
00161 #undef LEDS_OFF
00162 #undef LEDS_TOGGLE
00163 
00164 #define LEDS_ON(x) leds_on(x)
00165 #define LEDS_OFF(x) leds_off(x)
00166 #define LEDS_TOGGLE(x) leds_toggle(x)
00167 #define DEBUG 0
00168 #if DEBUG
00169 #include <stdio.h>
00170 #define PRINTF(...) printf(__VA_ARGS__)
00171 #define PRINTDEBUG(...) printf(__VA_ARGS__)
00172 #else
00173 #undef LEDS_ON
00174 #undef LEDS_OFF
00175 #undef LEDS_TOGGLE
00176 #define LEDS_ON(x)
00177 #define LEDS_OFF(x)
00178 #define LEDS_TOGGLE(x)
00179 #define PRINTF(...)
00180 #define PRINTDEBUG(...)
00181 #endif
00182 
00183 #if CXMAC_CONF_ANNOUNCEMENTS
00184 /* Timers for keeping track of when to send announcements. */
00185 static struct ctimer announcement_cycle_ctimer, announcement_ctimer;
00186 
00187 static int announcement_radio_txpower;
00188 #endif /* CXMAC_CONF_ANNOUNCEMENTS */
00189 
00190 /* Flag that is used to keep track of whether or not we are listening
00191    for announcements from neighbors. */
00192 static uint8_t is_listening;
00193 
00194 #if CXMAC_CONF_COMPOWER
00195 static struct compower_activity current_packet;
00196 #endif /* CXMAC_CONF_COMPOWER */
00197 
00198 #if WITH_ENCOUNTER_OPTIMIZATION
00199 
00200 #include "lib/list.h"
00201 #include "lib/memb.h"
00202 
00203 struct encounter {
00204   struct encounter *next;
00205   rimeaddr_t neighbor;
00206   rtimer_clock_t time;
00207 };
00208 
00209 #define MAX_ENCOUNTERS 4
00210 LIST(encounter_list);
00211 MEMB(encounter_memb, struct encounter, MAX_ENCOUNTERS);
00212 #endif /* WITH_ENCOUNTER_OPTIMIZATION */
00213 
00214 static uint8_t is_streaming;
00215 static rimeaddr_t is_streaming_to, is_streaming_to_too;
00216 static rtimer_clock_t stream_until;
00217 #define DEFAULT_STREAM_TIME (RTIMER_ARCH_SECOND)
00218 
00219 #ifndef MIN
00220 #define MIN(a, b) ((a) < (b)? (a) : (b))
00221 #endif /* MIN */
00222 
00223 /*---------------------------------------------------------------------------*/
00224 static void
00225 on(void)
00226 {
00227   if(cxmac_is_on && radio_is_on == 0) {
00228     radio_is_on = 1;
00229     NETSTACK_RADIO.on();
00230     LEDS_ON(LEDS_RED);
00231   }
00232 }
00233 /*---------------------------------------------------------------------------*/
00234 static void
00235 off(void)
00236 {
00237   if(cxmac_is_on && radio_is_on != 0 && is_listening == 0 &&
00238      is_streaming == 0) {
00239     radio_is_on = 0;
00240     NETSTACK_RADIO.off();
00241     LEDS_OFF(LEDS_RED);
00242   }
00243 }
00244 /*---------------------------------------------------------------------------*/
00245 static void
00246 powercycle_turn_radio_off(void)
00247 {
00248   if(we_are_sending == 0 &&
00249      waiting_for_packet == 0) {
00250     off();
00251   }
00252 #if CXMAC_CONF_COMPOWER
00253   compower_accumulate(&compower_idle_activity);
00254 #endif /* CXMAC_CONF_COMPOWER */
00255 }
00256 static void
00257 powercycle_turn_radio_on(void)
00258 {
00259   if(we_are_sending == 0 &&
00260      waiting_for_packet == 0) {
00261     on();
00262   }
00263 }
00264 /*---------------------------------------------------------------------------*/
00265 static struct ctimer cpowercycle_ctimer;
00266 #define CSCHEDULE_POWERCYCLE(rtime) cschedule_powercycle((1ul * CLOCK_SECOND * (rtime)) / RTIMER_ARCH_SECOND)
00267 static char cpowercycle(void *ptr);
00268 static void
00269 cschedule_powercycle(clock_time_t time)
00270 {
00271 
00272   if(cxmac_is_on) {
00273     if(time == 0) {
00274       time = 1;
00275     }
00276     ctimer_set(&cpowercycle_ctimer, time,
00277                (void (*)(void *))cpowercycle, NULL);
00278   }
00279 }
00280 /*---------------------------------------------------------------------------*/
00281 static char
00282 cpowercycle(void *ptr)
00283 {
00284   if(is_streaming) {
00285     if(!RTIMER_CLOCK_LT(RTIMER_NOW(), stream_until)) {
00286       is_streaming = 0;
00287       rimeaddr_copy(&is_streaming_to, &rimeaddr_null);
00288       rimeaddr_copy(&is_streaming_to_too, &rimeaddr_null);
00289     }
00290   }
00291 
00292   PT_BEGIN(&pt);
00293 
00294   while(1) {
00295     /* Only wait for some cycles to pass for someone to start sending */
00296     if(someone_is_sending > 0) {
00297       someone_is_sending--;
00298     }
00299 
00300     /* If there were a strobe in the air, turn radio on */
00301     powercycle_turn_radio_on();
00302     CSCHEDULE_POWERCYCLE(DEFAULT_ON_TIME);
00303     PT_YIELD(&pt);
00304 
00305     if(cxmac_config.off_time > 0) {
00306       powercycle_turn_radio_off();
00307       if(waiting_for_packet != 0) {
00308         waiting_for_packet++;
00309         if(waiting_for_packet > 2) {
00310           /* We should not be awake for more than two consecutive
00311              power cycles without having heard a packet, so we turn off
00312              the radio. */
00313           waiting_for_packet = 0;
00314           powercycle_turn_radio_off();
00315         }
00316       }
00317       CSCHEDULE_POWERCYCLE(DEFAULT_OFF_TIME);
00318       PT_YIELD(&pt);
00319     }
00320   }
00321 
00322   PT_END(&pt);
00323 }
00324 /*---------------------------------------------------------------------------*/
00325 #if CXMAC_CONF_ANNOUNCEMENTS
00326 static int
00327 parse_announcements(const rimeaddr_t *from)
00328 {
00329   /* Parse incoming announcements */
00330   struct announcement_msg adata;
00331   int i;
00332 
00333   memcpy(&adata, packetbuf_dataptr(), MIN(packetbuf_datalen(), sizeof(adata)));
00334 
00335   /*  printf("%d.%d: probe from %d.%d with %d announcements\n",
00336          rimeaddr_node_addr.u8[0], rimeaddr_node_addr.u8[1],
00337          from->u8[0], from->u8[1], adata->num);*/
00338   /*  for(i = 0; i < packetbuf_datalen(); ++i) {
00339     printf("%02x ", ((uint8_t *)packetbuf_dataptr())[i]);
00340   }
00341   printf("\n");*/
00342 
00343   for(i = 0; i < adata.num; ++i) {
00344     /*   printf("%d.%d: announcement %d: %d\n",
00345           rimeaddr_node_addr.u8[0], rimeaddr_node_addr.u8[1],
00346           adata->data[i].id,
00347           adata->data[i].value);*/
00348 
00349     announcement_heard(from,
00350                        adata.data[i].id,
00351                        adata.data[i].value);
00352   }
00353   return i;
00354 }
00355 /*---------------------------------------------------------------------------*/
00356 static int
00357 format_announcement(char *hdr)
00358 {
00359   struct announcement_msg adata;
00360   struct announcement *a;
00361 
00362   /* Construct the announcements */
00363   /*  adata = (struct announcement_msg *)hdr;*/
00364 
00365   adata.num = 0;
00366   for(a = announcement_list();
00367       a != NULL && adata.num < ANNOUNCEMENT_MAX;
00368       a = list_item_next(a)) {
00369     adata.data[adata.num].id = a->id;
00370     adata.data[adata.num].value = a->value;
00371     adata.num++;
00372   }
00373 
00374   memcpy(hdr, &adata, sizeof(struct announcement_msg));
00375 
00376   if(adata.num > 0) {
00377     return ANNOUNCEMENT_MSG_HEADERLEN +
00378       sizeof(struct announcement_data) * adata.num;
00379   } else {
00380     return 0;
00381   }
00382 }
00383 #endif /* CXMAC_CONF_ANNOUNCEMENTS */
00384 /*---------------------------------------------------------------------------*/
00385 #if WITH_ENCOUNTER_OPTIMIZATION
00386 static void
00387 register_encounter(const rimeaddr_t *neighbor, rtimer_clock_t time)
00388 {
00389   struct encounter *e;
00390 
00391   /* If we have an entry for this neighbor already, we renew it. */
00392   for(e = list_head(encounter_list); e != NULL; e = list_item_next(e)) {
00393     if(rimeaddr_cmp(neighbor, &e->neighbor)) {
00394       e->time = time;
00395       break;
00396     }
00397   }
00398   /* No matching encounter was found, so we allocate a new one. */
00399   if(e == NULL) {
00400     e = memb_alloc(&encounter_memb);
00401     if(e == NULL) {
00402       /* We could not allocate memory for this encounter, so we just drop it. */
00403       return;
00404     }
00405     rimeaddr_copy(&e->neighbor, neighbor);
00406     e->time = time;
00407     list_add(encounter_list, e);
00408   }
00409 }
00410 #endif /* WITH_ENCOUNTER_OPTIMIZATION */
00411 /*---------------------------------------------------------------------------*/
00412 static int
00413 send_packet(void)
00414 {
00415   rtimer_clock_t t0;
00416   rtimer_clock_t t;
00417   rtimer_clock_t encounter_time = 0;
00418   int strobes;
00419   struct cxmac_hdr *hdr;
00420   int got_strobe_ack = 0;
00421   uint8_t strobe[MAX_STROBE_SIZE];
00422   int strobe_len, len;
00423   int is_broadcast = 0;
00424   int is_reliable;
00425   struct encounter *e;
00426   struct queuebuf *packet;
00427   int is_already_streaming = 0;
00428   uint8_t collisions;
00429 
00430 
00431   /* Create the X-MAC header for the data packet. */
00432   packetbuf_set_addr(PACKETBUF_ADDR_SENDER, &rimeaddr_node_addr);
00433   if(rimeaddr_cmp(packetbuf_addr(PACKETBUF_ADDR_RECEIVER), &rimeaddr_null)) {
00434     is_broadcast = 1;
00435     PRINTDEBUG("cxmac: send broadcast\n");
00436   } else {
00437 #if UIP_CONF_IPV6
00438     PRINTDEBUG("cxmac: send unicast to %02x%02x:%02x%02x:%02x%02x:%02x%02x\n",
00439            packetbuf_addr(PACKETBUF_ADDR_RECEIVER)->u8[0],
00440            packetbuf_addr(PACKETBUF_ADDR_RECEIVER)->u8[1],
00441            packetbuf_addr(PACKETBUF_ADDR_RECEIVER)->u8[2],
00442            packetbuf_addr(PACKETBUF_ADDR_RECEIVER)->u8[3],
00443            packetbuf_addr(PACKETBUF_ADDR_RECEIVER)->u8[4],
00444            packetbuf_addr(PACKETBUF_ADDR_RECEIVER)->u8[5],
00445            packetbuf_addr(PACKETBUF_ADDR_RECEIVER)->u8[6],
00446            packetbuf_addr(PACKETBUF_ADDR_RECEIVER)->u8[7]);
00447 #else
00448     PRINTDEBUG("cxmac: send unicast to %u.%u\n",
00449            packetbuf_addr(PACKETBUF_ADDR_RECEIVER)->u8[0],
00450            packetbuf_addr(PACKETBUF_ADDR_RECEIVER)->u8[1]);
00451 #endif /* UIP_CONF_IPV6 */
00452   }
00453   is_reliable = packetbuf_attr(PACKETBUF_ATTR_RELIABLE) ||
00454     packetbuf_attr(PACKETBUF_ATTR_ERELIABLE);
00455   len = NETSTACK_FRAMER.create();
00456   strobe_len = len + sizeof(struct cxmac_hdr);
00457   if(len == 0 || strobe_len > (int)sizeof(strobe)) {
00458     /* Failed to send */
00459    PRINTF("cxmac: send failed, too large header\n");
00460     return MAC_TX_ERR_FATAL;
00461   }
00462   memcpy(strobe, packetbuf_hdrptr(), len);
00463   strobe[len] = DISPATCH; /* dispatch */
00464   strobe[len + 1] = TYPE_STROBE; /* type */
00465 
00466   packetbuf_compact();
00467   packet = queuebuf_new_from_packetbuf();
00468   if(packet == NULL) {
00469     /* No buffer available */
00470     PRINTF("cxmac: send failed, no queue buffer available (of %u)\n",
00471            QUEUEBUF_CONF_NUM);
00472     return MAC_TX_ERR;
00473   }
00474 
00475 #if WITH_STREAMING
00476   if(is_streaming == 1 &&
00477      (rimeaddr_cmp(packetbuf_addr(PACKETBUF_ADDR_RECEIVER),
00478                    &is_streaming_to) ||
00479       rimeaddr_cmp(packetbuf_addr(PACKETBUF_ADDR_RECEIVER),
00480                    &is_streaming_to_too))) {
00481     is_already_streaming = 1;
00482   }
00483   if(packetbuf_attr(PACKETBUF_ATTR_PACKET_TYPE) ==
00484      PACKETBUF_ATTR_PACKET_TYPE_STREAM) {
00485     is_streaming = 1;
00486     if(rimeaddr_cmp(&is_streaming_to, &rimeaddr_null)) {
00487       rimeaddr_copy(&is_streaming_to, packetbuf_addr(PACKETBUF_ADDR_RECEIVER));
00488     } else if(!rimeaddr_cmp(&is_streaming_to, packetbuf_addr(PACKETBUF_ADDR_RECEIVER))) {
00489       rimeaddr_copy(&is_streaming_to_too, packetbuf_addr(PACKETBUF_ADDR_RECEIVER));
00490     }
00491     stream_until = RTIMER_NOW() + DEFAULT_STREAM_TIME;
00492   }
00493 #endif /* WITH_STREAMING */
00494 
00495   off();
00496 
00497 #if WITH_ENCOUNTER_OPTIMIZATION
00498   /* We go through the list of encounters to find if we have recorded
00499      an encounter with this particular neighbor. If so, we can compute
00500      the time for the next expected encounter and setup a ctimer to
00501      switch on the radio just before the encounter. */
00502   for(e = list_head(encounter_list); e != NULL; e = list_item_next(e)) {
00503     const rimeaddr_t *neighbor = packetbuf_addr(PACKETBUF_ADDR_RECEIVER);
00504 
00505     if(rimeaddr_cmp(neighbor, &e->neighbor)) {
00506       rtimer_clock_t wait, now, expected;
00507 
00508       /* We expect encounters to happen every DEFAULT_PERIOD time
00509          units. The next expected encounter is at time e->time +
00510          DEFAULT_PERIOD. To compute a relative offset, we subtract
00511          with clock_time(). Because we are only interested in turning
00512          on the radio within the DEFAULT_PERIOD period, we compute the
00513          waiting time with modulo DEFAULT_PERIOD. */
00514 
00515       now = RTIMER_NOW();
00516       wait = ((rtimer_clock_t)(e->time - now)) % (DEFAULT_PERIOD);
00517       expected = now + wait - 2 * DEFAULT_ON_TIME;
00518 
00519 #if WITH_ACK_OPTIMIZATION
00520       /* Wait until the receiver is expected to be awake */
00521       if(packetbuf_attr(PACKETBUF_ATTR_PACKET_TYPE) !=
00522          PACKETBUF_ATTR_PACKET_TYPE_ACK &&
00523          is_streaming == 0) {
00524         /* Do not wait if we are sending an ACK, because then the
00525            receiver will already be awake. */
00526         while(RTIMER_CLOCK_LT(RTIMER_NOW(), expected));
00527       }
00528 #else /* WITH_ACK_OPTIMIZATION */
00529       /* Wait until the receiver is expected to be awake */
00530       while(RTIMER_CLOCK_LT(RTIMER_NOW(), expected));
00531 #endif /* WITH_ACK_OPTIMIZATION */
00532     }
00533   }
00534 #endif /* WITH_ENCOUNTER_OPTIMIZATION */
00535 
00536   /* By setting we_are_sending to one, we ensure that the rtimer
00537      powercycle interrupt do not interfere with us sending the packet. */
00538   we_are_sending = 1;
00539   
00540   t0 = RTIMER_NOW();
00541   strobes = 0;
00542 
00543   LEDS_ON(LEDS_BLUE);
00544 
00545   /* Send a train of strobes until the receiver answers with an ACK. */
00546 
00547   /* Turn on the radio to listen for the strobe ACK. */
00548   on();
00549   collisions = 0;
00550   if(!is_already_streaming) {
00551     watchdog_stop();
00552     got_strobe_ack = 0;
00553     t = RTIMER_NOW();
00554     for(strobes = 0, collisions = 0;
00555         got_strobe_ack == 0 && collisions == 0 &&
00556           RTIMER_CLOCK_LT(RTIMER_NOW(), t0 + cxmac_config.strobe_time);
00557         strobes++) {
00558 
00559       while(got_strobe_ack == 0 &&
00560             RTIMER_CLOCK_LT(RTIMER_NOW(), t + cxmac_config.strobe_wait_time)) {
00561         rtimer_clock_t now = RTIMER_NOW();
00562 
00563         /* See if we got an ACK */
00564         packetbuf_clear();
00565         len = NETSTACK_RADIO.read(packetbuf_dataptr(), PACKETBUF_SIZE);
00566         if(len > 0) {
00567           packetbuf_set_datalen(len);
00568           if(NETSTACK_FRAMER.parse()) {
00569             hdr = packetbuf_dataptr();
00570             if(hdr->dispatch == DISPATCH && hdr->type == TYPE_STROBE_ACK) {
00571               if(rimeaddr_cmp(packetbuf_addr(PACKETBUF_ADDR_RECEIVER),
00572                               &rimeaddr_node_addr)) {
00573                 /* We got an ACK from the receiver, so we can immediately send
00574                    the packet. */
00575                 got_strobe_ack = 1;
00576                 encounter_time = now;
00577               } else {
00578                 PRINTDEBUG("cxmac: strobe ack for someone else\n");
00579               }
00580             } else /*if(hdr->dispatch == DISPATCH && hdr->type == TYPE_STROBE)*/ {
00581               PRINTDEBUG("cxmac: strobe from someone else\n");
00582               collisions++;
00583             }
00584           } else {
00585             PRINTF("cxmac: send failed to parse %u\n", len);
00586           }
00587         }
00588       }
00589 
00590       t = RTIMER_NOW();
00591       /* Send the strobe packet. */
00592       if(got_strobe_ack == 0 && collisions == 0) {
00593         if(is_broadcast) {
00594 #if WITH_STROBE_BROADCAST
00595           NETSTACK_RADIO.send(strobe, strobe_len);
00596 #else
00597           /* restore the packet to send */
00598           queuebuf_to_packetbuf(packet);
00599           NETSTACK_RADIO.send(packetbuf_hdrptr(), packetbuf_totlen());
00600 #endif
00601           off();
00602         } else {
00603           NETSTACK_RADIO.send(strobe, strobe_len);
00604 #if 0
00605           /* Turn off the radio for a while to let the other side
00606              respond. We don't need to keep our radio on when we know
00607              that the other side needs some time to produce a reply. */
00608           off();
00609           rtimer_clock_t wt = RTIMER_NOW();
00610           while(RTIMER_CLOCK_LT(RTIMER_NOW(), wt + WAIT_TIME_BEFORE_STROBE_ACK));
00611 #endif /* 0 */
00612           on();
00613         }
00614       }
00615     }
00616   }
00617 
00618 #if WITH_ACK_OPTIMIZATION
00619   /* If we have received the strobe ACK, and we are sending a packet
00620      that will need an upper layer ACK (as signified by the
00621      PACKETBUF_ATTR_RELIABLE packet attribute), we keep the radio on. */
00622   if(got_strobe_ack && (packetbuf_attr(PACKETBUF_ATTR_RELIABLE) ||
00623                         packetbuf_attr(PACKETBUF_ATTR_ERELIABLE) ||
00624                         packetbuf_attr(PACKETBUF_ATTR_PACKET_TYPE) ==
00625                         PACKETBUF_ATTR_PACKET_TYPE_STREAM)) {
00626     on(); /* Wait for ACK packet */
00627     waiting_for_packet = 1;
00628   } else {
00629     off();
00630   }
00631 #else /* WITH_ACK_OPTIMIZATION */
00632   off();
00633 #endif /* WITH_ACK_OPTIMIZATION */
00634 
00635   /* restore the packet to send */
00636   queuebuf_to_packetbuf(packet);
00637   queuebuf_free(packet);
00638 
00639   /* Send the data packet. */
00640   if((is_broadcast || got_strobe_ack || is_streaming) && collisions == 0) {
00641     NETSTACK_RADIO.send(packetbuf_hdrptr(), packetbuf_totlen());
00642   }
00643 
00644 #if WITH_ENCOUNTER_OPTIMIZATION
00645   if(got_strobe_ack && !is_streaming) {
00646     register_encounter(packetbuf_addr(PACKETBUF_ADDR_RECEIVER), encounter_time);
00647   }
00648 #endif /* WITH_ENCOUNTER_OPTIMIZATION */
00649   watchdog_start();
00650 
00651   PRINTF("cxmac: send (strobes=%u,len=%u,%s), done\n", strobes,
00652          packetbuf_totlen(), got_strobe_ack ? "ack" : "no ack");
00653 
00654 #if CXMAC_CONF_COMPOWER
00655   /* Accumulate the power consumption for the packet transmission. */
00656   compower_accumulate(&current_packet);
00657 
00658   /* Convert the accumulated power consumption for the transmitted
00659      packet to packet attributes so that the higher levels can keep
00660      track of the amount of energy spent on transmitting the
00661      packet. */
00662   compower_attrconv(&current_packet);
00663 
00664   /* Clear the accumulated power consumption so that it is ready for
00665      the next packet. */
00666   compower_clear(&current_packet);
00667 #endif /* CXMAC_CONF_COMPOWER */
00668 
00669   we_are_sending = 0;
00670 
00671   LEDS_OFF(LEDS_BLUE);
00672   if(collisions == 0) {
00673     if(!is_broadcast && !got_strobe_ack) {
00674       return MAC_TX_NOACK;
00675     } else {
00676       return MAC_TX_OK;
00677     }
00678   } else {
00679     someone_is_sending++;
00680     return MAC_TX_COLLISION;
00681   }
00682 
00683 }
00684 /*---------------------------------------------------------------------------*/
00685 static void
00686 qsend_packet(mac_callback_t sent, void *ptr)
00687 {
00688   int ret;
00689   if(someone_is_sending) {
00690     PRINTF("cxmac: should queue packet, now just dropping %d %d %d %d.\n",
00691            waiting_for_packet, someone_is_sending, we_are_sending, radio_is_on);
00692     RIMESTATS_ADD(sendingdrop);
00693     ret = MAC_TX_COLLISION;
00694   } else {
00695     PRINTF("cxmac: send immediately.\n");
00696     ret = send_packet();
00697   }
00698 
00699   mac_call_sent_callback(sent, ptr, ret, 1);
00700 }
00701 /*---------------------------------------------------------------------------*/
00702 static void
00703 input_packet(void)
00704 {
00705   struct cxmac_hdr *hdr;
00706 
00707   if(NETSTACK_FRAMER.parse()) {
00708     hdr = packetbuf_dataptr();
00709 
00710     if(hdr->dispatch != DISPATCH) {
00711       someone_is_sending = 0;
00712       if(rimeaddr_cmp(packetbuf_addr(PACKETBUF_ADDR_RECEIVER),
00713                                      &rimeaddr_node_addr) ||
00714          rimeaddr_cmp(packetbuf_addr(PACKETBUF_ADDR_RECEIVER),
00715                       &rimeaddr_null)) {
00716         /* This is a regular packet that is destined to us or to the
00717            broadcast address. */
00718 
00719         /* We have received the final packet, so we can go back to being
00720            asleep. */
00721         off();
00722 
00723 #if CXMAC_CONF_COMPOWER
00724         /* Accumulate the power consumption for the packet reception. */
00725         compower_accumulate(&current_packet);
00726         /* Convert the accumulated power consumption for the received
00727            packet to packet attributes so that the higher levels can
00728            keep track of the amount of energy spent on receiving the
00729            packet. */
00730         compower_attrconv(&current_packet);
00731 
00732         /* Clear the accumulated power consumption so that it is ready
00733            for the next packet. */
00734         compower_clear(&current_packet);
00735 #endif /* CXMAC_CONF_COMPOWER */
00736 
00737         waiting_for_packet = 0;
00738 
00739         PRINTDEBUG("cxmac: data(%u)\n", packetbuf_datalen());
00740         NETSTACK_MAC.input();
00741         return;
00742       } else {
00743         PRINTDEBUG("cxmac: data not for us\n");
00744       }
00745 
00746     } else if(hdr->type == TYPE_STROBE) {
00747       someone_is_sending = 2;
00748 
00749       if(rimeaddr_cmp(packetbuf_addr(PACKETBUF_ADDR_RECEIVER),
00750                       &rimeaddr_node_addr)) {
00751         /* This is a strobe packet for us. */
00752 
00753         /* If the sender address is someone else, we should
00754            acknowledge the strobe and wait for the packet. By using
00755            the same address as both sender and receiver, we flag the
00756            message is a strobe ack. */
00757         hdr->type = TYPE_STROBE_ACK;
00758         packetbuf_set_addr(PACKETBUF_ADDR_RECEIVER,
00759                            packetbuf_addr(PACKETBUF_ADDR_SENDER));
00760         packetbuf_set_addr(PACKETBUF_ADDR_SENDER, &rimeaddr_node_addr);
00761         packetbuf_compact();
00762         if(NETSTACK_FRAMER.create()) {
00763           /* We turn on the radio in anticipation of the incoming
00764              packet. */
00765           someone_is_sending = 1;
00766           waiting_for_packet = 1;
00767           on();
00768           NETSTACK_RADIO.send(packetbuf_hdrptr(), packetbuf_totlen());
00769           PRINTDEBUG("cxmac: send strobe ack %u\n", packetbuf_totlen());
00770         } else {
00771           PRINTF("cxmac: failed to send strobe ack\n");
00772         }
00773       } else if(rimeaddr_cmp(packetbuf_addr(PACKETBUF_ADDR_RECEIVER),
00774                              &rimeaddr_null)) {
00775         /* If the receiver address is null, the strobe is sent to
00776            prepare for an incoming broadcast packet. If this is the
00777            case, we turn on the radio and wait for the incoming
00778            broadcast packet. */
00779         waiting_for_packet = 1;
00780         on();
00781       } else {
00782         PRINTDEBUG("cxmac: strobe not for us\n");
00783       }
00784 
00785       /* We are done processing the strobe and we therefore return
00786          to the caller. */
00787       return;
00788 #if CXMAC_CONF_ANNOUNCEMENTS
00789     } else if(hdr->type == TYPE_ANNOUNCEMENT) {
00790       packetbuf_hdrreduce(sizeof(struct cxmac_hdr));
00791       parse_announcements(packetbuf_addr(PACKETBUF_ADDR_SENDER));
00792 #endif /* CXMAC_CONF_ANNOUNCEMENTS */
00793     } else if(hdr->type == TYPE_STROBE_ACK) {
00794       PRINTDEBUG("cxmac: stray strobe ack\n");
00795     } else {
00796       PRINTF("cxmac: unknown type %u (%u)\n", hdr->type,
00797              packetbuf_datalen());
00798     }
00799   } else {
00800     PRINTF("cxmac: failed to parse (%u)\n", packetbuf_totlen());
00801   }
00802 }
00803 /*---------------------------------------------------------------------------*/
00804 #if CXMAC_CONF_ANNOUNCEMENTS
00805 static void
00806 send_announcement(void *ptr)
00807 {
00808   struct cxmac_hdr *hdr;
00809   int announcement_len;
00810 
00811   /* Set up the probe header. */
00812   packetbuf_clear();
00813   hdr = packetbuf_dataptr();
00814 
00815   announcement_len = format_announcement((char *)hdr +
00816                                          sizeof(struct cxmac_hdr));
00817 
00818   if(announcement_len > 0) {
00819     packetbuf_set_datalen(sizeof(struct cxmac_hdr) + announcement_len);
00820     hdr->dispatch = DISPATCH;
00821     hdr->type = TYPE_ANNOUNCEMENT;
00822 
00823     packetbuf_set_addr(PACKETBUF_ADDR_SENDER, &rimeaddr_node_addr);
00824     packetbuf_set_addr(PACKETBUF_ADDR_RECEIVER, &rimeaddr_null);
00825     packetbuf_set_attr(PACKETBUF_ATTR_RADIO_TXPOWER, announcement_radio_txpower);
00826     if(NETSTACK_FRAMER.create()) {
00827       NETSTACK_RADIO.send(packetbuf_hdrptr(), packetbuf_totlen());
00828     }
00829   }
00830 }
00831 /*---------------------------------------------------------------------------*/
00832 static void
00833 cycle_announcement(void *ptr)
00834 {
00835   ctimer_set(&announcement_ctimer, ANNOUNCEMENT_TIME,
00836              send_announcement, NULL);
00837   ctimer_set(&announcement_cycle_ctimer, ANNOUNCEMENT_PERIOD,
00838              cycle_announcement, NULL);
00839   if(is_listening > 0) {
00840     is_listening--;
00841     /*    printf("is_listening %d\n", is_listening);*/
00842   }
00843 }
00844 /*---------------------------------------------------------------------------*/
00845 static void
00846 listen_callback(int periods)
00847 {
00848   is_listening = periods + 1;
00849 }
00850 #endif /* CXMAC_CONF_ANNOUNCEMENTS */
00851 /*---------------------------------------------------------------------------*/
00852 void
00853 cxmac_set_announcement_radio_txpower(int txpower)
00854 {
00855 #if CXMAC_CONF_ANNOUNCEMENTS
00856   announcement_radio_txpower = txpower;
00857 #endif /* CXMAC_CONF_ANNOUNCEMENTS */
00858 }
00859 /*---------------------------------------------------------------------------*/
00860 void
00861 cxmac_init(void)
00862 {
00863   radio_is_on = 0;
00864   waiting_for_packet = 0;
00865   PT_INIT(&pt);
00866   /*  rtimer_set(&rt, RTIMER_NOW() + cxmac_config.off_time, 1,
00867       (void (*)(struct rtimer *, void *))powercycle, NULL);*/
00868 
00869   cxmac_is_on = 1;
00870 
00871 #if WITH_ENCOUNTER_OPTIMIZATION
00872   list_init(encounter_list);
00873   memb_init(&encounter_memb);
00874 #endif /* WITH_ENCOUNTER_OPTIMIZATION */
00875 
00876 #if CXMAC_CONF_ANNOUNCEMENTS
00877   announcement_register_listen_callback(listen_callback);
00878   ctimer_set(&announcement_cycle_ctimer, ANNOUNCEMENT_TIME,
00879              cycle_announcement, NULL);
00880 #endif /* CXMAC_CONF_ANNOUNCEMENTS */
00881 
00882   CSCHEDULE_POWERCYCLE(DEFAULT_OFF_TIME);
00883 }
00884 /*---------------------------------------------------------------------------*/
00885 static int
00886 turn_on(void)
00887 {
00888   cxmac_is_on = 1;
00889   /*  rtimer_set(&rt, RTIMER_NOW() + cxmac_config.off_time, 1,
00890       (void (*)(struct rtimer *, void *))powercycle, NULL);*/
00891   CSCHEDULE_POWERCYCLE(DEFAULT_OFF_TIME);
00892   return 1;
00893 }
00894 /*---------------------------------------------------------------------------*/
00895 static int
00896 turn_off(int keep_radio_on)
00897 {
00898   cxmac_is_on = 0;
00899   if(keep_radio_on) {
00900     return NETSTACK_RADIO.on();
00901   } else {
00902     return NETSTACK_RADIO.off();
00903   }
00904 }
00905 /*---------------------------------------------------------------------------*/
00906 static unsigned short
00907 channel_check_interval(void)
00908 {
00909   return (1ul * CLOCK_SECOND * DEFAULT_PERIOD) / RTIMER_ARCH_SECOND;
00910 }
00911 /*---------------------------------------------------------------------------*/
00912 const struct rdc_driver cxmac_driver =
00913   {
00914     "CX-MAC",
00915     cxmac_init,
00916     qsend_packet,
00917     input_packet,
00918     turn_on,
00919     turn_off,
00920     channel_check_interval,
00921   };

Generated on Mon Apr 11 14:23:29 2011 for Contiki 2.5 by  doxygen 1.6.1