Contiki 3.x
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  */
32 
33 /**
34  * \file
35  * Common functionality for phase optimization in duty cycling radio protocols
36  * \author
37  * Adam Dunkels <adam@sics.se>
38  */
39 
40 #include "net/mac/phase.h"
41 #include "net/packetbuf.h"
42 #include "sys/clock.h"
43 #include "sys/ctimer.h"
44 #include "net/queuebuf.h"
45 #include "net/nbr-table.h"
46 
47 #if PHASE_CONF_DRIFT_CORRECT
48 #define PHASE_DRIFT_CORRECT PHASE_CONF_DRIFT_CORRECT
49 #else
50 #define PHASE_DRIFT_CORRECT 0
51 #endif
52 
53 struct phase {
54  rtimer_clock_t time;
55 #if PHASE_DRIFT_CORRECT
56  rtimer_clock_t drift;
57 #endif
58  uint8_t noacks;
59  struct timer noacks_timer;
60 };
61 
62 struct phase_queueitem {
63  struct ctimer timer;
64  mac_callback_t mac_callback;
65  void *mac_callback_ptr;
66  struct queuebuf *q;
67  struct rdc_buf_list *buf_list;
68 };
69 
70 #define PHASE_DEFER_THRESHOLD 1
71 #define PHASE_QUEUESIZE 8
72 
73 #define MAX_NOACKS 16
74 
75 #define MAX_NOACKS_TIME CLOCK_SECOND * 30
76 
77 MEMB(queued_packets_memb, struct phase_queueitem, PHASE_QUEUESIZE);
78 NBR_TABLE(struct phase, nbr_phase);
79 
80 #define DEBUG 0
81 #if DEBUG
82 #include <stdio.h>
83 #define PRINTF(...) printf(__VA_ARGS__)
84 #define PRINTDEBUG(...) printf(__VA_ARGS__)
85 #else
86 #define PRINTF(...)
87 #define PRINTDEBUG(...)
88 #endif
89 /*---------------------------------------------------------------------------*/
90 void
91 phase_update(const linkaddr_t *neighbor, rtimer_clock_t time,
92  int mac_status)
93 {
94  struct phase *e;
95 
96  /* If we have an entry for this neighbor already, we renew it. */
97  e = nbr_table_get_from_lladdr(nbr_phase, neighbor);
98  if(e != NULL) {
99  if(mac_status == MAC_TX_OK) {
100 #if PHASE_DRIFT_CORRECT
101  e->drift = time-e->time;
102 #endif
103  e->time = time;
104  }
105  /* If the neighbor didn't reply to us, it may have switched
106  phase (rebooted). We try a number of transmissions to it
107  before we drop it from the phase list. */
108  if(mac_status == MAC_TX_NOACK) {
109  PRINTF("phase noacks %d to %d.%d\n", e->noacks, neighbor->u8[0], neighbor->u8[1]);
110  e->noacks++;
111  if(e->noacks == 1) {
112  timer_set(&e->noacks_timer, MAX_NOACKS_TIME);
113  }
114  if(e->noacks >= MAX_NOACKS || timer_expired(&e->noacks_timer)) {
115  PRINTF("drop %d\n", neighbor->u8[0]);
116  nbr_table_remove(nbr_phase, e);
117  return;
118  }
119  } else if(mac_status == MAC_TX_OK) {
120  e->noacks = 0;
121  }
122  } else {
123  /* No matching phase was found, so we allocate a new one. */
124  if(mac_status == MAC_TX_OK && e == NULL) {
125  e = nbr_table_add_lladdr(nbr_phase, neighbor);
126  if(e) {
127  e->time = time;
128 #if PHASE_DRIFT_CORRECT
129  e->drift = 0;
130 #endif
131  e->noacks = 0;
132  }
133  }
134  }
135 }
136 /*---------------------------------------------------------------------------*/
137 static void
138 send_packet(void *ptr)
139 {
140  struct phase_queueitem *p = ptr;
141 
142  if(p->buf_list == NULL) {
143  queuebuf_to_packetbuf(p->q);
144  queuebuf_free(p->q);
145  NETSTACK_RDC.send(p->mac_callback, p->mac_callback_ptr);
146  } else {
147  NETSTACK_RDC.send_list(p->mac_callback, p->mac_callback_ptr, p->buf_list);
148  }
149 
150  memb_free(&queued_packets_memb, p);
151 }
152 /*---------------------------------------------------------------------------*/
153 phase_status_t
154 phase_wait(const linkaddr_t *neighbor, rtimer_clock_t cycle_time,
155  rtimer_clock_t guard_time,
156  mac_callback_t mac_callback, void *mac_callback_ptr,
157  struct rdc_buf_list *buf_list)
158 {
159  struct phase *e;
160  // const linkaddr_t *neighbor = packetbuf_addr(PACKETBUF_ADDR_RECEIVER);
161  /* We go through the list of phases to find if we have recorded a
162  phase for this particular neighbor. If so, we can compute the
163  time for the next expected phase and setup a ctimer to switch on
164  the radio just before the phase. */
165  e = nbr_table_get_from_lladdr(nbr_phase, neighbor);
166  if(e != NULL) {
167  rtimer_clock_t wait, now, expected, sync;
168  clock_time_t ctimewait;
169 
170  /* We expect phases to happen every CYCLE_TIME time
171  units. The next expected phase is at time e->time +
172  CYCLE_TIME. To compute a relative offset, we subtract
173  with clock_time(). Because we are only interested in turning
174  on the radio within the CYCLE_TIME period, we compute the
175  waiting time with modulo CYCLE_TIME. */
176 
177  /* printf("neighbor phase 0x%02x (cycle 0x%02x)\n", e->time & (cycle_time - 1),
178  cycle_time);*/
179 
180  /* if(e->noacks > 0) {
181  printf("additional wait %d\n", additional_wait);
182  }*/
183 
184  now = RTIMER_NOW();
185 
186  sync = (e == NULL) ? now : e->time;
187 
188 #if PHASE_DRIFT_CORRECT
189  {
190  int32_t s;
191  if(e->drift > cycle_time) {
192  s = e->drift % cycle_time / (e->drift / cycle_time); /* drift per cycle */
193  s = s * (now - sync) / cycle_time; /* estimated drift to now */
194  sync += s; /* add it in */
195  }
196  }
197 #endif
198 
199  /* Check if cycle_time is a power of two */
200  if(!(cycle_time & (cycle_time - 1))) {
201  /* Faster if cycle_time is a power of two */
202  wait = (rtimer_clock_t)((sync - now) & (cycle_time - 1));
203  } else {
204  /* Works generally */
205  wait = cycle_time - (rtimer_clock_t)((now - sync) % cycle_time);
206  }
207 
208  if(wait < guard_time) {
209  wait += cycle_time;
210  }
211 
212  ctimewait = (CLOCK_SECOND * (wait - guard_time)) / RTIMER_ARCH_SECOND;
213 
214  if(ctimewait > PHASE_DEFER_THRESHOLD) {
215  struct phase_queueitem *p;
216 
217  p = memb_alloc(&queued_packets_memb);
218  if(p != NULL) {
219  if(buf_list == NULL) {
220  packetbuf_set_attr(PACKETBUF_ATTR_IS_CREATED_AND_SECURED, 1);
221  p->q = queuebuf_new_from_packetbuf();
222  }
223  p->mac_callback = mac_callback;
224  p->mac_callback_ptr = mac_callback_ptr;
225  p->buf_list = buf_list;
226  ctimer_set(&p->timer, ctimewait, send_packet, p);
227  return PHASE_DEFERRED;
228  }
229  }
230 
231  expected = now + wait - guard_time;
232  if(!RTIMER_CLOCK_LT(expected, now)) {
233  /* Wait until the receiver is expected to be awake */
234  while(RTIMER_CLOCK_LT(RTIMER_NOW(), expected));
235  }
236  return PHASE_SEND_NOW;
237  }
238  return PHASE_UNKNOWN;
239 }
240 /*---------------------------------------------------------------------------*/
241 void
242 phase_init(void)
243 {
244  memb_init(&queued_packets_memb);
245  nbr_table_register(nbr_phase, NULL);
246 }
247 /*---------------------------------------------------------------------------*/
Common functionality for phase optimization in duty cycling radio protocols
A timer.
Definition: timer.h:86
void memb_init(struct memb *m)
Initialize a memory block that was declared with MEMB().
Definition: memb.c:52
The MAC layer deferred the transmission for a later time.
Definition: mac.h:86
void timer_set(struct timer *t, clock_time_t interval)
Set a timer.
Definition: timer.c:64
char memb_free(struct memb *m, void *ptr)
Deallocate a memory block from a memory block previously declared with MEMB().
Definition: memb.c:79
Header file for the Rime buffer (packetbuf) management
void * memb_alloc(struct memb *m)
Allocate a memory block from a block of memory declared with MEMB().
Definition: memb.c:59
#define NULL
The null pointer.
void ctimer_set(struct ctimer *c, clock_time_t t, void(*f)(void *), void *ptr)
Set a callback timer.
Definition: ctimer.c:99
#define MEMB(name, structure, num)
Declare a memory block.
Definition: memb.h:89
Header file for the Rime queue buffer management
Header file for the callback timer
The MAC layer transmission was OK.
Definition: mac.h:79
#define RTIMER_NOW()
Get the current clock time.
Definition: rtimer.h:133
int timer_expired(struct timer *t)
Check if a timer has expired.
Definition: timer.c:121
#define CLOCK_SECOND
A second, measured in system clock time.
Definition: clock.h:82