Contiki-NG
Loading...
Searching...
No Matches
tsch.c
Go to the documentation of this file.
1/*
2 * Copyright (c) 2015, 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 implementation.
36 * Does not use any RDC layer. Should be used with nordc.
37 * \author
38 * Simon Duquennoy <simonduq@sics.se>
39 * Beshr Al Nahas <beshr@sics.se>
40 *
41 */
42
43/**
44 * \addtogroup tsch
45 * @{
46*/
47
48#include "contiki.h"
49#include "dev/radio.h"
50#include "net/netstack.h"
51#include "net/packetbuf.h"
52#include "net/queuebuf.h"
53#include "net/nbr-table.h"
54#include "net/link-stats.h"
56#include "net/mac/tsch/tsch.h"
58#include "lib/random.h"
59#include "net/routing/routing.h"
60#include <inttypes.h>
61
62#if TSCH_WITH_SIXTOP
64#endif
65
66#if FRAME802154_VERSION < FRAME802154_IEEE802154_2015
67#error TSCH: FRAME802154_VERSION must be at least FRAME802154_IEEE802154_2015
68#endif
69
70/* Log configuration */
71#include "sys/log.h"
72#define LOG_MODULE "TSCH"
73#define LOG_LEVEL LOG_LEVEL_MAC
74
75/* The address of the last node we received an EB from (other than our time source).
76 * Used for recovery */
77static linkaddr_t last_eb_nbr_addr;
78/* The join priority advertised by last_eb_nbr_addr */
79static uint8_t last_eb_nbr_jp;
80
81/* Let TSCH select a time source with no help of an upper layer.
82 * We do so using statistics from incoming EBs */
83#if TSCH_AUTOSELECT_TIME_SOURCE
84int best_neighbor_eb_count;
85struct eb_stat {
86 int rx_count;
87 int jp;
88};
89NBR_TABLE(struct eb_stat, eb_stats);
90#endif /* TSCH_AUTOSELECT_TIME_SOURCE */
91
92/* TSCH channel hopping sequence */
93uint8_t tsch_hopping_sequence[TSCH_HOPPING_SEQUENCE_MAX_LEN];
94struct tsch_asn_divisor_t tsch_hopping_sequence_length;
95
96/* Default TSCH timeslot timing (in micro-second) */
97static const uint16_t *tsch_default_timing_us;
98/* TSCH timeslot timing (in micro-second) */
99uint16_t tsch_timing_us[tsch_ts_elements_count];
100/* TSCH timeslot timing (in rtimer ticks) */
101rtimer_clock_t tsch_timing[tsch_ts_elements_count];
102
103#if LINKADDR_SIZE == 8
104/* 802.15.4 broadcast MAC address */
105const linkaddr_t tsch_broadcast_address = { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } };
106/* Address used for the EB virtual neighbor queue */
107const linkaddr_t tsch_eb_address = { { 0, 0, 0, 0, 0, 0, 0, 0 } };
108#else /* LINKADDR_SIZE == 8 */
109const linkaddr_t tsch_broadcast_address = { { 0xff, 0xff } };
110const linkaddr_t tsch_eb_address = { { 0, 0 } };
111#endif /* LINKADDR_SIZE == 8 */
112
113/* Is TSCH started? */
114int tsch_is_started = 0;
115/* Has TSCH initialization failed? */
116int tsch_is_initialized = 0;
117/* Are we coordinator of the TSCH network? */
118int tsch_is_coordinator = 0;
119/* Are we associated to a TSCH network? */
120int tsch_is_associated = 0;
121/* Total number of associations since boot */
122int tsch_association_count = 0;
123/* Is the PAN running link-layer security? */
124int tsch_is_pan_secured = LLSEC802154_ENABLED;
125/* The current Absolute Slot Number (ASN) */
126struct tsch_asn_t tsch_current_asn;
127/* Device rank or join priority:
128 * For PAN coordinator: 0 -- lower is better */
129uint8_t tsch_join_priority;
130/* Current period for EB output */
131static clock_time_t tsch_current_eb_period;
132/* Current period for keepalive output */
133static clock_time_t tsch_current_ka_timeout;
134
135/* For scheduling keepalive messages */
136enum tsch_keepalive_status {
137 KEEPALIVE_SCHEDULING_UNCHANGED,
138 KEEPALIVE_SCHEDULE_OR_STOP,
139 KEEPALIVE_SEND_IMMEDIATELY,
140};
141/* Should we send or schedule a keepalive? */
142static volatile enum tsch_keepalive_status keepalive_status;
143
144/* timer for sending keepalive messages */
145static struct ctimer keepalive_timer;
146
147/* Statistics on the current session */
148unsigned long tx_count;
149unsigned long rx_count;
150unsigned long sync_count;
151int32_t min_drift_seen;
152int32_t max_drift_seen;
153
154/* TSCH processes and protothreads */
155PT_THREAD(tsch_scan(struct pt *pt));
156PROCESS(tsch_process, "main process");
157PROCESS(tsch_send_eb_process, "send EB process");
158PROCESS(tsch_pending_events_process, "pending events process");
159
160/* Other function prototypes */
161static void packet_input(void);
162
163/* Getters and setters */
164
165/*---------------------------------------------------------------------------*/
166void
168{
169 if(tsch_is_coordinator != enable) {
170 tsch_is_associated = 0;
171 }
172 tsch_is_coordinator = enable;
173 tsch_set_eb_period(TSCH_EB_PERIOD);
174 tsch_roots_set_self_to_root(tsch_is_coordinator ? 1 : 0);
175}
176/*---------------------------------------------------------------------------*/
177void
179{
180 tsch_is_pan_secured = LLSEC802154_ENABLED && enable;
181}
182/*---------------------------------------------------------------------------*/
183void
185{
186 tsch_join_priority = jp;
187}
188/*---------------------------------------------------------------------------*/
189void
190tsch_set_ka_timeout(uint32_t timeout)
191{
192 tsch_current_ka_timeout = timeout;
194}
195/*---------------------------------------------------------------------------*/
196void
197tsch_set_eb_period(uint32_t period)
198{
199 tsch_current_eb_period = MIN(period, TSCH_MAX_EB_PERIOD);
200}
201/*---------------------------------------------------------------------------*/
202static void
203tsch_reset(void)
204{
205 int i;
206 frame802154_set_pan_id(0xffff);
207 /* First make sure pending packet callbacks are sent etc */
208 process_post_synch(&tsch_pending_events_process, PROCESS_EVENT_POLL, NULL);
209 /* Reset neighbor queues */
211 /* Remove unused neighbors */
214 /* Initialize global variables */
215 tsch_join_priority = 0xff;
216 TSCH_ASN_INIT(tsch_current_asn, 0, 0);
217 current_link = NULL;
218 /* Reset timeslot timing to defaults */
219 tsch_default_timing_us = TSCH_DEFAULT_TIMESLOT_TIMING;
220 for(i = 0; i < tsch_ts_elements_count; i++) {
221 tsch_timing_us[i] = tsch_default_timing_us[i];
222 tsch_timing[i] = US_TO_RTIMERTICKS(tsch_timing_us[i]);
223 }
224#ifdef TSCH_CALLBACK_LEAVING_NETWORK
225 TSCH_CALLBACK_LEAVING_NETWORK();
226#endif
227 linkaddr_copy(&last_eb_nbr_addr, &linkaddr_null);
228#if TSCH_AUTOSELECT_TIME_SOURCE
229 struct eb_stat *stat;
230 best_neighbor_eb_count = 0;
231 /* Remove all nbr stats */
232 stat = nbr_table_head(eb_stats);
233 while(stat != NULL) {
234 nbr_table_remove(eb_stats, stat);
235 stat = nbr_table_next(eb_stats, stat);
236 }
237#endif /* TSCH_AUTOSELECT_TIME_SOURCE */
238 tsch_set_eb_period(TSCH_EB_PERIOD);
239 keepalive_status = KEEPALIVE_SCHEDULING_UNCHANGED;
240}
241/* TSCH keep-alive functions */
242
243/*---------------------------------------------------------------------------*/
244/* Resynchronize to last_eb_nbr.
245 * Return non-zero if this function schedules the next keepalive.
246 * Return zero otherwise.
247 */
248static int
249resynchronize(const linkaddr_t *original_time_source_addr)
250{
251 const struct tsch_neighbor *current_time_source = tsch_queue_get_time_source();
252 const linkaddr_t *ts_addr = tsch_queue_get_nbr_address(current_time_source);
253 if(ts_addr != NULL && !linkaddr_cmp(ts_addr, original_time_source_addr)) {
254 /* Time source has already been changed (e.g. by RPL). Let's see if it works. */
255 LOG_INFO("time source has been changed to ");
256 LOG_INFO_LLADDR(ts_addr);
257 LOG_INFO_("\n");
258 return 0;
259 }
260 /* Switch time source to the last neighbor we received an EB from */
261 if(linkaddr_cmp(&last_eb_nbr_addr, &linkaddr_null)) {
262 LOG_WARN("not able to re-synchronize, received no EB from other neighbors\n");
263 if(sync_count == 0) {
264 /* We got no synchronization at all in this session, leave the network */
266 }
267 return 0;
268 } else {
269 LOG_WARN("re-synchronizing on ");
270 LOG_WARN_LLADDR(&last_eb_nbr_addr);
271 LOG_WARN_("\n");
272 /* We simply pick the last neighbor we receiver sync information from */
273 tsch_queue_update_time_source(&last_eb_nbr_addr);
274 tsch_join_priority = last_eb_nbr_jp + 1;
275 linkaddr_copy(&last_eb_nbr_addr, &linkaddr_null);
276 /* Try to get in sync ASAP */
278 return 1;
279 }
280}
281
282/*---------------------------------------------------------------------------*/
283/* Tx callback for keepalive messages */
284static void
285keepalive_packet_sent(void *ptr, int status, int transmissions)
286{
287 int schedule_next_keepalive = 1;
288 /* Update neighbor link statistics */
289 link_stats_packet_sent(packetbuf_addr(PACKETBUF_ADDR_RECEIVER), status, transmissions);
290 /* Call RPL callback if RPL is enabled */
291#ifdef TSCH_CALLBACK_KA_SENT
292 TSCH_CALLBACK_KA_SENT(status, transmissions);
293#endif /* TSCH_CALLBACK_KA_SENT */
294 LOG_INFO("KA sent to ");
295 LOG_INFO_LLADDR(packetbuf_addr(PACKETBUF_ADDR_RECEIVER));
296 LOG_INFO_(", st %d-%d\n", status, transmissions);
297
298 /* We got no ack, try to resynchronize */
299 if(status == MAC_TX_NOACK) {
300 schedule_next_keepalive = !resynchronize(packetbuf_addr(PACKETBUF_ADDR_RECEIVER));
301 }
302
303 if(schedule_next_keepalive) {
305 }
306}
307/*---------------------------------------------------------------------------*/
308/* Prepare and send a keepalive message */
309static void
310keepalive_send(void *ptr)
311{
312 /* If not here from a timer callback, the timer must be stopped */
313 ctimer_stop(&keepalive_timer);
314
315 if(tsch_is_associated) {
317 if(n != NULL) {
318 linkaddr_t *destination = tsch_queue_get_nbr_address(n);
319 /* Simply send an empty packet */
321 packetbuf_set_addr(PACKETBUF_ADDR_RECEIVER, destination);
322 NETSTACK_MAC.send(keepalive_packet_sent, NULL);
323 LOG_INFO("sending KA to ");
324 LOG_INFO_LLADDR(destination);
325 LOG_INFO_("\n");
326 } else {
327 LOG_ERR("no timesource - KA not sent\n");
328 }
329 }
330}
331/*---------------------------------------------------------------------------*/
332void
334{
335 if(immediate) {
336 /* send as soon as possible */
337 keepalive_status = KEEPALIVE_SEND_IMMEDIATELY;
338 } else if(keepalive_status != KEEPALIVE_SEND_IMMEDIATELY) {
339 /* send based on the tsch_current_ka_timeout */
340 keepalive_status = KEEPALIVE_SCHEDULE_OR_STOP;
341 }
342 process_poll(&tsch_pending_events_process);
343}
344/*---------------------------------------------------------------------------*/
345static void
346tsch_keepalive_process_pending(void)
347{
348 if(keepalive_status != KEEPALIVE_SCHEDULING_UNCHANGED) {
349 /* first, save and reset the old status */
350 enum tsch_keepalive_status scheduled_status = keepalive_status;
351 keepalive_status = KEEPALIVE_SCHEDULING_UNCHANGED;
352
353 if(!tsch_is_coordinator && tsch_is_associated) {
354 switch(scheduled_status) {
355 case KEEPALIVE_SEND_IMMEDIATELY:
356 /* always send, and as soon as possible (now) */
357 keepalive_send(NULL);
358 break;
359
360 case KEEPALIVE_SCHEDULE_OR_STOP:
361 if(tsch_current_ka_timeout > 0) {
362 /* Pick a delay in the range [tsch_current_ka_timeout*0.9, tsch_current_ka_timeout[ */
363 unsigned long delay;
364 if(tsch_current_ka_timeout >= 10) {
365 delay = (tsch_current_ka_timeout - tsch_current_ka_timeout / 10)
366 + random_rand() % (tsch_current_ka_timeout / 10);
367 } else {
368 delay = tsch_current_ka_timeout - 1;
369 }
370 ctimer_set(&keepalive_timer, delay, keepalive_send, NULL);
371 } else {
372 /* zero timeout set, stop sending keepalives */
373 ctimer_stop(&keepalive_timer);
374 }
375 break;
376
377 default:
378 break;
379 }
380 } else {
381 /* either coordinator or not associated */
382 ctimer_stop(&keepalive_timer);
383 }
384 }
385}
386/*---------------------------------------------------------------------------*/
387static void
388eb_input(struct input_packet *current_input)
389{
390 /* LOG_INFO("EB received\n"); */
391 frame802154_t frame;
392 /* Verify incoming EB (does its ASN match our Rx time?),
393 * and update our join priority. */
394 struct ieee802154_ies eb_ies;
395
396 if(tsch_packet_parse_eb(current_input->payload, current_input->len,
397 &frame, &eb_ies, NULL, 1)) {
398 /* PAN ID check and authentication done at rx time */
399
400 /* Got an EB from a different neighbor than our time source, keep enough data
401 * to switch to it in case we lose the link to our time source */
403 linkaddr_t *ts_addr = tsch_queue_get_nbr_address(ts);
404 if(ts_addr == NULL || !linkaddr_cmp((linkaddr_t *)&frame.src_addr, ts_addr)) {
405 linkaddr_copy(&last_eb_nbr_addr, (linkaddr_t *)&frame.src_addr);
406 last_eb_nbr_jp = eb_ies.ie_join_priority;
407 }
408
409#if TSCH_AUTOSELECT_TIME_SOURCE
410 if(!tsch_is_coordinator) {
411 /* Maintain EB received counter for every neighbor */
412 struct eb_stat *stat = (struct eb_stat *)nbr_table_get_from_lladdr(eb_stats, (linkaddr_t *)&frame.src_addr);
413 if(stat == NULL) {
414 stat = (struct eb_stat *)nbr_table_add_lladdr(eb_stats, (linkaddr_t *)&frame.src_addr, NBR_TABLE_REASON_MAC, NULL);
415 }
416 if(stat != NULL) {
417 stat->rx_count++;
418 stat->jp = eb_ies.ie_join_priority;
419 best_neighbor_eb_count = MAX(best_neighbor_eb_count, stat->rx_count);
420 }
421 /* Select best time source */
422 struct eb_stat *best_stat = NULL;
423 stat = nbr_table_head(eb_stats);
424 while(stat != NULL) {
425 /* Is neighbor eligible as a time source? */
426 if(stat->rx_count > best_neighbor_eb_count / 2) {
427 if(best_stat == NULL ||
428 stat->jp < best_stat->jp) {
429 best_stat = stat;
430 }
431 }
432 stat = nbr_table_next(eb_stats, stat);
433 }
434 /* Update time source */
435 if(best_stat != NULL) {
436 tsch_queue_update_time_source(nbr_table_get_lladdr(eb_stats, best_stat));
437 tsch_join_priority = best_stat->jp + 1;
438 }
439 }
440#endif /* TSCH_AUTOSELECT_TIME_SOURCE */
441
442 /* If this EB is coming from the root, add it to the root list */
443 if(eb_ies.ie_join_priority == 0) {
444 tsch_roots_add_address((linkaddr_t *)&frame.src_addr);
445 }
446
447 /* Did the EB come from our time source? */
448 if(ts_addr != NULL && linkaddr_cmp((linkaddr_t *)&frame.src_addr, ts_addr)) {
449 /* Check for ASN drift */
450 int32_t asn_diff = TSCH_ASN_DIFF(current_input->rx_asn, eb_ies.ie_asn);
451 if(asn_diff != 0) {
452 /* We disagree with our time source's ASN -- leave the network */
453 LOG_WARN("! ASN drifted by %"PRId32", leaving the network\n", asn_diff);
455 }
456
457 if(eb_ies.ie_join_priority >= TSCH_MAX_JOIN_PRIORITY) {
458 /* Join priority unacceptable. Leave network. */
459 LOG_WARN("! EB JP too high %u, leaving the network\n",
460 eb_ies.ie_join_priority);
462 } else {
463#if TSCH_AUTOSELECT_TIME_SOURCE
464 /* Update join priority */
465 if(tsch_join_priority != eb_ies.ie_join_priority + 1) {
466 LOG_INFO("update JP from EB %u -> %u\n",
467 tsch_join_priority, eb_ies.ie_join_priority + 1);
468 tsch_join_priority = eb_ies.ie_join_priority + 1;
469 }
470#endif /* TSCH_AUTOSELECT_TIME_SOURCE */
471 }
472
473 /* TSCH hopping sequence */
474 if(eb_ies.ie_channel_hopping_sequence_id != 0) {
475 if(eb_ies.ie_hopping_sequence_len != tsch_hopping_sequence_length.val
476 || memcmp((uint8_t *)tsch_hopping_sequence, eb_ies.ie_hopping_sequence_list, tsch_hopping_sequence_length.val)) {
477 if(eb_ies.ie_hopping_sequence_len <= sizeof(tsch_hopping_sequence)) {
478 memcpy((uint8_t *)tsch_hopping_sequence, eb_ies.ie_hopping_sequence_list,
479 eb_ies.ie_hopping_sequence_len);
480 TSCH_ASN_DIVISOR_INIT(tsch_hopping_sequence_length, eb_ies.ie_hopping_sequence_len);
481
482 LOG_WARN("Updating TSCH hopping sequence from EB\n");
483 } else {
484 LOG_WARN("parse_eb: Hopping sequence too long (%u)\n",
485 eb_ies.ie_hopping_sequence_len);
486 }
487 }
488 }
489 }
490 }
491}
492/*---------------------------------------------------------------------------*/
493/* Process pending input packet(s) */
494static void
495tsch_rx_process_pending()
496{
497 int16_t input_index;
498 /* Loop on accessing (without removing) a pending output packet */
499 while((input_index = ringbufindex_peek_get(&input_ringbuf)) != -1) {
500 struct input_packet *current_input = &input_array[input_index];
501 frame802154_t frame;
502 uint8_t ret = frame802154_parse(current_input->payload, current_input->len, &frame);
503 int is_data = ret && frame.fcf.frame_type == FRAME802154_DATAFRAME;
504 int is_eb = ret
505 && frame.fcf.frame_version == FRAME802154_IEEE802154_2015
506 && frame.fcf.frame_type == FRAME802154_BEACONFRAME;
507
508 if(is_data) {
509 /* Copy payload to packetbuf for processing */
510 packetbuf_copyfrom(current_input->payload, current_input->len);
511 packetbuf_set_attr(PACKETBUF_ATTR_RSSI, current_input->rssi);
512 packetbuf_set_attr(PACKETBUF_ATTR_CHANNEL, current_input->channel);
513
514 /* Pass to upper layers */
515 packet_input();
516
517 } else if(is_eb) {
518 /* Don't pass to upper layers, but still count it in link stats */
519 packetbuf_set_attr(PACKETBUF_ATTR_RSSI, current_input->rssi);
520 packetbuf_set_attr(PACKETBUF_ATTR_CHANNEL, current_input->channel);
521 link_stats_input_callback((const linkaddr_t *)frame.src_addr);
522
523 /* Process EB without copying the payload to packetbuf */
524 eb_input(current_input);
525 }
526
527 /* Remove input from ringbuf */
528 ringbufindex_get(&input_ringbuf);
529 }
530}
531/*---------------------------------------------------------------------------*/
532/* Pass sent packets to upper layer */
533static void
534tsch_tx_process_pending(void)
535{
536 uint16_t num_packets_freed = 0;
537 int16_t dequeued_index;
538 /* Loop on accessing (without removing) a pending input packet */
539 while((dequeued_index = ringbufindex_peek_get(&dequeued_ringbuf)) != -1) {
540 struct tsch_packet *p = dequeued_array[dequeued_index];
541 /* Put packet into packetbuf for packet_sent callback */
542 queuebuf_to_packetbuf(p->qb);
543 LOG_INFO("packet sent to ");
544 LOG_INFO_LLADDR(packetbuf_addr(PACKETBUF_ADDR_RECEIVER));
545 LOG_INFO_(", seqno %u, status %d, tx %d\n",
546 packetbuf_attr(PACKETBUF_ATTR_MAC_SEQNO), p->ret, p->transmissions);
547 /* Call packet_sent callback */
548 mac_call_sent_callback(p->sent, p->ptr, p->ret, p->transmissions);
549 /* Free packet queuebuf */
551 /* Remove dequeued packet from ringbuf */
552 ringbufindex_get(&dequeued_ringbuf);
553 num_packets_freed++;
554 }
555
556 if(num_packets_freed > 0) {
557 /* Free all unused neighbors */
559 }
560}
561/*---------------------------------------------------------------------------*/
562/* Setup TSCH as a coordinator */
563static void
564tsch_start_coordinator(void)
565{
566 frame802154_set_pan_id(IEEE802154_PANID);
567 /* Initialize hopping sequence as default */
568 memcpy(tsch_hopping_sequence, TSCH_DEFAULT_HOPPING_SEQUENCE, sizeof(TSCH_DEFAULT_HOPPING_SEQUENCE));
569 TSCH_ASN_DIVISOR_INIT(tsch_hopping_sequence_length, sizeof(TSCH_DEFAULT_HOPPING_SEQUENCE));
570#if TSCH_SCHEDULE_WITH_6TISCH_MINIMAL
572#endif
573
574 tsch_is_associated = 1;
575 tsch_join_priority = 0;
576
577 LOG_INFO("starting as coordinator, PAN ID %x, asn-%x.%"PRIx32"\n",
578 frame802154_get_pan_id(), tsch_current_asn.ms1b, tsch_current_asn.ls4b);
579
580 /* Start slot operation */
581 tsch_slot_operation_sync(RTIMER_NOW(), &tsch_current_asn);
582}
583/*---------------------------------------------------------------------------*/
584/* Leave the TSCH network */
585void
587{
588 if(tsch_is_associated == 1) {
589 tsch_is_associated = 0;
591 process_poll(&tsch_process);
592 }
593}
594/*---------------------------------------------------------------------------*/
595/* Attempt to associate to a network form an incoming EB */
596static int
597tsch_associate(const struct input_packet *input_eb, rtimer_clock_t timestamp)
598{
599 frame802154_t frame;
600 struct ieee802154_ies ies;
601 uint8_t hdrlen;
602 int i;
603
604 if(input_eb == NULL || tsch_packet_parse_eb(input_eb->payload, input_eb->len,
605 &frame, &ies, &hdrlen, 0) == 0) {
606 LOG_DBG("! failed to parse packet as EB while scanning (len %u)\n",
607 input_eb->len);
608 return 0;
609 }
610
611 tsch_current_asn = ies.ie_asn;
612 tsch_join_priority = ies.ie_join_priority + 1;
613
614#if TSCH_JOIN_SECURED_ONLY
615 if(frame.fcf.security_enabled == 0) {
616 LOG_ERR("! parse_eb: EB is not secured\n");
617 return 0;
618 }
619#endif /* TSCH_JOIN_SECURED_ONLY */
620#if LLSEC802154_ENABLED
621 if(!tsch_security_parse_frame(input_eb->payload, hdrlen,
622 input_eb->len - hdrlen - tsch_security_mic_len(&frame),
623 &frame, (linkaddr_t*)&frame.src_addr, &tsch_current_asn)) {
624 LOG_ERR("! parse_eb: failed to authenticate\n");
625 return 0;
626 }
627#endif /* LLSEC802154_ENABLED */
628
629#if !LLSEC802154_ENABLED
630 if(frame.fcf.security_enabled == 1) {
631 LOG_ERR("! parse_eb: we do not support security, but EB is secured\n");
632 return 0;
633 }
634#endif /* !LLSEC802154_ENABLED */
635
636#if TSCH_JOIN_MY_PANID_ONLY
637 /* Check if the EB comes from the PAN ID we expect */
638 if(frame.src_pid != IEEE802154_PANID) {
639 LOG_ERR("! parse_eb: PAN ID %x != %x\n", frame.src_pid, IEEE802154_PANID);
640 return 0;
641 }
642#endif /* TSCH_JOIN_MY_PANID_ONLY */
643
644 /* There was no join priority (or 0xff) in the EB, do not join */
645 if(ies.ie_join_priority == 0xff) {
646 LOG_ERR("! parse_eb: no join priority\n");
647 return 0;
648 }
649
650 /* TSCH timeslot timing */
651 for(i = 0; i < tsch_ts_elements_count; i++) {
652 if(ies.ie_tsch_timeslot_id == 0) {
653 tsch_timing_us[i] = tsch_default_timing_us[i];
654 } else {
655 tsch_timing_us[i] = ies.ie_tsch_timeslot[i];
656 }
657 tsch_timing[i] = US_TO_RTIMERTICKS(tsch_timing_us[i]);
658 }
659
660 /* TSCH hopping sequence */
661 if(ies.ie_channel_hopping_sequence_id == 0) {
662 memcpy(tsch_hopping_sequence, TSCH_DEFAULT_HOPPING_SEQUENCE, sizeof(TSCH_DEFAULT_HOPPING_SEQUENCE));
663 TSCH_ASN_DIVISOR_INIT(tsch_hopping_sequence_length, sizeof(TSCH_DEFAULT_HOPPING_SEQUENCE));
664 } else {
665 if(ies.ie_hopping_sequence_len <= sizeof(tsch_hopping_sequence)) {
666 memcpy(tsch_hopping_sequence, ies.ie_hopping_sequence_list, ies.ie_hopping_sequence_len);
667 TSCH_ASN_DIVISOR_INIT(tsch_hopping_sequence_length, ies.ie_hopping_sequence_len);
668 } else {
669 LOG_ERR("! parse_eb: hopping sequence too long (%u)\n", ies.ie_hopping_sequence_len);
670 return 0;
671 }
672 }
673
674#if TSCH_CHECK_TIME_AT_ASSOCIATION > 0
675 /* Divide by 4k and multiply again to avoid integer overflow */
676 uint32_t expected_asn = 4096 * TSCH_CLOCK_TO_SLOTS(clock_time() / 4096, tsch_timing_timeslot_length); /* Expected ASN based on our current time*/
677 int32_t asn_threshold = TSCH_CHECK_TIME_AT_ASSOCIATION * 60ul * TSCH_CLOCK_TO_SLOTS(CLOCK_SECOND, tsch_timing_timeslot_length);
678 int32_t asn_diff = (int32_t)tsch_current_asn.ls4b - expected_asn;
679 if(asn_diff > asn_threshold) {
680 LOG_ERR("! EB ASN rejected %lx %lx %ld\n",
681 tsch_current_asn.ls4b, expected_asn, asn_diff);
682 return 0;
683 }
684#endif
685
686#if TSCH_INIT_SCHEDULE_FROM_EB
687 /* Create schedule */
688 if(ies.ie_tsch_slotframe_and_link.num_slotframes == 0) {
689#if TSCH_SCHEDULE_WITH_6TISCH_MINIMAL
690 LOG_INFO("parse_eb: no schedule, setting up minimal schedule\n");
692#else
693 LOG_INFO("parse_eb: no schedule\n");
694#endif
695 } else {
696 /* First, empty current schedule */
698 /* We support only 0 or 1 slotframe in this IE */
699 int num_links = ies.ie_tsch_slotframe_and_link.num_links;
700 if(num_links <= FRAME802154E_IE_MAX_LINKS) {
701 int i;
703 ies.ie_tsch_slotframe_and_link.slotframe_handle,
704 ies.ie_tsch_slotframe_and_link.slotframe_size);
705 for(i = 0; i < num_links; i++) {
707 ies.ie_tsch_slotframe_and_link.links[i].link_options,
708 LINK_TYPE_ADVERTISING, &tsch_broadcast_address,
709 ies.ie_tsch_slotframe_and_link.links[i].timeslot,
710 ies.ie_tsch_slotframe_and_link.links[i].channel_offset, 1);
711 }
712 } else {
713 LOG_ERR("! parse_eb: too many links in schedule (%u)\n", num_links);
714 return 0;
715 }
716 }
717#endif /* TSCH_INIT_SCHEDULE_FROM_EB */
718
719 if(tsch_join_priority < TSCH_MAX_JOIN_PRIORITY) {
720 struct tsch_neighbor *n;
721
722 /* Add coordinator to list of neighbors, lock the entry */
723 n = tsch_queue_add_nbr((linkaddr_t *)&frame.src_addr);
724
725 if(n != NULL) {
726 tsch_queue_update_time_source((linkaddr_t *)&frame.src_addr);
727
728 /* Set PANID */
729 frame802154_set_pan_id(frame.src_pid);
730
731 /* Synchronize on EB */
732 tsch_slot_operation_sync(timestamp - tsch_timing[tsch_ts_tx_offset], &tsch_current_asn);
733
734 /* Update global flags */
735 tsch_is_associated = 1;
736 tsch_is_pan_secured = frame.fcf.security_enabled;
737 tx_count = 0;
738 rx_count = 0;
739 sync_count = 0;
740 min_drift_seen = 0;
741 max_drift_seen = 0;
742
743 /* Start sending keep-alives now that tsch_is_associated is set */
745
746 /* If this EB is coming from the root, add it to the root list */
747 if(ies.ie_join_priority == 0) {
748 tsch_roots_add_address((linkaddr_t *)&frame.src_addr);
749 }
750
751#ifdef TSCH_CALLBACK_JOINING_NETWORK
752 TSCH_CALLBACK_JOINING_NETWORK();
753#endif
754
755 tsch_association_count++;
756 LOG_INFO("association done (%u), sec %u, PAN ID %x, asn-%x.%"PRIx32", jp %u, timeslot id %u, hopping id %u, slotframe len %u with %u links, from ",
757 tsch_association_count,
758 tsch_is_pan_secured,
759 frame.src_pid,
760 tsch_current_asn.ms1b, tsch_current_asn.ls4b, tsch_join_priority,
761 ies.ie_tsch_timeslot_id,
762 ies.ie_channel_hopping_sequence_id,
763 ies.ie_tsch_slotframe_and_link.slotframe_size,
764 ies.ie_tsch_slotframe_and_link.num_links);
765 LOG_INFO_LLADDR((const linkaddr_t *)&frame.src_addr);
766 LOG_INFO_("\n");
767
768 return 1;
769 }
770 }
771 LOG_ERR("! did not associate.\n");
772 return 0;
773}
774/* Processes and protothreads used by TSCH */
775
776/*---------------------------------------------------------------------------*/
777/* Scanning protothread, called by tsch_process:
778 * Listen to different channels, and when receiving an EB,
779 * attempt to associate.
780 */
781PT_THREAD(tsch_scan(struct pt *pt))
782{
783 PT_BEGIN(pt);
784
785 static struct input_packet input_eb;
786 static struct etimer scan_timer;
787 /* Time when we started scanning on current_channel */
788 static clock_time_t current_channel_since;
789
790 TSCH_ASN_INIT(tsch_current_asn, 0, 0);
791
792 etimer_set(&scan_timer, MAX(1, CLOCK_SECOND / TSCH_ASSOCIATION_POLL_FREQUENCY));
793 current_channel_since = clock_time();
794
795 while(!tsch_is_associated && !tsch_is_coordinator) {
796 /* Hop to any channel offset */
797 static uint8_t current_channel = 0;
798
799 /* We are not coordinator, try to associate */
800 rtimer_clock_t t0;
801 int is_packet_pending = 0;
802 clock_time_t now_time = clock_time();
803
804 /* Switch to a (new) channel for scanning */
805 if(current_channel == 0 || now_time - current_channel_since > TSCH_CHANNEL_SCAN_DURATION) {
806 /* Pick a channel at random in TSCH_JOIN_HOPPING_SEQUENCE */
807 uint8_t scan_channel = TSCH_JOIN_HOPPING_SEQUENCE[
808 random_rand() % sizeof(TSCH_JOIN_HOPPING_SEQUENCE)];
809
810 NETSTACK_RADIO.set_value(RADIO_PARAM_CHANNEL, scan_channel);
811 current_channel = scan_channel;
812 LOG_INFO("scanning on channel %u\n", scan_channel);
813
814 current_channel_since = now_time;
815 }
816
817 /* Turn radio on and wait for EB */
818 NETSTACK_RADIO.on();
819
820 is_packet_pending = NETSTACK_RADIO.pending_packet();
821 if(!is_packet_pending && NETSTACK_RADIO.receiving_packet()) {
822 /* If we are currently receiving a packet, wait until end of reception */
823 t0 = RTIMER_NOW();
824 RTIMER_BUSYWAIT_UNTIL_ABS((is_packet_pending = NETSTACK_RADIO.pending_packet()), t0, RTIMER_SECOND / 100);
825 }
826
827 if(is_packet_pending) {
828 rtimer_clock_t t1;
829 /* Read packet */
830 input_eb.len = NETSTACK_RADIO.read(input_eb.payload, TSCH_PACKET_MAX_LEN);
831
832 if(input_eb.len > 0) {
833 /* Save packet timestamp */
834 NETSTACK_RADIO.get_object(RADIO_PARAM_LAST_PACKET_TIMESTAMP, &t0, sizeof(rtimer_clock_t));
835 t1 = RTIMER_NOW();
836
837 /* Parse EB and attempt to associate */
838 LOG_INFO("scan: received packet (%u bytes) on channel %u\n", input_eb.len, current_channel);
839
840 /* Sanity-check the timestamp */
841 if(ABS(RTIMER_CLOCK_DIFF(t0, t1)) < 2ul * RTIMER_SECOND) {
842 tsch_associate(&input_eb, t0);
843 } else {
844 LOG_WARN("scan: dropping packet, timestamp too far from current time %u %u\n",
845 (unsigned)t0,
846 (unsigned)t1
847 );
848 }
849 }
850 }
851
852 if(tsch_is_associated) {
853 /* End of association, turn the radio off */
854 NETSTACK_RADIO.off();
855 } else if(!tsch_is_coordinator) {
856 /* Go back to scanning */
857 etimer_restart(&scan_timer);
858 PT_WAIT_UNTIL(pt, etimer_expired(&scan_timer));
859 }
860 }
861
862 PT_END(pt);
863}
864
865/*---------------------------------------------------------------------------*/
866/* The main TSCH process */
867PROCESS_THREAD(tsch_process, ev, data)
868{
869 static struct pt scan_pt;
870
872
873 while(1) {
874
875 while(!tsch_is_associated) {
876 if(tsch_is_coordinator) {
877 /* We are coordinator, start operating now */
878 tsch_start_coordinator();
879 } else {
880 /* Start scanning, will attempt to join when receiving an EB */
881 PROCESS_PT_SPAWN(&scan_pt, tsch_scan(&scan_pt));
882 }
883 }
884
885 /* We are part of a TSCH network, start slot operation */
887
888 /* Yield our main process. Slot operation will re-schedule itself
889 * as long as we are associated */
890 PROCESS_YIELD_UNTIL(!tsch_is_associated);
891
892 LOG_WARN("leaving the network, stats: tx %lu, rx %lu, sync %lu\n",
893 tx_count, rx_count, sync_count);
894
895 /* Will need to re-synchronize */
896 tsch_reset();
897 }
898
899 PROCESS_END();
900}
901
902/*---------------------------------------------------------------------------*/
903/* A periodic process to send TSCH Enhanced Beacons (EB) */
904PROCESS_THREAD(tsch_send_eb_process, ev, data)
905{
906 static struct etimer eb_timer;
907
909
910 /* Wait until association */
911 etimer_set(&eb_timer, CLOCK_SECOND / 10);
912 while(!tsch_is_associated) {
914 etimer_reset(&eb_timer);
915 }
916
917 /* Set an initial delay except for coordinator, which should send an EB asap */
918 if(!tsch_is_coordinator) {
919 etimer_set(&eb_timer, TSCH_EB_PERIOD ? random_rand() % TSCH_EB_PERIOD : 0);
921 }
922
923 while(1) {
924 unsigned long delay;
925
926 if(!tsch_is_associated) {
927 LOG_DBG("skip sending EB: not joined a TSCH network\n");
928 } else if(tsch_current_eb_period <= 0) {
929 LOG_DBG("skip sending EB: EB period disabled\n");
930#ifdef TSCH_RPL_CHECK_DODAG_JOINED
931 } else if(!TSCH_RPL_CHECK_DODAG_JOINED()) {
932 /* Implementation section 6.3 of RFC 8180 */
933 LOG_DBG("skip sending EB: not joined a routing DAG\n");
934#endif /* TSCH_RPL_CHECK_DODAG_JOINED */
935 } else if(NETSTACK_ROUTING.is_in_leaf_mode()) {
936 /* don't send when in leaf mode */
937 LOG_DBG("skip sending EB: in the leaf mode\n");
938 } else if(tsch_queue_nbr_packet_count(n_eb) != 0) {
939 /* Enqueue EB only if there isn't already one in queue */
940 LOG_DBG("skip sending EB: already queued\n");
941 } else {
942 uint8_t hdr_len = 0;
943 uint8_t tsch_sync_ie_offset;
944 /* Prepare the EB packet and schedule it to be sent */
945 if(tsch_packet_create_eb(&hdr_len, &tsch_sync_ie_offset) > 0) {
946 struct tsch_packet *p;
947 /* Enqueue EB packet, for a single transmission only */
948 if(!(p = tsch_queue_add_packet(&tsch_eb_address, 1, NULL, NULL))) {
949 LOG_ERR("! could not enqueue EB packet\n");
950 } else {
951 LOG_INFO("Enqueuing EB packet %u %u\n",
953 p->tsch_sync_ie_offset = tsch_sync_ie_offset;
954 p->header_len = hdr_len;
955 }
956 }
957 }
958 if(tsch_current_eb_period > 0) {
959 /* Next EB transmission with a random delay
960 * within [tsch_current_eb_period*0.75, tsch_current_eb_period[ */
961 delay = (tsch_current_eb_period - tsch_current_eb_period / 4)
962 + random_rand() % (tsch_current_eb_period / 4);
963 } else {
964 delay = TSCH_EB_PERIOD;
965 }
966 etimer_set(&eb_timer, delay);
968 }
969 PROCESS_END();
970}
971
972/*---------------------------------------------------------------------------*/
973/* A process that is polled from interrupt and calls tx/rx input
974 * callbacks, outputs pending logs. */
975PROCESS_THREAD(tsch_pending_events_process, ev, data)
976{
978 while(1) {
979 PROCESS_YIELD_UNTIL(ev == PROCESS_EVENT_POLL);
980 tsch_rx_process_pending();
981 tsch_tx_process_pending();
983 tsch_keepalive_process_pending();
984#ifdef TSCH_CALLBACK_SELECT_CHANNELS
985 TSCH_CALLBACK_SELECT_CHANNELS();
986#endif
987 }
988 PROCESS_END();
989}
990
991/* Functions from the Contiki MAC layer driver interface */
992
993/*---------------------------------------------------------------------------*/
994static void
995tsch_init(void)
996{
997 radio_value_t radio_rx_mode;
998 radio_value_t radio_tx_mode;
999 radio_value_t radio_max_payload_len;
1000
1001 rtimer_clock_t t;
1002
1003#if TSCH_DYNAMIC_TIMESLOT_TEMPLATE
1004 /* Check that the platform provides a TSCH timeslot timing template */
1005 if(TSCH_DEFAULT_TIMESLOT_TIMING == NULL) {
1006 LOG_ERR("! platform does not provide a timeslot timing template.\n");
1007 return;
1008 }
1009#endif
1010
1011 /* Check that the radio can correctly report its max supported payload */
1012 if(NETSTACK_RADIO.get_value(RADIO_CONST_MAX_PAYLOAD_LEN, &radio_max_payload_len) != RADIO_RESULT_OK) {
1013 LOG_ERR("! radio does not support getting RADIO_CONST_MAX_PAYLOAD_LEN. Abort init.\n");
1014 return;
1015 }
1016
1017 /* Radio Rx mode */
1018 if(NETSTACK_RADIO.get_value(RADIO_PARAM_RX_MODE, &radio_rx_mode) != RADIO_RESULT_OK) {
1019 LOG_ERR("! radio does not support getting RADIO_PARAM_RX_MODE. Abort init.\n");
1020 return;
1021 }
1022 /* Disable radio in frame filtering */
1023 radio_rx_mode &= ~RADIO_RX_MODE_ADDRESS_FILTER;
1024 /* Unset autoack */
1025 radio_rx_mode &= ~RADIO_RX_MODE_AUTOACK;
1026 /* Set radio in poll mode */
1027 radio_rx_mode |= RADIO_RX_MODE_POLL_MODE;
1028 if(NETSTACK_RADIO.set_value(RADIO_PARAM_RX_MODE, radio_rx_mode) != RADIO_RESULT_OK) {
1029 LOG_ERR("! radio does not support setting required RADIO_PARAM_RX_MODE. Abort init.\n");
1030 return;
1031 }
1032
1033 /* Radio Tx mode */
1034 if(NETSTACK_RADIO.get_value(RADIO_PARAM_TX_MODE, &radio_tx_mode) != RADIO_RESULT_OK) {
1035 LOG_ERR("! radio does not support getting RADIO_PARAM_TX_MODE. Abort init.\n");
1036 return;
1037 }
1038 /* Unset CCA */
1039 radio_tx_mode &= ~RADIO_TX_MODE_SEND_ON_CCA;
1040 if(NETSTACK_RADIO.set_value(RADIO_PARAM_TX_MODE, radio_tx_mode) != RADIO_RESULT_OK) {
1041 LOG_ERR("! radio does not support setting required RADIO_PARAM_TX_MODE. Abort init.\n");
1042 return;
1043 }
1044 /* Test setting channel */
1045 if(NETSTACK_RADIO.set_value(RADIO_PARAM_CHANNEL, TSCH_DEFAULT_HOPPING_SEQUENCE[0]) != RADIO_RESULT_OK) {
1046 LOG_ERR("! radio does not support setting channel. Abort init.\n");
1047 return;
1048 }
1049 /* Test getting timestamp */
1050 if(NETSTACK_RADIO.get_object(RADIO_PARAM_LAST_PACKET_TIMESTAMP, &t, sizeof(rtimer_clock_t)) != RADIO_RESULT_OK) {
1051 LOG_ERR("! radio does not support getting last packet timestamp. Abort init.\n");
1052 return;
1053 }
1054 /* Check max hopping sequence length vs default sequence length */
1055 if(TSCH_HOPPING_SEQUENCE_MAX_LEN < sizeof(TSCH_DEFAULT_HOPPING_SEQUENCE)) {
1056 LOG_ERR("! TSCH_HOPPING_SEQUENCE_MAX_LEN < sizeof(TSCH_DEFAULT_HOPPING_SEQUENCE). Abort init.\n");
1057 return;
1058 }
1059
1060 /* Init TSCH sub-modules */
1061#if TSCH_AUTOSELECT_TIME_SOURCE
1062 nbr_table_register(eb_stats, NULL);
1063#endif /* TSCH_AUTOSELECT_TIME_SOURCE */
1064 tsch_reset();
1067 tsch_log_init();
1068 ringbufindex_init(&input_ringbuf, TSCH_MAX_INCOMING_PACKETS);
1069 ringbufindex_init(&dequeued_ringbuf, TSCH_DEQUEUED_ARRAY_SIZE);
1070
1072 tsch_is_initialized = 1;
1073
1074#if TSCH_AUTOSTART
1075 /* Start TSCH operation.
1076 * If TSCH_AUTOSTART is not set, one needs to call NETSTACK_MAC.on() to start TSCH. */
1077 NETSTACK_MAC.on();
1078#endif /* TSCH_AUTOSTART */
1079
1080#if TSCH_WITH_SIXTOP
1081 sixtop_init();
1082#endif
1083
1084 tsch_stats_init();
1086}
1087/*---------------------------------------------------------------------------*/
1088/* Function send for TSCH-MAC, puts the packet in packetbuf in the MAC queue */
1089static void
1090send_packet(mac_callback_t sent, void *ptr)
1091{
1092 int ret = MAC_TX_DEFERRED;
1093 int hdr_len = 0;
1094 const linkaddr_t *addr = packetbuf_addr(PACKETBUF_ADDR_RECEIVER);
1095 uint8_t max_transmissions = 0;
1096
1097 if(!tsch_is_associated) {
1098 if(!tsch_is_initialized) {
1099 LOG_WARN("! not initialized (see earlier logs), drop outgoing packet\n");
1100 } else {
1101 LOG_WARN("! not associated, drop outgoing packet\n");
1102 }
1103 ret = MAC_TX_ERR;
1104 mac_call_sent_callback(sent, ptr, ret, 1);
1105 return;
1106 }
1107
1108 /* Ask for ACK if we are sending anything other than broadcast */
1111 packetbuf_set_attr(PACKETBUF_ATTR_MAC_ACK, 1);
1112 } else {
1113 /* Broadcast packets shall be added to broadcast queue
1114 * The broadcast address in Contiki is linkaddr_null which is equal
1115 * to tsch_eb_address */
1116 addr = &tsch_broadcast_address;
1117 }
1118
1119 packetbuf_set_attr(PACKETBUF_ATTR_FRAME_TYPE, FRAME802154_DATAFRAME);
1120
1121#if LLSEC802154_ENABLED
1122 tsch_security_set_packetbuf_attr(FRAME802154_DATAFRAME);
1123#endif /* LLSEC802154_ENABLED */
1124
1125 packetbuf_set_addr(PACKETBUF_ADDR_SENDER, &linkaddr_node_addr);
1126
1127 max_transmissions = packetbuf_attr(PACKETBUF_ATTR_MAX_MAC_TRANSMISSIONS);
1128 if(max_transmissions == 0) {
1129 /* If not set by the application, use the default TSCH value */
1130 max_transmissions = TSCH_MAC_MAX_FRAME_RETRIES + 1;
1131 }
1132
1133 if((hdr_len = NETSTACK_FRAMER.create()) < 0) {
1134 LOG_ERR("! can't send packet due to framer error\n");
1135 ret = MAC_TX_ERR;
1136 } else {
1137 struct tsch_packet *p;
1138 struct tsch_neighbor *n;
1139 /* Enqueue packet */
1140 p = tsch_queue_add_packet(addr, max_transmissions, sent, ptr);
1142 if(p == NULL) {
1143 LOG_ERR("! can't send packet to ");
1144 LOG_ERR_LLADDR(addr);
1145 LOG_ERR_(" with seqno %u, queue %u/%u %u/%u\n",
1146 packetbuf_attr(PACKETBUF_ATTR_MAC_SEQNO),
1148 TSCH_QUEUE_NUM_PER_NEIGHBOR, tsch_queue_global_packet_count(),
1149 QUEUEBUF_NUM);
1150 ret = MAC_TX_QUEUE_FULL;
1151 } else {
1152 p->header_len = hdr_len;
1153 LOG_INFO("send packet to ");
1154 LOG_INFO_LLADDR(addr);
1155 LOG_INFO_(" with seqno %u, queue %u/%u %u/%u, len %u %u\n",
1156 packetbuf_attr(PACKETBUF_ATTR_MAC_SEQNO),
1158 TSCH_QUEUE_NUM_PER_NEIGHBOR, tsch_queue_global_packet_count(),
1159 QUEUEBUF_NUM, p->header_len, queuebuf_datalen(p->qb));
1160 }
1161 }
1162 if(ret != MAC_TX_DEFERRED) {
1163 mac_call_sent_callback(sent, ptr, ret, 1);
1164 }
1165}
1166/*---------------------------------------------------------------------------*/
1167static void
1168packet_input(void)
1169{
1170 int frame_parsed = 1;
1171
1172 frame_parsed = NETSTACK_FRAMER.parse();
1173
1174 if(frame_parsed < 0) {
1175 LOG_ERR("! failed to parse %u\n", packetbuf_datalen());
1176 } else {
1177 int duplicate = 0;
1178
1179 /* Seqno of 0xffff means no seqno */
1180 if(packetbuf_attr(PACKETBUF_ATTR_MAC_SEQNO) != 0xffff) {
1181 /* Check for duplicates */
1182 duplicate = mac_sequence_is_duplicate();
1183 if(duplicate) {
1184 /* Drop the packet. */
1185 LOG_WARN("! drop dup ll from ");
1186 LOG_WARN_LLADDR(packetbuf_addr(PACKETBUF_ADDR_SENDER));
1187 LOG_WARN_(" seqno %u\n", packetbuf_attr(PACKETBUF_ATTR_MAC_SEQNO));
1188 } else {
1190 }
1191 }
1192
1193 if(!duplicate) {
1194 LOG_INFO("received from ");
1195 LOG_INFO_LLADDR(packetbuf_addr(PACKETBUF_ADDR_SENDER));
1196 LOG_INFO_(" with seqno %u\n", packetbuf_attr(PACKETBUF_ATTR_MAC_SEQNO));
1197#if TSCH_WITH_SIXTOP
1198 sixtop_input();
1199#endif /* TSCH_WITH_SIXTOP */
1200 NETSTACK_NETWORK.input();
1201 }
1202 }
1203}
1204/*---------------------------------------------------------------------------*/
1205static int
1206turn_on(void)
1207{
1208 if(tsch_is_initialized == 1 && tsch_is_started == 0) {
1209 tsch_is_started = 1;
1210 /* Process tx/rx callback and log messages whenever polled */
1211 process_start(&tsch_pending_events_process, NULL);
1212 if(TSCH_EB_PERIOD > 0) {
1213 /* periodically send TSCH EBs */
1214 process_start(&tsch_send_eb_process, NULL);
1215 }
1216 /* try to associate to a network or start one if setup as coordinator */
1217 process_start(&tsch_process, NULL);
1218 LOG_INFO("starting as %s\n", tsch_is_coordinator ? "coordinator": "node");
1219 return 1;
1220 }
1221 return 0;
1222}
1223/*---------------------------------------------------------------------------*/
1224static int
1225turn_off(void)
1226{
1227 NETSTACK_RADIO.off();
1228 return 1;
1229}
1230/*---------------------------------------------------------------------------*/
1231static int
1232max_payload(void)
1233{
1234 int framer_hdrlen;
1235 radio_value_t max_radio_payload_len;
1236 radio_result_t res;
1237
1238 if(!tsch_is_associated) {
1239 LOG_WARN("Cannot compute max payload size: not associated\n");
1240 return 0;
1241 }
1242
1243 res = NETSTACK_RADIO.get_value(RADIO_CONST_MAX_PAYLOAD_LEN,
1244 &max_radio_payload_len);
1245
1246 if(res == RADIO_RESULT_NOT_SUPPORTED) {
1247 LOG_ERR("Failed to retrieve max radio driver payload length\n");
1248 return 0;
1249 }
1250
1251 /* Set packetbuf security attributes */
1252 tsch_security_set_packetbuf_attr(FRAME802154_DATAFRAME);
1253
1254 framer_hdrlen = NETSTACK_FRAMER.length();
1255 if(framer_hdrlen < 0) {
1256 return 0;
1257 }
1258
1259 /* Setup security... before. */
1260 return MIN(max_radio_payload_len, TSCH_PACKET_MAX_LEN)
1261 - framer_hdrlen
1262 - LLSEC802154_PACKETBUF_MIC_LEN();
1263}
1264/*---------------------------------------------------------------------------*/
1265const struct mac_driver tschmac_driver = {
1266 "TSCH",
1267 tsch_init,
1270 turn_on,
1271 turn_off,
1273};
1274/*---------------------------------------------------------------------------*/
1275/** @} */
A MAC framer for IEEE 802.15.4.
clock_time_t clock_time(void)
Get the current clock time.
Definition clock.c:118
#define CLOCK_SECOND
A second, measured in system clock time.
Definition clock.h:105
void ctimer_stop(struct ctimer *c)
Stop a pending callback timer.
Definition ctimer.c:150
static void ctimer_set(struct ctimer *c, clock_time_t t, void(*f)(void *), void *ptr)
Set a callback timer.
Definition ctimer.h:150
void etimer_restart(struct etimer *et)
Restart an event timer from the current point in time.
Definition etimer.c:199
void etimer_reset(struct etimer *et)
Reset an event timer with the same interval as was previously set.
Definition etimer.c:192
static bool etimer_expired(struct etimer *et)
Check if an event timer has expired.
Definition etimer.h:201
void etimer_set(struct etimer *et, clock_time_t interval)
Set an event timer.
Definition etimer.c:177
int frame802154_parse(uint8_t *data, int len, frame802154_t *pf)
Parses an input frame.
linkaddr_t linkaddr_node_addr
The link-layer address of the node.
Definition linkaddr.c:48
void linkaddr_copy(linkaddr_t *dest, const linkaddr_t *src)
Copy a link-layer address.
Definition linkaddr.c:63
bool linkaddr_cmp(const linkaddr_t *addr1, const linkaddr_t *addr2)
Compare two link-layer addresses.
Definition linkaddr.c:69
const linkaddr_t linkaddr_null
The null link-layer address.
uint16_t packetbuf_totlen(void)
Get the total length of the header and data in the packetbuf.
Definition packetbuf.c:167
uint16_t packetbuf_datalen(void)
Get the length of the data in the packetbuf.
Definition packetbuf.c:155
uint8_t packetbuf_hdrlen(void)
Get the length of the header in the packetbuf.
Definition packetbuf.c:161
int packetbuf_copyfrom(const void *from, uint16_t len)
Copy from external data into the packetbuf.
Definition packetbuf.c:84
void packetbuf_clear(void)
Clear and reset the packetbuf.
Definition packetbuf.c:75
#define PROCESS_WAIT_UNTIL(c)
Wait for a condition to occur.
Definition process.h:194
#define PROCESS(name, strname)
Declare a process.
Definition process.h:309
void process_post_synch(struct process *p, process_event_t ev, process_data_t data)
Post a synchronous event to a process.
Definition process.c:357
#define PROCESS_PT_SPAWN(pt, thread)
Spawn a protothread from the process.
Definition process.h:213
#define PROCESS_BEGIN()
Define the beginning of a process.
Definition process.h:122
#define PROCESS_END()
Define the end of a process.
Definition process.h:133
void process_start(struct process *p, process_data_t data)
Start a process.
Definition process.c:121
#define PROCESS_THREAD(name, ev, data)
Define the body of a process.
Definition process.h:275
#define PROCESS_YIELD_UNTIL(c)
Yield the currently running process until a condition occurs.
Definition process.h:180
void process_poll(struct process *p)
Request a process to be polled.
Definition process.c:366
#define PT_BEGIN(pt)
Declare the start of a protothread inside the C function implementing the protothread.
Definition pt.h:280
#define PT_THREAD(name_args)
Declaration of a protothread.
Definition pt.h:265
#define PT_END(pt)
Declare the end of a protothread.
Definition pt.h:292
#define PT_WAIT_UNTIL(pt, condition)
Block and wait until condition is true.
Definition pt.h:313
#define RADIO_RX_MODE_POLL_MODE
Enable/disable/get the state of radio driver poll mode operation.
Definition radio.h:461
enum radio_result_e radio_result_t
Radio return values when setting or getting radio parameters.
int radio_value_t
Each radio has a set of parameters that designate the current configuration and state of the radio.
Definition radio.h:88
@ RADIO_RESULT_NOT_SUPPORTED
The parameter is not supported.
Definition radio.h:481
@ RADIO_RESULT_OK
The parameter was set/read successfully.
Definition radio.h:480
@ RADIO_PARAM_LAST_PACKET_TIMESTAMP
Last packet timestamp, of type rtimer_clock_t.
Definition radio.h:286
@ RADIO_PARAM_RX_MODE
Radio receiver mode determines if the radio has address filter (RADIO_RX_MODE_ADDRESS_FILTER) and aut...
Definition radio.h:173
@ RADIO_PARAM_CHANNEL
Channel used for radio communication.
Definition radio.h:134
@ RADIO_PARAM_TX_MODE
Radio transmission mode determines if the radio has send on CCA (RADIO_TX_MODE_SEND_ON_CCA) enabled o...
Definition radio.h:180
#define RTIMER_SECOND
Number of rtimer ticks for 1 second.
Definition rtimer.h:116
#define RTIMER_NOW()
Get the current clock time.
Definition rtimer.h:191
static void send_packet(void)
This function is called by the 6lowpan code to send out a packet.
void sixtop_input(void)
Input a packet stored in packetbuf.
Definition sixtop.c:204
void sixtop_init(void)
Initialize 6top module This initialization function removes all the SFs which has been installed into...
Definition sixtop.c:261
struct tsch_neighbor * tsch_queue_get_nbr(const linkaddr_t *addr)
Get a TSCH neighbor.
Definition tsch-queue.c:110
void tsch_schedule_keepalive(int immediate)
Schedule a keep-alive transmission within [timeout*0.9, timeout[ Can be called from an interrupt.
Definition tsch.c:333
void tsch_queue_free_unused_neighbors(void)
Deallocate all neighbors with empty queue.
Definition tsch-queue.c:398
int tsch_schedule_init(void)
Module initialization, call only once at init.
struct tsch_neighbor * tsch_queue_get_time_source(void)
Get the TSCH time source (we currently assume there is only one)
Definition tsch-queue.c:120
void tsch_queue_free_packet(struct tsch_packet *p)
Free a packet.
Definition tsch-queue.c:314
int tsch_packet_parse_eb(const uint8_t *buf, int buf_size, frame802154_t *frame, struct ieee802154_ies *ies, uint8_t *hdr_len, int frame_without_mic)
Parse EB.
void tsch_security_set_packetbuf_attr(uint8_t frame_type)
Set packetbuf (or eackbuf) attributes depending on a given frame type.
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, uint8_t do_remove)
Adds a link to a slotframe.
#define TSCH_ASN_DIVISOR_INIT(div, val_)
Initialize a struct asn_divisor_t.
Definition tsch-asn.h:86
void tsch_schedule_create_minimal(void)
Create a 6tisch minimal schedule with length TSCH_SCHEDULE_DEFAULT_LENGTH.
#define TSCH_ASN_INIT(asn, ms1b_, ls4b_)
Initialize ASN.
Definition tsch-asn.h:62
#define TSCH_ASN_DIFF(asn1, asn2)
Returns the 32-bit diff between asn1 and asn2.
Definition tsch-asn.h:82
int tsch_queue_global_packet_count(void)
Returns the number of packets currently in all TSCH queues.
Definition tsch-queue.c:279
int tsch_schedule_remove_all_slotframes(void)
Removes all slotframes, resulting in an empty schedule.
int tsch_queue_update_time_source(const linkaddr_t *new_addr)
Update TSCH time source.
Definition tsch-queue.c:142
struct tsch_packet * tsch_queue_add_packet(const linkaddr_t *addr, uint8_t max_transmissions, mac_callback_t sent, void *ptr)
Add packet to neighbor queue.
Definition tsch-queue.c:229
unsigned int tsch_security_mic_len(const frame802154_t *frame)
Return MIC length.
int tsch_queue_nbr_packet_count(const struct tsch_neighbor *n)
Returns the number of packets currently a given neighbor queue (by pointer)
Definition tsch-queue.c:286
void tsch_adaptive_timesync_reset(void)
Reset the status of the module.
void tsch_slot_operation_sync(rtimer_clock_t next_slot_start, struct tsch_asn_t *next_slot_asn)
Set global time before starting slot operation, with a rtimer time and an ASN.
unsigned int tsch_security_parse_frame(const uint8_t *hdr, int hdrlen, int datalen, const frame802154_t *frame, const linkaddr_t *sender, struct tsch_asn_t *asn)
Parse and check a frame protected with encryption and/or MIC.
void tsch_set_ka_timeout(uint32_t timeout)
Set the desynchronization timeout after which a node sends a unicasst keep-alive (KA) to its time sou...
Definition tsch.c:190
void tsch_queue_reset(void)
Reset neighbor queues module.
Definition tsch-queue.c:380
struct tsch_neighbor * tsch_queue_add_nbr(const linkaddr_t *addr)
Add a TSCH neighbor queue.
Definition tsch-queue.c:81
void tsch_queue_init(void)
Initialize TSCH queue module.
Definition tsch-queue.c:538
int tsch_packet_create_eb(uint8_t *hdr_len, uint8_t *tsch_sync_ie_offset)
Create an EB packet directly in packetbuf.
void tsch_log_init(void)
Initialize log module.
void tsch_set_eb_period(uint32_t period)
Set the period at wich TSCH enhanced beacons (EBs) are sent.
Definition tsch.c:197
struct tsch_slotframe * tsch_schedule_add_slotframe(uint16_t handle, uint16_t size)
Creates and adds a new slotframe.
void tsch_set_coordinator(int enable)
Set the node as PAN coordinator.
Definition tsch.c:167
void tsch_log_process_pending(void)
Process pending log messages.
linkaddr_t * tsch_queue_get_nbr_address(const struct tsch_neighbor *n)
Get the address of a neighbor.
Definition tsch-queue.c:135
void tsch_set_pan_secured(int enable)
Enable/disable security.
Definition tsch.c:178
void tsch_set_join_priority(uint8_t jp)
Set the TSCH join priority (JP)
Definition tsch.c:184
void tsch_disassociate(void)
Leave the TSCH network we are currently in.
Definition tsch.c:586
void tsch_slot_operation_start(void)
Start actual slot operation.
Header file for the logging system.
void mac_sequence_set_dsn(void)
Sets and increments the destination sequence number.
int mac_sequence_is_duplicate(void)
Tell whether the packetbuf is a duplicate packet.
void mac_sequence_register_seqno(void)
Register the sequence number of the packetbuf.
void mac_sequence_init(void)
brief Initializes the destination sequence number to a random value.
Header file for MAC sequence numbers management.
@ MAC_TX_DEFERRED
The MAC layer transmission could not be performed because of an error.
Definition mac.h:103
@ MAC_TX_NOACK
The MAC layer deferred the transmission for a later time.
Definition mac.h:100
@ MAC_TX_ERR
The MAC layer transmission could not be performed because of a fatal error.
Definition mac.h:107
Include file for the Contiki low-layer network stack (NETSTACK)
static void packet_input(void)
Called by the radio driver process when a frame has been received.
Header file for the Packet buffer (packetbuf) management.
Header file for the Packet queue buffer management.
Header file for the radio API.
Header file for generating non-cryptographic random numbers.
int ringbufindex_peek_get(const struct ringbufindex *r)
Return the index of the first element which will be removed if calling ringbufindex_get.
void ringbufindex_init(struct ringbufindex *r, uint8_t size)
Initialize a ring buffer.
int ringbufindex_get(struct ringbufindex *r)
Remove the first element and return its index.
Routing driver header file.
6TiSCH Operation Sublayer (6top) APIs
A timer.
Definition etimer.h:79
uint8_t frame_type
3 bit.
uint8_t frame_version
2 bit.
uint8_t security_enabled
1 bit.
Parameters used by the frame802154_create() function.
uint8_t src_addr[8]
Source address.
uint16_t src_pid
Source PAN ID.
frame802154_fcf_t fcf
Frame control field
Stores data about an incoming packet.
Definition tsch-types.h:148
The structure of a MAC protocol driver in Contiki.
Definition mac.h:68
int(* on)(void)
Turn the MAC layer on.
Definition mac.h:81
int(* max_payload)(void)
Read out estimated max payload size based on payload in packetbuf.
Definition mac.h:87
void(* send)(mac_callback_t sent_callback, void *ptr)
Send a packet from the packetbuf
Definition mac.h:75
uint8_t(* is_in_leaf_mode)(void)
Tells whether the protocol is in leaf mode.
Definition routing.h:188
For quick modulo operation on ASN.
Definition tsch-asn.h:54
The ASN is an absolute slot number over 5 bytes.
Definition tsch-asn.h:48
TSCH neighbor information.
Definition tsch-types.h:109
TSCH packet information.
Definition tsch-types.h:97
802.15.4e slotframe (contains links)
Definition tsch-types.h:84
void tsch_roots_set_self_to_root(uint8_t is_root)
Set the root status of the local node.
Definition tsch-roots.c:172
void tsch_roots_add_address(const linkaddr_t *root_address)
Add address as a potential RPL root that is a single-hop neighbor in the TSCH network.
Definition tsch-roots.c:168
void tsch_roots_init(void)
Initialize the list of RPL network roots.
Definition tsch-roots.c:181
Main API declarations for TSCH.
static uip_ds6_addr_t * addr
Pointer to a nbr cache entry.
Definition uip-nd6.c:107