Contiki-NG
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 
109 enum 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 
115 enum 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 */
123 struct ringbufindex dequeued_ringbuf;
124 struct tsch_packet *dequeued_array[TSCH_DEQUEUED_ARRAY_SIZE];
125 /* A ringbuf storing incoming packets.
126  * Will be processed layer by tsch_rx_process_pending */
127 struct ringbufindex input_ringbuf;
128 struct 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) */
132 static struct tsch_asn_t last_sync_asn;
133 clock_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 */
136 static volatile int tsch_locked = 0;
137 /* As long as this is set, skip all slot operation */
138 static volatile int tsch_lock_requested = 0;
139 
140 /* Last estimated drift in RTIMER ticks
141  * (Sky: 1 tick = 30.517578125 usec exactly) */
142 static int32_t drift_correction = 0;
143 /* Is drift correction used? (Can be true even if drift_correction == 0) */
144 static uint8_t is_drift_correction_used;
145 
146 /* Used from tsch_slot_operation and sub-protothreads */
147 static rtimer_clock_t volatile current_slot_start;
148 
149 /* Are we currently inside a slot? */
150 static volatile int tsch_in_slot_operation = 0;
151 
152 /* If we are inside a slot, these tell the current channel and channel offset */
153 uint8_t tsch_current_channel;
154 uint8_t tsch_current_channel_offset;
155 
156 /* Info about the link, packet and neighbor of
157  * the current (or next) slot */
158 struct 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. */
162 static struct tsch_link *backup_link = NULL;
163 static struct tsch_packet *current_packet = NULL;
164 static struct tsch_neighbor *current_neighbor = NULL;
165 
166 /* Indicates whether an extra link is needed to handle the current burst */
167 static int burst_link_scheduled = 0;
168 /* Counts the length of the current burst */
169 int tsch_current_burst_count = 0;
170 
171 /* Protothread for association */
172 PT_THREAD(tsch_scan(struct pt *pt));
173 /* Protothread for slot operation, called from rtimer interrupt
174  * and scheduled from tsch_schedule_slot_operation */
175 static PT_THREAD(tsch_slot_operation(struct rtimer *t, void *ptr));
176 static struct pt slot_operation_pt;
177 /* Sub-protothreads of tsch_slot_operation */
178 static PT_THREAD(tsch_tx_slot(struct pt *pt, struct rtimer *t));
179 static 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? */
185 int
187 {
188  return tsch_locked;
189 }
190 
191 /* Lock TSCH (no slot operation) */
192 int
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 */
231 void
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 */
241 static uint8_t
242 tsch_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  */
264 static uint8_t
265 tsch_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. */
278 static uint8_t
279 check_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. */
300 static uint8_t
301 tsch_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  RTIMER_BUSYWAIT_UNTIL_ABS(0, ref_time, offset); \
335  } \
336  } while(0);
337 /*---------------------------------------------------------------------------*/
338 /* Get EB, broadcast or unicast packet to be sent, and target neighbor. */
339 static struct tsch_packet *
340 get_packet_and_neighbor_for_link(struct tsch_link *link, struct tsch_neighbor **target_neighbor)
341 {
342  struct tsch_packet *p = NULL;
343  struct tsch_neighbor *n = NULL;
344 
345  /* Is this a Tx link? */
346  if(link->link_options & LINK_OPTION_TX) {
347  /* is it for advertisement of EB? */
348  if(link->link_type == LINK_TYPE_ADVERTISING || link->link_type == LINK_TYPE_ADVERTISING_ONLY) {
349  /* fetch EB packets */
350  n = n_eb;
351  p = tsch_queue_get_packet_for_nbr(n, link);
352  }
353  if(link->link_type != LINK_TYPE_ADVERTISING_ONLY) {
354  /* NORMAL link or no EB to send, pick a data packet */
355  if(p == NULL) {
356  /* Get neighbor queue associated to the link and get packet from it */
357  n = tsch_queue_get_nbr(&link->addr);
358  p = tsch_queue_get_packet_for_nbr(n, link);
359  /* if it is a broadcast slot and there were no broadcast packets, pick any unicast packet */
360  if(p == NULL && n == n_broadcast) {
362  }
363  }
364  }
365  }
366  /* return nbr (by reference) */
367  if(target_neighbor != NULL) {
368  *target_neighbor = n;
369  }
370 
371  return p;
372 }
373 /*---------------------------------------------------------------------------*/
374 static
375 void update_link_backoff(struct tsch_link *link) {
376  if(link != NULL
377  && (link->link_options & LINK_OPTION_TX)
378  && (link->link_options & LINK_OPTION_SHARED)) {
379  /* Decrement the backoff window for all neighbors able to transmit over
380  * this Tx, Shared link. */
382  }
383 }
384 /*---------------------------------------------------------------------------*/
385 uint64_t
387 {
388  uint64_t uptime_asn;
389  uint64_t uptime_ticks;
390  int_master_status_t status;
391 
392  if(!tsch_is_associated) {
393  /* not associated, network uptime is not known */
394  return (uint64_t)-1;
395  }
396 
397  status = critical_enter();
398 
399  uptime_asn = last_sync_asn.ls4b + ((uint64_t)last_sync_asn.ms1b << 32);
400  /* first calculate the at the uptime at the last sync in rtimer ticks */
401  uptime_ticks = uptime_asn * tsch_timing[tsch_ts_timeslot_length];
402  /* then convert to clock ticks (assume that CLOCK_SECOND divides RTIMER_ARCH_SECOND) */
403  uptime_ticks /= (RTIMER_ARCH_SECOND / CLOCK_SECOND);
404  /* then add the ticks passed since the last timesync */
405  uptime_ticks += (clock_time() - tsch_last_sync_time);
406 
407  critical_exit(status);
408 
409  return uptime_ticks;
410 }
411 /*---------------------------------------------------------------------------*/
412 /**
413  * This function turns on the radio. Its semantics is dependent on
414  * the value of TSCH_RADIO_ON_DURING_TIMESLOT constant:
415  * - if enabled, the radio is turned on at the start of the slot
416  * - if disabled, the radio is turned on within the slot,
417  * directly before the packet Rx guard time and ACK Rx guard time.
418  */
419 static void
420 tsch_radio_on(enum tsch_radio_state_on_cmd command)
421 {
422  int do_it = 0;
423  switch(command) {
424  case TSCH_RADIO_CMD_ON_START_OF_TIMESLOT:
425  if(TSCH_RADIO_ON_DURING_TIMESLOT) {
426  do_it = 1;
427  }
428  break;
429  case TSCH_RADIO_CMD_ON_WITHIN_TIMESLOT:
430  if(!TSCH_RADIO_ON_DURING_TIMESLOT) {
431  do_it = 1;
432  }
433  break;
434  case TSCH_RADIO_CMD_ON_FORCE:
435  do_it = 1;
436  break;
437  }
438  if(do_it) {
439  NETSTACK_RADIO.on();
440  }
441 }
442 /*---------------------------------------------------------------------------*/
443 /**
444  * This function turns off the radio. In the same way as for tsch_radio_on(),
445  * it depends on the value of TSCH_RADIO_ON_DURING_TIMESLOT constant:
446  * - if enabled, the radio is turned off at the end of the slot
447  * - if disabled, the radio is turned off within the slot,
448  * directly after Tx'ing or Rx'ing a packet or Tx'ing an ACK.
449  */
450 static void
451 tsch_radio_off(enum tsch_radio_state_off_cmd command)
452 {
453  int do_it = 0;
454  switch(command) {
455  case TSCH_RADIO_CMD_OFF_END_OF_TIMESLOT:
456  if(TSCH_RADIO_ON_DURING_TIMESLOT) {
457  do_it = 1;
458  }
459  break;
460  case TSCH_RADIO_CMD_OFF_WITHIN_TIMESLOT:
461  if(!TSCH_RADIO_ON_DURING_TIMESLOT) {
462  do_it = 1;
463  }
464  break;
465  case TSCH_RADIO_CMD_OFF_FORCE:
466  do_it = 1;
467  break;
468  }
469  if(do_it) {
470  NETSTACK_RADIO.off();
471  }
472 }
473 /*---------------------------------------------------------------------------*/
474 static
475 PT_THREAD(tsch_tx_slot(struct pt *pt, struct rtimer *t))
476 {
477  /**
478  * TX slot:
479  * 1. Copy packet to radio buffer
480  * 2. Perform CCA if enabled
481  * 3. Sleep until it is time to transmit
482  * 4. Wait for ACK if it is a unicast packet
483  * 5. Extract drift if we received an E-ACK from a time source neighbor
484  * 6. Update CSMA parameters according to TX status
485  * 7. Schedule mac_call_sent_callback
486  **/
487 
488  /* tx status */
489  static uint8_t mac_tx_status;
490  /* is the packet in its neighbor's queue? */
491  uint8_t in_queue;
492  static int dequeued_index;
493  static int packet_ready = 1;
494 
495  PT_BEGIN(pt);
496 
497  TSCH_DEBUG_TX_EVENT();
498 
499  /* First check if we have space to store a newly dequeued packet (in case of
500  * successful Tx or Drop) */
501  dequeued_index = ringbufindex_peek_put(&dequeued_ringbuf);
502  if(dequeued_index != -1) {
503  if(current_packet == NULL || current_packet->qb == NULL) {
504  mac_tx_status = MAC_TX_ERR_FATAL;
505  } else {
506  /* packet payload */
507  static void *packet;
508 #if LLSEC802154_ENABLED
509  /* encrypted payload */
510  static uint8_t encrypted_packet[TSCH_PACKET_MAX_LEN];
511 #endif /* LLSEC802154_ENABLED */
512  /* packet payload length */
513  static uint8_t packet_len;
514  /* packet seqno */
515  static uint8_t seqno;
516  /* wait for ack? */
517  static uint8_t do_wait_for_ack;
518  static rtimer_clock_t tx_start_time;
519  /* Did we set the frame pending bit to request an extra burst link? */
520  static int burst_link_requested;
521 
522 #if TSCH_CCA_ENABLED
523  static uint8_t cca_status;
524 #endif /* TSCH_CCA_ENABLED */
525 
526  /* get payload */
527  packet = queuebuf_dataptr(current_packet->qb);
528  packet_len = queuebuf_datalen(current_packet->qb);
529  /* if is this a broadcast packet, don't wait for ack */
530  do_wait_for_ack = !current_neighbor->is_broadcast;
531  /* Unicast. More packets in queue for the neighbor? */
532  burst_link_requested = 0;
533  if(do_wait_for_ack
534  && tsch_current_burst_count + 1 < TSCH_BURST_MAX_LEN
535  && tsch_queue_nbr_packet_count(current_neighbor) > 1) {
536  burst_link_requested = 1;
537  tsch_packet_set_frame_pending(packet, packet_len);
538  }
539  /* read seqno from payload */
540  seqno = ((uint8_t *)(packet))[2];
541  /* if this is an EB, then update its Sync-IE */
542  if(current_neighbor == n_eb) {
543  packet_ready = tsch_packet_update_eb(packet, packet_len, current_packet->tsch_sync_ie_offset);
544  } else {
545  packet_ready = 1;
546  }
547 
548 #if LLSEC802154_ENABLED
549  if(tsch_is_pan_secured) {
550  /* If we are going to encrypt, we need to generate the output in a separate buffer and keep
551  * the original untouched. This is to allow for future retransmissions. */
552  int with_encryption = queuebuf_attr(current_packet->qb, PACKETBUF_ATTR_SECURITY_LEVEL) & 0x4;
553  packet_len += tsch_security_secure_frame(packet, with_encryption ? encrypted_packet : packet, current_packet->header_len,
554  packet_len - current_packet->header_len, &tsch_current_asn);
555  if(with_encryption) {
556  packet = encrypted_packet;
557  }
558  }
559 #endif /* LLSEC802154_ENABLED */
560 
561  /* prepare packet to send: copy to radio buffer */
562  if(packet_ready && NETSTACK_RADIO.prepare(packet, packet_len) == 0) { /* 0 means success */
563  static rtimer_clock_t tx_duration;
564 
565 #if TSCH_CCA_ENABLED
566  cca_status = 1;
567  /* delay before CCA */
568  TSCH_SCHEDULE_AND_YIELD(pt, t, current_slot_start, tsch_timing[tsch_ts_cca_offset], "cca");
569  TSCH_DEBUG_TX_EVENT();
570  tsch_radio_on(TSCH_RADIO_CMD_ON_WITHIN_TIMESLOT);
571  /* CCA */
572  RTIMER_BUSYWAIT_UNTIL_ABS(!(cca_status &= NETSTACK_RADIO.channel_clear()),
573  current_slot_start, tsch_timing[tsch_ts_cca_offset] + tsch_timing[tsch_ts_cca]);
574  TSCH_DEBUG_TX_EVENT();
575  /* there is not enough time to turn radio off */
576  /* NETSTACK_RADIO.off(); */
577  if(cca_status == 0) {
578  mac_tx_status = MAC_TX_COLLISION;
579  } else
580 #endif /* TSCH_CCA_ENABLED */
581  {
582  /* delay before TX */
583  TSCH_SCHEDULE_AND_YIELD(pt, t, current_slot_start, tsch_timing[tsch_ts_tx_offset] - RADIO_DELAY_BEFORE_TX, "TxBeforeTx");
584  TSCH_DEBUG_TX_EVENT();
585  /* send packet already in radio tx buffer */
586  mac_tx_status = NETSTACK_RADIO.transmit(packet_len);
587  tx_count++;
588  /* Save tx timestamp */
589  tx_start_time = current_slot_start + tsch_timing[tsch_ts_tx_offset];
590  /* calculate TX duration based on sent packet len */
591  tx_duration = TSCH_PACKET_DURATION(packet_len);
592  /* limit tx_time to its max value */
593  tx_duration = MIN(tx_duration, tsch_timing[tsch_ts_max_tx]);
594  /* turn tadio off -- will turn on again to wait for ACK if needed */
595  tsch_radio_off(TSCH_RADIO_CMD_OFF_WITHIN_TIMESLOT);
596 
597  if(mac_tx_status == RADIO_TX_OK) {
598  if(do_wait_for_ack) {
599  uint8_t ackbuf[TSCH_PACKET_MAX_LEN];
600  int ack_len;
601  rtimer_clock_t ack_start_time;
602  int is_time_source;
603  struct ieee802154_ies ack_ies;
604  uint8_t ack_hdrlen;
605  frame802154_t frame;
606 
607 #if TSCH_HW_FRAME_FILTERING
608  radio_value_t radio_rx_mode;
609  /* Entering promiscuous mode so that the radio accepts the enhanced ACK */
610  NETSTACK_RADIO.get_value(RADIO_PARAM_RX_MODE, &radio_rx_mode);
611  NETSTACK_RADIO.set_value(RADIO_PARAM_RX_MODE, radio_rx_mode & (~RADIO_RX_MODE_ADDRESS_FILTER));
612 #endif /* TSCH_HW_FRAME_FILTERING */
613  /* Unicast: wait for ack after tx: sleep until ack time */
614  TSCH_SCHEDULE_AND_YIELD(pt, t, current_slot_start,
615  tsch_timing[tsch_ts_tx_offset] + tx_duration + tsch_timing[tsch_ts_rx_ack_delay] - RADIO_DELAY_BEFORE_RX, "TxBeforeAck");
616  TSCH_DEBUG_TX_EVENT();
617  tsch_radio_on(TSCH_RADIO_CMD_ON_WITHIN_TIMESLOT);
618  /* Wait for ACK to come */
619  RTIMER_BUSYWAIT_UNTIL_ABS(NETSTACK_RADIO.receiving_packet(),
620  tx_start_time, tx_duration + tsch_timing[tsch_ts_rx_ack_delay] + tsch_timing[tsch_ts_ack_wait] + RADIO_DELAY_BEFORE_DETECT);
621  TSCH_DEBUG_TX_EVENT();
622 
623  ack_start_time = RTIMER_NOW() - RADIO_DELAY_BEFORE_DETECT;
624 
625  /* Wait for ACK to finish */
626  RTIMER_BUSYWAIT_UNTIL_ABS(!NETSTACK_RADIO.receiving_packet(),
627  ack_start_time, tsch_timing[tsch_ts_max_ack]);
628  TSCH_DEBUG_TX_EVENT();
629  tsch_radio_off(TSCH_RADIO_CMD_OFF_WITHIN_TIMESLOT);
630 
631 #if TSCH_HW_FRAME_FILTERING
632  /* Leaving promiscuous mode */
633  NETSTACK_RADIO.get_value(RADIO_PARAM_RX_MODE, &radio_rx_mode);
634  NETSTACK_RADIO.set_value(RADIO_PARAM_RX_MODE, radio_rx_mode | RADIO_RX_MODE_ADDRESS_FILTER);
635 #endif /* TSCH_HW_FRAME_FILTERING */
636 
637  /* Read ack frame */
638  ack_len = NETSTACK_RADIO.read((void *)ackbuf, sizeof(ackbuf));
639 
640  is_time_source = 0;
641  /* The radio driver should return 0 if no valid packets are in the rx buffer */
642  if(ack_len > 0) {
643  is_time_source = current_neighbor != NULL && current_neighbor->is_time_source;
644  if(tsch_packet_parse_eack(ackbuf, ack_len, seqno,
645  &frame, &ack_ies, &ack_hdrlen) == 0) {
646  ack_len = 0;
647  }
648 
649 #if LLSEC802154_ENABLED
650  if(ack_len != 0) {
651  if(!tsch_security_parse_frame(ackbuf, ack_hdrlen, ack_len - ack_hdrlen - tsch_security_mic_len(&frame),
652  &frame, tsch_queue_get_nbr_address(current_neighbor), &tsch_current_asn)) {
653  TSCH_LOG_ADD(tsch_log_message,
654  snprintf(log->message, sizeof(log->message),
655  "!failed to authenticate ACK"));
656  ack_len = 0;
657  }
658  } else {
659  TSCH_LOG_ADD(tsch_log_message,
660  snprintf(log->message, sizeof(log->message),
661  "!failed to parse ACK"));
662  }
663 #endif /* LLSEC802154_ENABLED */
664  }
665 
666  if(ack_len != 0) {
667  if(is_time_source) {
668  int32_t eack_time_correction = US_TO_RTIMERTICKS(ack_ies.ie_time_correction);
669  int32_t since_last_timesync = TSCH_ASN_DIFF(tsch_current_asn, last_sync_asn);
670  if(eack_time_correction > SYNC_IE_BOUND) {
671  drift_correction = SYNC_IE_BOUND;
672  } else if(eack_time_correction < -SYNC_IE_BOUND) {
673  drift_correction = -SYNC_IE_BOUND;
674  } else {
675  drift_correction = eack_time_correction;
676  }
677  if(drift_correction != eack_time_correction) {
678  TSCH_LOG_ADD(tsch_log_message,
679  snprintf(log->message, sizeof(log->message),
680  "!truncated dr %d %d", (int)eack_time_correction, (int)drift_correction);
681  );
682  }
683  tsch_stats_on_time_synchronization(eack_time_correction);
684  is_drift_correction_used = 1;
685  tsch_timesync_update(current_neighbor, since_last_timesync, drift_correction);
686  /* Keep track of sync time */
687  last_sync_asn = tsch_current_asn;
688  tsch_last_sync_time = clock_time();
690  }
691  mac_tx_status = MAC_TX_OK;
692 
693  /* We requested an extra slot and got an ack. This means
694  the extra slot will be scheduled at the received */
695  if(burst_link_requested) {
696  burst_link_scheduled = 1;
697  }
698  } else {
699  mac_tx_status = MAC_TX_NOACK;
700  }
701  } else {
702  mac_tx_status = MAC_TX_OK;
703  }
704  } else {
705  mac_tx_status = MAC_TX_ERR;
706  }
707  }
708  } else {
709  mac_tx_status = MAC_TX_ERR;
710  }
711  }
712 
713  tsch_radio_off(TSCH_RADIO_CMD_OFF_END_OF_TIMESLOT);
714 
715  current_packet->transmissions++;
716  current_packet->ret = mac_tx_status;
717 
718  /* Post TX: Update neighbor queue state */
719  in_queue = tsch_queue_packet_sent(current_neighbor, current_packet, current_link, mac_tx_status);
720 
721  /* The packet was dequeued, add it to dequeued_ringbuf for later processing */
722  if(in_queue == 0) {
723  dequeued_array[dequeued_index] = current_packet;
724  ringbufindex_put(&dequeued_ringbuf);
725  }
726 
727  /* If this is an unicast packet to timesource, update stats */
728  if(current_neighbor != NULL && current_neighbor->is_time_source) {
729  tsch_stats_tx_packet(current_neighbor, mac_tx_status, tsch_current_channel);
730  }
731 
732  /* Log every tx attempt */
733  TSCH_LOG_ADD(tsch_log_tx,
734  log->tx.mac_tx_status = mac_tx_status;
735  log->tx.num_tx = current_packet->transmissions;
736  log->tx.datalen = queuebuf_datalen(current_packet->qb);
737  log->tx.drift = drift_correction;
738  log->tx.drift_used = is_drift_correction_used;
739  log->tx.is_data = ((((uint8_t *)(queuebuf_dataptr(current_packet->qb)))[0]) & 7) == FRAME802154_DATAFRAME;
740 #if LLSEC802154_ENABLED
741  log->tx.sec_level = queuebuf_attr(current_packet->qb, PACKETBUF_ATTR_SECURITY_LEVEL);
742 #else /* LLSEC802154_ENABLED */
743  log->tx.sec_level = 0;
744 #endif /* LLSEC802154_ENABLED */
745  linkaddr_copy(&log->tx.dest, queuebuf_addr(current_packet->qb, PACKETBUF_ADDR_RECEIVER));
746  log->tx.seqno = queuebuf_attr(current_packet->qb, PACKETBUF_ATTR_MAC_SEQNO);
747  );
748 
749  /* Poll process for later processing of packet sent events and logs */
750  process_poll(&tsch_pending_events_process);
751  }
752 
753  TSCH_DEBUG_TX_EVENT();
754 
755  PT_END(pt);
756 }
757 /*---------------------------------------------------------------------------*/
758 static
759 PT_THREAD(tsch_rx_slot(struct pt *pt, struct rtimer *t))
760 {
761  /**
762  * RX slot:
763  * 1. Check if it is used for TIME_KEEPING
764  * 2. Sleep and wake up just before expected RX time (with a guard time: TS_LONG_GT)
765  * 3. Check for radio activity for the guard time: TS_LONG_GT
766  * 4. Prepare and send ACK if needed
767  * 5. Drift calculated in the ACK callback registered with the radio driver. Use it if receiving from a time source neighbor.
768  **/
769 
770  struct tsch_neighbor *n;
771  static linkaddr_t source_address;
772  static linkaddr_t destination_address;
773  static int16_t input_index;
774  static int input_queue_drop = 0;
775 
776  PT_BEGIN(pt);
777 
778  TSCH_DEBUG_RX_EVENT();
779 
780  input_index = ringbufindex_peek_put(&input_ringbuf);
781  if(input_index == -1) {
782  input_queue_drop++;
783  } else {
784  static struct input_packet *current_input;
785  /* Estimated drift based on RX time */
786  static int32_t estimated_drift;
787  /* Rx timestamps */
788  static rtimer_clock_t rx_start_time;
789  static rtimer_clock_t expected_rx_time;
790  static rtimer_clock_t packet_duration;
791  uint8_t packet_seen;
792 
793  expected_rx_time = current_slot_start + tsch_timing[tsch_ts_tx_offset];
794  /* Default start time: expected Rx time */
795  rx_start_time = expected_rx_time;
796 
797  current_input = &input_array[input_index];
798 
799  /* Wait before starting to listen */
800  TSCH_SCHEDULE_AND_YIELD(pt, t, current_slot_start, tsch_timing[tsch_ts_rx_offset] - RADIO_DELAY_BEFORE_RX, "RxBeforeListen");
801  TSCH_DEBUG_RX_EVENT();
802 
803  /* Start radio for at least guard time */
804  tsch_radio_on(TSCH_RADIO_CMD_ON_WITHIN_TIMESLOT);
805  packet_seen = NETSTACK_RADIO.receiving_packet() || NETSTACK_RADIO.pending_packet();
806  if(!packet_seen) {
807  /* Check if receiving within guard time */
808  RTIMER_BUSYWAIT_UNTIL_ABS((packet_seen = (NETSTACK_RADIO.receiving_packet() || NETSTACK_RADIO.pending_packet())),
809  current_slot_start, tsch_timing[tsch_ts_rx_offset] + tsch_timing[tsch_ts_rx_wait] + RADIO_DELAY_BEFORE_DETECT);
810  }
811  if(!packet_seen) {
812  /* no packets on air */
813  tsch_radio_off(TSCH_RADIO_CMD_OFF_FORCE);
814  } else {
815  TSCH_DEBUG_RX_EVENT();
816  /* Save packet timestamp */
817  rx_start_time = RTIMER_NOW() - RADIO_DELAY_BEFORE_DETECT;
818 
819  /* Wait until packet is received, turn radio off */
820  RTIMER_BUSYWAIT_UNTIL_ABS(!NETSTACK_RADIO.receiving_packet(),
821  current_slot_start, tsch_timing[tsch_ts_rx_offset] + tsch_timing[tsch_ts_rx_wait] + tsch_timing[tsch_ts_max_tx]);
822  TSCH_DEBUG_RX_EVENT();
823  tsch_radio_off(TSCH_RADIO_CMD_OFF_WITHIN_TIMESLOT);
824 
825  if(NETSTACK_RADIO.pending_packet()) {
826  static int frame_valid;
827  static int header_len;
828  static frame802154_t frame;
829  radio_value_t radio_last_rssi;
830  radio_value_t radio_last_lqi;
831 
832  /* Read packet */
833  current_input->len = NETSTACK_RADIO.read((void *)current_input->payload, TSCH_PACKET_MAX_LEN);
834  NETSTACK_RADIO.get_value(RADIO_PARAM_LAST_RSSI, &radio_last_rssi);
835  current_input->rx_asn = tsch_current_asn;
836  current_input->rssi = (signed)radio_last_rssi;
837  current_input->channel = tsch_current_channel;
838  header_len = frame802154_parse((uint8_t *)current_input->payload, current_input->len, &frame);
839  frame_valid = header_len > 0 &&
840  frame802154_check_dest_panid(&frame) &&
841  frame802154_extract_linkaddr(&frame, &source_address, &destination_address);
842 
843 #if TSCH_RESYNC_WITH_SFD_TIMESTAMPS
844  /* At the end of the reception, get an more accurate estimate of SFD arrival time */
845  NETSTACK_RADIO.get_object(RADIO_PARAM_LAST_PACKET_TIMESTAMP, &rx_start_time, sizeof(rtimer_clock_t));
846 #endif
847 
848  packet_duration = TSCH_PACKET_DURATION(current_input->len);
849  /* limit packet_duration to its max value */
850  packet_duration = MIN(packet_duration, tsch_timing[tsch_ts_max_tx]);
851 
852  if(!frame_valid) {
853  TSCH_LOG_ADD(tsch_log_message,
854  snprintf(log->message, sizeof(log->message),
855  "!failed to parse frame %u %u", header_len, current_input->len));
856  }
857 
858  if(frame_valid) {
859  if(frame.fcf.frame_type != FRAME802154_DATAFRAME
860  && frame.fcf.frame_type != FRAME802154_BEACONFRAME) {
861  TSCH_LOG_ADD(tsch_log_message,
862  snprintf(log->message, sizeof(log->message),
863  "!discarding frame with type %u, len %u", frame.fcf.frame_type, current_input->len));
864  frame_valid = 0;
865  }
866  }
867 
868 #if LLSEC802154_ENABLED
869  /* Decrypt and verify incoming frame */
870  if(frame_valid) {
872  current_input->payload, header_len, current_input->len - header_len - tsch_security_mic_len(&frame),
873  &frame, &source_address, &tsch_current_asn)) {
874  current_input->len -= tsch_security_mic_len(&frame);
875  } else {
876  TSCH_LOG_ADD(tsch_log_message,
877  snprintf(log->message, sizeof(log->message),
878  "!failed to authenticate frame %u", current_input->len));
879  frame_valid = 0;
880  }
881  }
882 #endif /* LLSEC802154_ENABLED */
883 
884  if(frame_valid) {
885  /* Check that frome is for us or broadcast, AND that it is not from
886  * ourselves. This is for consistency with CSMA and to avoid adding
887  * ourselves to neighbor tables in case frames are being replayed. */
888  if((linkaddr_cmp(&destination_address, &linkaddr_node_addr)
889  || linkaddr_cmp(&destination_address, &linkaddr_null))
890  && !linkaddr_cmp(&source_address, &linkaddr_node_addr)) {
891  int do_nack = 0;
892  rx_count++;
893  estimated_drift = RTIMER_CLOCK_DIFF(expected_rx_time, rx_start_time);
894  tsch_stats_on_time_synchronization(estimated_drift);
895 
896 #if TSCH_TIMESYNC_REMOVE_JITTER
897  /* remove jitter due to measurement errors */
898  if(ABS(estimated_drift) <= TSCH_TIMESYNC_MEASUREMENT_ERROR) {
899  estimated_drift = 0;
900  } else if(estimated_drift > 0) {
901  estimated_drift -= TSCH_TIMESYNC_MEASUREMENT_ERROR;
902  } else {
903  estimated_drift += TSCH_TIMESYNC_MEASUREMENT_ERROR;
904  }
905 #endif
906 
907 #ifdef TSCH_CALLBACK_DO_NACK
908  if(frame.fcf.ack_required) {
909  do_nack = TSCH_CALLBACK_DO_NACK(current_link,
910  &source_address, &destination_address);
911  }
912 #endif
913 
914  if(frame.fcf.ack_required) {
915  static uint8_t ack_buf[TSCH_PACKET_MAX_LEN];
916  static int ack_len;
917 
918  /* Build ACK frame */
919  ack_len = tsch_packet_create_eack(ack_buf, sizeof(ack_buf),
920  &source_address, frame.seq, (int16_t)RTIMERTICKS_TO_US(estimated_drift), do_nack);
921 
922  if(ack_len > 0) {
923 #if LLSEC802154_ENABLED
924  if(tsch_is_pan_secured) {
925  /* Secure ACK frame. There is only header and header IEs, therefore data len == 0. */
926  ack_len += tsch_security_secure_frame(ack_buf, ack_buf, ack_len, 0, &tsch_current_asn);
927  }
928 #endif /* LLSEC802154_ENABLED */
929 
930  /* Copy to radio buffer */
931  NETSTACK_RADIO.prepare((const void *)ack_buf, ack_len);
932 
933  /* Wait for time to ACK and transmit ACK */
934  TSCH_SCHEDULE_AND_YIELD(pt, t, rx_start_time,
935  packet_duration + tsch_timing[tsch_ts_tx_ack_delay] - RADIO_DELAY_BEFORE_TX, "RxBeforeAck");
936  TSCH_DEBUG_RX_EVENT();
937  NETSTACK_RADIO.transmit(ack_len);
938  tsch_radio_off(TSCH_RADIO_CMD_OFF_WITHIN_TIMESLOT);
939 
940  /* Schedule a burst link iff the frame pending bit was set */
941  burst_link_scheduled = tsch_packet_get_frame_pending(current_input->payload, current_input->len);
942  }
943  }
944 
945  /* If the sender is a time source, proceed to clock drift compensation */
946  n = tsch_queue_get_nbr(&source_address);
947  if(n != NULL && n->is_time_source) {
948  int32_t since_last_timesync = TSCH_ASN_DIFF(tsch_current_asn, last_sync_asn);
949  /* Keep track of last sync time */
950  last_sync_asn = tsch_current_asn;
951  tsch_last_sync_time = clock_time();
952  /* Save estimated drift */
953  drift_correction = -estimated_drift;
954  is_drift_correction_used = 1;
955  sync_count++;
956  tsch_timesync_update(n, since_last_timesync, -estimated_drift);
958  }
959 
960  /* Add current input to ringbuf */
961  ringbufindex_put(&input_ringbuf);
962 
963  /* If the neighbor is known, update its stats */
964  if(n != NULL) {
965  NETSTACK_RADIO.get_value(RADIO_PARAM_LAST_LINK_QUALITY, &radio_last_lqi);
966  tsch_stats_rx_packet(n, current_input->rssi, radio_last_lqi, tsch_current_channel);
967  }
968 
969  /* Log every reception */
970  TSCH_LOG_ADD(tsch_log_rx,
971  linkaddr_copy(&log->rx.src, (linkaddr_t *)&frame.src_addr);
972  log->rx.is_unicast = frame.fcf.ack_required;
973  log->rx.datalen = current_input->len;
974  log->rx.drift = drift_correction;
975  log->rx.drift_used = is_drift_correction_used;
976  log->rx.is_data = frame.fcf.frame_type == FRAME802154_DATAFRAME;
977  log->rx.sec_level = frame.aux_hdr.security_control.security_level;
978  log->rx.estimated_drift = estimated_drift;
979  log->rx.seqno = frame.seq;
980  );
981  }
982 
983  /* Poll process for processing of pending input and logs */
984  process_poll(&tsch_pending_events_process);
985  }
986  }
987 
988  tsch_radio_off(TSCH_RADIO_CMD_OFF_END_OF_TIMESLOT);
989  }
990 
991  if(input_queue_drop != 0) {
992  TSCH_LOG_ADD(tsch_log_message,
993  snprintf(log->message, sizeof(log->message),
994  "!queue full skipped %u", input_queue_drop);
995  );
996  input_queue_drop = 0;
997  }
998  }
999 
1000  TSCH_DEBUG_RX_EVENT();
1001 
1002  PT_END(pt);
1003 }
1004 /*---------------------------------------------------------------------------*/
1005 /* Protothread for slot operation, called from rtimer interrupt
1006  * and scheduled from tsch_schedule_slot_operation */
1007 static
1008 PT_THREAD(tsch_slot_operation(struct rtimer *t, void *ptr))
1009 {
1010  TSCH_DEBUG_INTERRUPT();
1011  PT_BEGIN(&slot_operation_pt);
1012 
1013  /* Loop over all active slots */
1014  while(tsch_is_associated) {
1015 
1016  if(current_link == NULL || tsch_lock_requested) { /* Skip slot operation if there is no link
1017  or if there is a pending request for getting the lock */
1018  /* Issue a log whenever skipping a slot */
1019  TSCH_LOG_ADD(tsch_log_message,
1020  snprintf(log->message, sizeof(log->message),
1021  "!skipped slot %u %u %u",
1022  tsch_locked,
1023  tsch_lock_requested,
1024  current_link == NULL);
1025  );
1026 
1027  } else {
1028  int is_active_slot;
1029  TSCH_DEBUG_SLOT_START();
1030  tsch_in_slot_operation = 1;
1031  /* Measure on-air noise level while TSCH is idle */
1032  tsch_stats_sample_rssi();
1033  /* Reset drift correction */
1034  drift_correction = 0;
1035  is_drift_correction_used = 0;
1036  /* Get a packet ready to be sent */
1037  current_packet = get_packet_and_neighbor_for_link(current_link, &current_neighbor);
1038  uint8_t do_skip_best_link = 0;
1039  if(current_packet == NULL && backup_link != NULL) {
1040  /* There is no packet to send, and this link does not have Rx flag. Instead of doing
1041  * nothing, switch to the backup link (has Rx flag) if any
1042  * and if the current link cannot Rx or both links can Rx, but the backup link has priority. */
1043  if(!(current_link->link_options & LINK_OPTION_RX)
1044  || backup_link->slotframe_handle < current_link->slotframe_handle) {
1045  do_skip_best_link = 1;
1046  }
1047  }
1048 
1049  if(do_skip_best_link) {
1050  /* skipped a Tx link, refresh its backoff */
1051  update_link_backoff(current_link);
1052 
1053  current_link = backup_link;
1054  current_packet = get_packet_and_neighbor_for_link(current_link, &current_neighbor);
1055  }
1056  is_active_slot = current_packet != NULL || (current_link->link_options & LINK_OPTION_RX);
1057  if(is_active_slot) {
1058  /* If we are in a burst, we stick to current channel instead of
1059  * doing channel hopping, as per IEEE 802.15.4-2015 */
1060  if(burst_link_scheduled) {
1061  /* Reset burst_link_scheduled flag. Will be set again if burst continue. */
1062  burst_link_scheduled = 0;
1063  } else {
1064  /* Hop channel */
1065  tsch_current_channel_offset = tsch_get_channel_offset(current_link, current_packet);
1066  tsch_current_channel = tsch_calculate_channel(&tsch_current_asn, tsch_current_channel_offset);
1067  }
1068  NETSTACK_RADIO.set_value(RADIO_PARAM_CHANNEL, tsch_current_channel);
1069  /* Turn the radio on already here if configured so; necessary for radios with slow startup */
1070  tsch_radio_on(TSCH_RADIO_CMD_ON_START_OF_TIMESLOT);
1071  /* Decide whether it is a TX/RX/IDLE or OFF slot */
1072  /* Actual slot operation */
1073  if(current_packet != NULL) {
1074  /* We have something to transmit, do the following:
1075  * 1. send
1076  * 2. update_backoff_state(current_neighbor)
1077  * 3. post tx callback
1078  **/
1079  static struct pt slot_tx_pt;
1080  PT_SPAWN(&slot_operation_pt, &slot_tx_pt, tsch_tx_slot(&slot_tx_pt, t));
1081  } else {
1082  /* Listen */
1083  static struct pt slot_rx_pt;
1084  PT_SPAWN(&slot_operation_pt, &slot_rx_pt, tsch_rx_slot(&slot_rx_pt, t));
1085  }
1086  } else {
1087  /* Make sure to end the burst in cast, for some reason, we were
1088  * in a burst but now without any more packet to send. */
1089  burst_link_scheduled = 0;
1090  }
1091  TSCH_DEBUG_SLOT_END();
1092  }
1093 
1094  /* End of slot operation, schedule next slot or resynchronize */
1095 
1096  if(tsch_is_coordinator) {
1097  /* Update the `last_sync_*` variables to avoid large errors
1098  * in the application-level time synchronization */
1099  last_sync_asn = tsch_current_asn;
1100  tsch_last_sync_time = clock_time();
1101  }
1102 
1103  /* Do we need to resynchronize? i.e., wait for EB again */
1104  if(!tsch_is_coordinator && (TSCH_ASN_DIFF(tsch_current_asn, last_sync_asn) >
1105  (100 * TSCH_CLOCK_TO_SLOTS(TSCH_DESYNC_THRESHOLD / 100, tsch_timing[tsch_ts_timeslot_length])))) {
1106  TSCH_LOG_ADD(tsch_log_message,
1107  snprintf(log->message, sizeof(log->message),
1108  "! leaving the network, last sync %u",
1109  (unsigned)TSCH_ASN_DIFF(tsch_current_asn, last_sync_asn));
1110  );
1112  } else {
1113  /* backup of drift correction for printing debug messages */
1114  /* int32_t drift_correction_backup = drift_correction; */
1115  uint16_t timeslot_diff = 0;
1116  rtimer_clock_t prev_slot_start;
1117  /* Time to next wake up */
1118  rtimer_clock_t time_to_next_active_slot;
1119  /* Schedule next wakeup skipping slots if missed deadline */
1120  do {
1121  update_link_backoff(current_link);
1122 
1123  /* A burst link was scheduled. Replay the current link at the
1124  next time offset */
1125  if(burst_link_scheduled && current_link != NULL) {
1126  timeslot_diff = 1;
1127  backup_link = NULL;
1128  /* Keep track of the number of repetitions */
1129  tsch_current_burst_count++;
1130  } else {
1131  /* Get next active link */
1132  current_link = tsch_schedule_get_next_active_link(&tsch_current_asn, &timeslot_diff, &backup_link);
1133  if(current_link == NULL) {
1134  /* There is no next link. Fall back to default
1135  * behavior: wake up at the next slot. */
1136  timeslot_diff = 1;
1137  } else {
1138  /* Reset burst index now that the link was scheduled from
1139  normal schedule (as opposed to from ongoing burst) */
1140  tsch_current_burst_count = 0;
1141  }
1142  }
1143 
1144  /* Update ASN */
1145  TSCH_ASN_INC(tsch_current_asn, timeslot_diff);
1146  /* Time to next wake up */
1147  time_to_next_active_slot = timeslot_diff * tsch_timing[tsch_ts_timeslot_length] + drift_correction;
1148  time_to_next_active_slot += tsch_timesync_adaptive_compensate(time_to_next_active_slot);
1149  drift_correction = 0;
1150  is_drift_correction_used = 0;
1151  /* Update current slot start */
1152  prev_slot_start = current_slot_start;
1153  current_slot_start += time_to_next_active_slot;
1154  } while(!tsch_schedule_slot_operation(t, prev_slot_start, time_to_next_active_slot, "main"));
1155  }
1156 
1157  tsch_in_slot_operation = 0;
1158  PT_YIELD(&slot_operation_pt);
1159  }
1160 
1161  PT_END(&slot_operation_pt);
1162 }
1163 /*---------------------------------------------------------------------------*/
1164 /* Set global time before starting slot operation,
1165  * with a rtimer time and an ASN */
1166 void
1168 {
1169  static struct rtimer slot_operation_timer;
1170  rtimer_clock_t time_to_next_active_slot;
1171  rtimer_clock_t prev_slot_start;
1172  TSCH_DEBUG_INIT();
1173  do {
1174  uint16_t timeslot_diff;
1175  /* Get next active link */
1176  current_link = tsch_schedule_get_next_active_link(&tsch_current_asn, &timeslot_diff, &backup_link);
1177  if(current_link == NULL) {
1178  /* There is no next link. Fall back to default
1179  * behavior: wake up at the next slot. */
1180  timeslot_diff = 1;
1181  }
1182  /* Update ASN */
1183  TSCH_ASN_INC(tsch_current_asn, timeslot_diff);
1184  /* Time to next wake up */
1185  time_to_next_active_slot = timeslot_diff * tsch_timing[tsch_ts_timeslot_length];
1186  /* Compensate for the base drift */
1187  time_to_next_active_slot += tsch_timesync_adaptive_compensate(time_to_next_active_slot);
1188  /* Update current slot start */
1189  prev_slot_start = current_slot_start;
1190  current_slot_start += time_to_next_active_slot;
1191  } while(!tsch_schedule_slot_operation(&slot_operation_timer, prev_slot_start, time_to_next_active_slot, "assoc"));
1192 }
1193 /*---------------------------------------------------------------------------*/
1194 /* Start actual slot operation */
1195 void
1196 tsch_slot_operation_sync(rtimer_clock_t next_slot_start,
1197  struct tsch_asn_t *next_slot_asn)
1198 {
1199  int_master_status_t status;
1200 
1201  current_slot_start = next_slot_start;
1202  tsch_current_asn = *next_slot_asn;
1203  status = critical_enter();
1204  last_sync_asn = tsch_current_asn;
1205  tsch_last_sync_time = clock_time();
1206  critical_exit(status);
1207  current_link = NULL;
1208 }
1209 /*---------------------------------------------------------------------------*/
1210 /** @} */
TSCH packet information.
Definition: tsch-types.h:97
#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
frame802154_scf_t security_control
Security control bitfield.
Definition: frame802154.h:188
struct tsch_neighbor * tsch_queue_get_nbr(const linkaddr_t *addr)
Get a TSCH neighbor.
Definition: tsch-queue.c:110
static void tsch_radio_off(enum tsch_radio_state_off_cmd command)
This function turns off the radio.
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:67
frame802154_fcf_t fcf
Frame control field.
Definition: frame802154.h:204
Representation of a real-time task.
Definition: rtimer.h:133
The MAC layer transmission could not be performed because of a fatal error.
Definition: mac.h:101
int tsch_get_lock(void)
Takes the TSCH lock.
Header file for the radio API
uint8_t security_level
3 bit.
Definition: frame802154.h:168
static void critical_exit(int_master_status_t status)
Exit a critical section and restore the master interrupt.
Definition: critical.h:81
void tsch_release_lock(void)
Releases the TSCH lock.
int frame802154_parse(uint8_t *data, int len, frame802154_t *pf)
Parses an input frame.
Definition: frame802154.c:500
TSCH neighbor information.
Definition: tsch-types.h:109
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:505
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.
#define PT_BEGIN(pt)
Declare the start of a protothread inside the C function implementing the protothread.
Definition: pt.h:114
Channel used for radio communication.
Definition: radio.h:134
void tsch_timesync_update(struct tsch_neighbor *n, uint16_t time_delta_asn, int32_t drift_correction)
Updates timesync information for a given neighbor.
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
uint8_t src_addr[8]
Source address.
Definition: frame802154.h:203
A MAC framer for IEEE 802.15.4
const linkaddr_t linkaddr_null
The null link-layer address.
#define PT_SPAWN(pt, child, thread)
Spawn a child protothread and wait until it exits.
Definition: pt.h:205
int tsch_packet_update_eb(uint8_t *buf, int buf_size, uint8_t tsch_sync_ie_offset)
Update ASN in EB packet.
Definition: tsch-packet.c:377
linkaddr_t linkaddr_node_addr
The link-layer address of the node.
Definition: linkaddr.c:48
static int_master_status_t critical_enter()
Enter a critical section.
Definition: critical.h:65
#define RTIMER_NOW()
Get the current clock time.
Definition: rtimer.h:185
The RSSI value of the last received packet.
Definition: radio.h:226
int32_t tsch_timesync_adaptive_compensate(rtimer_clock_t delta_ticks)
Computes time compensation for a given point in the future.
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
unsigned int tsch_security_mic_len(const frame802154_t *frame)
Return MIC length.
INT_MASTER_STATUS_DATATYPE int_master_status_t
Master interrupt state representation data type.
Definition: int-master.h:62
#define CLOCK_SECOND
A second, measured in system clock time.
Definition: clock.h:82
#define PT_END(pt)
Declare the end of a protothread.
Definition: pt.h:126
linkaddr_t * tsch_queue_get_nbr_address(const struct tsch_neighbor *n)
Get the address of a neighbor.
Definition: tsch-queue.c:135
Header file for the Packet queue buffer management
The MAC layer did not get an acknowledgement for the packet.
Definition: mac.h:91
void process_poll(struct process *p)
Request a process to be polled.
Definition: process.c:371
struct tsch_link * tsch_schedule_get_next_active_link(struct tsch_asn_t *asn, uint16_t *time_offset, struct tsch_link **backup_link)
Returns the next active link after a given ASN, and a backup link (for the same ASN, with Rx flag)
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_schedule_keepalive(int immediate)
Schedule a keep-alive transmission within [timeout*0.9, timeout[ Can be called from an interrupt...
Definition: tsch.c:333
Radio receiver mode determines if the radio has address filter (RADIO_RX_MODE_ADDRESS_FILTER) and aut...
Definition: radio.h:173
The MAC layer transmission was OK.
Definition: mac.h:87
#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
#define PT_YIELD(pt)
Yield from the current protothread.
Definition: pt.h:289
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.
Definition: tsch-packet.c:155
Main API declarations for TSCH.
clock_time_t clock_time(void)
Get the current clock time.
Definition: clock.c:118
#define RTIMER_BUSYWAIT_UNTIL_ABS(cond, t0, max_time)
Busy-wait until a condition.
Definition: rtimer.h:202
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:412
#define RADIO_RX_MODE_ADDRESS_FILTER
Enable address-based frame filtering.
Definition: radio.h:443
int tsch_is_locked(void)
Checks if the TSCH lock is set.
uint8_t frame_type
3 bit.
Definition: frame802154.h:153
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.
The MAC layer deferred the transmission for a later time.
Definition: mac.h:94
Parameters used by the frame802154_create() function.
Definition: frame802154.h:198
void linkaddr_copy(linkaddr_t *dest, const linkaddr_t *src)
Copy a link-layer address.
Definition: linkaddr.c:63
#define PT_THREAD(name_args)
Declaration of a protothread.
Definition: pt.h:99
uint64_t tsch_get_network_uptime_ticks(void)
Get the time, in clock ticks, since the TSCH network was started.
void tsch_packet_set_frame_pending(uint8_t *buf, int buf_size)
Set frame pending bit in a packet (whose header was already build)
Definition: tsch-packet.c:454
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:324
#define TSCH_ASN_DIFF(asn1, asn2)
Returns the 32-bit diff between asn1 and asn2.
Definition: tsch-asn.h:82
int linkaddr_cmp(const linkaddr_t *addr1, const linkaddr_t *addr2)
Compare two link-layer addresses.
Definition: linkaddr.c:69
static void tsch_radio_on(enum tsch_radio_state_on_cmd command)
This function turns on the radio.
#define TSCH_ASN_INC(asn, inc)
Increment an ASN by inc (32 bits)
Definition: tsch-asn.h:68
Link quality indicator of the last received packet.
Definition: radio.h:236
uint8_t seq
Sequence number.
Definition: frame802154.h:205
int tsch_packet_get_frame_pending(uint8_t *buf, int buf_size)
Get frame pending bit from a packet.
Definition: tsch-packet.c:461
int ringbufindex_peek_put(const struct ringbufindex *r)
Check if there is space to put an element.
Definition: ringbufindex.c:78
rtimer task is scheduled successfully
Definition: rtimer.h:143
void tsch_disassociate(void)
Leave the TSCH network we are currently in.
Definition: tsch.c:571
Header file for the Packet buffer (packetbuf) management
Include file for the Contiki low-layer network stack (NETSTACK)
void watchdog_periodic(void)
Writes the WDT clear sequence.
Definition: watchdog.c:85
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:451
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.
Last packet timestamp, of type rtimer_clock_t.
Definition: radio.h:278
Stores data about an incoming packet.
Definition: tsch-types.h:149
Header file for the logging system
uint8_t ack_required
1 bit.
Definition: frame802154.h:156
int ringbufindex_put(struct ringbufindex *r)
Put one element to the ring buffer.
Definition: ringbufindex.c:58
void tsch_slot_operation_start(void)
Start actual slot operation.
The ASN is an absolute slot number over 5 bytes.
Definition: tsch-asn.h:48
TX was successful and where an ACK was requested one was received.
Definition: radio.h:490
frame802154_aux_hdr_t aux_hdr
Aux security header.
Definition: frame802154.h:208
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