diff --git a/features/lorawan/LoRaWANStack.cpp b/features/lorawan/LoRaWANStack.cpp index 1b28e7c9d7..692524eb25 100644 --- a/features/lorawan/LoRaWANStack.cpp +++ b/features/lorawan/LoRaWANStack.cpp @@ -40,7 +40,7 @@ SPDX-License-Identifier: BSD-3-Clause * Control flags for transient states */ #define IDLE_FLAG 0x00000000 -#define TX_ONGOING_FLAG 0x00000001 +#define RETRY_EXHAUSTED_FLAG 0x00000001 #define MSG_RECVD_FLAG 0x00000002 #define CONNECTED_FLAG 0x00000004 #define USING_OTAA_FLAG 0x00000008 @@ -68,6 +68,7 @@ LoRaWANStack::LoRaWANStack() _tx_metadata(), _rx_metadata(), _num_retry(1), + _qos_cnt(1), _ctrl_flags(IDLE_FLAG), _app_port(INVALID_PORT), _link_check_requested(false), @@ -274,7 +275,6 @@ lorawan_status_t LoRaWANStack::stop_sending(void) if (_loramac.clear_tx_pipe() == LORAWAN_STATUS_OK) { _ctrl_flags &= ~TX_DONE_FLAG; - _ctrl_flags &= ~TX_ONGOING_FLAG; _loramac.set_tx_ongoing(false); _device_current_state = DEVICE_STATE_IDLE; return LORAWAN_STATUS_OK; @@ -449,7 +449,8 @@ lorawan_status_t LoRaWANStack::set_device_class(const device_class_t &device_cla if (device_class == CLASS_B) { return LORAWAN_STATUS_UNSUPPORTED; } - _loramac.set_device_class(device_class, mbed::callback(this, &LoRaWANStack::handle_ack_expiry_for_class_c)); + _loramac.set_device_class(device_class, + mbed::callback(this, &LoRaWANStack::post_process_tx_no_reception)); return LORAWAN_STATUS_OK; } @@ -559,7 +560,6 @@ void LoRaWANStack::process_transmission_timeout() // this is a fatal error and should not happen tr_debug("TX Timeout"); _loramac.on_radio_tx_timeout(); - _ctrl_flags &= ~TX_ONGOING_FLAG; _ctrl_flags &= ~TX_DONE_FLAG; if (_device_current_state == DEVICE_STATE_JOINING) { mlme_confirm_handler(); @@ -573,9 +573,6 @@ void LoRaWANStack::process_transmission_timeout() void LoRaWANStack::process_transmission(void) { tr_debug("Transmission completed"); - _loramac.on_radio_tx_done(_tx_timestamp); - - make_tx_metadata_available(); if (_device_current_state == DEVICE_STATE_JOINING) { _device_current_state = DEVICE_STATE_AWAITING_JOIN_ACCEPT; @@ -583,31 +580,103 @@ void LoRaWANStack::process_transmission(void) if (_device_current_state == DEVICE_STATE_SENDING) { if (_loramac.get_mcps_confirmation()->req_type == MCPS_CONFIRMED) { - _ctrl_flags |= TX_ONGOING_FLAG; - _ctrl_flags &= ~TX_DONE_FLAG; tr_debug("Awaiting ACK"); _device_current_state = DEVICE_STATE_AWAITING_ACK; - } else if (_loramac.get_device_class() == CLASS_A) { - // Class A unconfirmed message sent, TX_DONE event will be sent to - // application when RX2 windows is elapsed, i.e., in process_reception_timeout() - _ctrl_flags &= ~TX_ONGOING_FLAG; + } + } + + _loramac.on_radio_tx_done(_tx_timestamp); +} + +void LoRaWANStack::post_process_tx_with_reception() +{ + if (_loramac.get_mcps_confirmation()->req_type == MCPS_CONFIRMED) { + // if ack was not received, we will try retransmission after + // ACK_TIMEOUT. handle_data_frame() already disables ACK_TIMEOUT timer + // if ack was received. Otherwise, following method will be called in + // LoRaMac.cpp, on_ack_timeout_timer_event(). + if (_loramac.get_mcps_indication()->is_ack_recvd) { _ctrl_flags |= TX_DONE_FLAG; - } else if (_loramac.get_device_class() == CLASS_C) { - // In Class C, reception timeout never happens, so we handle the state - // progression for TX_DONE in UNCONFIRMED case here + _ctrl_flags &= ~RETRY_EXHAUSTED_FLAG; + tr_debug("Ack=OK, NbTrials=%d", + _loramac.get_mcps_confirmation()->nb_retries); _loramac.post_process_mcps_req(); + make_tx_metadata_available(); + state_controller(DEVICE_STATE_STATUS_CHECK); + } else { + if (!_loramac.continue_sending_process() + && _loramac.get_current_slot() != RX_SLOT_WIN_1) { + tr_error("Retries exhausted for Class %s device", + _loramac.get_device_class() == CLASS_A ? "A" : "C"); + _ctrl_flags &= ~TX_DONE_FLAG; + _ctrl_flags |= RETRY_EXHAUSTED_FLAG; + state_controller(DEVICE_STATE_STATUS_CHECK); + } + } + } else { + // handle UNCONFIRMED case here, RX slots were turned off due to + // valid packet reception. + uint8_t prev_QOS_level = _loramac.get_prev_QOS_level(); + uint8_t QOS_level = _loramac.get_QOS_level(); + + // We will not apply QOS on the post-processing of the previous + // outgoing message as we would have received QOS instruction in response + // to that particular message + if (QOS_level > LORAWAN_DEFAULT_QOS && _qos_cnt < QOS_level + && (prev_QOS_level == QOS_level)) { + _ctrl_flags &= ~TX_DONE_FLAG; + const int ret = _queue->call(this, &LoRaWANStack::state_controller, + DEVICE_STATE_SCHEDULING); + MBED_ASSERT(ret != 0); + (void) ret; + _qos_cnt++; + tr_info("QOS: repeated transmission #%d queued", _qos_cnt); + } else { + _loramac.post_process_mcps_req(); + _ctrl_flags |= TX_DONE_FLAG; + make_tx_metadata_available(); state_controller(DEVICE_STATE_STATUS_CHECK); - state_machine_run_to_completion(); } } } -void LoRaWANStack::handle_ack_expiry_for_class_c(void) +void LoRaWANStack::post_process_tx_no_reception() { - _ctrl_flags &= ~TX_DONE_FLAG; - _ctrl_flags |= TX_ONGOING_FLAG; - tr_error("Retries exhausted for Class C device"); + if (_loramac.get_mcps_confirmation()->req_type == MCPS_CONFIRMED) { + if (_loramac.continue_sending_process()) { + _ctrl_flags &= ~TX_DONE_FLAG; + _ctrl_flags &= ~RETRY_EXHAUSTED_FLAG; + return; + } + + tr_error("Retries exhausted for Class %s device", + _loramac.get_device_class() == CLASS_A ? "A" : "C"); + _ctrl_flags &= ~TX_DONE_FLAG; + _ctrl_flags |= RETRY_EXHAUSTED_FLAG; + } else { + _ctrl_flags |= TX_DONE_FLAG; + + uint8_t prev_QOS_level = _loramac.get_prev_QOS_level(); + uint8_t QOS_level = _loramac.get_QOS_level(); + + if (QOS_level > LORAWAN_DEFAULT_QOS && (prev_QOS_level == QOS_level)) { + if (_qos_cnt < QOS_level) { + const int ret = _queue->call(this, &LoRaWANStack::state_controller, + DEVICE_STATE_SCHEDULING); + MBED_ASSERT(ret != 0); + (void)ret; + _qos_cnt++; + tr_info("QOS: repeated transmission #%d queued", _qos_cnt); + state_machine_run_to_completion(); + return; + } + } + } + + _loramac.post_process_mcps_req(); + make_tx_metadata_available(); state_controller(DEVICE_STATE_STATUS_CHECK); + state_machine_run_to_completion(); } void LoRaWANStack::handle_scheduling_failure(void) @@ -617,16 +686,18 @@ void LoRaWANStack::handle_scheduling_failure(void) state_machine_run_to_completion(); } + void LoRaWANStack::process_reception(const uint8_t *const payload, uint16_t size, int16_t rssi, int8_t snr) { _device_current_state = DEVICE_STATE_RECEIVING; + _ctrl_flags &= ~MSG_RECVD_FLAG; + _ctrl_flags &= ~TX_DONE_FLAG; + _ctrl_flags &= ~RETRY_EXHAUSTED_FLAG; _loramac.on_radio_rx_done(payload, size, rssi, snr); - make_rx_metadata_available(); - if (_loramac.get_mlme_confirmation()->pending) { _loramac.post_process_mlme_request(); mlme_confirm_handler(); @@ -642,36 +713,10 @@ void LoRaWANStack::process_reception(const uint8_t *const payload, uint16_t size return; } - // if the outgoing message was of CONFIRMED type - if (_loramac.get_mcps_confirmation()->req_type == MCPS_CONFIRMED) { - // if ack was not received, we will try retransmission after - // ACK_TIMEOUT. handle_data_frame() already disables ACK_TIMEOUT timer - // if ack was received. Otherwise, following method will be called in - // LoRaMac.cpp, on_ack_timeout_timer_event(). - if (_loramac.get_mcps_indication()->is_ack_recvd) { - tr_debug("Ack=OK, NbTrials=%d", - _loramac.get_mcps_confirmation()->nb_retries); - _loramac.post_process_mcps_req(); - _ctrl_flags |= TX_DONE_FLAG; - _ctrl_flags &= ~TX_ONGOING_FLAG; - state_controller(DEVICE_STATE_STATUS_CHECK); - } else { - if (!_loramac.continue_sending_process() && - _loramac.get_current_slot() != RX_SLOT_WIN_1) { - tr_error("Retries exhausted for Class A device"); - _ctrl_flags &= ~TX_DONE_FLAG; - _ctrl_flags |= TX_ONGOING_FLAG; - state_controller(DEVICE_STATE_STATUS_CHECK); - } - } - } else if (_loramac.get_device_class() == CLASS_A) { - // handle UNCONFIRMED case here, RX slots were turned off due to - // valid packet reception. For Class C, an outgoing UNCONFIRMED message - // gets its handling in process_transmission. - _loramac.post_process_mcps_req(); - _ctrl_flags |= TX_DONE_FLAG; - state_controller(DEVICE_STATE_STATUS_CHECK); - } + make_rx_metadata_available(); + + // Post process transmission in response to the reception + post_process_tx_with_reception(); // handle any pending MCPS indication if (_loramac.get_mcps_indication()->pending) { @@ -680,15 +725,13 @@ void LoRaWANStack::process_reception(const uint8_t *const payload, uint16_t size state_controller(DEVICE_STATE_STATUS_CHECK); } - // change the state only if a TX cycle completes for Class A - // For class C it's not needed as it will already be in receiving - // state, no matter if the TX cycle completed or not. - if (!(_ctrl_flags & TX_ONGOING_FLAG)) { - // we are done here, update the state + // complete the cycle only if TX_DONE_FLAG is set + if (_ctrl_flags & TX_DONE_FLAG) { state_machine_run_to_completion(); } - if (_loramac.get_mlme_indication()->pending) { + // suppress auto uplink if another auto-uplink is in AWAITING_ACK state + if (_loramac.get_mlme_indication()->pending && !_automatic_uplink_ongoing) { tr_debug("MLME Indication pending"); _loramac.post_process_mlme_ind(); tr_debug("Immediate Uplink requested"); @@ -724,18 +767,7 @@ void LoRaWANStack::process_reception_timeout(bool is_timeout) * never occurs. */ if (slot == RX_SLOT_WIN_2) { - _loramac.post_process_mcps_req(); - - if (_loramac.get_mcps_confirmation()->req_type == MCPS_CONFIRMED) { - if (_loramac.continue_sending_process()) { - return; - } else { - tr_error("Retries exhausted for Class A device"); - } - } - - state_controller(DEVICE_STATE_STATUS_CHECK); - state_machine_run_to_completion(); + post_process_tx_no_reception(); } } @@ -1024,11 +1056,15 @@ void LoRaWANStack::mcps_indication_handler() || (_loramac.get_device_class() == CLASS_C && mcps_indication->type == MCPS_CONFIRMED)) { #if (MBED_CONF_LORA_AUTOMATIC_UPLINK_MESSAGE) - tr_debug("Sending empty uplink message..."); - _automatic_uplink_ongoing = true; - const int ret = _queue->call(this, &LoRaWANStack::send_automatic_uplink_message, mcps_indication->port); - MBED_ASSERT(ret != 0); - (void)ret; + // Do not queue an automatic uplink of there is one already outgoing + // This means we have not received an ack for the previous automatic uplink + if (!_automatic_uplink_ongoing) { + tr_debug("Sending empty uplink message..."); + _automatic_uplink_ongoing = true; + const int ret = _queue->call(this, &LoRaWANStack::send_automatic_uplink_message, mcps_indication->port); + MBED_ASSERT(ret != 0); + (void)ret; + } #else send_event_to_application(UPLINK_REQUIRED); #endif @@ -1082,8 +1118,7 @@ void LoRaWANStack::process_shutdown_state(lorawan_status_t &op_status) _lw_session.active = false; _device_current_state = DEVICE_STATE_SHUTDOWN; op_status = LORAWAN_STATUS_DEVICE_OFF; - _ctrl_flags &= ~CONNECTED_FLAG; - _ctrl_flags &= ~CONN_IN_PROGRESS_FLAG; + _ctrl_flags = 0; send_event_to_application(DISCONNECTED); } @@ -1099,20 +1134,15 @@ void LoRaWANStack::process_status_check_state() // Another possibility is the case when the stack fails to schedule a // deferred transmission and a scheduling failure handler is invoked. _ctrl_flags &= ~TX_DONE_FLAG; - _ctrl_flags &= ~TX_ONGOING_FLAG; _loramac.set_tx_ongoing(false); _loramac.reset_ongoing_tx(); mcps_confirm_handler(); } else if (_device_current_state == DEVICE_STATE_RECEIVING) { - if ((_ctrl_flags & TX_DONE_FLAG) || (_ctrl_flags & TX_ONGOING_FLAG)) { - // for CONFIRMED case, ack validity is already checked - // If it was a successful transmission, TX_ONGOING_FLAG will not be set. - // If it was indeed set, that means the device was in Class C mode and - // CONFIRMED transmission was in place and the ack retries maxed out. + if ((_ctrl_flags & TX_DONE_FLAG) || (_ctrl_flags & RETRY_EXHAUSTED_FLAG)) { _ctrl_flags &= ~TX_DONE_FLAG; - _ctrl_flags &= ~TX_ONGOING_FLAG; + _ctrl_flags &= ~RETRY_EXHAUSTED_FLAG; _loramac.set_tx_ongoing(false); _loramac.reset_ongoing_tx(); // if an automatic uplink is ongoing, we should not send a TX_DONE @@ -1144,7 +1174,6 @@ void LoRaWANStack::process_scheduling_state(lorawan_status_t &op_status) op_status = _loramac.send_ongoing_tx(); if (op_status == LORAWAN_STATUS_OK) { - _ctrl_flags |= TX_ONGOING_FLAG; _ctrl_flags &= ~TX_DONE_FLAG; _loramac.set_tx_ongoing(true); _device_current_state = DEVICE_STATE_SENDING; diff --git a/features/lorawan/LoRaWANStack.h b/features/lorawan/LoRaWANStack.h index d417108b88..6c5ecafd74 100644 --- a/features/lorawan/LoRaWANStack.h +++ b/features/lorawan/LoRaWANStack.h @@ -483,9 +483,11 @@ private: void make_tx_metadata_available(void); void make_rx_metadata_available(void); - void handle_ack_expiry_for_class_c(void); void handle_scheduling_failure(void); + void post_process_tx_with_reception(void); + void post_process_tx_no_reception(void); + private: LoRaMac _loramac; radio_events_t radio_events; @@ -497,6 +499,7 @@ private: lorawan_tx_metadata _tx_metadata; lorawan_rx_metadata _rx_metadata; uint8_t _num_retry; + uint8_t _qos_cnt; uint32_t _ctrl_flags; uint8_t _app_port; bool _link_check_requested; diff --git a/features/lorawan/lorastack/mac/LoRaMac.cpp b/features/lorawan/lorastack/mac/LoRaMac.cpp index 43dbec93e8..bb82ffb400 100644 --- a/features/lorawan/lorastack/mac/LoRaMac.cpp +++ b/features/lorawan/lorastack/mac/LoRaMac.cpp @@ -78,7 +78,8 @@ LoRaMac::LoRaMac() _mlme_confirmation(), _is_nwk_joined(false), _continuous_rx2_window_open(false), - _device_class(CLASS_A) + _device_class(CLASS_A), + _prev_qos_level(LORAWAN_DEFAULT_QOS) { _params.keys.dev_eui = NULL; _params.keys.app_eui = NULL; @@ -173,6 +174,9 @@ void LoRaMac::post_process_mcps_req() if (_params.is_ul_frame_counter_fixed == false) { _params.ul_frame_counter++; _params.adr_ack_counter++; + if (_params.sys_params.nb_trans > 1) { + _mcps_confirmation.nb_retries = _params.ul_nb_rep_counter; + } } } } @@ -514,10 +518,6 @@ void LoRaMac::handle_data_frame(const uint8_t *const payload, return; } - // message is intended for us and MIC have passed, stop RX2 Window - // Spec: 3.3.4 Receiver Activity during the receive windows - _lora_time.stop(_params.timers.rx_window2_timer); - _mcps_confirmation.ack_received = false; _mcps_indication.is_ack_recvd = false; _mcps_indication.pending = true; @@ -567,6 +567,8 @@ void LoRaMac::handle_data_frame(const uint8_t *const payload, tr_debug("Discarding duplicate frame"); _mcps_indication.pending = false; _mcps_indication.status = LORAMAC_EVENT_INFO_STATUS_DOWNLINK_REPEATED; + + return; } } else if (msg_type == FRAME_TYPE_DATA_UNCONFIRMED_DOWN) { _params.is_srv_ack_requested = false; @@ -584,6 +586,19 @@ void LoRaMac::handle_data_frame(const uint8_t *const payload, _params.dl_frame_counter = downlink_counter; } + // message is intended for us and MIC have passed, stop RX2 Window + // Spec: 3.3.4 Receiver Activity during the receive windows + if (get_current_slot() == RX_SLOT_WIN_1) { + _lora_time.stop(_params.timers.rx_window2_timer); + } else { + _lora_time.stop(_params.timers.rx_window1_timer); + _lora_time.stop(_params.timers.rx_window2_timer); + } + + if (_device_class == CLASS_C) { + _lora_time.stop(_rx2_closure_timer_for_class_c); + } + if (_params.is_node_ack_requested && fctrl.bits.ack) { _mcps_confirmation.ack_received = fctrl.bits.ack; _mcps_indication.is_ack_recvd = fctrl.bits.ack; @@ -629,17 +644,32 @@ void LoRaMac::on_radio_tx_done(lorawan_time_t timestamp) _lora_phy->put_radio_to_sleep(); } + if ((_mcps_confirmation.req_type == MCPS_UNCONFIRMED) + && (_params.sys_params.nb_trans > 1)) { + _params.ul_nb_rep_counter++; + MBED_ASSERT(_params.ul_nb_rep_counter <= _params.sys_params.nb_trans); + } + if (_params.is_rx_window_enabled == true) { lorawan_time_t time_diff = _lora_time.get_current_time() - timestamp; // start timer after which rx1_window will get opened _lora_time.start(_params.timers.rx_window1_timer, _params.rx_window1_delay - time_diff); - if (_device_class != CLASS_C) { - _lora_time.start(_params.timers.rx_window2_timer, - _params.rx_window2_delay - time_diff); + // start timer after which rx2_window will get opened + _lora_time.start(_params.timers.rx_window2_timer, + _params.rx_window2_delay - time_diff); + + // If class C and an Unconfirmed messgae is outgoing, + // this will start a timer which will invoke rx2 would be + // closure handler + if (get_device_class() == CLASS_C) { + _lora_time.start(_rx2_closure_timer_for_class_c, + (_params.rx_window2_delay - time_diff) + + _params.rx_window2_config.window_timeout); } + // start timer after which ack wait will timeout (for Confirmed messages) if (_params.is_node_ack_requested) { _lora_time.start(_params.timers.ack_timeout_timer, (_params.rx_window2_delay - time_diff) + @@ -663,6 +693,7 @@ void LoRaMac::on_radio_rx_done(const uint8_t *const payload, uint16_t size, int16_t rssi, int8_t snr) { if (_device_class == CLASS_C && !_continuous_rx2_window_open) { + _lora_time.stop(_rx2_closure_timer_for_class_c); open_rx2_window(); } else if (_device_class != CLASS_C){ _lora_time.stop(_params.timers.rx_window1_timer); @@ -704,6 +735,7 @@ void LoRaMac::on_radio_tx_timeout(void) { _lora_time.stop(_params.timers.rx_window1_timer); _lora_time.stop(_params.timers.rx_window2_timer); + _lora_time.stop(_rx2_closure_timer_for_class_c); _lora_time.stop(_params.timers.ack_timeout_timer); if (_device_class == CLASS_C) { @@ -717,16 +749,19 @@ void LoRaMac::on_radio_tx_timeout(void) _mac_commands.clear_command_buffer(); - _mcps_confirmation.nb_retries = _params.ack_timeout_retry_counter; + if (_mcps_confirmation.req_type == MCPS_CONFIRMED) { + _mcps_confirmation.nb_retries = _params.ack_timeout_retry_counter; + } else { + _mcps_confirmation.nb_retries = _params.ul_nb_rep_counter; + } + _mcps_confirmation.ack_received = false; _mcps_confirmation.tx_toa = 0; } void LoRaMac::on_radio_rx_timeout(bool is_timeout) { - if (_device_class == CLASS_C && !_continuous_rx2_window_open) { - open_rx2_window(); - } else { + if (_device_class == CLASS_A) { _lora_phy->put_radio_to_sleep(); } @@ -868,7 +903,7 @@ void LoRaMac::open_rx1_window(void) _lora_phy->rx_config(&_params.rx_window1_config); _lora_phy->handle_receive(); - tr_debug("Opening RX1 Window"); + tr_debug("RX1 slot open, Freq = %lu", _params.rx_window1_config.frequency); } void LoRaMac::open_rx2_window() @@ -897,7 +932,7 @@ void LoRaMac::open_rx2_window() _lora_phy->handle_receive(); _params.rx_slot = _params.rx_window2_config.rx_slot; - tr_debug("Opening RX2 Window, Frequency = %lu", _params.rx_window2_config.frequency); + tr_debug("RX2 slot open, Freq = %lu", _params.rx_window2_config.frequency); } void LoRaMac::on_ack_timeout_timer_event(void) @@ -905,12 +940,6 @@ void LoRaMac::on_ack_timeout_timer_event(void) Lock lock(*this); if (_params.ack_timeout_retry_counter > _params.max_ack_timeout_retries) { - if (get_device_class() == CLASS_C) { - // no need to use EventQueue as LoRaWANStack and LoRaMac are always - // in same context - _mcps_confirmation.status = LORAMAC_EVENT_INFO_STATUS_ERROR; - _ack_expiry_handler_for_class_c.call(); - } return; } @@ -1280,7 +1309,7 @@ int16_t LoRaMac::prepare_ongoing_tx(const uint8_t port, if (flags & MSG_PROPRIETARY_FLAG) { _ongoing_tx_msg.type = MCPS_PROPRIETARY; _ongoing_tx_msg.fport = port; - _ongoing_tx_msg.nb_trials = 1; + _ongoing_tx_msg.nb_trials = _params.sys_params.nb_trans; // a proprietary frame only includes an MHDR field which contains MTYPE field. // Everything else is at the discretion of the implementer fopts_len = 0; @@ -1365,10 +1394,12 @@ device_class_t LoRaMac::get_device_class() const } void LoRaMac::set_device_class(const device_class_t &device_class, - mbed::Callbackack_expiry_handler) + mbed::Callbackrx2_would_be_closure_handler) { _device_class = device_class; - _ack_expiry_handler_for_class_c = ack_expiry_handler; + _rx2_would_be_closure_for_class_c = rx2_would_be_closure_handler; + + _lora_time.init(_rx2_closure_timer_for_class_c, _rx2_would_be_closure_for_class_c); if (CLASS_A == _device_class) { tr_debug("Changing device class to -> CLASS_A"); @@ -1729,6 +1760,8 @@ lorawan_status_t LoRaMac::initialize(EventQueue *queue, _ev_queue = queue; _scheduling_failure_handler = scheduling_failure_handler; + _rx2_closure_timer_for_class_c.callback = NULL; + _rx2_closure_timer_for_class_c.timer_id = -1; _channel_plan.activate_channelplan_subsystem(_lora_phy); @@ -1742,7 +1775,7 @@ lorawan_status_t LoRaMac::initialize(EventQueue *queue, _params.timers.aggregated_timeoff = 0; _lora_phy->reset_to_default_values(&_params, true); - _params.sys_params.retry_num = 1; + _params.sys_params.nb_trans = 1; reset_mac_parameters(); @@ -1814,7 +1847,6 @@ uint8_t LoRaMac::get_max_possible_tx_size(uint8_t fopts_len) max_possible_payload_size = allowed_frm_payload_size - fopts_len; } else { max_possible_payload_size = allowed_frm_payload_size; - fopts_len = 0; _mac_commands.clear_command_buffer(); _mac_commands.clear_repeat_buffer(); } @@ -1922,3 +1954,18 @@ void LoRaMac::bind_phy(LoRaPHY &phy) { _lora_phy = &phy; } + +uint8_t LoRaMac::get_QOS_level() +{ + if (_prev_qos_level != _params.sys_params.nb_trans) { + _prev_qos_level = _params.sys_params.nb_trans; + } + + return _params.sys_params.nb_trans; +} + +uint8_t LoRaMac::get_prev_QOS_level() +{ + return _prev_qos_level; +} + diff --git a/features/lorawan/lorastack/mac/LoRaMac.h b/features/lorawan/lorastack/mac/LoRaMac.h index 8ec5a8793f..c04f8db406 100644 --- a/features/lorawan/lorastack/mac/LoRaMac.h +++ b/features/lorawan/lorastack/mac/LoRaMac.h @@ -295,10 +295,11 @@ public: /** * @brief set_device_class Sets active device class. * @param device_class Device class to use. - * @param ack_expiry_handler callback function to inform about ack expiry + * @param rx2_would_be_closure_handler callback function to inform about + * would be closure of RX2 window */ void set_device_class(const device_class_t &device_class, - mbed::Callbackack_expiry_handler); + mbed::Callbackrx2_would_be_closure_handler); /** * @brief setup_link_check_request Adds link check request command @@ -404,6 +405,17 @@ public: */ rx_slot_t get_current_slot(void); + /** + * Indicates what level of QOS is set by network server. QOS level is set + * in response to a LinkADRReq for UNCONFIRMED messages + */ + uint8_t get_QOS_level(void); + + /** + *Indicates level of QOS used for the previous outgoing message + */ + uint8_t get_prev_QOS_level(void); + /** * These locks trample through to the upper layers and make * the stack thread safe. @@ -632,10 +644,16 @@ private: /** * Class C doesn't timeout in RX2 window as it is a continuous window. - * We use this callback to inform the LoRaWANStack controller that the - * system cannot do more retries. + * We use this callback to inform the LoRaWANStack controller that we did + * not receive a downlink in a time equal to normal Class A type RX2 + * window timeout. This marks a 'would-be' closure for RX2, actual RX2 is + * not closed. Mostly network servers will send right at the beginning of + * RX2 window if they have something to send. So if we didn't receive anything + * in the time period equal to would be RX2 delay (which is a function of + * uplink message length and data rate), we will invoke this callback to let + * the upper layer know. */ - mbed::Callback _ack_expiry_handler_for_class_c; + mbed::Callback _rx2_would_be_closure_for_class_c; /** * Transmission is async, i.e., a call to schedule_tx() may be deferred to @@ -645,6 +663,8 @@ private: */ mbed::Callback _scheduling_failure_handler; + timer_event_t _rx2_closure_timer_for_class_c; + /** * Structure to hold MCPS indication data. */ @@ -672,6 +692,8 @@ private: bool _continuous_rx2_window_open; device_class_t _device_class; + + uint8_t _prev_qos_level; }; #endif // MBED_LORAWAN_MAC_H__ diff --git a/features/lorawan/lorastack/mac/LoRaMacCommand.cpp b/features/lorawan/lorastack/mac/LoRaMacCommand.cpp index 87afc3409f..ee33d0bf7f 100644 --- a/features/lorawan/lorastack/mac/LoRaMacCommand.cpp +++ b/features/lorawan/lorastack/mac/LoRaMacCommand.cpp @@ -145,40 +145,40 @@ lorawan_status_t LoRaMacCommand::process_mac_commands(const uint8_t *payload, ui mlme_conf.nb_gateways = payload[mac_index++]; break; case SRV_MAC_LINK_ADR_REQ: { - adr_req_params_t linkAdrReq; - int8_t linkAdrDatarate = DR_0; - int8_t linkAdrTxPower = TX_POWER_0; - uint8_t linkAdrNbRep = 0; - uint8_t linkAdrNbBytesParsed = 0; + adr_req_params_t link_adr_req; + int8_t link_adr_dr = DR_0; + int8_t link_adr_txpower = TX_POWER_0; + uint8_t link_adr_nbtrans = 0; + uint8_t link_adr_nb_bytes_pasred = 0; // Fill parameter structure - linkAdrReq.payload = &payload[mac_index - 1]; - linkAdrReq.payload_size = commands_size - (mac_index - 1); - linkAdrReq.adr_enabled = mac_sys_params.adr_on; - linkAdrReq.ul_dwell_time = mac_sys_params.uplink_dwell_time; - linkAdrReq.current_datarate = mac_sys_params.channel_data_rate; - linkAdrReq.current_tx_power = mac_sys_params.channel_tx_power; - linkAdrReq.current_nb_rep = mac_sys_params.retry_num; + link_adr_req.payload = &payload[mac_index - 1]; + link_adr_req.payload_size = commands_size - (mac_index - 1); + link_adr_req.adr_enabled = mac_sys_params.adr_on; + link_adr_req.ul_dwell_time = mac_sys_params.uplink_dwell_time; + link_adr_req.current_datarate = mac_sys_params.channel_data_rate; + link_adr_req.current_tx_power = mac_sys_params.channel_tx_power; + link_adr_req.current_nb_trans = mac_sys_params.nb_trans; // Process the ADR requests - status = lora_phy.link_ADR_request(&linkAdrReq, - &linkAdrDatarate, - &linkAdrTxPower, - &linkAdrNbRep, - &linkAdrNbBytesParsed); + status = lora_phy.link_ADR_request(&link_adr_req, + &link_adr_dr, + &link_adr_txpower, + &link_adr_nbtrans, + &link_adr_nb_bytes_pasred); if ((status & 0x07) == 0x07) { - mac_sys_params.channel_data_rate = linkAdrDatarate; - mac_sys_params.channel_tx_power = linkAdrTxPower; - mac_sys_params.retry_num = linkAdrNbRep; + mac_sys_params.channel_data_rate = link_adr_dr; + mac_sys_params.channel_tx_power = link_adr_txpower; + mac_sys_params.nb_trans = link_adr_nbtrans; } // Add the answers to the buffer - for (uint8_t i = 0; i < (linkAdrNbBytesParsed / 5); i++) { + for (uint8_t i = 0; i < (link_adr_nb_bytes_pasred / 5); i++) { ret_value = add_link_adr_ans(status); } // Update MAC index - mac_index += linkAdrNbBytesParsed - 1; + mac_index += link_adr_nb_bytes_pasred - 1; } break; case SRV_MAC_DUTY_CYCLE_REQ: diff --git a/features/lorawan/lorastack/phy/LoRaPHY.cpp b/features/lorawan/lorastack/phy/LoRaPHY.cpp index 6a6b5869b1..c9af5dcda2 100644 --- a/features/lorawan/lorastack/phy/LoRaPHY.cpp +++ b/features/lorawan/lorastack/phy/LoRaPHY.cpp @@ -36,7 +36,8 @@ SPDX-License-Identifier: BSD-3-Clause #define CHANNELS_IN_MASK 16 LoRaPHY::LoRaPHY() - : _radio(NULL) + : _radio(NULL), + _lora_time(NULL) { memset(&phy_params, 0, sizeof(phy_params)); } @@ -806,6 +807,11 @@ void LoRaPHY::compute_rx_win_params(int8_t datarate, uint8_t min_rx_symbols, ((uint32_t *)phy_params.bandwidths.table)[rx_conf_params->datarate]); } + if (rx_conf_params->rx_slot == RX_SLOT_WIN_1) { + rx_conf_params->frequency = phy_params.channels.channel_list[rx_conf_params->channel].frequency; + } + + get_rx_window_params(t_symbol, min_rx_symbols, rx_error, RADIO_WAKEUP_TIME, &rx_conf_params->window_timeout, &rx_conf_params->window_offset); } @@ -988,7 +994,7 @@ uint8_t LoRaPHY::link_ADR_request(adr_req_params_t *link_adr_req, verify_params.adr_enabled = link_adr_req->adr_enabled; verify_params.current_datarate = link_adr_req->current_datarate; verify_params.current_tx_power = link_adr_req->current_tx_power; - verify_params.current_nb_rep = link_adr_req->current_nb_rep; + verify_params.current_nb_rep = link_adr_req->current_nb_trans; verify_params.datarate = adr_settings.datarate; verify_params.tx_power = adr_settings.tx_power; diff --git a/features/lorawan/lorastack/phy/LoRaPHYAU915.cpp b/features/lorawan/lorastack/phy/LoRaPHYAU915.cpp index 97d80016f4..77c65579aa 100644 --- a/features/lorawan/lorastack/phy/LoRaPHYAU915.cpp +++ b/features/lorawan/lorastack/phy/LoRaPHYAU915.cpp @@ -479,7 +479,7 @@ uint8_t LoRaPHYAU915::link_ADR_request(adr_req_params_t* params, verify_params.nb_rep = adr_settings.nb_rep; verify_params.current_datarate = params->current_datarate; verify_params.current_tx_power = params->current_tx_power; - verify_params.current_nb_rep = params->current_nb_rep; + verify_params.current_nb_rep = params->current_nb_trans; verify_params.channel_mask = temp_channel_masks; diff --git a/features/lorawan/lorastack/phy/LoRaPHYCN470.cpp b/features/lorawan/lorastack/phy/LoRaPHYCN470.cpp index 777677765a..af87d0b913 100644 --- a/features/lorawan/lorastack/phy/LoRaPHYCN470.cpp +++ b/features/lorawan/lorastack/phy/LoRaPHYCN470.cpp @@ -510,7 +510,7 @@ uint8_t LoRaPHYCN470::link_ADR_request(adr_req_params_t* params, verify_params.nb_rep = adr_settings.nb_rep; verify_params.current_datarate = params->current_datarate; verify_params.current_tx_power = params->current_tx_power; - verify_params.current_nb_rep = params->current_nb_rep; + verify_params.current_nb_rep = params->current_nb_trans; verify_params.channel_mask = temp_channel_masks; diff --git a/features/lorawan/lorastack/phy/LoRaPHYUS915.cpp b/features/lorawan/lorastack/phy/LoRaPHYUS915.cpp index 95d52d212d..d7cd27c3ba 100644 --- a/features/lorawan/lorastack/phy/LoRaPHYUS915.cpp +++ b/features/lorawan/lorastack/phy/LoRaPHYUS915.cpp @@ -517,7 +517,7 @@ uint8_t LoRaPHYUS915::link_ADR_request(adr_req_params_t* params, verify_params.nb_rep = adr_settings.nb_rep; verify_params.current_datarate = params->current_datarate; verify_params.current_tx_power = params->current_tx_power; - verify_params.current_nb_rep = params->current_nb_rep; + verify_params.current_nb_rep = params->current_nb_trans; verify_params.channel_mask = temp_channel_masks; // Verify the parameters and update, if necessary diff --git a/features/lorawan/lorastack/phy/lora_phy_ds.h b/features/lorawan/lorastack/phy/lora_phy_ds.h index 79af3e9d31..7b7081c539 100644 --- a/features/lorawan/lorastack/phy/lora_phy_ds.h +++ b/features/lorawan/lorastack/phy/lora_phy_ds.h @@ -329,9 +329,10 @@ typedef struct { */ int8_t current_tx_power; /*! - * The current number of repetitions. + * The current number of repetitions for obtaining a QOS level set by + * NS (applicable only to unconfirmed messages). */ - uint8_t current_nb_rep; + uint8_t current_nb_trans; } adr_req_params_t; /** diff --git a/features/lorawan/system/lorawan_data_structures.h b/features/lorawan/system/lorawan_data_structures.h index fcf9d88f35..66ed659a13 100644 --- a/features/lorawan/system/lorawan_data_structures.h +++ b/features/lorawan/system/lorawan_data_structures.h @@ -70,6 +70,8 @@ typedef uint32_t lorawan_time_t; */ #define LORAMAC_PHY_MAXPAYLOAD 255 +#define LORAWAN_DEFAULT_QOS 1 + /** * * Default user application maximum data size for transmission @@ -187,9 +189,10 @@ typedef struct { */ uint32_t join_accept_delay2; /*! - * The number of uplink messages repetitions (confirmed messages only). + * The number of uplink messages repetitions for QOS set by network server + * in LinkADRReq mac command (unconfirmed messages only). */ - uint8_t retry_num; + uint8_t nb_trans; /*! * The datarate offset between uplink and downlink on first window. */ @@ -874,6 +877,9 @@ typedef struct { */ int8_t data_rate; /*! + * + * For CONFIRMED Messages: + * * The number of trials to transmit the frame, if the LoRaMAC layer did not * receive an acknowledgment. The MAC performs a datarate adaptation * according to the LoRaWAN Specification V1.0.2, chapter 18.4, as in @@ -892,6 +898,13 @@ typedef struct { * * Note that if nb_trials is set to 1 or 2, the MAC will not decrease * the datarate, if the LoRaMAC layer did not receive an acknowledgment. + * + * For UNCONFIRMED Messages: + * + * Provides a certain QOS level set by network server in LinkADRReq MAC + * command. The device will transmit the given UNCONFIRMED message nb_trials + * time with same frame counter until a downlink is received. Standard defined + * range is 1:15. Data rates will NOT be adapted according to chapter 18.4. */ uint8_t nb_trials;