Contiki 3.x
tsch-schedule.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2014, SICS Swedish ICT.
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  * IEEE 802.15.4 TSCH MAC schedule manager.
36  * \author
37  * Simon Duquennoy <simonduq@sics.se>
38  * Beshr Al Nahas <beshr@sics.se>
39  */
40 
41 #include "contiki.h"
42 #include "dev/leds.h"
43 #include "lib/memb.h"
44 #include "net/nbr-table.h"
45 #include "net/packetbuf.h"
46 #include "net/queuebuf.h"
47 #include "net/mac/tsch/tsch.h"
48 #include "net/mac/tsch/tsch-queue.h"
50 #include "net/mac/tsch/tsch-packet.h"
51 #include "net/mac/tsch/tsch-schedule.h"
52 #include "net/mac/tsch/tsch-log.h"
53 #include "net/mac/frame802154.h"
54 #include "sys/process.h"
55 #include "sys/rtimer.h"
56 #include <string.h>
57 
58 #if TSCH_LOG_LEVEL >= 1
59 #define DEBUG DEBUG_PRINT
60 #else /* TSCH_LOG_LEVEL */
61 #define DEBUG DEBUG_NONE
62 #endif /* TSCH_LOG_LEVEL */
63 #include "net/net-debug.h"
64 
65 /* Pre-allocated space for links */
66 MEMB(link_memb, struct tsch_link, TSCH_SCHEDULE_MAX_LINKS);
67 /* Pre-allocated space for slotframes */
68 MEMB(slotframe_memb, struct tsch_slotframe, TSCH_SCHEDULE_MAX_SLOTFRAMES);
69 /* List of slotframes (each slotframe holds its own list of links) */
70 LIST(slotframe_list);
71 
72 /* Adds and returns a slotframe (NULL if failure) */
73 struct tsch_slotframe *
74 tsch_schedule_add_slotframe(uint16_t handle, uint16_t size)
75 {
76  if(size == 0) {
77  return NULL;
78  }
79 
80  if(tsch_schedule_get_slotframe_by_handle(handle)) {
81  /* A slotframe with this handle already exists */
82  return NULL;
83  }
84 
85  if(tsch_get_lock()) {
86  struct tsch_slotframe *sf = memb_alloc(&slotframe_memb);
87  if(sf != NULL) {
88  /* Initialize the slotframe */
89  sf->handle = handle;
90  ASN_DIVISOR_INIT(sf->size, size);
91  LIST_STRUCT_INIT(sf, links_list);
92  /* Add the slotframe to the global list */
93  list_add(slotframe_list, sf);
94  }
95  PRINTF("TSCH-schedule: add_slotframe %u %u\n",
96  handle, size);
97  tsch_release_lock();
98  return sf;
99  }
100  return NULL;
101 }
102 /*---------------------------------------------------------------------------*/
103 /* Removes all slotframes, resulting in an empty schedule */
104 int
105 tsch_schedule_remove_all_slotframes(void)
106 {
107  struct tsch_slotframe *sf;
108  while((sf = list_head(slotframe_list))) {
109  if(tsch_schedule_remove_slotframe(sf) == 0) {
110  return 0;
111  }
112  }
113  return 1;
114 }
115 /*---------------------------------------------------------------------------*/
116 /* Removes a slotframe Return 1 if success, 0 if failure */
117 int
118 tsch_schedule_remove_slotframe(struct tsch_slotframe *slotframe)
119 {
120  if(slotframe != NULL) {
121  /* Remove all links belonging to this slotframe */
122  struct tsch_link *l;
123  while((l = list_head(slotframe->links_list))) {
124  tsch_schedule_remove_link(slotframe, l);
125  }
126 
127  /* Now that the slotframe has no links, remove it. */
128  if(tsch_get_lock()) {
129  PRINTF("TSCH-schedule: remove slotframe %u %u\n", slotframe->handle, slotframe->size.val);
130  memb_free(&slotframe_memb, slotframe);
131  list_remove(slotframe_list, slotframe);
132  tsch_release_lock();
133  return 1;
134  }
135  }
136  return 0;
137 }
138 /*---------------------------------------------------------------------------*/
139 /* Looks for a slotframe from a handle */
140 struct tsch_slotframe *
141 tsch_schedule_get_slotframe_by_handle(uint16_t handle)
142 {
143  if(!tsch_is_locked()) {
144  struct tsch_slotframe *sf = list_head(slotframe_list);
145  while(sf != NULL) {
146  if(sf->handle == handle) {
147  return sf;
148  }
149  sf = list_item_next(sf);
150  }
151  }
152  return NULL;
153 }
154 /*---------------------------------------------------------------------------*/
155 /* Looks for a link from a handle */
156 struct tsch_link *
157 tsch_schedule_get_link_by_handle(uint16_t handle)
158 {
159  if(!tsch_is_locked()) {
160  struct tsch_slotframe *sf = list_head(slotframe_list);
161  while(sf != NULL) {
162  struct tsch_link *l = list_head(sf->links_list);
163  /* Loop over all items. Assume there is max one link per timeslot */
164  while(l != NULL) {
165  if(l->handle == handle) {
166  return l;
167  }
168  l = list_item_next(l);
169  }
170  sf = list_item_next(sf);
171  }
172  }
173  return NULL;
174 }
175 /*---------------------------------------------------------------------------*/
176 /* Adds a link to a slotframe, return a pointer to it (NULL if failure) */
177 struct tsch_link *
178 tsch_schedule_add_link(struct tsch_slotframe *slotframe,
179  uint8_t link_options, enum link_type link_type, const linkaddr_t *address,
180  uint16_t timeslot, uint16_t channel_offset)
181 {
182  struct tsch_link *l = NULL;
183  if(slotframe != NULL) {
184  /* We currently support only one link per timeslot in a given slotframe. */
185  /* Start with removing the link currently installed at this timeslot (needed
186  * to keep neighbor state in sync with link options etc.) */
187  tsch_schedule_remove_link_by_timeslot(slotframe, timeslot);
188  if(!tsch_get_lock()) {
189  PRINTF("TSCH-schedule:! add_link memb_alloc couldn't take lock\n");
190  } else {
191  l = memb_alloc(&link_memb);
192  if(l == NULL) {
193  PRINTF("TSCH-schedule:! add_link memb_alloc failed\n");
194  tsch_release_lock();
195  } else {
196  static int current_link_handle = 0;
197  struct tsch_neighbor *n;
198  /* Add the link to the slotframe */
199  list_add(slotframe->links_list, l);
200  /* Initialize link */
201  l->handle = current_link_handle++;
202  l->link_options = link_options;
203  l->link_type = link_type;
204  l->slotframe_handle = slotframe->handle;
205  l->timeslot = timeslot;
206  l->channel_offset = channel_offset;
207  l->data = NULL;
208  if(address == NULL) {
209  address = &linkaddr_null;
210  }
211  linkaddr_copy(&l->addr, address);
212 
213  PRINTF("TSCH-schedule: add_link %u %u %u %u %u %u\n",
214  slotframe->handle, link_options, link_type, timeslot, channel_offset, TSCH_LOG_ID_FROM_LINKADDR(address));
215 
216  /* Release the lock before we update the neighbor (will take the lock) */
217  tsch_release_lock();
218 
219  if(l->link_options & LINK_OPTION_TX) {
220  n = tsch_queue_add_nbr(&l->addr);
221  /* We have a tx link to this neighbor, update counters */
222  if(n != NULL) {
223  n->tx_links_count++;
224  if(!(l->link_options & LINK_OPTION_SHARED)) {
225  n->dedicated_tx_links_count++;
226  }
227  }
228  }
229  }
230  }
231  }
232  return l;
233 }
234 /*---------------------------------------------------------------------------*/
235 /* Removes a link from slotframe. Return 1 if success, 0 if failure */
236 int
237 tsch_schedule_remove_link(struct tsch_slotframe *slotframe, struct tsch_link *l)
238 {
239  if(slotframe != NULL && l != NULL && l->slotframe_handle == slotframe->handle) {
240  if(tsch_get_lock()) {
241  uint8_t link_options;
242  linkaddr_t addr;
243 
244  /* Save link option and addr in local variables as we need them
245  * after freeing the link */
246  link_options = l->link_options;
247  linkaddr_copy(&addr, &l->addr);
248 
249  /* The link to be removed is scheduled as next, set it to NULL
250  * to abort the next link operation */
251  if(l == current_link) {
252  current_link = NULL;
253  }
254  PRINTF("TSCH-schedule: remove_link %u %u %u %u %u\n",
255  slotframe->handle, l->link_options, l->timeslot, l->channel_offset,
256  TSCH_LOG_ID_FROM_LINKADDR(&l->addr));
257 
258  list_remove(slotframe->links_list, l);
259  memb_free(&link_memb, l);
260 
261  /* Release the lock before we update the neighbor (will take the lock) */
262  tsch_release_lock();
263 
264  /* This was a tx link to this neighbor, update counters */
265  if(link_options & LINK_OPTION_TX) {
266  struct tsch_neighbor *n = tsch_queue_add_nbr(&addr);
267  if(n != NULL) {
268  n->tx_links_count--;
269  if(!(link_options & LINK_OPTION_SHARED)) {
270  n->dedicated_tx_links_count--;
271  }
272  }
273  }
274 
275  return 1;
276  } else {
277  PRINTF("TSCH-schedule:! remove_link memb_alloc couldn't take lock\n");
278  }
279  }
280  return 0;
281 }
282 /*---------------------------------------------------------------------------*/
283 /* Removes a link from slotframe and timeslot. Return a 1 if success, 0 if failure */
284 int
285 tsch_schedule_remove_link_by_timeslot(struct tsch_slotframe *slotframe, uint16_t timeslot)
286 {
287  return slotframe != NULL &&
288  tsch_schedule_remove_link(slotframe, tsch_schedule_get_link_by_timeslot(slotframe, timeslot));
289 }
290 /*---------------------------------------------------------------------------*/
291 /* Looks within a slotframe for a link with a given timeslot */
292 struct tsch_link *
293 tsch_schedule_get_link_by_timeslot(struct tsch_slotframe *slotframe, uint16_t timeslot)
294 {
295  if(!tsch_is_locked()) {
296  if(slotframe != NULL) {
297  struct tsch_link *l = list_head(slotframe->links_list);
298  /* Loop over all items. Assume there is max one link per timeslot */
299  while(l != NULL) {
300  if(l->timeslot == timeslot) {
301  return l;
302  }
303  l = list_item_next(l);
304  }
305  return l;
306  }
307  }
308  return NULL;
309 }
310 /*---------------------------------------------------------------------------*/
311 /* Returns the next active link after a given ASN, and a backup link (for the same ASN, with Rx flag) */
312 struct tsch_link *
313 tsch_schedule_get_next_active_link(struct asn_t *asn, uint16_t *time_offset,
314  struct tsch_link **backup_link)
315 {
316  uint16_t time_to_curr_best = 0;
317  struct tsch_link *curr_best = NULL;
318  struct tsch_link *curr_backup = NULL; /* Keep a back link in case the current link
319  turns out useless when the time comes. For instance, for a Tx-only link, if there is
320  no outgoing packet in queue. In that case, run the backup link instead. The backup link
321  must have Rx flag set. */
322  if(!tsch_is_locked()) {
323  struct tsch_slotframe *sf = list_head(slotframe_list);
324  /* For each slotframe, look for the earliest occurring link */
325  while(sf != NULL) {
326  /* Get timeslot from ASN, given the slotframe length */
327  uint16_t timeslot = ASN_MOD(*asn, sf->size);
328  struct tsch_link *l = list_head(sf->links_list);
329  while(l != NULL) {
330  uint16_t time_to_timeslot =
331  l->timeslot > timeslot ?
332  l->timeslot - timeslot :
333  sf->size.val + l->timeslot - timeslot;
334  if(curr_best == NULL || time_to_timeslot < time_to_curr_best) {
335  time_to_curr_best = time_to_timeslot;
336  curr_best = l;
337  curr_backup = NULL;
338  } else if(time_to_timeslot == time_to_curr_best) {
339  struct tsch_link *new_best = NULL;
340  /* Two links are overlapping, we need to select one of them.
341  * By standard: prioritize Tx links first, second by lowest handle */
342  if((curr_best->link_options & LINK_OPTION_TX) == (l->link_options & LINK_OPTION_TX)) {
343  /* Both or neither links have Tx, select the one with lowest handle */
344  if(l->slotframe_handle < curr_best->slotframe_handle) {
345  new_best = l;
346  }
347  } else {
348  /* Select the link that has the Tx option */
349  if(l->link_options & LINK_OPTION_TX) {
350  new_best = l;
351  }
352  }
353 
354  /* Maintain backup_link */
355  if(curr_backup == NULL) {
356  /* Check if 'l' best can be used as backup */
357  if(new_best != l && (l->link_options & LINK_OPTION_RX)) { /* Does 'l' have Rx flag? */
358  curr_backup = l;
359  }
360  /* Check if curr_best can be used as backup */
361  if(new_best != curr_best && (curr_best->link_options & LINK_OPTION_RX)) { /* Does curr_best have Rx flag? */
362  curr_backup = curr_best;
363  }
364  }
365 
366  /* Maintain curr_best */
367  if(new_best != NULL) {
368  curr_best = new_best;
369  }
370  }
371 
372  l = list_item_next(l);
373  }
374  sf = list_item_next(sf);
375  }
376  if(time_offset != NULL) {
377  *time_offset = time_to_curr_best;
378  }
379  }
380  if(backup_link != NULL) {
381  *backup_link = curr_backup;
382  }
383  return curr_best;
384 }
385 /*---------------------------------------------------------------------------*/
386 /* Module initialization, call only once at startup. Returns 1 is success, 0 if failure. */
387 int
388 tsch_schedule_init(void)
389 {
390  if(tsch_get_lock()) {
391  memb_init(&link_memb);
392  memb_init(&slotframe_memb);
393  list_init(slotframe_list);
394  tsch_release_lock();
395  return 1;
396  } else {
397  return 0;
398  }
399 }
400 /*---------------------------------------------------------------------------*/
401 /* Create a 6TiSCH minimal schedule */
402 void
403 tsch_schedule_create_minimal(void)
404 {
405  struct tsch_slotframe *sf_min;
406  /* First, empty current schedule */
407  tsch_schedule_remove_all_slotframes();
408  /* Build 6TiSCH minimal schedule.
409  * We pick a slotframe length of TSCH_SCHEDULE_DEFAULT_LENGTH */
410  sf_min = tsch_schedule_add_slotframe(0, TSCH_SCHEDULE_DEFAULT_LENGTH);
411  /* Add a single Tx|Rx|Shared slot using broadcast address (i.e. usable for unicast and broadcast).
412  * We set the link type to advertising, which is not compliant with 6TiSCH minimal schedule
413  * but is required according to 802.15.4e if also used for EB transmission.
414  * Timeslot: 0, channel offset: 0. */
415  tsch_schedule_add_link(sf_min,
416  LINK_OPTION_RX | LINK_OPTION_TX | LINK_OPTION_SHARED | LINK_OPTION_TIME_KEEPING,
417  LINK_TYPE_ADVERTISING, &tsch_broadcast_address,
418  0, 0);
419 }
420 /*---------------------------------------------------------------------------*/
421 /* Prints out the current schedule (all slotframes and links) */
422 void
423 tsch_schedule_print(void)
424 {
425  if(!tsch_is_locked()) {
426  struct tsch_slotframe *sf = list_head(slotframe_list);
427 
428  printf("Schedule: slotframe list\n");
429 
430  while(sf != NULL) {
431  struct tsch_link *l = list_head(sf->links_list);
432 
433  printf("[Slotframe] Handle %u, size %u\n", sf->handle, sf->size.val);
434  printf("List of links:\n");
435 
436  while(l != NULL) {
437  printf("[Link] Options %02x, type %u, timeslot %u, channel offset %u, address %u\n",
438  l->link_options, l->link_type, l->timeslot, l->channel_offset, l->addr.u8[7]);
439  l = list_item_next(l);
440  }
441 
442  sf = list_item_next(sf);
443  }
444 
445  printf("Schedule: end of slotframe list\n");
446  }
447 }
448 /*---------------------------------------------------------------------------*/
Header file for the real-time timer module.
void list_remove(list_t list, void *item)
Remove a specific element from a list.
Definition: list.c:240
#define LIST(name)
Declare a linked list.
Definition: list.h:86
static uip_ds6_addr_t * addr
Pointer to a router list entry.
Definition: uip-nd6.c:124
#define MEMB(name, structure, num)
Declare a memory block.
Definition: memb.h:89
void * list_item_next(void *item)
Get the next item following this item.
Definition: list.c:325
Private TSCH definitions (meant for use by TSCH implementation files only) ...
const linkaddr_t linkaddr_null
The null Rime address.
Definition: eth-conf.c:37
Header file for the Rime buffer (packetbuf) management
void list_init(list_t list)
Initialize a list.
Definition: list.c:66
void memb_init(struct memb *m)
Initialize a memory block that was declared with MEMB().
Definition: memb.c:52
void * list_head(list_t list)
Get a pointer to the first element of a list.
Definition: list.c:83
A set of debugging macros for the netstack
#define NULL
The null pointer.
802.15.4 frame creation and parsing functions
void list_add(list_t list, void *item)
Add an item at the end of a list.
Definition: list.c:143
Header file for the Contiki process interface.
Memory block allocation routines.
Header file for the Rime queue buffer management
void linkaddr_copy(linkaddr_t *dest, const linkaddr_t *src)
Copy a Rime address.
Definition: linkaddr.c:60
#define LIST_STRUCT_INIT(struct_ptr, name)
Initialize a linked list that is part of a structure.
Definition: list.h:122
void * memb_alloc(struct memb *m)
Allocate a memory block from a block of memory declared with MEMB().
Definition: memb.c:59
char memb_free(struct memb *m, void *ptr)
Deallocate a memory block from a memory block previously declared with MEMB().
Definition: memb.c:79