diff --git a/core/net/mac/tsch/tsch-conf.h b/core/net/mac/tsch/tsch-conf.h index ffd99dc98..631f47d03 100644 --- a/core/net/mac/tsch/tsch-conf.h +++ b/core/net/mac/tsch/tsch-conf.h @@ -184,4 +184,11 @@ #define TSCH_HW_FRAME_FILTERING 1 #endif /* TSCH_CONF_HW_FRAME_FILTERING */ +/* Keep radio always on within TSCH timeslot (1) or turn it off between packet and ACK? (0) */ +#ifdef TSCH_CONF_RADIO_ON_DURING_TIMESLOT +#define TSCH_RADIO_ON_DURING_TIMESLOT TSCH_CONF_RADIO_ON_DURING_TIMESLOT +#else +#define TSCH_RADIO_ON_DURING_TIMESLOT 0 +#endif + #endif /* __TSCH_CONF_H__ */ diff --git a/core/net/mac/tsch/tsch-slot-operation.c b/core/net/mac/tsch/tsch-slot-operation.c index 5f0701895..54f1b7e65 100644 --- a/core/net/mac/tsch/tsch-slot-operation.c +++ b/core/net/mac/tsch/tsch-slot-operation.c @@ -36,6 +36,7 @@ * \author * Simon Duquennoy * Beshr Al Nahas + * Atis Elsts * */ @@ -109,6 +110,18 @@ #define RTIMER_GUARD 2u #endif +enum tsch_radio_state_on_cmd { + TSCH_RADIO_CMD_ON_START_OF_TIMESLOT, + TSCH_RADIO_CMD_ON_WITHIN_TIMESLOT, + TSCH_RADIO_CMD_ON_FORCE, +}; + +enum tsch_radio_state_off_cmd { + TSCH_RADIO_CMD_OFF_END_OF_TIMESLOT, + TSCH_RADIO_CMD_OFF_WITHIN_TIMESLOT, + TSCH_RADIO_CMD_OFF_FORCE, +}; + /* A ringbuf storing outgoing packets after they were dequeued. * Will be processed layer by tsch_tx_process_pending */ struct ringbufindex dequeued_ringbuf; @@ -370,6 +383,68 @@ update_neighbor_state(struct tsch_neighbor *n, struct tsch_packet *p, return in_queue; } /*---------------------------------------------------------------------------*/ +/** + * This function turns on the radio. Its semantics is dependent on + * the value of TSCH_RADIO_ON_DURING_TIMESLOT constant: + * - if enabled, the radio is turned on at the start of the slot + * - if disabled, the radio is turned on within the slot, + * directly before the packet Rx guard time and ACK Rx guard time. + */ +static void +tsch_radio_on(enum tsch_radio_state_on_cmd command) +{ + int do_it = 0; + switch(command) { + case TSCH_RADIO_CMD_ON_START_OF_TIMESLOT: + if(TSCH_RADIO_ON_DURING_TIMESLOT) { + do_it = 1; + } + break; + case TSCH_RADIO_CMD_ON_WITHIN_TIMESLOT: + if(!TSCH_RADIO_ON_DURING_TIMESLOT) { + do_it = 1; + } + break; + case TSCH_RADIO_CMD_ON_FORCE: + do_it = 1; + break; + } + if(do_it) { + NETSTACK_RADIO.on(); + } +} +/*---------------------------------------------------------------------------*/ +/** + * This function turns off the radio. In the same way as for tsch_radio_on(), + * it depends on the value of TSCH_RADIO_ON_DURING_TIMESLOT constant: + * - if enabled, the radio is turned off at the end of the slot + * - if disabled, the radio is turned off within the slot, + * directly after Tx'ing or Rx'ing a packet or Tx'ing an ACK. + */ +static void +tsch_radio_off(enum tsch_radio_state_off_cmd command) +{ + int do_it = 0; + switch(command) { + case TSCH_RADIO_CMD_OFF_END_OF_TIMESLOT: + if(TSCH_RADIO_ON_DURING_TIMESLOT) { + do_it = 1; + } + break; + case TSCH_RADIO_CMD_OFF_WITHIN_TIMESLOT: + if(!TSCH_RADIO_ON_DURING_TIMESLOT) { + do_it = 1; + } + break; + case TSCH_RADIO_CMD_OFF_FORCE: + do_it = 1; + break; + } + if(do_it) { + NETSTACK_RADIO.off(); + } +} +/*---------------------------------------------------------------------------*/ static PT_THREAD(tsch_tx_slot(struct pt *pt, struct rtimer *t)) { @@ -456,7 +531,7 @@ PT_THREAD(tsch_tx_slot(struct pt *pt, struct rtimer *t)) /* delay before CCA */ TSCH_SCHEDULE_AND_YIELD(pt, t, current_slot_start, TS_CCA_OFFSET, "cca"); TSCH_DEBUG_TX_EVENT(); - NETSTACK_RADIO.on(); + tsch_radio_on(TSCH_RADIO_CMD_ON_WITHIN_TIMESLOT); /* CCA */ BUSYWAIT_UNTIL_ABS(!(cca_status |= NETSTACK_RADIO.channel_clear()), current_slot_start, TS_CCA_OFFSET + TS_CCA); @@ -480,7 +555,7 @@ PT_THREAD(tsch_tx_slot(struct pt *pt, struct rtimer *t)) /* limit tx_time to its max value */ tx_duration = MIN(tx_duration, tsch_timing[tsch_ts_max_tx]); /* turn tadio off -- will turn on again to wait for ACK if needed */ - NETSTACK_RADIO.off(); + tsch_radio_off(TSCH_RADIO_CMD_OFF_WITHIN_TIMESLOT); if(mac_tx_status == RADIO_TX_OK) { if(!is_broadcast) { @@ -502,7 +577,7 @@ PT_THREAD(tsch_tx_slot(struct pt *pt, struct rtimer *t)) TSCH_SCHEDULE_AND_YIELD(pt, t, current_slot_start, tsch_timing[tsch_ts_tx_offset] + tx_duration + tsch_timing[tsch_ts_rx_ack_delay] - RADIO_DELAY_BEFORE_RX, "TxBeforeAck"); TSCH_DEBUG_TX_EVENT(); - NETSTACK_RADIO.on(); + tsch_radio_on(TSCH_RADIO_CMD_ON_WITHIN_TIMESLOT); /* Wait for ACK to come */ BUSYWAIT_UNTIL_ABS(NETSTACK_RADIO.receiving_packet(), tx_start_time, tx_duration + tsch_timing[tsch_ts_rx_ack_delay] + tsch_timing[tsch_ts_ack_wait]); @@ -514,7 +589,7 @@ PT_THREAD(tsch_tx_slot(struct pt *pt, struct rtimer *t)) BUSYWAIT_UNTIL_ABS(!NETSTACK_RADIO.receiving_packet(), ack_start_time, tsch_timing[tsch_ts_max_ack]); TSCH_DEBUG_TX_EVENT(); - NETSTACK_RADIO.off(); + tsch_radio_off(TSCH_RADIO_CMD_OFF_WITHIN_TIMESLOT); #if TSCH_HW_FRAME_FILTERING /* Leaving promiscuous mode */ @@ -588,6 +663,8 @@ PT_THREAD(tsch_tx_slot(struct pt *pt, struct rtimer *t)) } } + tsch_radio_off(TSCH_RADIO_CMD_OFF_END_OF_TIMESLOT); + current_packet->transmissions++; current_packet->ret = mac_tx_status; @@ -671,27 +748,26 @@ PT_THREAD(tsch_rx_slot(struct pt *pt, struct rtimer *t)) TSCH_DEBUG_RX_EVENT(); /* Start radio for at least guard time */ - NETSTACK_RADIO.on(); - packet_seen = NETSTACK_RADIO.receiving_packet(); + tsch_radio_on(TSCH_RADIO_CMD_ON_WITHIN_TIMESLOT); + packet_seen = NETSTACK_RADIO.receiving_packet() || NETSTACK_RADIO.pending_packet(); if(!packet_seen) { /* Check if receiving within guard time */ BUSYWAIT_UNTIL_ABS((packet_seen = NETSTACK_RADIO.receiving_packet()), current_slot_start, tsch_timing[tsch_ts_rx_offset] + tsch_timing[tsch_ts_rx_wait]); } - if(packet_seen) { + if(!packet_seen) { + /* no packets on air */ + tsch_radio_off(TSCH_RADIO_CMD_OFF_FORCE); + } else { TSCH_DEBUG_RX_EVENT(); /* Save packet timestamp */ rx_start_time = RTIMER_NOW() - RADIO_DELAY_BEFORE_DETECT; - } - if(!NETSTACK_RADIO.receiving_packet() && !NETSTACK_RADIO.pending_packet()) { - NETSTACK_RADIO.off(); - /* no packets on air */ - } else { + /* Wait until packet is received, turn radio off */ BUSYWAIT_UNTIL_ABS(!NETSTACK_RADIO.receiving_packet(), current_slot_start, tsch_timing[tsch_ts_rx_offset] + tsch_timing[tsch_ts_rx_wait] + tsch_timing[tsch_ts_max_tx]); TSCH_DEBUG_RX_EVENT(); - NETSTACK_RADIO.off(); + tsch_radio_off(TSCH_RADIO_CMD_OFF_WITHIN_TIMESLOT); #if TSCH_RESYNC_WITH_SFD_TIMESTAMPS /* At the end of the reception, get an more accurate estimate of SFD arrival time */ @@ -784,7 +860,7 @@ PT_THREAD(tsch_rx_slot(struct pt *pt, struct rtimer *t)) packet_duration + tsch_timing[tsch_ts_tx_ack_delay] - RADIO_DELAY_BEFORE_TX, "RxBeforeAck"); TSCH_DEBUG_RX_EVENT(); NETSTACK_RADIO.transmit(ack_len); - NETSTACK_RADIO.off(); + tsch_radio_off(TSCH_RADIO_CMD_OFF_WITHIN_TIMESLOT); } /* If the sender is a time source, proceed to clock drift compensation */ @@ -820,6 +896,8 @@ PT_THREAD(tsch_rx_slot(struct pt *pt, struct rtimer *t)) process_poll(&tsch_pending_events_process); } } + + tsch_radio_off(TSCH_RADIO_CMD_OFF_END_OF_TIMESLOT); } if(input_queue_drop != 0) { @@ -860,8 +938,12 @@ PT_THREAD(tsch_slot_operation(struct rtimer *t, void *ptr)) } else { uint8_t current_channel; + int is_active_slot; TSCH_DEBUG_SLOT_START(); tsch_in_slot_operation = 1; + /* Reset drift correction */ + drift_correction = 0; + is_drift_correction_used = 0; /* Get a packet ready to be sent */ current_packet = get_packet_and_neighbor_for_link(current_link, ¤t_neighbor); /* There is no packet to send, and this link does not have Rx flag. Instead of doing @@ -870,26 +952,28 @@ PT_THREAD(tsch_slot_operation(struct rtimer *t, void *ptr)) current_link = backup_link; current_packet = get_packet_and_neighbor_for_link(current_link, ¤t_neighbor); } - /* Hop channel */ - current_channel = tsch_calculate_channel(¤t_asn, current_link->channel_offset); - NETSTACK_RADIO.set_value(RADIO_PARAM_CHANNEL, current_channel); - /* Reset drift correction */ - drift_correction = 0; - is_drift_correction_used = 0; - /* Decide whether it is a TX/RX/IDLE or OFF slot */ - /* Actual slot operation */ - if(current_packet != NULL) { - /* We have something to transmit, do the following: - * 1. send - * 2. update_backoff_state(current_neighbor) - * 3. post tx callback - **/ - static struct pt slot_tx_pt; - PT_SPAWN(&slot_operation_pt, &slot_tx_pt, tsch_tx_slot(&slot_tx_pt, t)); - } else if((current_link->link_options & LINK_OPTION_RX)) { - /* Listen */ - static struct pt slot_rx_pt; - PT_SPAWN(&slot_operation_pt, &slot_rx_pt, tsch_rx_slot(&slot_rx_pt, t)); + is_active_slot = current_packet != NULL || (current_link->link_options & LINK_OPTION_RX); + if(is_active_slot) { + /* Hop channel */ + current_channel = tsch_calculate_channel(¤t_asn, current_link->channel_offset); + NETSTACK_RADIO.set_value(RADIO_PARAM_CHANNEL, 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 */ + /* Actual slot operation */ + if(current_packet != NULL) { + /* We have something to transmit, do the following: + * 1. send + * 2. update_backoff_state(current_neighbor) + * 3. post tx callback + **/ + static struct pt slot_tx_pt; + PT_SPAWN(&slot_operation_pt, &slot_tx_pt, tsch_tx_slot(&slot_tx_pt, t)); + } else { + /* Listen */ + static struct pt slot_rx_pt; + PT_SPAWN(&slot_operation_pt, &slot_rx_pt, tsch_rx_slot(&slot_rx_pt, t)); + } } TSCH_DEBUG_SLOT_END(); }