2 * Copyright (c) 2010, Swedish Institute of Computer Science.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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
29 * This file is part of the Contiki operating system.
31 * $Id: phase.c,v 1.9 2010/04/04 21:02:09 adamdunkels Exp $
36 * Common functionality for phase optimization in duty cycling radio protocols
38 * Adam Dunkels <adam@sics.se>
41 #include "net/mac/phase.h"
42 #include "net/rime/packetbuf.h"
43 #include "sys/clock.h"
45 #include "net/rime/ctimer.h"
46 #include "net/rime/queuebuf.h"
47 #include "dev/watchdog.h"
50 struct phase_queueitem
{
52 mac_callback_t mac_callback
;
53 void *mac_callback_ptr
;
57 #define PHASE_DEFER_THRESHOLD 1
58 #define PHASE_QUEUESIZE 8
62 MEMB(queued_packets_memb
, struct phase_queueitem
, PHASE_QUEUESIZE
);
67 #define PRINTF(...) printf(__VA_ARGS__)
68 #define PRINTDEBUG(...) printf(__VA_ARGS__)
71 #define PRINTDEBUG(...)
73 /*---------------------------------------------------------------------------*/
75 find_neighbor(const struct phase_list
*list
, const rimeaddr_t
*addr
)
78 for(e
= list_head(*list
->list
); e
!= NULL
; e
= e
->next
) {
79 if(rimeaddr_cmp(addr
, &e
->neighbor
)) {
85 /*---------------------------------------------------------------------------*/
87 phase_remove(const struct phase_list
*list
, const rimeaddr_t
*neighbor
)
90 e
= find_neighbor(list
, neighbor
);
92 list_remove(*list
->list
, e
);
93 memb_free(list
->memb
, e
);
96 /*---------------------------------------------------------------------------*/
98 phase_update(const struct phase_list
*list
,
99 const rimeaddr_t
*neighbor
, rtimer_clock_t time
,
104 /* If we have an entry for this neighbor already, we renew it. */
105 e
= find_neighbor(list
, neighbor
);
107 if(mac_status
== MAC_TX_OK
) {
111 /* If the neighbor didn't reply to us, it may have switched
112 phase (rebooted). We try a number of transmissions to it
113 before we drop it from the phase list. */
114 if(mac_status
== MAC_TX_NOACK
) {
115 PRINTF("phase noacks %d to %d.%d\n", e
->noacks
, neighbor
->u8
[0], neighbor
->u8
[1]);
117 if(e
->noacks
>= MAX_NOACKS
) {
118 list_remove(*list
->list
, e
);
119 memb_free(list
->memb
, e
);
122 } else if(mac_status
== MAC_TX_OK
) {
126 /* No matching phase was found, so we allocate a new one. */
127 if(mac_status
== MAC_TX_OK
&& e
== NULL
) {
128 e
= memb_alloc(list
->memb
);
130 printf("phase alloc NULL\n");
131 /* We could not allocate memory for this phase, so we drop
132 the last item on the list and reuse it for our phase. */
133 e
= list_chop(*list
->list
);
135 rimeaddr_copy(&e
->neighbor
, neighbor
);
138 list_push(*list
->list
, e
);
142 /*---------------------------------------------------------------------------*/
144 send_packet(void *ptr
)
146 struct phase_queueitem
*p
= ptr
;
148 queuebuf_to_packetbuf(p
->q
);
150 memb_free(&queued_packets_memb
, p
);
151 NETSTACK_RDC
.send(p
->mac_callback
, p
->mac_callback_ptr
);
153 /*---------------------------------------------------------------------------*/
155 phase_wait(struct phase_list
*list
,
156 const rimeaddr_t
*neighbor
, rtimer_clock_t cycle_time
,
157 rtimer_clock_t wait_before
,
158 mac_callback_t mac_callback
, void *mac_callback_ptr
)
161 // const rimeaddr_t *neighbor = packetbuf_addr(PACKETBUF_ADDR_RECEIVER);
162 /* We go through the list of phases to find if we have recorded a
163 phase for this particular neighbor. If so, we can compute the
164 time for the next expected phase and setup a ctimer to switch on
165 the radio just before the phase. */
166 e
= find_neighbor(list
, neighbor
);
168 rtimer_clock_t wait
, now
, expected
, additional_wait
;
169 clock_time_t ctimewait
;
171 /* We expect phases to happen every CYCLE_TIME time
172 units. The next expected phase is at time e->time +
173 CYCLE_TIME. To compute a relative offset, we subtract
174 with clock_time(). Because we are only interested in turning
175 on the radio within the CYCLE_TIME period, we compute the
176 waiting time with modulo CYCLE_TIME. */
178 /* printf("neighbor phase 0x%02x (cycle 0x%02x)\n", e->time & (cycle_time - 1),
181 additional_wait
= 2 * e
->noacks
* wait_before
;
183 /* if(e->noacks > 0) {
184 printf("additional wait %d\n", additional_wait);
188 wait
= (rtimer_clock_t
)((e
->time
- now
) &
190 if(wait
< wait_before
+ additional_wait
) {
194 ctimewait
= (CLOCK_SECOND
* (wait
- wait_before
- additional_wait
)) / RTIMER_ARCH_SECOND
;
196 if(ctimewait
> PHASE_DEFER_THRESHOLD
) {
197 struct phase_queueitem
*p
;
199 p
= memb_alloc(&queued_packets_memb
);
201 p
->q
= queuebuf_new_from_packetbuf();
203 p
->mac_callback
= mac_callback
;
204 p
->mac_callback_ptr
= mac_callback_ptr
;
205 ctimer_set(&p
->timer
, ctimewait
, send_packet
, p
);
206 return PHASE_DEFERRED
;
208 memb_free(&queued_packets_memb
, p
);
213 expected
= now
+ wait
- wait_before
- additional_wait
;
214 if(!RTIMER_CLOCK_LT(expected
, now
)) {
215 /* Wait until the receiver is expected to be awake */
216 while(RTIMER_CLOCK_LT(RTIMER_NOW(), expected
)) {
219 return PHASE_SEND_NOW
;
221 return PHASE_UNKNOWN
;
223 /*---------------------------------------------------------------------------*/
225 phase_init(struct phase_list
*list
)
227 list_init(*list
->list
);
228 memb_init(list
->memb
);
229 memb_init(&queued_packets_memb
);
231 /*---------------------------------------------------------------------------*/