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, channel_offset);
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_get_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
336  uint16_t timeslot, uint16_t channel_offset)
337 {
338  return tsch_schedule_remove_link(slotframe,
339  tsch_schedule_get_link_by_timeslot(slotframe, timeslot, channel_offset));
340 }
341 /*---------------------------------------------------------------------------*/
342 /* Looks within a slotframe for a link with a given timeslot */
343 struct tsch_link *
345  uint16_t timeslot, uint16_t channel_offset)
346 {
347  if(!tsch_is_locked()) {
348  if(slotframe != NULL) {
349  struct tsch_link *l = list_head(slotframe->links_list);
350  /* Loop over all items. Assume there is max one link per timeslot */
351  while(l != NULL) {
352  if(l->timeslot == timeslot && l->channel_offset == channel_offset) {
353  return l;
354  }
355  l = list_item_next(l);
356  }
357  return l;
358  }
359  }
360  return NULL;
361 }
362 /*---------------------------------------------------------------------------*/
363 static struct tsch_link *
364 default_tsch_link_comparator(struct tsch_link *a, struct tsch_link *b)
365 {
366  if(!(a->link_options & LINK_OPTION_TX)) {
367  /* None of the links are Tx: simply return the first link */
368  return a;
369  }
370 
371  /* Two Tx links at the same slotframe; return the one with most packets to send */
372  if(!linkaddr_cmp(&a->addr, &b->addr)) {
373  struct tsch_neighbor *an = tsch_queue_get_nbr(&a->addr);
374  struct tsch_neighbor *bn = tsch_queue_get_nbr(&b->addr);
375  int a_packet_count = an ? ringbufindex_elements(&an->tx_ringbuf) : 0;
376  int b_packet_count = bn ? ringbufindex_elements(&bn->tx_ringbuf) : 0;
377  /* Compare the number of packets in the queue */
378  return a_packet_count >= b_packet_count ? a : b;
379  }
380 
381  /* Same neighbor address; simply return the first link */
382  return a;
383 }
384 
385 /*---------------------------------------------------------------------------*/
386 /* Returns the next active link after a given ASN, and a backup link (for the same ASN, with Rx flag) */
387 struct tsch_link *
388 tsch_schedule_get_next_active_link(struct tsch_asn_t *asn, uint16_t *time_offset,
389  struct tsch_link **backup_link)
390 {
391  uint16_t time_to_curr_best = 0;
392  struct tsch_link *curr_best = NULL;
393  struct tsch_link *curr_backup = NULL; /* Keep a back link in case the current link
394  turns out useless when the time comes. For instance, for a Tx-only link, if there is
395  no outgoing packet in queue. In that case, run the backup link instead. The backup link
396  must have Rx flag set. */
397  if(!tsch_is_locked()) {
398  struct tsch_slotframe *sf = list_head(slotframe_list);
399  /* For each slotframe, look for the earliest occurring link */
400  while(sf != NULL) {
401  /* Get timeslot from ASN, given the slotframe length */
402  uint16_t timeslot = TSCH_ASN_MOD(*asn, sf->size);
403  struct tsch_link *l = list_head(sf->links_list);
404  while(l != NULL) {
405  uint16_t time_to_timeslot =
406  l->timeslot > timeslot ?
407  l->timeslot - timeslot :
408  sf->size.val + l->timeslot - timeslot;
409  if(curr_best == NULL || time_to_timeslot < time_to_curr_best) {
410  time_to_curr_best = time_to_timeslot;
411  curr_best = l;
412  curr_backup = NULL;
413  } else if(time_to_timeslot == time_to_curr_best) {
414  struct tsch_link *new_best = NULL;
415  /* Two links are overlapping, we need to select one of them.
416  * By standard: prioritize Tx links first, second by lowest handle */
417  if((curr_best->link_options & LINK_OPTION_TX) == (l->link_options & LINK_OPTION_TX)) {
418  /* Both or neither links have Tx, select the one with lowest handle */
419  if(l->slotframe_handle != curr_best->slotframe_handle) {
420  if(l->slotframe_handle < curr_best->slotframe_handle) {
421  new_best = l;
422  }
423  } else {
424  /* compare the link against the current best link and return the newly selected one */
425  new_best = TSCH_LINK_COMPARATOR(curr_best, l);
426  }
427  } else {
428  /* Select the link that has the Tx option */
429  if(l->link_options & LINK_OPTION_TX) {
430  new_best = l;
431  }
432  }
433 
434  /* Maintain backup_link */
435  if(curr_backup == NULL) {
436  /* Check if 'l' best can be used as backup */
437  if(new_best != l && (l->link_options & LINK_OPTION_RX)) { /* Does 'l' have Rx flag? */
438  curr_backup = l;
439  }
440  /* Check if curr_best can be used as backup */
441  if(new_best != curr_best && (curr_best->link_options & LINK_OPTION_RX)) { /* Does curr_best have Rx flag? */
442  curr_backup = curr_best;
443  }
444  }
445 
446  /* Maintain curr_best */
447  if(new_best != NULL) {
448  curr_best = new_best;
449  }
450  }
451 
452  l = list_item_next(l);
453  }
454  sf = list_item_next(sf);
455  }
456  if(time_offset != NULL) {
457  *time_offset = time_to_curr_best;
458  }
459  }
460  if(backup_link != NULL) {
461  *backup_link = curr_backup;
462  }
463  return curr_best;
464 }
465 /*---------------------------------------------------------------------------*/
466 /* Module initialization, call only once at startup. Returns 1 is success, 0 if failure. */
467 int
469 {
470  if(tsch_get_lock()) {
471  memb_init(&link_memb);
472  memb_init(&slotframe_memb);
473  list_init(slotframe_list);
475  return 1;
476  } else {
477  return 0;
478  }
479 }
480 /*---------------------------------------------------------------------------*/
481 /* Create a 6TiSCH minimal schedule */
482 void
484 {
485  struct tsch_slotframe *sf_min;
486  /* First, empty current schedule */
488  /* Build 6TiSCH minimal schedule.
489  * We pick a slotframe length of TSCH_SCHEDULE_DEFAULT_LENGTH */
490  sf_min = tsch_schedule_add_slotframe(0, TSCH_SCHEDULE_DEFAULT_LENGTH);
491  /* Add a single Tx|Rx|Shared slot using broadcast address (i.e. usable for unicast and broadcast).
492  * We set the link type to advertising, which is not compliant with 6TiSCH minimal schedule
493  * but is required according to 802.15.4e if also used for EB transmission.
494  * Timeslot: 0, channel offset: 0. */
495  tsch_schedule_add_link(sf_min,
496  (LINK_OPTION_RX | LINK_OPTION_TX | LINK_OPTION_SHARED | LINK_OPTION_TIME_KEEPING),
497  LINK_TYPE_ADVERTISING, &tsch_broadcast_address,
498  0, 0);
499 }
500 /*---------------------------------------------------------------------------*/
501 struct tsch_slotframe *
503 {
504  return list_head(slotframe_list);
505 }
506 /*---------------------------------------------------------------------------*/
507 struct tsch_slotframe *
509 {
510  return list_item_next(sf);
511 }
512 /*---------------------------------------------------------------------------*/
513 /* Prints out the current schedule (all slotframes and links) */
514 void
516 {
517  if(!tsch_is_locked()) {
518  struct tsch_slotframe *sf = list_head(slotframe_list);
519 
520  LOG_PRINT("----- start slotframe list -----\n");
521 
522  while(sf != NULL) {
523  struct tsch_link *l = list_head(sf->links_list);
524 
525  LOG_PRINT("Slotframe Handle %u, size %u\n", sf->handle, sf->size.val);
526 
527  while(l != NULL) {
528  LOG_PRINT("* Link Options %02x, type %u, timeslot %u, channel offset %u, address %u\n",
529  l->link_options, l->link_type, l->timeslot, l->channel_offset, l->addr.u8[7]);
530  l = list_item_next(l);
531  }
532 
533  sf = list_item_next(sf);
534  }
535 
536  LOG_PRINT("----- end slotframe list -----\n");
537  }
538 }
539 /*---------------------------------------------------------------------------*/
540 /** @} */
#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_neighbor * tsch_queue_get_nbr(const linkaddr_t *addr)
Get a TSCH neighbor.
Definition: tsch-queue.c:108
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
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.
int tsch_schedule_remove_link_by_timeslot(struct tsch_slotframe *slotframe, uint16_t timeslot, uint16_t channel_offset)
Removes a link from a slotframe and timeslot.
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
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
int ringbufindex_elements(const struct ringbufindex *r)
Return the number of elements currently in the ring buffer.
Definition: ringbufindex.c:134
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
int linkaddr_cmp(const linkaddr_t *addr1, const linkaddr_t *addr2)
Compare two link-layer addresses.
Definition: linkaddr.c:69
void list_init(list_t list)
Initialize a list.
Definition: list.c:65
struct tsch_link * tsch_schedule_get_link_by_timeslot(struct tsch_slotframe *slotframe, uint16_t timeslot, uint16_t channel_offset)
Looks within a slotframe for a link with a given timeslot.
#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