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