Skip to content

Commit 94eb4c0

Browse files
author
Hasnain Virk
committed
Adding QOS handling and fixing bugs for Class C
LinkADRReq mac command can be used by the network server to set a certain level of QOS using NbTrans field which is applicable to Unconfirmed traffic only for 1.0.2 spec. This commit introduces mechanisms to facilitate this QOS. It means to repeat an outgoing unconfirmed message NbTrans times without changing its frame counter. For class C, we have retired the ack_expiry_timer_for_class_c and have replaced it with another timer which mimics the RX2 closure as in Class A but doesn't actually close RX2 window. It's just a mechanism by which the state machine is informed that the you can proceed forward, we have not received anything in RX2 window either. This is needed as RX2 doesn't timeout in class C (i.e., the radio remains in continuous mode). In addition to that we need to close any pending timers for Receive windows after the MIC has passed and the Duplicate counter check has also been passed.
1 parent 5bfa7c0 commit 94eb4c0

File tree

10 files changed

+226
-115
lines changed

10 files changed

+226
-115
lines changed

features/lorawan/LoRaWANStack.cpp

Lines changed: 103 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ SPDX-License-Identifier: BSD-3-Clause
4040
* Control flags for transient states
4141
*/
4242
#define IDLE_FLAG 0x00000000
43-
#define TX_ONGOING_FLAG 0x00000001
43+
#define RETRY_EXHAUSTED_FLAG 0x00000001
4444
#define MSG_RECVD_FLAG 0x00000002
4545
#define CONNECTED_FLAG 0x00000004
4646
#define USING_OTAA_FLAG 0x00000008
@@ -68,6 +68,7 @@ LoRaWANStack::LoRaWANStack()
6868
_tx_metadata(),
6969
_rx_metadata(),
7070
_num_retry(1),
71+
_qos_cnt(1),
7172
_ctrl_flags(IDLE_FLAG),
7273
_app_port(INVALID_PORT),
7374
_link_check_requested(false),
@@ -274,7 +275,6 @@ lorawan_status_t LoRaWANStack::stop_sending(void)
274275

