diff --git a/os/net/mac/tsch/tsch-conf.h b/os/net/mac/tsch/tsch-conf.h index 3ee47b9cb..8613eb0e3 100644 --- a/os/net/mac/tsch/tsch-conf.h +++ b/os/net/mac/tsch/tsch-conf.h @@ -317,6 +317,15 @@ #define TSCH_SCHEDULE_WITH_6TISCH_MINIMAL (!(BUILD_WITH_ORCHESTRA)) #endif +/* Set an upper bound on burst length. Set to 0 to never set the frame pending + * bit, i.e., never trigger a burst. Note that receiver-side support for burst + * is always enabled, as it is part of IEEE 802.1.5.4-2015 (Section 7.2.1.3)*/ +#ifdef TSCH_CONF_BURST_MAX_LEN +#define TSCH_BURST_MAX_LEN TSCH_CONF_BURST_MAX_LEN +#else +#define TSCH_BURST_MAX_LEN 32 +#endif + /* 6TiSCH Minimal schedule slotframe length */ #ifdef TSCH_SCHEDULE_CONF_DEFAULT_LENGTH #define TSCH_SCHEDULE_DEFAULT_LENGTH TSCH_SCHEDULE_CONF_DEFAULT_LENGTH diff --git a/os/net/mac/tsch/tsch-log.c b/os/net/mac/tsch/tsch-log.c index d27dac064..e705f50ab 100644 --- a/os/net/mac/tsch/tsch-log.c +++ b/os/net/mac/tsch/tsch-log.c @@ -82,10 +82,11 @@ tsch_log_process_pending(void) printf("[INFO: TSCH-LOG ] {asn %02x.%08lx link-NULL} ", log->asn.ms1b, log->asn.ls4b); } else { struct tsch_slotframe *sf = tsch_schedule_get_slotframe_by_handle(log->link->slotframe_handle); - printf("[INFO: TSCH-LOG ] {asn %02x.%08lx link %2u %3u %3u %2u ch %2u} ", + printf("[INFO: TSCH-LOG ] {asn %02x.%08lx link %2u %3u %3u %2u %2u ch %2u} ", log->asn.ms1b, log->asn.ls4b, - log->link->slotframe_handle, sf ? sf->size.val : 0, log->link->timeslot, log->link->channel_offset, - tsch_calculate_channel(&log->asn, log->link->channel_offset)); + log->link->slotframe_handle, sf ? sf->size.val : 0, + log->burst_count, log->link->timeslot + log->burst_count, log->link->channel_offset, + log->channel); } switch(log->type) { case tsch_log_tx: @@ -135,6 +136,8 @@ tsch_log_prepare_add(void) struct tsch_log_t *log = &log_array[log_index]; log->asn = tsch_current_asn; log->link = current_link; + log->burst_count = tsch_current_burst_count; + log->channel = tsch_current_channel; return log; } else { log_dropped++; diff --git a/os/net/mac/tsch/tsch-log.h b/os/net/mac/tsch/tsch-log.h index 468eff869..186cda120 100644 --- a/os/net/mac/tsch/tsch-log.h +++ b/os/net/mac/tsch/tsch-log.h @@ -80,6 +80,8 @@ struct tsch_log_t { } type; struct tsch_asn_t asn; struct tsch_link *link; + uint8_t burst_count; + uint8_t channel; union { char message[48]; struct { diff --git a/os/net/mac/tsch/tsch-packet.c b/os/net/mac/tsch/tsch-packet.c index 64f03b588..133e9a668 100644 --- a/os/net/mac/tsch/tsch-packet.c +++ b/os/net/mac/tsch/tsch-packet.c @@ -70,6 +70,9 @@ */ static struct packetbuf_attr eackbuf_attrs[PACKETBUF_NUM_ATTRS]; +/* The offset of the frame pending bit flag within the first byte of FCF */ +#define IEEE802154_FRAME_PENDING_BIT_OFFSET 4 + /*---------------------------------------------------------------------------*/ static int tsch_packet_eackbuf_set_attr(uint8_t type, const packetbuf_attr_t val) @@ -459,4 +462,18 @@ tsch_packet_parse_eb(const uint8_t *buf, int buf_size, return curr_len; } /*---------------------------------------------------------------------------*/ +/* Set frame pending bit in a packet (whose header was already build) */ +void +tsch_packet_set_frame_pending(uint8_t *buf, int buf_size) +{ + buf[0] |= (1 << IEEE802154_FRAME_PENDING_BIT_OFFSET); +} +/*---------------------------------------------------------------------------*/ +/* Get frame pending bit from a packet */ +int +tsch_packet_get_frame_pending(uint8_t *buf, int buf_size) +{ + return (buf[0] >> IEEE802154_FRAME_PENDING_BIT_OFFSET) & 1; +} +/*---------------------------------------------------------------------------*/ /** @} */ diff --git a/os/net/mac/tsch/tsch-packet.h b/os/net/mac/tsch/tsch-packet.h index 6b05d6653..cdf7c7bed 100644 --- a/os/net/mac/tsch/tsch-packet.h +++ b/os/net/mac/tsch/tsch-packet.h @@ -101,6 +101,19 @@ int tsch_packet_update_eb(uint8_t *buf, int buf_size, uint8_t tsch_sync_ie_offse int tsch_packet_parse_eb(const uint8_t *buf, int buf_size, frame802154_t *frame, struct ieee802154_ies *ies, uint8_t *hdrlen, int frame_without_mic); +/** + * \brief Set frame pending bit in a packet (whose header was already build) + * \param buf The buffer where the packet resides + * \param buf_size The buffer size + */ +void tsch_packet_set_frame_pending(uint8_t *buf, int buf_size); +/** + * \brief Get frame pending bit from a packet + * \param buf The buffer where the packet resides + * \param buf_size The buffer size + * \return The value of the frame pending bit, 1 or 0 + */ +int tsch_packet_get_frame_pending(uint8_t *buf, int buf_size); #endif /* __TSCH_PACKET_H__ */ /** @} */ diff --git a/os/net/mac/tsch/tsch-schedule.c b/os/net/mac/tsch/tsch-schedule.c index 70a6b92ae..4f94b8e30 100644 --- a/os/net/mac/tsch/tsch-schedule.c +++ b/os/net/mac/tsch/tsch-schedule.c @@ -423,7 +423,7 @@ tsch_schedule_create_minimal(void) * but is required according to 802.15.4e if also used for EB transmission. * Timeslot: 0, channel offset: 0. */ tsch_schedule_add_link(sf_min, - LINK_OPTION_RX | LINK_OPTION_TX | LINK_OPTION_SHARED | LINK_OPTION_TIME_KEEPING, + (LINK_OPTION_RX | LINK_OPTION_TX | LINK_OPTION_SHARED | LINK_OPTION_TIME_KEEPING), LINK_TYPE_ADVERTISING, &tsch_broadcast_address, 0, 0); } diff --git a/os/net/mac/tsch/tsch-slot-operation.c b/os/net/mac/tsch/tsch-slot-operation.c index c4bb25e88..917d13816 100644 --- a/os/net/mac/tsch/tsch-slot-operation.c +++ b/os/net/mac/tsch/tsch-slot-operation.c @@ -155,7 +155,7 @@ static rtimer_clock_t volatile current_slot_start; static volatile int tsch_in_slot_operation = 0; /* If we are inside a slot, this tells the current channel */ -static uint8_t current_channel; +uint8_t tsch_current_channel; /* Info about the link, packet and neighbor of * the current (or next) slot */ @@ -167,6 +167,11 @@ static struct tsch_link *backup_link = NULL; static struct tsch_packet *current_packet = NULL; static struct tsch_neighbor *current_neighbor = NULL; +/* Indicates whether an extra link is needed to handle the current burst */ +static int burst_link_scheduled = 0; +/* Counts the length of the current burst */ +int tsch_current_burst_count = 0; + /* Protothread for association */ PT_THREAD(tsch_scan(struct pt *pt)); /* Protothread for slot operation, called from rtimer interrupt @@ -456,6 +461,8 @@ PT_THREAD(tsch_tx_slot(struct pt *pt, struct rtimer *t)) /* is this a broadcast packet? (wait for ack?) */ static uint8_t is_broadcast; static rtimer_clock_t tx_start_time; + /* Did we set the frame pending bit to request an extra burst link? */ + static int burst_link_requested; #if CCA_ENABLED static uint8_t cca_status; @@ -466,6 +473,14 @@ PT_THREAD(tsch_tx_slot(struct pt *pt, struct rtimer *t)) packet_len = queuebuf_datalen(current_packet->qb); /* is this a broadcast packet? (wait for ack?) */ is_broadcast = current_neighbor->is_broadcast; + /* Unicast. More packets in queue for the neighbor? */ + burst_link_requested = 0; + if(!is_broadcast + && tsch_current_burst_count + 1 < TSCH_BURST_MAX_LEN + && tsch_queue_packet_count(¤t_neighbor->addr) > 1) { + burst_link_requested = 1; + tsch_packet_set_frame_pending(packet, packet_len); + } /* read seqno from payload */ seqno = ((uint8_t *)(packet))[2]; /* if this is an EB, then update its Sync-IE */ @@ -618,6 +633,12 @@ PT_THREAD(tsch_tx_slot(struct pt *pt, struct rtimer *t)) tsch_schedule_keepalive(); } mac_tx_status = MAC_TX_OK; + + /* We requested an extra slot and got an ack. This means + the extra slot will be scheduled at the received */ + if(burst_link_requested) { + burst_link_scheduled = 1; + } } else { mac_tx_status = MAC_TX_NOACK; } @@ -749,7 +770,7 @@ PT_THREAD(tsch_rx_slot(struct pt *pt, struct rtimer *t)) NETSTACK_RADIO.get_value(RADIO_PARAM_LAST_RSSI, &radio_last_rssi); current_input->rx_asn = tsch_current_asn; current_input->rssi = (signed)radio_last_rssi; - current_input->channel = current_channel; + current_input->channel = tsch_current_channel; header_len = frame802154_parse((uint8_t *)current_input->payload, current_input->len, &frame); frame_valid = header_len > 0 && frame802154_check_dest_panid(&frame) && @@ -844,6 +865,9 @@ PT_THREAD(tsch_rx_slot(struct pt *pt, struct rtimer *t)) TSCH_DEBUG_RX_EVENT(); NETSTACK_RADIO.transmit(ack_len); tsch_radio_off(TSCH_RADIO_CMD_OFF_WITHIN_TIMESLOT); + + /* Schedule a burst link iff the frame pending bit was set */ + burst_link_scheduled = tsch_packet_get_frame_pending(current_input->payload, current_input->len); } } @@ -940,9 +964,16 @@ PT_THREAD(tsch_slot_operation(struct rtimer *t, void *ptr)) } is_active_slot = current_packet != NULL || (current_link->link_options & LINK_OPTION_RX); if(is_active_slot) { - /* Hop channel */ - current_channel = tsch_calculate_channel(&tsch_current_asn, current_link->channel_offset); - NETSTACK_RADIO.set_value(RADIO_PARAM_CHANNEL, current_channel); + /* If we are in a burst, we stick to current channel instead of + * doing channel hopping, as per IEEE 802.15.4-2015 */ + if(burst_link_scheduled) { + /* Reset burst_link_scheduled flag. Will be set again if burst continue. */ + burst_link_scheduled = 0; + } else { + /* Hop channel */ + tsch_current_channel = tsch_calculate_channel(&tsch_current_asn, current_link->channel_offset); + } + NETSTACK_RADIO.set_value(RADIO_PARAM_CHANNEL, tsch_current_channel); /* Turn the radio on already here if configured so; necessary for radios with slow startup */ tsch_radio_on(TSCH_RADIO_CMD_ON_START_OF_TIMESLOT); /* Decide whether it is a TX/RX/IDLE or OFF slot */ @@ -960,6 +991,10 @@ PT_THREAD(tsch_slot_operation(struct rtimer *t, void *ptr)) static struct pt slot_rx_pt; PT_SPAWN(&slot_operation_pt, &slot_rx_pt, tsch_rx_slot(&slot_rx_pt, t)); } + } else { + /* Make sure to end the burst in cast, for some reason, we were + * in a burst but now without any more packet to send. */ + burst_link_scheduled = 0; } TSCH_DEBUG_SLOT_END(); } @@ -993,13 +1028,27 @@ PT_THREAD(tsch_slot_operation(struct rtimer *t, void *ptr)) tsch_queue_update_all_backoff_windows(¤t_link->addr); } - /* Get next active link */ - current_link = tsch_schedule_get_next_active_link(&tsch_current_asn, ×lot_diff, &backup_link); - if(current_link == NULL) { - /* There is no next link. Fall back to default - * behavior: wake up at the next slot. */ + /* A burst link was scheduled. Replay the current link at the + next time offset */ + if(burst_link_scheduled) { timeslot_diff = 1; + backup_link = NULL; + /* Keep track of the number of repetitions */ + tsch_current_burst_count++; + } else { + /* Get next active link */ + current_link = tsch_schedule_get_next_active_link(&tsch_current_asn, ×lot_diff, &backup_link); + if(current_link == NULL) { + /* There is no next link. Fall back to default + * behavior: wake up at the next slot. */ + timeslot_diff = 1; + } else { + /* Reset burst index now that the link was scheduled from + normal schedule (as opposed to from ongoing burst) */ + tsch_current_burst_count = 0; + } } + /* Update ASN */ TSCH_ASN_INC(tsch_current_asn, timeslot_diff); /* Time to next wake up */ diff --git a/os/net/mac/tsch/tsch-slot-operation.h b/os/net/mac/tsch/tsch-slot-operation.h index 1c71c5587..b1c05697b 100644 --- a/os/net/mac/tsch/tsch-slot-operation.h +++ b/os/net/mac/tsch/tsch-slot-operation.h @@ -57,6 +57,8 @@ extern struct ringbufindex input_ringbuf; extern struct input_packet input_array[TSCH_MAX_INCOMING_PACKETS]; /* Last clock_time_t where synchronization happened */ extern clock_time_t last_sync_time; +/* Counts the length of the current burst */ +extern int tsch_current_burst_count; /********** Functions *********/ diff --git a/os/net/mac/tsch/tsch.h b/os/net/mac/tsch/tsch.h index 0f108bd93..93b6e0d88 100644 --- a/os/net/mac/tsch/tsch.h +++ b/os/net/mac/tsch/tsch.h @@ -164,6 +164,8 @@ extern const linkaddr_t tsch_eb_address; extern struct tsch_asn_t tsch_current_asn; extern uint8_t tsch_join_priority; extern struct tsch_link *current_link; +/* If we are inside a slot, this tells the current channel */ +extern uint8_t tsch_current_channel; /* TSCH channel hopping sequence */ extern uint8_t tsch_hopping_sequence[TSCH_HOPPING_SEQUENCE_MAX_LEN]; extern struct tsch_asn_divisor_t tsch_hopping_sequence_length;