Skip to content

Commit d5ce0cc

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 dc702c2 commit d5ce0cc

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),
@@ -276,7 +277,6 @@ lorawan_status_t LoRaWANStack::stop_sending(void)
276277

277278
if (status == LORAWAN_STATUS_OK) {
278279
_ctrl_flags &= ~TX_DONE_FLAG;
279-
_ctrl_flags &= ~TX_ONGOING_FLAG;
280280
_loramac.set_tx_ongoing(false);
281281
_device_current_state = DEVICE_STATE_IDLE;
282282
return LORAWAN_STATUS_OK;
@@ -452,7 +452,8 @@ lorawan_status_t LoRaWANStack::set_device_class(const device_class_t &device_cla
452452
if (device_class == CLASS_B) {
453453
return LORAWAN_STATUS_UNSUPPORTED;
454454
}
455-
_loramac.set_device_class(device_class, mbed::callback(this, &LoRaWANStack::handle_ack_expiry_for_class_c));
455+
_loramac.set_device_class(device_class,
456+
mbed::callback(this, &LoRaWANStack::post_process_tx_no_reception));
456457
return LORAWAN_STATUS_OK;
457458
}
458459

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

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

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

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

616685
void LoRaWANStack::handle_scheduling_failure(void)
@@ -620,16 +689,18 @@ void LoRaWANStack::handle_scheduling_failure(void)
620689
state_machine_run_to_completion();
621690
}
622691

692+
623693
void LoRaWANStack::process_reception(const uint8_t *const payload, uint16_t size,
624694
int16_t rssi, int8_t snr)
625695
{
626696
_device_current_state = DEVICE_STATE_RECEIVING;
697+
627698
_ctrl_flags &= ~MSG_RECVD_FLAG;
699+
_ctrl_flags &= ~TX_DONE_FLAG;
700+
_ctrl_flags &= ~RETRY_EXHAUSTED_FLAG;
628701

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

631-
make_rx_metadata_available();
632-
633704
if (_loramac.get_mlme_confirmation()->pending) {
634705
_loramac.post_process_mlme_request();
635706
mlme_confirm_handler();
@@ -645,36 +716,10 @@ void LoRaWANStack::process_reception(const uint8_t *const payload, uint16_t size
645716
return;
646717
}
647718

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

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

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

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

@@ -1090,8 +1121,7 @@ void LoRaWANStack::process_shutdown_state(lorawan_status_t &op_status)
10901121
_lw_session.active = false;
10911122
_device_current_state = DEVICE_STATE_SHUTDOWN;
10921123
op_status = LORAWAN_STATUS_DEVICE_OFF;
1093-
_ctrl_flags &= ~CONNECTED_FLAG;
1094-
_ctrl_flags &= ~CONN_IN_PROGRESS_FLAG;
1124+
_ctrl_flags = 0;
10951125
send_event_to_application(DISCONNECTED);
10961126
}
10971127

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

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

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

11531178
op_status = _loramac.send_ongoing_tx();
11541179
if (op_status == LORAWAN_STATUS_OK) {
1155-
_ctrl_flags |= TX_ONGOING_FLAG;
11561180
_ctrl_flags &= ~TX_DONE_FLAG;
11571181
_loramac.set_tx_ongoing(true);
11581182
_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)