diff --git a/features/lorawan/LoRaRadio.h b/features/lorawan/LoRaRadio.h index e78bdb3fbf..c586d59fc7 100644 --- a/features/lorawan/LoRaRadio.h +++ b/features/lorawan/LoRaRadio.h @@ -154,7 +154,7 @@ typedef struct radio_events { * FSK : N/A (set to 0) * LoRa: SNR value in dB */ - mbed::Callback rx_done; + mbed::Callback rx_done; /** * Callback when Reception is timed out diff --git a/features/lorawan/LoRaWANBase.h b/features/lorawan/LoRaWANBase.h index e0b33d5d1c..ce2f47ef09 100644 --- a/features/lorawan/LoRaWANBase.h +++ b/features/lorawan/LoRaWANBase.h @@ -301,9 +301,9 @@ public: * lorawan.connect(); * } * - * static void my_event_handler(lora_events_t events) + * static void my_event_handler(lorawan_event_t event) * { - * switch(events) { + * switch(event) { * case CONNECTED: * //do something * break; diff --git a/features/lorawan/LoRaWANInterface.cpp b/features/lorawan/LoRaWANInterface.cpp index f0f19f5b55..3ef705e0c8 100644 --- a/features/lorawan/LoRaWANInterface.cpp +++ b/features/lorawan/LoRaWANInterface.cpp @@ -34,95 +34,114 @@ LoRaWANInterface::~LoRaWANInterface() lorawan_status_t LoRaWANInterface::initialize(EventQueue *queue) { + Lock lock(*this); return _lw_stack.initialize_mac_layer(queue); } lorawan_status_t LoRaWANInterface::connect() { + Lock lock(*this); return _lw_stack.connect(); } lorawan_status_t LoRaWANInterface::connect(const lorawan_connect_t &connect) { + Lock lock(*this); return _lw_stack.connect(connect); } lorawan_status_t LoRaWANInterface::disconnect() { + Lock lock(*this); return _lw_stack.shutdown(); } lorawan_status_t LoRaWANInterface::add_link_check_request() { + Lock lock(*this); return _lw_stack.set_link_check_request(); } void LoRaWANInterface::remove_link_check_request() { + Lock lock(*this); _lw_stack.remove_link_check_request(); } lorawan_status_t LoRaWANInterface::set_datarate(uint8_t data_rate) { + Lock lock(*this); return _lw_stack.set_channel_data_rate(data_rate); } lorawan_status_t LoRaWANInterface::set_confirmed_msg_retries(uint8_t count) { + Lock lock(*this); return _lw_stack.set_confirmed_msg_retry(count); } lorawan_status_t LoRaWANInterface::enable_adaptive_datarate() { + Lock lock(*this); return _lw_stack.enable_adaptive_datarate(true); } lorawan_status_t LoRaWANInterface::disable_adaptive_datarate() { + Lock lock(*this); return _lw_stack.enable_adaptive_datarate(false); } lorawan_status_t LoRaWANInterface::set_channel_plan(const lorawan_channelplan_t &channel_plan) { + Lock lock(*this); return _lw_stack.add_channels(channel_plan); } lorawan_status_t LoRaWANInterface::get_channel_plan(lorawan_channelplan_t &channel_plan) { + Lock lock(*this); return _lw_stack.get_enabled_channels(channel_plan); } lorawan_status_t LoRaWANInterface::remove_channel(uint8_t id) { + Lock lock(*this); return _lw_stack.remove_a_channel(id); } lorawan_status_t LoRaWANInterface::remove_channel_plan() { + Lock lock(*this); return _lw_stack.drop_channel_list(); } int16_t LoRaWANInterface::send(uint8_t port, const uint8_t* data, uint16_t length, int flags) { + Lock lock(*this); return _lw_stack.handle_tx(port, data, length, flags); } int16_t LoRaWANInterface::receive(uint8_t port, uint8_t* data, uint16_t length, int flags) { + Lock lock(*this); return _lw_stack.handle_rx(data, length, port, flags, true); } int16_t LoRaWANInterface::receive(uint8_t* data, uint16_t length, uint8_t& port, int& flags) { + Lock lock(*this); return _lw_stack.handle_rx(data, length, port, flags, false); } lorawan_status_t LoRaWANInterface::add_app_callbacks(lorawan_app_callbacks_t *callbacks) { + Lock lock(*this); return _lw_stack.set_lora_callbacks(callbacks); } lorawan_status_t LoRaWANInterface::set_device_class(const device_class_t device_class) { + Lock lock(*this); return _lw_stack.set_device_class(device_class); } diff --git a/features/lorawan/LoRaWANInterface.h b/features/lorawan/LoRaWANInterface.h index 87bdad12a2..1f5f20cdf5 100644 --- a/features/lorawan/LoRaWANInterface.h +++ b/features/lorawan/LoRaWANInterface.h @@ -19,6 +19,7 @@ #define LORAWANINTERFACE_H_ #include "platform/Callback.h" +#include "platform/ScopedLock.h" #include "LoRaWANStack.h" #include "LoRaRadio.h" #include "LoRaWANBase.h" @@ -398,9 +399,9 @@ public: * lorawan.connect(); * } * - * static void my_event_handler(lora_events_t events) + * static void my_event_handler(lorawan_event_t event) * { - * switch(events) { + * switch(event) { * case CONNECTED: * //do something * break; @@ -435,7 +436,13 @@ public: */ virtual lorawan_status_t set_device_class(const device_class_t device_class); + void lock(void) { _lw_stack.lock(); } + void unlock(void) { _lw_stack.unlock(); } + + private: + typedef mbed::ScopedLock Lock; + LoRaWANStack _lw_stack; }; diff --git a/features/lorawan/LoRaWANStack.cpp b/features/lorawan/LoRaWANStack.cpp index fc0b68fe38..eddb60373b 100644 --- a/features/lorawan/LoRaWANStack.cpp +++ b/features/lorawan/LoRaWANStack.cpp @@ -41,6 +41,15 @@ SPDX-License-Identifier: BSD-3-Clause #define INVALID_PORT 0xFF #define MAX_CONFIRMED_MSG_RETRIES 255 +/** + * Control flags for transient states + */ +#define IDLE_FLAG 0x00000000 +#define TX_ONGOING_FLAG 0x00000001 +#define MSG_RECVD_FLAG 0x00000002 +#define CONNECTED_FLAG 0x00000004 +#define USING_OTAA_FLAG 0x00000008 +#define TX_DONE_FLAG 0x00000010 using namespace mbed; using namespace events; @@ -56,35 +65,21 @@ using namespace events; #endif /***************************************************************************** - * Private Member Functions * - ****************************************************************************/ -bool LoRaWANStack::is_port_valid(uint8_t port, bool allow_port_0) -{ - //Application should not use reserved and illegal port numbers. - if (port == 0) { - return allow_port_0; - } else { - return true; - } -} - -lorawan_status_t LoRaWANStack::set_application_port(uint8_t port, bool allow_port_0) -{ - if (is_port_valid(port, allow_port_0)) { - _app_port = port; - return LORAWAN_STATUS_OK; - } - - return LORAWAN_STATUS_PORT_INVALID; -} - -/***************************************************************************** - * Constructor and destructor * + * Constructor * ****************************************************************************/ LoRaWANStack::LoRaWANStack() : _loramac(), - _device_current_state(DEVICE_STATE_NOT_INITIALIZED), _num_retry(1), - _app_port(INVALID_PORT), _link_check_requested(false), _queue(NULL) + _device_current_state(DEVICE_STATE_NOT_INITIALIZED), + _lw_session(), + _tx_msg(), + _rx_msg(), + _num_retry(1), + _ctrl_flags(IDLE_FLAG), + _app_port(INVALID_PORT), + _link_check_requested(false), + _automatic_uplink_ongoing(false), + _ready_for_rx(true), + _queue(NULL) { #ifdef MBED_CONF_LORA_APP_PORT if (is_port_valid(MBED_CONF_LORA_APP_PORT)) { @@ -93,22 +88,55 @@ LoRaWANStack::LoRaWANStack() tr_error("User defined port in .json is illegal."); } #endif - - memset(&_lw_session, 0, sizeof(_lw_session)); - memset(&_rx_msg, 0, sizeof(_rx_msg)); - - LoRaMacPrimitives.mcps_confirm = callback(this, &LoRaWANStack::mcps_confirm_handler); - LoRaMacPrimitives.mcps_indication = callback(this, &LoRaWANStack::mcps_indication_handler); - LoRaMacPrimitives.mlme_confirm = callback(this, &LoRaWANStack::mlme_confirm_handler); - LoRaMacPrimitives.mlme_indication = callback(this, &LoRaWANStack::mlme_indication_handler); } /***************************************************************************** - * Public member functions * + * Public Methods * ****************************************************************************/ void LoRaWANStack::bind_radio_driver(LoRaRadio& radio) { + radio_events.tx_done = mbed::callback(this, &LoRaWANStack::tx_interrupt_handler); + radio_events.rx_done = mbed::callback(this, &LoRaWANStack::rx_interrupt_handler); + radio_events.rx_error = mbed::callback(this, &LoRaWANStack::rx_error_interrupt_handler); + radio_events.tx_timeout = mbed::callback(this, &LoRaWANStack::tx_timeout_interrupt_handler); + radio_events.rx_timeout = mbed::callback(this, &LoRaWANStack::rx_timeout_interrupt_handler); + _loramac.bind_radio_driver(radio); + + radio.lock(); + radio.init_radio(&radio_events); + radio.unlock(); +} + +lorawan_status_t LoRaWANStack::initialize_mac_layer(EventQueue *queue) +{ + if(!queue) { + return LORAWAN_STATUS_PARAMETER_INVALID; + } + + tr_debug("Initializing MAC layer"); + _queue = queue; + + return state_controller(DEVICE_STATE_IDLE); +} + +lorawan_status_t LoRaWANStack::set_lora_callbacks(const lorawan_app_callbacks_t *callbacks) +{ + if (!callbacks || !callbacks->events) { + return LORAWAN_STATUS_PARAMETER_INVALID; + } + + _callbacks.events = callbacks->events; + + if (callbacks->link_check_resp) { + _callbacks.link_check_resp = callbacks->link_check_resp; + } + + if (callbacks->battery_level) { + _callbacks.battery_level = callbacks->battery_level; + } + + return LORAWAN_STATUS_OK; } lorawan_status_t LoRaWANStack::connect() @@ -149,117 +177,6 @@ lorawan_status_t LoRaWANStack::connect(const lorawan_connect_t &connect) return handle_connect(is_otaa); } -lorawan_status_t LoRaWANStack::handle_connect(bool is_otaa) -{ - device_states_t new_state; - - if (is_otaa) { - tr_debug("Initiating OTAA"); - - // As mentioned in the comment above, in 1.0.2 spec, counters are always set - // to zero for new connection. This section is common for both normal and - // connection restore at this moment. Will change in future with 1.1 support. - _lw_session.downlink_counter = 0; - _lw_session.uplink_counter = 0; - new_state = DEVICE_STATE_JOINING; - } else { - // If current state is SHUTDOWN, device may be trying to re-establish - // communication. In case of ABP specification is meddled about frame counters. - // It says to reset counters to zero but there is no mechanism to tell the - // network server that the device was disconnected or restarted. - // At the moment, this implementation does not support a non-volatile - // memory storage. - //_lw_session.downlink_counter; //Get from NVM - //_lw_session.uplink_counter; //Get from NVM - - tr_debug("Initiating ABP"); - tr_debug("Frame Counters. UpCnt=%lu, DownCnt=%lu", - _lw_session.uplink_counter, _lw_session.downlink_counter); - new_state = DEVICE_STATE_ABP_CONNECTING; - } - - return lora_state_machine(new_state); -} - -lorawan_status_t LoRaWANStack::initialize_mac_layer(EventQueue *queue) -{ - if(!queue) { - return LORAWAN_STATUS_PARAMETER_INVALID; - } - - if (DEVICE_STATE_NOT_INITIALIZED != _device_current_state) - { - tr_debug("Initialized already"); - return LORAWAN_STATUS_OK; - } - - tr_debug("Initializing MAC layer"); - _queue = queue; - - _loramac.initialize(&LoRaMacPrimitives, queue); - - // Reset counters to zero. Will change in future with 1.1 support. - _lw_session.downlink_counter = 0; - _lw_session.uplink_counter = 0; - - return lora_state_machine(DEVICE_STATE_INIT); -} - -lorawan_status_t LoRaWANStack::set_confirmed_msg_retry(uint8_t count) -{ - if (count >= MAX_CONFIRMED_MSG_RETRIES) { - return LORAWAN_STATUS_PARAMETER_INVALID; - } - - _num_retry = count; - - return LORAWAN_STATUS_OK; -} - -/*! - * \brief MLME-Indication event function - * - * \param [IN] mlmeIndication - Pointer to the indication structure. - */ -void LoRaWANStack::mlme_indication_handler(loramac_mlme_indication_t *mlmeIndication) -{ - switch( mlmeIndication->indication_type ) - { - case MLME_SCHEDULE_UPLINK: - { - // The MAC signals that we shall provide an uplink as soon as possible -#if (MBED_CONF_LORA_AUTOMATIC_UPLINK_MESSAGE) - tr_debug("mlme indication: sending empty uplink to port 0 to acknowledge MAC commands..."); - send_automatic_uplink_message(0); -#else - send_event_to_application(UPLINK_REQUIRED); -#endif - break; - } - default: - break; - } -} - -lorawan_status_t LoRaWANStack::set_lora_callbacks(lorawan_app_callbacks_t *cbs) -{ - if (!cbs || !cbs->events) { - return LORAWAN_STATUS_PARAMETER_INVALID; - } - - _callbacks.events = cbs->events; - - if (cbs->link_check_resp) { - _callbacks.link_check_resp = cbs->link_check_resp; - } - - if (cbs->battery_level) { - _callbacks.battery_level = cbs->battery_level; - } - - return LORAWAN_STATUS_OK; -} - lorawan_status_t LoRaWANStack::add_channels(const lorawan_channelplan_t &channel_plan) { if (_device_current_state == DEVICE_STATE_NOT_INITIALIZED) { @@ -270,6 +187,16 @@ lorawan_status_t LoRaWANStack::add_channels(const lorawan_channelplan_t &channel return _loramac.add_channel_plan(channel_plan); } +lorawan_status_t LoRaWANStack::remove_a_channel(uint8_t channel_id) +{ + if (_device_current_state == DEVICE_STATE_NOT_INITIALIZED) { + tr_error("Stack not initialized!"); + return LORAWAN_STATUS_NOT_INITIALIZED; + } + + return _loramac.remove_single_channel(channel_id); +} + lorawan_status_t LoRaWANStack::drop_channel_list() { if (_device_current_state == DEVICE_STATE_NOT_INITIALIZED) { @@ -280,38 +207,24 @@ lorawan_status_t LoRaWANStack::drop_channel_list() return _loramac.remove_channel_plan(); } -lorawan_status_t LoRaWANStack::remove_a_channel(uint8_t channel_id) -{ - if (_device_current_state == DEVICE_STATE_NOT_INITIALIZED ) - { - tr_error("Stack not initialized!"); - return LORAWAN_STATUS_NOT_INITIALIZED; - } - - return _loramac.remove_single_channel(channel_id); -} - lorawan_status_t LoRaWANStack::get_enabled_channels(lorawan_channelplan_t& channel_plan) { - if (_device_current_state == DEVICE_STATE_JOINING - || _device_current_state == DEVICE_STATE_NOT_INITIALIZED - || _device_current_state == DEVICE_STATE_INIT) - { - tr_error("Cannot get channel plan until Joined!"); - return LORAWAN_STATUS_BUSY; - } - - return _loramac.get_channel_plan(channel_plan); -} - -lorawan_status_t LoRaWANStack::enable_adaptive_datarate(bool adr_enabled) -{ - if (DEVICE_STATE_NOT_INITIALIZED == _device_current_state) - { + if (_device_current_state == DEVICE_STATE_NOT_INITIALIZED) { tr_error("Stack not initialized!"); return LORAWAN_STATUS_NOT_INITIALIZED; } - _loramac.enable_adaptive_datarate(adr_enabled); + + return _loramac.get_channel_plan(channel_plan); +} + +lorawan_status_t LoRaWANStack::set_confirmed_msg_retry(uint8_t count) +{ + if (count >= MAX_CONFIRMED_MSG_RETRIES) { + return LORAWAN_STATUS_PARAMETER_INVALID; + } + + _num_retry = count; + return LORAWAN_STATUS_OK; } @@ -326,24 +239,19 @@ lorawan_status_t LoRaWANStack::set_channel_data_rate(uint8_t data_rate) return _loramac.set_channel_data_rate(data_rate); } -void LoRaWANStack::send_event_to_application(const lorawan_event_t event) const + +lorawan_status_t LoRaWANStack::enable_adaptive_datarate(bool adr_enabled) { - if (_callbacks.events) { - const int ret = _queue->call(_callbacks.events, event); - MBED_ASSERT(ret != 0); - (void)ret; + if (_device_current_state == DEVICE_STATE_NOT_INITIALIZED) + { + tr_error("Stack not initialized!"); + return LORAWAN_STATUS_NOT_INITIALIZED; } + _loramac.enable_adaptive_datarate(adr_enabled); + return LORAWAN_STATUS_OK; } -void LoRaWANStack::send_automatic_uplink_message(const uint8_t port) -{ - const int16_t ret = handle_tx(port, NULL, 0, MSG_CONFIRMED_FLAG, true, true); - if (ret < 0) { - send_event_to_application(AUTOMATIC_UPLINK_ERROR); - } -} - -int16_t LoRaWANStack::handle_tx(uint8_t port, const uint8_t* data, +int16_t LoRaWANStack::handle_tx(const uint8_t port, const uint8_t* data, uint16_t length, uint8_t flags, bool null_allowed, bool allow_port_0) { @@ -384,48 +292,20 @@ int16_t LoRaWANStack::handle_tx(uint8_t port, const uint8_t* data, } if (flags == 0 || - (flags & MSG_FLAG_MASK) == (MSG_CONFIRMED_FLAG|MSG_UNCONFIRMED_FLAG)) { + (flags & MSG_FLAG_MASK) == (MSG_CONFIRMED_FLAG|MSG_UNCONFIRMED_FLAG)) { tr_error("CONFIRMED and UNCONFIRMED are mutually exclusive for send()"); return LORAWAN_STATUS_PARAMETER_INVALID; } int16_t len = _loramac.prepare_ongoing_tx(port, data, length, flags, _num_retry); - status = lora_state_machine(DEVICE_STATE_SEND); + status = state_controller(DEVICE_STATE_SCHEDULING); // send user the length of data which is scheduled now. // user should take care of the pending data. return (status == LORAWAN_STATUS_OK) ? len : (int16_t) status; } -int convert_to_msg_flag(const mcps_type_t type) -{ - int msg_flag = MSG_UNCONFIRMED_FLAG; - switch (type) { - case MCPS_UNCONFIRMED: - msg_flag = MSG_UNCONFIRMED_FLAG; - break; - - case MCPS_CONFIRMED: - msg_flag = MSG_CONFIRMED_FLAG; - break; - - case MCPS_MULTICAST: - msg_flag = MSG_MULTICAST_FLAG; - break; - - case MCPS_PROPRIETARY: - msg_flag = MSG_PROPRIETARY_FLAG; - break; - - default: - tr_error("Unknown message type!"); - MBED_ASSERT(0); - } - - return msg_flag; -} - int16_t LoRaWANStack::handle_rx(uint8_t* data, uint16_t length, uint8_t& port, int& flags, bool validate_params) { if (!_lw_session.active) { @@ -492,181 +372,14 @@ int16_t LoRaWANStack::handle_rx(uint8_t* data, uint16_t length, uint8_t& port, i // anything pending. If not, memset the buffer to zero and indicate // that no read is in progress if (read_complete) { - memset(_rx_msg.msg.mcps_indication.buffer, 0, LORAMAC_PHY_MAXPAYLOAD); + _rx_msg.msg.mcps_indication.buffer = NULL; + _rx_msg.msg.mcps_indication.buffer_size = 0; _rx_msg.receive_ready = false; } return base_size; } -void LoRaWANStack::mlme_confirm_handler(loramac_mlme_confirm_t *mlme_confirm) -{ - switch (mlme_confirm->req_type) { - case MLME_JOIN: - if (mlme_confirm->status == LORAMAC_EVENT_INFO_STATUS_OK) { - if (lora_state_machine(DEVICE_STATE_JOINED) != LORAWAN_STATUS_OK) { - tr_error("Lora state machine did not return LORAWAN_STATUS_OK"); - } - } else { - if (lora_state_machine(DEVICE_STATE_IDLE) != LORAWAN_STATUS_IDLE) { - tr_error("Lora state machine did not return DEVICE_STATE_IDLE !"); - } - - send_event_to_application(JOIN_FAILURE); - } - break; - case MLME_LINK_CHECK: - if (mlme_confirm->status == LORAMAC_EVENT_INFO_STATUS_OK) { -#if defined(LORAWAN_COMPLIANCE_TEST) - if (_compliance_test.running == true) { - _compliance_test.link_check = true; - _compliance_test.demod_margin = mlme_confirm->demod_margin; - _compliance_test.nb_gateways = mlme_confirm->nb_gateways; - } else -#endif - { - if (_callbacks.link_check_resp) { - const int ret = _queue->call(_callbacks.link_check_resp, - mlme_confirm->demod_margin, - mlme_confirm->nb_gateways); - MBED_ASSERT(ret != 0); - (void)ret; - } - } - } - break; - default: - break; - } -} - -void LoRaWANStack::mcps_confirm_handler(loramac_mcps_confirm_t *mcps_confirm) -{ - if (mcps_confirm->status != LORAMAC_EVENT_INFO_STATUS_OK) { - _loramac.reset_ongoing_tx(); - - tr_error("mcps_confirm_handler: Error code = %d", mcps_confirm->status); - - if (mcps_confirm->status == LORAMAC_EVENT_INFO_STATUS_TX_TIMEOUT) { - send_event_to_application(TX_TIMEOUT); - return; - } else if (mcps_confirm->status == LORAMAC_EVENT_INFO_STATUS_RX2_TIMEOUT) { - tr_debug("Did not receive Ack"); - } - - send_event_to_application(TX_ERROR); - return; - } - - if (mcps_confirm->req_type == MCPS_CONFIRMED && - mcps_confirm->ack_received) { - tr_debug("Ack received."); - } - - _lw_session.uplink_counter = mcps_confirm->ul_frame_counter; - _loramac.set_tx_ongoing(false); - send_event_to_application(TX_DONE); -} - -void LoRaWANStack::mcps_indication_handler(loramac_mcps_indication_t *mcps_indication) -{ - if (mcps_indication->status != LORAMAC_EVENT_INFO_STATUS_OK) { - tr_error("RX_ERROR: mcps_indication status = %d", mcps_indication->status); - send_event_to_application(RX_ERROR); - return; - } - - switch (mcps_indication->type) { - case MCPS_UNCONFIRMED: - break; - case MCPS_CONFIRMED: - break; - case MCPS_PROPRIETARY: - break; - case MCPS_MULTICAST: - break; - default: - break; - } - - //TODO: - // Check Multicast - // Check Port - // Check Datarate - // Check FramePending - // Check Buffer - // Check BufferSize - // Check Rssi - // Check Snr - // Check RxSlot - - _lw_session.downlink_counter++; - -#if defined(LORAWAN_COMPLIANCE_TEST) - if (_compliance_test.running == true) { - _compliance_test.downlink_counter++; - } -#endif - - if (!mcps_indication->is_data_recvd) { - return; - } - - switch (mcps_indication->port) { - case 224: { -#if defined(LORAWAN_COMPLIANCE_TEST) - tr_debug("Compliance test command received."); - compliance_test_handler(mcps_indication); -#else - tr_info("Compliance test disabled."); -#endif - break; - } - default: { - if (is_port_valid(mcps_indication->port) == true || - mcps_indication->type == MCPS_PROPRIETARY) { - // Valid message arrived. - _rx_msg.type = LORAMAC_RX_MCPS_INDICATION; - _rx_msg.msg.mcps_indication.buffer_size = mcps_indication->buffer_size; - _rx_msg.msg.mcps_indication.port = mcps_indication->port; - _rx_msg.msg.mcps_indication.buffer = mcps_indication->buffer; - _rx_msg.msg.mcps_indication.type = mcps_indication->type; - - // Notify application about received frame.. - tr_debug("Received %d bytes", _rx_msg.msg.mcps_indication.buffer_size); - _rx_msg.receive_ready = true; - - send_event_to_application(RX_DONE); - - /* - * If fPending bit is set we try to generate an empty packet - * with CONFIRMED flag set. We always set a CONFIRMED flag so - * that we could retry a certain number of times if the uplink - * failed for some reason - * or - * Class C and node received a confirmed message so we need to - * send an empty packet to acknowledge the message. - * This scenario is unspecified by LoRaWAN 1.0.2 specification, - * but version 1.1.0 says that network SHALL not send any new - * confirmed messages until ack has been sent - */ - if ((_loramac.get_device_class() != CLASS_C && mcps_indication->fpending_status) || - (_loramac.get_device_class() == CLASS_C && mcps_indication->type == MCPS_CONFIRMED)) { -#if (MBED_CONF_LORA_AUTOMATIC_UPLINK_MESSAGE) - tr_debug("Sending empty uplink message..."); - send_automatic_uplink_message(mcps_indication->port); -#else - send_event_to_application(UPLINK_REQUIRED); -#endif - } - } else { - // Invalid port, ports 0, 224 and 225-255 are reserved. - } - break; - } - } -} - lorawan_status_t LoRaWANStack::set_link_check_request() { _link_check_requested = true; @@ -686,7 +399,7 @@ void LoRaWANStack::remove_link_check_request() lorawan_status_t LoRaWANStack::shutdown() { - return lora_state_machine(DEVICE_STATE_SHUTDOWN); + return state_controller(DEVICE_STATE_SHUTDOWN); } lorawan_status_t LoRaWANStack::set_device_class(const device_class_t& device_class) @@ -698,117 +411,466 @@ lorawan_status_t LoRaWANStack::set_device_class(const device_class_t& device_cla return LORAWAN_STATUS_OK; } -lorawan_status_t LoRaWANStack::lora_state_machine(device_states_t new_state) +/***************************************************************************** + * Interrupt handlers * + ****************************************************************************/ +void LoRaWANStack::tx_interrupt_handler(void) { - lorawan_status_t status = LORAWAN_STATUS_DEVICE_OFF; + const int ret = _queue->call(this, &LoRaWANStack::process_transmission); + MBED_ASSERT(ret != 0); + (void)ret; +} - _device_current_state = new_state; +void LoRaWANStack::rx_interrupt_handler(const uint8_t *payload, uint16_t size, + int16_t rssi, int8_t snr) +{ + if (!_ready_for_rx || size > sizeof _rx_payload) { + return; + } - switch (_device_current_state) { - case DEVICE_STATE_SHUTDOWN: - /* - * Remove channels - * Radio will be put to sleep by the APIs underneath - */ - drop_channel_list(); - _loramac.disconnect(); + _ready_for_rx = false; + memcpy(_rx_payload, payload, size); + + const uint8_t *ptr = _rx_payload; + const int ret = _queue->call(this, &LoRaWANStack::process_reception, + ptr, size, rssi, snr); + MBED_ASSERT(ret != 0); + (void)ret; +} + +void LoRaWANStack::rx_error_interrupt_handler(void) +{ + const int ret = _queue->call(this, &LoRaWANStack::process_reception_timeout, + false); + MBED_ASSERT(ret != 0); + (void)ret; +} + +void LoRaWANStack::tx_timeout_interrupt_handler(void) +{ + const int ret = _queue->call(this, &LoRaWANStack::process_transmission_timeout); + MBED_ASSERT(ret != 0); + (void)ret; +} + +void LoRaWANStack::rx_timeout_interrupt_handler(void) +{ + const int ret = _queue->call(this, &LoRaWANStack::process_reception_timeout, + true); + MBED_ASSERT(ret != 0); + (void)ret; +} + +/***************************************************************************** + * Processors for deferred interrupts * + ****************************************************************************/ +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; + state_controller(DEVICE_STATE_STATUS_CHECK); + state_machine_run_to_completion(); +} + +void LoRaWANStack::process_transmission(void) +{ + _loramac.on_radio_tx_done(); + tr_debug("Transmission completed"); + + if (_device_current_state == DEVICE_STATE_JOINING) { + _device_current_state = DEVICE_STATE_AWAITING_JOIN_ACCEPT; + } + + 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; + _device_current_state = DEVICE_STATE_AWAITING_ACK; + return; + } + + // 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; + _ctrl_flags |= TX_DONE_FLAG; + + // In Class C, reception timeout never happens, so we handle the state + // progression here + if (_loramac.get_device_class() == CLASS_C) { + _loramac.post_process_mcps_req(); + state_controller(DEVICE_STATE_STATUS_CHECK); + 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; + + _loramac.on_radio_rx_done(payload, size, rssi, snr); + + if (_loramac.get_mlme_confirmation()->pending) { + _loramac.post_process_mlme_request(); + mlme_confirm_handler(); + } + + if (_loramac.nwk_joined()) { + if (_loramac.get_mcps_indication()->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 + 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; + state_controller(DEVICE_STATE_STATUS_CHECK); + } + } else { + // handle UNCONFIRMED case here, RX slots were turned off due to + // valid packet reception + _loramac.post_process_mcps_req(); + _ctrl_flags |= TX_DONE_FLAG; + state_controller(DEVICE_STATE_STATUS_CHECK); + } + + // handle any pending MCPS indication + if (_loramac.get_mcps_indication()->pending) { + _loramac.post_process_mcps_ind(); + _ctrl_flags |= MSG_RECVD_FLAG; + 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 + state_machine_run_to_completion(); + } + + if (_loramac.get_mlme_indication()->pending) { + tr_debug("MLME Indication pending"); + _loramac.post_process_mlme_ind(); + tr_debug("Automatic uplink requested"); + mlme_indication_handler(); + } + } + + _ready_for_rx = true; +} + +void LoRaWANStack::process_reception_timeout(bool is_timeout) +{ + // when is_timeout == false, a CRC error took place in the received frame + // we treat that erroneous frame as no frame received at all, hence handle + // it exactly as we would handle timeout + rx_slot_t slot = _loramac.on_radio_rx_timeout(is_timeout); + + if (slot == RX_SLOT_WIN_2 && !_loramac.nwk_joined()) { + state_controller(DEVICE_STATE_JOINING); + return; + } + + /** + * LoRaWAN Specification 1.0.2. Section 3.3.6 + * Main point: + * We indicate successful transmission + * of UNCONFIRMED message after RX windows are done with. + * For a CONFIRMED message, it means that we have not received + * ack (actually nothing was received), and we should retransmit if we can. + */ + if (slot == RX_SLOT_WIN_2) { + _loramac.post_process_mcps_req(); + + if (_loramac.get_mcps_confirmation()->req_type == MCPS_CONFIRMED + && _loramac.continue_sending_process()) { + return; + } + + state_controller(DEVICE_STATE_STATUS_CHECK); + state_machine_run_to_completion(); + } +} + +/***************************************************************************** + * Private methods * + ****************************************************************************/ +bool LoRaWANStack::is_port_valid(const uint8_t port, bool allow_port_0) +{ + //Application should not use reserved and illegal port numbers. + if (port == 0) { + return allow_port_0; + } else { + return true; + } +} + +lorawan_status_t LoRaWANStack::set_application_port(const uint8_t port, bool allow_port_0) +{ + if (is_port_valid(port, allow_port_0)) { + _app_port = port; + return LORAWAN_STATUS_OK; + } + + return LORAWAN_STATUS_PORT_INVALID; +} + +void LoRaWANStack::state_machine_run_to_completion() +{ + if (_loramac.get_device_class() == CLASS_C) { + _device_current_state = DEVICE_STATE_RECEIVING; + return; + } + + _device_current_state = DEVICE_STATE_IDLE; +} + +void LoRaWANStack::send_event_to_application(const lorawan_event_t event) const +{ + if (_callbacks.events) { + const int ret = _queue->call(_callbacks.events, event); + MBED_ASSERT(ret != 0); + (void)ret; + } +} + +void LoRaWANStack::send_automatic_uplink_message(const uint8_t port) +{ + const int16_t ret = handle_tx(port, NULL, 0, MSG_CONFIRMED_FLAG, true, true); + if (ret < 0) { + send_event_to_application(AUTOMATIC_UPLINK_ERROR); + } +} + +int LoRaWANStack::convert_to_msg_flag(const mcps_type_t type) +{ + int msg_flag = MSG_UNCONFIRMED_FLAG; + switch (type) { + case MCPS_UNCONFIRMED: + msg_flag = MSG_UNCONFIRMED_FLAG; + break; + + case MCPS_CONFIRMED: + msg_flag = MSG_CONFIRMED_FLAG; + break; + + case MCPS_MULTICAST: + msg_flag = MSG_MULTICAST_FLAG; + break; + + case MCPS_PROPRIETARY: + msg_flag = MSG_PROPRIETARY_FLAG; + break; + + default: + tr_error("Unknown message type!"); + MBED_ASSERT(0); + } + + return msg_flag; +} + +lorawan_status_t LoRaWANStack::handle_connect(bool is_otaa) +{ + if (is_otaa) { + tr_debug("Initiating OTAA"); + + // In 1.0.2 spec, counters are always set to zero for new connection. + // This section is common for both normal and + // connection restore at this moment. Will change in future with 1.1 support. + _lw_session.downlink_counter = 0; + _lw_session.uplink_counter = 0; + _ctrl_flags |= USING_OTAA_FLAG; + } else { + // If current state is SHUTDOWN, device may be trying to re-establish + // communication. In case of ABP specification is meddled about frame counters. + // It says to reset counters to zero but there is no mechanism to tell the + // network server that the device was disconnected or restarted. + // At the moment, this implementation does not support a non-volatile + // memory storage. + //_lw_session.downlink_counter; //Get from NVM + //_lw_session.uplink_counter; //Get from NVM + + tr_debug("Initiating ABP"); + tr_debug("Frame Counters. UpCnt=%lu, DownCnt=%lu", + _lw_session.uplink_counter, _lw_session.downlink_counter); + _ctrl_flags &= ~USING_OTAA_FLAG; + } + + return state_controller(DEVICE_STATE_CONNECTING); +} + +void LoRaWANStack::mlme_indication_handler() +{ + if (_loramac.get_mlme_indication()->indication_type == MLME_SCHEDULE_UPLINK) { + // The MAC signals that we shall provide an uplink as soon as possible +#if MBED_CONF_LORA_AUTOMATIC_UPLINK_MESSAGE + _automatic_uplink_ongoing = true; + tr_debug("mlme indication: sending empty uplink to port 0 to acknowledge MAC commands..."); + send_automatic_uplink_message(0); +#else + + send_event_to_application(UPLINK_REQUIRED); +#endif + return; + } + + tr_error("Unknown MLME Indication type."); +} + +void LoRaWANStack::mlme_confirm_handler() +{ + if (_loramac.get_mlme_confirmation()->req_type == MLME_LINK_CHECK) { + if (_loramac.get_mlme_confirmation()->status == LORAMAC_EVENT_INFO_STATUS_OK) { +#if defined(LORAWAN_COMPLIANCE_TEST) + if (_compliance_test.running == true) { + _compliance_test.link_check = true; + _compliance_test.demod_margin = _loramac.get_mlme_confirmation()->demod_margin; + _compliance_test.nb_gateways = _loramac.get_mlme_confirmation()->nb_gateways; + } else +#endif + { + if (_callbacks.link_check_resp) { + const int ret = _queue->call(_callbacks.link_check_resp, + _loramac.get_mlme_confirmation()->demod_margin, + _loramac.get_mlme_confirmation()->nb_gateways); + MBED_ASSERT(ret != 0); + (void)ret; + } + } + } + } else if (_loramac.get_mlme_confirmation()->req_type == MLME_JOIN) { + if (_loramac.get_mlme_confirmation()->status == LORAMAC_EVENT_INFO_STATUS_OK) { + state_controller(DEVICE_STATE_CONNECTED); + } else { + tr_error("Joining error: %d", _loramac.get_mlme_confirmation()->status); + _device_current_state = DEVICE_STATE_AWAITING_JOIN_ACCEPT; + state_controller(DEVICE_STATE_JOINING); + } + } +} + +void LoRaWANStack::mcps_confirm_handler() +{ + // success case + if (_loramac.get_mcps_confirmation()->status == LORAMAC_EVENT_INFO_STATUS_OK) { + _lw_session.uplink_counter = _loramac.get_mcps_confirmation()->ul_frame_counter; + send_event_to_application(TX_DONE); + return; + } + + // failure case + tr_error("mcps_confirmation: Error code = %d", _loramac.get_mcps_confirmation()->status); + + if (_loramac.get_mcps_confirmation()->status == LORAMAC_EVENT_INFO_STATUS_TX_TIMEOUT) { + send_event_to_application(TX_TIMEOUT); + return; + } + + // if no ack was received, send TX_ERROR + send_event_to_application(TX_ERROR); +} + +void LoRaWANStack::mcps_indication_handler() +{ + const loramac_mcps_indication_t *mcps_indication = _loramac.get_mcps_indication(); + if (mcps_indication->status != LORAMAC_EVENT_INFO_STATUS_OK) { + tr_error("RX_ERROR: mcps_indication status = %d", mcps_indication->status); + send_event_to_application(RX_ERROR); + return; + } + + _lw_session.downlink_counter = mcps_indication->dl_frame_counter; #if defined(LORAWAN_COMPLIANCE_TEST) - _loramac.LoRaMacStopTxTimer(); + if (_compliance_test.running == true) { + _compliance_test.downlink_counter++; + } #endif - _loramac.set_nwk_joined(false); - _loramac.reset_ongoing_tx(true); + if (mcps_indication->port == 224) { +#if defined(LORAWAN_COMPLIANCE_TEST) + tr_debug("Compliance test command received."); + compliance_test_handler(mcps_indication); +#else + tr_info("Compliance test disabled."); +#endif + } else { + if (mcps_indication->is_data_recvd) { + // Valid message arrived. + _rx_msg.type = LORAMAC_RX_MCPS_INDICATION; + _rx_msg.msg.mcps_indication.buffer_size = mcps_indication->buffer_size; + _rx_msg.msg.mcps_indication.port = mcps_indication->port; + _rx_msg.msg.mcps_indication.buffer = mcps_indication->buffer; + _rx_msg.msg.mcps_indication.type = mcps_indication->type; - _rx_msg.msg.mcps_indication.buffer = NULL; - _rx_msg.receive_ready = false; - _rx_msg.prev_read_size = 0; - _rx_msg.msg.mcps_indication.buffer_size = 0; + // Notify application about received frame.. + tr_debug("Packet Received %d bytes", + _rx_msg.msg.mcps_indication.buffer_size); + _rx_msg.receive_ready = true; + send_event_to_application(RX_DONE); + } - _lw_session.active = false; + /* + * If fPending bit is set we try to generate an empty packet + * with CONFIRMED flag set. We always set a CONFIRMED flag so + * that we could retry a certain number of times if the uplink + * failed for some reason + * or + * Class C and node received a confirmed message so we need to + * send an empty packet to acknowledge the message. + * This scenario is unspecified by LoRaWAN 1.0.2 specification, + * but version 1.1.0 says that network SHALL not send any new + * confirmed messages until ack has been sent + */ + if ((_loramac.get_device_class() != CLASS_C && mcps_indication->fpending_status) + || + (_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; + send_automatic_uplink_message(mcps_indication->port); +#else + send_event_to_application(UPLINK_REQUIRED); +#endif + } + } +} - tr_debug("LoRaWAN protocol has been shut down."); - send_event_to_application(DISCONNECTED); - status = LORAWAN_STATUS_DEVICE_OFF; +lorawan_status_t LoRaWANStack::state_controller(device_states_t new_state) +{ + lorawan_status_t status = LORAWAN_STATUS_OK; + + switch (new_state) { + case DEVICE_STATE_IDLE: + process_idle_state(status); break; - case DEVICE_STATE_NOT_INITIALIZED: - status = LORAWAN_STATUS_DEVICE_OFF; - break; - case DEVICE_STATE_INIT: - status = LORAWAN_STATUS_OK; + case DEVICE_STATE_CONNECTING: + process_connecting_state(status); break; case DEVICE_STATE_JOINING: - if (_lw_session.connect_type == LORAWAN_CONNECTION_OTAA) { - tr_debug("Send Join-request.."); - - status = _loramac.join(true); - if (status != LORAWAN_STATUS_OK) { - return status; - } - - return LORAWAN_STATUS_CONNECT_IN_PROGRESS; - } else { - status = LORAWAN_STATUS_PARAMETER_INVALID; - } + process_joining_state(status); break; - case DEVICE_STATE_JOINED: - tr_debug("Join OK!"); - - _lw_session.active = true; - - send_event_to_application(CONNECTED); - status = LORAWAN_STATUS_OK; + case DEVICE_STATE_CONNECTED: + process_connected_state(); break; - case DEVICE_STATE_ABP_CONNECTING: - - _loramac.join(false); - - tr_debug("ABP Connection OK!"); - - status = LORAWAN_STATUS_OK; - - _lw_session.active = true; - send_event_to_application(CONNECTED); + case DEVICE_STATE_SCHEDULING: + process_scheduling_state(status); break; - case DEVICE_STATE_SEND: - if (_loramac.tx_ongoing()) { - status = LORAWAN_STATUS_OK; - } else { - _loramac.set_tx_ongoing(true); - status = _loramac.send_ongoing_tx(); - - switch (status) { - case LORAWAN_STATUS_OK: - tr_debug("Frame scheduled to TX.."); - break; - case LORAWAN_STATUS_CRYPTO_FAIL: - tr_error("Crypto failed. Clearing TX buffers"); - send_event_to_application(TX_CRYPTO_ERROR); - break; - default: - tr_error("Failure to schedule TX!"); - send_event_to_application(TX_SCHEDULING_ERROR); - break; - } - } - - _device_current_state = DEVICE_STATE_IDLE; + case DEVICE_STATE_STATUS_CHECK: + process_status_check_state(); break; - case DEVICE_STATE_IDLE: - status = LORAWAN_STATUS_IDLE; + case DEVICE_STATE_SHUTDOWN: + process_shutdown_state(status); break; -#if defined(LORAWAN_COMPLIANCE_TEST) - case DEVICE_STATE_COMPLIANCE_TEST: - tr_debug("Device is in compliance test mode."); - - _loramac.LoRaMacSetTxTimer(5000); //ms - if (_compliance_test.running == true) { - send_compliance_test_frame_to_mac(); - } - status = LORAWAN_STATUS_COMPLIANCE_TEST_ON; - break; -#endif default: + tr_debug("state_controller: Unknown state!"); status = LORAWAN_STATUS_SERVICE_UNKNOWN; break; } @@ -816,6 +878,163 @@ lorawan_status_t LoRaWANStack::lora_state_machine(device_states_t new_state) return status; } +void LoRaWANStack::process_shutdown_state(lorawan_status_t& op_status) +{ + /** + * Remove channels + * Radio will be put to sleep by the APIs underneath + */ + drop_channel_list(); + _loramac.disconnect(); + _lw_session.active = false; + _device_current_state = DEVICE_STATE_SHUTDOWN; + op_status = LORAWAN_STATUS_DEVICE_OFF; + _ctrl_flags &= ~CONNECTED_FLAG; + send_event_to_application(DISCONNECTED); +} + +void LoRaWANStack::process_status_check_state() +{ + if (_device_current_state == DEVICE_STATE_SENDING || + _device_current_state == DEVICE_STATE_AWAITING_ACK) { + // this happens after RX2 slot is exhausted + // we may or may not have a successful UNCONFIRMED transmission + // here. In CONFIRMED case this block is invoked only + // when the MAX number of retries are exhausted, i.e., only error + // case will fall here. + _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) { + // for CONFIRMED case, ack validity is already checked + _ctrl_flags &= ~TX_DONE_FLAG; + _ctrl_flags &= ~TX_ONGOING_FLAG; + _loramac.set_tx_ongoing(false); + _loramac.reset_ongoing_tx(); + // if an automatic uplink is ongoing, we should not send a TX_DONE + // event to application + if (_automatic_uplink_ongoing) { + _automatic_uplink_ongoing = false; + } else { + mcps_confirm_handler(); + } + } + + // handle any received data and send event accordingly + if (_ctrl_flags & MSG_RECVD_FLAG) { + _ctrl_flags &= ~MSG_RECVD_FLAG; + mcps_indication_handler(); + } + } +} + +void LoRaWANStack::process_scheduling_state(lorawan_status_t& op_status) +{ + if (_device_current_state != DEVICE_STATE_IDLE) { + if (_device_current_state != DEVICE_STATE_RECEIVING + && _loramac.get_device_class() != CLASS_C) { + op_status = LORAWAN_STATUS_BUSY; + return; + } + } + + 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; + } +} + +void LoRaWANStack::process_joining_state(lorawan_status_t& op_status) +{ + if (_device_current_state == DEVICE_STATE_CONNECTING) { + _device_current_state = DEVICE_STATE_JOINING; + tr_debug("Sending Join Request ..."); + op_status = _loramac.join(true); + return; + } + + if (_device_current_state == DEVICE_STATE_AWAITING_JOIN_ACCEPT) { + _device_current_state = DEVICE_STATE_JOINING; + // retry join + bool can_continue = _loramac.continue_joining_process(); + + if (!can_continue) { + send_event_to_application(JOIN_FAILURE); + _device_current_state = DEVICE_STATE_IDLE; + return; + } + } +} + +void LoRaWANStack::process_connected_state() +{ + if (_ctrl_flags & USING_OTAA_FLAG) { + tr_debug("OTAA Connection OK!"); + } + + _lw_session.active = true; + send_event_to_application(CONNECTED); + _ctrl_flags |= CONNECTED_FLAG; + + _device_current_state = DEVICE_STATE_IDLE; +} + +void LoRaWANStack::process_connecting_state(lorawan_status_t& op_status) +{ + if (_device_current_state != DEVICE_STATE_IDLE + && + _device_current_state != DEVICE_STATE_SHUTDOWN) { + op_status = LORAWAN_STATUS_BUSY; + return; + } + + if (_ctrl_flags & CONNECTED_FLAG) { + tr_debug("Already connected"); + op_status = LORAWAN_STATUS_OK; + return; + } + + _device_current_state = DEVICE_STATE_CONNECTING; + + if (_ctrl_flags & USING_OTAA_FLAG) { + process_joining_state(op_status); + return; + } + + op_status = _loramac.join(false); + tr_debug("ABP connection OK."); + process_connected_state(); +} + +void LoRaWANStack::process_idle_state(lorawan_status_t& op_status) +{ + if (_device_current_state == DEVICE_STATE_NOT_INITIALIZED) { + _device_current_state = DEVICE_STATE_IDLE; + process_uninitialized_state(op_status); + return; + } + + _device_current_state = DEVICE_STATE_IDLE; + op_status = LORAWAN_STATUS_OK; +} + +void LoRaWANStack::process_uninitialized_state(lorawan_status_t& op_status) +{ + op_status = _loramac.initialize(_queue); + + if (op_status == LORAWAN_STATUS_OK) { + _device_current_state = DEVICE_STATE_IDLE; + } +} + #if defined(LORAWAN_COMPLIANCE_TEST) lorawan_status_t LoRaWANStack::send_compliance_test_frame_to_mac() diff --git a/features/lorawan/LoRaWANStack.h b/features/lorawan/LoRaWANStack.h index 5bc311e112..ee3cd934aa 100644 --- a/features/lorawan/LoRaWANStack.h +++ b/features/lorawan/LoRaWANStack.h @@ -44,6 +44,7 @@ #include "events/EventQueue.h" #include "platform/Callback.h" #include "platform/NonCopyable.h" +#include "platform/ScopedLock.h" #include "lorastack/mac/LoRaMac.h" #include "system/LoRaWANTimer.h" @@ -51,23 +52,6 @@ #include "LoRaRadio.h" class LoRaWANStack: private mbed::NonCopyable { -private: - /** End-device states. - * - */ - typedef enum device_states { - DEVICE_STATE_NOT_INITIALIZED, - DEVICE_STATE_INIT, - DEVICE_STATE_JOINING, - DEVICE_STATE_ABP_CONNECTING, - DEVICE_STATE_JOINED, - DEVICE_STATE_SEND, - DEVICE_STATE_IDLE, -#if defined(LORAWAN_COMPLIANCE_TEST) - DEVICE_STATE_COMPLIANCE_TEST, -#endif - DEVICE_STATE_SHUTDOWN - } device_states_t; public: LoRaWANStack(); @@ -85,6 +69,19 @@ public: */ void bind_radio_driver(LoRaRadio& radio); + /** End device initialization. + * @param queue A pointer to an EventQueue passed from the application. + * @return LORAWAN_STATUS_OK on success, a negative error code on failure. + */ + lorawan_status_t initialize_mac_layer(events::EventQueue *queue); + + /** Sets all callbacks for the application. + * + * @param callbacks A pointer to the structure carrying callbacks. + * @return LORAWAN_STATUS_OK on success, a negative error code on failure. + */ + lorawan_status_t set_lora_callbacks(const lorawan_app_callbacks_t *callbacks); + /** Connect OTAA or ABP using Mbed-OS config system * * Connect by Over The Air Activation or Activation By Personalization. @@ -162,19 +159,6 @@ public: */ lorawan_status_t connect(const lorawan_connect_t &connect); - /** End device initialization. - * @param queue A pointer to an EventQueue passed from the application. - * @return LORAWAN_STATUS_OK on success, a negative error code on failure. - */ - lorawan_status_t initialize_mac_layer(events::EventQueue *queue); - - /** Sets all callbacks for the application. - * - * @param callbacks A pointer to the structure carrying callbacks. - * @return LORAWAN_STATUS_OK on success, a negative error code on failure. - */ - lorawan_status_t set_lora_callbacks(lorawan_app_callbacks_t *callbacks); - /** Adds channels to use. * * You can provide a list of channels with appropriate parameters filled @@ -395,7 +379,11 @@ public: */ lorawan_status_t set_device_class(const device_class_t& device_class); + void lock(void) { _loramac.lock(); } + void unlock(void) { _loramac.unlock(); } + private: + typedef mbed::ScopedLock Lock; /** * Checks if the user provided port is valid or not */ @@ -404,37 +392,40 @@ private: /** * State machine for stack controller layer. */ - lorawan_status_t lora_state_machine(device_states_t new_state); + lorawan_status_t state_controller(device_states_t new_state); /** - * Callback function for MLME indication. Mac layer calls this function once - * an MLME indication is received. This method translates Mac layer data - * structure into stack layer data structure. + * Helpers for state controller */ - void mlme_indication_handler(loramac_mlme_indication_t *mlmeIndication); + void process_uninitialized_state(lorawan_status_t& op_status); + void process_idle_state(lorawan_status_t& op_status); + void process_connected_state(); + void process_connecting_state(lorawan_status_t& op_status); + void process_joining_state(lorawan_status_t& op_status); + void process_scheduling_state(lorawan_status_t& op_status); + void process_status_check_state(); + void process_shutdown_state(lorawan_status_t& op_status); + void state_machine_run_to_completion(void); /** - * Handles an MLME confirmation coming from the Mac layer and uses it to - * update the state for example, a Join Accept triggers an MLME confirmation, - * that eventually comes here and we take necessary steps accordingly. + * Handles MLME indications */ - void mlme_confirm_handler(loramac_mlme_confirm_t *mlme_confirm); + void mlme_indication_handler(void); /** - * Handles an MCPS confirmation coming from the Mac layer in response to an - * MCPS request. We take appropriate actions in response to the confirmation, - * e.g., letting the application know that ack was not received in case of - * a CONFIRMED message or scheduling error etc. + * Handles an MLME confirmation */ - void mcps_confirm_handler(loramac_mcps_confirm_t *mcps_confirm); + void mlme_confirm_handler(void); /** - * Handles an MCPS indication coming from the Mac layer, e.g., once we - * receive a packet from the Network Server, it is indicated to this handler - * and consequently this handler posts an event to the application that - * there is something available to read. + * Handles an MCPS confirmation */ - void mcps_indication_handler(loramac_mcps_indication_t *mcps_indication); + void mcps_confirm_handler(void); + + /** + * Handles an MCPS indication + */ + void mcps_indication_handler(void); /** * Sets up user application port @@ -459,21 +450,44 @@ private: * * @param port The event to be sent. */ - void send_automatic_uplink_message(const uint8_t port); + void send_automatic_uplink_message(uint8_t port); + + /** + * TX interrupt handlers and corresponding processors + */ + void tx_interrupt_handler(void); + void tx_timeout_interrupt_handler(void); + void process_transmission(void); + void process_transmission_timeout(void); + + /** + * RX interrupt handlers and corresponding processors + */ + void rx_interrupt_handler(const uint8_t *payload, uint16_t size, int16_t rssi, + int8_t snr); + void rx_timeout_interrupt_handler(void); + void rx_error_interrupt_handler(void); + void process_reception(const uint8_t *payload, uint16_t size, int16_t rssi, + int8_t snr); + void process_reception_timeout(bool is_timeout); + + int convert_to_msg_flag(const mcps_type_t type); private: LoRaMac _loramac; - loramac_primitives_t LoRaMacPrimitives; - + radio_events_t radio_events; device_states_t _device_current_state; lorawan_app_callbacks_t _callbacks; lorawan_session_t _lw_session; loramac_tx_message_t _tx_msg; loramac_rx_message_t _rx_msg; uint8_t _num_retry; + uint32_t _ctrl_flags; uint8_t _app_port; bool _link_check_requested; - + bool _automatic_uplink_ongoing; + volatile bool _ready_for_rx; + uint8_t _rx_payload[LORAMAC_PHY_MAXPAYLOAD]; events::EventQueue *_queue; #if defined(LORAWAN_COMPLIANCE_TEST) diff --git a/features/lorawan/lorastack/mac/LoRaMac.cpp b/features/lorawan/lorastack/mac/LoRaMac.cpp index 7cb78cd4f1..f37f63bb0c 100644 --- a/features/lorawan/lorastack/mac/LoRaMac.cpp +++ b/features/lorawan/lorastack/mac/LoRaMac.cpp @@ -35,14 +35,13 @@ SPDX-License-Identifier: BSD-3-Clause #endif //defined(FEATURE_COMMON_PAL) using namespace events; - +using namespace mbed; /* * LoRaWAN spec 6.2: AppKey is AES-128 key */ #define APPKEY_KEY_LENGTH 128 - /*! * Maximum length of the fOpts field */ @@ -63,16 +62,6 @@ using namespace events; */ #define BACKOFF_DC_24_HOURS 10000 -/*! - * Check the MAC layer state every MAC_STATE_CHECK_TIMEOUT in ms. - */ -#define MAC_STATE_CHECK_TIMEOUT 1000 - -/*! - * The maximum number of times the MAC layer tries to get an acknowledge. - */ -#define MAX_ACK_RETRIES 8 - /*! * The frame direction definition for uplink communications. */ @@ -90,9 +79,19 @@ using namespace events; LoRaMac::LoRaMac() - : _lora_phy(_lora_time), mac_commands(), _is_nwk_joined(false) + : _lora_time(), + _lora_phy(_lora_time), + _mac_commands(), + _channel_plan(), + _lora_crypto(), + _ev_queue(NULL), + _mcps_indication(), + _mcps_confirmation(), + _mlme_indication(), + _mlme_confirmation(), + _is_nwk_joined(false), + _device_class(CLASS_A) { - //radio_events_t RadioEvents; _params.keys.dev_eui = NULL; _params.keys.app_eui = NULL; _params.keys.app_key = NULL; @@ -104,8 +103,8 @@ LoRaMac::LoRaMac() _params.dev_nonce = 0; _params.net_id = 0; _params.dev_addr = 0; - _params.buffer_pkt_len = 0; - _params.payload_length = 0; + _params.tx_buffer_len = 0; + _params.rx_buffer_len = 0; _params.ul_frame_counter = 0; _params.dl_frame_counter = 0; _params.is_ul_frame_counter_fixed = false; @@ -115,7 +114,6 @@ LoRaMac::LoRaMac() _params.is_srv_ack_requested = false; _params.ul_nb_rep_counter = 0; _params.timers.mac_init_time = 0; - _params.mac_state = LORAMAC_IDLE; _params.max_ack_timeout_retries = 1; _params.ack_timeout_retry_counter = 1; _params.is_ack_retry_timeout_expired = false; @@ -126,110 +124,104 @@ LoRaMac::LoRaMac() _params.sys_params.adr_on = false; _params.sys_params.max_duty_cycle = 0; - mac_primitives = NULL; - ev_queue = NULL; + reset_mcps_confirmation(); + reset_mlme_confirmation(); + reset_mcps_indication(); } LoRaMac::~LoRaMac() { } - -/*************************************************************************** - * ISRs - Handlers * - **************************************************************************/ -void LoRaMac::handle_tx_done(void) -{ - const int ret = ev_queue->call(this, &LoRaMac::on_radio_tx_done); - MBED_ASSERT(ret != 0); - (void)ret; -} - -void LoRaMac::handle_rx_done(uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr) -{ - const int ret = ev_queue->call(this, &LoRaMac::on_radio_rx_done, payload, size, rssi, snr); - MBED_ASSERT(ret != 0); - (void)ret; -} - -void LoRaMac::handle_rx_error(void) -{ - const int ret = ev_queue->call(this, &LoRaMac::on_radio_rx_error); - MBED_ASSERT(ret != 0); - (void)ret; -} - -void LoRaMac::handle_rx_timeout(void) -{ - const int ret = ev_queue->call(this, &LoRaMac::on_radio_rx_timeout); - MBED_ASSERT(ret != 0); - (void)ret; -} - -void LoRaMac::handle_tx_timeout(void) -{ - const int ret = ev_queue->call(this, &LoRaMac::on_radio_tx_timeout); - MBED_ASSERT(ret != 0); - (void)ret; -} - -void LoRaMac::handle_cad_done(bool cad) -{ - //TODO Not implemented yet - //const int ret = ev_queue->call(this, &LoRaMac::OnRadioCadDone, cad); - //MBED_ASSERT(ret != 0); - //(void)ret; -} - -void LoRaMac::handle_fhss_change_channel(uint8_t cur_channel) -{ - // TODO Not implemented yet - //const int ret = ev_queue->call(this, &LoRaMac::OnRadioFHSSChangeChannel, cur_channel); - //MBED_ASSERT(ret != 0); - //(void)ret; -} - /*************************************************************************** * Radio event callbacks - delegated to Radio driver * **************************************************************************/ -void LoRaMac::on_radio_tx_done( void ) + +const loramac_mcps_confirm_t *LoRaMac::get_mcps_confirmation() const { - lorawan_time_t cur_time = _lora_time.get_current_time( ); + return &_mcps_confirmation; +} + +const loramac_mcps_indication_t *LoRaMac::get_mcps_indication() const +{ + return &_mcps_indication; +} + +const loramac_mlme_confirm_t *LoRaMac::get_mlme_confirmation() const +{ + return &_mlme_confirmation; +} + +const loramac_mlme_indication_t *LoRaMac::get_mlme_indication() const +{ + return &_mlme_indication; +} + +void LoRaMac::post_process_mlme_request() +{ + _mlme_confirmation.pending = false; +} + +void LoRaMac::post_process_mcps_req() +{ + _params.is_last_tx_join_request = false; + _mcps_confirmation.status = LORAMAC_EVENT_INFO_STATUS_OK; + if (_mcps_indication.type == MCPS_CONFIRMED) { + // An MCPS request for a CONFIRMED message has received an ack + // in the downlink message + if (_mcps_confirmation.ack_received) { + _params.is_node_ack_requested = false; + _mcps_confirmation.ack_received = false; + _mcps_indication.is_ack_recvd = false; + if (_params.is_ul_frame_counter_fixed == false) { + _params.ul_frame_counter++; + } + } + + } else { + //UNCONFIRMED + if (_params.is_ul_frame_counter_fixed == false) { + _params.ul_frame_counter++; + } + } +} + +void LoRaMac::post_process_mcps_ind() +{ + _mcps_indication.pending = false; +} + +void LoRaMac::post_process_mlme_ind() +{ + _mlme_indication.pending = false; +} + +void LoRaMac::on_radio_tx_done(void) +{ + lorawan_time_t cur_time = _lora_time.get_current_time(); if (_device_class != CLASS_C) { _lora_phy.put_radio_to_sleep(); } else { - open_continuous_rx2_window(); + // this will open a continuous RX2 window until time==RECV_DELAY1 + open_rx2_window(); } if(_params.is_rx_window_enabled == true) { + // start timer after which rx1_window will get opened _lora_time.start(_params.timers.rx_window1_timer, _params.rx_window1_delay); if (_device_class != CLASS_C) { _lora_time.start(_params.timers.rx_window2_timer, _params.rx_window2_delay); } - if ((_device_class == CLASS_C ) || - (_params.is_node_ack_requested == true)) { + if (_params.is_node_ack_requested == true) { _lora_time.start(_params.timers.ack_timeout_timer, _params.rx_window2_delay + _lora_phy.get_ack_timeout()); } } else { _mcps_confirmation.status = LORAMAC_EVENT_INFO_STATUS_OK; _mlme_confirmation.status = LORAMAC_EVENT_INFO_STATUS_RX2_TIMEOUT; - - if (_params.flags.value == 0) { - _params.flags.bits.mcps_req = 1; - } - - _params.flags.bits.mac_done = 1; - } - - if ((_params.flags.bits.mlme_req == 1) && - (_mlme_confirmation.req_type == MLME_JOIN)) { - _params.is_last_tx_join_request = true; - } else { - _params.is_last_tx_join_request = false; } _params.last_channel_idx = _params.channel; @@ -237,443 +229,508 @@ void LoRaMac::on_radio_tx_done( void ) _lora_phy.set_last_tx_done(_params.channel, _is_nwk_joined, cur_time); _params.timers.aggregated_last_tx_time = cur_time; - - if (_params.is_node_ack_requested == false) { - _mcps_confirmation.status = LORAMAC_EVENT_INFO_STATUS_OK; - _params.ul_nb_rep_counter++; - } } -void LoRaMac::prepare_rx_done_abort(void) +void LoRaMac::abort_rx(void) { - _params.mac_state |= LORAMAC_RX_ABORT; - if (_params.is_node_ack_requested) { - const int ret = ev_queue->call(this, &LoRaMac::on_ack_timeout_timer_event); + const int ret = _ev_queue->call(this, &LoRaMac::on_ack_timeout_timer_event); MBED_ASSERT(ret != 0); (void)ret; } - - _params.flags.bits.mcps_ind = 1; - _params.flags.bits.mac_done = 1; - - _lora_time.start(_params.timers.mac_state_check_timer, 1); } -void LoRaMac::on_radio_rx_done(uint8_t *payload, uint16_t size, int16_t rssi, - int8_t snr) +/** + * This part handles incoming frames in response to Radio RX Interrupt + */ +void LoRaMac::handle_join_accept_frame(const uint8_t *payload, + uint16_t size) { - loramac_mhdr_t mac_hdr; - loramac_frame_ctrl_t fctrl; + uint32_t mic = 0; + uint32_t mic_rx = 0; - uint8_t pkt_header_len = 0; - uint32_t address = 0; - uint8_t app_payload_start_index = 0; - uint8_t frame_len = 0; + _mlme_confirmation.nb_retries = _params.join_request_trial_counter; + + if (0 != _lora_crypto.decrypt_join_frame(payload + 1, size - 1, + _params.keys.app_key, APPKEY_KEY_LENGTH, + _params.rx_buffer + 1)) { + _mlme_confirmation.status = LORAMAC_EVENT_INFO_STATUS_CRYPTO_FAIL; + return; + } + + _params.rx_buffer[0] = payload[0]; + + if (_lora_crypto.compute_join_frame_mic(_params.rx_buffer, + size - LORAMAC_MFR_LEN, + _params.keys.app_key, + APPKEY_KEY_LENGTH, + &mic) != 0) { + _mlme_confirmation.status = LORAMAC_EVENT_INFO_STATUS_CRYPTO_FAIL; + return; + } + + mic_rx |= (uint32_t) _params.rx_buffer[size - LORAMAC_MFR_LEN]; + mic_rx |= ((uint32_t) _params.rx_buffer[size - LORAMAC_MFR_LEN + 1] << 8); + mic_rx |= ((uint32_t) _params.rx_buffer[size - LORAMAC_MFR_LEN + 2] << 16); + mic_rx |= ((uint32_t) _params.rx_buffer[size - LORAMAC_MFR_LEN + 3] << 24); + + if (mic_rx == mic) { + + if (_lora_crypto.compute_skeys_for_join_frame(_params.keys.app_key, + APPKEY_KEY_LENGTH, + _params.rx_buffer + 1, + _params.dev_nonce, + _params.keys.nwk_skey, + _params.keys.app_skey) != 0) { + _mlme_confirmation.status = LORAMAC_EVENT_INFO_STATUS_CRYPTO_FAIL; + return; + } + + _params.net_id = (uint32_t) _params.rx_buffer[4]; + _params.net_id |= ((uint32_t) _params.rx_buffer[5] << 8); + _params.net_id |= ((uint32_t) _params.rx_buffer[6] << 16); + + _params.dev_addr = (uint32_t) _params.rx_buffer[7]; + _params.dev_addr |= ((uint32_t) _params.rx_buffer[8] << 8); + _params.dev_addr |= ((uint32_t) _params.rx_buffer[9] << 16); + _params.dev_addr |= ((uint32_t) _params.rx_buffer[10] << 24); + + _params.sys_params.rx1_dr_offset = (_params.rx_buffer[11] >> 4) & 0x07; + _params.sys_params.rx2_channel.datarate = _params.rx_buffer[11] & 0x0F; + + _params.sys_params.recv_delay1 = (_params.rx_buffer[12] & 0x0F); + + if (_params.sys_params.recv_delay1 == 0) { + _params.sys_params.recv_delay1 = 1; + } + + _params.sys_params.recv_delay1 *= 1000; + _params.sys_params.recv_delay2 = _params.sys_params.recv_delay1 + 1000; + + // Size of the regular payload is 12. Plus 1 byte MHDR and 4 bytes MIC + _lora_phy.apply_cf_list(&_params.rx_buffer[13], size - 17); + + _mlme_confirmation.status = LORAMAC_EVENT_INFO_STATUS_OK; + _is_nwk_joined = true; + // Node joined successfully + _params.ul_frame_counter = 0; + _params.ul_nb_rep_counter = 0; + + } else { + _mlme_confirmation.status = LORAMAC_EVENT_INFO_STATUS_JOIN_FAIL; + } +} + +void LoRaMac::check_frame_size(uint16_t size) +{ + uint8_t value = _lora_phy.get_max_payload(_mcps_indication.rx_datarate, + _params.is_repeater_supported); + + if (MAX(0, (int16_t) ((int16_t)size - (int16_t)LORA_MAC_FRMPAYLOAD_OVERHEAD)) + > (int32_t) value) { + tr_error("Invalid frame size"); + } +} + +bool LoRaMac::message_integrity_check(const uint8_t * const payload, + const uint16_t size, + uint8_t * const ptr_pos, + uint32_t address, + uint32_t* downlink_counter, + const uint8_t *nwk_skey) +{ uint32_t mic = 0; uint32_t mic_rx = 0; uint16_t sequence_counter = 0; uint16_t sequence_counter_prev = 0; uint16_t sequence_counter_diff = 0; - uint32_t downlink_counter = 0; - multicast_params_t *cur_multicast_params = NULL; + sequence_counter = (uint16_t) payload[(*ptr_pos)++]; + sequence_counter |= (uint16_t) payload[(*ptr_pos)++] << 8; + + mic_rx |= (uint32_t) payload[size - LORAMAC_MFR_LEN]; + mic_rx |= ((uint32_t) payload[size - LORAMAC_MFR_LEN + 1] << 8); + mic_rx |= ((uint32_t) payload[size - LORAMAC_MFR_LEN + 2] << 16); + mic_rx |= ((uint32_t) payload[size - LORAMAC_MFR_LEN + 3] << 24); + + sequence_counter_prev = (uint16_t) *downlink_counter; + sequence_counter_diff = sequence_counter - sequence_counter_prev; + *downlink_counter += sequence_counter_diff; + if (sequence_counter < sequence_counter_prev) { + *downlink_counter += 0x10000; + } + + // sizeof nws_skey must be the same as _params.keys.nwk_skey, + _lora_crypto.compute_mic(payload, size - LORAMAC_MFR_LEN, + nwk_skey, + sizeof(_params.keys.nwk_skey)*8, + address, DOWN_LINK, *downlink_counter, &mic); + + if (mic_rx != mic) { + _mcps_indication.status = LORAMAC_EVENT_INFO_STATUS_MIC_FAIL; + return false; + } + + if (sequence_counter_diff >= _lora_phy.get_maximum_frame_counter_gap()) { + _mcps_indication.status = LORAMAC_EVENT_INFO_STATUS_DOWNLINK_TOO_MANY_FRAMES_LOST; + _mcps_indication.dl_frame_counter = *downlink_counter; + return false; + } + + return true; +} + +void LoRaMac::extract_data_and_mac_commands(const uint8_t *payload, + uint16_t size, + uint8_t fopts_len, + uint8_t *nwk_skey, + uint8_t *app_skey, + uint32_t address, + uint32_t downlink_counter, + int16_t rssi, + int8_t snr) +{ + uint8_t frame_len = 0; + uint8_t payload_start_index = 8 + fopts_len; + uint8_t port = payload[payload_start_index++]; + frame_len = (size - 4) - payload_start_index; + + _mcps_indication.port = port; + + // special handling of control port 0 + if (port == 0) { + if (fopts_len == 0) { + // sizeof nws_skey must be the same as _params.keys.nwk_skey, + if (_lora_crypto.decrypt_payload(payload + payload_start_index, + frame_len, + nwk_skey, + sizeof(_params.keys.nwk_skey)*8, + address, + DOWN_LINK, + downlink_counter, + _params.rx_buffer) != 0) { + _mcps_indication.status = LORAMAC_EVENT_INFO_STATUS_CRYPTO_FAIL; + } + + if (_mac_commands.process_mac_commands(_params.rx_buffer, 0, frame_len, + snr, _mlme_confirmation, + _params.sys_params, _lora_phy) + != LORAWAN_STATUS_OK) { + _mcps_indication.status = LORAMAC_EVENT_INFO_STATUS_ERROR; + return; + } + + if (_mac_commands.has_sticky_mac_cmd()) { + set_mlme_schedule_ul_indication(); + _mac_commands.clear_sticky_mac_cmd(); + } + + return; + } + + _mcps_indication.pending = false; + _mcps_confirmation.ack_received = false; + _mcps_indication.is_ack_recvd = false; + + return; + } + + // normal unicast/multicast port handling + if (fopts_len > 0) { + // Decode Options field MAC commands. Omit the fPort. + if (_mac_commands.process_mac_commands(payload, 8, + payload_start_index - 1, snr, + _mlme_confirmation, + _params.sys_params, _lora_phy) + != LORAWAN_STATUS_OK) { + _mcps_indication.status = LORAMAC_EVENT_INFO_STATUS_ERROR; + return; + } + + if (_mac_commands.has_sticky_mac_cmd()) { + set_mlme_schedule_ul_indication(); + _mac_commands.clear_sticky_mac_cmd(); + } + } + + // sizeof app_skey must be the same as _params.keys.app_skey + if (_lora_crypto.decrypt_payload(payload + payload_start_index, + frame_len, + app_skey, + sizeof(_params.keys.app_skey)*8, + address, + DOWN_LINK, + downlink_counter, + _params.rx_buffer) != 0) { + _mcps_indication.status = LORAMAC_EVENT_INFO_STATUS_CRYPTO_FAIL; + } else { + _mcps_indication.buffer = _params.rx_buffer; + _mcps_indication.buffer_size = frame_len; + _mcps_indication.is_data_recvd = true; + } +} + +void LoRaMac::extract_mac_commands_only(const uint8_t *payload, + int8_t snr, + uint8_t fopts_len) +{ + uint8_t payload_start_index = 8 + fopts_len; + if (fopts_len > 0) { + if (_mac_commands.process_mac_commands(payload, 8, payload_start_index, + snr, _mlme_confirmation, + _params.sys_params, _lora_phy) + != LORAWAN_STATUS_OK) { + _mcps_indication.status = LORAMAC_EVENT_INFO_STATUS_ERROR; + return; + } + + if (_mac_commands.has_sticky_mac_cmd()) { + set_mlme_schedule_ul_indication(); + _mac_commands.clear_sticky_mac_cmd(); + } + } +} + +void LoRaMac::handle_data_frame(const uint8_t* const payload, + const uint16_t size, + uint8_t ptr_pos, + uint8_t msg_type, + int16_t rssi, + int8_t snr) +{ + check_frame_size(size); + + bool is_multicast = false; + loramac_frame_ctrl_t fctrl; + multicast_params_t *cur_multicast_params; + uint32_t address = 0; + uint32_t downlink_counter = 0; + uint8_t app_payload_start_index = 0; uint8_t *nwk_skey = _params.keys.nwk_skey; uint8_t *app_skey = _params.keys.app_skey; - uint8_t multicast = 0; + address = payload[ptr_pos++]; + address |= ((uint32_t) payload[ptr_pos++] << 8); + address |= ((uint32_t) payload[ptr_pos++] << 16); + address |= ((uint32_t) payload[ptr_pos++] << 24); - bool is_mic_ok = false; + if (address != _params.dev_addr) { + // check if Multicast is destined for us + cur_multicast_params = _params.multicast_channels; - _mcps_confirmation.ack_received = false; - _mcps_indication.rssi = rssi; - _mcps_indication.snr = snr; - _mcps_indication.rx_slot = _params.rx_slot; - _mcps_indication.port = 0; - _mcps_indication.multicast = 0; - _mcps_indication.fpending_status = 0; - _mcps_indication.buffer = NULL; - _mcps_indication.buffer_size = 0; - _mcps_indication.is_data_recvd = false; - _mcps_indication.is_ack_recvd = false; - _mcps_indication.dl_frame_counter = 0; - _mcps_indication.type = MCPS_UNCONFIRMED; - _mcps_indication.status = LORAMAC_EVENT_INFO_STATUS_OK; - - if (_device_class != CLASS_C) { - _lora_phy.put_radio_to_sleep(); - } - - _lora_time.stop( _params.timers.rx_window2_timer ); - - mac_hdr.value = payload[pkt_header_len++]; - - switch (mac_hdr.bits.mtype) { - case FRAME_TYPE_JOIN_ACCEPT: - if (_is_nwk_joined) { - _mcps_indication.status = LORAMAC_EVENT_INFO_STATUS_ERROR; - prepare_rx_done_abort(); - return; - } - - if (0 != _lora_crypto.decrypt_join_frame(payload + 1, - size - 1, - _params.keys.app_key, - APPKEY_KEY_LENGTH, - _params.payload + 1)) { - _mcps_indication.status = LORAMAC_EVENT_INFO_STATUS_CRYPTO_FAIL; - return; - } - - _params.payload[0] = mac_hdr.value; - - if (0 != _lora_crypto.compute_join_frame_mic(_params.payload, - size - LORAMAC_MFR_LEN, - _params.keys.app_key, - APPKEY_KEY_LENGTH, - &mic)) { - _mcps_indication.status = LORAMAC_EVENT_INFO_STATUS_CRYPTO_FAIL; - return; - } - - mic_rx |= (uint32_t) _params.payload[size - LORAMAC_MFR_LEN]; - mic_rx |= ((uint32_t) _params.payload[size - LORAMAC_MFR_LEN + 1] << 8); - mic_rx |= ((uint32_t) _params.payload[size - LORAMAC_MFR_LEN + 2] << 16); - mic_rx |= ((uint32_t) _params.payload[size - LORAMAC_MFR_LEN + 3] << 24); - - if (mic_rx == mic) { - - if (0 != _lora_crypto.compute_skeys_for_join_frame(_params.keys.app_key, - APPKEY_KEY_LENGTH, - _params.payload + 1, - _params.dev_nonce, - _params.keys.nwk_skey, - _params.keys.app_skey)) { - _mcps_indication.status = LORAMAC_EVENT_INFO_STATUS_CRYPTO_FAIL; - return; - } - - _params.net_id = (uint32_t) _params.payload[4]; - _params.net_id |= ((uint32_t) _params.payload[5] << 8); - _params.net_id |= ((uint32_t) _params.payload[6] << 16); - - _params.dev_addr = (uint32_t) _params.payload[7]; - _params.dev_addr |= ((uint32_t) _params.payload[8] << 8); - _params.dev_addr |= ((uint32_t) _params.payload[9] << 16); - _params.dev_addr |= ((uint32_t) _params.payload[10] << 24); - - _params.sys_params.rx1_dr_offset = (_params.payload[11] >> 4) & 0x07; - _params.sys_params.rx2_channel.datarate = _params.payload[11] & 0x0F; - - _params.sys_params.recv_delay1 = (_params.payload[12] & 0x0F); - - if (_params.sys_params.recv_delay1 == 0) { - _params.sys_params.recv_delay1 = 1; - } - - _params.sys_params.recv_delay1 *= 1000; - _params.sys_params.recv_delay2 = _params.sys_params.recv_delay1 + 1000; - - // Size of the regular payload is 12. Plus 1 byte MHDR and 4 bytes MIC - _lora_phy.apply_cf_list(&_params.payload[13], size - 17); - - _mlme_confirmation.status = LORAMAC_EVENT_INFO_STATUS_OK; - _is_nwk_joined = true; - } else { - _mlme_confirmation.status = LORAMAC_EVENT_INFO_STATUS_JOIN_FAIL; - } - - break; - - case FRAME_TYPE_DATA_CONFIRMED_DOWN: - case FRAME_TYPE_DATA_UNCONFIRMED_DOWN: - { - uint8_t value = _lora_phy.get_max_payload(_mcps_indication.rx_datarate, _params.is_repeater_supported); - - if (MAX(0, (int16_t) ((int16_t)size - (int16_t)LORA_MAC_FRMPAYLOAD_OVERHEAD )) > (int32_t)value) { - _mcps_indication.status = LORAMAC_EVENT_INFO_STATUS_ERROR; - prepare_rx_done_abort(); - return; - } - - address = payload[pkt_header_len++]; - address |= ((uint32_t)payload[pkt_header_len++] << 8); - address |= ((uint32_t)payload[pkt_header_len++] << 16); - address |= ((uint32_t)payload[pkt_header_len++] << 24); - - if (address != _params.dev_addr) { - - cur_multicast_params = _params.multicast_channels; - - while (cur_multicast_params != NULL) { - if (address == cur_multicast_params->address) { - multicast = 1; - nwk_skey = cur_multicast_params->nwk_skey; - app_skey = cur_multicast_params->app_skey; - downlink_counter = cur_multicast_params->dl_frame_counter; - break; - } - - cur_multicast_params = cur_multicast_params->next; - } - - if (multicast == 0) { - // We are not the destination of this frame. - _mcps_indication.status = LORAMAC_EVENT_INFO_STATUS_ADDRESS_FAIL; - prepare_rx_done_abort(); - return; - } - } else { - multicast = 0; - nwk_skey = _params.keys.nwk_skey; - app_skey = _params.keys.app_skey; - downlink_counter = _params.dl_frame_counter; - } - - fctrl.value = payload[pkt_header_len++]; - - sequence_counter = (uint16_t )payload[pkt_header_len++]; - sequence_counter |= (uint16_t)payload[pkt_header_len++] << 8; - - app_payload_start_index = 8 + fctrl.bits.fopts_len; - - mic_rx |= (uint32_t)payload[size - LORAMAC_MFR_LEN]; - mic_rx |= ((uint32_t)payload[size - LORAMAC_MFR_LEN + 1] << 8); - mic_rx |= ((uint32_t)payload[size - LORAMAC_MFR_LEN + 2] << 16); - mic_rx |= ((uint32_t)payload[size - LORAMAC_MFR_LEN + 3] << 24); - - sequence_counter_prev = (uint16_t)downlink_counter; - sequence_counter_diff = (sequence_counter - sequence_counter_prev); - - if (sequence_counter_diff < (1 << 15)) { - downlink_counter += sequence_counter_diff; - _lora_crypto.compute_mic(payload, size - LORAMAC_MFR_LEN, - nwk_skey, sizeof(_params.keys.nwk_skey)*8, // sizeof nws_skey must be the same as _params.keys.nwk_skey - address, DOWN_LINK, downlink_counter, &mic); - if (mic_rx == mic) { - is_mic_ok = true; - } - } else { - uint32_t downlink_counter_tmp = downlink_counter + 0x10000 + (int16_t)sequence_counter_diff; - _lora_crypto.compute_mic(payload, size - LORAMAC_MFR_LEN, - nwk_skey, sizeof(_params.keys.nwk_skey)*8, // sizeof nws_skey must be the same as _params.keys.nwk_skey - address, DOWN_LINK, downlink_counter_tmp, &mic); - - if (mic_rx == mic ) { - is_mic_ok = true; - downlink_counter = downlink_counter_tmp; - } - } - - if (sequence_counter_diff >= _lora_phy.get_maximum_frame_counter_gap()) { - _mcps_indication.status = LORAMAC_EVENT_INFO_STATUS_DOWNLINK_TOO_MANY_FRAMES_LOSS; - _mcps_indication.dl_frame_counter = downlink_counter; - prepare_rx_done_abort( ); - return; - } - - if (is_mic_ok == true) { - _mcps_indication.status = LORAMAC_EVENT_INFO_STATUS_OK; - _mcps_indication.multicast = multicast; - _mcps_indication.fpending_status = fctrl.bits.fpending; - _mcps_indication.buffer = NULL; - _mcps_indication.buffer_size = 0; - _mcps_indication.dl_frame_counter = downlink_counter; - - _mcps_confirmation.status = LORAMAC_EVENT_INFO_STATUS_OK; - - _params.adr_ack_counter = 0; - mac_commands.clear_repeat_buffer(); - - if (multicast == 1) { - _mcps_indication.type = MCPS_MULTICAST; - - if ((cur_multicast_params->dl_frame_counter == downlink_counter) && - (cur_multicast_params->dl_frame_counter != 0)) { - - _mcps_indication.status = LORAMAC_EVENT_INFO_STATUS_DOWNLINK_REPEATED; - _mcps_indication.dl_frame_counter = downlink_counter; - prepare_rx_done_abort(); - - return; - } - - cur_multicast_params->dl_frame_counter = downlink_counter; - - } else { - - if (mac_hdr.bits.mtype == FRAME_TYPE_DATA_CONFIRMED_DOWN) { - _params.is_srv_ack_requested = true; - _mcps_indication.type = MCPS_CONFIRMED; - - if ((_params.dl_frame_counter == downlink_counter ) && - (_params.dl_frame_counter != 0)) { - // Duplicated confirmed downlink. Skip indication. - // In this case, the MAC layer shall accept the MAC commands - // which are included in the downlink retransmission. - // It should not provide the same frame to the application - // layer again. The MAC layer accepts the acknowledgement. - _params.flags.bits.mcps_ind_skip = 1; - } - } else { - _params.is_srv_ack_requested = false; - _mcps_indication.type = MCPS_UNCONFIRMED; - - if ((_params.dl_frame_counter == downlink_counter) && - (_params.dl_frame_counter != 0)) { - _mcps_indication.status = LORAMAC_EVENT_INFO_STATUS_DOWNLINK_REPEATED; - _mcps_indication.dl_frame_counter = downlink_counter; - prepare_rx_done_abort(); - return; - } - } - _params.dl_frame_counter = downlink_counter; - } - - // This must be done before parsing the payload and the MAC commands. - // We need to reset the MacCommandsBufferIndex here, since we need - // to take retransmissions and repetitions into account. Error cases - // will be handled in function OnMacStateCheckTimerEvent. - if (_mcps_confirmation.req_type == MCPS_CONFIRMED) { - if (fctrl.bits.ack == 1) { - mac_commands.clear_command_buffer(); - _mcps_confirmation.ack_received = fctrl.bits.ack; - _mcps_indication.is_ack_recvd = fctrl.bits.ack; - } - } else { - mac_commands.clear_command_buffer(); - } - - if (((size - 4) - app_payload_start_index) > 0) { - uint8_t port = payload[app_payload_start_index++]; - frame_len = (size - 4) - app_payload_start_index; - - _mcps_indication.port = port; - - if (port == 0) { - if (fctrl.bits.fopts_len == 0) { - if (0 != _lora_crypto.decrypt_payload(payload + app_payload_start_index, - frame_len, - nwk_skey, - sizeof(_params.keys.nwk_skey)*8, // sizeof nws_skey must be the same as _params.keys.nwk_skey - address, - DOWN_LINK, - downlink_counter, - _params.payload)) { - _mcps_indication.status = LORAMAC_EVENT_INFO_STATUS_CRYPTO_FAIL; - } - - if (LORAWAN_STATUS_OK != mac_commands.process_mac_commands( - _params.payload, 0, frame_len, snr, - _mlme_confirmation, _params.sys_params, _lora_phy)) { - _mcps_indication.status = LORAMAC_EVENT_INFO_STATUS_ERROR; - } else if (mac_commands.has_sticky_mac_cmd()) { - set_mlme_schedule_ul_indication(); - mac_commands.clear_sticky_mac_cmd(); - } - } else { - _params.flags.bits.mcps_ind_skip = 1; - _mcps_confirmation.ack_received = false; - _mcps_indication.is_ack_recvd = false; - } - } else { - if (fctrl.bits.fopts_len > 0) { - // Decode Options field MAC commands. Omit the fPort. - if (LORAWAN_STATUS_OK != mac_commands.process_mac_commands( - payload, 8, app_payload_start_index - 1, snr, - _mlme_confirmation, _params.sys_params, _lora_phy )) { - _mcps_indication.status = LORAMAC_EVENT_INFO_STATUS_ERROR; - } else if (mac_commands.has_sticky_mac_cmd()) { - set_mlme_schedule_ul_indication(); - mac_commands.clear_sticky_mac_cmd(); - } - } - - if (0 != _lora_crypto.decrypt_payload(payload + app_payload_start_index, - frame_len, - app_skey, - sizeof(_params.keys.app_skey)*8, // sizeof app_skey must be the same as _params.keys.app_skey - address, - DOWN_LINK, - downlink_counter, - _params.payload)) { - _mcps_indication.status = LORAMAC_EVENT_INFO_STATUS_CRYPTO_FAIL; - } - - _mcps_indication.buffer = _params.payload; - _mcps_indication.buffer_size = frame_len; - _mcps_indication.is_data_recvd = true; - } - } else { - if (fctrl.bits.fopts_len > 0) { - if (LORAWAN_STATUS_OK != mac_commands.process_mac_commands( - payload, 8, app_payload_start_index, snr, - _mlme_confirmation, - _params.sys_params, _lora_phy)) { - _mcps_indication.status = LORAMAC_EVENT_INFO_STATUS_ERROR; - } else if (mac_commands.has_sticky_mac_cmd()) { - set_mlme_schedule_ul_indication(); - mac_commands.clear_sticky_mac_cmd(); - } - } - } - - // Provide always an indication, skip the callback to the user application, - // in case of a confirmed downlink retransmission. - _params.flags.bits.mcps_ind = 1; - } else { - _mcps_indication.status = LORAMAC_EVENT_INFO_STATUS_MIC_FAIL; - - prepare_rx_done_abort( ); - return; - } - } - break; - - case FRAME_TYPE_PROPRIETARY: - { - memcpy(_params.payload, &payload[pkt_header_len], size); - - _mcps_indication.type = MCPS_PROPRIETARY; - _mcps_indication.status = LORAMAC_EVENT_INFO_STATUS_OK; - _mcps_indication.buffer = _params.payload; - _mcps_indication.buffer_size = size - pkt_header_len; - - _params.flags.bits.mcps_ind = 1; + while (cur_multicast_params != NULL) { + if (address == cur_multicast_params->address) { + is_multicast = true; + nwk_skey = cur_multicast_params->nwk_skey; + app_skey = cur_multicast_params->app_skey; + downlink_counter = cur_multicast_params->dl_frame_counter; break; } - default: - _mcps_indication.status = LORAMAC_EVENT_INFO_STATUS_ERROR; - prepare_rx_done_abort(); - break; + + cur_multicast_params = cur_multicast_params->next; + } + + if (!is_multicast) { + // We are not the destination of this frame. + _mcps_indication.status = LORAMAC_EVENT_INFO_STATUS_ADDRESS_FAIL; + abort_rx(); + return; + } + } else { + is_multicast = false; + nwk_skey = _params.keys.nwk_skey; + app_skey = _params.keys.app_skey; + downlink_counter = _params.dl_frame_counter; + } + + fctrl.value = payload[ptr_pos++]; + app_payload_start_index = 8 + fctrl.bits.fopts_len; + + tr_debug("RX at port %d", payload[app_payload_start_index]); + + //perform MIC check + if (!message_integrity_check(payload, size, &ptr_pos, address, + &downlink_counter, nwk_skey)) { + tr_error("MIC failed"); + abort_rx(); + return; + } + + _mcps_confirmation.ack_received = false; + _mcps_indication.is_ack_recvd = false; + _mcps_indication.pending = true; + _mcps_indication.is_data_recvd = false; + _mcps_indication.status = LORAMAC_EVENT_INFO_STATUS_OK; + _mcps_indication.multicast = is_multicast; + _mcps_indication.fpending_status = fctrl.bits.fpending; + _mcps_indication.buffer = NULL; + _mcps_indication.buffer_size = 0; + _mcps_indication.dl_frame_counter = downlink_counter; + _mcps_indication.rssi = rssi; + _mcps_indication.snr = snr; + + _mcps_confirmation.status = LORAMAC_EVENT_INFO_STATUS_OK; + + _params.adr_ack_counter = 0; + _mac_commands.clear_repeat_buffer(); + + if (is_multicast) { + _mcps_indication.type = MCPS_MULTICAST; + + // Discard if its a repeated message + if ((cur_multicast_params->dl_frame_counter == downlink_counter) + && (cur_multicast_params->dl_frame_counter != 0)) { + _mcps_indication.status = LORAMAC_EVENT_INFO_STATUS_DOWNLINK_REPEATED; + _mcps_indication.dl_frame_counter = downlink_counter; + _mcps_indication.pending = false; + abort_rx(); + + return; + } + + cur_multicast_params->dl_frame_counter = downlink_counter; + + } else { + if (msg_type == FRAME_TYPE_DATA_CONFIRMED_DOWN) { + _params.is_srv_ack_requested = true; + _mcps_indication.type = MCPS_CONFIRMED; + + if ((_params.dl_frame_counter == downlink_counter) + && (_params.dl_frame_counter != 0)) { + // Duplicated confirmed downlink. Skip indication. + // In this case, the MAC layer shall accept the MAC commands + // which are included in the downlink retransmission. + // It should not provide the same frame to the application + // layer again. The MAC layer accepts the acknowledgement. + tr_debug("Discarding duplicate frame"); + _mcps_indication.pending = false; + _mcps_indication.status = LORAMAC_EVENT_INFO_STATUS_DOWNLINK_REPEATED; + } + } else if (msg_type == FRAME_TYPE_DATA_UNCONFIRMED_DOWN) { + _params.is_srv_ack_requested = false; + _mcps_indication.type = MCPS_UNCONFIRMED; + + if ((_params.dl_frame_counter == downlink_counter) + && (_params.dl_frame_counter != 0)) { + tr_debug("Discarding duplicate frame"); + _mcps_indication.pending = false; + _mcps_indication.status = LORAMAC_EVENT_INFO_STATUS_DOWNLINK_REPEATED; + abort_rx(); + return; + } + } + _params.dl_frame_counter = downlink_counter; + } + + // This must be done before parsing the payload and the MAC commands. + // We need to reset the MacCommandsBufferIndex here, since we need + // to take retransmissions and repetitions into account. Error cases + // will be handled in function OnMacStateCheckTimerEvent. + if (_params.is_node_ack_requested) { + if (fctrl.bits.ack == 1) { + _mac_commands.clear_command_buffer(); + _mcps_confirmation.ack_received = fctrl.bits.ack; + _mcps_indication.is_ack_recvd = fctrl.bits.ack; + } + } else { + _mac_commands.clear_command_buffer(); + } + + uint8_t frame_len = (size - 4) - app_payload_start_index; + + if (frame_len > 0) { + extract_data_and_mac_commands(payload, size, fctrl.bits.fopts_len, + nwk_skey, app_skey, address, + downlink_counter, rssi, snr); + } else { + extract_mac_commands_only(payload, snr, fctrl.bits.fopts_len); + } + + // Handle proprietary messages. + if (msg_type == FRAME_TYPE_PROPRIETARY) { + memcpy(_params.rx_buffer, &payload[ptr_pos], size); + + _mcps_indication.type = MCPS_PROPRIETARY; + _mcps_indication.status = LORAMAC_EVENT_INFO_STATUS_OK; + _mcps_indication.buffer = _params.rx_buffer; + _mcps_indication.buffer_size = size - ptr_pos; } check_to_disable_ack_timeout(_params.is_node_ack_requested, _device_class, _mcps_confirmation.ack_received, _params.ack_timeout_retry_counter, _params.max_ack_timeout_retries ); +} - if(_params.timers.ack_timeout_timer.timer_id == 0) { - _params.flags.bits.mac_done = 1; +void LoRaMac::on_radio_rx_done(const uint8_t* const payload, uint16_t size, + int16_t rssi, int8_t snr) +{ + // on reception turn off queued timers + _lora_time.stop(_params.timers.rx_window1_timer); + _lora_time.stop(_params.timers.rx_window2_timer); - _lora_time.start(_params.timers.mac_state_check_timer, 1); + if (_device_class == CLASS_C) { + open_rx2_window(); + } else { + _lora_phy.put_radio_to_sleep(); + } + + loramac_mhdr_t mac_hdr; + uint8_t pos = 0; + mac_hdr.value = payload[pos++]; + + switch (mac_hdr.bits.mtype) { + + case FRAME_TYPE_JOIN_ACCEPT: + + if (nwk_joined()) { + abort_rx(); + _mlme_confirmation.pending = false; + return; + } else { + handle_join_accept_frame(payload, size); + _mlme_confirmation.pending = true; + } + + break; + + case FRAME_TYPE_DATA_UNCONFIRMED_DOWN: + case FRAME_TYPE_DATA_CONFIRMED_DOWN: + case FRAME_TYPE_PROPRIETARY: + + handle_data_frame(payload, size, pos, mac_hdr.bits.mtype, rssi, snr); + + break; + + default: + break; } } void LoRaMac::on_radio_tx_timeout( void ) { - if (_device_class != CLASS_C) { - _lora_phy.put_radio_to_sleep(); + _lora_time.stop(_params.timers.rx_window1_timer); + _lora_time.stop(_params.timers.rx_window2_timer); + _lora_time.stop(_params.timers.ack_timeout_timer); + + if (_device_class == CLASS_C) { + open_rx2_window(); } else { - open_continuous_rx2_window(); + _lora_phy.put_radio_to_sleep(); } _mcps_confirmation.status = LORAMAC_EVENT_INFO_STATUS_TX_TIMEOUT; _mlme_confirmation.status = LORAMAC_EVENT_INFO_STATUS_TX_TIMEOUT; - _params.flags.bits.mac_done = 1; + _mac_commands.clear_command_buffer(); + + _mcps_confirmation.nb_retries = _params.ack_timeout_retry_counter; + _mcps_confirmation.ack_received = false; + _mcps_confirmation.tx_toa = 0; + + post_process_mcps_req(); } -void LoRaMac::on_radio_rx_error( void ) +rx_slot_t LoRaMac::on_radio_rx_timeout(bool is_timeout) { if (_device_class != CLASS_C) { _lora_phy.put_radio_to_sleep(); @@ -681,295 +738,119 @@ void LoRaMac::on_radio_rx_error( void ) if (_params.rx_slot == RX_SLOT_WIN_1) { if (_params.is_node_ack_requested == true) { - _mcps_confirmation.status = LORAMAC_EVENT_INFO_STATUS_RX1_ERROR; + _mcps_confirmation.status = is_timeout ? LORAMAC_EVENT_INFO_STATUS_RX1_TIMEOUT + : LORAMAC_EVENT_INFO_STATUS_RX1_ERROR; } - - _mlme_confirmation.status = LORAMAC_EVENT_INFO_STATUS_RX1_ERROR; - - if (_device_class != CLASS_C) { - if (_lora_time.get_elapsed_time(_params.timers.aggregated_last_tx_time) >= _params.rx_window2_delay) { - _lora_time.stop(_params.timers.rx_window2_timer); - _params.flags.bits.mac_done = 1; - } - } - } else { - if (_params.is_node_ack_requested == true) { - _mcps_confirmation.status = LORAMAC_EVENT_INFO_STATUS_RX2_ERROR; - } - - _mlme_confirmation.status = LORAMAC_EVENT_INFO_STATUS_RX2_ERROR; - - if (_device_class != CLASS_C) { - _params.flags.bits.mac_done = 1; - } - } - - if (_device_class == CLASS_C) { - open_continuous_rx2_window(); - } -} - -void LoRaMac::on_radio_rx_timeout(void) -{ - if (_device_class != CLASS_C) { - _lora_phy.put_radio_to_sleep(); - } - - if (_params.rx_slot == RX_SLOT_WIN_1) { - if (_params.is_node_ack_requested == true) { - _mcps_confirmation.status = LORAMAC_EVENT_INFO_STATUS_RX1_TIMEOUT; - } - _mlme_confirmation.status = LORAMAC_EVENT_INFO_STATUS_RX1_TIMEOUT; + _mlme_confirmation.status = is_timeout ? LORAMAC_EVENT_INFO_STATUS_RX1_TIMEOUT + : LORAMAC_EVENT_INFO_STATUS_RX1_ERROR; if (_device_class != CLASS_C) { if (_lora_time.get_elapsed_time(_params.timers.aggregated_last_tx_time ) >= _params.rx_window2_delay) { _lora_time.stop(_params.timers.rx_window2_timer); - _params.flags.bits.mac_done = 1; } } } else { if (_params.is_node_ack_requested == true) { - _mcps_confirmation.status = LORAMAC_EVENT_INFO_STATUS_RX2_TIMEOUT; + _mcps_confirmation.status = is_timeout ? LORAMAC_EVENT_INFO_STATUS_RX2_TIMEOUT + : LORAMAC_EVENT_INFO_STATUS_RX2_ERROR; } - _mlme_confirmation.status = LORAMAC_EVENT_INFO_STATUS_RX2_TIMEOUT; - - if (_device_class != CLASS_C) { - _params.flags.bits.mac_done = 1; - } + _mlme_confirmation.status = is_timeout ? LORAMAC_EVENT_INFO_STATUS_RX2_TIMEOUT + : LORAMAC_EVENT_INFO_STATUS_RX2_ERROR; } if (_device_class == CLASS_C) { - open_continuous_rx2_window(); + open_rx2_window(); } + + return _params.rx_slot; } -/*************************************************************************** - * Timer event callbacks - deliberated locally * - **************************************************************************/ -void LoRaMac::on_mac_state_check_timer_event(void) +bool LoRaMac::continue_joining_process() { - bool tx_timeout = false; - - _lora_time.stop(_params.timers.mac_state_check_timer); - - if (_params.flags.bits.mac_done == 1) { - - if ((_params.mac_state & LORAMAC_RX_ABORT) == LORAMAC_RX_ABORT) { - _params.mac_state &= ~LORAMAC_RX_ABORT; - _params.mac_state &= ~LORAMAC_TX_RUNNING; - } - - if ((_params.flags.bits.mlme_req == 1) || (_params.flags.bits.mcps_req == 1)) { - - if ((_mcps_confirmation.status == LORAMAC_EVENT_INFO_STATUS_TX_TIMEOUT) || - (_mlme_confirmation.status == LORAMAC_EVENT_INFO_STATUS_TX_TIMEOUT)) { - _params.mac_state &= ~LORAMAC_TX_RUNNING; - mac_commands.clear_command_buffer(); - _mcps_confirmation.nb_retries = _params.ack_timeout_retry_counter; - _mcps_confirmation.ack_received = false; - _mcps_confirmation.tx_toa = 0; - tx_timeout = true; - } - } - - if ((_params.is_node_ack_requested == false) && (tx_timeout == false)) { - if ((_params.flags.bits.mlme_req == 1) || ((_params.flags.bits.mcps_req == 1))) { - if ((_params.flags.bits.mlme_req == 1) && (_mlme_confirmation.req_type == MLME_JOIN)) { - _mlme_confirmation.nb_retries = _params.join_request_trial_counter; - - if (_mlme_confirmation.status == LORAMAC_EVENT_INFO_STATUS_OK) { - // Node joined successfully - _params.ul_frame_counter = 0; - _params.ul_nb_rep_counter = 0; - _params.mac_state &= ~LORAMAC_TX_RUNNING; - } else { - if (_params.join_request_trial_counter >= _params.max_join_request_trials) { - _params.mac_state &= ~LORAMAC_TX_RUNNING; - } else { - _params.flags.bits.mac_done = 0; - // Schedule a retry - const int ret = ev_queue->call(this, &LoRaMac::on_tx_delayed_timer_event); - MBED_ASSERT(ret != 0); - (void)ret; - - } - } - } else { - if ((_params.ul_nb_rep_counter >= _params.sys_params.retry_num) || - (_params.flags.bits.mcps_ind == 1)) { - if (_params.flags.bits.mcps_ind == 0) { - // Maximum repetitions without downlink. - // Only process the case when the MAC did not receive a downlink. - mac_commands.clear_command_buffer(); - _params.adr_ack_counter++; - } - - _params.ul_nb_rep_counter = 0; - - if (_params.is_ul_frame_counter_fixed == false) { - _params.ul_frame_counter++; - } - - _params.mac_state &= ~LORAMAC_TX_RUNNING; - } else { - _params.flags.bits.mac_done = 0; - // Schedule a retry - const int ret = ev_queue->call(this, &LoRaMac::on_tx_delayed_timer_event); - MBED_ASSERT(ret != 0); - (void)ret; - } - } - } - } - - if (_params.flags.bits.mcps_ind == 1) { - if ((_mcps_confirmation.ack_received == true) || - (_params.ack_timeout_retry_counter > _params.max_ack_timeout_retries)) { - _params.is_ack_retry_timeout_expired = false; - _params.is_node_ack_requested = false; - if (_params.is_ul_frame_counter_fixed == false) { - _params.ul_frame_counter++; - } - _mcps_confirmation.nb_retries = _params.ack_timeout_retry_counter; - - _params.mac_state &= ~LORAMAC_TX_RUNNING; - } - } - - if ((_params.is_ack_retry_timeout_expired == true) && - ((_params.mac_state & LORAMAC_TX_DELAYED) == 0)) { - - // Retransmissions procedure for confirmed uplinks - _params.is_ack_retry_timeout_expired = false; - if ((_params.ack_timeout_retry_counter < _params.max_ack_timeout_retries) && - (_params.ack_timeout_retry_counter <= MAX_ACK_RETRIES)) { - - _params.ack_timeout_retry_counter++; - - if ((_params.ack_timeout_retry_counter % 2) == 1) { - _params.sys_params.channel_data_rate = _lora_phy.get_next_lower_tx_datarate( - _params.sys_params.channel_data_rate); - } - - // Try to send the frame again - if (schedule_tx() == LORAWAN_STATUS_OK) { - _params.flags.bits.mac_done = 0; - } else { - // The DR is not applicable for the payload size - _mcps_confirmation.status = LORAMAC_EVENT_INFO_STATUS_TX_DR_PAYLOAD_SIZE_ERROR; - - mac_commands.clear_command_buffer(); - _params.mac_state &= ~LORAMAC_TX_RUNNING; - _params.is_node_ack_requested = false; - _mcps_confirmation.ack_received = false; - _mcps_confirmation.nb_retries = _params.ack_timeout_retry_counter; - _mcps_confirmation.data_rate = _params.sys_params.channel_data_rate; - - if (_params.is_ul_frame_counter_fixed == false) { - _params.ul_frame_counter++; - } - } - } else { - _lora_phy.restore_default_channels(); - - _params.mac_state &= ~LORAMAC_TX_RUNNING; - - mac_commands.clear_command_buffer(); - _params.is_node_ack_requested = false; - _mcps_confirmation.ack_received = false; - _mcps_confirmation.nb_retries = _params.ack_timeout_retry_counter; - - if (_params.is_ul_frame_counter_fixed == false) { - _params.ul_frame_counter++; - } - } - } + if (_params.join_request_trial_counter >= _params.max_join_request_trials) { + return false; } - // Handle reception for Class B and Class C - if ((_params.mac_state & LORAMAC_RX) == LORAMAC_RX) { - _params.mac_state &= ~LORAMAC_RX; + // Schedule a retry + if (handle_retransmission() != LORAWAN_STATUS_OK) { + return false; } - if (_params.mac_state == LORAMAC_IDLE) { - if (_params.flags.bits.mcps_req == 1) { - _params.flags.bits.mcps_req = 0; - mac_primitives->mcps_confirm(&_mcps_confirmation); - } - - if (_params.flags.bits.mlme_req == 1) { - _params.flags.bits.mlme_req = 0; - mac_primitives->mlme_confirm(&_mlme_confirmation); - } - - if (mac_commands.is_sticky_mac_command_pending() == true) { - set_mlme_schedule_ul_indication(); - } - - _params.flags.bits.mac_done = 0; - } else { - _lora_time.start(_params.timers.mac_state_check_timer, MAC_STATE_CHECK_TIMEOUT); - } - - if (_params.flags.bits.mcps_ind == 1) { - _params.flags.bits.mcps_ind = 0; - - if (_device_class == CLASS_C) { - open_continuous_rx2_window(); - } - - if (_params.flags.bits.mcps_ind_skip == 0) { - mac_primitives->mcps_indication(&_mcps_indication); - } - - _params.flags.bits.mcps_ind_skip = 0; - } - - if (_params.flags.bits.mlme_ind == 1) { - _params.flags.bits.mlme_ind = 0; - mac_primitives->mlme_indication(&_mlme_indication); - } + return true; } -void LoRaMac::on_tx_delayed_timer_event(void) +bool LoRaMac::continue_sending_process() { + if (_params.ack_timeout_retry_counter >= _params.max_ack_timeout_retries) { + _mac_commands.clear_command_buffer(); + _params.adr_ack_counter++; + return false; + } + + // retransmission will be handled in on_ack_timeout() whence the ACK timeout + // gets fired + return true; +} + +lorawan_status_t LoRaMac::send_join_request() +{ + lorawan_status_t status = LORAWAN_STATUS_OK; loramac_mhdr_t mac_hdr; loramac_frame_ctrl_t fctrl; - lorawan_status_t status = LORAWAN_STATUS_OK; + _params.sys_params.channel_data_rate = _lora_phy.get_alternate_DR( + _params.join_request_trial_counter + 1); - _lora_time.stop(_params.timers.tx_delayed_timer); - _params.mac_state &= ~LORAMAC_TX_DELAYED; + mac_hdr.value = 0; + mac_hdr.bits.mtype = FRAME_TYPE_JOIN_REQ; - if ((_params.flags.bits.mlme_req == 1 ) && - (_mlme_confirmation.req_type == MLME_JOIN)) { + fctrl.value = 0; + fctrl.bits.adr = _params.sys_params.adr_on; + _params.is_last_tx_join_request = true; - reset_mac_parameters(); - - _params.sys_params.channel_data_rate = _lora_phy.get_alternate_DR(_params.join_request_trial_counter + 1); - - mac_hdr.value = 0; - mac_hdr.bits.mtype = FRAME_TYPE_JOIN_REQ; - - fctrl.value = 0; - fctrl.bits.adr = _params.sys_params.adr_on; - - /* In case of join request retransmissions, the stack must prepare - * the frame again, because the network server keeps track of the random - * LoRaMacDevNonce values to prevent reply attacks. */ - status = prepare_frame(&mac_hdr, &fctrl, 0, NULL, 0); - } + /* In case of join request retransmissions, the stack must prepare + * the frame again, because the network server keeps track of the random + * LoRaMacDevNonce values to prevent reply attacks. */ + status = prepare_frame(&mac_hdr, &fctrl, 0, NULL, 0); if (status == LORAWAN_STATUS_OK) { - schedule_tx(); + status = schedule_tx(); } else { - tr_error("Delayed TX: PrepareFrame returned error %d", status); + tr_error("Retransmission: error %d", status); } + + return status; } -void LoRaMac::on_rx_window1_timer_event(void) +/** + * This function handles retransmission of failed or unacknowledged + * outgoing traffic + */ +lorawan_status_t LoRaMac::handle_retransmission() { + if (!nwk_joined() && (_mlme_confirmation.req_type == MLME_JOIN)) { + return send_join_request(); + } + + return schedule_tx(); +} + +/** + * This function is called when the backoff_timer gets fired. + * It is used for re-scheduling an unsent packet in the pipe. This packet + * can be a Join Request or any other data packet. + */ +void LoRaMac::on_backoff_timer_expiry(void) +{ + Lock lock(*this); + lorawan_status_t status = schedule_tx(); + MBED_ASSERT(status==LORAWAN_STATUS_OK); +} + +void LoRaMac::open_rx1_window(void) +{ + Lock lock(*this); _lora_time.stop(_params.timers.rx_window1_timer); _params.rx_slot = RX_SLOT_WIN_1; @@ -984,15 +865,16 @@ void LoRaMac::on_rx_window1_timer_event(void) _lora_phy.put_radio_to_standby(); } - _lora_phy.rx_config(&_params.rx_window1_config, - (int8_t*) &_mcps_indication.rx_datarate); + _mcps_indication.rx_datarate = _params.rx_window1_config.datarate; + _lora_phy.rx_config(&_params.rx_window1_config); _lora_phy.setup_rx_window(_params.rx_window1_config.is_rx_continuous, _params.sys_params.max_rx_win_time); } -void LoRaMac::on_rx_window2_timer_event(void) +void LoRaMac::open_rx2_window() { + Lock lock(*this); _lora_time.stop(_params.timers.rx_window2_timer); _params.rx_window2_config.channel = _params.channel; @@ -1001,19 +883,17 @@ void LoRaMac::on_rx_window2_timer_event(void) _params.rx_window2_config.is_repeater_supported = _params.is_repeater_supported; _params.rx_window2_config.rx_slot = RX_SLOT_WIN_2; - _params.rx_window2_config.is_rx_continuous = true; + _params.rx_window2_config.is_rx_continuous = get_device_class()==CLASS_C ? true : false; - if (_device_class != CLASS_C) { - _params.rx_window2_config.is_rx_continuous = false; - } + _mcps_indication.rx_datarate = _params.rx_window2_config.datarate; - if (_lora_phy.rx_config(&_params.rx_window2_config, - (int8_t*) &_mcps_indication.rx_datarate) == true) { + if (_lora_phy.rx_config(&_params.rx_window2_config)) { _lora_phy.setup_rx_window(_params.rx_window2_config.is_rx_continuous, _params.sys_params.max_rx_win_time); - _params.rx_slot = RX_SLOT_WIN_2; + _params.rx_slot = _params.rx_window2_config.is_rx_continuous ? + RX_SLOT_WIN_CLASS_C : RX_SLOT_WIN_2; } } @@ -1048,18 +928,35 @@ void LoRaMac::check_to_disable_ack_timeout(bool node_ack_requested, void LoRaMac::on_ack_timeout_timer_event(void) { - _lora_time.stop(_params.timers.ack_timeout_timer); + Lock lock(*this); + _params.ack_timeout_retry_counter++; - if (_params.is_node_ack_requested == true) { - _params.is_ack_retry_timeout_expired = true; - _params.mac_state &= ~LORAMAC_ACK_REQ; + // reduce data rate + if ((_params.ack_timeout_retry_counter % 2)) { + _params.sys_params.channel_data_rate = _lora_phy.get_next_lower_tx_datarate( + _params.sys_params.channel_data_rate); } - if (_device_class == CLASS_C) { - _params.flags.bits.mac_done = 1; + + // Schedule a retry + if (handle_retransmission() != LORAWAN_STATUS_OK) { + // In a case when enabled channels are not found, PHY layer + // resorts to default channels. Next attempt should go forward as the + // default channels are always available if there is a base station in the + // vicinity. Otherwise something is wrong with the stack, we should assert + // here + _mac_commands.clear_command_buffer(); + _params.is_node_ack_requested = false; + _mcps_confirmation.ack_received = false; + _mcps_confirmation.nb_retries = _params.ack_timeout_retry_counter; + + // now that is a critical failure + lorawan_status_t status = handle_retransmission(); + MBED_ASSERT(status==LORAWAN_STATUS_OK); } } -bool LoRaMac::validate_payload_length(uint8_t length, int8_t datarate, +bool LoRaMac::validate_payload_length(uint16_t length, + int8_t datarate, uint8_t fopts_len) { uint16_t max_value = 0; @@ -1081,13 +978,13 @@ bool LoRaMac::validate_payload_length(uint8_t length, int8_t datarate, void LoRaMac::set_mlme_schedule_ul_indication(void) { _mlme_indication.indication_type = MLME_SCHEDULE_UPLINK; - _params.flags.bits.mlme_ind = 1; + _mlme_indication.pending= true; } // This is not actual transmission. It just schedules a message in response // to MCPS request -lorawan_status_t LoRaMac::send(loramac_mhdr_t *machdr, uint8_t fport, - void *fbuffer, uint16_t fbuffer_size) +lorawan_status_t LoRaMac::send(loramac_mhdr_t *machdr, const uint8_t fport, + const void *fbuffer, uint16_t fbuffer_size) { loramac_frame_ctrl_t fctrl; @@ -1115,10 +1012,10 @@ lorawan_status_t LoRaMac::send(loramac_mhdr_t *machdr, uint8_t fport, return status; } -lorawan_status_t LoRaMac::schedule_tx(void) +lorawan_status_t LoRaMac::schedule_tx() { - lorawan_time_t dutyCycleTimeOff = 0; - channel_selection_params_t nextChan; + channel_selection_params_t next_channel; + lorawan_time_t backoff_time = 0; if (_params.sys_params.max_duty_cycle == 255) { return LORAWAN_STATUS_DEVICE_OFF; @@ -1130,32 +1027,33 @@ lorawan_status_t LoRaMac::schedule_tx(void) calculate_backOff(_params.last_channel_idx); - nextChan.aggregate_timeoff = _params.timers.aggregated_timeoff; - nextChan.current_datarate = _params.sys_params.channel_data_rate; + next_channel.aggregate_timeoff = _params.timers.aggregated_timeoff; + next_channel.current_datarate = _params.sys_params.channel_data_rate; _params.is_dutycycle_on = MBED_CONF_LORA_DUTY_CYCLE_ON; - nextChan.dc_enabled = _params.is_dutycycle_on; - nextChan.joined = _is_nwk_joined; - nextChan.last_aggregate_tx_time = _params.timers.aggregated_last_tx_time; + next_channel.dc_enabled = _params.is_dutycycle_on; + next_channel.joined = _is_nwk_joined; + next_channel.last_aggregate_tx_time = _params.timers.aggregated_last_tx_time; - lorawan_status_t status = _lora_phy.set_next_channel(&nextChan, + lorawan_status_t status = _lora_phy.set_next_channel(&next_channel, &_params.channel, - &dutyCycleTimeOff, + &backoff_time, &_params.timers.aggregated_timeoff); + switch (status) { case LORAWAN_STATUS_NO_CHANNEL_FOUND: case LORAWAN_STATUS_NO_FREE_CHANNEL_FOUND: return status; case LORAWAN_STATUS_DUTYCYCLE_RESTRICTED: - if (dutyCycleTimeOff != 0) { - tr_debug("Next Transmission in %lu ms", dutyCycleTimeOff); - _params.mac_state |= LORAMAC_TX_DELAYED; - _lora_time.start(_params.timers.tx_delayed_timer, dutyCycleTimeOff); + if (backoff_time != 0) { + tr_debug("Next Transmission in %lu ms", backoff_time); + _lora_time.start(_params.timers.backoff_timer, backoff_time); } return LORAWAN_STATUS_OK; default: break; } - tr_debug("Next Channel Idx=%d, DR=%d", _params.channel, nextChan.current_datarate); + + tr_debug("Next Channel Idx=%d, DR=%d", _params.channel, next_channel.current_datarate); uint8_t dr_offset = _lora_phy.apply_DR_offset(_params.sys_params.channel_data_rate, _params.sys_params.rx1_dr_offset); @@ -1175,9 +1073,9 @@ lorawan_status_t LoRaMac::schedule_tx(void) _params.rx_window2_delay = _params.sys_params.join_accept_delay2 + _params.rx_window2_config.window_offset; } else { - if (validate_payload_length(_params.payload_length, + if (validate_payload_length(_params.tx_buffer_len, _params.sys_params.channel_data_rate, - mac_commands.get_mac_cmd_length()) == false) { + _mac_commands.get_mac_cmd_length()) == false) { return LORAWAN_STATUS_LENGTH_ERROR; } _params.rx_window1_delay = _params.sys_params.recv_delay1 @@ -1221,9 +1119,9 @@ void LoRaMac::reset_mac_parameters(void) _params.sys_params.max_duty_cycle = 0; _params.sys_params.aggregated_duty_cycle = 1; - mac_commands.clear_command_buffer(); - mac_commands.clear_repeat_buffer(); - mac_commands.clear_mac_commands_in_next_tx(); + _mac_commands.clear_command_buffer(); + _mac_commands.clear_repeat_buffer(); + _mac_commands.clear_mac_commands_in_next_tx(); _params.is_rx_window_enabled = true; @@ -1241,15 +1139,6 @@ void LoRaMac::reset_mac_parameters(void) _params.last_channel_idx = _params.channel; } -void LoRaMac::open_continuous_rx2_window (void) -{ - const int ret = ev_queue->call(this, &LoRaMac::on_rx_window2_timer_event); - MBED_ASSERT(ret != 0); - (void)ret; - - _params.rx_slot = RX_SLOT_WIN_CLASS_C; -} - uint8_t LoRaMac::get_default_tx_datarate() { return _lora_phy.get_default_tx_datarate(); @@ -1296,8 +1185,11 @@ void LoRaMac::reset_ongoing_tx(bool reset_pending) } } -int16_t LoRaMac::prepare_ongoing_tx(uint8_t port, const uint8_t* data, - uint16_t length, uint8_t flags, uint8_t num_retries) +int16_t LoRaMac::prepare_ongoing_tx(const uint8_t port, + const uint8_t* const data, + uint16_t length, + uint8_t flags, + uint8_t num_retries) { _ongoing_tx_msg.port = port; @@ -1342,18 +1234,17 @@ int16_t LoRaMac::prepare_ongoing_tx(uint8_t port, const uint8_t* data, _ongoing_tx_msg.nb_trials = num_retries; } - tr_info("RTS = %u bytes, PEND = %u", _ongoing_tx_msg.f_buffer_size, _ongoing_tx_msg.pending_size); + tr_info("RTS = %u bytes, PEND = %u, Port: %u", + _ongoing_tx_msg.f_buffer_size, _ongoing_tx_msg.pending_size, + _ongoing_tx_msg.fport); + return _ongoing_tx_msg.f_buffer_size; } lorawan_status_t LoRaMac::send_ongoing_tx() { lorawan_status_t status; - - if (_params.mac_state != LORAMAC_IDLE) { - return LORAWAN_STATUS_BUSY; - } - + _params.is_last_tx_join_request = false; int8_t datarate = _params.sys_params.channel_data_rate; // TODO: The comment is different than the code??? @@ -1374,7 +1265,7 @@ lorawan_status_t LoRaMac::send_ongoing_tx() } else if (_ongoing_tx_msg.type == MCPS_CONFIRMED) { machdr.bits.mtype = FRAME_TYPE_DATA_CONFIRMED_UP; _params.max_ack_timeout_retries = _ongoing_tx_msg.nb_trials; - } else if ( _ongoing_tx_msg.type == MCPS_PROPRIETARY) { + } else if (_ongoing_tx_msg.type == MCPS_PROPRIETARY) { //TODO: Is this dead code currently??? Nobody sets this type machdr.bits.mtype = FRAME_TYPE_PROPRIETARY; } else { @@ -1393,9 +1284,6 @@ lorawan_status_t LoRaMac::send_ongoing_tx() _ongoing_tx_msg.f_buffer_size); if (status == LORAWAN_STATUS_OK) { _mcps_confirmation.req_type = _ongoing_tx_msg.type; - _params.flags.bits.mcps_req = 1; - } else { - _params.is_node_ack_requested = false; } return status; @@ -1422,7 +1310,7 @@ void LoRaMac::set_device_class(const device_class_t& device_class) &_params.rx_window2_config); } if (CLASS_C == _device_class) { - open_continuous_rx2_window(); + open_rx2_window(); } } @@ -1431,8 +1319,8 @@ void LoRaMac::setup_link_check_request() reset_mlme_confirmation(); _mlme_confirmation.req_type = MLME_LINK_CHECK; - _params.flags.bits.mlme_req = 1; - mac_commands.add_link_check_req(); + _mlme_confirmation.pending = true; + _mac_commands.add_link_check_req(); } lorawan_status_t LoRaMac::prepare_join(const lorawan_connect_t *params, bool is_otaa) @@ -1456,11 +1344,6 @@ lorawan_status_t LoRaMac::prepare_join(const lorawan_connect_t *params, bool is_ } // Reset variable JoinRequestTrials _params.join_request_trial_counter = 0; - - reset_mac_parameters(); - - _params.sys_params.channel_data_rate = - _lora_phy.get_alternate_DR(_params.join_request_trial_counter + 1); } else { _params.net_id = params->connection_u.abp.nwk_id; _params.dev_addr = params->connection_u.abp.dev_addr; @@ -1514,22 +1397,14 @@ lorawan_status_t LoRaMac::join(bool is_otaa) return LORAWAN_STATUS_OK; } - if (LORAMAC_IDLE != _params.mac_state) { - return LORAWAN_STATUS_BUSY; - } - reset_mlme_confirmation(); - _mlme_confirmation.req_type = MLME_JOIN; - _params.flags.bits.mlme_req = 1; - loramac_mhdr_t machdr; - machdr.value = 0; - machdr.bits.mtype = FRAME_TYPE_JOIN_REQ; - return send(&machdr, 0, NULL, 0); + return send_join_request(); } -static void memcpy_convert_endianess(uint8_t *dst, const uint8_t *src, +static void memcpy_convert_endianess(uint8_t *dst, + const uint8_t *src, uint16_t size) { dst = dst + (size - 1); @@ -1540,7 +1415,8 @@ static void memcpy_convert_endianess(uint8_t *dst, const uint8_t *src, lorawan_status_t LoRaMac::prepare_frame(loramac_mhdr_t *machdr, loramac_frame_ctrl_t *fctrl, - uint8_t fport, void *fbuffer, + const uint8_t fport, + const void *fbuffer, uint16_t fbuffer_size) { uint16_t i; @@ -1550,7 +1426,7 @@ lorawan_status_t LoRaMac::prepare_frame(loramac_mhdr_t *machdr, uint8_t frame_port = fport; lorawan_status_t status = LORAWAN_STATUS_OK; - _params.buffer_pkt_len = 0; + _params.tx_buffer_len = 0; _params.is_node_ack_requested = false; @@ -1558,39 +1434,39 @@ lorawan_status_t LoRaMac::prepare_frame(loramac_mhdr_t *machdr, fbuffer_size = 0; } - _params.payload_length = fbuffer_size; + _params.tx_buffer_len = fbuffer_size; - _params.buffer[pkt_header_len++] = machdr->value; + _params.tx_buffer[pkt_header_len++] = machdr->value; switch (machdr->bits.mtype) { case FRAME_TYPE_JOIN_REQ: - _params.buffer_pkt_len = pkt_header_len; - memcpy_convert_endianess(_params.buffer + _params.buffer_pkt_len, + _params.tx_buffer_len = pkt_header_len; + memcpy_convert_endianess(_params.tx_buffer + _params.tx_buffer_len, _params.keys.app_eui, 8); - _params.buffer_pkt_len += 8; - memcpy_convert_endianess(_params.buffer + _params.buffer_pkt_len, + _params.tx_buffer_len += 8; + memcpy_convert_endianess(_params.tx_buffer + _params.tx_buffer_len, _params.keys.dev_eui, 8); - _params.buffer_pkt_len += 8; + _params.tx_buffer_len += 8; _params.dev_nonce = _lora_phy.get_radio_rng(); - _params.buffer[_params.buffer_pkt_len++] = _params.dev_nonce & 0xFF; - _params.buffer[_params.buffer_pkt_len++] = (_params.dev_nonce >> 8) & 0xFF; + _params.tx_buffer[_params.tx_buffer_len++] = _params.dev_nonce & 0xFF; + _params.tx_buffer[_params.tx_buffer_len++] = (_params.dev_nonce >> 8) & 0xFF; - if (0 != _lora_crypto.compute_join_frame_mic(_params.buffer, - _params.buffer_pkt_len & 0xFF, + if (0 != _lora_crypto.compute_join_frame_mic(_params.tx_buffer, + _params.tx_buffer_len & 0xFF, _params.keys.app_key, APPKEY_KEY_LENGTH, &mic)) { return LORAWAN_STATUS_CRYPTO_FAIL; } - _params.buffer[_params.buffer_pkt_len++] = mic & 0xFF; - _params.buffer[_params.buffer_pkt_len++] = (mic >> 8) & 0xFF; - _params.buffer[_params.buffer_pkt_len++] = (mic >> 16) & 0xFF; - _params.buffer[_params.buffer_pkt_len++] = (mic >> 24) & 0xFF; + _params.tx_buffer[_params.tx_buffer_len++] = mic & 0xFF; + _params.tx_buffer[_params.tx_buffer_len++] = (mic >> 8) & 0xFF; + _params.tx_buffer[_params.tx_buffer_len++] = (mic >> 16) & 0xFF; + _params.tx_buffer[_params.tx_buffer_len++] = (mic >> 24) & 0xFF; break; case FRAME_TYPE_DATA_CONFIRMED_UP: @@ -1615,98 +1491,100 @@ lorawan_status_t LoRaMac::prepare_frame(loramac_mhdr_t *machdr, fctrl->bits.ack = 1; } - _params.buffer[pkt_header_len++] = (_params.dev_addr) & 0xFF; - _params.buffer[pkt_header_len++] = (_params.dev_addr >> 8) & 0xFF; - _params.buffer[pkt_header_len++] = (_params.dev_addr >> 16) & 0xFF; - _params.buffer[pkt_header_len++] = (_params.dev_addr >> 24) & 0xFF; + _params.tx_buffer[pkt_header_len++] = (_params.dev_addr) & 0xFF; + _params.tx_buffer[pkt_header_len++] = (_params.dev_addr >> 8) & 0xFF; + _params.tx_buffer[pkt_header_len++] = (_params.dev_addr >> 16) & 0xFF; + _params.tx_buffer[pkt_header_len++] = (_params.dev_addr >> 24) & 0xFF; - _params.buffer[pkt_header_len++] = fctrl->value; + _params.tx_buffer[pkt_header_len++] = fctrl->value; - _params.buffer[pkt_header_len++] = _params.ul_frame_counter & 0xFF; - _params.buffer[pkt_header_len++] = (_params.ul_frame_counter >> 8) + _params.tx_buffer[pkt_header_len++] = _params.ul_frame_counter & 0xFF; + _params.tx_buffer[pkt_header_len++] = (_params.ul_frame_counter >> 8) & 0xFF; - mac_commands.copy_repeat_commands_to_buffer(); + _mac_commands.copy_repeat_commands_to_buffer(); - const uint8_t mac_commands_len = mac_commands.get_mac_cmd_length(); + const uint8_t mac_commands_len = _mac_commands.get_mac_cmd_length(); - if ((payload != NULL) && (_params.payload_length > 0)) { - if (mac_commands.is_mac_command_in_next_tx() == true) { + if ((payload != NULL) && (_params.tx_buffer_len > 0)) { + if (_mac_commands.is_mac_command_in_next_tx() == true) { if (mac_commands_len <= LORA_MAC_COMMAND_MAX_FOPTS_LENGTH) { fctrl->bits.fopts_len += mac_commands_len; // Update FCtrl field with new value of OptionsLength - _params.buffer[0x05] = fctrl->value; + _params.tx_buffer[0x05] = fctrl->value; const uint8_t *buffer = - mac_commands.get_mac_commands_buffer(); + _mac_commands.get_mac_commands_buffer(); for (i = 0; i < mac_commands_len; i++) { - _params.buffer[pkt_header_len++] = buffer[i]; + _params.tx_buffer[pkt_header_len++] = buffer[i]; } } else { - _params.payload_length = mac_commands_len; - payload = mac_commands.get_mac_commands_buffer(); + _params.tx_buffer_len = mac_commands_len; + payload = _mac_commands.get_mac_commands_buffer(); frame_port = 0; } } } else { if ((mac_commands_len > 0) - && (mac_commands.is_mac_command_in_next_tx() == true)) { - _params.payload_length = mac_commands_len; - payload = mac_commands.get_mac_commands_buffer(); + && (_mac_commands.is_mac_command_in_next_tx() == true)) { + _params.tx_buffer_len = mac_commands_len; + payload = _mac_commands.get_mac_commands_buffer(); frame_port = 0; } } - mac_commands.parse_mac_commands_to_repeat(); + _mac_commands.parse_mac_commands_to_repeat(); - if ((payload != NULL) && (_params.payload_length > 0)) { - _params.buffer[pkt_header_len++] = frame_port; + if ((payload != NULL) && (_params.tx_buffer_len > 0)) { + _params.tx_buffer[pkt_header_len++] = frame_port; uint8_t *key = _params.keys.app_skey; uint32_t key_length = sizeof(_params.keys.app_skey)*8; if (frame_port == 0) { - mac_commands.clear_command_buffer(); + _mac_commands.clear_command_buffer(); key = _params.keys.nwk_skey; key_length = sizeof(_params.keys.nwk_skey)*8; } - if (0 != _lora_crypto.encrypt_payload((uint8_t*) payload, _params.payload_length, + if (0 != _lora_crypto.encrypt_payload((uint8_t*) payload, _params.tx_buffer_len, key, key_length, _params.dev_addr, UP_LINK, _params.ul_frame_counter, - &_params.buffer[pkt_header_len])) { + &_params.tx_buffer[pkt_header_len])) { status = LORAWAN_STATUS_CRYPTO_FAIL; } } - _params.buffer_pkt_len = pkt_header_len + _params.payload_length; + _params.tx_buffer_len = pkt_header_len + _params.tx_buffer_len; - if (0 != _lora_crypto.compute_mic(_params.buffer, _params.buffer_pkt_len, + if (0 != _lora_crypto.compute_mic(_params.tx_buffer, _params.tx_buffer_len, _params.keys.nwk_skey, sizeof(_params.keys.nwk_skey)*8, _params.dev_addr, UP_LINK, _params.ul_frame_counter, &mic)) { status = LORAWAN_STATUS_CRYPTO_FAIL; } - _params.buffer[_params.buffer_pkt_len + 0] = mic & 0xFF; - _params.buffer[_params.buffer_pkt_len + 1] = (mic >> 8) & 0xFF; - _params.buffer[_params.buffer_pkt_len + 2] = (mic >> 16) & 0xFF; - _params.buffer[_params.buffer_pkt_len + 3] = (mic >> 24) & 0xFF; + _params.tx_buffer[_params.tx_buffer_len + 0] = mic & 0xFF; + _params.tx_buffer[_params.tx_buffer_len + 1] = (mic >> 8) & 0xFF; + _params.tx_buffer[_params.tx_buffer_len + 2] = (mic >> 16) & 0xFF; + _params.tx_buffer[_params.tx_buffer_len + 3] = (mic >> 24) & 0xFF; - _params.buffer_pkt_len += LORAMAC_MFR_LEN; + _params.tx_buffer_len += LORAMAC_MFR_LEN; } break; case FRAME_TYPE_PROPRIETARY: - if ((fbuffer != NULL) && (_params.payload_length > 0)) { - memcpy(_params.buffer + pkt_header_len, (uint8_t*) fbuffer, - _params.payload_length); - _params.buffer_pkt_len = pkt_header_len + _params.payload_length; + if ((fbuffer != NULL) && (_params.tx_buffer_len > 0)) { + memcpy(_params.tx_buffer + pkt_header_len, (uint8_t*) fbuffer, + _params.tx_buffer_len); + _params.tx_buffer_len = pkt_header_len + _params.tx_buffer_len; } break; default: status = LORAWAN_STATUS_SERVICE_UNKNOWN; } + tr_debug("Frame prepared to send at port %u", frame_port); + return status; } @@ -1720,7 +1598,7 @@ lorawan_status_t LoRaMac::send_frame_on_channel(uint8_t channel) tx_config.tx_power = _params.sys_params.channel_tx_power; tx_config.max_eirp = _params.sys_params.max_eirp; tx_config.antenna_gain = _params.sys_params.antenna_gain; - tx_config.pkt_len = _params.buffer_pkt_len; + tx_config.pkt_len = _params.tx_buffer_len; _lora_phy.tx_config(&tx_config, &tx_power, &_params.timers.tx_toa); @@ -1734,16 +1612,14 @@ lorawan_status_t LoRaMac::send_frame_on_channel(uint8_t channel) _mcps_confirmation.tx_toa = _params.timers.tx_toa; _mlme_confirmation.tx_toa = _params.timers.tx_toa; - _lora_time.start(_params.timers.mac_state_check_timer, - MAC_STATE_CHECK_TIMEOUT); + // _lora_time.start(_params.timers.mac_state_check_timer, + // MAC_STATE_CHECK_TIMEOUT); if (!_is_nwk_joined) { _params.join_request_trial_counter++; } - _lora_phy.handle_send(_params.buffer, _params.buffer_pkt_len); - - _params.mac_state |= LORAMAC_TX_RUNNING; + _lora_phy.handle_send(_params.tx_buffer, _params.tx_buffer_len); return LORAWAN_STATUS_OK; } @@ -1760,6 +1636,12 @@ void LoRaMac::reset_mlme_confirmation() _mlme_confirmation.status = LORAMAC_EVENT_INFO_STATUS_ERROR; } +void LoRaMac::reset_mcps_indication() +{ + memset((uint8_t*) &_mcps_indication, 0, sizeof(_mcps_indication)); + _mcps_indication.status = LORAMAC_EVENT_INFO_STATUS_ERROR; +} + void LoRaMac::set_tx_continuous_wave(uint8_t channel, int8_t datarate, int8_t tx_power, float max_eirp, float antenna_gain, uint16_t timeout) { @@ -1775,25 +1657,15 @@ void LoRaMac::set_tx_continuous_wave(uint8_t channel, int8_t datarate, int8_t tx _lora_phy.set_tx_cont_mode(&continuous_wave); } -lorawan_status_t LoRaMac::initialize(loramac_primitives_t *primitives, - EventQueue *queue) +lorawan_status_t LoRaMac::initialize(EventQueue *queue) { _lora_time.activate_timer_subsystem(queue); - ev_queue = queue; + _ev_queue = queue; - if (!primitives) { - return LORAWAN_STATUS_PARAMETER_INVALID; - } - - channel_plan.activate_channelplan_subsystem(&_lora_phy); - - mac_primitives = primitives; - - _params.flags.value = 0; + _channel_plan.activate_channelplan_subsystem(&_lora_phy); _device_class = CLASS_A; - _params.mac_state = LORAMAC_IDLE; _params.join_request_trial_counter = 0; _params.max_join_request_trials = 1; @@ -1816,14 +1688,12 @@ lorawan_status_t LoRaMac::initialize(loramac_primitives_t *primitives, _lora_phy.setup_public_network_mode(_params.is_nwk_public); _lora_phy.put_radio_to_sleep(); - _lora_time.init(_params.timers.mac_state_check_timer, - mbed::callback(this, &LoRaMac::on_mac_state_check_timer_event)); - _lora_time.init(_params.timers.tx_delayed_timer, - mbed::callback(this, &LoRaMac::on_tx_delayed_timer_event)); + _lora_time.init(_params.timers.backoff_timer, + mbed::callback(this, &LoRaMac::on_backoff_timer_expiry)); _lora_time.init(_params.timers.rx_window1_timer, - mbed::callback(this, &LoRaMac::on_rx_window1_timer_event)); + mbed::callback(this, &LoRaMac::open_rx1_window)); _lora_time.init(_params.timers.rx_window2_timer, - mbed::callback(this, &LoRaMac::on_rx_window2_timer_event)); + mbed::callback(this, &LoRaMac::open_rx2_window)); _lora_time.init(_params.timers.ack_timeout_timer, mbed::callback(this, &LoRaMac::on_ack_timeout_timer_event)); @@ -1839,8 +1709,7 @@ lorawan_status_t LoRaMac::initialize(loramac_primitives_t *primitives, void LoRaMac::disconnect() { - _lora_time.stop(_params.timers.mac_state_check_timer); - _lora_time.stop(_params.timers.tx_delayed_timer); + _lora_time.stop(_params.timers.backoff_timer); _lora_time.stop(_params.timers.rx_window1_timer); _lora_time.stop(_params.timers.rx_window2_timer); _lora_time.stop(_params.timers.ack_timeout_timer); @@ -1852,22 +1721,22 @@ void LoRaMac::disconnect() _params.is_rx_window_enabled = true; _params.is_node_ack_requested = false; _params.is_srv_ack_requested = false; - _params.flags.value = 0; - _params.mac_state = 0; - mac_commands.clear_command_buffer(); - mac_commands.clear_repeat_buffer(); - mac_commands.clear_mac_commands_in_next_tx(); + _mac_commands.clear_command_buffer(); + _mac_commands.clear_repeat_buffer(); + _mac_commands.clear_mac_commands_in_next_tx(); - _params.mac_state = LORAMAC_IDLE; + reset_mcps_confirmation(); + reset_mlme_confirmation(); + reset_mcps_indication(); } uint8_t LoRaMac::get_max_possible_tx_size(uint8_t size) { uint8_t max_possible_payload_size = 0; uint8_t current_payload_size = 0; - uint8_t fopt_len = mac_commands.get_mac_cmd_length() - + mac_commands.get_repeat_commands_length(); + uint8_t fopt_len = _mac_commands.get_mac_cmd_length() + + _mac_commands.get_repeat_commands_length(); if (_params.sys_params.adr_on) { _lora_phy.get_next_ADR(false, _params.sys_params.channel_data_rate, @@ -1882,8 +1751,8 @@ uint8_t LoRaMac::get_max_possible_tx_size(uint8_t size) } else { max_possible_payload_size = current_payload_size; fopt_len = 0; - mac_commands.clear_command_buffer(); - mac_commands.clear_repeat_buffer(); + _mac_commands.clear_command_buffer(); + _mac_commands.clear_repeat_buffer(); } if (validate_payload_length(size, _params.sys_params.channel_data_rate, @@ -1905,40 +1774,34 @@ void LoRaMac::set_nwk_joined(bool joined) lorawan_status_t LoRaMac::add_channel_plan(const lorawan_channelplan_t& plan) { - if ((_params.mac_state & LORAMAC_TX_RUNNING) == LORAMAC_TX_RUNNING) { - if ((_params.mac_state & LORAMAC_TX_CONFIG) != LORAMAC_TX_CONFIG) { - return LORAWAN_STATUS_BUSY; - } + if (tx_ongoing()) { + return LORAWAN_STATUS_BUSY; } - return channel_plan.set_plan(plan); + return _channel_plan.set_plan(plan); } lorawan_status_t LoRaMac::remove_channel_plan() { - if ((_params.mac_state & LORAMAC_TX_RUNNING) == LORAMAC_TX_RUNNING) { - if ((_params.mac_state & LORAMAC_TX_CONFIG) != LORAMAC_TX_CONFIG) { - return LORAWAN_STATUS_BUSY; - } + if (tx_ongoing()) { + return LORAWAN_STATUS_BUSY; } - return channel_plan.remove_plan(); + return _channel_plan.remove_plan(); } lorawan_status_t LoRaMac::get_channel_plan(lorawan_channelplan_t& plan) { - return channel_plan.get_plan(plan, _lora_phy.get_phy_channels()); + return _channel_plan.get_plan(plan, _lora_phy.get_phy_channels()); } lorawan_status_t LoRaMac::remove_single_channel(uint8_t id) { - if ((_params.mac_state & LORAMAC_TX_RUNNING) == LORAMAC_TX_RUNNING) { - if ((_params.mac_state & LORAMAC_TX_CONFIG) != LORAMAC_TX_CONFIG) { - return LORAWAN_STATUS_BUSY; - } + if (tx_ongoing()) { + return LORAWAN_STATUS_BUSY; } - return channel_plan.remove_single_channel(id); + return _channel_plan.remove_single_channel(id); } lorawan_status_t LoRaMac::multicast_channel_link(multicast_params_t *channel_param) @@ -1946,7 +1809,7 @@ lorawan_status_t LoRaMac::multicast_channel_link(multicast_params_t *channel_par if (channel_param == NULL) { return LORAWAN_STATUS_PARAMETER_INVALID; } - if ((_params.mac_state & LORAMAC_TX_RUNNING) == LORAMAC_TX_RUNNING) { + if (tx_ongoing()) { return LORAWAN_STATUS_BUSY; } @@ -1971,7 +1834,8 @@ lorawan_status_t LoRaMac::multicast_channel_unlink( if (channel_param == NULL) { return LORAWAN_STATUS_PARAMETER_INVALID; } - if ((_params.mac_state & LORAMAC_TX_RUNNING) == LORAMAC_TX_RUNNING) { + + if (tx_ongoing()) { return LORAWAN_STATUS_BUSY; } @@ -1997,16 +1861,7 @@ lorawan_status_t LoRaMac::multicast_channel_unlink( void LoRaMac::bind_radio_driver(LoRaRadio& radio) { - radio_events.tx_done = mbed::callback(this, &LoRaMac::handle_tx_done); - radio_events.rx_done = mbed::callback(this, &LoRaMac::handle_rx_done); - radio_events.rx_error = mbed::callback(this, &LoRaMac::handle_rx_error); - radio_events.tx_timeout = mbed::callback(this, &LoRaMac::handle_tx_timeout); - radio_events.rx_timeout = mbed::callback(this, &LoRaMac::handle_rx_timeout); - _lora_phy.set_radio_instance(radio); - radio.lock(); - radio.init_radio(&radio_events); - radio.unlock(); } #if defined(LORAWAN_COMPLIANCE_TEST) diff --git a/features/lorawan/lorastack/mac/LoRaMac.h b/features/lorawan/lorastack/mac/LoRaMac.h index 04c5f00b72..19cd83aa43 100644 --- a/features/lorawan/lorastack/mac/LoRaMac.h +++ b/features/lorawan/lorastack/mac/LoRaMac.h @@ -51,7 +51,11 @@ #include "LoRaMacChannelPlan.h" #include "LoRaMacCommand.h" #include "LoRaMacCrypto.h" +#if MBED_CONF_RTOS_PRESENT +#include "rtos/Mutex.h" +#endif +#include "platform/ScopedLock.h" class LoRaMac { @@ -70,13 +74,8 @@ public: /** * @brief LoRaMAC layer initialization * - * @details In addition to the initialization of the LoRaMAC layer, this - * function initializes the callback primitives of the MCPS and - * MLME services. Every data field of \ref loramac_primitives_t must be - * set to a valid callback function. + * @details Initializes the LoRaMAC layer, * - * @param primitives [in] A pointer to the structure defining the LoRaMAC - * event functions. Refer to \ref loramac_primitives_t. * * @param queue [in] A pointer to the application provided EventQueue. * @@ -84,8 +83,7 @@ public: * \ref LORAWAN_STATUS_OK * \ref LORAWAN_STATUS_PARAMETER_INVALID */ - lorawan_status_t initialize(loramac_primitives_t *primitives, - events::EventQueue *queue); + lorawan_status_t initialize(events::EventQueue *queue); /** * @brief Disconnect LoRaMac layer @@ -246,8 +244,8 @@ public: * of success and a negative error code in case of * failure. */ - lorawan_status_t send(loramac_mhdr_t *mac_hdr, uint8_t fport, void *fbuffer, - uint16_t fbuffer_size); + lorawan_status_t send(loramac_mhdr_t *mac_hdr, const uint8_t fport, + const void *fbuffer, uint16_t fbuffer_size); /** * @brief Puts the system in continuous transmission mode @@ -280,12 +278,6 @@ public: */ void reset_mac_parameters(void); - /** - * @brief Opens up a continuous RX 2 window. This is used for - * class c devices. - */ - void open_continuous_rx2_window(void); - /** * @brief get_default_tx_datarate Gets the default TX datarate * @return default TX datarate. @@ -341,14 +333,14 @@ public: * @param num_retries Number of retries for a confirmed type message * @return The number of bytes prepared for sending. */ - int16_t prepare_ongoing_tx(uint8_t port, const uint8_t* data, + int16_t prepare_ongoing_tx(const uint8_t port, const uint8_t* data, uint16_t length, uint8_t flags, uint8_t num_retries); /** * @brief send_ongoing_tx Sends the ongoing_tx_msg * @return LORAWAN_STATUS_OK or a negative error code on failure. */ - lorawan_status_t send_ongoing_tx(); + lorawan_status_t send_ongoing_tx(void); /** * @brief device_class Returns active device class @@ -362,6 +354,11 @@ public: */ void set_device_class(const device_class_t& device_class); + /** + * @brief opens a continuous RX2 window for Class C devices + */ + void open_continuous_rx_window(void); + /** * @brief setup_link_check_request Adds link check request command * to be put on next outgoing message (when it fits) @@ -384,59 +381,153 @@ public: */ lorawan_status_t join(bool is_otaa); -private: /** - * Function to be executed on Radio Tx Done event + * MAC operations upon successful transmission */ void on_radio_tx_done(void); /** - * This function prepares the MAC to abort the execution of function - * on_radio_rx_done() in case of a reception error. + * MAC operations upon reception */ - void prepare_rx_done_abort(void); + void on_radio_rx_done(const uint8_t* const payload, uint16_t size, + int16_t rssi, int8_t snr); /** - * Function to be executed on Radio Rx Done event - */ - void on_radio_rx_done(uint8_t *payload, uint16_t size, int16_t rssi, - int8_t snr); - - /** - * Function executed on Radio Tx Timeout event + * MAC operations upon transmission timeout */ void on_radio_tx_timeout(void); /** - * Function executed on Radio Rx error event + * MAC operations upon empty reception slots + * + * @param is_timeout false when radio encountered an error + * true when the an RX slot went empty + * + * @return current RX slot */ - void on_radio_rx_error(void); + rx_slot_t on_radio_rx_timeout(bool is_timeout); /** - * Function executed on Radio Rx Timeout event + * Handles retransmissions of Join requests if an Accept + * was not received. + * + * @returns true if a retry will be made */ - void on_radio_rx_timeout(void); + bool continue_joining_process(void); /** - *Function executed on Resend Frame timer event. + * Checks if the CONFIRMED data can be sent again or not. */ - void on_mac_state_check_timer_event(void); + bool continue_sending_process(void); /** - * Function executed on duty cycle delayed Tx timer event + * Read-only access to MAC primitive blocks */ - void on_tx_delayed_timer_event(void); + const loramac_mcps_confirm_t *get_mcps_confirmation() const; + const loramac_mcps_indication_t *get_mcps_indication() const; + const loramac_mlme_confirm_t *get_mlme_confirmation() const; + const loramac_mlme_indication_t *get_mlme_indication() const; /** - * Function executed on first Rx window timer event + * Post processing steps in response to actions carried out + * by controller layer and Mac */ - void on_rx_window1_timer_event(void); + void post_process_mcps_req(void); + void post_process_mcps_ind(void); + void post_process_mlme_request(void); + void post_process_mlme_ind(void); /** - * Function executed on second Rx window timer event + * These locks trample through to the upper layers and make + * the stack thread safe. */ - void on_rx_window2_timer_event(void); +#if MBED_CONF_RTOS_PRESENT + void lock(void) { osStatus status = _mutex.lock(); MBED_ASSERT(status == osOK); } + void unlock(void) { osStatus status = _mutex.unlock(); MBED_ASSERT(status == osOK); } +#else + void lock(void) { } + void unlock(void) { } +#endif +private: + typedef mbed::ScopedLock Lock; +#if MBED_CONF_RTOS_PRESENT + rtos::Mutex _mutex; +#endif + + /** + * Aborts reception + */ + void abort_rx(void); + + /** + * Handles a Join Accept frame + */ + void handle_join_accept_frame(const uint8_t *payload, uint16_t size); + + /** + * Handles data frames + */ + void handle_data_frame(const uint8_t *payload, uint16_t size, uint8_t ptr_pos, + uint8_t msg_type, int16_t rssi, int8_t snr); + + /** + * Send a Join Request + */ + lorawan_status_t send_join_request(); + + /** + * Handles retransmissions + */ + lorawan_status_t handle_retransmission(); + + /** + * Checks if the frame is valid + */ + void check_frame_size(uint16_t size); + + /** + * Performs MIC + */ + bool message_integrity_check(const uint8_t *payload, uint16_t size, + uint8_t *ptr_pos, uint32_t address, + uint32_t *downlink_counter, const uint8_t *nwk_skey); + + /** + * Decrypts and extracts data and MAC commands from the received encrypted + * payload + */ + void extract_data_and_mac_commands(const uint8_t *payload, uint16_t size, + uint8_t fopts_len, uint8_t *nwk_skey, + uint8_t *app_skey, uint32_t address, + uint32_t downlink_frame_counter, + int16_t rssi, int8_t snr); + /** + * Decrypts and extracts MAC commands from the received encrypted + * payload if there is no data + */ + void extract_mac_commands_only(const uint8_t *payload, int8_t snr, uint8_t fopts_len); + + /** + * Callback function to be executed when the DC backoff timer expires + */ + void on_backoff_timer_expiry(void); + + /** + * At the end of an RX1 window timer, an RX1 window is opened using this method. + */ + void open_rx1_window(void); + + /** + * At the end of an RX2 window timer, an RX2 window is opened using this method. + */ + void open_rx2_window(void); + + /** + * A method to retry a CONFIRMED message after a particular time period + * (ACK_TIMEOUT = TIME_IN_MS) if the ack was not received + */ + void on_ack_timeout_timer_event(void); /*! * \brief Check if the OnAckTimeoutTimer has do be disabled. If so, the @@ -454,30 +545,25 @@ private: uint8_t ack_timeout_retries_counter, uint8_t ack_timeout_retries); - /** - * Function executed on AckTimeout timer event - */ - void on_ack_timeout_timer_event(void); - /** * Validates if the payload fits into the frame, taking the datarate * into account. * * Please Refer to chapter 4.3.2 of the LoRaWAN specification, v1.0.2 */ - bool validate_payload_length(uint8_t length, int8_t datarate, uint8_t fopts_len); + bool validate_payload_length(uint16_t length, int8_t datarate, uint8_t fopts_len); /** * Prepares MAC frame on the behest of send() API. */ lorawan_status_t prepare_frame(loramac_mhdr_t *mac_hdr, - loramac_frame_ctrl_t *fctrl, uint8_t fport, - void *fbuffer, uint16_t fbuffer_size); + loramac_frame_ctrl_t *fctrl, const uint8_t fport, + const void *fbuffer, uint16_t fbuffer_size); /** * Schedules a transmission on the behest of send() API. */ - lorawan_status_t schedule_tx(void); + lorawan_status_t schedule_tx(); /** * Calculates the back-off time for the band of a channel. @@ -491,14 +577,11 @@ private: lorawan_status_t send_frame_on_channel(uint8_t channel); /** - * @brief reset_mcps_confirmation Resets the MCPS confirmation struct + * Resets MAC primitive blocks */ - void reset_mcps_confirmation(); - - /** - * @brief reset_mlme_confirmation Resets the MLME confirmation struct - */ - void reset_mlme_confirmation(); + void reset_mcps_confirmation(void); + void reset_mlme_confirmation(void); + void reset_mcps_indication(void); /** * @brief set_tx_continuous_wave Puts the system in continuous transmission mode @@ -512,17 +595,6 @@ private: void set_tx_continuous_wave(uint8_t channel, int8_t datarate, int8_t tx_power, float max_eirp, float antenna_gain, uint16_t timeout); - /** - * Prototypes for ISR handlers - */ - void handle_cad_done(bool cad); - void handle_tx_done(void); - void handle_rx_done(uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr); - void handle_rx_error(void); - void handle_rx_timeout(void); - void handle_tx_timeout(void); - void handle_fhss_change_channel(uint8_t cur_channel); - private: /** * Timer subsystem handle @@ -537,12 +609,12 @@ private: /** * MAC command handle */ - LoRaMacCommand mac_commands; + LoRaMacCommand _mac_commands; /** * Channel planning subsystem */ - LoRaMacChannelPlan channel_plan; + LoRaMacChannelPlan _channel_plan; /** * Crypto handling subsystem @@ -554,20 +626,10 @@ private: */ loramac_protocol_params _params; - /** - * Radio event callback handlers for MAC - */ - radio_events_t radio_events; - - /** - * LoRaMac upper layer event functions - */ - loramac_primitives_t *mac_primitives; - /** * EventQueue object storage */ - events::EventQueue *ev_queue; + events::EventQueue *_ev_queue; /** * Structure to hold MCPS indication data. diff --git a/features/lorawan/lorastack/mac/LoRaMacChannelPlan.cpp b/features/lorawan/lorastack/mac/LoRaMacChannelPlan.cpp index 339aa949fd..5ad159379a 100644 --- a/features/lorawan/lorastack/mac/LoRaMacChannelPlan.cpp +++ b/features/lorawan/lorastack/mac/LoRaMacChannelPlan.cpp @@ -40,7 +40,6 @@ void LoRaMacChannelPlan::activate_channelplan_subsystem(LoRaPHY *phy) lorawan_status_t LoRaMacChannelPlan::set_plan(const lorawan_channelplan_t& plan) { - channel_params_t mac_layer_ch_params; lorawan_status_t status; uint8_t max_num_channels; @@ -57,16 +56,7 @@ lorawan_status_t LoRaMacChannelPlan::set_plan(const lorawan_channelplan_t& plan) } for (uint8_t i = 0; i < plan.nb_channels; i++) { - - mac_layer_ch_params.band = plan.channels[i].ch_param.band; - - mac_layer_ch_params.dr_range.fields.max = plan.channels[i].ch_param.dr_range.fields.max; - mac_layer_ch_params.dr_range.fields.min = plan.channels[i].ch_param.dr_range.fields.min; - mac_layer_ch_params.dr_range.value = plan.channels[i].ch_param.dr_range.value; - mac_layer_ch_params.frequency = plan.channels[i].ch_param.frequency; - mac_layer_ch_params.rx1_frequency = plan.channels[i].ch_param.rx1_frequency; - - status = _lora_phy->add_channel(&mac_layer_ch_params, plan.channels[i].id); + status = _lora_phy->add_channel(&plan.channels[i].ch_param, plan.channels[i].id); if (status != LORAWAN_STATUS_OK) { return status; diff --git a/features/lorawan/lorastack/mac/LoRaMacCommand.cpp b/features/lorawan/lorastack/mac/LoRaMacCommand.cpp index c9c7535797..0c618fef53 100644 --- a/features/lorawan/lorastack/mac/LoRaMacCommand.cpp +++ b/features/lorawan/lorastack/mac/LoRaMacCommand.cpp @@ -150,7 +150,7 @@ bool LoRaMacCommand::has_sticky_mac_cmd() const return sticky_mac_cmd; } -lorawan_status_t LoRaMacCommand::process_mac_commands(uint8_t *payload, uint8_t mac_index, +lorawan_status_t LoRaMacCommand::process_mac_commands(const uint8_t *payload, uint8_t mac_index, uint8_t commands_size, uint8_t snr, loramac_mlme_confirm_t& mlme_conf, lora_mac_system_params_t &mac_sys_params, diff --git a/features/lorawan/lorastack/mac/LoRaMacCommand.h b/features/lorawan/lorastack/mac/LoRaMacCommand.h index 02ee85787e..ba5528206f 100644 --- a/features/lorawan/lorastack/mac/LoRaMacCommand.h +++ b/features/lorawan/lorastack/mac/LoRaMacCommand.h @@ -126,7 +126,7 @@ public: * * @return status Function status. LORAWAN_STATUS_OK if command successful. */ - lorawan_status_t process_mac_commands(uint8_t *payload, uint8_t mac_index, + lorawan_status_t process_mac_commands(const uint8_t *payload, uint8_t mac_index, uint8_t commands_size, uint8_t snr, loramac_mlme_confirm_t& mlme_conf, lora_mac_system_params_t& mac_params, diff --git a/features/lorawan/lorastack/phy/LoRaPHY.cpp b/features/lorawan/lorastack/phy/LoRaPHY.cpp index 13ead9f326..b0bbb6b2c1 100644 --- a/features/lorawan/lorastack/phy/LoRaPHY.cpp +++ b/features/lorawan/lorastack/phy/LoRaPHY.cpp @@ -307,7 +307,7 @@ lorawan_time_t LoRaPHY::update_band_timeoff(bool joined, bool duty_cycle, return next_tx_delay; } -uint8_t LoRaPHY::parse_link_ADR_req(uint8_t* payload, link_adr_params_t* params) +uint8_t LoRaPHY::parse_link_ADR_req(const uint8_t* payload, link_adr_params_t* params) { uint8_t ret_index = 0; @@ -808,7 +808,7 @@ void LoRaPHY::compute_rx_win_params(int8_t datarate, uint8_t min_rx_symbols, &rx_conf_params->window_timeout, &rx_conf_params->window_offset); } -bool LoRaPHY::rx_config(rx_config_params_t* rx_conf, int8_t* datarate) +bool LoRaPHY::rx_config(rx_config_params_t* rx_conf) { radio_modems_t modem; uint8_t dr = rx_conf->datarate; @@ -868,8 +868,6 @@ bool LoRaPHY::rx_config(rx_config_params_t* rx_conf, int8_t* datarate) _radio->unlock(); - *datarate = phy_dr; - return true; } diff --git a/features/lorawan/lorastack/phy/LoRaPHY.h b/features/lorawan/lorastack/phy/LoRaPHY.h index 7e23c5cf55..a0030e7e2c 100644 --- a/features/lorawan/lorastack/phy/LoRaPHY.h +++ b/features/lorawan/lorastack/phy/LoRaPHY.h @@ -194,11 +194,9 @@ public: * * @param [in] config A pointer to the RX configuration. * - * @param [out] datarate The datarate index set. - * * @return True, if the configuration was applied successfully. */ - virtual bool rx_config(rx_config_params_t* config, int8_t* datarate); + virtual bool rx_config(rx_config_params_t* config); /** Computing Receive Windows * @@ -578,7 +576,7 @@ protected: /** * Parses the parameter of an LinkAdrRequest. */ - uint8_t parse_link_ADR_req(uint8_t* payload, link_adr_params_t* adr_params); + uint8_t parse_link_ADR_req(const uint8_t* payload, link_adr_params_t* adr_params); /** * Verifies and updates the datarate, the TX power and the number of repetitions diff --git a/features/lorawan/lorastack/phy/LoRaPHYAS923.cpp b/features/lorawan/lorastack/phy/LoRaPHYAS923.cpp index 4ce26dd84c..648a944ba6 100644 --- a/features/lorawan/lorastack/phy/LoRaPHYAS923.cpp +++ b/features/lorawan/lorastack/phy/LoRaPHYAS923.cpp @@ -249,7 +249,9 @@ LoRaPHYAS923::LoRaPHYAS923(LoRaWANTimeHandler &lora_time) // Default Channels are always enabled in the channel list, // rest will be added later channels[0] = AS923_LC1; + channels[0].band = 0; channels[1] = AS923_LC2; + channels[1].band = 0; // Initialize the default channel mask default_channel_mask[0] = LC(1) + LC(2); @@ -291,7 +293,8 @@ LoRaPHYAS923::LoRaPHYAS923(LoRaWANTimeHandler &lora_time) phy_params.accept_tx_param_setup_req = true; phy_params.fsk_supported = true; phy_params.cflist_supported = true; - + phy_params.dl_channel_req_supported = true; + phy_params.custom_channelplans_supported = true; phy_params.default_channel_cnt = AS923_NUMB_DEFAULT_CHANNELS; phy_params.max_channel_cnt = AS923_MAX_NB_CHANNELS; phy_params.cflist_channel_cnt = AS923_NUMB_CHANNELS_CF_LIST; diff --git a/features/lorawan/lorastack/phy/LoRaPHYAU915.cpp b/features/lorawan/lorastack/phy/LoRaPHYAU915.cpp index 4a9abe3d43..f7ae219b85 100644 --- a/features/lorawan/lorastack/phy/LoRaPHYAU915.cpp +++ b/features/lorawan/lorastack/phy/LoRaPHYAU915.cpp @@ -335,7 +335,7 @@ LoRaPHYAU915::~LoRaPHYAU915() { } -bool LoRaPHYAU915::rx_config(rx_config_params_t* params, int8_t* datarate) +bool LoRaPHYAU915::rx_config(rx_config_params_t* params) { int8_t dr = params->datarate; uint8_t max_payload = 0; @@ -374,7 +374,6 @@ bool LoRaPHYAU915::rx_config(rx_config_params_t* params, int8_t* datarate) _radio->unlock(); - *datarate = (uint8_t) dr; return true; } diff --git a/features/lorawan/lorastack/phy/LoRaPHYAU915.h b/features/lorawan/lorastack/phy/LoRaPHYAU915.h index 027a3fcdb1..14f63b8915 100644 --- a/features/lorawan/lorastack/phy/LoRaPHYAU915.h +++ b/features/lorawan/lorastack/phy/LoRaPHYAU915.h @@ -56,7 +56,7 @@ public: LoRaPHYAU915(LoRaWANTimeHandler &lora_time); virtual ~LoRaPHYAU915(); - virtual bool rx_config(rx_config_params_t* config, int8_t* datarate); + virtual bool rx_config(rx_config_params_t* config); virtual bool tx_config(tx_config_params_t* config, int8_t* txPower, lorawan_time_t* txTimeOnAir); diff --git a/features/lorawan/lorastack/phy/LoRaPHYCN470.cpp b/features/lorawan/lorastack/phy/LoRaPHYCN470.cpp index c3415a554d..b3bdef530d 100644 --- a/features/lorawan/lorastack/phy/LoRaPHYCN470.cpp +++ b/features/lorawan/lorastack/phy/LoRaPHYCN470.cpp @@ -303,7 +303,7 @@ LoRaPHYCN470::~LoRaPHYCN470() { } -bool LoRaPHYCN470::rx_config(rx_config_params_t* config, int8_t* datarate) +bool LoRaPHYCN470::rx_config(rx_config_params_t* config) { int8_t dr = config->datarate; uint8_t max_payload = 0; @@ -349,7 +349,6 @@ bool LoRaPHYCN470::rx_config(rx_config_params_t* config, int8_t* datarate) _radio->set_max_payload_length(MODEM_LORA, max_payload + LORA_MAC_FRMPAYLOAD_OVERHEAD); _radio->unlock(); - *datarate = (uint8_t) dr; return true; } diff --git a/features/lorawan/lorastack/phy/LoRaPHYCN470.h b/features/lorawan/lorastack/phy/LoRaPHYCN470.h index 846c849acb..6132fb6b65 100644 --- a/features/lorawan/lorastack/phy/LoRaPHYCN470.h +++ b/features/lorawan/lorastack/phy/LoRaPHYCN470.h @@ -56,7 +56,7 @@ public: LoRaPHYCN470(LoRaWANTimeHandler &lora_time); virtual ~LoRaPHYCN470(); - virtual bool rx_config(rx_config_params_t* config, int8_t* datarate ); + virtual bool rx_config(rx_config_params_t* config); virtual bool tx_config(tx_config_params_t* config, int8_t* tx_power, lorawan_time_t* tx_toa); diff --git a/features/lorawan/lorastack/phy/LoRaPHYCN779.cpp b/features/lorawan/lorastack/phy/LoRaPHYCN779.cpp index a5e2137396..6f1f618bde 100644 --- a/features/lorawan/lorastack/phy/LoRaPHYCN779.cpp +++ b/features/lorawan/lorastack/phy/LoRaPHYCN779.cpp @@ -237,8 +237,11 @@ LoRaPHYCN779::LoRaPHYCN779(LoRaWANTimeHandler &lora_time) // Channels channels[0] = CN779_LC1; + channels[0].band = 0; channels[1] = CN779_LC2; + channels[1].band = 0; channels[2] = CN779_LC3; + channels[2].band = 0; // Initialize the channels default mask default_channel_mask[0] = LC(1) + LC(2) + LC(3); diff --git a/features/lorawan/lorastack/phy/LoRaPHYEU433.cpp b/features/lorawan/lorastack/phy/LoRaPHYEU433.cpp index b0e5f3841e..fdd9361736 100644 --- a/features/lorawan/lorastack/phy/LoRaPHYEU433.cpp +++ b/features/lorawan/lorastack/phy/LoRaPHYEU433.cpp @@ -238,8 +238,11 @@ LoRaPHYEU433::LoRaPHYEU433(LoRaWANTimeHandler &lora_time) // Channels channels[0] = EU433_LC1; - channels[1] = EU433_LC2;; - channels[2] = EU433_LC3;; + channels[0].band = 0; + channels[1] = EU433_LC2; + channels[1].band = 0; + channels[2] = EU433_LC3; + channels[2].band = 0; // Initialize the channels default mask default_channel_mask[0] = LC(1) + LC(2) + LC(3); diff --git a/features/lorawan/lorastack/phy/LoRaPHYEU868.cpp b/features/lorawan/lorastack/phy/LoRaPHYEU868.cpp index 4c0b1a369b..381cd003b7 100644 --- a/features/lorawan/lorastack/phy/LoRaPHYEU868.cpp +++ b/features/lorawan/lorastack/phy/LoRaPHYEU868.cpp @@ -268,8 +268,11 @@ LoRaPHYEU868::LoRaPHYEU868(LoRaWANTimeHandler &lora_time) // Default Channels are always enabled, rest will be added later channels[0] = EU868_LC1; + channels[0].band = 1; channels[1] = EU868_LC2; + channels[1].band = 1; channels[2] = EU868_LC3; + channels[2].band = 1; // Initialize the channels default mask default_channel_mask[0] = LC(1) + LC(2) + LC(3); diff --git a/features/lorawan/lorastack/phy/LoRaPHYIN865.cpp b/features/lorawan/lorastack/phy/LoRaPHYIN865.cpp index 5594647ebc..fffe91cc4b 100644 --- a/features/lorawan/lorastack/phy/LoRaPHYIN865.cpp +++ b/features/lorawan/lorastack/phy/LoRaPHYIN865.cpp @@ -239,8 +239,11 @@ LoRaPHYIN865::LoRaPHYIN865(LoRaWANTimeHandler &lora_time) // Default Channels are always enabled, rest will be added later channels[0] = IN865_LC1; + channels[0].band = 0; channels[1] = IN865_LC2; + channels[1].band = 0; channels[2] = IN865_LC3; + channels[2].band = 0; // Initialize the channels default mask default_channel_mask[0] = LC(1) + LC(2) + LC(3); diff --git a/features/lorawan/lorastack/phy/LoRaPHYKR920.cpp b/features/lorawan/lorastack/phy/LoRaPHYKR920.cpp index a2d40f736b..fd238b4ba5 100644 --- a/features/lorawan/lorastack/phy/LoRaPHYKR920.cpp +++ b/features/lorawan/lorastack/phy/LoRaPHYKR920.cpp @@ -248,8 +248,11 @@ LoRaPHYKR920::LoRaPHYKR920(LoRaWANTimeHandler &lora_time) // Channels channels[0] = KR920_LC1; + channels[0].band = 0; channels[1] = KR920_LC2; + channels[1].band = 0; channels[2] = KR920_LC3; + channels[2].band = 0; // Initialize the channels default mask default_channel_mask[0] = LC( 1 ) + LC( 2 ) + LC( 3 ); diff --git a/features/lorawan/lorastack/phy/LoRaPHYUS915.cpp b/features/lorawan/lorastack/phy/LoRaPHYUS915.cpp index bf73b53fb6..e3ae1ad583 100644 --- a/features/lorawan/lorastack/phy/LoRaPHYUS915.cpp +++ b/features/lorawan/lorastack/phy/LoRaPHYUS915.cpp @@ -353,7 +353,7 @@ void LoRaPHYUS915::restore_default_channels() } } -bool LoRaPHYUS915::rx_config(rx_config_params_t* config, int8_t* datarate) +bool LoRaPHYUS915::rx_config(rx_config_params_t* config) { int8_t dr = config->datarate; uint8_t max_payload = 0; @@ -409,7 +409,6 @@ bool LoRaPHYUS915::rx_config(rx_config_params_t* config, int8_t* datarate) _radio->unlock(); - *datarate = (uint8_t) dr; return true; } diff --git a/features/lorawan/lorastack/phy/LoRaPHYUS915.h b/features/lorawan/lorastack/phy/LoRaPHYUS915.h index 479695f651..b5e34bd1d8 100644 --- a/features/lorawan/lorastack/phy/LoRaPHYUS915.h +++ b/features/lorawan/lorastack/phy/LoRaPHYUS915.h @@ -56,7 +56,7 @@ public: virtual void restore_default_channels(); - virtual bool rx_config(rx_config_params_t* config, int8_t* datarate); + virtual bool rx_config(rx_config_params_t* config); virtual bool tx_config(tx_config_params_t* config, int8_t* tx_power, lorawan_time_t* tx_toa); diff --git a/features/lorawan/lorastack/phy/LoRaPHYUS915Hybrid.cpp b/features/lorawan/lorastack/phy/LoRaPHYUS915Hybrid.cpp index d729901642..4ae99dc791 100644 --- a/features/lorawan/lorastack/phy/LoRaPHYUS915Hybrid.cpp +++ b/features/lorawan/lorastack/phy/LoRaPHYUS915Hybrid.cpp @@ -369,7 +369,7 @@ bool LoRaPHYUS915Hybrid::get_next_ADR(bool restore_channel_mask, int8_t& dr_out, return adrAckReq; } -bool LoRaPHYUS915Hybrid::rx_config(rx_config_params_t* config, int8_t* datarate) +bool LoRaPHYUS915Hybrid::rx_config(rx_config_params_t* config) { int8_t dr = config->datarate; uint8_t max_payload = 0; @@ -416,7 +416,6 @@ bool LoRaPHYUS915Hybrid::rx_config(rx_config_params_t* config, int8_t* datarate) _radio->set_max_payload_length(MODEM_LORA, max_payload + LORA_MAC_FRMPAYLOAD_OVERHEAD); _radio->unlock(); - *datarate = (uint8_t) dr; return true; } diff --git a/features/lorawan/lorastack/phy/LoRaPHYUS915Hybrid.h b/features/lorawan/lorastack/phy/LoRaPHYUS915Hybrid.h index ee0ee23362..fefd7ddac3 100644 --- a/features/lorawan/lorastack/phy/LoRaPHYUS915Hybrid.h +++ b/features/lorawan/lorastack/phy/LoRaPHYUS915Hybrid.h @@ -59,7 +59,7 @@ public: virtual bool get_next_ADR(bool restore_channel_mask, int8_t& dr_out, int8_t& tx_power_out, uint32_t& adr_ack_cnt); - virtual bool rx_config(rx_config_params_t* rxConfig, int8_t* datarate); + virtual bool rx_config(rx_config_params_t* rxConfig); virtual bool tx_config(tx_config_params_t* tx_config, int8_t* tx_power, lorawan_time_t* tx_toa); diff --git a/features/lorawan/lorastack/phy/lora_phy_ds.h b/features/lorawan/lorastack/phy/lora_phy_ds.h index 4a039ca974..b5de898fa6 100644 --- a/features/lorawan/lorastack/phy/lora_phy_ds.h +++ b/features/lorawan/lorastack/phy/lora_phy_ds.h @@ -564,7 +564,7 @@ typedef struct /*! * A pointer to the payload containing the MAC commands. */ - uint8_t* payload; + const uint8_t* payload; /*! * The size of the payload. */ diff --git a/features/lorawan/lorawan_types.h b/features/lorawan/lorawan_types.h index 834f88d441..d4c12c1d47 100644 --- a/features/lorawan/lorawan_types.h +++ b/features/lorawan/lorawan_types.h @@ -34,6 +34,40 @@ #include "platform/Callback.h" +/** + * Option Flags for send(), receive() APIs + */ +#define MSG_UNCONFIRMED_FLAG 0x01 +#define MSG_CONFIRMED_FLAG 0x02 +#define MSG_MULTICAST_FLAG 0x04 +#define MSG_PROPRIETARY_FLAG 0x08 + +/** + * Bit mask for message flags + */ + +#define MSG_FLAG_MASK 0x0F + +/** + * Mask for unconfirmed multicast message + */ +#define MSG_UNCONFIRMED_MULTICAST 0x05 + +/** + * Mask for confirmed multicast message + */ +#define MSG_CONFIRMED_MULTICAST 0x06 + +/** + * Mask for unconfirmed message proprietary message + */ +#define MSG_UNCONFIRMED_PROPRIETARY 0x09 + +/** + * Mask for confirmed proprietary message + */ +#define MSG_CONFIRMED_PROPRIETARY 0x0A + /** * LoRaWAN device classes definition. * diff --git a/features/lorawan/system/lorawan_data_structures.h b/features/lorawan/system/lorawan_data_structures.h index 7c1e9fd03e..a1465aadba 100644 --- a/features/lorawan/system/lorawan_data_structures.h +++ b/features/lorawan/system/lorawan_data_structures.h @@ -47,40 +47,6 @@ typedef uint32_t lorawan_time_t; // Radio wake-up time from sleep - unit ms. #define RADIO_WAKEUP_TIME 1 -/** - * Option Flags for send(), receive() APIs - */ -#define MSG_UNCONFIRMED_FLAG 0x01 -#define MSG_CONFIRMED_FLAG 0x02 -#define MSG_MULTICAST_FLAG 0x04 -#define MSG_PROPRIETARY_FLAG 0x08 - -/** - * Bit mask for message flags - */ - -#define MSG_FLAG_MASK 0x0F - -/** - * Mask for unconfirmed multicast message - */ -#define MSG_UNCONFIRMED_MULTICAST 0x05 - -/** - * Mask for confirmed multicast message - */ -#define MSG_CONFIRMED_MULTICAST 0x06 - -/** - * Mask for unconfirmed message proprietary message - */ -#define MSG_UNCONFIRMED_PROPRIETARY 0x09 - -/** - * Mask for confirmed proprietary message - */ -#define MSG_CONFIRMED_PROPRIETARY 0x0A - /*! * Sets the length of the LoRaMAC footer field. * Mainly indicates the MIC field length. @@ -570,7 +536,7 @@ typedef enum { /*! * The node has lost MAX_FCNT_GAP or more frames. */ - LORAMAC_EVENT_INFO_STATUS_DOWNLINK_TOO_MANY_FRAMES_LOSS, + LORAMAC_EVENT_INFO_STATUS_DOWNLINK_TOO_MANY_FRAMES_LOST, /*! * An address error occurred. */ @@ -585,46 +551,6 @@ typedef enum { LORAMAC_EVENT_INFO_STATUS_CRYPTO_FAIL, } loramac_event_info_status_t; -/*! - * LoRaMac service state flags. - */ -typedef union { - /*! - * Byte-access to the bits. - */ - uint8_t value; - /*! - * The structure containing single access to bits. - */ - struct mac_flag_bits_s - { - /*! - * MCPS-Req pending - */ - uint8_t mcps_req : 1; - /*! - * MCPS-Ind pending - */ - uint8_t mcps_ind : 1; - /*! - * MCPS-Ind pending. Skip indication to the application layer. - */ - uint8_t mcps_ind_skip : 1; - /*! - * MLME-Req pending - */ - uint8_t mlme_req : 1; - /*! - * MLME-Ind pending - */ - uint8_t mlme_ind : 1; - /*! - * MAC cycle done - */ - uint8_t mac_done : 1; - } bits; -} loramac_flags_t; - /*! * * \brief LoRaMAC data services @@ -639,14 +565,6 @@ typedef union { * \ref MCPS_MULTICAST | NO | YES | NO | NO * \ref MCPS_PROPRIETARY | YES | YES | NO | YES * - * The following table provides links to the function implementations of the - * related MCPS primitives: - * - * Primitive | Function - * ---------------- | :---------------------: - * MCPS-Request | LoRaMacMlmeRequest - * MCPS-Confirm | MacMcpsConfirm in \ref loramac_primitives_t - * MCPS-Indication | MacMcpsIndication in \ref loramac_primitives_t */ typedef enum { /*! @@ -714,6 +632,10 @@ typedef struct { * LoRaMAC MCPS-Indication primitive. */ typedef struct { + /*! + * True if an MCPS indication was pending + */ + bool pending; /*! * MCPS-Indication type. */ @@ -741,7 +663,7 @@ typedef struct { /*! * A pointer to the received data stream. */ - uint8_t *buffer; + const uint8_t *buffer; /*! * The size of the received data stream. */ @@ -787,14 +709,6 @@ typedef struct { * \ref MLME_TXCW | YES | NO | NO | YES * \ref MLME_SCHEDULE_UPLINK | NO | YES | NO | NO * - * The following table provides links to the function implementations of the - * related MLME primitives. - * - * Primitive | Function - * ---------------- | :---------------------: - * MLME-Request | LoRaMacMlmeRequest - * MLME-Confirm | MacMlmeConfirm in \ref loramac_primitives_t - * MLME-Indication | MacMlmeIndication in \ref loramac_primitives_t */ typedef enum { /*! @@ -879,6 +793,10 @@ typedef struct { * LoRaMAC MLME-Confirm primitive. */ typedef struct { + /*! + * Indicates if a request is pending or not + */ + bool pending; /*! * The previously performed MLME-Request. i.e., the request type * for which the confirmation is being generated @@ -915,41 +833,29 @@ typedef struct { * MLME-Indication type */ mlme_type_t indication_type; + bool pending; } loramac_mlme_indication_t; -/*! - * LoRaMAC events structure. - * Used to notify upper layers of MAC events. +/** + * End-device states. */ -typedef struct { - /*! - * \brief MCPS-Confirm primitive. - * - * \param [OUT] MCPS-Confirm parameters. - */ - mbed::Callback mcps_confirm; - - /*! - * \brief MCPS-Indication primitive. - * - * \param [OUT] MCPS-Indication parameters. - */ - mbed::Callback mcps_indication; - - /*! - * \brief MLME-Confirm primitive. - * - * \param [OUT] MLME-Confirm parameters. - */ - mbed::Callback mlme_confirm; - - /*! - * \brief MLME-Indication primitive - * - * \param [OUT] MLME-Indication parameters - */ - mbed::Callback mlme_indication; -}loramac_primitives_t; +typedef enum device_states { + DEVICE_STATE_NOT_INITIALIZED, + DEVICE_STATE_JOINING, + DEVICE_STATE_IDLE, + DEVICE_STATE_CONNECTING, + DEVICE_STATE_AWAITING_JOIN_ACCEPT, + DEVICE_STATE_RECEIVING, + DEVICE_STATE_CONNECTED, + DEVICE_STATE_SCHEDULING, + DEVICE_STATE_SENDING, + DEVICE_STATE_AWAITING_ACK, + DEVICE_STATE_STATUS_CHECK, +#if defined(LORAWAN_COMPLIANCE_TEST) + DEVICE_STATE_COMPLIANCE_TEST, +#endif + DEVICE_STATE_SHUTDOWN +} device_states_t; /** * Enumeration for LoRaWAN connection type. @@ -1090,70 +996,6 @@ typedef struct lorawan_session { uint32_t downlink_counter; } lorawan_session_t; -/** Structure containing the uplink status - * - */ -typedef struct { - /** Is acked - * - */ - uint8_t acked; - /** Uplink data rate - * - */ - int8_t datarate; - /** Uplink counter - * - */ - uint16_t uplink_counter; - /** Port is used by application - * - */ - uint8_t port; - /** Payload - * - */ - uint8_t *buffer; - /** Payload size - * - */ - uint8_t buffer_size; -} loramac_uplink_status_t; - -/** A structure containing the downlink status - * - */ -typedef struct { - /** RSSI of downlink - * - */ - int16_t rssi; - /** SNR of downlink - * - */ - int8_t snr; - /** Downlink counter - * - */ - uint16_t downlink_counter; - /** Is RX data received - * - */ - bool rx_data; - /** Port used by application - * - */ - uint8_t port; - /** Payload - * - */ - uint8_t *buffer; - /** Payload size - * - */ - uint8_t buffer_size; -} loramac_downlink_status_t; - /*! * The parameter structure for the function for regional rx configuration. */ @@ -1163,7 +1005,7 @@ typedef struct { */ uint8_t channel; /*! - * The RX datarate. + * The RX datarate index. */ uint8_t datarate; /*! @@ -1212,20 +1054,6 @@ typedef struct { int timer_id; } timer_event_t; -/*! - * LoRaMac internal states - */ -typedef enum { - LORAMAC_IDLE = 0x00000000, - LORAMAC_TX_RUNNING = 0x00000001, - LORAMAC_RX = 0x00000002, - LORAMAC_ACK_REQ = 0x00000004, - LORAMAC_ACK_RETRY = 0x00000008, - LORAMAC_TX_DELAYED = 0x00000010, - LORAMAC_TX_CONFIG = 0x00000020, - LORAMAC_RX_ABORT = 0x00000040, -} loramac_internal_state; - typedef struct { /*! * Device IEEE EUI @@ -1270,21 +1098,15 @@ typedef struct { */ lorawan_time_t mac_init_time; - /*! * Last transmission time on air */ lorawan_time_t tx_toa; /*! - * LoRaMac timer used to check the LoRaMacState (runs every second) + * LoRaMac duty cycle backoff timer */ - timer_event_t mac_state_check_timer; - - /*! - * LoRaMac duty cycle delayed Tx timer - */ - timer_event_t tx_delayed_timer; + timer_event_t backoff_timer; /*! * LoRaMac reception windows timers @@ -1375,24 +1197,24 @@ typedef struct { uint8_t ul_nb_rep_counter; /*! - * Buffer containing the data to be sent or received. + * TX buffer used for encrypted outgoing frames */ - uint8_t buffer[LORAMAC_PHY_MAXPAYLOAD]; + uint8_t tx_buffer[LORAMAC_PHY_MAXPAYLOAD]; /*! - * Length of packet in LoRaMacBuffer + * Length of TX buffer */ - uint16_t buffer_pkt_len; + uint16_t tx_buffer_len; /*! - * Buffer containing the upper layer data. + * Used for storing decrypted RX data. */ - uint8_t payload[LORAMAC_PHY_MAXPAYLOAD]; + uint8_t rx_buffer[LORAMAC_PHY_MAXPAYLOAD]; /*! - * Length of the payload in LoRaMacBuffer + * Length of the RX buffer */ - uint8_t payload_length; + uint8_t rx_buffer_len; /*! * Number of trials to get a frame acknowledged @@ -1419,11 +1241,6 @@ typedef struct { */ loramac_keys keys; - /*! - * LoRaMac tx/rx operation state - */ - loramac_flags_t flags; - /*! * Device nonce is a random value extracted by issuing a sequence of RSSI * measurements @@ -1457,11 +1274,6 @@ typedef struct { */ uint32_t adr_ack_counter; - /*! - * LoRaMac internal state - */ - uint32_t mac_state; - /*! * LoRaMac reception windows delay * \remark normal frame: RxWindowXDelay = ReceiveDelayX - RADIO_WAKEUP_TIME