Contiki-NG
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 /**
42  * \addtogroup tsch
43  * @{
44 */
45 
46 #include "contiki.h"
47 #include "dev/leds.h"
48 #include "lib/memb.h"
49 #include "net/nbr-table.h"
50 #include "net/packetbuf.h"
51 #include "net/queuebuf.h"
52 #include "net/mac/tsch/tsch.h"
54 #include "sys/process.h"
55 #include "sys/rtimer.h"
56 #include <string.h>
57 
58 /* Log configuration */
59 #include "sys/log.h"
60 #define LOG_MODULE "TSCH Sched"
61 #define LOG_LEVEL LOG_LEVEL_MAC
62 
63 /* Pre-allocated space for links */
64 MEMB(link_memb, struct tsch_link, TSCH_SCHEDULE_MAX_LINKS);
65 /* Pre-allocated space for slotframes */
66 MEMB(slotframe_memb, struct tsch_slotframe, TSCH_SCHEDULE_MAX_SLOTFRAMES);
67 /* List of slotframes (each slotframe holds its own list of links) */
68 LIST(slotframe_list);
69 
70 /* Adds and returns a slotframe (NULL if failure) */
71 struct tsch_slotframe *
72 tsch_schedule_add_slotframe(uint16_t handle, uint16_t size)
73 {
74  if(size == 0) {
75  return NULL;
76  }
77 
79  /* A slotframe with this handle already exists */
80  return NULL;
81  }
82 
83  if(tsch_get_lock()) {
84  struct tsch_slotframe *sf = memb_alloc(&slotframe_memb);
85  if(sf != NULL) {
86  /* Initialize the slotframe */
87  sf->handle = handle;
88  TSCH_ASN_DIVISOR_INIT(sf->size, size);
89  LIST_STRUCT_INIT(sf, links_list);
90  /* Add the slotframe to the global list */
91  list_add(slotframe_list, sf);
92  }
93  LOG_INFO("add_slotframe %u %u\n",
94  handle, size);
96  return sf;
97  }
98  return NULL;
99 }
100 /*---------------------------------------------------------------------------*/
101 /* Removes all slotframes, resulting in an empty schedule */
102 int
104 {
105  struct tsch_slotframe *sf;
106  while((sf = list_head(slotframe_list))) {
107  if(tsch_schedule_remove_slotframe(sf) == 0) {
108  return 0;
109  }
110  }
111  return 1;
112 }
113 /*---------------------------------------------------------------------------*/
114 /* Removes a slotframe Return 1 if success, 0 if failure */
115 int
117 {
118  if(slotframe != NULL) {
119  /* Remove all links belonging to this slotframe */
120  struct tsch_link *l;
121  while((l = list_head(slotframe->links_list))) {
122  tsch_schedule_remove_link(slotframe, l);
123  }
124 
125  /* Now that the slotframe has no links, remove it. */
126  if(tsch_get_lock()) {
127  LOG_INFO("remove slotframe %u %u\n", slotframe->handle, slotframe->size.val);
128  memb_free(&slotframe_memb, slotframe);
129  list_remove(slotframe_list, slotframe);
131  return 1;
132  }
133  }
134  return 0;
135 }
136 /*---------------------------------------------------------------------------*/
137 /* Looks for a slotframe from a handle */
138 struct tsch_slotframe *
140 {
141  if(!tsch_is_locked()) {
142  struct tsch_slotframe *sf = list_head(slotframe_list);
143  while(sf != NULL) {
144  if(sf->handle == handle) {
145  return sf;
146  }
147  sf = list_item_next(sf);
148  }
149  }
150  return NULL;
151 }
152 /*---------------------------------------------------------------------------*/
153 /* Looks for a link from a handle */
154 struct tsch_link *
156 {
157  if(!tsch_is_locked()) {
158  struct tsch_slotframe *sf = list_head(slotframe_list);
159  while(sf != NULL) {
160  struct tsch_link *l = list_head(sf->links_list);
161  /* Loop over all items. Assume there is max one link per timeslot */
162  while(l != NULL) {
163  if(l->handle == handle) {
164  return l;
165  }
166  l = list_item_next(l);
167  }
168  sf = list_item_next(sf);
169  }
170  }
171  return NULL;
172 }
173 /*---------------------------------------------------------------------------*/
174 static const char *
175 print_link_options(uint16_t link_options)
176 {
177  static char buffer[20];
178  unsigned length;
179 
180  buffer[0] = '\0';
181  if(link_options & LINK_OPTION_TX) {
182  strcat(buffer, "Tx|");
183  }
184  if(link_options & LINK_OPTION_RX) {
185  strcat(buffer, "Rx|");
186  }
187  if(link_options & LINK_OPTION_SHARED) {
188  strcat(buffer, "Sh|");
189  }
190  length = strlen(buffer);
191  if(length > 0) {
192  buffer[length - 1] = '\0';
193  }
194 
195  return buffer;
196 }
197 /*---------------------------------------------------------------------------*/
198 static const char *
199 print_link_type(uint16_t link_type)
200 {
201  switch(link_type) {
202  case LINK_TYPE_NORMAL:
203  return "NORMAL";
204  case LINK_TYPE_ADVERTISING:
205  return "ADV";
206  case LINK_TYPE_ADVERTISING_ONLY:
207  return "ADV_ONLY";
208  default:
209  return "?";
210  }
211 }
212 /*---------------------------------------------------------------------------*/
213 /* Adds a link to a slotframe, return a pointer to it (NULL if failure) */
214 struct tsch_link *
216  uint8_t link_options, enum link_type link_type, const linkaddr_t *address,
217  uint16_t timeslot, uint16_t channel_offset)
218 {
219  struct tsch_link *l = NULL;
220  if(slotframe != NULL) {
221  /* We currently support only one link per timeslot in a given slotframe. */
222 
223  /* Validation of specified timeslot and channel_offset */
224  if(timeslot > (slotframe->size.val - 1)) {
225  LOG_ERR("! add_link invalid timeslot: %u\n", timeslot);
226  return NULL;
227  }
228 
229  /* Start with removing the link currently installed at this timeslot (needed
230  * to keep neighbor state in sync with link options etc.) */
231  tsch_schedule_remove_link_by_timeslot(slotframe, timeslot);
232  if(!tsch_get_lock()) {
233  LOG_ERR("! add_link memb_alloc couldn't take lock\n");
234  } else {
235  l = memb_alloc(&link_memb);
236  if(l == NULL) {
237  LOG_ERR("! add_link memb_alloc failed\n");
239  } else {
240  static int current_link_handle = 0;
241  struct tsch_neighbor *n;
242  /* Add the link to the slotframe */
243  list_add(slotframe->links_list, l);
244  /* Initialize link */
245  l->handle = current_link_handle++;
246  l->link_options = link_options;
247  l->link_type = link_type;
248  l->slotframe_handle = slotframe->handle;
249  l->timeslot = timeslot;
250  l->channel_offset = channel_offset;
251  l->data = NULL;
252  if(address == NULL) {
253  address = &linkaddr_null;
254  }
255  linkaddr_copy(&l->addr, address);
256 
257  LOG_INFO("add_link sf=%u opt=%s type=%s ts=%u ch=%u addr=",
258  slotframe->handle,
259  print_link_options(link_options),
260  print_link_type(link_type), timeslot, channel_offset);
261  LOG_INFO_LLADDR(address);
262  LOG_INFO_("\n");
263  /* Release the lock before we update the neighbor (will take the lock) */
265 
266  if(l->link_options & LINK_OPTION_TX) {
267  n = tsch_queue_add_nbr(&l->addr);
268  /* We have a tx link to this neighbor, update counters */
269  if(n != NULL) {
270  n->tx_links_count++;
271  if(!(l->link_options & LINK_OPTION_SHARED)) {
272  n->dedicated_tx_links_count++;
273  }
274  }
275  }
276  }
277  }
278  }
279  return l;
280 }
281 /*---------------------------------------------------------------------------*/
282 /* Removes a link from slotframe. Return 1 if success, 0 if failure */
283 int
285 {
286  if(slotframe != NULL && l != NULL && l->slotframe_handle == slotframe->handle) {
287  if(tsch_get_lock()) {
288  uint8_t link_options;
289  linkaddr_t addr;
290 
291  /* Save link option and addr in local variables as we need them
292  * after freeing the link */
293  link_options = l->link_options;
294  linkaddr_copy(&addr, &l->addr);
295 
296  /* The link to be removed is scheduled as next, set it to NULL
297  * to abort the next link operation */
298  if(l == current_link) {
299  current_link = NULL;
300  }
301  LOG_INFO("remove_link sf=%u opt=%s type=%s ts=%u ch=%u addr=",
302  slotframe->handle,
303  print_link_options(l->link_options),
304  print_link_type(l->link_type), l->timeslot, l->channel_offset);
305  LOG_INFO_LLADDR(&l->addr);
306  LOG_INFO_("\n");
307 
308  list_remove(slotframe->links_list, l);
309  memb_free(&link_memb, l);
310 
311  /* Release the lock before we update the neighbor (will take the lock) */
313 
314  /* This was a tx link to this neighbor, update counters */
315  if(link_options & LINK_OPTION_TX) {
316  struct tsch_neighbor *n = tsch_queue_add_nbr(&addr);
317  if(n != NULL) {
318  n->tx_links_count--;
319  if(!(link_options & LINK_OPTION_SHARED)) {
320  n->dedicated_tx_links_count--;
321  }
322  }
323  }
324 
325  return 1;
326  } else {
327  LOG_ERR("! remove_link memb_alloc couldn't take lock\n");
328  }
329  }
330  return 0;
331 }
332 /*---------------------------------------------------------------------------*/
333 /* Removes a link from slotframe and timeslot. Return a 1 if success, 0 if failure */
334 int
335 tsch_schedule_remove_link_by_timeslot(struct tsch_slotframe *slotframe, uint16_t timeslot)
336 {
337  return slotframe != NULL &&
338  tsch_schedule_remove_link(slotframe, tsch_schedule_get_link_by_timeslot(slotframe, timeslot));
339 }
340 /*---------------------------------------------------------------------------*/
341 /* Looks within a slotframe for a link with a given timeslot */
342 struct tsch_link *
343 tsch_schedule_get_link_by_timeslot(struct tsch_slotframe *slotframe, uint16_t timeslot)
344 {
345  if(!tsch_is_locked()) {
346  if(slotframe != NULL) {
347  struct tsch_link *l = list_head(slotframe->links_list);
348  /* Loop over all items. Assume there is max one link per timeslot */
349  while(l != NULL) {
350  if(l->timeslot == timeslot) {
351  return l;
352  }
353  l = list_item_next(l);
354  }
355  return l;
356  }
357  }
358  return NULL;
359 }
360 /*---------------------------------------------------------------------------*/
361 /* Returns the next active link after a given ASN, and a backup link (for the same ASN, with Rx flag) */
362 struct tsch_link *
363 tsch_schedule_get_next_active_link(struct tsch_asn_t *asn, uint16_t *time_offset,
364  struct tsch_link **backup_link)
365 {
366  uint16_t time_to_curr_best = 0;
367  struct tsch_link *curr_best = NULL;
368  struct tsch_link *curr_backup = NULL; /* Keep a back link in case the current link
369  turns out useless when the time comes. For instance, for a Tx-only link, if there is
370  no outgoing packet in queue. In that case, run the backup link instead. The backup link
371  must have Rx flag set. */
372  if(!tsch_is_locked()) {
373  struct tsch_slotframe *sf = list_head(slotframe_list);
374  /* For each slotframe, look for the earliest occurring link */
375  while(sf != NULL) {
376  /* Get timeslot from ASN, given the slotframe length */
377  uint16_t timeslot = TSCH_ASN_MOD(*asn, sf->size);
378  struct tsch_link *l = list_head(sf->links_list);
379  while(l != NULL) {
380  uint16_t time_to_timeslot =
381  l->timeslot > timeslot ?
382  l->timeslot - timeslot :
383  sf->size.val + l->timeslot - timeslot;
384  if(curr_best == NULL || time_to_timeslot < time_to_curr_best) {
385  time_to_curr_best = time_to_timeslot;
386  curr_best = l;
387  curr_backup = NULL;
388  } else if(time_to_timeslot == time_to_curr_best) {
389  struct tsch_link *new_best = NULL;
390  /* Two links are overlapping, we need to select one of them.
391  * By standard: prioritize Tx links first, second by lowest handle */
392  if((curr_best->link_options & LINK_OPTION_TX) == (l->link_options & LINK_OPTION_TX)) {
393  /* Both or neither links have Tx, select the one with lowest handle */
394  if(l->slotframe_handle < curr_best->slotframe_handle) {
395  new_best = l;
396  }
397  } else {
398  /* Select the link that has the Tx option */
399  if(l->link_options & LINK_OPTION_TX) {
400  new_best = l;
401  }
402  }
403 
404  /* Maintain backup_link */
405  if(curr_backup == NULL) {
406  /* Check if 'l' best can be used as backup */
407  if(new_best != l && (l->link_options & LINK_OPTION_RX)) { /* Does 'l' have Rx flag? */
408  curr_backup = l;
409  }
410  /* Check if curr_best can be used as backup */
411  if(new_best != curr_best && (curr_best->link_options & LINK_OPTION_RX)) { /* Does curr_best have Rx flag? */
412  curr_backup = curr_best;
413  }
414  }
415 
416  /* Maintain curr_best */
417  if(new_best != NULL) {
418  curr_best = new_best;
419  }
420  }
421 
422  l = list_item_next(l);
423  }
424  sf = list_item_next(sf);
425  }
426  if(time_offset != NULL) {
427  *time_offset = time_to_curr_best;
428  }
429  }
430  if(backup_link != NULL) {
431  *backup_link = curr_backup;
432  }
433  return curr_best;
434 }
435 /*---------------------------------------------------------------------------*/
436 /* Module initialization, call only once at startup. Returns 1 is success, 0 if failure. */
437 int
439 {
440  if(tsch_get_lock()) {
441  memb_init(&link_memb);
442  memb_init(&slotframe_memb);
443  list_init(slotframe_list);
445  return 1;
446  } else {
447  return 0;
448  }
449 }
450 /*---------------------------------------------------------------------------*/
451 /* Create a 6TiSCH minimal schedule */
452 void
454 {
455  struct tsch_slotframe *sf_min;
456  /* First, empty current schedule */
458  /* Build 6TiSCH minimal schedule.
459  * We pick a slotframe length of TSCH_SCHEDULE_DEFAULT_LENGTH */
460  sf_min = tsch_schedule_add_slotframe(0, TSCH_SCHEDULE_DEFAULT_LENGTH);
461  /* Add a single Tx|Rx|Shared slot using broadcast address (i.e. usable for unicast and broadcast).
462  * We set the link type to advertising, which is not compliant with 6TiSCH minimal schedule
463  * but is required according to 802.15.4e if also used for EB transmission.
464  * Timeslot: 0, channel offset: 0. */
465  tsch_schedule_add_link(sf_min,
466  (LINK_OPTION_RX | LINK_OPTION_TX | LINK_OPTION_SHARED | LINK_OPTION_TIME_KEEPING),
467  LINK_TYPE_ADVERTISING, &tsch_broadcast_address,
468  0, 0);
469 }
470 /*---------------------------------------------------------------------------*/
471 struct tsch_slotframe *
473 {
474  return list_head(slotframe_list);
475 }
476 /*---------------------------------------------------------------------------*/
477 struct tsch_slotframe *
479 {
480  return list_item_next(sf);
481 }
482 /*---------------------------------------------------------------------------*/
483 /* Prints out the current schedule (all slotframes and links) */
484 void
486 {
487  if(!tsch_is_locked()) {
488  struct tsch_slotframe *sf = list_head(slotframe_list);
489 
490  LOG_PRINT("----- start slotframe list -----\n");
491 
492  while(sf != NULL) {
493  struct tsch_link *l = list_head(sf->links_list);
494 
495  LOG_PRINT("Slotframe Handle %u, size %u\n", sf->handle, sf->size.val);
496 
497  while(l != NULL) {
498  LOG_PRINT("* Link Options %02x, type %u, timeslot %u, channel offset %u, address %u\n",
499  l->link_options, l->link_type, l->timeslot, l->channel_offset, l->addr.u8[7]);
500  l = list_item_next(l);
501  }
502 
503  sf = list_item_next(sf);
504  }
505 
506  LOG_PRINT("----- end slotframe list -----\n");
507  }
508 }
509 /*---------------------------------------------------------------------------*/
510 /** @} */
#define TSCH_ASN_DIVISOR_INIT(div, val_)
Initialize a struct asn_divisor_t.
Definition: tsch-asn.h:86
#define LIST_STRUCT_INIT(struct_ptr, name)
Initialize a linked list that is part of a structure.
Definition: list.h:125
struct tsch_slotframe * tsch_schedule_slotframe_next(struct tsch_slotframe *sf)
Access the next item in the list of slotframes.
int memb_free(struct memb *m, void *ptr)
Deallocate a memory block from a memory block previously declared with MEMB().
Definition: memb.c:78
static uip_ds6_addr_t * addr
Pointer to a nbr cache entry.
Definition: uip-nd6.c:107
int tsch_schedule_init(void)
Module initialization, call only once at init.
int tsch_get_lock(void)
Takes the TSCH lock.
void tsch_release_lock(void)
Releases the TSCH lock.
TSCH neighbor information.
Definition: tsch-types.h:109
802.15.4e slotframe (contains links)
Definition: tsch-types.h:84
struct tsch_slotframe * tsch_schedule_add_slotframe(uint16_t handle, uint16_t size)
Creates and adds a new slotframe.
Definition: tsch-schedule.c:72
struct tsch_link * tsch_schedule_get_link_by_timeslot(struct tsch_slotframe *slotframe, uint16_t timeslot)
Looks within a slotframe for a link with a given timeslot.
const linkaddr_t linkaddr_null
The null link-layer address.
struct tsch_slotframe * tsch_schedule_get_slotframe_by_handle(uint16_t handle)
Looks up a slotframe by handle.
struct tsch_neighbor * tsch_queue_add_nbr(const linkaddr_t *addr)
Add a TSCH neighbor queue.
Definition: tsch-queue.c:80
Header file for the Packet queue buffer management
struct tsch_link * tsch_schedule_get_next_active_link(struct tsch_asn_t *asn, uint16_t *time_offset, struct tsch_link **backup_link)
Returns the next active link after a given ASN, and a backup link (for the same ASN, with Rx flag)
void tsch_schedule_print(void)
Prints out the current schedule (all slotframes and links)
void * list_head(list_t list)
Get a pointer to the first element of a list.
Definition: list.c:82
Header file for the real-time timer module.
#define TSCH_ASN_MOD(asn, div)
Returns the result (16 bits) of a modulo operation on ASN, with divisor being a struct asn_divisor_t...
Definition: tsch-asn.h:93
int tsch_schedule_remove_link_by_timeslot(struct tsch_slotframe *slotframe, uint16_t timeslot)
Removes a link from a slotframe and timeslot.
Header file for the Contiki process interface.
Main API declarations for TSCH.
int tsch_schedule_remove_link(struct tsch_slotframe *slotframe, struct tsch_link *l)
Removes a link.
struct tsch_link * tsch_schedule_add_link(struct tsch_slotframe *slotframe, uint8_t link_options, enum link_type link_type, const linkaddr_t *address, uint16_t timeslot, uint16_t channel_offset)
Adds a link to a slotframe.
802.15.4 frame creation and parsing functions
Memory block allocation routines.
int tsch_is_locked(void)
Checks if the TSCH lock is set.
struct tsch_slotframe * tsch_schedule_slotframe_head(void)
Access the first item in the list of slotframes.
link_type
802.15.4e link types.
Definition: tsch-types.h:54
void linkaddr_copy(linkaddr_t *dest, const linkaddr_t *src)
Copy a link-layer address.
Definition: linkaddr.c:63
int tsch_schedule_remove_slotframe(struct tsch_slotframe *slotframe)
Removes a slotframe.
void list_add(list_t list, void *item)
Add an item at the end of a list.
Definition: list.c:142
void list_init(list_t list)
Initialize a list.
Definition: list.c:65
#define LIST(name)
Declare a linked list.
Definition: list.h:89
int tsch_schedule_remove_all_slotframes(void)
Removes all slotframes, resulting in an empty schedule.
void * memb_alloc(struct memb *m)
Allocate a memory block from a block of memory declared with MEMB().
Definition: memb.c:59
struct tsch_link * tsch_schedule_get_link_by_handle(uint16_t handle)
Looks for a link from a handle.
Header file for the Packet buffer (packetbuf) management
void memb_init(struct memb *m)
Initialize a memory block that was declared with MEMB().
Definition: memb.c:52
Header file for the logging system
Header file for the LED HAL.
The ASN is an absolute slot number over 5 bytes.
Definition: tsch-asn.h:48
void list_remove(list_t list, void *item)
Remove a specific element from a list.
Definition: list.c:237
void tsch_schedule_create_minimal(void)
Create a 6tisch minimal schedule with length TSCH_SCHEDULE_DEFAULT_LENGTH.
void * list_item_next(void *item)
Get the next item following this item.
Definition: list.c:322
#define MEMB(name, structure, num)
Declare a memory block.
Definition: memb.h:90