Contiki-NG
Loading...
Searching...
No Matches
tsch-slot-operation.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 * TSCH slot operation implementation, running from interrupt.
36 * \author
37 * Simon Duquennoy <simonduq@sics.se>
38 * Beshr Al Nahas <beshr@sics.se>
39 * Atis Elsts <atis.elsts@bristol.ac.uk>
40 *
41 */
42
43/**
44 * \addtogroup tsch
45 * @{
46*/
47
48#include "dev/radio.h"
49#include "contiki.h"
50#include "net/netstack.h"
51#include "net/packetbuf.h"
52#include "net/queuebuf.h"
54#include "net/mac/tsch/tsch.h"
55#include "sys/critical.h"
56
57#include "sys/log.h"
58/* TSCH debug macros, i.e. to set LEDs or GPIOs on various TSCH
59 * timeslot events */
60#ifndef TSCH_DEBUG_INIT
61#define TSCH_DEBUG_INIT()
62#endif
63#ifndef TSCH_DEBUG_INTERRUPT
64#define TSCH_DEBUG_INTERRUPT()
65#endif
66#ifndef TSCH_DEBUG_RX_EVENT
67#define TSCH_DEBUG_RX_EVENT()
68#endif
69#ifndef TSCH_DEBUG_TX_EVENT
70#define TSCH_DEBUG_TX_EVENT()
71#endif
72#ifndef TSCH_DEBUG_SLOT_START
73#define TSCH_DEBUG_SLOT_START()
74#endif
75#ifndef TSCH_DEBUG_SLOT_END
76#define TSCH_DEBUG_SLOT_END()
77#endif
78
79/* Check if TSCH_MAX_INCOMING_PACKETS is power of two */
80#if (TSCH_MAX_INCOMING_PACKETS & (TSCH_MAX_INCOMING_PACKETS - 1)) != 0
81#error TSCH_MAX_INCOMING_PACKETS must be power of two
82#endif
83
84/* Check if TSCH_DEQUEUED_ARRAY_SIZE is power of two and greater or equal to QUEUEBUF_NUM */
85#if TSCH_DEQUEUED_ARRAY_SIZE < QUEUEBUF_NUM
86#error TSCH_DEQUEUED_ARRAY_SIZE must be greater or equal to QUEUEBUF_NUM
87#endif
88#if (TSCH_DEQUEUED_ARRAY_SIZE & (TSCH_DEQUEUED_ARRAY_SIZE - 1)) != 0
89#error TSCH_DEQUEUED_ARRAY_SIZE must be power of two
90#endif
91
92/* Truncate received drift correction information to maximum half
93 * of the guard time (one fourth of TSCH_DEFAULT_TS_RX_WAIT) */
94#define SYNC_IE_BOUND ((int32_t)US_TO_RTIMERTICKS(tsch_timing_us[tsch_ts_rx_wait] / 4))
95
96/* By default: check that rtimer runs at >=32kHz and use a guard time of 10us */
97#if RTIMER_SECOND < (32 * 1024)
98#error "TSCH: RTIMER_SECOND < (32 * 1024)"
99#endif
100#if CONTIKI_TARGET_COOJA
101/* Use 0 usec guard time for Cooja Mote with a 1 MHz Rtimer*/
102#define RTIMER_GUARD 0u
103#elif RTIMER_SECOND >= 200000
104#define RTIMER_GUARD (RTIMER_SECOND / 100000)
105#else
106#define RTIMER_GUARD 2u
107#endif
108
109enum tsch_radio_state_on_cmd {
110 TSCH_RADIO_CMD_ON_START_OF_TIMESLOT,
111 TSCH_RADIO_CMD_ON_WITHIN_TIMESLOT,
112 TSCH_RADIO_CMD_ON_FORCE,
113};
114
115enum tsch_radio_state_off_cmd {
116 TSCH_RADIO_CMD_OFF_END_OF_TIMESLOT,
117 TSCH_RADIO_CMD_OFF_WITHIN_TIMESLOT,
118 TSCH_RADIO_CMD_OFF_FORCE,
119};
120
121/* A ringbuf storing outgoing packets after they were dequeued.
122 * Will be processed layer by tsch_tx_process_pending */
123struct ringbufindex dequeued_ringbuf;
124struct tsch_packet *dequeued_array[TSCH_DEQUEUED_ARRAY_SIZE];
125/* A ringbuf storing incoming packets.
126 * Will be processed layer by tsch_rx_process_pending */
127struct ringbufindex input_ringbuf;
128struct input_packet input_array[TSCH_MAX_INCOMING_PACKETS];
129
130/* Updates and reads of the next two variables must be atomic (i.e. both together) */
131/* Last time we received Sync-IE (ACK or data packet from a time source) */
132static struct tsch_asn_t last_sync_asn;
133clock_time_t tsch_last_sync_time; /* Same info, in clock_time_t units */
134
135/* A global lock for manipulating data structures safely from outside of interrupt */
136static volatile int tsch_locked = 0;
137/* As long as this is set, skip all slot operation */
138static volatile int tsch_lock_requested = 0;
139
140/* Last estimated drift in RTIMER ticks
141 * (Sky: 1 tick = 30.517578125 usec exactly) */
142static int32_t drift_correction = 0;
143/* Is drift correction used? (Can be true even if drift_correction == 0) */
144static uint8_t is_drift_correction_used;
145
146/* Used from tsch_slot_operation and sub-protothreads */
147static rtimer_clock_t volatile current_slot_start;
148
149/* Are we currently inside a slot? */
150static volatile int tsch_in_slot_operation = 0;
151
152/* If we are inside a slot, these tell the current channel and channel offset */
153uint8_t tsch_current_channel;
154uint8_t tsch_current_channel_offset;
155
156/* Info about the link, packet and neighbor of
157 * the current (or next) slot */
158struct tsch_link *current_link = NULL;
159/* A backup link with Rx flag, overlapping with current_link.
160 * If the current link is Tx-only and the Tx queue
161 * is empty while executing the link, fallback to the backup link. */
162static struct tsch_link *backup_link = NULL;
163static struct tsch_packet *current_packet = NULL;
164static struct tsch_neighbor *current_neighbor = NULL;
165
166/* Indicates whether an extra link is needed to handle the current burst */
167static int burst_link_scheduled = 0;
168/* Counts the length of the current burst */
169int tsch_current_burst_count = 0;
170
171/* Protothread for association */
172PT_THREAD(tsch_scan(struct pt *pt));
173/* Protothread for slot operation, called from rtimer interrupt
174 * and scheduled from tsch_schedule_slot_operation */
175static PT_THREAD(tsch_slot_operation(struct rtimer *t, void *ptr));
176static struct pt slot_operation_pt;
177/* Sub-protothreads of tsch_slot_operation */
178static PT_THREAD(tsch_tx_slot(struct pt *pt, struct rtimer *t));
179static PT_THREAD(tsch_rx_slot(struct pt *pt, struct rtimer *t));
180
181/*---------------------------------------------------------------------------*/
182/* TSCH locking system. TSCH is locked during slot operations */
183
184/* Is TSCH locked? */
185int
187{
188 return tsch_locked;
189}
190
191/* Lock TSCH (no slot operation) */
192int
194{
195 if(!tsch_locked) {
196 rtimer_clock_t busy_wait_time;
197 int busy_wait = 0; /* Flag used for logging purposes */
198 /* Make sure no new slot operation will start */
199 tsch_lock_requested = 1;
200 /* Wait for the end of current slot operation. */
201 if(tsch_in_slot_operation) {
202 busy_wait = 1;
203 busy_wait_time = RTIMER_NOW();
204 while(tsch_in_slot_operation) {
206 }
207 busy_wait_time = RTIMER_NOW() - busy_wait_time;
208 }
209 if(!tsch_locked) {
210 /* Take the lock if it is free */
211 tsch_locked = 1;
212 tsch_lock_requested = 0;
213 if(busy_wait) {
214 /* Issue a log whenever we had to busy wait until getting the lock */
215 TSCH_LOG_ADD(tsch_log_message,
216 snprintf(log->message, sizeof(log->message),
217 "!get lock delay %u", (unsigned)busy_wait_time);
218 );
219 }
220 return 1;
221 }
222 }
223 TSCH_LOG_ADD(tsch_log_message,
224 snprintf(log->message, sizeof(log->message),
225 "!failed to lock");
226 );
227 return 0;
228}
229
230/* Release TSCH lock */
231void
233{
234 tsch_locked = 0;
235}
236
237/*---------------------------------------------------------------------------*/
238/* Channel hopping utility functions */
239
240/* Return the channel offset to use for the current slot */
241static uint8_t
242tsch_get_channel_offset(struct tsch_link *link, struct tsch_packet *p)
243{
244#if TSCH_WITH_LINK_SELECTOR
245 if(p != NULL) {
246 uint16_t packet_channel_offset = queuebuf_attr(p->qb, PACKETBUF_ATTR_TSCH_CHANNEL_OFFSET);
247 if(packet_channel_offset != 0xffff) {
248 /* The schedule specifies a channel offset for this one; use it */
249 return packet_channel_offset;
250 }
251 }
252#endif
253 return link->channel_offset;
254}
255
256/**
257 * Returns a 802.15.4 channel from an ASN and channel offset. Basically adds
258 * The offset to the ASN and performs a hopping sequence lookup.
259 *
260 * \param asn A given ASN
261 * \param channel_offset Given channel offset
262 * \return The resulting channel
263 */
264static uint8_t
265tsch_calculate_channel(struct tsch_asn_t *asn, uint16_t channel_offset)
266{
267 uint16_t index_of_0, index_of_offset;
268 index_of_0 = TSCH_ASN_MOD(*asn, tsch_hopping_sequence_length);
269 index_of_offset = (index_of_0 + channel_offset) % tsch_hopping_sequence_length.val;
270 return tsch_hopping_sequence[index_of_offset];
271}
272
273/*---------------------------------------------------------------------------*/
274/* Timing utility functions */
275
276/* Checks if the current time has passed a ref time + offset. Assumes
277 * a single overflow and ref time prior to now. */
278static uint8_t
279check_timer_miss(rtimer_clock_t ref_time, rtimer_clock_t offset, rtimer_clock_t now)
280{
281 rtimer_clock_t target = ref_time + offset;
282 int now_has_overflowed = now < ref_time;
283 int target_has_overflowed = target < ref_time;
284
285 if(now_has_overflowed == target_has_overflowed) {
286 /* Both or none have overflowed, just compare now to the target */
287 return target <= now;
288 } else {
289 /* Either now or target of overflowed.
290 * If it is now, then it has passed the target.
291 * If it is target, then we haven't reached it yet.
292 * */
293 return now_has_overflowed;
294 }
295}
296/*---------------------------------------------------------------------------*/
297/* Schedule a wakeup at a specified offset from a reference time.
298 * Provides basic protection against missed deadlines and timer overflows
299 * A return value of zero signals a missed deadline: no rtimer was scheduled. */
300static uint8_t
301tsch_schedule_slot_operation(struct rtimer *tm, rtimer_clock_t ref_time, rtimer_clock_t offset, const char *str)
302{
303 rtimer_clock_t now = RTIMER_NOW();
304 int r;
305 /* Subtract RTIMER_GUARD before checking for deadline miss
306 * because we can not schedule rtimer less than RTIMER_GUARD in the future */
307 int missed = check_timer_miss(ref_time, offset - RTIMER_GUARD, now);
308
309 if(missed) {
310 TSCH_LOG_ADD(tsch_log_message,
311 snprintf(log->message, sizeof(log->message),
312 "!dl-miss %s %d %d",
313 str, (int)(now-ref_time), (int)offset);
314 );
315 } else {
316 r = rtimer_set(tm, ref_time + offset, 1, (void (*)(struct rtimer *, void *))tsch_slot_operation, NULL);
317 if(r == RTIMER_OK) {
318 return 1;
319 }
320 }
321
322 /* block until the time to schedule comes */
323 RTIMER_BUSYWAIT_UNTIL_ABS(0, ref_time, offset);
324 return 0;
325}
326/*---------------------------------------------------------------------------*/
327/* Schedule slot operation conditionally, and YIELD if success only.
328 * Always attempt to schedule RTIMER_GUARD before the target to make sure to wake up
329 * ahead of time and then busy wait to exactly hit the target. */
330#define TSCH_SCHEDULE_AND_YIELD(pt, tm, ref_time, offset, str) \
331 do { \
332 if(tsch_schedule_slot_operation(tm, ref_time, offset - RTIMER_GUARD, str)) { \
333 PT_YIELD(pt); \
334 } \
335 RTIMER_BUSYWAIT_UNTIL_ABS(0, ref_time, offset); \
336 } while(0);
337/*---------------------------------------------------------------------------*/
338/*
339 * Check whether the current channel is in the join hopping sequence.
340 * If a custom join hopping sequence is defined, EB packets are only
341 * sent using that sequence.
342 */
343static uint8_t
344is_current_channel_in_join_sequence(struct tsch_link *link)
345{
346#ifdef TSCH_CONF_JOIN_HOPPING_SEQUENCE
347 /* custom join sequence is defined, some channels might not part of it */
348 uint8_t current_channel = tsch_calculate_channel(&tsch_current_asn,
349 link->channel_offset);
350 int i;
351 for(i = 0; i < sizeof(TSCH_JOIN_HOPPING_SEQUENCE); ++i) {
352 if(TSCH_JOIN_HOPPING_SEQUENCE[i] == current_channel) {
353 /* the channel is in the join sequence */
354 return 1;
355 }
356 }
357 /* the channel is not in the join sequence */
358 return 0;
359#else
360 /* all channels are in the join sequence */
361 return 1;
362#endif
363}
364/*---------------------------------------------------------------------------*/
365/* Get EB, broadcast or unicast packet to be sent, and target neighbor. */
366static struct tsch_packet *
367get_packet_and_neighbor_for_link(struct tsch_link *link, struct tsch_neighbor **target_neighbor)
368{
369 struct tsch_packet *p = NULL;
370 struct tsch_neighbor *n = NULL;
371
372 /* Is this a Tx link? */
373 if(link->link_options & LINK_OPTION_TX) {
374 /* is it for advertisement of EB? */
375 if(link->link_type == LINK_TYPE_ADVERTISING || link->link_type == LINK_TYPE_ADVERTISING_ONLY) {
376 /* is the current channel in the join hopping sequence? */
377 if(is_current_channel_in_join_sequence(link)) {
378 /* fetch EB packets */
379 n = n_eb;
381 }
382 }
383 if(link->link_type != LINK_TYPE_ADVERTISING_ONLY) {
384 /* NORMAL link or no EB to send, pick a data packet */
385 if(p == NULL) {
386 /* Get neighbor queue associated to the link and get packet from it */
387 n = tsch_queue_get_nbr(&link->addr);
389 /* if it is a broadcast slot and there were no broadcast packets, pick any unicast packet */
390 if(p == NULL && n == n_broadcast) {
392 }
393 }
394 }
395 }
396 /* return nbr (by reference) */
397 if(target_neighbor != NULL) {
398 *target_neighbor = n;
399 }
400
401 return p;
402}
403/*---------------------------------------------------------------------------*/
404static
405void update_link_backoff(struct tsch_link *link) {
406 if(link != NULL
407 && (link->link_options & LINK_OPTION_TX)
408 && (link->link_options & LINK_OPTION_SHARED)) {
409 /* Decrement the backoff window for all neighbors able to transmit over
410 * this Tx, Shared link. */
412 }
413}
414/*---------------------------------------------------------------------------*/
415uint64_t
417{
418 uint64_t uptime_asn;
419 uint64_t uptime_ticks;
420 int_master_status_t status;
421
422 if(!tsch_is_associated) {
423 /* not associated, network uptime is not known */
424 return (uint64_t)-1;
425 }
426
427 status = critical_enter();
428
429 uptime_asn = last_sync_asn.ls4b + ((uint64_t)last_sync_asn.ms1b << 32);
430 /* first calculate the at the uptime at the last sync in rtimer ticks */
431 uptime_ticks = uptime_asn * tsch_timing[tsch_ts_timeslot_length];
432 /* then convert to clock ticks (assume that CLOCK_SECOND divides RTIMER_ARCH_SECOND) */
433 uptime_ticks /= (RTIMER_ARCH_SECOND / CLOCK_SECOND);
434 /* then add the ticks passed since the last timesync */
435 uptime_ticks += (clock_time() - tsch_last_sync_time);
436
437 critical_exit(status);
438
439 return uptime_ticks;
440}
441/*---------------------------------------------------------------------------*/
442/**
443 * This function turns on the radio. Its semantics is dependent on
444 * the value of TSCH_RADIO_ON_DURING_TIMESLOT constant:
445 * - if enabled, the radio is turned on at the start of the slot
446 * - if disabled, the radio is turned on within the slot,
447 * directly before the packet Rx guard time and ACK Rx guard time.
448 */
449static void
450tsch_radio_on(enum tsch_radio_state_on_cmd command)
451{
452 int do_it = 0;
453 switch(command) {
454 case TSCH_RADIO_CMD_ON_START_OF_TIMESLOT:
455 if(TSCH_RADIO_ON_DURING_TIMESLOT) {
456 do_it = 1;
457 }
458 break;
459 case TSCH_RADIO_CMD_ON_WITHIN_TIMESLOT:
460 if(!TSCH_RADIO_ON_DURING_TIMESLOT) {
461 do_it = 1;
462 }
463 break;
464 case TSCH_RADIO_CMD_ON_FORCE:
465 do_it = 1;
466 break;
467 }
468 if(do_it) {
469 NETSTACK_RADIO.on();
470 }
471}
472/*---------------------------------------------------------------------------*/
473/**
474 * This function turns off the radio. In the same way as for tsch_radio_on(),
475 * it depends on the value of TSCH_RADIO_ON_DURING_TIMESLOT constant:
476 * - if enabled, the radio is turned off at the end of the slot
477 * - if disabled, the radio is turned off within the slot,
478 * directly after Tx'ing or Rx'ing a packet or Tx'ing an ACK.
479 */
480static void
481tsch_radio_off(enum tsch_radio_state_off_cmd command)
482{
483 int do_it = 0;
484 switch(command) {
485 case TSCH_RADIO_CMD_OFF_END_OF_TIMESLOT:
486 if(TSCH_RADIO_ON_DURING_TIMESLOT) {
487 do_it = 1;
488 }
489 break;
490 case TSCH_RADIO_CMD_OFF_WITHIN_TIMESLOT:
491 if(!TSCH_RADIO_ON_DURING_TIMESLOT) {
492 do_it = 1;
493 }
494 break;
495 case TSCH_RADIO_CMD_OFF_FORCE:
496 do_it = 1;
497 break;
498 }
499 if(do_it) {
500 NETSTACK_RADIO.off();
501 }
502}
503/*---------------------------------------------------------------------------*/
504static
505PT_THREAD(tsch_tx_slot(struct pt *pt, struct rtimer *t))
506{
507 /**
508 * TX slot:
509 * 1. Copy packet to radio buffer
510 * 2. Perform CCA if enabled
511 * 3. Sleep until it is time to transmit
512 * 4. Wait for ACK if it is a unicast packet
513 * 5. Extract drift if we received an E-ACK from a time source neighbor
514 * 6. Update CSMA parameters according to TX status
515 * 7. Schedule mac_call_sent_callback
516 **/
517
518 /* tx status */
519 static uint8_t mac_tx_status;
520 /* is the packet in its neighbor's queue? */
521 uint8_t in_queue;
522 static int dequeued_index;
523 static int packet_ready = 1;
524
525 PT_BEGIN(pt);
526
527 TSCH_DEBUG_TX_EVENT();
528
529 /* First check if we have space to store a newly dequeued packet (in case of
530 * successful Tx or Drop) */
531 dequeued_index = ringbufindex_peek_put(&dequeued_ringbuf);
532 if(dequeued_index != -1) {
533 if(current_packet == NULL || current_packet->qb == NULL) {
534 mac_tx_status = MAC_TX_ERR_FATAL;
535 } else {
536 /* packet payload */
537 static void *packet;
538#if LLSEC802154_ENABLED
539 /* encrypted payload */
540 static uint8_t encrypted_packet[TSCH_PACKET_MAX_LEN];
541#endif /* LLSEC802154_ENABLED */
542 /* packet payload length */
543 static uint8_t packet_len;
544 /* packet seqno */
545 static uint8_t seqno;
546 /* wait for ack? */
547 static uint8_t do_wait_for_ack;
548 static rtimer_clock_t tx_start_time;
549 /* Did we set the frame pending bit to request an extra burst link? */
550 static int burst_link_requested;
551
552#if TSCH_CCA_ENABLED
553 static uint8_t cca_status;
554#endif /* TSCH_CCA_ENABLED */
555
556 /* get payload */
557 packet = queuebuf_dataptr(current_packet->qb);
558 packet_len = queuebuf_datalen(current_packet->qb);
559 /* if is this a broadcast packet, don't wait for ack */
560 do_wait_for_ack = !current_neighbor->is_broadcast;
561 /* Unicast. More packets in queue for the neighbor? */
562 burst_link_requested = 0;
563 if(do_wait_for_ack
564 && tsch_current_burst_count + 1 < TSCH_BURST_MAX_LEN
565 && tsch_queue_nbr_packet_count(current_neighbor) > 1) {
566 burst_link_requested = 1;
567 tsch_packet_set_frame_pending(packet, packet_len);
568 }
569 /* read seqno from payload */
570 seqno = ((uint8_t *)(packet))[2];
571 /* if this is an EB, then update its Sync-IE */
572 if(current_neighbor == n_eb) {
573 packet_ready = tsch_packet_update_eb(packet, packet_len, current_packet->tsch_sync_ie_offset);
574 } else {
575 packet_ready = 1;
576 }
577
578#if LLSEC802154_ENABLED
579 if(tsch_is_pan_secured) {
580 /* If we are going to encrypt, we need to generate the output in a separate buffer and keep
581 * the original untouched. This is to allow for future retransmissions. */
582 int with_encryption = queuebuf_attr(current_packet->qb, PACKETBUF_ATTR_SECURITY_LEVEL) & 0x4;
583 packet_len += tsch_security_secure_frame(packet, with_encryption ? encrypted_packet : packet, current_packet->header_len,
584 packet_len - current_packet->header_len, &tsch_current_asn);
585 if(with_encryption) {
586 packet = encrypted_packet;
587 }
588 }
589#endif /* LLSEC802154_ENABLED */
590
591 /* prepare packet to send: copy to radio buffer */
592 if(packet_ready && NETSTACK_RADIO.prepare(packet, packet_len) == 0) { /* 0 means success */
593 static rtimer_clock_t tx_duration;
594
595#if TSCH_CCA_ENABLED
596 cca_status = 1;
597 /* delay before CCA */
598 TSCH_SCHEDULE_AND_YIELD(pt, t, current_slot_start, tsch_timing[tsch_ts_cca_offset], "cca");
599 TSCH_DEBUG_TX_EVENT();
600 tsch_radio_on(TSCH_RADIO_CMD_ON_WITHIN_TIMESLOT);
601 /* CCA */
602 RTIMER_BUSYWAIT_UNTIL_ABS(!(cca_status &= NETSTACK_RADIO.channel_clear()),
603 current_slot_start, tsch_timing[tsch_ts_cca_offset] + tsch_timing[tsch_ts_cca]);
604 TSCH_DEBUG_TX_EVENT();
605 /* there is not enough time to turn radio off */
606 /* NETSTACK_RADIO.off(); */
607 if(cca_status == 0) {
608 mac_tx_status = MAC_TX_COLLISION;
609 } else
610#endif /* TSCH_CCA_ENABLED */
611 {
612 /* delay before TX */
613 TSCH_SCHEDULE_AND_YIELD(pt, t, current_slot_start, tsch_timing[tsch_ts_tx_offset] - RADIO_DELAY_BEFORE_TX, "TxBeforeTx");
614 TSCH_DEBUG_TX_EVENT();
615 /* send packet already in radio tx buffer */
616 mac_tx_status = NETSTACK_RADIO.transmit(packet_len);
617 tx_count++;
618 /* Save tx timestamp */
619 tx_start_time = current_slot_start + tsch_timing[tsch_ts_tx_offset];
620 /* calculate TX duration based on sent packet len */
621 tx_duration = TSCH_PACKET_DURATION(packet_len);
622 /* limit tx_time to its max value */
623 tx_duration = MIN(tx_duration, tsch_timing[tsch_ts_max_tx]);
624 /* turn tadio off -- will turn on again to wait for ACK if needed */
625 tsch_radio_off(TSCH_RADIO_CMD_OFF_WITHIN_TIMESLOT);
626
627 if(mac_tx_status == RADIO_TX_OK) {
628 if(do_wait_for_ack) {
629 uint8_t ackbuf[TSCH_PACKET_MAX_LEN];
630 int ack_len;
631 rtimer_clock_t ack_start_time;
632 int is_time_source;
633 struct ieee802154_ies ack_ies;
634 uint8_t ack_hdrlen;
635 frame802154_t frame;
636
637#if TSCH_HW_FRAME_FILTERING
638 radio_value_t radio_rx_mode;
639 /* Entering promiscuous mode so that the radio accepts the enhanced ACK */
640 NETSTACK_RADIO.get_value(RADIO_PARAM_RX_MODE, &radio_rx_mode);
641 NETSTACK_RADIO.set_value(RADIO_PARAM_RX_MODE, radio_rx_mode & (~RADIO_RX_MODE_ADDRESS_FILTER));
642#endif /* TSCH_HW_FRAME_FILTERING */
643 /* Unicast: wait for ack after tx: sleep until ack time */
644 TSCH_SCHEDULE_AND_YIELD(pt, t, current_slot_start,
645 tsch_timing[tsch_ts_tx_offset] + tx_duration + tsch_timing[tsch_ts_rx_ack_delay] - RADIO_DELAY_BEFORE_RX, "TxBeforeAck");
646 TSCH_DEBUG_TX_EVENT();
647 tsch_radio_on(TSCH_RADIO_CMD_ON_WITHIN_TIMESLOT);
648 /* Wait for ACK to come */
649 RTIMER_BUSYWAIT_UNTIL_ABS(NETSTACK_RADIO.receiving_packet(),
650 tx_start_time, tx_duration + tsch_timing[tsch_ts_rx_ack_delay] + tsch_timing[tsch_ts_ack_wait] + RADIO_DELAY_BEFORE_DETECT);
651 TSCH_DEBUG_TX_EVENT();
652
653 ack_start_time = RTIMER_NOW() - RADIO_DELAY_BEFORE_DETECT;
654
655 /* Wait for ACK to finish */
656 RTIMER_BUSYWAIT_UNTIL_ABS(!NETSTACK_RADIO.receiving_packet(),
657 ack_start_time, tsch_timing[tsch_ts_max_ack]);
658 TSCH_DEBUG_TX_EVENT();
659 tsch_radio_off(TSCH_RADIO_CMD_OFF_WITHIN_TIMESLOT);
660
661#if TSCH_HW_FRAME_FILTERING
662 /* Leaving promiscuous mode */
663 NETSTACK_RADIO.get_value(RADIO_PARAM_RX_MODE, &radio_rx_mode);
664 NETSTACK_RADIO.set_value(RADIO_PARAM_RX_MODE, radio_rx_mode | RADIO_RX_MODE_ADDRESS_FILTER);
665#endif /* TSCH_HW_FRAME_FILTERING */
666
667 /* Read ack frame */
668 ack_len = NETSTACK_RADIO.read((void *)ackbuf, sizeof(ackbuf));
669
670 is_time_source = 0;
671 /* The radio driver should return 0 if no valid packets are in the rx buffer */
672 if(ack_len > 0) {
673 is_time_source = current_neighbor != NULL && current_neighbor->is_time_source;
674 if(tsch_packet_parse_eack(ackbuf, ack_len, seqno,
675 &frame, &ack_ies, &ack_hdrlen) == 0) {
676 ack_len = 0;
677 }
678
679#if LLSEC802154_ENABLED
680 if(ack_len != 0) {
681 if(!tsch_security_parse_frame(ackbuf, ack_hdrlen, ack_len - ack_hdrlen - tsch_security_mic_len(&frame),
682 &frame, tsch_queue_get_nbr_address(current_neighbor), &tsch_current_asn)) {
683 TSCH_LOG_ADD(tsch_log_message,
684 snprintf(log->message, sizeof(log->message),
685 "!failed to authenticate ACK"));
686 ack_len = 0;
687 }
688 } else {
689 TSCH_LOG_ADD(tsch_log_message,
690 snprintf(log->message, sizeof(log->message),
691 "!failed to parse ACK"));
692 }
693#endif /* LLSEC802154_ENABLED */
694 }
695
696 if(ack_len != 0) {
697 if(is_time_source) {
698 int32_t eack_time_correction = US_TO_RTIMERTICKS(ack_ies.ie_time_correction);
699 int32_t since_last_timesync = TSCH_ASN_DIFF(tsch_current_asn, last_sync_asn);
700 if(eack_time_correction > SYNC_IE_BOUND) {
701 drift_correction = SYNC_IE_BOUND;
702 } else if(eack_time_correction < -SYNC_IE_BOUND) {
703 drift_correction = -SYNC_IE_BOUND;
704 } else {
705 drift_correction = eack_time_correction;
706 }
707 if(drift_correction != eack_time_correction) {
708 TSCH_LOG_ADD(tsch_log_message,
709 snprintf(log->message, sizeof(log->message),
710 "!truncated dr %d %d", (int)eack_time_correction, (int)drift_correction);
711 );
712 }
713 tsch_stats_on_time_synchronization(eack_time_correction);
714 is_drift_correction_used = 1;
715 tsch_timesync_update(current_neighbor, since_last_timesync, drift_correction);
716 /* Keep track of sync time */
717 last_sync_asn = tsch_current_asn;
718 tsch_last_sync_time = clock_time();
720 }
721 mac_tx_status = MAC_TX_OK;
722
723 /* We requested an extra slot and got an ack. This means
724 the extra slot will be scheduled at the received */
725 if(burst_link_requested) {
726 burst_link_scheduled = 1;
727 }
728 } else {
729 mac_tx_status = MAC_TX_NOACK;
730 }
731 } else {
732 mac_tx_status = MAC_TX_OK;
733 }
734 } else {
735 mac_tx_status = MAC_TX_ERR;
736 }
737 }
738 } else {
739 mac_tx_status = MAC_TX_ERR;
740 }
741 }
742
743 tsch_radio_off(TSCH_RADIO_CMD_OFF_END_OF_TIMESLOT);
744
745 current_packet->transmissions++;
746 current_packet->ret = mac_tx_status;
747
748 /* Post TX: Update neighbor queue state */
749 in_queue = tsch_queue_packet_sent(current_neighbor, current_packet, current_link, mac_tx_status);
750
751 /* The packet was dequeued, add it to dequeued_ringbuf for later processing */
752 if(in_queue == 0) {
753 dequeued_array[dequeued_index] = current_packet;
754 ringbufindex_put(&dequeued_ringbuf);
755 }
756
757 /* If this is an unicast packet to timesource, update stats */
758 if(current_neighbor != NULL && current_neighbor->is_time_source) {
759 tsch_stats_tx_packet(current_neighbor, mac_tx_status, tsch_current_channel);
760 }
761
762 /* Log every tx attempt */
763 TSCH_LOG_ADD(tsch_log_tx,
764 log->tx.mac_tx_status = mac_tx_status;
765 log->tx.num_tx = current_packet->transmissions;
766 log->tx.datalen = queuebuf_datalen(current_packet->qb);
767 log->tx.drift = drift_correction;
768 log->tx.drift_used = is_drift_correction_used;
769 log->tx.is_data = ((((uint8_t *)(queuebuf_dataptr(current_packet->qb)))[0]) & 7) == FRAME802154_DATAFRAME;
770#if LLSEC802154_ENABLED
771 log->tx.sec_level = queuebuf_attr(current_packet->qb, PACKETBUF_ATTR_SECURITY_LEVEL);
772#else /* LLSEC802154_ENABLED */
773 log->tx.sec_level = 0;
774#endif /* LLSEC802154_ENABLED */
775 linkaddr_copy(&log->tx.dest, queuebuf_addr(current_packet->qb, PACKETBUF_ADDR_RECEIVER));
776 log->tx.seqno = queuebuf_attr(current_packet->qb, PACKETBUF_ATTR_MAC_SEQNO);
777 );
778
779 /* Poll process for later processing of packet sent events and logs */
780 process_poll(&tsch_pending_events_process);
781 }
782
783 TSCH_DEBUG_TX_EVENT();
784
785 PT_END(pt);
786}
787/*---------------------------------------------------------------------------*/
788static
789PT_THREAD(tsch_rx_slot(struct pt *pt, struct rtimer *t))
790{
791 /**
792 * RX slot:
793 * 1. Check if it is used for TIME_KEEPING
794 * 2. Sleep and wake up just before expected RX time (with a guard time: TS_LONG_GT)
795 * 3. Check for radio activity for the guard time: TS_LONG_GT
796 * 4. Prepare and send ACK if needed
797 * 5. Drift calculated in the ACK callback registered with the radio driver. Use it if receiving from a time source neighbor.
798 **/
799
800 struct tsch_neighbor *n;
801 static linkaddr_t source_address;
802 static linkaddr_t destination_address;
803 static int16_t input_index;
804 static int input_queue_drop = 0;
805
806 PT_BEGIN(pt);
807
808 TSCH_DEBUG_RX_EVENT();
809
810 input_index = ringbufindex_peek_put(&input_ringbuf);
811 if(input_index == -1) {
812 input_queue_drop++;
813 } else {
814 static struct input_packet *current_input;
815 /* Estimated drift based on RX time */
816 static int32_t estimated_drift;
817 /* Rx timestamps */
818 static rtimer_clock_t rx_start_time;
819 static rtimer_clock_t expected_rx_time;
820 static rtimer_clock_t packet_duration;
821 uint8_t packet_seen;
822
823 expected_rx_time = current_slot_start + tsch_timing[tsch_ts_tx_offset];
824 /* Default start time: expected Rx time */
825 rx_start_time = expected_rx_time;
826
827 current_input = &input_array[input_index];
828
829 /* Wait before starting to listen */
830 TSCH_SCHEDULE_AND_YIELD(pt, t, current_slot_start, tsch_timing[tsch_ts_rx_offset] - RADIO_DELAY_BEFORE_RX, "RxBeforeListen");
831 TSCH_DEBUG_RX_EVENT();
832
833 /* Start radio for at least guard time */
834 tsch_radio_on(TSCH_RADIO_CMD_ON_WITHIN_TIMESLOT);
835 packet_seen = NETSTACK_RADIO.receiving_packet() || NETSTACK_RADIO.pending_packet();
836 if(!packet_seen) {
837 /* Check if receiving within guard time */
838 RTIMER_BUSYWAIT_UNTIL_ABS((packet_seen = (NETSTACK_RADIO.receiving_packet() || NETSTACK_RADIO.pending_packet())),
839 current_slot_start, tsch_timing[tsch_ts_rx_offset] + tsch_timing[tsch_ts_rx_wait] + RADIO_DELAY_BEFORE_DETECT);
840 }
841 if(!packet_seen) {
842 /* no packets on air */
843 tsch_radio_off(TSCH_RADIO_CMD_OFF_FORCE);
844 } else {
845 TSCH_DEBUG_RX_EVENT();
846 /* Save packet timestamp */
847 rx_start_time = RTIMER_NOW() - RADIO_DELAY_BEFORE_DETECT;
848
849 /* Wait until packet is received, turn radio off */
850 RTIMER_BUSYWAIT_UNTIL_ABS(!NETSTACK_RADIO.receiving_packet(),
851 current_slot_start, tsch_timing[tsch_ts_rx_offset] + tsch_timing[tsch_ts_rx_wait] + tsch_timing[tsch_ts_max_tx]);
852 TSCH_DEBUG_RX_EVENT();
853 tsch_radio_off(TSCH_RADIO_CMD_OFF_WITHIN_TIMESLOT);
854
855 if(NETSTACK_RADIO.pending_packet()) {
856 static int frame_valid;
857 static int header_len;
858 static frame802154_t frame;
859 radio_value_t radio_last_rssi;
860 radio_value_t radio_last_lqi;
861
862 /* Read packet */
863 current_input->len = NETSTACK_RADIO.read((void *)current_input->payload, TSCH_PACKET_MAX_LEN);
864 NETSTACK_RADIO.get_value(RADIO_PARAM_LAST_RSSI, &radio_last_rssi);
865 current_input->rx_asn = tsch_current_asn;
866 current_input->rssi = (signed)radio_last_rssi;
867 current_input->channel = tsch_current_channel;
868 header_len = frame802154_parse((uint8_t *)current_input->payload, current_input->len, &frame);
869 frame_valid = header_len > 0 &&
870 frame802154_check_dest_panid(&frame) &&
871 frame802154_extract_linkaddr(&frame, &source_address, &destination_address);
872
873#if TSCH_RESYNC_WITH_SFD_TIMESTAMPS
874 /* At the end of the reception, get an more accurate estimate of SFD arrival time */
875 NETSTACK_RADIO.get_object(RADIO_PARAM_LAST_PACKET_TIMESTAMP, &rx_start_time, sizeof(rtimer_clock_t));
876#endif
877
878 packet_duration = TSCH_PACKET_DURATION(current_input->len);
879 /* limit packet_duration to its max value */
880 packet_duration = MIN(packet_duration, tsch_timing[tsch_ts_max_tx]);
881
882 if(!frame_valid) {
883 TSCH_LOG_ADD(tsch_log_message,
884 snprintf(log->message, sizeof(log->message),
885 "!failed to parse frame %u %u", header_len, current_input->len));
886 }
887
888 if(frame_valid) {
889 if(frame.fcf.frame_type != FRAME802154_DATAFRAME
890 && frame.fcf.frame_type != FRAME802154_BEACONFRAME) {
891 TSCH_LOG_ADD(tsch_log_message,
892 snprintf(log->message, sizeof(log->message),
893 "!discarding frame with type %u, len %u", frame.fcf.frame_type, current_input->len));
894 frame_valid = 0;
895 }
896 }
897
898#if LLSEC802154_ENABLED
899 /* Decrypt and verify incoming frame */
900 if(frame_valid) {
902 current_input->payload, header_len, current_input->len - header_len - tsch_security_mic_len(&frame),
903 &frame, &source_address, &tsch_current_asn)) {
904 current_input->len -= tsch_security_mic_len(&frame);
905 } else {
906 TSCH_LOG_ADD(tsch_log_message,
907 snprintf(log->message, sizeof(log->message),
908 "!failed to authenticate frame %u", current_input->len));
909 frame_valid = 0;
910 }
911 }
912#endif /* LLSEC802154_ENABLED */
913
914 if(frame_valid) {
915 /* Check that frome is for us or broadcast, AND that it is not from
916 * ourselves. This is for consistency with CSMA and to avoid adding
917 * ourselves to neighbor tables in case frames are being replayed. */
918 if((linkaddr_cmp(&destination_address, &linkaddr_node_addr)
919 || linkaddr_cmp(&destination_address, &linkaddr_null))
920 && !linkaddr_cmp(&source_address, &linkaddr_node_addr)) {
921 int do_nack = 0;
922 rx_count++;
923 estimated_drift = RTIMER_CLOCK_DIFF(expected_rx_time, rx_start_time);
924 tsch_stats_on_time_synchronization(estimated_drift);
925
926#if TSCH_TIMESYNC_REMOVE_JITTER
927 /* remove jitter due to measurement errors */
928 if(ABS(estimated_drift) <= TSCH_TIMESYNC_MEASUREMENT_ERROR) {
929 estimated_drift = 0;
930 } else if(estimated_drift > 0) {
931 estimated_drift -= TSCH_TIMESYNC_MEASUREMENT_ERROR;
932 } else {
933 estimated_drift += TSCH_TIMESYNC_MEASUREMENT_ERROR;
934 }
935#endif
936
937#ifdef TSCH_CALLBACK_DO_NACK
938 if(frame.fcf.ack_required) {
939 do_nack = TSCH_CALLBACK_DO_NACK(current_link,
940 &source_address, &destination_address);
941 }
942#endif
943
944 if(frame.fcf.ack_required) {
945 static uint8_t ack_buf[TSCH_PACKET_MAX_LEN];
946 static int ack_len;
947
948 /* Build ACK frame */
949 ack_len = tsch_packet_create_eack(ack_buf, sizeof(ack_buf),
950 &source_address, frame.seq, (int16_t)RTIMERTICKS_TO_US(estimated_drift), do_nack);
951
952 if(ack_len > 0) {
953#if LLSEC802154_ENABLED
954 if(tsch_is_pan_secured) {
955 /* Secure ACK frame. There is only header and header IEs, therefore data len == 0. */
956 ack_len += tsch_security_secure_frame(ack_buf, ack_buf, ack_len, 0, &tsch_current_asn);
957 }
958#endif /* LLSEC802154_ENABLED */
959
960 /* Copy to radio buffer */
961 NETSTACK_RADIO.prepare((const void *)ack_buf, ack_len);
962
963 /* Wait for time to ACK and transmit ACK */
964 TSCH_SCHEDULE_AND_YIELD(pt, t, rx_start_time,
965 packet_duration + tsch_timing[tsch_ts_tx_ack_delay] - RADIO_DELAY_BEFORE_TX, "RxBeforeAck");
966 TSCH_DEBUG_RX_EVENT();
967 NETSTACK_RADIO.transmit(ack_len);
968 tsch_radio_off(TSCH_RADIO_CMD_OFF_WITHIN_TIMESLOT);
969
970 /* Schedule a burst link iff the frame pending bit was set */
971 burst_link_scheduled = tsch_packet_get_frame_pending(current_input->payload, current_input->len);
972 }
973 }
974
975 /* If the sender is a time source, proceed to clock drift compensation */
976 n = tsch_queue_get_nbr(&source_address);
977 if(n != NULL && n->is_time_source) {
978 int32_t since_last_timesync = TSCH_ASN_DIFF(tsch_current_asn, last_sync_asn);
979 /* Keep track of last sync time */
980 last_sync_asn = tsch_current_asn;
981 tsch_last_sync_time = clock_time();
982 /* Save estimated drift */
983 drift_correction = -estimated_drift;
984 is_drift_correction_used = 1;
985 sync_count++;
986 tsch_timesync_update(n, since_last_timesync, -estimated_drift);
988 }
989
990 /* Add current input to ringbuf */
991 ringbufindex_put(&input_ringbuf);
992
993 /* If the neighbor is known, update its stats */
994 if(n != NULL) {
995 NETSTACK_RADIO.get_value(RADIO_PARAM_LAST_LINK_QUALITY, &radio_last_lqi);
996 tsch_stats_rx_packet(n, current_input->rssi, radio_last_lqi, tsch_current_channel);
997 }
998
999 /* Log every reception */
1000 TSCH_LOG_ADD(tsch_log_rx,
1001 linkaddr_copy(&log->rx.src, (linkaddr_t *)&frame.src_addr);
1002 log->rx.is_unicast = frame.fcf.ack_required;
1003 log->rx.datalen = current_input->len;
1004 log->rx.drift = drift_correction;
1005 log->rx.drift_used = is_drift_correction_used;
1006 log->rx.is_data = frame.fcf.frame_type == FRAME802154_DATAFRAME;
1007 log->rx.sec_level = frame.aux_hdr.security_control.security_level;
1008 log->rx.estimated_drift = estimated_drift;
1009 log->rx.seqno = frame.seq;
1010 );
1011 }
1012
1013 /* Poll process for processing of pending input and logs */
1014 process_poll(&tsch_pending_events_process);
1015 }
1016 }
1017
1018 tsch_radio_off(TSCH_RADIO_CMD_OFF_END_OF_TIMESLOT);
1019 }
1020
1021 if(input_queue_drop != 0) {
1022 TSCH_LOG_ADD(tsch_log_message,
1023 snprintf(log->message, sizeof(log->message),
1024 "!queue full skipped %u", input_queue_drop);
1025 );
1026 input_queue_drop = 0;
1027 }
1028 }
1029
1030 TSCH_DEBUG_RX_EVENT();
1031
1032 PT_END(pt);
1033}
1034/*---------------------------------------------------------------------------*/
1035/* Protothread for slot operation, called from rtimer interrupt
1036 * and scheduled from tsch_schedule_slot_operation */
1037static
1038PT_THREAD(tsch_slot_operation(struct rtimer *t, void *ptr))
1039{
1040 TSCH_DEBUG_INTERRUPT();
1041 PT_BEGIN(&slot_operation_pt);
1042
1043 /* Loop over all active slots */
1044 while(tsch_is_associated) {
1045
1046 if(current_link == NULL || tsch_lock_requested) { /* Skip slot operation if there is no link
1047 or if there is a pending request for getting the lock */
1048 /* Issue a log whenever skipping a slot */
1049 TSCH_LOG_ADD(tsch_log_message,
1050 snprintf(log->message, sizeof(log->message),
1051 "!skipped slot %u %u %u",
1052 tsch_locked,
1053 tsch_lock_requested,
1054 current_link == NULL);
1055 );
1056
1057 } else {
1058 int is_active_slot;
1059 TSCH_DEBUG_SLOT_START();
1060 tsch_in_slot_operation = 1;
1061 /* Measure on-air noise level while TSCH is idle */
1062 tsch_stats_sample_rssi();
1063 /* Reset drift correction */
1064 drift_correction = 0;
1065 is_drift_correction_used = 0;
1066 /* Get a packet ready to be sent */
1067 current_packet = get_packet_and_neighbor_for_link(current_link, &current_neighbor);
1068 uint8_t do_skip_best_link = 0;
1069 if(current_packet == NULL && backup_link != NULL) {
1070 /* There is no packet to send, and this link does not have Rx flag. Instead of doing
1071 * nothing, switch to the backup link (has Rx flag) if any
1072 * and if the current link cannot Rx or both links can Rx, but the backup link has priority. */
1073 if(!(current_link->link_options & LINK_OPTION_RX)
1074 || backup_link->slotframe_handle < current_link->slotframe_handle) {
1075 do_skip_best_link = 1;
1076 }
1077 }
1078
1079 if(do_skip_best_link) {
1080 /* skipped a Tx link, refresh its backoff */
1081 update_link_backoff(current_link);
1082
1083 current_link = backup_link;
1084 current_packet = get_packet_and_neighbor_for_link(current_link, &current_neighbor);
1085 }
1086 is_active_slot = current_packet != NULL || (current_link->link_options & LINK_OPTION_RX);
1087 if(is_active_slot) {
1088 /* If we are in a burst, we stick to current channel instead of
1089 * doing channel hopping, as per IEEE 802.15.4-2015 */
1090 if(burst_link_scheduled) {
1091 /* Reset burst_link_scheduled flag. Will be set again if burst continue. */
1092 burst_link_scheduled = 0;
1093 } else {
1094 /* Hop channel */
1095 tsch_current_channel_offset = tsch_get_channel_offset(current_link, current_packet);
1096 tsch_current_channel = tsch_calculate_channel(&tsch_current_asn, tsch_current_channel_offset);
1097 }
1098 NETSTACK_RADIO.set_value(RADIO_PARAM_CHANNEL, tsch_current_channel);
1099 /* Turn the radio on already here if configured so; necessary for radios with slow startup */
1100 tsch_radio_on(TSCH_RADIO_CMD_ON_START_OF_TIMESLOT);
1101 /* Decide whether it is a TX/RX/IDLE or OFF slot */
1102 /* Actual slot operation */
1103 if(current_packet != NULL) {
1104 /* We have something to transmit, do the following:
1105 * 1. send
1106 * 2. update_backoff_state(current_neighbor)
1107 * 3. post tx callback
1108 **/
1109 static struct pt slot_tx_pt;
1110 PT_SPAWN(&slot_operation_pt, &slot_tx_pt, tsch_tx_slot(&slot_tx_pt, t));
1111 } else {
1112 /* Listen */
1113 static struct pt slot_rx_pt;
1114 PT_SPAWN(&slot_operation_pt, &slot_rx_pt, tsch_rx_slot(&slot_rx_pt, t));
1115 }
1116 } else {
1117 /* Make sure to end the burst in cast, for some reason, we were
1118 * in a burst but now without any more packet to send. */
1119 burst_link_scheduled = 0;
1120 }
1121 TSCH_DEBUG_SLOT_END();
1122 }
1123
1124 /* End of slot operation, schedule next slot or resynchronize */
1125
1126 if(tsch_is_coordinator) {
1127 /* Update the `last_sync_*` variables to avoid large errors
1128 * in the application-level time synchronization */
1129 last_sync_asn = tsch_current_asn;
1130 tsch_last_sync_time = clock_time();
1131 }
1132
1133 /* Do we need to resynchronize? i.e., wait for EB again */
1134 if(!tsch_is_coordinator && (TSCH_ASN_DIFF(tsch_current_asn, last_sync_asn) >
1135 (100 * TSCH_CLOCK_TO_SLOTS(TSCH_DESYNC_THRESHOLD / 100, tsch_timing[tsch_ts_timeslot_length])))) {
1136 TSCH_LOG_ADD(tsch_log_message,
1137 snprintf(log->message, sizeof(log->message),
1138 "! leaving the network, last sync %u",
1139 (unsigned)TSCH_ASN_DIFF(tsch_current_asn, last_sync_asn));
1140 );
1142 } else {
1143 /* backup of drift correction for printing debug messages */
1144 /* int32_t drift_correction_backup = drift_correction; */
1145 uint16_t timeslot_diff = 0;
1146 rtimer_clock_t prev_slot_start;
1147 /* Time to next wake up */
1148 rtimer_clock_t time_to_next_active_slot;
1149 /* Schedule next wakeup skipping slots if missed deadline */
1150 do {
1151 update_link_backoff(current_link);
1152
1153 /* A burst link was scheduled. Replay the current link at the
1154 next time offset */
1155 if(burst_link_scheduled && current_link != NULL) {
1156 timeslot_diff = 1;
1157 backup_link = NULL;
1158 /* Keep track of the number of repetitions */
1159 tsch_current_burst_count++;
1160 } else {
1161 /* Get next active link */
1162 current_link = tsch_schedule_get_next_active_link(&tsch_current_asn, &timeslot_diff, &backup_link);
1163 if(current_link == NULL) {
1164 /* There is no next link. Fall back to default
1165 * behavior: wake up at the next slot. */
1166 timeslot_diff = 1;
1167 } else {
1168 /* Reset burst index now that the link was scheduled from
1169 normal schedule (as opposed to from ongoing burst) */
1170 tsch_current_burst_count = 0;
1171 }
1172 }
1173
1174 /* Update ASN */
1175 TSCH_ASN_INC(tsch_current_asn, timeslot_diff);
1176 /* Time to next wake up */
1177 time_to_next_active_slot = timeslot_diff * tsch_timing[tsch_ts_timeslot_length] + drift_correction;
1178 time_to_next_active_slot += tsch_timesync_adaptive_compensate(time_to_next_active_slot);
1179 drift_correction = 0;
1180 is_drift_correction_used = 0;
1181 /* Update current slot start */
1182 prev_slot_start = current_slot_start;
1183 current_slot_start += time_to_next_active_slot;
1184 } while(!tsch_schedule_slot_operation(t, prev_slot_start, time_to_next_active_slot, "main"));
1185 }
1186
1187 tsch_in_slot_operation = 0;
1188 PT_YIELD(&slot_operation_pt);
1189 }
1190
1191 PT_END(&slot_operation_pt);
1192}
1193/*---------------------------------------------------------------------------*/
1194/* Set global time before starting slot operation,
1195 * with a rtimer time and an ASN */
1196void
1198{
1199 static struct rtimer slot_operation_timer;
1200 rtimer_clock_t time_to_next_active_slot;
1201 rtimer_clock_t prev_slot_start;
1202 TSCH_DEBUG_INIT();
1203 do {
1204 uint16_t timeslot_diff;
1205 /* Get next active link */
1206 current_link = tsch_schedule_get_next_active_link(&tsch_current_asn, &timeslot_diff, &backup_link);
1207 if(current_link == NULL) {
1208 /* There is no next link. Fall back to default
1209 * behavior: wake up at the next slot. */
1210 timeslot_diff = 1;
1211 }
1212 /* Update ASN */
1213 TSCH_ASN_INC(tsch_current_asn, timeslot_diff);
1214 /* Time to next wake up */
1215 time_to_next_active_slot = timeslot_diff * tsch_timing[tsch_ts_timeslot_length];
1216 /* Compensate for the base drift */
1217 time_to_next_active_slot += tsch_timesync_adaptive_compensate(time_to_next_active_slot);
1218 /* Update current slot start */
1219 prev_slot_start = current_slot_start;
1220 current_slot_start += time_to_next_active_slot;
1221 } while(!tsch_schedule_slot_operation(&slot_operation_timer, prev_slot_start, time_to_next_active_slot, "assoc"));
1222}
1223/*---------------------------------------------------------------------------*/
1224/* Start actual slot operation */
1225void
1226tsch_slot_operation_sync(rtimer_clock_t next_slot_start,
1227 struct tsch_asn_t *next_slot_asn)
1228{
1229 int_master_status_t status;
1230
1231 current_slot_start = next_slot_start;
1232 tsch_current_asn = *next_slot_asn;
1233 status = critical_enter();
1234 last_sync_asn = tsch_current_asn;
1235 tsch_last_sync_time = clock_time();
1236 critical_exit(status);
1237 current_link = NULL;
1238}
1239/*---------------------------------------------------------------------------*/
1240/** @} */
A MAC framer for IEEE 802.15.4.
clock_time_t clock_time(void)
Get the current clock time.
Definition clock.c:118
void watchdog_periodic(void)
Writes the WDT clear sequence.
Definition watchdog.c:85
#define CLOCK_SECOND
A second, measured in system clock time.
Definition clock.h:103
static void critical_exit(int_master_status_t status)
Exit a critical section and restore the master interrupt.
Definition critical.h:81
static int_master_status_t critical_enter()
Enter a critical section.
Definition critical.h:65
int frame802154_parse(uint8_t *data, int len, frame802154_t *pf)
Parses an input frame.
uint32_t int_master_status_t
Master interrupt state representation data type.
Definition int-master.h:62
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.
void process_poll(struct process *p)
Request a process to be polled.
Definition process.c:375
#define PT_YIELD(pt)
Yield from the current protothread.
Definition pt.h:455
#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_SPAWN(pt, child, thread)
Spawn a child protothread and wait until it exits.
Definition pt.h:371
#define RADIO_RX_MODE_ADDRESS_FILTER
Enable address-based frame filtering.
Definition radio.h:451
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_PARAM_LAST_PACKET_TIMESTAMP
Last packet timestamp, of type rtimer_clock_t.
Definition radio.h:286
@ RADIO_PARAM_LAST_RSSI
The RSSI value of the last received packet.
Definition radio.h:226
@ 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_LAST_LINK_QUALITY
Link quality indicator of the last received packet.
Definition radio.h:244
@ RADIO_TX_OK
TX was successful and where an ACK was requested one was received.
Definition radio.h:498
int rtimer_set(struct rtimer *rtimer, rtimer_clock_t time, rtimer_clock_t duration, rtimer_callback_t func, void *ptr)
Post a real-time task.
Definition rtimer.c:57
#define RTIMER_NOW()
Get the current clock time.
Definition rtimer.h:187
@ RTIMER_OK
rtimer task is scheduled successfully
Definition rtimer.h:145
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
int tsch_packet_update_eb(uint8_t *buf, int buf_size, uint8_t tsch_sync_ie_offset)
Update ASN in EB packet.
void tsch_queue_update_all_backoff_windows(const linkaddr_t *dest_addr)
Decrement backoff window for the queue(s) able to Tx to a given address.
Definition tsch-queue.c:520
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,...
uint64_t tsch_get_network_uptime_ticks(void)
Get the time, in clock ticks, since the TSCH network was started.
int tsch_packet_parse_eack(const uint8_t *buf, int buf_size, uint8_t seqno, frame802154_t *frame, struct ieee802154_ies *ies, uint8_t *hdr_len)
Parse enhanced ACK packet.
int tsch_packet_create_eack(uint8_t *buf, uint16_t buf_len, const linkaddr_t *dest_addr, uint8_t seqno, int16_t drift, int nack)
Construct Enhanced ACK packet.
Definition tsch-packet.c:93
#define TSCH_ASN_INC(asn, inc)
Increment an ASN by inc (32 bits)
Definition tsch-asn.h:68
#define TSCH_ASN_DIFF(asn1, asn2)
Returns the 32-bit diff between asn1 and asn2.
Definition tsch-asn.h:82
struct tsch_packet * tsch_queue_get_packet_for_nbr(const struct tsch_neighbor *n, struct tsch_link *link)
Returns the first packet that can be sent from a queue on a given link.
Definition tsch-queue.c:425
static void tsch_radio_on(enum tsch_radio_state_on_cmd command)
This function turns on the radio.
int tsch_packet_get_frame_pending(uint8_t *buf, int buf_size)
Get frame pending bit from a packet.
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
#define TSCH_LOG_ADD(log_type, init_code)
Use this macro to add a log to the queue (will be printed out later, after leaving interrupt context)
Definition tsch-log.h:141
int tsch_get_lock(void)
Takes the TSCH lock.
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.
static void tsch_radio_off(enum tsch_radio_state_off_cmd command)
This function turns off the radio.
void tsch_release_lock(void)
Releases the TSCH lock.
int32_t tsch_timesync_adaptive_compensate(rtimer_clock_t delta_ticks)
Computes time compensation for a given point in the future.
int tsch_queue_packet_sent(struct tsch_neighbor *n, struct tsch_packet *p, struct tsch_link *link, uint8_t mac_tx_status)
Updates neighbor queue state after a transmission.
Definition tsch-queue.c:337
#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
struct tsch_packet * tsch_queue_get_unicast_packet_for_any(struct tsch_neighbor **n, struct tsch_link *link)
Gets the head packet of any neighbor queue with zero backoff counter.
Definition tsch-queue.c:464
unsigned int tsch_security_secure_frame(uint8_t *hdr, uint8_t *outbuf, int hdrlen, int datalen, struct tsch_asn_t *asn)
Protect a frame with encryption and/or MIC.
int tsch_is_locked(void)
Checks if the TSCH lock is set.
static uint8_t tsch_calculate_channel(struct tsch_asn_t *asn, uint16_t channel_offset)
Returns a 802.15.4 channel from an ASN and channel offset.
void tsch_packet_set_frame_pending(uint8_t *buf, int buf_size)
Set frame pending bit in a packet (whose header was already build)
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_disassociate(void)
Leave the TSCH network we are currently in.
Definition tsch.c:586
void tsch_timesync_update(struct tsch_neighbor *n, uint16_t time_delta_asn, int32_t drift_correction)
Updates timesync information for a given neighbor.
void tsch_slot_operation_start(void)
Start actual slot operation.
Header file for the logging system.
@ MAC_TX_COLLISION
The MAC layer did not get an acknowledgement for the packet.
Definition mac.h:97
@ MAC_TX_OK
The MAC layer transmission was OK.
Definition mac.h:93
@ MAC_TX_NOACK
The MAC layer deferred the transmission for a later time.
Definition mac.h:100
@ MAC_TX_ERR_FATAL
The MAC layer transmission could not be performed because of insufficient queue space,...
Definition mac.h:112
@ 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)
Header file for the Packet buffer (packetbuf) management.
Header file for the Packet queue buffer management.
Header file for the radio API.
int ringbufindex_put(struct ringbufindex *r)
Put one element to the ring buffer.
int ringbufindex_peek_put(const struct ringbufindex *r)
Check if there is space to put an element.
frame802154_scf_t security_control
Security control bitfield.
uint8_t frame_type
3 bit.
uint8_t ack_required
1 bit.
uint8_t security_level
3 bit.
Parameters used by the frame802154_create() function.
uint8_t seq
Sequence number.
frame802154_aux_hdr_t aux_hdr
Aux security header.
uint8_t src_addr[8]
Source address.
frame802154_fcf_t fcf
Frame control field
Stores data about an incoming packet.
Definition tsch-types.h:148
Representation of a real-time task.
Definition rtimer.h:135
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
Main API declarations for TSCH.