Contiki 2.5
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  * $Id: cxmac.c,v 1.15 2010/10/03 20:37:32 adamdunkels Exp $
32  */
33 
34 /**
35  * \file
36  * A simple power saving MAC protocol based on X-MAC [SenSys 2006]
37  * \author
38  * Adam Dunkels <adam@sics.se>
39  * Niclas Finne <nfi@sics.se>
40  * Joakim Eriksson <joakime@sics.se>
41  */
42 
43 #include "dev/leds.h"
44 #include "dev/radio.h"
45 #include "dev/watchdog.h"
46 #include "net/netstack.h"
47 #include "lib/random.h"
48 #include "net/mac/cxmac.h"
49 #include "net/rime.h"
50 #include "net/rime/timesynch.h"
51 #include "sys/compower.h"
52 #include "sys/pt.h"
53 #include "sys/rtimer.h"
54 
55 #include "contiki-conf.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  rimeaddr_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 rimeaddr_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 #ifndef MIN
220 #define MIN(a, b) ((a) < (b)? (a) : (b))
221 #endif /* MIN */
222 
223 /*---------------------------------------------------------------------------*/
224 static void
225 on(void)
226 {
227  if(cxmac_is_on && radio_is_on == 0) {
228  radio_is_on = 1;
229  NETSTACK_RADIO.on();
230  LEDS_ON(LEDS_RED);
231  }
232 }
233 /*---------------------------------------------------------------------------*/
234 static void
235 off(void)
236 {
237  if(cxmac_is_on && radio_is_on != 0 && is_listening == 0 &&
238  is_streaming == 0) {
239  radio_is_on = 0;
240  NETSTACK_RADIO.off();
241  LEDS_OFF(LEDS_RED);
242  }
243 }
244 /*---------------------------------------------------------------------------*/
245 static void
246 powercycle_turn_radio_off(void)
247 {
248  if(we_are_sending == 0 &&
249  waiting_for_packet == 0) {
250  off();
251  }
252 #if CXMAC_CONF_COMPOWER
254 #endif /* CXMAC_CONF_COMPOWER */
255 }
256 static void
257 powercycle_turn_radio_on(void)
258 {
259  if(we_are_sending == 0 &&
260  waiting_for_packet == 0) {
261  on();
262  }
263 }
264 /*---------------------------------------------------------------------------*/
265 static struct ctimer cpowercycle_ctimer;
266 #define CSCHEDULE_POWERCYCLE(rtime) cschedule_powercycle((1ul * CLOCK_SECOND * (rtime)) / RTIMER_ARCH_SECOND)
267 static char cpowercycle(void *ptr);
268 static void
269 cschedule_powercycle(clock_time_t time)
270 {
271 
272  if(cxmac_is_on) {
273  if(time == 0) {
274  time = 1;
275  }
276  ctimer_set(&cpowercycle_ctimer, time,
277  (void (*)(void *))cpowercycle, NULL);
278  }
279 }
280 /*---------------------------------------------------------------------------*/
281 static char
282 cpowercycle(void *ptr)
283 {
284  if(is_streaming) {
285  if(!RTIMER_CLOCK_LT(RTIMER_NOW(), stream_until)) {
286  is_streaming = 0;
287  rimeaddr_copy(&is_streaming_to, &rimeaddr_null);
288  rimeaddr_copy(&is_streaming_to_too, &rimeaddr_null);
289  }
290  }
291 
292  PT_BEGIN(&pt);
293 
294  while(1) {
295  /* Only wait for some cycles to pass for someone to start sending */
296  if(someone_is_sending > 0) {
297  someone_is_sending--;
298  }
299 
300  /* If there were a strobe in the air, turn radio on */
301  powercycle_turn_radio_on();
302  CSCHEDULE_POWERCYCLE(DEFAULT_ON_TIME);
303  PT_YIELD(&pt);
304 
305  if(cxmac_config.off_time > 0) {
306  powercycle_turn_radio_off();
307  if(waiting_for_packet != 0) {
308  waiting_for_packet++;
309  if(waiting_for_packet > 2) {
310  /* We should not be awake for more than two consecutive
311  power cycles without having heard a packet, so we turn off
312  the radio. */
313  waiting_for_packet = 0;
314  powercycle_turn_radio_off();
315  }
316  }
317  CSCHEDULE_POWERCYCLE(DEFAULT_OFF_TIME);
318  PT_YIELD(&pt);
319  }
320  }
321 
322  PT_END(&pt);
323 }
324 /*---------------------------------------------------------------------------*/
325 #if CXMAC_CONF_ANNOUNCEMENTS
326 static int
327 parse_announcements(const rimeaddr_t *from)
328 {
329  /* Parse incoming announcements */
330  struct announcement_msg adata;
331  int i;
332 
333  memcpy(&adata, packetbuf_dataptr(), MIN(packetbuf_datalen(), sizeof(adata)));
334 
335  /* printf("%d.%d: probe from %d.%d with %d announcements\n",
336  rimeaddr_node_addr.u8[0], rimeaddr_node_addr.u8[1],
337  from->u8[0], from->u8[1], adata->num);*/
338  /* for(i = 0; i < packetbuf_datalen(); ++i) {
339  printf("%02x ", ((uint8_t *)packetbuf_dataptr())[i]);
340  }
341  printf("\n");*/
342 
343  for(i = 0; i < adata.num; ++i) {
344  /* printf("%d.%d: announcement %d: %d\n",
345  rimeaddr_node_addr.u8[0], rimeaddr_node_addr.u8[1],
346  adata->data[i].id,
347  adata->data[i].value);*/
348 
349  announcement_heard(from,
350  adata.data[i].id,
351  adata.data[i].value);
352  }
353  return i;
354 }
355 /*---------------------------------------------------------------------------*/
356 static int
357 format_announcement(char *hdr)
358 {
359  struct announcement_msg adata;
360  struct announcement *a;
361 
362  /* Construct the announcements */
363  /* adata = (struct announcement_msg *)hdr;*/
364 
365  adata.num = 0;
366  for(a = announcement_list();
367  a != NULL && adata.num < ANNOUNCEMENT_MAX;
368  a = list_item_next(a)) {
369  adata.data[adata.num].id = a->id;
370  adata.data[adata.num].value = a->value;
371  adata.num++;
372  }
373 
374  memcpy(hdr, &adata, sizeof(struct announcement_msg));
375 
376  if(adata.num > 0) {
377  return ANNOUNCEMENT_MSG_HEADERLEN +
378  sizeof(struct announcement_data) * adata.num;
379  } else {
380  return 0;
381  }
382 }
383 #endif /* CXMAC_CONF_ANNOUNCEMENTS */
384 /*---------------------------------------------------------------------------*/
385 #if WITH_ENCOUNTER_OPTIMIZATION
386 static void
387 register_encounter(const rimeaddr_t *neighbor, rtimer_clock_t time)
388 {
389  struct encounter *e;
390 
391  /* If we have an entry for this neighbor already, we renew it. */
392  for(e = list_head(encounter_list); e != NULL; e = list_item_next(e)) {
393  if(rimeaddr_cmp(neighbor, &e->neighbor)) {
394  e->time = time;
395  break;
396  }
397  }
398  /* No matching encounter was found, so we allocate a new one. */
399  if(e == NULL) {
400  e = memb_alloc(&encounter_memb);
401  if(e == NULL) {
402  /* We could not allocate memory for this encounter, so we just drop it. */
403  return;
404  }
405  rimeaddr_copy(&e->neighbor, neighbor);
406  e->time = time;
407  list_add(encounter_list, e);
408  }
409 }
410 #endif /* WITH_ENCOUNTER_OPTIMIZATION */
411 /*---------------------------------------------------------------------------*/
412 static int
413 send_packet(void)
414 {
415  rtimer_clock_t t0;
416  rtimer_clock_t t;
417  rtimer_clock_t encounter_time = 0;
418  int strobes;
419  struct cxmac_hdr *hdr;
420  int got_strobe_ack = 0;
421  uint8_t strobe[MAX_STROBE_SIZE];
422  int strobe_len, len;
423  int is_broadcast = 0;
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  packetbuf_set_addr(PACKETBUF_ADDR_SENDER, &rimeaddr_node_addr);
433  if(rimeaddr_cmp(packetbuf_addr(PACKETBUF_ADDR_RECEIVER), &rimeaddr_null)) {
434  is_broadcast = 1;
435  PRINTDEBUG("cxmac: send broadcast\n");
436  } else {
437 #if UIP_CONF_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 /* UIP_CONF_IPV6 */
452  }
453  is_reliable = packetbuf_attr(PACKETBUF_ATTR_RELIABLE) ||
454  packetbuf_attr(PACKETBUF_ATTR_ERELIABLE);
455  len = NETSTACK_FRAMER.create();
456  strobe_len = len + sizeof(struct cxmac_hdr);
457  if(len == 0 || strobe_len > (int)sizeof(strobe)) {
458  /* Failed to send */
459  PRINTF("cxmac: send failed, too large header\n");
460  return MAC_TX_ERR_FATAL;
461  }
462  memcpy(strobe, packetbuf_hdrptr(), len);
463  strobe[len] = DISPATCH; /* dispatch */
464  strobe[len + 1] = TYPE_STROBE; /* type */
465 
467  packet = queuebuf_new_from_packetbuf();
468  if(packet == NULL) {
469  /* No buffer available */
470  PRINTF("cxmac: send failed, no queue buffer available (of %u)\n",
471  QUEUEBUF_CONF_NUM);
472  return MAC_TX_ERR;
473  }
474 
475 #if WITH_STREAMING
476  if(is_streaming == 1 &&
477  (rimeaddr_cmp(packetbuf_addr(PACKETBUF_ADDR_RECEIVER),
478  &is_streaming_to) ||
479  rimeaddr_cmp(packetbuf_addr(PACKETBUF_ADDR_RECEIVER),
480  &is_streaming_to_too))) {
481  is_already_streaming = 1;
482  }
483  if(packetbuf_attr(PACKETBUF_ATTR_PACKET_TYPE) ==
484  PACKETBUF_ATTR_PACKET_TYPE_STREAM) {
485  is_streaming = 1;
486  if(rimeaddr_cmp(&is_streaming_to, &rimeaddr_null)) {
487  rimeaddr_copy(&is_streaming_to, packetbuf_addr(PACKETBUF_ADDR_RECEIVER));
488  } else if(!rimeaddr_cmp(&is_streaming_to, packetbuf_addr(PACKETBUF_ADDR_RECEIVER))) {
489  rimeaddr_copy(&is_streaming_to_too, packetbuf_addr(PACKETBUF_ADDR_RECEIVER));
490  }
491  stream_until = RTIMER_NOW() + DEFAULT_STREAM_TIME;
492  }
493 #endif /* WITH_STREAMING */
494 
495  off();
496 
497 #if WITH_ENCOUNTER_OPTIMIZATION
498  /* We go through the list of encounters to find if we have recorded
499  an encounter with this particular neighbor. If so, we can compute
500  the time for the next expected encounter and setup a ctimer to
501  switch on the radio just before the encounter. */
502  for(e = list_head(encounter_list); e != NULL; e = list_item_next(e)) {
503  const rimeaddr_t *neighbor = packetbuf_addr(PACKETBUF_ADDR_RECEIVER);
504 
505  if(rimeaddr_cmp(neighbor, &e->neighbor)) {
506  rtimer_clock_t wait, now, expected;
507 
508  /* We expect encounters to happen every DEFAULT_PERIOD time
509  units. The next expected encounter is at time e->time +
510  DEFAULT_PERIOD. To compute a relative offset, we subtract
511  with clock_time(). Because we are only interested in turning
512  on the radio within the DEFAULT_PERIOD period, we compute the
513  waiting time with modulo DEFAULT_PERIOD. */
514 
515  now = RTIMER_NOW();
516  wait = ((rtimer_clock_t)(e->time - now)) % (DEFAULT_PERIOD);
517  expected = now + wait - 2 * DEFAULT_ON_TIME;
518 
519 #if WITH_ACK_OPTIMIZATION
520  /* Wait until the receiver is expected to be awake */
521  if(packetbuf_attr(PACKETBUF_ATTR_PACKET_TYPE) !=
522  PACKETBUF_ATTR_PACKET_TYPE_ACK &&
523  is_streaming == 0) {
524  /* Do not wait if we are sending an ACK, because then the
525  receiver will already be awake. */
526  while(RTIMER_CLOCK_LT(RTIMER_NOW(), expected));
527  }
528 #else /* WITH_ACK_OPTIMIZATION */
529  /* Wait until the receiver is expected to be awake */
530  while(RTIMER_CLOCK_LT(RTIMER_NOW(), expected));
531 #endif /* WITH_ACK_OPTIMIZATION */
532  }
533  }
534 #endif /* WITH_ENCOUNTER_OPTIMIZATION */
535 
536  /* By setting we_are_sending to one, we ensure that the rtimer
537  powercycle interrupt do not interfere with us sending the packet. */
538  we_are_sending = 1;
539 
540  t0 = RTIMER_NOW();
541  strobes = 0;
542 
543  LEDS_ON(LEDS_BLUE);
544 
545  /* Send a train of strobes until the receiver answers with an ACK. */
546 
547  /* Turn on the radio to listen for the strobe ACK. */
548  on();
549  collisions = 0;
550  if(!is_already_streaming) {
551  watchdog_stop();
552  got_strobe_ack = 0;
553  t = RTIMER_NOW();
554  for(strobes = 0, collisions = 0;
555  got_strobe_ack == 0 && collisions == 0 &&
556  RTIMER_CLOCK_LT(RTIMER_NOW(), t0 + cxmac_config.strobe_time);
557  strobes++) {
558 
559  while(got_strobe_ack == 0 &&
560  RTIMER_CLOCK_LT(RTIMER_NOW(), t + cxmac_config.strobe_wait_time)) {
561  rtimer_clock_t now = RTIMER_NOW();
562 
563  /* See if we got an ACK */
564  packetbuf_clear();
565  len = NETSTACK_RADIO.read(packetbuf_dataptr(), PACKETBUF_SIZE);
566  if(len > 0) {
568  if(NETSTACK_FRAMER.parse()) {
569  hdr = packetbuf_dataptr();
570  if(hdr->dispatch == DISPATCH && hdr->type == TYPE_STROBE_ACK) {
571  if(rimeaddr_cmp(packetbuf_addr(PACKETBUF_ADDR_RECEIVER),
572  &rimeaddr_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 && (packetbuf_attr(PACKETBUF_ATTR_RELIABLE) ||
623  packetbuf_attr(PACKETBUF_ATTR_ERELIABLE) ||
624  packetbuf_attr(PACKETBUF_ATTR_PACKET_TYPE) ==
625  PACKETBUF_ATTR_PACKET_TYPE_STREAM)) {
626  on(); /* Wait for ACK packet */
627  waiting_for_packet = 1;
628  } else {
629  off();
630  }
631 #else /* WITH_ACK_OPTIMIZATION */
632  off();
633 #endif /* WITH_ACK_OPTIMIZATION */
634 
635  /* restore the packet to send */
636  queuebuf_to_packetbuf(packet);
637  queuebuf_free(packet);
638 
639  /* Send the data packet. */
640  if((is_broadcast || got_strobe_ack || is_streaming) && collisions == 0) {
641  NETSTACK_RADIO.send(packetbuf_hdrptr(), packetbuf_totlen());
642  }
643 
644 #if WITH_ENCOUNTER_OPTIMIZATION
645  if(got_strobe_ack && !is_streaming) {
646  register_encounter(packetbuf_addr(PACKETBUF_ADDR_RECEIVER), encounter_time);
647  }
648 #endif /* WITH_ENCOUNTER_OPTIMIZATION */
649  watchdog_start();
650 
651  PRINTF("cxmac: send (strobes=%u,len=%u,%s), done\n", strobes,
652  packetbuf_totlen(), got_strobe_ack ? "ack" : "no ack");
653 
654 #if CXMAC_CONF_COMPOWER
655  /* Accumulate the power consumption for the packet transmission. */
656  compower_accumulate(&current_packet);
657 
658  /* Convert the accumulated power consumption for the transmitted
659  packet to packet attributes so that the higher levels can keep
660  track of the amount of energy spent on transmitting the
661  packet. */
662  compower_attrconv(&current_packet);
663 
664  /* Clear the accumulated power consumption so that it is ready for
665  the next packet. */
666  compower_clear(&current_packet);
667 #endif /* CXMAC_CONF_COMPOWER */
668 
669  we_are_sending = 0;
670 
671  LEDS_OFF(LEDS_BLUE);
672  if(collisions == 0) {
673  if(!is_broadcast && !got_strobe_ack) {
674  return MAC_TX_NOACK;
675  } else {
676  return MAC_TX_OK;
677  }
678  } else {
679  someone_is_sending++;
680  return MAC_TX_COLLISION;
681  }
682 
683 }
684 /*---------------------------------------------------------------------------*/
685 static void
686 qsend_packet(mac_callback_t sent, void *ptr)
687 {
688  int ret;
689  if(someone_is_sending) {
690  PRINTF("cxmac: should queue packet, now just dropping %d %d %d %d.\n",
691  waiting_for_packet, someone_is_sending, we_are_sending, radio_is_on);
692  RIMESTATS_ADD(sendingdrop);
693  ret = MAC_TX_COLLISION;
694  } else {
695  PRINTF("cxmac: send immediately.\n");
696  ret = send_packet();
697  }
698 
699  mac_call_sent_callback(sent, ptr, ret, 1);
700 }
701 /*---------------------------------------------------------------------------*/
702 static void
703 input_packet(void)
704 {
705  struct cxmac_hdr *hdr;
706 
707  if(NETSTACK_FRAMER.parse()) {
708  hdr = packetbuf_dataptr();
709 
710  if(hdr->dispatch != DISPATCH) {
711  someone_is_sending = 0;
712  if(rimeaddr_cmp(packetbuf_addr(PACKETBUF_ADDR_RECEIVER),
713  &rimeaddr_node_addr) ||
714  rimeaddr_cmp(packetbuf_addr(PACKETBUF_ADDR_RECEIVER),
715  &rimeaddr_null)) {
716  /* This is a regular packet that is destined to us or to the
717  broadcast address. */
718 
719  /* We have received the final packet, so we can go back to being
720  asleep. */
721  off();
722 
723 #if CXMAC_CONF_COMPOWER
724  /* Accumulate the power consumption for the packet reception. */
725  compower_accumulate(&current_packet);
726  /* Convert the accumulated power consumption for the received
727  packet to packet attributes so that the higher levels can
728  keep track of the amount of energy spent on receiving the
729  packet. */
730  compower_attrconv(&current_packet);
731 
732  /* Clear the accumulated power consumption so that it is ready
733  for the next packet. */
734  compower_clear(&current_packet);
735 #endif /* CXMAC_CONF_COMPOWER */
736 
737  waiting_for_packet = 0;
738 
739  PRINTDEBUG("cxmac: data(%u)\n", packetbuf_datalen());
740  NETSTACK_MAC.input();
741  return;
742  } else {
743  PRINTDEBUG("cxmac: data not for us\n");
744  }
745 
746  } else if(hdr->type == TYPE_STROBE) {
747  someone_is_sending = 2;
748 
749  if(rimeaddr_cmp(packetbuf_addr(PACKETBUF_ADDR_RECEIVER),
750  &rimeaddr_node_addr)) {
751  /* This is a strobe packet for us. */
752 
753  /* If the sender address is someone else, we should
754  acknowledge the strobe and wait for the packet. By using
755  the same address as both sender and receiver, we flag the
756  message is a strobe ack. */
757  hdr->type = TYPE_STROBE_ACK;
758  packetbuf_set_addr(PACKETBUF_ADDR_RECEIVER,
759  packetbuf_addr(PACKETBUF_ADDR_SENDER));
760  packetbuf_set_addr(PACKETBUF_ADDR_SENDER, &rimeaddr_node_addr);
762  if(NETSTACK_FRAMER.create()) {
763  /* We turn on the radio in anticipation of the incoming
764  packet. */
765  someone_is_sending = 1;
766  waiting_for_packet = 1;
767  on();
768  NETSTACK_RADIO.send(packetbuf_hdrptr(), packetbuf_totlen());
769  PRINTDEBUG("cxmac: send strobe ack %u\n", packetbuf_totlen());
770  } else {
771  PRINTF("cxmac: failed to send strobe ack\n");
772  }
773  } else if(rimeaddr_cmp(packetbuf_addr(PACKETBUF_ADDR_RECEIVER),
774  &rimeaddr_null)) {
775  /* If the receiver address is null, the strobe is sent to
776  prepare for an incoming broadcast packet. If this is the
777  case, we turn on the radio and wait for the incoming
778  broadcast packet. */
779  waiting_for_packet = 1;
780  on();
781  } else {
782  PRINTDEBUG("cxmac: strobe not for us\n");
783  }
784 
785  /* We are done processing the strobe and we therefore return
786  to the caller. */
787  return;
788 #if CXMAC_CONF_ANNOUNCEMENTS
789  } else if(hdr->type == TYPE_ANNOUNCEMENT) {
790  packetbuf_hdrreduce(sizeof(struct cxmac_hdr));
791  parse_announcements(packetbuf_addr(PACKETBUF_ADDR_SENDER));
792 #endif /* CXMAC_CONF_ANNOUNCEMENTS */
793  } else if(hdr->type == TYPE_STROBE_ACK) {
794  PRINTDEBUG("cxmac: stray strobe ack\n");
795  } else {
796  PRINTF("cxmac: unknown type %u (%u)\n", hdr->type,
798  }
799  } else {
800  PRINTF("cxmac: failed to parse (%u)\n", packetbuf_totlen());
801  }
802 }
803 /*---------------------------------------------------------------------------*/
804 #if CXMAC_CONF_ANNOUNCEMENTS
805 static void
806 send_announcement(void *ptr)
807 {
808  struct cxmac_hdr *hdr;
809  int announcement_len;
810 
811  /* Set up the probe header. */
812  packetbuf_clear();
813  hdr = packetbuf_dataptr();
814 
815  announcement_len = format_announcement((char *)hdr +
816  sizeof(struct cxmac_hdr));
817 
818  if(announcement_len > 0) {
819  packetbuf_set_datalen(sizeof(struct cxmac_hdr) + announcement_len);
820  hdr->dispatch = DISPATCH;
821  hdr->type = TYPE_ANNOUNCEMENT;
822 
823  packetbuf_set_addr(PACKETBUF_ADDR_SENDER, &rimeaddr_node_addr);
824  packetbuf_set_addr(PACKETBUF_ADDR_RECEIVER, &rimeaddr_null);
825  packetbuf_set_attr(PACKETBUF_ATTR_RADIO_TXPOWER, announcement_radio_txpower);
826  if(NETSTACK_FRAMER.create()) {
827  NETSTACK_RADIO.send(packetbuf_hdrptr(), packetbuf_totlen());
828  }
829  }
830 }
831 /*---------------------------------------------------------------------------*/
832 static void
833 cycle_announcement(void *ptr)
834 {
835  ctimer_set(&announcement_ctimer, ANNOUNCEMENT_TIME,
836  send_announcement, NULL);
837  ctimer_set(&announcement_cycle_ctimer, ANNOUNCEMENT_PERIOD,
838  cycle_announcement, NULL);
839  if(is_listening > 0) {
840  is_listening--;
841  /* printf("is_listening %d\n", is_listening);*/
842  }
843 }
844 /*---------------------------------------------------------------------------*/
845 static void
846 listen_callback(int periods)
847 {
848  is_listening = periods + 1;
849 }
850 #endif /* CXMAC_CONF_ANNOUNCEMENTS */
851 /*---------------------------------------------------------------------------*/
852 void
853 cxmac_set_announcement_radio_txpower(int txpower)
854 {
855 #if CXMAC_CONF_ANNOUNCEMENTS
856  announcement_radio_txpower = txpower;
857 #endif /* CXMAC_CONF_ANNOUNCEMENTS */
858 }
859 /*---------------------------------------------------------------------------*/
860 void
861 cxmac_init(void)
862 {
863  radio_is_on = 0;
864  waiting_for_packet = 0;
865  PT_INIT(&pt);
866  /* rtimer_set(&rt, RTIMER_NOW() + cxmac_config.off_time, 1,
867  (void (*)(struct rtimer *, void *))powercycle, NULL);*/
868 
869  cxmac_is_on = 1;
870 
871 #if WITH_ENCOUNTER_OPTIMIZATION
872  list_init(encounter_list);
873  memb_init(&encounter_memb);
874 #endif /* WITH_ENCOUNTER_OPTIMIZATION */
875 
876 #if CXMAC_CONF_ANNOUNCEMENTS
877  announcement_register_listen_callback(listen_callback);
878  ctimer_set(&announcement_cycle_ctimer, ANNOUNCEMENT_TIME,
879  cycle_announcement, NULL);
880 #endif /* CXMAC_CONF_ANNOUNCEMENTS */
881 
882  CSCHEDULE_POWERCYCLE(DEFAULT_OFF_TIME);
883 }
884 /*---------------------------------------------------------------------------*/
885 static int
886 turn_on(void)
887 {
888  cxmac_is_on = 1;
889  /* rtimer_set(&rt, RTIMER_NOW() + cxmac_config.off_time, 1,
890  (void (*)(struct rtimer *, void *))powercycle, NULL);*/
891  CSCHEDULE_POWERCYCLE(DEFAULT_OFF_TIME);
892  return 1;
893 }
894 /*---------------------------------------------------------------------------*/
895 static int
896 turn_off(int keep_radio_on)
897 {
898  cxmac_is_on = 0;
899  if(keep_radio_on) {
900  return NETSTACK_RADIO.on();
901  } else {
902  return NETSTACK_RADIO.off();
903  }
904 }
905 /*---------------------------------------------------------------------------*/
906 static unsigned short
907 channel_check_interval(void)
908 {
909  return (1ul * CLOCK_SECOND * DEFAULT_PERIOD) / RTIMER_ARCH_SECOND;
910 }
911 /*---------------------------------------------------------------------------*/
912 const struct rdc_driver cxmac_driver =
913  {
914  "CX-MAC",
915  cxmac_init,
916  qsend_packet,
917  input_packet,
918  turn_on,
919  turn_off,
921  };