/* Adds and returns a slotframe (NULL if failure) */ struct tsch_slotframe * tsch_schedule_add_slotframe(uint16_t handle, uint16_t size) { if(size == 0) { return NULL; } if(tsch_schedule_get_slotframe_by_handle(handle)) { /* A slotframe with this handle already exists */ return NULL; } if(tsch_get_lock()) { struct tsch_slotframe *sf = memb_alloc(&slotframe_memb); if(sf != NULL) { /* Initialize the slotframe */ sf->handle = handle; ASN_DIVISOR_INIT(sf->size, size); LIST_STRUCT_INIT(sf, links_list); /* Add the slotframe to the global list */ list_add(slotframe_list, sf); } PRINTF("TSCH-schedule: add_slotframe %u %u\n", handle, size); tsch_release_lock(); return sf; } return NULL; }
/* Add a TSCH neighbor */ struct tsch_neighbor * tsch_queue_add_nbr(const linkaddr_t *addr) { struct tsch_neighbor *n = NULL; /* If we have an entry for this neighbor already, we simply update it */ n = tsch_queue_get_nbr(addr); if(n == NULL) { if(tsch_get_lock()) { /* Allocate a neighbor */ n = memb_alloc(&neighbor_memb); if(n != NULL) { /* Initialize neighbor entry */ memset(n, 0, sizeof(struct tsch_neighbor)); ringbufindex_init(&n->tx_ringbuf, TSCH_QUEUE_NUM_PER_NEIGHBOR); linkaddr_copy(&n->addr, addr); n->is_broadcast = linkaddr_cmp(addr, &tsch_eb_address) || linkaddr_cmp(addr, &tsch_broadcast_address); tsch_queue_backoff_reset(n); /* Add neighbor to the list */ list_add(neighbor_list, n); } tsch_release_lock(); } } return n; }
/* Adds a link to a slotframe, return a pointer to it (NULL if failure) */ 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) { struct tsch_link *l = NULL; if(slotframe != NULL) { /* We currently support only one link per timeslot in a given slotframe. */ /* Start with removing the link currently installed at this timeslot (needed * to keep neighbor state in sync with link options etc.) */ tsch_schedule_remove_link_by_timeslot(slotframe, timeslot); if(!tsch_get_lock()) { PRINTF("TSCH-schedule:! add_link memb_alloc couldn't take lock\n"); } else { l = memb_alloc(&link_memb); if(l == NULL) { PRINTF("TSCH-schedule:! add_link memb_alloc failed\n"); } else { static int current_link_handle = 0; struct tsch_neighbor *n; /* Add the link to the slotframe */ list_add(slotframe->links_list, l); /* Initialize link */ l->handle = current_link_handle++; l->link_options = link_options; l->link_type = link_type; l->slotframe_handle = slotframe->handle; l->timeslot = timeslot; l->channel_offset = channel_offset; l->data = NULL; if(address == NULL) { address = &linkaddr_null; } linkaddr_copy(&l->addr, address); PRINTF("TSCH-schedule: add_link %u %u %u %u %u %u\n", slotframe->handle, link_options, link_type, timeslot, channel_offset, TSCH_LOG_ID_FROM_LINKADDR(address)); /* Release the lock before we update the neighbor (will take the lock) */ tsch_release_lock(); if(l->link_options & LINK_OPTION_TX) { n = tsch_queue_add_nbr(&l->addr); /* We have a tx link to this neighbor, update counters */ if(n != NULL) { n->tx_links_count++; if(!(l->link_options & LINK_OPTION_SHARED)) { n->dedicated_tx_links_count++; } } } } } } return l; }
/* Module initialization, call only once at startup. Returns 1 is success, 0 if failure. */ int tsch_schedule_init(void) { if(tsch_get_lock()) { memb_init(&link_memb); memb_init(&slotframe_memb); list_init(slotframe_list); tsch_release_lock(); return 1; } else { return 0; } }
/* Removes a link from slotframe. Return 1 if success, 0 if failure */ int tsch_schedule_remove_link(struct tsch_slotframe *slotframe, struct tsch_link *l) { if(slotframe != NULL && l != NULL && l->slotframe_handle == slotframe->handle) { if(tsch_get_lock()) { uint8_t link_options; linkaddr_t addr; /* Save link option and addr in local variables as we need them * after freeing the link */ link_options = l->link_options; linkaddr_copy(&addr, &l->addr); /* The link to be removed is scheduled as next, set it to NULL * to abort the next link operation */ if(l == current_link) { current_link = NULL; } PRINTF("TSCH-schedule: remove_link %u %u %u %u %u\n", slotframe->handle, l->link_options, l->timeslot, l->channel_offset, TSCH_LOG_ID_FROM_LINKADDR(&l->addr)); list_remove(slotframe->links_list, l); memb_free(&link_memb, l); /* Release the lock before we update the neighbor (will take the lock) */ tsch_release_lock(); /* This was a tx link to this neighbor, update counters */ if(link_options & LINK_OPTION_TX) { struct tsch_neighbor *n = tsch_queue_add_nbr(&addr); if(n != NULL) { n->tx_links_count--; if(!(link_options & LINK_OPTION_SHARED)) { n->dedicated_tx_links_count--; } } } return 1; } else { PRINTF("TSCH-schedule:! remove_link memb_alloc couldn't take lock\n"); } } return 0; }
/* Remove TSCH neighbor queue */ static void tsch_queue_remove_nbr(struct tsch_neighbor *n) { if(n != NULL) { if(tsch_get_lock()) { /* Remove neighbor from list */ list_remove(neighbor_list, n); tsch_release_lock(); /* Flush queue */ tsch_queue_flush_nbr_queue(n); /* Free neighbor */ memb_free(&neighbor_memb, n); } } }
/* Removes a slotframe Return 1 if success, 0 if failure */ int tsch_schedule_remove_slotframe(struct tsch_slotframe *slotframe) { if(slotframe != NULL) { /* Remove all links belonging to this slotframe */ struct tsch_link *l; while((l = list_head(slotframe->links_list))) { tsch_schedule_remove_link(slotframe, l); } /* Now that the slotframe has no links, remove it. */ if(tsch_get_lock()) { PRINTF("TSCH-schedule: remove slotframe %u %u\n", slotframe->handle, slotframe->size.val); memb_free(&slotframe_memb, slotframe); list_remove(slotframe_list, slotframe); tsch_release_lock(); return 1; } } return 0; }