Contiki 3.x
cxmac.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2007, Swedish Institute of Computer Science.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  * notice, this list of conditions and the following disclaimer in the
12  * documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the Institute nor the names of its contributors
14  * may be used to endorse or promote products derived from this software
15  * without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  * This file is part of the Contiki operating system.
30  *
31  */
32 
33 /**
34  * \file
35  * A simple power saving MAC protocol based on X-MAC [SenSys 2006]
36  * \author
37  * Adam Dunkels <adam@sics.se>
38  * Niclas Finne <nfi@sics.se>
39  * Joakim Eriksson <joakime@sics.se>
40  */
41 
42 #include "dev/leds.h"
43 #include "dev/radio.h"
44 #include "dev/watchdog.h"
45 #include "net/netstack.h"
46 #include "lib/random.h"
47 #include "net/mac/cxmac/cxmac.h"
48 #include "net/rime/rime.h"
49 #include "net/rime/timesynch.h"
50 #include "sys/compower.h"
51 #include "sys/pt.h"
52 #include "sys/rtimer.h"
53 
54 #include "contiki-conf.h"
55 
56 #ifdef EXPERIMENT_SETUP
57 #include "experiment-setup.h"
58 #endif
59 
60 #include <string.h>
61 
62 #ifndef WITH_ACK_OPTIMIZATION
63 #define WITH_ACK_OPTIMIZATION 1
64 #endif
65 #ifndef WITH_ENCOUNTER_OPTIMIZATION
66 #define WITH_ENCOUNTER_OPTIMIZATION 1
67 #endif
68 #ifndef WITH_STREAMING
69 #define WITH_STREAMING 1
70 #endif
71 #ifndef WITH_STROBE_BROADCAST
72 #define WITH_STROBE_BROADCAST 0
73 #endif
74 
75 struct announcement_data {
76  uint16_t id;
77  uint16_t value;
78 };
79 
80 /* The maximum number of announcements in a single announcement
81  message - may need to be increased in the future. */
82 #define ANNOUNCEMENT_MAX 10
83 
84 /* The structure of the announcement messages. */
85 struct announcement_msg {
86  uint16_t num;
87  struct announcement_data data[ANNOUNCEMENT_MAX];
88 };
89 
90 /* The length of the header of the announcement message, i.e., the
91  "num" field in the struct. */
92 #define ANNOUNCEMENT_MSG_HEADERLEN (sizeof (uint16_t))
93 
94 #define DISPATCH 0
95 #define TYPE_STROBE 0x10
96 /* #define TYPE_DATA 0x11 */
97 #define TYPE_ANNOUNCEMENT 0x12
98 #define TYPE_STROBE_ACK 0x13
99 
100 struct cxmac_hdr {
101  uint8_t dispatch;
102  uint8_t type;
103 };
104 
105 #define MAX_STROBE_SIZE 50
106 
107 #ifdef CXMAC_CONF_ON_TIME
108 #define DEFAULT_ON_TIME (CXMAC_CONF_ON_TIME)
109 #else
110 #define DEFAULT_ON_TIME (RTIMER_ARCH_SECOND / 160)
111 #endif
112 
113 #ifdef CXMAC_CONF_OFF_TIME
114 #define DEFAULT_OFF_TIME (CXMAC_CONF_OFF_TIME)
115 #else
116 #define DEFAULT_OFF_TIME (RTIMER_ARCH_SECOND / NETSTACK_RDC_CHANNEL_CHECK_RATE - DEFAULT_ON_TIME)
117 #endif
118 
119 #define DEFAULT_PERIOD (DEFAULT_OFF_TIME + DEFAULT_ON_TIME)
120 
121 #define WAIT_TIME_BEFORE_STROBE_ACK RTIMER_ARCH_SECOND / 1000
122 
123 /* On some platforms, we may end up with a DEFAULT_PERIOD that is 0
124  which will make compilation fail due to a modulo operation in the
125  code. To ensure that DEFAULT_PERIOD is greater than zero, we use
126  the construct below. */
127 #if DEFAULT_PERIOD == 0
128 #undef DEFAULT_PERIOD
129 #define DEFAULT_PERIOD 1
130 #endif
131 
132 /* The cycle time for announcements. */
133 #define ANNOUNCEMENT_PERIOD 4 * CLOCK_SECOND
134 
135 /* The time before sending an announcement within one announcement
136  cycle. */
137 #define ANNOUNCEMENT_TIME (random_rand() % (ANNOUNCEMENT_PERIOD))
138 
139 #define DEFAULT_STROBE_WAIT_TIME (7 * DEFAULT_ON_TIME / 8)
140 
141 struct cxmac_config cxmac_config = {
142  DEFAULT_ON_TIME,
143  DEFAULT_OFF_TIME,
144  4 * DEFAULT_ON_TIME + DEFAULT_OFF_TIME,
145  DEFAULT_STROBE_WAIT_TIME
146 };
147 
148 #include <stdio.h>
149 
150 static struct pt pt;
151 
152 static volatile uint8_t cxmac_is_on = 0;
153 
154 static volatile unsigned char waiting_for_packet = 0;
155 static volatile unsigned char someone_is_sending = 0;
156 static volatile unsigned char we_are_sending = 0;
157 static volatile unsigned char radio_is_on = 0;
158 
159 #undef LEDS_ON
160 #undef LEDS_OFF
161 #undef LEDS_TOGGLE
162 
163 #define LEDS_ON(x) leds_on(x)
164 #define LEDS_OFF(x) leds_off(x)
165 #define LEDS_TOGGLE(x) leds_toggle(x)
166 #define DEBUG 0
167 #if DEBUG
168 #include <stdio.h>
169 #define PRINTF(...) printf(__VA_ARGS__)
170 #define PRINTDEBUG(...) printf(__VA_ARGS__)
171 #else
172 #undef LEDS_ON
173 #undef LEDS_OFF
174 #undef LEDS_TOGGLE
175 #define LEDS_ON(x)
176 #define LEDS_OFF(x)
177 #define LEDS_TOGGLE(x)
178 #define PRINTF(...)
179 #define PRINTDEBUG(...)
180 #endif
181 
182 #if CXMAC_CONF_ANNOUNCEMENTS
183 /* Timers for keeping track of when to send announcements. */
184 static struct ctimer announcement_cycle_ctimer, announcement_ctimer;
185 
186 static int announcement_radio_txpower;
187 #endif /* CXMAC_CONF_ANNOUNCEMENTS */
188 
189 /* Flag that is used to keep track of whether or not we are listening
190  for announcements from neighbors. */
191 static uint8_t is_listening;
192 
193 #if CXMAC_CONF_COMPOWER
194 static struct compower_activity current_packet;
195 #endif /* CXMAC_CONF_COMPOWER */
196 
197 #if WITH_ENCOUNTER_OPTIMIZATION
198 
199 #include "lib/list.h"
200 #include "lib/memb.h"
201 
202 struct encounter {
203  struct encounter *next;
204  linkaddr_t neighbor;
205  rtimer_clock_t time;
206 };
207 
208 #define MAX_ENCOUNTERS 4
209 LIST(encounter_list);
210 MEMB(encounter_memb, struct encounter, MAX_ENCOUNTERS);
211 #endif /* WITH_ENCOUNTER_OPTIMIZATION */
212 
213 static uint8_t is_streaming;
214 static linkaddr_t is_streaming_to, is_streaming_to_too;
215 static rtimer_clock_t stream_until;
216 #define DEFAULT_STREAM_TIME (RTIMER_ARCH_SECOND)
217 
218 #ifndef MIN
219 #define MIN(a, b) ((a) < (b)? (a) : (b))
220 #endif /* MIN */
221 
222 /*---------------------------------------------------------------------------*/
223 static void
224 on(void)
225 {
226  if(cxmac_is_on && radio_is_on == 0) {
227  radio_is_on = 1;
228  NETSTACK_RADIO.on();
229  LEDS_ON(LEDS_RED);
230  }
231 }
232 /*---------------------------------------------------------------------------*/
233 static void
234 off(void)
235 {
236  if(cxmac_is_on && radio_is_on != 0 && is_listening == 0 &&
237  is_streaming == 0) {
238  radio_is_on = 0;
239  NETSTACK_RADIO.off();
240  LEDS_OFF(LEDS_RED);
241  }
242 }
243 /*---------------------------------------------------------------------------*/
244 static void
245 powercycle_turn_radio_off(void)
246 {
247  if(we_are_sending == 0 &&
248  waiting_for_packet == 0) {
249  off();
250  }
251 #if CXMAC_CONF_COMPOWER
253 #endif /* CXMAC_CONF_COMPOWER */
254 }
255 static void
256 powercycle_turn_radio_on(void)
257 {
258  if(we_are_sending == 0 &&
259  waiting_for_packet == 0) {
260  on();
261  }
262 }
263 /*---------------------------------------------------------------------------*/
264 static struct ctimer cpowercycle_ctimer;
265 #define CSCHEDULE_POWERCYCLE(rtime) cschedule_powercycle((1ul * CLOCK_SECOND * (rtime)) / RTIMER_ARCH_SECOND)
266 static char cpowercycle(void *ptr);
267 static void
268 cschedule_powercycle(clock_time_t time)
269 {
270 
271  if(cxmac_is_on) {
272  if(time == 0) {
273  time = 1;
274  }
275  ctimer_set(&cpowercycle_ctimer, time,
276  (void (*)(void *))cpowercycle, NULL);
277  }
278 }
279 /*---------------------------------------------------------------------------*/
280 static char
281 cpowercycle(void *ptr)
282 {
283  if(is_streaming) {
284  if(!RTIMER_CLOCK_LT(RTIMER_NOW(), stream_until)) {
285  is_streaming = 0;
286  linkaddr_copy(&is_streaming_to, &linkaddr_null);
287  linkaddr_copy(&is_streaming_to_too, &linkaddr_null);
288  }
289  }
290 
291  PT_BEGIN(&pt);
292 
293  while(1) {
294  /* Only wait for some cycles to pass for someone to start sending */
295  if(someone_is_sending > 0) {
296  someone_is_sending--;
297  }
298 
299  /* If there were a strobe in the air, turn radio on */
300  powercycle_turn_radio_on();
301  CSCHEDULE_POWERCYCLE(DEFAULT_ON_TIME);
302  PT_YIELD(&pt);
303 
304  if(cxmac_config.off_time > 0) {
305  powercycle_turn_radio_off();
306  if(waiting_for_packet != 0) {
307  waiting_for_packet++;
308  if(waiting_for_packet > 2) {
309  /* We should not be awake for more than two consecutive
310  power cycles without having heard a packet, so we turn off
311  the radio. */
312  waiting_for_packet = 0;
313  powercycle_turn_radio_off();
314  }
315  }
316  CSCHEDULE_POWERCYCLE(DEFAULT_OFF_TIME);
317  PT_YIELD(&pt);
318  }
319  }
320 
321  PT_END(&pt);
322 }
323 /*---------------------------------------------------------------------------*/
324 #if CXMAC_CONF_ANNOUNCEMENTS
325 static int
326 parse_announcements(const linkaddr_t *from)
327 {
328  /* Parse incoming announcements */
329  struct announcement_msg adata;
330  int i;
331 
332  memcpy(&adata, packetbuf_dataptr(), MIN(packetbuf_datalen(), sizeof(adata)));
333 
334  /* printf("%d.%d: probe from %d.%d with %d announcements\n",
335  linkaddr_node_addr.u8[0], linkaddr_node_addr.u8[1],
336  from->u8[0], from->u8[1], adata->num);*/
337  /* for(i = 0; i < packetbuf_datalen(); ++i) {
338  printf("%02x ", ((uint8_t *)packetbuf_dataptr())[i]);
339  }
340  printf("\n");*/
341 
342  for(i = 0; i < adata.num; ++i) {
343  /* printf("%d.%d: announcement %d: %d\n",
344  linkaddr_node_addr.u8[0], linkaddr_node_addr.u8[1],
345  adata->data[i].id,
346  adata->data[i].value);*/
347 
348  announcement_heard(from,
349  adata.data[i].id,
350  adata.data[i].value);
351  }
352  return i;
353 }
354 /*---------------------------------------------------------------------------*/
355 static int
356 format_announcement(char *hdr)
357 {
358  struct announcement_msg adata;
359  struct announcement *a;
360 
361  /* Construct the announcements */
362  /* adata = (struct announcement_msg *)hdr;*/
363 
364  adata.num = 0;
365  for(a = announcement_list();
366  a != NULL && adata.num < ANNOUNCEMENT_MAX;
367  a = list_item_next(a)) {
368  adata.data[adata.num].id = a->id;
369  adata.data[adata.num].value = a->value;
370  adata.num++;
371  }
372 
373  memcpy(hdr, &adata, sizeof(struct announcement_msg));
374 
375  if(adata.num > 0) {
376  return ANNOUNCEMENT_MSG_HEADERLEN +
377  sizeof(struct announcement_data) * adata.num;
378  } else {
379  return 0;
380  }
381 }
382 #endif /* CXMAC_CONF_ANNOUNCEMENTS */
383 /*---------------------------------------------------------------------------*/
384 #if WITH_ENCOUNTER_OPTIMIZATION
385 static void
386 register_encounter(const linkaddr_t *neighbor, rtimer_clock_t time)
387 {
388  struct encounter *e;
389 
390  /* If we have an entry for this neighbor already, we renew it. */
391  for(e = list_head(encounter_list); e != NULL; e = list_item_next(e)) {
392  if(linkaddr_cmp(neighbor, &e->neighbor)) {
393  e->time = time;
394  break;
395  }
396  }
397  /* No matching encounter was found, so we allocate a new one. */
398  if(e == NULL) {
399  e = memb_alloc(&encounter_memb);
400  if(e == NULL) {
401  /* We could not allocate memory for this encounter, so we just drop it. */
402  return;
403  }
404  linkaddr_copy(&e->neighbor, neighbor);
405  e->time = time;
406  list_add(encounter_list, e);
407  }
408 }
409 #endif /* WITH_ENCOUNTER_OPTIMIZATION */
410 /*---------------------------------------------------------------------------*/
411 static int
412 send_packet(void)
413 {
414  rtimer_clock_t t0;
415  rtimer_clock_t t;
416  rtimer_clock_t encounter_time = 0;
417  int strobes;
418  struct cxmac_hdr *hdr;
419  int got_strobe_ack = 0;
420  uint8_t strobe[MAX_STROBE_SIZE];
421  int strobe_len, len;
422  int is_broadcast = 0;
423  int is_dispatch, is_strobe_ack;
424  /*int is_reliable;*/
425  struct encounter *e;
426  struct queuebuf *packet;
427  int is_already_streaming = 0;
428  uint8_t collisions;
429 
430 
431  /* Create the X-MAC header for the data packet. */
432 #if !NETSTACK_CONF_BRIDGE_MODE
433  /* If NETSTACK_CONF_BRIDGE_MODE is set, assume PACKETBUF_ADDR_SENDER is already set. */
434  packetbuf_set_addr(PACKETBUF_ADDR_SENDER, &linkaddr_node_addr);
435 #endif
436  if(linkaddr_cmp(packetbuf_addr(PACKETBUF_ADDR_RECEIVER), &linkaddr_null)) {
437  is_broadcast = 1;
438  PRINTDEBUG("cxmac: send broadcast\n");
439  } else {
440 #if UIP_CONF_IPV6
441  PRINTDEBUG("cxmac: send unicast to %02x%02x:%02x%02x:%02x%02x:%02x%02x\n",
442  packetbuf_addr(PACKETBUF_ADDR_RECEIVER)->u8[0],
443  packetbuf_addr(PACKETBUF_ADDR_RECEIVER)->u8[1],
444  packetbuf_addr(PACKETBUF_ADDR_RECEIVER)->u8[2],
445  packetbuf_addr(PACKETBUF_ADDR_RECEIVER)->u8[3],
446  packetbuf_addr(PACKETBUF_ADDR_RECEIVER)->u8[4],
447  packetbuf_addr(PACKETBUF_ADDR_RECEIVER)->u8[5],
448  packetbuf_addr(PACKETBUF_ADDR_RECEIVER)->u8[6],
449  packetbuf_addr(PACKETBUF_ADDR_RECEIVER)->u8[7]);
450 #else
451  PRINTDEBUG("cxmac: send unicast to %u.%u\n",
452  packetbuf_addr(PACKETBUF_ADDR_RECEIVER)->u8[0],
453  packetbuf_addr(PACKETBUF_ADDR_RECEIVER)->u8[1]);
454 #endif /* UIP_CONF_IPV6 */
455  }
456 /* is_reliable = packetbuf_attr(PACKETBUF_ATTR_RELIABLE) ||
457  packetbuf_attr(PACKETBUF_ATTR_ERELIABLE);*/
458  len = NETSTACK_FRAMER.create();
459  strobe_len = len + sizeof(struct cxmac_hdr);
460  if(len < 0 || strobe_len > (int)sizeof(strobe)) {
461  /* Failed to send */
462  PRINTF("cxmac: send failed, too large header\n");
463  return MAC_TX_ERR_FATAL;
464  }
465  memcpy(strobe, packetbuf_hdrptr(), len);
466  strobe[len] = DISPATCH; /* dispatch */
467  strobe[len + 1] = TYPE_STROBE; /* type */
468 
470  packet = queuebuf_new_from_packetbuf();
471  if(packet == NULL) {
472  /* No buffer available */
473  PRINTF("cxmac: send failed, no queue buffer available (of %u)\n",
474  QUEUEBUF_CONF_NUM);
475  return MAC_TX_ERR;
476  }
477 
478 #if WITH_STREAMING
479  if(is_streaming == 1 &&
480  (linkaddr_cmp(packetbuf_addr(PACKETBUF_ADDR_RECEIVER),
481  &is_streaming_to) ||
482  linkaddr_cmp(packetbuf_addr(PACKETBUF_ADDR_RECEIVER),
483  &is_streaming_to_too))) {
484  is_already_streaming = 1;
485  }
486  if(packetbuf_attr(PACKETBUF_ATTR_PACKET_TYPE) ==
487  PACKETBUF_ATTR_PACKET_TYPE_STREAM) {
488  is_streaming = 1;
489  if(linkaddr_cmp(&is_streaming_to, &linkaddr_null)) {
490  linkaddr_copy(&is_streaming_to, packetbuf_addr(PACKETBUF_ADDR_RECEIVER));
491  } else if(!linkaddr_cmp(&is_streaming_to, packetbuf_addr(PACKETBUF_ADDR_RECEIVER))) {
492  linkaddr_copy(&is_streaming_to_too, packetbuf_addr(PACKETBUF_ADDR_RECEIVER));
493  }
494  stream_until = RTIMER_NOW() + DEFAULT_STREAM_TIME;
495  }
496 #endif /* WITH_STREAMING */
497 
498  off();
499 
500 #if WITH_ENCOUNTER_OPTIMIZATION
501  /* We go through the list of encounters to find if we have recorded
502  an encounter with this particular neighbor. If so, we can compute
503  the time for the next expected encounter and setup a ctimer to
504  switch on the radio just before the encounter. */
505  for(e = list_head(encounter_list); e != NULL; e = list_item_next(e)) {
506  const linkaddr_t *neighbor = packetbuf_addr(PACKETBUF_ADDR_RECEIVER);
507 
508  if(linkaddr_cmp(neighbor, &e->neighbor)) {
509  rtimer_clock_t wait, now, expected;
510 
511  /* We expect encounters to happen every DEFAULT_PERIOD time
512  units. The next expected encounter is at time e->time +
513  DEFAULT_PERIOD. To compute a relative offset, we subtract
514  with clock_time(). Because we are only interested in turning
515  on the radio within the DEFAULT_PERIOD period, we compute the
516  waiting time with modulo DEFAULT_PERIOD. */
517 
518  now = RTIMER_NOW();
519  wait = ((rtimer_clock_t)(e->time - now)) % (DEFAULT_PERIOD);
520  expected = now + wait - 2 * DEFAULT_ON_TIME;
521 
522 #if WITH_ACK_OPTIMIZATION
523  /* Wait until the receiver is expected to be awake */
524  if(packetbuf_attr(PACKETBUF_ATTR_PACKET_TYPE) !=
525  PACKETBUF_ATTR_PACKET_TYPE_ACK &&
526  is_streaming == 0) {
527  /* Do not wait if we are sending an ACK, because then the
528  receiver will already be awake. */
529  while(RTIMER_CLOCK_LT(RTIMER_NOW(), expected));
530  }
531 #else /* WITH_ACK_OPTIMIZATION */
532  /* Wait until the receiver is expected to be awake */
533  while(RTIMER_CLOCK_LT(RTIMER_NOW(), expected));
534 #endif /* WITH_ACK_OPTIMIZATION */
535  }
536  }
537 #endif /* WITH_ENCOUNTER_OPTIMIZATION */
538 
539  /* By setting we_are_sending to one, we ensure that the rtimer
540  powercycle interrupt do not interfere with us sending the packet. */
541  we_are_sending = 1;
542 
543  t0 = RTIMER_NOW();
544  strobes = 0;
545 
546  LEDS_ON(LEDS_BLUE);
547 
548  /* Send a train of strobes until the receiver answers with an ACK. */
549 
550  /* Turn on the radio to listen for the strobe ACK. */
551  on();
552  collisions = 0;
553  if(!is_already_streaming) {
554  watchdog_stop();
555  got_strobe_ack = 0;
556  t = RTIMER_NOW();
557  for(strobes = 0, collisions = 0;
558  got_strobe_ack == 0 && collisions == 0 &&
559  RTIMER_CLOCK_LT(RTIMER_NOW(), t0 + cxmac_config.strobe_time);
560  strobes++) {
561 
562  while(got_strobe_ack == 0 &&
563  RTIMER_CLOCK_LT(RTIMER_NOW(), t + cxmac_config.strobe_wait_time)) {
564  rtimer_clock_t now = RTIMER_NOW();
565 
566  /* See if we got an ACK */
567  packetbuf_clear();
568  len = NETSTACK_RADIO.read(packetbuf_dataptr(), PACKETBUF_SIZE);
569  if(len > 0) {
571  if(NETSTACK_FRAMER.parse() >= 0) {
572  hdr = packetbuf_dataptr();
573  is_dispatch = hdr->dispatch == DISPATCH;
574  is_strobe_ack = hdr->type == TYPE_STROBE_ACK;
575  if(is_dispatch && is_strobe_ack) {
576  if(linkaddr_cmp(packetbuf_addr(PACKETBUF_ADDR_RECEIVER),
577  &linkaddr_node_addr)) {
578  /* We got an ACK from the receiver, so we can immediately send
579  the packet. */
580  got_strobe_ack = 1;
581  encounter_time = now;
582  } else {
583  PRINTDEBUG("cxmac: strobe ack for someone else\n");
584  }
585  } else /*if(hdr->dispatch == DISPATCH && hdr->type == TYPE_STROBE)*/ {
586  PRINTDEBUG("cxmac: strobe from someone else\n");
587  collisions++;
588  }
589  } else {
590  PRINTF("cxmac: send failed to parse %u\n", len);
591  }
592  }
593  }
594 
595  t = RTIMER_NOW();
596  /* Send the strobe packet. */
597  if(got_strobe_ack == 0 && collisions == 0) {
598  if(is_broadcast) {
599 #if WITH_STROBE_BROADCAST
600  NETSTACK_RADIO.send(strobe, strobe_len);
601 #else
602  /* restore the packet to send */
603  queuebuf_to_packetbuf(packet);
604  NETSTACK_RADIO.send(packetbuf_hdrptr(), packetbuf_totlen());
605 #endif
606  off();
607  } else {
608  NETSTACK_RADIO.send(strobe, strobe_len);
609 #if 0
610  /* Turn off the radio for a while to let the other side
611  respond. We don't need to keep our radio on when we know
612  that the other side needs some time to produce a reply. */
613  off();
614  rtimer_clock_t wt = RTIMER_NOW();
615  while(RTIMER_CLOCK_LT(RTIMER_NOW(), wt + WAIT_TIME_BEFORE_STROBE_ACK));
616 #endif /* 0 */
617  on();
618  }
619  }
620  }
621  }
622 
623 #if WITH_ACK_OPTIMIZATION
624  /* If we have received the strobe ACK, and we are sending a packet
625  that will need an upper layer ACK (as signified by the
626  PACKETBUF_ATTR_RELIABLE packet attribute), we keep the radio on. */
627  if(got_strobe_ack && (packetbuf_attr(PACKETBUF_ATTR_RELIABLE) ||
628  packetbuf_attr(PACKETBUF_ATTR_ERELIABLE) ||
629  packetbuf_attr(PACKETBUF_ATTR_PACKET_TYPE) ==
630  PACKETBUF_ATTR_PACKET_TYPE_STREAM)) {
631  on(); /* Wait for ACK packet */
632  waiting_for_packet = 1;
633  } else {
634  off();
635  }
636 #else /* WITH_ACK_OPTIMIZATION */
637  off();
638 #endif /* WITH_ACK_OPTIMIZATION */
639 
640  /* restore the packet to send */
641  queuebuf_to_packetbuf(packet);
642  queuebuf_free(packet);
643 
644  /* Send the data packet. */
645  if((is_broadcast || got_strobe_ack || is_streaming) && collisions == 0) {
646  NETSTACK_RADIO.send(packetbuf_hdrptr(), packetbuf_totlen());
647  }
648 
649 #if WITH_ENCOUNTER_OPTIMIZATION
650  if(got_strobe_ack && !is_streaming) {
651  register_encounter(packetbuf_addr(PACKETBUF_ADDR_RECEIVER), encounter_time);
652  }
653 #endif /* WITH_ENCOUNTER_OPTIMIZATION */
654  watchdog_start();
655 
656  PRINTF("cxmac: send (strobes=%u,len=%u,%s), done\n", strobes,
657  packetbuf_totlen(), got_strobe_ack ? "ack" : "no ack");
658 
659 #if CXMAC_CONF_COMPOWER
660  /* Accumulate the power consumption for the packet transmission. */
661  compower_accumulate(&current_packet);
662 
663  /* Convert the accumulated power consumption for the transmitted
664  packet to packet attributes so that the higher levels can keep
665  track of the amount of energy spent on transmitting the
666  packet. */
667  compower_attrconv(&current_packet);
668 
669  /* Clear the accumulated power consumption so that it is ready for
670  the next packet. */
671  compower_clear(&current_packet);
672 #endif /* CXMAC_CONF_COMPOWER */
673 
674  we_are_sending = 0;
675 
676  LEDS_OFF(LEDS_BLUE);
677  if(collisions == 0) {
678  if(!is_broadcast && !got_strobe_ack) {
679  return MAC_TX_NOACK;
680  } else {
681  return MAC_TX_OK;
682  }
683  } else {
684  someone_is_sending++;
685  return MAC_TX_COLLISION;
686  }
687 
688 }
689 /*---------------------------------------------------------------------------*/
690 static void
691 qsend_packet(mac_callback_t sent, void *ptr)
692 {
693  int ret;
694  if(someone_is_sending) {
695  PRINTF("cxmac: should queue packet, now just dropping %d %d %d %d.\n",
696  waiting_for_packet, someone_is_sending, we_are_sending, radio_is_on);
697  RIMESTATS_ADD(sendingdrop);
698  ret = MAC_TX_COLLISION;
699  } else {
700  PRINTF("cxmac: send immediately.\n");
701  ret = send_packet();
702  }
703 
704  mac_call_sent_callback(sent, ptr, ret, 1);
705 }
706 /*---------------------------------------------------------------------------*/
707 static void
708 qsend_list(mac_callback_t sent, void *ptr, struct rdc_buf_list *buf_list)
709 {
710  if(buf_list != NULL) {
711  queuebuf_to_packetbuf(buf_list->buf);
712  qsend_packet(sent, ptr);
713  }
714 }
715 /*---------------------------------------------------------------------------*/
716 static void
717 input_packet(void)
718 {
719  struct cxmac_hdr *hdr;
720 
721  if(NETSTACK_FRAMER.parse() >= 0) {
722  hdr = packetbuf_dataptr();
723 
724  if(hdr->dispatch != DISPATCH) {
725  someone_is_sending = 0;
726  if(linkaddr_cmp(packetbuf_addr(PACKETBUF_ADDR_RECEIVER),
727  &linkaddr_node_addr) ||
728  linkaddr_cmp(packetbuf_addr(PACKETBUF_ADDR_RECEIVER),
729  &linkaddr_null)) {
730  /* This is a regular packet that is destined to us or to the
731  broadcast address. */
732 
733  /* We have received the final packet, so we can go back to being
734  asleep. */
735  off();
736 
737 #if CXMAC_CONF_COMPOWER
738  /* Accumulate the power consumption for the packet reception. */
739  compower_accumulate(&current_packet);
740  /* Convert the accumulated power consumption for the received
741  packet to packet attributes so that the higher levels can
742  keep track of the amount of energy spent on receiving the
743  packet. */
744  compower_attrconv(&current_packet);
745 
746  /* Clear the accumulated power consumption so that it is ready
747  for the next packet. */
748  compower_clear(&current_packet);
749 #endif /* CXMAC_CONF_COMPOWER */
750 
751  waiting_for_packet = 0;
752 
753  PRINTDEBUG("cxmac: data(%u)\n", packetbuf_datalen());
754  NETSTACK_MAC.input();
755  return;
756  } else {
757  PRINTDEBUG("cxmac: data not for us\n");
758  }
759 
760  } else if(hdr->type == TYPE_STROBE) {
761  someone_is_sending = 2;
762 
763  if(linkaddr_cmp(packetbuf_addr(PACKETBUF_ADDR_RECEIVER),
764  &linkaddr_node_addr)) {
765  /* This is a strobe packet for us. */
766 
767  /* If the sender address is someone else, we should
768  acknowledge the strobe and wait for the packet. By using
769  the same address as both sender and receiver, we flag the
770  message is a strobe ack. */
771  hdr->type = TYPE_STROBE_ACK;
772  packetbuf_set_addr(PACKETBUF_ADDR_RECEIVER,
773  packetbuf_addr(PACKETBUF_ADDR_SENDER));
774  packetbuf_set_addr(PACKETBUF_ADDR_SENDER, &linkaddr_node_addr);
776  if(NETSTACK_FRAMER.create() >= 0) {
777  /* We turn on the radio in anticipation of the incoming
778  packet. */
779  someone_is_sending = 1;
780  waiting_for_packet = 1;
781  on();
782  NETSTACK_RADIO.send(packetbuf_hdrptr(), packetbuf_totlen());
783  PRINTDEBUG("cxmac: send strobe ack %u\n", packetbuf_totlen());
784  } else {
785  PRINTF("cxmac: failed to send strobe ack\n");
786  }
787  } else if(linkaddr_cmp(packetbuf_addr(PACKETBUF_ADDR_RECEIVER),
788  &linkaddr_null)) {
789  /* If the receiver address is null, the strobe is sent to
790  prepare for an incoming broadcast packet. If this is the
791  case, we turn on the radio and wait for the incoming
792  broadcast packet. */
793  waiting_for_packet = 1;
794  on();
795  } else {
796  PRINTDEBUG("cxmac: strobe not for us\n");
797  }
798 
799  /* We are done processing the strobe and we therefore return
800  to the caller. */
801  return;
802 #if CXMAC_CONF_ANNOUNCEMENTS
803  } else if(hdr->type == TYPE_ANNOUNCEMENT) {
804  packetbuf_hdrreduce(sizeof(struct cxmac_hdr));
805  parse_announcements(packetbuf_addr(PACKETBUF_ADDR_SENDER));
806 #endif /* CXMAC_CONF_ANNOUNCEMENTS */
807  } else if(hdr->type == TYPE_STROBE_ACK) {
808  PRINTDEBUG("cxmac: stray strobe ack\n");
809  } else {
810  PRINTF("cxmac: unknown type %u (%u)\n", hdr->type,
812  }
813  } else {
814  PRINTF("cxmac: failed to parse (%u)\n", packetbuf_totlen());
815  }
816 }
817 /*---------------------------------------------------------------------------*/
818 #if CXMAC_CONF_ANNOUNCEMENTS
819 static void
820 send_announcement(void *ptr)
821 {
822  struct cxmac_hdr *hdr;
823  int announcement_len;
824 
825  /* Set up the probe header. */
826  packetbuf_clear();
827  hdr = packetbuf_dataptr();
828 
829  announcement_len = format_announcement((char *)hdr +
830  sizeof(struct cxmac_hdr));
831 
832  if(announcement_len > 0) {
833  packetbuf_set_datalen(sizeof(struct cxmac_hdr) + announcement_len);
834  hdr->dispatch = DISPATCH;
835  hdr->type = TYPE_ANNOUNCEMENT;
836 
837  packetbuf_set_addr(PACKETBUF_ADDR_SENDER, &linkaddr_node_addr);
838  packetbuf_set_addr(PACKETBUF_ADDR_RECEIVER, &linkaddr_null);
839  packetbuf_set_attr(PACKETBUF_ATTR_RADIO_TXPOWER, announcement_radio_txpower);
840  if(NETSTACK_FRAMER.create() >= 0) {
841  NETSTACK_RADIO.send(packetbuf_hdrptr(), packetbuf_totlen());
842  }
843  }
844 }
845 /*---------------------------------------------------------------------------*/
846 static void
847 cycle_announcement(void *ptr)
848 {
849  ctimer_set(&announcement_ctimer, ANNOUNCEMENT_TIME,
850  send_announcement, NULL);
851  ctimer_set(&announcement_cycle_ctimer, ANNOUNCEMENT_PERIOD,
852  cycle_announcement, NULL);
853  if(is_listening > 0) {
854  is_listening--;
855  /* printf("is_listening %d\n", is_listening);*/
856  }
857 }
858 /*---------------------------------------------------------------------------*/
859 static void
860 listen_callback(int periods)
861 {
862  is_listening = periods + 1;
863 }
864 #endif /* CXMAC_CONF_ANNOUNCEMENTS */
865 /*---------------------------------------------------------------------------*/
866 void
867 cxmac_set_announcement_radio_txpower(int txpower)
868 {
869 #if CXMAC_CONF_ANNOUNCEMENTS
870  announcement_radio_txpower = txpower;
871 #endif /* CXMAC_CONF_ANNOUNCEMENTS */
872 }
873 /*---------------------------------------------------------------------------*/
874 void
875 cxmac_init(void)
876 {
877  radio_is_on = 0;
878  waiting_for_packet = 0;
879  PT_INIT(&pt);
880  /* rtimer_set(&rt, RTIMER_NOW() + cxmac_config.off_time, 1,
881  (void (*)(struct rtimer *, void *))powercycle, NULL);*/
882 
883  cxmac_is_on = 1;
884 
885 #if WITH_ENCOUNTER_OPTIMIZATION
886  list_init(encounter_list);
887  memb_init(&encounter_memb);
888 #endif /* WITH_ENCOUNTER_OPTIMIZATION */
889 
890 #if CXMAC_CONF_ANNOUNCEMENTS
891  announcement_register_listen_callback(listen_callback);
892  ctimer_set(&announcement_cycle_ctimer, ANNOUNCEMENT_TIME,
893  cycle_announcement, NULL);
894 #endif /* CXMAC_CONF_ANNOUNCEMENTS */
895 
896  CSCHEDULE_POWERCYCLE(DEFAULT_OFF_TIME);
897 }
898 /*---------------------------------------------------------------------------*/
899 static int
900 turn_on(void)
901 {
902  cxmac_is_on = 1;
903  /* rtimer_set(&rt, RTIMER_NOW() + cxmac_config.off_time, 1,
904  (void (*)(struct rtimer *, void *))powercycle, NULL);*/
905  CSCHEDULE_POWERCYCLE(DEFAULT_OFF_TIME);
906  return 1;
907 }
908 /*---------------------------------------------------------------------------*/
909 static int
910 turn_off(int keep_radio_on)
911 {
912  cxmac_is_on = 0;
913  if(keep_radio_on) {
914  return NETSTACK_RADIO.on();
915  } else {
916  return NETSTACK_RADIO.off();
917  }
918 }
919 /*---------------------------------------------------------------------------*/
920 static unsigned short
921 channel_check_interval(void)
922 {
923  return (1ul * CLOCK_SECOND * DEFAULT_PERIOD) / RTIMER_ARCH_SECOND;
924 }
925 /*---------------------------------------------------------------------------*/
926 const struct rdc_driver cxmac_driver =
927  {
928  "CX-MAC",
929  cxmac_init,
930  qsend_packet,
931  qsend_list,
932  input_packet,
933  turn_on,
934  turn_off,
936  };
Linked list manipulation routines.
linkaddr_t linkaddr_node_addr
The Rime address of the node.
Definition: linkaddr.c:48
void compower_accumulate(struct compower_activity *e)
Accumulate power contumption for a communication activity.
Definition: compower.c:60
void memb_init(struct memb *m)
Initialize a memory block that was declared with MEMB().
Definition: memb.c:52
#define LEDS_RED
LED1 (Red) -&gt; PC0.
Definition: board.h:89
void watchdog_start(void)
Starts the WDT in watchdog mode if enabled by user configuration, maximum interval.
Definition: watchdog.c:49
#define PT_YIELD(pt)
Yield from the current protothread.
Definition: pt.h:289
Header file for the radio API
const linkaddr_t linkaddr_null
The null Rime address.
The MAC layer deferred the transmission for a later time.
Definition: mac.h:86
void * list_item_next(void *item)
Get the next item following this item.
Definition: list.c:325
void compower_attrconv(struct compower_activity *e)
Convert power contumption information to packet attributes.
Definition: compower.c:83
void announcement_heard(const linkaddr_t *from, uint16_t id, uint16_t value)
Inform the announcement module of an incoming announcement.
Definition: announcement.c:140
void * memb_alloc(struct memb *m)
Allocate a memory block from a block of memory declared with MEMB().
Definition: memb.c:59
#define NULL
The null pointer.
The MAC layer transmission could not be performed because of a fatal error.
Definition: mac.h:93
#define PT_INIT(pt)
Initialize a protothread.
Definition: pt.h:79
Header file for a simple time synchronization mechanism
struct compower_activity compower_idle_activity
The default idle communication activity.
Definition: compower.c:50
void packetbuf_set_datalen(uint16_t len)
Set the length of the data in the packetbuf.
Definition: packetbuf.c:200
uint16_t packetbuf_totlen(void)
Get the total length of the header and data in the packetbuf.
Definition: packetbuf.c:260
Header file for the Rime stack
void compower_clear(struct compower_activity *e)
Clear power consumption information for a communication activity.
Definition: compower.c:77
Header file for the communication power accounting module
uint16_t packetbuf_datalen(void)
Get the length of the data in the packetbuf.
Definition: packetbuf.c:239
struct announcement * announcement_list(void)
Get the list of registered announcements.
Definition: announcement.c:134
void linkaddr_copy(linkaddr_t *dest, const linkaddr_t *src)
Copy a Rime address.
Definition: linkaddr.c:60
void watchdog_stop(void)
In watchdog mode, the WDT can not be stopped.
Definition: watchdog.c:58
void list_init(list_t list)
Initialize a list.
Definition: list.c:66
void ctimer_set(struct ctimer *c, clock_time_t t, void(*f)(void *), void *ptr)
Set a callback timer.
Definition: ctimer.c:99
void * packetbuf_hdrptr(void)
Get a pointer to the header in the packetbuf, for outbound packets.
Definition: packetbuf.c:213
Header file for the real-time timer module.
void * list_head(list_t list)
Get a pointer to the first element of a list.
Definition: list.c:83
Protothreads implementation.
#define MEMB(name, structure, num)
Declare a memory block.
Definition: memb.h:89
void packetbuf_compact(void)
Compact the packetbuf.
Definition: packetbuf.c:105
void list_add(list_t list, void *item)
Add an item at the end of a list.
Definition: list.c:143
#define PT_BEGIN(pt)
Declare the start of a protothread inside the C function implementing the protothread.
Definition: pt.h:114
void announcement_register_listen_callback(void(*callback)(int time))
Register a listen callback with the announcement module.
Definition: announcement.c:122
#define LIST(name)
Declare a linked list.
Definition: list.h:86
unsigned short(* channel_check_interval)(void)
Returns the channel check interval, expressed in clock_time_t ticks.
Definition: rdc.h:89
The structure of a RDC (radio duty cycling) driver in Contiki.
Definition: rdc.h:67
void packetbuf_clear(void)
Clear and reset the packetbuf.
Definition: packetbuf.c:77
Representation of an announcement.
Definition: announcement.h:83
#define PT_END(pt)
Declare the end of a protothread.
Definition: pt.h:126
The MAC layer transmission was OK.
Definition: mac.h:79
#define RTIMER_NOW()
Get the current clock time.
Definition: rtimer.h:133
int linkaddr_cmp(const linkaddr_t *addr1, const linkaddr_t *addr2)
Compare two Rime addresses.
Definition: linkaddr.c:66
A simple power saving MAC protocol based on X-MAC [SenSys 2006]
#define PACKETBUF_SIZE
The size of the packetbuf, in bytes.
Definition: packetbuf.h:65
The MAC layer did not get an acknowledgement for the packet.
Definition: mac.h:83
void * packetbuf_dataptr(void)
Get a pointer to the data in the packetbuf.
Definition: packetbuf.c:207
Memory block allocation routines.
An activity record that contains power consumption information for a specific communication activity...
Definition: compower.h:64
int packetbuf_hdrreduce(int size)
Reduce the header in the packetbuf, for incoming packets.
Definition: packetbuf.c:188
Include file for the Contiki low-layer network stack (NETSTACK)
#define CLOCK_SECOND
A second, measured in system clock time.
Definition: clock.h:82