Fix compiler warnings
[contiki-2.x.git] / core / net / mac / cxmac.c
bloba346268d57f382c3e85b1c3e52382c0a9df27f6c
1 /*
2 * Copyright (c) 2007, Swedish Institute of Computer Science.
3 * All rights reserved.
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.
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.
29 * This file is part of the Contiki operating system.
31 * $Id: cxmac.c,v 1.14 2010/08/01 21:18:07 dak664 Exp $
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>
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"
55 #include "contiki-conf.h"
57 #ifdef EXPERIMENT_SETUP
58 #include "experiment-setup.h"
59 #endif
61 #include <string.h>
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
76 struct announcement_data {
77 uint16_t id;
78 uint16_t value;
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
85 /* The structure of the announcement messages. */
86 struct announcement_msg {
87 uint16_t num;
88 struct announcement_data data[ANNOUNCEMENT_MAX];
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))
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
101 struct cxmac_hdr {
102 uint8_t dispatch;
103 uint8_t type;
106 #define MAX_STROBE_SIZE 50
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
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 / MAC_CHANNEL_CHECK_RATE - DEFAULT_ON_TIME)
118 #endif
120 #define DEFAULT_PERIOD (DEFAULT_OFF_TIME + DEFAULT_ON_TIME)
122 #define WAIT_TIME_BEFORE_STROBE_ACK RTIMER_ARCH_SECOND / 1000
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
133 /* The cycle time for announcements. */
134 #define ANNOUNCEMENT_PERIOD 4 * CLOCK_SECOND
136 /* The time before sending an announcement within one announcement
137 cycle. */
138 #define ANNOUNCEMENT_TIME (random_rand() % (ANNOUNCEMENT_PERIOD))
140 #define DEFAULT_STROBE_WAIT_TIME (7 * DEFAULT_ON_TIME / 8)
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
149 #include <stdio.h>
151 static struct pt pt;
153 static volatile uint8_t cxmac_is_on = 0;
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;
160 #undef LEDS_ON
161 #undef LEDS_OFF
162 #undef LEDS_TOGGLE
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
183 #if CXMAC_CONF_ANNOUNCEMENTS
184 /* Timers for keeping track of when to send announcements. */
185 static struct ctimer announcement_cycle_ctimer, announcement_ctimer;
187 static int announcement_radio_txpower;
188 #endif /* CXMAC_CONF_ANNOUNCEMENTS */
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;
194 #if CXMAC_CONF_COMPOWER
195 static struct compower_activity current_packet;
196 #endif /* CXMAC_CONF_COMPOWER */
198 #if WITH_ENCOUNTER_OPTIMIZATION
200 #include "lib/list.h"
201 #include "lib/memb.h"
203 struct encounter {
204 struct encounter *next;
205 rimeaddr_t neighbor;
206 rtimer_clock_t time;
209 #define MAX_ENCOUNTERS 4
210 LIST(encounter_list);
211 MEMB(encounter_memb, struct encounter, MAX_ENCOUNTERS);
212 #endif /* WITH_ENCOUNTER_OPTIMIZATION */
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)
219 #ifndef MIN
220 #define MIN(a, b) ((a) < (b)? (a) : (b))
221 #endif /* MIN */
223 /*---------------------------------------------------------------------------*/
224 static void
225 on(void)
227 if(cxmac_is_on && radio_is_on == 0) {
228 radio_is_on = 1;
229 NETSTACK_RADIO.on();
230 LEDS_ON(LEDS_RED);
233 /*---------------------------------------------------------------------------*/
234 static void
235 off(void)
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);
244 /*---------------------------------------------------------------------------*/
245 static void
246 powercycle_turn_radio_off(void)
248 if(we_are_sending == 0 &&
249 waiting_for_packet == 0) {
250 off();
252 #if CXMAC_CONF_COMPOWER
253 compower_accumulate(&compower_idle_activity);
254 #endif /* CXMAC_CONF_COMPOWER */
256 static void
257 powercycle_turn_radio_on(void)
259 if(we_are_sending == 0 &&
260 waiting_for_packet == 0) {
261 on();
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)
272 if(cxmac_is_on) {
273 if(time == 0) {
274 time = 1;
276 ctimer_set(&cpowercycle_ctimer, time,
277 (void (*)(void *))cpowercycle, NULL);
280 /*---------------------------------------------------------------------------*/
281 static char
282 cpowercycle(void *ptr)
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);
292 PT_BEGIN(&pt);
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--;
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);
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();
317 CSCHEDULE_POWERCYCLE(DEFAULT_OFF_TIME);
318 PT_YIELD(&pt);
322 PT_END(&pt);
324 /*---------------------------------------------------------------------------*/
325 #if CXMAC_CONF_ANNOUNCEMENTS
326 static int
327 parse_announcements(const rimeaddr_t *from)
329 /* Parse incoming announcements */
330 struct announcement_msg adata;
331 int i;
333 memcpy(&adata, packetbuf_dataptr(), MIN(packetbuf_datalen(), sizeof(adata)));
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]);
341 printf("\n");*/
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);*/
349 announcement_heard(from,
350 adata.data[i].id,
351 adata.data[i].value);
353 return i;
355 /*---------------------------------------------------------------------------*/
356 static int
357 format_announcement(char *hdr)
359 struct announcement_msg adata;
360 struct announcement *a;
362 /* Construct the announcements */
363 /* adata = (struct announcement_msg *)hdr;*/
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++;
374 memcpy(hdr, &adata, sizeof(struct announcement_msg));
376 if(adata.num > 0) {
377 return ANNOUNCEMENT_MSG_HEADERLEN +
378 sizeof(struct announcement_data) * adata.num;
379 } else {
380 return 0;
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)
389 struct encounter *e;
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;
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;
405 rimeaddr_copy(&e->neighbor, neighbor);
406 e->time = time;
407 list_add(encounter_list, e);
410 #endif /* WITH_ENCOUNTER_OPTIMIZATION */
411 /*---------------------------------------------------------------------------*/
412 static int
413 send_packet(void)
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;
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 */
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 > sizeof(strobe)) {
458 /* Failed to send */
459 PRINTF("cxmac: send failed, too large header\n");
460 return MAC_TX_ERR_FATAL;
462 memcpy(strobe, packetbuf_hdrptr(), len);
463 strobe[len] = DISPATCH; /* dispatch */
464 strobe[len + 1] = TYPE_STROBE; /* type */
466 packetbuf_compact();
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;
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;
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));
491 stream_until = RTIMER_NOW() + DEFAULT_STREAM_TIME;
493 #endif /* WITH_STREAMING */
495 off();
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);
505 if(rimeaddr_cmp(neighbor, &e->neighbor)) {
506 rtimer_clock_t wait, now, expected;
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. */
515 now = RTIMER_NOW();
516 wait = ((rtimer_clock_t)(e->time - now)) % (DEFAULT_PERIOD);
517 expected = now + wait - 2 * DEFAULT_ON_TIME;
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));
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 */
534 #endif /* WITH_ENCOUNTER_OPTIMIZATION */
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;
540 t0 = RTIMER_NOW();
541 strobes = 0;
543 LEDS_ON(LEDS_BLUE);
545 /* Send a train of strobes until the receiver answers with an ACK. */
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++) {
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();
563 /* See if we got an ACK */
564 packetbuf_clear();
565 len = NETSTACK_RADIO.read(packetbuf_dataptr(), PACKETBUF_SIZE);
566 if(len > 0) {
567 packetbuf_set_datalen(len);
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");
580 } else /*if(hdr->dispatch == DISPATCH && hdr->type == TYPE_STROBE)*/ {
581 PRINTDEBUG("cxmac: strobe from someone else\n");
582 collisions++;
584 } else {
585 PRINTF("cxmac: send failed to parse %u\n", len);
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();
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();
631 #else /* WITH_ACK_OPTIMIZATION */
632 off();
633 #endif /* WITH_ACK_OPTIMIZATION */
635 /* restore the packet to send */
636 queuebuf_to_packetbuf(packet);
637 queuebuf_free(packet);
639 /* Send the data packet. */
640 if((is_broadcast || got_strobe_ack || is_streaming) && collisions == 0) {
641 NETSTACK_RADIO.send(packetbuf_hdrptr(), packetbuf_totlen());
644 #if WITH_ENCOUNTER_OPTIMIZATION
645 if(got_strobe_ack && !is_streaming) {
646 register_encounter(packetbuf_addr(PACKETBUF_ADDR_RECEIVER), encounter_time);
648 #endif /* WITH_ENCOUNTER_OPTIMIZATION */
649 watchdog_start();
651 PRINTF("cxmac: send (strobes=%u,len=%u,%s), done\n", strobes,
652 packetbuf_totlen(), got_strobe_ack ? "ack" : "no ack");
654 #if CXMAC_CONF_COMPOWER
655 /* Accumulate the power consumption for the packet transmission. */
656 compower_accumulate(&current_packet);
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);
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 */
669 we_are_sending = 0;
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;
678 } else {
679 someone_is_sending++;
680 return MAC_TX_COLLISION;
684 /*---------------------------------------------------------------------------*/
685 static void
686 qsend_packet(mac_callback_t sent, void *ptr)
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();
699 mac_call_sent_callback(sent, ptr, ret, 1);
701 /*---------------------------------------------------------------------------*/
702 static void
703 input_packet(void)
705 struct cxmac_hdr *hdr;
707 if(NETSTACK_FRAMER.parse()) {
708 hdr = packetbuf_dataptr();
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. */
719 /* We have received the final packet, so we can go back to being
720 asleep. */
721 off();
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);
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 */
737 waiting_for_packet = 0;
739 PRINTDEBUG("cxmac: data(%u)\n", packetbuf_datalen());
740 NETSTACK_MAC.input();
741 return;
742 } else {
743 PRINTDEBUG("cxmac: data not for us\n");
746 } else if(hdr->type == TYPE_STROBE) {
747 someone_is_sending = 2;
749 if(rimeaddr_cmp(packetbuf_addr(PACKETBUF_ADDR_RECEIVER),
750 &rimeaddr_node_addr)) {
751 /* This is a strobe packet for us. */
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);
761 packetbuf_compact();
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");
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");
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,
797 packetbuf_datalen());
799 } else {
800 PRINTF("cxmac: failed to parse (%u)\n", packetbuf_totlen());
803 /*---------------------------------------------------------------------------*/
804 #if CXMAC_CONF_ANNOUNCEMENTS
805 static void
806 send_announcement(void *ptr)
808 struct cxmac_hdr *hdr;
809 int announcement_len;
811 /* Set up the probe header. */
812 packetbuf_clear();
813 hdr = packetbuf_dataptr();
815 announcement_len = format_announcement((char *)hdr +
816 sizeof(struct cxmac_hdr));
818 if(announcement_len > 0) {
819 packetbuf_set_datalen(sizeof(struct cxmac_hdr) + announcement_len);
820 hdr->dispatch = DISPATCH;
821 hdr->type = TYPE_ANNOUNCEMENT;
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());
831 /*---------------------------------------------------------------------------*/
832 static void
833 cycle_announcement(void *ptr)
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);*/
844 /*---------------------------------------------------------------------------*/
845 static void
846 listen_callback(int periods)
848 is_listening = periods + 1;
850 #endif /* CXMAC_CONF_ANNOUNCEMENTS */
851 /*---------------------------------------------------------------------------*/
852 void
853 cxmac_set_announcement_radio_txpower(int txpower)
855 #if CXMAC_CONF_ANNOUNCEMENTS
856 announcement_radio_txpower = txpower;
857 #endif /* CXMAC_CONF_ANNOUNCEMENTS */
859 /*---------------------------------------------------------------------------*/
860 void
861 cxmac_init(void)
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);*/
869 cxmac_is_on = 1;
871 #if WITH_ENCOUNTER_OPTIMIZATION
872 list_init(encounter_list);
873 memb_init(&encounter_memb);
874 #endif /* WITH_ENCOUNTER_OPTIMIZATION */
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 */
882 CSCHEDULE_POWERCYCLE(DEFAULT_OFF_TIME);
884 /*---------------------------------------------------------------------------*/
885 static int
886 turn_on(void)
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;
894 /*---------------------------------------------------------------------------*/
895 static int
896 turn_off(int keep_radio_on)
898 cxmac_is_on = 0;
899 if(keep_radio_on) {
900 return NETSTACK_RADIO.on();
901 } else {
902 return NETSTACK_RADIO.off();
905 /*---------------------------------------------------------------------------*/
906 static unsigned short
907 channel_check_interval(void)
909 return (1ul * CLOCK_SECOND * DEFAULT_PERIOD) / RTIMER_ARCH_SECOND;
911 /*---------------------------------------------------------------------------*/
912 const struct rdc_driver cxmac_driver =
914 "CX-MAC",
915 cxmac_init,
916 qsend_packet,
917 input_packet,
918 turn_on,
919 turn_off,
920 channel_check_interval,