275276
if (_loramac.clear_tx_pipe() == LORAWAN_STATUS_OK) {
276277
_ctrl_flags &= ~TX_DONE_FLAG;
277-
_ctrl_flags &= ~TX_ONGOING_FLAG;
278278
_loramac.set_tx_ongoing(false);
279279
_device_current_state = DEVICE_STATE_IDLE;
280280
return LORAWAN_STATUS_OK;
@@ -453,7 +453,8 @@ lorawan_status_t LoRaWANStack::set_device_class(const device_class_t &device_cla
453453
if (device_class == CLASS_B) {
454454
return LORAWAN_STATUS_UNSUPPORTED;
455455
}
456-
_loramac.set_device_class(device_class, mbed::callback(this, &LoRaWANStack::handle_ack_expiry_for_class_c));
456+
_loramac.set_device_class(device_class,
457+
mbed::callback(this, &LoRaWANStack::post_process_tx_no_reception));
457458
return LORAWAN_STATUS_OK;
458459
}
459460

@@ -563,7 +564,6 @@ void LoRaWANStack::process_transmission_timeout()
563564
// this is a fatal error and should not happen
564565
tr_debug("TX Timeout");
565566
_loramac.on_radio_tx_timeout();
566-
_ctrl_flags &= ~TX_ONGOING_FLAG;
567567
_ctrl_flags &= ~TX_DONE_FLAG;
568568
if (_device_current_state == DEVICE_STATE_JOINING) {
569569
mlme_confirm_handler();
@@ -579,39 +579,108 @@ void LoRaWANStack::process_transmission(void)
579579
tr_debug("Transmission completed");
580580
_loramac.on_radio_tx_done(_tx_timestamp);
581581

582-
make_tx_metadata_available();
583-
584582
if (_device_current_state == DEVICE_STATE_JOINING) {
585583
_device_current_state = DEVICE_STATE_AWAITING_JOIN_ACCEPT;
586584
}
587585

588586
if (_device_current_state == DEVICE_STATE_SENDING) {
589587
if (_loramac.get_mcps_confirmation()->req_type == MCPS_CONFIRMED) {
590-
_ctrl_flags |= TX_ONGOING_FLAG;
591-
_ctrl_flags &= ~TX_DONE_FLAG;
592588
tr_debug("Awaiting ACK");
593589
_device_current_state = DEVICE_STATE_AWAITING_ACK;
594-
} else if (_loramac.get_device_class() == CLASS_A) {
595-
// Class A unconfirmed message sent, TX_DONE event will be sent to
596-
// application when RX2 windows is elapsed, i.e., in process_reception_timeout()
597-
_ctrl_flags &= ~TX_ONGOING_FLAG;
590+
}
591+
}
592+
}
593+
594+
void LoRaWANStack::post_process_tx_with_reception()
595+
{
596+
if (_loramac.get_mcps_confirmation()->req_type == MCPS_CONFIRMED) {
597+
// if ack was not received, we will try retransmission after
598+
// ACK_TIMEOUT. handle_data_frame() already disables ACK_TIMEOUT timer
599+
// if ack was received. Otherwise, following method will be called in
600+
// LoRaMac.cpp, on_ack_timeout_timer_event().
601+
if (_loramac.get_mcps_indication()->is_ack_recvd) {
598602
_ctrl_flags |= TX_DONE_FLAG;
599-
} else if (_loramac.get_device_class() == CLASS_C) {
600-
// In Class C, reception timeout never happens, so we handle the state
601-
// progression for TX_DONE in UNCONFIRMED case here
603+
_ctrl_flags &= ~RETRY_EXHAUSTED_FLAG;
604+
tr_debug("Ack=OK, NbTrials=%d",
605+
_loramac.get_mcps_confirmation()->nb_retries);
606+
_loramac.post_process_mcps_req();
607+
make_tx_metadata_available();
608+
state_controller(DEVICE_STATE_STATUS_CHECK);
609+
} else {
610+
if (!_loramac.continue_sending_process()
611+
&& _loramac.get_current_slot() != RX_SLOT_WIN_1) {
612+
tr_error("Retries exhausted for Class %s device",
613+
_loramac.get_device_class() == CLASS_A ? "A" : "C");
614+
_ctrl_flags &= ~TX_DONE_FLAG;
615+
_ctrl_flags |= RETRY_EXHAUSTED_FLAG;
616+
state_controller(DEVICE_STATE_STATUS_CHECK);
617+
}
618+
}
619+
} else {
620+
// handle UNCONFIRMED case here, RX slots were turned off due to
621+
// valid packet reception.
622+
uint8_t prev_QOS_level = _loramac.get_prev_QOS_level();
623+
uint8_t QOS_level = _loramac.get_QOS_level();
624+
625+
// We will not apply QOS on the post-processing of the previous
626+
// outgoing message as we would have received QOS instruction in response
627+
// to that particular message
628+
if (QOS_level > LORAWAN_DEFAULT_QOS && _qos_cnt < QOS_level
629+
&& (prev_QOS_level == QOS_level)) {
630+
_ctrl_flags &= ~TX_DONE_FLAG;
631+
const int ret = _queue->call(this, &LoRaWANStack::state_controller,
632+
DEVICE_STATE_SCHEDULING);
633+
MBED_ASSERT(ret != 0);
634+
(void) ret;
635+
_qos_cnt++;
636+
tr_info("QOS: repeated transmission #%d queued", _qos_cnt);
637+
} else {
602638
_loramac.post_process_mcps_req();
639+
_ctrl_flags |= TX_DONE_FLAG;
640+
make_tx_metadata_available();
603641
state_controller(DEVICE_STATE_STATUS_CHECK);
604-
state_machine_run_to_completion();
605642
}
606643
}
607644
}
608645

609-
void LoRaWANStack::handle_ack_expiry_for_class_c(void)
646+
void LoRaWANStack::post_process_tx_no_reception()
610647
{
611-
_ctrl_flags &= ~TX_DONE_FLAG;
612-
_ctrl_flags |= TX_ONGOING_FLAG;
613-
tr_error("Retries exhausted for Class C device");
648+
if (_loramac.get_mcps_confirmation()->req_type == MCPS_CONFIRMED) {
649+
_loramac.post_process_mcps_req();
650+
if (_loramac.continue_sending_process()) {
651+
_ctrl_flags &= ~TX_DONE_FLAG;
652+
_ctrl_flags &= ~RETRY_EXHAUSTED_FLAG;
653+
return;
654+
}
655+
656+
tr_error("Retries exhausted for Class %s device",
657+
_loramac.get_device_class() == CLASS_A ? "A" : "C");
658+
_ctrl_flags &= ~TX_DONE_FLAG;
659+
_ctrl_flags |= RETRY_EXHAUSTED_FLAG;
660+
} else {
661+
_ctrl_flags |= TX_DONE_FLAG;
662+
663+
uint8_t prev_QOS_level = _loramac.get_prev_QOS_level();
664+
uint8_t QOS_level = _loramac.get_QOS_level();
665+
666+
if (QOS_level > LORAWAN_DEFAULT_QOS && (prev_QOS_level == QOS_level)) {
667+
if (_qos_cnt < QOS_level) {
668+
const int ret = _queue->call(this, &LoRaWANStack::state_controller,
669+
DEVICE_STATE_SCHEDULING);
670+
MBED_ASSERT(ret != 0);
671+
(void)ret;
672+
_qos_cnt++;
673+
tr_info("QOS: repeated transmission #%d queued", _qos_cnt);
674+
state_machine_run_to_completion();
675+
return;
676+
}
677+
}
678+
}
679+
680+
_loramac.post_process_mcps_req();
681+
make_tx_metadata_available();
614682
state_controller(DEVICE_STATE_STATUS_CHECK);
683+
state_machine_run_to_completion();
615684
}
616685

617686
void LoRaWANStack::handle_scheduling_failure(void)
@@ -621,16 +690,18 @@ void LoRaWANStack::handle_scheduling_failure(void)
621690
state_machine_run_to_completion();
622691
}
623692

