Contiki 2.5
phase.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2010, 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: phase.c,v 1.17 2010/12/18 22:12:53 dak664 Exp $
32  */
33 
34 /**
35  * \file
36  * Common functionality for phase optimization in duty cycling radio protocols
37  * \author
38  * Adam Dunkels <adam@sics.se>
39  */
40 
41 #include "net/mac/phase.h"
42 #include "net/packetbuf.h"
43 #include "sys/clock.h"
44 #include "lib/memb.h"
45 #include "sys/ctimer.h"
46 #include "net/queuebuf.h"
47 #include "dev/watchdog.h"
48 #include "dev/leds.h"
49 
50 struct phase_queueitem {
51  struct ctimer timer;
52  mac_callback_t mac_callback;
53  void *mac_callback_ptr;
54  struct queuebuf *q;
55 };
56 
57 #define PHASE_DEFER_THRESHOLD 1
58 #define PHASE_QUEUESIZE 8
59 
60 #define MAX_NOACKS 16
61 
62 #define MAX_NOACKS_TIME CLOCK_SECOND * 30
63 
64 MEMB(queued_packets_memb, struct phase_queueitem, PHASE_QUEUESIZE);
65 
66 #define DEBUG 0
67 #if DEBUG
68 #include <stdio.h>
69 #define PRINTF(...) printf(__VA_ARGS__)
70 #define PRINTDEBUG(...) printf(__VA_ARGS__)
71 #else
72 #define PRINTF(...)
73 #define PRINTDEBUG(...)
74 #endif
75 /*---------------------------------------------------------------------------*/
76 struct phase *
77 find_neighbor(const struct phase_list *list, const rimeaddr_t *addr)
78 {
79  struct phase *e;
80  for(e = list_head(*list->list); e != NULL; e = list_item_next(e)) {
81  if(rimeaddr_cmp(addr, &e->neighbor)) {
82  return e;
83  }
84  }
85  return NULL;
86 }
87 /*---------------------------------------------------------------------------*/
88 void
89 phase_remove(const struct phase_list *list, const rimeaddr_t *neighbor)
90 {
91  struct phase *e;
92  e = find_neighbor(list, neighbor);
93  if(e != NULL) {
94  list_remove(*list->list, e);
95  memb_free(list->memb, e);
96  }
97 }
98 /*---------------------------------------------------------------------------*/
99 void
100 phase_update(const struct phase_list *list,
101  const rimeaddr_t *neighbor, rtimer_clock_t time,
102  int mac_status)
103 {
104  struct phase *e;
105 
106  /* If we have an entry for this neighbor already, we renew it. */
107  e = find_neighbor(list, neighbor);
108  if(e != NULL) {
109  if(mac_status == MAC_TX_OK) {
110  e->time = time;
111  }
112  /* If the neighbor didn't reply to us, it may have switched
113  phase (rebooted). We try a number of transmissions to it
114  before we drop it from the phase list. */
115  if(mac_status == MAC_TX_NOACK) {
116  PRINTF("phase noacks %d to %d.%d\n", e->noacks, neighbor->u8[0], neighbor->u8[1]);
117  e->noacks++;
118  if(e->noacks == 1) {
119  timer_set(&e->noacks_timer, MAX_NOACKS_TIME);
120  }
121  if(e->noacks >= MAX_NOACKS || timer_expired(&e->noacks_timer)) {
122  PRINTF("drop %d\n", neighbor->u8[0]);
123  list_remove(*list->list, e);
124  memb_free(list->memb, e);
125  return;
126  }
127  } else if(mac_status == MAC_TX_OK) {
128  e->noacks = 0;
129  }
130  } else {
131  /* No matching phase was found, so we allocate a new one. */
132  if(mac_status == MAC_TX_OK && e == NULL) {
133  e = memb_alloc(list->memb);
134  if(e == NULL) {
135  PRINTF("phase alloc NULL\n");
136  /* We could not allocate memory for this phase, so we drop
137  the last item on the list and reuse it for our phase. */
138  e = list_chop(*list->list);
139  }
140  rimeaddr_copy(&e->neighbor, neighbor);
141  e->time = time;
142  e->noacks = 0;
143  list_push(*list->list, e);
144  }
145  }
146 }
147 /*---------------------------------------------------------------------------*/
148 static void
149 send_packet(void *ptr)
150 {
151  struct phase_queueitem *p = ptr;
152 
153  queuebuf_to_packetbuf(p->q);
154  queuebuf_free(p->q);
155  memb_free(&queued_packets_memb, p);
156  NETSTACK_RDC.send(p->mac_callback, p->mac_callback_ptr);
157 }
158 /*---------------------------------------------------------------------------*/
159 phase_status_t
160 phase_wait(struct phase_list *list,
161  const rimeaddr_t *neighbor, rtimer_clock_t cycle_time,
162  rtimer_clock_t guard_time,
163  mac_callback_t mac_callback, void *mac_callback_ptr)
164 {
165  struct phase *e;
166  // const rimeaddr_t *neighbor = packetbuf_addr(PACKETBUF_ADDR_RECEIVER);
167  /* We go through the list of phases to find if we have recorded a
168  phase for this particular neighbor. If so, we can compute the
169  time for the next expected phase and setup a ctimer to switch on
170  the radio just before the phase. */
171  e = find_neighbor(list, neighbor);
172  if(e != NULL) {
173  rtimer_clock_t wait, now, expected;
174  clock_time_t ctimewait;
175 
176  /* We expect phases to happen every CYCLE_TIME time
177  units. The next expected phase is at time e->time +
178  CYCLE_TIME. To compute a relative offset, we subtract
179  with clock_time(). Because we are only interested in turning
180  on the radio within the CYCLE_TIME period, we compute the
181  waiting time with modulo CYCLE_TIME. */
182 
183  /* printf("neighbor phase 0x%02x (cycle 0x%02x)\n", e->time & (cycle_time - 1),
184  cycle_time);*/
185 
186  /* if(e->noacks > 0) {
187  printf("additional wait %d\n", additional_wait);
188  }*/
189 
190  now = RTIMER_NOW();
191  wait = (rtimer_clock_t)((e->time - now) &
192  (cycle_time - 1));
193  if(wait < guard_time) {
194  wait += cycle_time;
195  }
196 
197  ctimewait = (CLOCK_SECOND * (wait - guard_time)) / RTIMER_ARCH_SECOND;
198 
199  if(ctimewait > PHASE_DEFER_THRESHOLD) {
200  struct phase_queueitem *p;
201 
202  p = memb_alloc(&queued_packets_memb);
203  if(p != NULL) {
204  p->q = queuebuf_new_from_packetbuf();
205  if(p->q != NULL) {
206  p->mac_callback = mac_callback;
207  p->mac_callback_ptr = mac_callback_ptr;
208  ctimer_set(&p->timer, ctimewait, send_packet, p);
209  return PHASE_DEFERRED;
210  } else {
211  memb_free(&queued_packets_memb, p);
212  }
213  }
214  }
215 
216  expected = now + wait - guard_time;
217  if(!RTIMER_CLOCK_LT(expected, now)) {
218  /* Wait until the receiver is expected to be awake */
219  while(RTIMER_CLOCK_LT(RTIMER_NOW(), expected));
220  }
221  return PHASE_SEND_NOW;
222  }
223  return PHASE_UNKNOWN;
224 }
225 /*---------------------------------------------------------------------------*/
226 void
227 phase_init(struct phase_list *list)
228 {
229  list_init(*list->list);
230  memb_init(list->memb);
231  memb_init(&queued_packets_memb);
232 }
233 /*---------------------------------------------------------------------------*/