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  } else if(channel_offset > 15) {
228  LOG_ERR("! add_link invalid channel_offset: %u\n", channel_offset);
229  return NULL;
230  }
231 
232  /* Start with removing the link currently installed at this timeslot (needed
233  * to keep neighbor state in sync with link options etc.) */
234  tsch_schedule_remove_link_by_timeslot(slotframe, timeslot);
235  if(!tsch_get_lock()) {
236  LOG_ERR("! add_link memb_alloc couldn't take lock\n");
237  } else {
238  l = memb_alloc(&link_memb);
239  if(l == NULL) {
240  LOG_ERR("! add_link memb_alloc failed\n");
242  } else {
243  static int current_link_handle = 0;
244  struct tsch_neighbor *n;
245  /* Add the link to the slotframe */
246  list_add(slotframe->links_list, l);
247  /* Initialize link */
248  l->handle = current_link_handle++;
249  l->link_options = link_options;
250  l->link_type = link_type;
251  l->slotframe_handle = slotframe->handle;
252  l->timeslot = timeslot;
253  l->channel_offset = channel_offset;
254  l->data = NULL;
255  if(address == NULL) {
256  address = &linkaddr_null;
257  }
258  linkaddr_copy(&l->addr, address);
259 
260  LOG_INFO("add_link sf=%u opt=%s type=%s ts=%u ch=%u addr=",
261  slotframe->handle,
262  print_link_options(link_options),
263  print_link_type(link_type), timeslot, channel_offset);
264  LOG_INFO_LLADDR(address);
265  LOG_INFO_("\n");
266  /* Release the lock before we update the neighbor (will take the lock) */
268 
269  if(l->link_options & LINK_OPTION_TX) {
270  n = tsch_queue_add_nbr(&l->addr);
271  /* We have a tx link to this neighbor, update counters */
272  if(n != NULL) {
273  n->tx_links_count++;
274  if(!(l->link_options & LINK_OPTION_SHARED)) {
275  n->dedicated_tx_links_count++;
276  }
277  }
278  }
279  }
280  }
281  }
282  return l;
283 }
284 /*---------------------------------------------------------------------------*/
285 /* Removes a link from slotframe. Return 1 if success, 0 if failure */
286 int
288 {
289  if(slotframe != NULL && l != NULL && l->slotframe_handle == slotframe->handle) {
290  if(tsch_get_lock()) {
291  uint8_t link_options;
292  linkaddr_t addr;
293 
294  /* Save link option and addr in local variables as we need them
295  * after freeing the link */
296  link_options = l->link_options;
297  linkaddr_copy(&addr, &l->addr);
298 
299  /* The link to be removed is scheduled as next, set it to NULL
300  * to abort the next link operation */
301  if(l == current_link) {
302  current_link = NULL;
303  }
304  LOG_INFO("remove_link sf=%u opt=%s type=%s ts=%u ch=%u addr=",
305  slotframe->handle,
306  print_link_options(l->link_options),
307  print_link_type(l->link_type), l->timeslot, l->channel_offset);
308  LOG_INFO_LLADDR(&l->addr);
309  LOG_INFO_("\n");
310 
311  list_remove(slotframe->links_list, l);
312  memb_free(&link_memb, l);
313 
314  /* Release the lock before we update the neighbor (will take the lock) */
316 
317  /* This was a tx link to this neighbor, update counters */
318  if(link_options & LINK_OPTION_TX) {
319  struct tsch_neighbor *n = tsch_queue_add_nbr(&addr);
320  if(n != NULL) {
321  n->tx_links_count--;
322  if(!(link_options & LINK_OPTION_SHARED)) {
323  n->dedicated_tx_links_count--;
324  }
325  }
326  }
327 
328  return 1;
329  } else {
330  LOG_ERR("! remove_link memb_alloc couldn't take lock\n");
331  }
332  }
333  return 0;
334 }
335 /*---------------------------------------------------------------------------*/
336 /* Removes a link from slotframe and timeslot. Return a 1 if success, 0 if failure */
337 int
338 tsch_schedule_remove_link_by_timeslot(struct tsch_slotframe *slotframe, uint16_t timeslot)
339 {
340  return slotframe != NULL &&
341  tsch_schedule_remove_link(slotframe, tsch_schedule_get_link_by_timeslot(slotframe, timeslot));
342 }
343 /*---------------------------------------------------------------------------*/
344 /* Looks within a slotframe for a link with a given timeslot */
345 struct tsch_link *
346 tsch_schedule_get_link_by_timeslot(struct tsch_slotframe *slotframe, uint16_t timeslot)
347 {
348  if(!tsch_is_locked()) {
349  if(slotframe != NULL) {
350  struct tsch_link *l = list_head(slotframe->links_list);
351  /* Loop over all items. Assume there is max one link per timeslot */
352  while(l != NULL) {
353  if(l->timeslot == timeslot) {
354  return l;
355  }
356  l = list_item_next(l);
357  }
358  return l;
359  }
360  }
361  return NULL;
362 }
363 /*---------------------------------------------------------------------------*/
364 /* Returns the next active link after a given ASN, and a backup link (for the same ASN, with Rx flag) */
365 struct tsch_link *
366 tsch_schedule_get_next_active_link(struct tsch_asn_t *asn, uint16_t *time_offset,
367  struct tsch_link **backup_link)
368 {
369  uint16_t time_to_curr_best = 0;
370  struct tsch_link *curr_best = NULL;
371  struct tsch_link *curr_backup = NULL; /* Keep a back link in case the current link
372  turns out useless when the time comes. For instance, for a Tx-only link, if there is
373  no outgoing packet in queue. In that case, run the backup link instead. The backup link
374  must have Rx flag set. */
375  if(!tsch_is_locked()) {
376  struct tsch_slotframe *sf = list_head(slotframe_list);
377  /* For each slotframe, look for the earliest occurring link */
378  while(sf != NULL) {
379  /* Get timeslot from ASN, given the slotframe length */
380  uint16_t timeslot = TSCH_ASN_MOD(*asn, sf->size);
381  struct tsch_link *l = list_head(sf->links_list);
382  while(l != NULL) {
383  uint16_t time_to_timeslot =
384  l->timeslot > timeslot ?
385  l->timeslot - timeslot :
386  sf->size.val + l->timeslot - timeslot;
387  if(curr_best == NULL || time_to_timeslot < time_to_curr_best) {
388  time_to_curr_best = time_to_timeslot;
389  curr_best = l;
390  curr_backup = NULL;
391  } else if(time_to_timeslot == time_to_curr_best) {
392  struct tsch_link *new_best = NULL;
393  /* Two links are overlapping, we need to select one of them.
394  * By standard: prioritize Tx links first, second by lowest handle */
395  if((curr_best->link_options & LINK_OPTION_TX) == (l->link_options & LINK_OPTION_TX)) {
396  /* Both or neither links have Tx, select the one with lowest handle */
397  if(l->slotframe_handle < curr_best->slotframe_handle) {
398  new_best = l;
399  }
400  } else {
401  /* Select the link that has the Tx option */
402  if(l->link_options & LINK_OPTION_TX) {
403  new_best = l;
404  }
405  }
406 
407  /* Maintain backup_link */
408  if(curr_backup == NULL) {
409  /* Check if 'l' best can be used as backup */
410  if(new_best != l && (l->link_options & LINK_OPTION_RX)) { /* Does 'l' have Rx flag? */
411  curr_backup = l;
412  }
413  /* Check if curr_best can be used as backup */
414  if(new_best != curr_best && (curr_best->link_options & LINK_OPTION_RX)) { /* Does curr_best have Rx flag? */
415  curr_backup = curr_best;
416  }
417  }
418 
419  /* Maintain curr_best */
420  if(new_best != NULL) {
421  curr_best = new_best;
422  }
423  }
424 
425  l = list_item_next(l);
426  }
427  sf = list_item_next(sf);
428  }
429  if(time_offset != NULL) {
430  *time_offset = time_to_curr_best;
431  }
432  }
433  if(backup_link != NULL) {
434  *backup_link = curr_backup;
435  }
436  return curr_best;
437 }
438 /*---------------------------------------------------------------------------*/
439 /* Module initialization, call only once at startup. Returns 1 is success, 0 if failure. */
440 int
442 {
443  if(tsch_get_lock()) {
444  memb_init(&link_memb);
445  memb_init(&slotframe_memb);
446  list_init(slotframe_list);
448  return 1;
449  } else {
450  return 0;
451  }
452 }
453 /*---------------------------------------------------------------------------*/
454 /* Create a 6TiSCH minimal schedule */
455 void
457 {
458  struct tsch_slotframe *sf_min;
459  /* First, empty current schedule */
461  /* Build 6TiSCH minimal schedule.
462  * We pick a slotframe length of TSCH_SCHEDULE_DEFAULT_LENGTH */
463  sf_min = tsch_schedule_add_slotframe(0, TSCH_SCHEDULE_DEFAULT_LENGTH);
464  /* Add a single Tx|Rx|Shared slot using broadcast address (i.e. usable for unicast and broadcast).
465  * We set the link type to advertising, which is not compliant with 6TiSCH minimal schedule
466  * but is required according to 802.15.4e if also used for EB transmission.
467  * Timeslot: 0, channel offset: 0. */
468  tsch_schedule_add_link(sf_min,
469  (LINK_OPTION_RX | LINK_OPTION_TX | LINK_OPTION_SHARED | LINK_OPTION_TIME_KEEPING),
470  LINK_TYPE_ADVERTISING, &tsch_broadcast_address,
471  0, 0);
472 }
473 /*---------------------------------------------------------------------------*/
474 struct tsch_slotframe *
476 {
477  return list_head(slotframe_list);
478 }
479 /*---------------------------------------------------------------------------*/
480 struct tsch_slotframe *
482 {
483  return list_item_next(sf);
484 }
485 /*---------------------------------------------------------------------------*/
486 /* Prints out the current schedule (all slotframes and links) */
487 void
489 {
490  if(!tsch_is_locked()) {
491  struct tsch_slotframe *sf = list_head(slotframe_list);
492 
493  LOG_PRINT("----- start slotframe list -----\n");
494 
495  while(sf != NULL) {
496  struct tsch_link *l = list_head(sf->links_list);
497 
498  LOG_PRINT("Slotframe Handle %u, size %u\n", sf->handle, sf->size.val);
499 
500  while(l != NULL) {
501  LOG_PRINT("* Link Options %02x, type %u, timeslot %u, channel offset %u, address %u\n",
502  l->link_options, l->link_type, l->timeslot, l->channel_offset, l->addr.u8[7]);
503  l = list_item_next(l);
504  }
505 
506  sf = list_item_next(sf);
507  }
508 
509  LOG_PRINT("----- end slotframe list -----\n");
510  }
511 }
512 /*---------------------------------------------------------------------------*/
513 /** @} */
#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.
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
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 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:89