693+
624694
void LoRaWANStack::process_reception(const uint8_t *const payload, uint16_t size,
625695
int16_t rssi, int8_t snr)
626696
{
627697
_device_current_state = DEVICE_STATE_RECEIVING;
698+
628699
_ctrl_flags &= ~MSG_RECVD_FLAG;
700+
_ctrl_flags &= ~TX_DONE_FLAG;
701+
_ctrl_flags &= ~RETRY_EXHAUSTED_FLAG;
629702

630703
_loramac.on_radio_rx_done(payload, size, rssi, snr);
631704

632-
make_rx_metadata_available();
633-
634705
if (_loramac.get_mlme_confirmation()->pending) {
635706
_loramac.post_process_mlme_request();
636707
mlme_confirm_handler();
@@ -646,36 +717,10 @@ void LoRaWANStack::process_reception(const uint8_t *const payload, uint16_t size
646717
return;
647718
}
648719

649-
// if the outgoing message was of CONFIRMED type
650-
if (_loramac.get_mcps_confirmation()->req_type == MCPS_CONFIRMED) {
651-
// if ack was not received, we will try retransmission after
652-
// ACK_TIMEOUT. handle_data_frame() already disables ACK_TIMEOUT timer
653-
// if ack was received. Otherwise, following method will be called in
654-
// LoRaMac.cpp, on_ack_timeout_timer_event().
655-
if (_loramac.get_mcps_indication()->is_ack_recvd) {
656-
tr_debug("Ack=OK, NbTrials=%d",
657-
_loramac.get_mcps_confirmation()->nb_retries);
658-
_loramac.post_process_mcps_req();
659-
_ctrl_flags |= TX_DONE_FLAG;
660-
_ctrl_flags &= ~TX_ONGOING_FLAG;
661-
state_controller(DEVICE_STATE_STATUS_CHECK);
662-
} else {
663-
if (!_loramac.continue_sending_process() &&
664-
_loramac.get_current_slot() != RX_SLOT_WIN_1) {
665-
tr_error("Retries exhausted for Class A device");
666-
_ctrl_flags &= ~TX_DONE_FLAG;
667-
_ctrl_flags |= TX_ONGOING_FLAG;
668-
state_controller(DEVICE_STATE_STATUS_CHECK);
669-
}
670-
}
671-
} else if (_loramac.get_device_class() == CLASS_A) {
672-
// handle UNCONFIRMED case here, RX slots were turned off due to
673-
// valid packet reception. For Class C, an outgoing UNCONFIRMED message
674-
// gets its handling in process_transmission.
675-
_loramac.post_process_mcps_req();
676-
_ctrl_flags |= TX_DONE_FLAG;
677-
state_controller(DEVICE_STATE_STATUS_CHECK);
678-
}
720+
make_rx_metadata_available();
721+
722+
// Post process transmission in response to the reception
723+
post_process_tx_with_reception();
679724

680725
// handle any pending MCPS indication
681726
if (_loramac.get_mcps_indication()->pending) {
@@ -684,11 +729,8 @@ void LoRaWANStack::process_reception(const uint8_t *const payload, uint16_t size
684729
state_controller(DEVICE_STATE_STATUS_CHECK);
685730
}
686731

687-
// change the state only if a TX cycle completes for Class A
688-
// For class C it's not needed as it will already be in receiving
689-
// state, no matter if the TX cycle completed or not.
690-
if (!(_ctrl_flags & TX_ONGOING_FLAG)) {
691-
// we are done here, update the state
732+
// complete the cycle only if TX_DONE_FLAG is set
733+
if (_ctrl_flags & TX_DONE_FLAG) {
692734
state_machine_run_to_completion();
693735
}
694736

@@ -729,18 +771,7 @@ void LoRaWANStack::process_reception_timeout(bool is_timeout)
729771
* never occurs.
730772
*/
731773
if (slot == RX_SLOT_WIN_2) {
732-
_loramac.post_process_mcps_req();
733-
734-
if (_loramac.get_mcps_confirmation()->req_type == MCPS_CONFIRMED) {
735-
if (_loramac.continue_sending_process()) {
736-
return;
737-
} else {
738-
tr_error("Retries exhausted for Class A device");
739-
}
740-
}
741-
742-
state_controller(DEVICE_STATE_STATUS_CHECK);
743-
state_machine_run_to_completion();
774+
post_process_tx_no_reception();
744775
}
745776
}
746777

@@ -1091,8 +1122,7 @@ void LoRaWANStack::process_shutdown_state(lorawan_status_t &op_status)
10911122
_lw_session.active = false;
10921123
_device_current_state = DEVICE_STATE_SHUTDOWN;
10931124
op_status = LORAWAN_STATUS_DEVICE_OFF;
1094-
_ctrl_flags &= ~CONNECTED_FLAG;
1095-
_ctrl_flags &= ~CONN_IN_PROGRESS_FLAG;
1125+
_ctrl_flags = 0;
10961126
send_event_to_application(DISCONNECTED);
10971127
}
10981128

@@ -1108,20 +1138,15 @@ void LoRaWANStack::process_status_check_state()
11081138
// Another possibility is the case when the stack fails to schedule a
11091139
// deferred transmission and a scheduling failure handler is invoked.
11101140
_ctrl_flags &= ~TX_DONE_FLAG;
1111-
_ctrl_flags &= ~TX_ONGOING_FLAG;
11121141
_loramac.set_tx_ongoing(false);
11131142
_loramac.reset_ongoing_tx();
11141143
mcps_confirm_handler();
11151144

11161145
} else if (_device_current_state == DEVICE_STATE_RECEIVING) {
11171146

1118-
if ((_ctrl_flags & TX_DONE_FLAG) || (_ctrl_flags & TX_ONGOING_FLAG)) {
1119-
// for CONFIRMED case, ack validity is already checked
1120-
// If it was a successful transmission, TX_ONGOING_FLAG will not be set.
1121-
// If it was indeed set, that means the device was in Class C mode and
1122-
// CONFIRMED transmission was in place and the ack retries maxed out.
1147+
if ((_ctrl_flags & TX_DONE_FLAG) || (_ctrl_flags & RETRY_EXHAUSTED_FLAG)) {
11231148
_ctrl_flags &= ~TX_DONE_FLAG;
1124-
_ctrl_flags &= ~TX_ONGOING_FLAG;
1149+
_ctrl_flags &= ~RETRY_EXHAUSTED_FLAG;
11251150
_loramac.set_tx_ongoing(false);
11261151
_loramac.reset_ongoing_tx();
11271152
// if an automatic uplink is ongoing, we should not send a TX_DONE
@@ -1153,7 +1178,6 @@ void LoRaWANStack::process_scheduling_state(lorawan_status_t &op_status)
11531178

11541179
op_status = _loramac.send_ongoing_tx();
11551180
if (op_status == LORAWAN_STATUS_OK) {
1156-
_ctrl_flags |= TX_ONGOING_FLAG;
11571181
_ctrl_flags &= ~TX_DONE_FLAG;
11581182
_loramac.set_tx_ongoing(true);
11591183
_device_current_state = DEVICE_STATE_SENDING;

features/lorawan/LoRaWANStack.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -483,9 +483,11 @@ class LoRaWANStack: private mbed::NonCopyable<LoRaWANStack> {
483483
void make_tx_metadata_available(void);
484484
void make_rx_metadata_available(void);
485485

486-
void handle_ack_expiry_for_class_c(void);
487486
void handle_scheduling_failure(void);
488487

488+
void post_process_tx_with_reception(void);
489+
void post_process_tx_no_reception(void);
490+
489491
private:
490492
LoRaMac _loramac;
491493
radio_events_t radio_events;
@@ -497,6 +499,7 @@ class LoRaWANStack: private mbed::NonCopyable<LoRaWANStack> {
497499
lorawan_tx_metadata _tx_metadata;
498500
lorawan_rx_metadata _rx_metadata;
499501
uint8_t _num_retry;
502+
uint8_t _qos_cnt;
500503
uint32_t _ctrl_flags;
501504
uint8_t _app_port;
502505
bool _link_check_requested;

0 commit comments

Comments
 (0)