/** / _____) _ | | ( (____ _____ ____ _| |_ _____ ____| |__ \____ \| ___ | (_ _) ___ |/ ___) _ \ _____) ) ____| | | || |_| ____( (___| | | | (______/|_____)_|_|_| \__)_____)\____)_| |_| (C)2013 Semtech ___ _____ _ ___ _ _____ ___ ___ ___ ___ / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| embedded.connectivity.solutions=============== Description: LoRaWAN stack layer that controls both MAC and PHY underneath License: Revised BSD License, see LICENSE.TXT file include in the project Maintainer: Miguel Luis ( Semtech ), Gregory Cristian ( Semtech ) and Daniel Jaeckle ( STACKFORCE ) Copyright (c) 2017, Arm Limited and affiliates. SPDX-License-Identifier: BSD-3-Clause */ #include #include #include "platform/Callback.h" #include "events/EventQueue.h" #include "lorawan/LoRaWANStack.h" #if defined(FEATURE_COMMON_PAL) #include "mbed_trace.h" #define TRACE_GROUP "LSTK" #else #define tr_debug(...) (void(0)) //dummies if feature common pal is not added #define tr_info(...) (void(0)) //dummies if feature common pal is not added #define tr_error(...) (void(0)) //dummies if feature common pal is not added #define tr_warn(...) (void(0)) //dummies if feature common pal is not added #endif //defined(FEATURE_COMMON_PAL) #define INVALID_PORT 0xFF #define MAX_CONFIRMED_MSG_RETRIES 255 using namespace mbed; using namespace events; /** * Helper function prototypes */ static Mcps_t interpret_mcps_confirm_type(const lora_mac_mcps_t& local); static Mib_t interpret_mib_req_confirm_type(const lora_mac_mib_t& mib_local); static lora_mac_event_info_status_t interpret_event_info_type(const LoRaMacEventInfoStatus_t& remote); #if defined(LORAWAN_COMPLIANCE_TEST) /** * * User application data buffer size if compliance test is used */ #if (MBED_CONF_LORA_PHY == 0 || MBED_CONF_LORA_PHY == 4 || MBED_CONF_LORA_PHY == 6 || MBED_CONF_LORA_PHY == 7) #define LORAWAN_COMPLIANCE_TEST_DATA_SIZE 16 #elif (MBED_CONF_LORA_PHY == 1 || MBED_CONF_LORA_PHY == 2 || MBED_CONF_LORA_PHY == 8 || MBED_CONF_LORA_PHY == 9) #define LORAWAN_COMPLIANCE_TEST_DATA_SIZE 11 #else #error "Must set LoRa PHY layer parameters." #endif #endif /***************************************************************************** * Private Member Functions * ****************************************************************************/ bool LoRaWANStack::is_port_valid(uint8_t port) { //Application should not use reserved and illegal port numbers. if (port >= 224 || port == 0) { return false; } else { return true; } } lora_mac_status_t LoRaWANStack::set_application_port(uint8_t port) { if (is_port_valid(port)) { _app_port = port; return LORA_MAC_STATUS_OK; } return LORA_MAC_STATUS_PORT_INVALID; } /***************************************************************************** * Constructor and destructor * ****************************************************************************/ LoRaWANStack::LoRaWANStack() : _loramac(_lora_time), _lora_phy(_lora_time), _device_current_state(DEVICE_STATE_NOT_INITIALIZED), _mac_handlers(NULL), _num_retry(1), _queue(NULL), _duty_cycle_on(LORAWAN_DUTYCYCLE_ON) { #ifdef MBED_CONF_LORA_APP_PORT // is_port_valid() is not virtual, so we can call it in constructor if (is_port_valid(MBED_CONF_LORA_APP_PORT)) { _app_port = MBED_CONF_LORA_APP_PORT; } else { tr_error("User defined port in .json is illegal."); _app_port = INVALID_PORT; } #else // initialize it to INVALID_PORT (255) an illegal port number. // user should set the port _app_port = INVALID_PORT; #endif memset(&_lw_session, 0, sizeof(_lw_session)); memset(&_tx_msg, 0, sizeof(_tx_msg)); memset(&_rx_msg, 0, sizeof(_rx_msg)); } LoRaWANStack::~LoRaWANStack() { } /***************************************************************************** * Public member functions * ****************************************************************************/ LoRaWANStack& LoRaWANStack::get_lorawan_stack() { static LoRaWANStack _lw_stack; return _lw_stack; } radio_events_t *LoRaWANStack::bind_radio_driver(LoRaRadio& radio) { // Store pointer to callback routines inside MAC layer (non-IRQ safe) _mac_handlers = _loramac.GetPhyEventHandlers(); // passes the reference to radio driver down to PHY layer _lora_phy.set_radio_instance(radio); return _mac_handlers; } lora_mac_status_t LoRaWANStack::initialize_mac_layer(EventQueue *queue) { if (DEVICE_STATE_NOT_INITIALIZED != _device_current_state) { tr_debug("Initialized already"); return LORA_MAC_STATUS_OK; } static LoRaMacPrimitives_t LoRaMacPrimitives; static LoRaMacCallback_t LoRaMacCallbacks; static lora_mac_mib_request_confirm_t mib_req; #if defined(LORAWAN_COMPLIANCE_TEST) static uint8_t compliance_test_buffer[LORAWAN_TX_MAX_SIZE]; #endif tr_debug("Initializing MAC layer"); //store a pointer to Event Queue _queue = queue; #if defined(LORAWAN_COMPLIANCE_TEST) // Allocate memory for compliance test _compliance_test.app_data_buffer = compliance_test_buffer; #endif _lora_time.TimerTimeCounterInit(queue); LoRaMacPrimitives.MacMcpsConfirm = callback(this, &LoRaWANStack::mcps_confirm); LoRaMacPrimitives.MacMcpsIndication = callback(this, &LoRaWANStack::mcps_indication); LoRaMacPrimitives.MacMlmeConfirm = callback(this, &LoRaWANStack::mlme_confirm); LoRaMacPrimitives.MacMlmeIndication = callback(this, &LoRaWANStack::mlme_indication); _loramac.LoRaMacInitialization(&LoRaMacPrimitives, &LoRaMacCallbacks, &_lora_phy, queue); mib_req.type = LORA_MIB_ADR; mib_req.param.adr_enable = LORAWAN_ADR_ON; mib_set_request(&mib_req); mib_req.type = LORA_MIB_PUBLIC_NETWORK; mib_req.param.enable_public_network = LORAWAN_PUBLIC_NETWORK; mib_set_request(&mib_req); // Reset counters to zero. Will change in future with 1.1 support. _lw_session.downlink_counter = 0; _lw_session.uplink_counter = 0; // Start loRaWAN state machine. set_device_state(DEVICE_STATE_INIT); return lora_state_machine(); } #if defined(LORAWAN_COMPLIANCE_TEST) /** * * Prepares the upload message to reserved ports * * \param port Application port */ void LoRaWANStack::prepare_special_tx_frame(uint8_t port) { if (port == 224) { // Clear any normal message stuff before compliance test. memset(&_tx_msg, 0, sizeof(_tx_msg)); if (_compliance_test.link_check == true) { _compliance_test.link_check = false; _compliance_test.state = 1; _tx_msg.f_buffer_size = 3; _tx_msg.f_buffer[0] = 5; _tx_msg.f_buffer[1] = _compliance_test.demod_margin; _tx_msg.f_buffer[2] = _compliance_test.nb_gateways; } else { switch (_compliance_test.state) { case 4: _compliance_test.state = 1; _tx_msg.f_buffer_size = _compliance_test.app_data_size; _tx_msg.f_buffer[0] = _compliance_test.app_data_buffer[0]; for(uint8_t i = 1; i < MIN(_compliance_test.app_data_size, LORAWAN_TX_MAX_SIZE); ++i) { _tx_msg.f_buffer[i] = _compliance_test.app_data_buffer[i]; } break; case 1: _tx_msg.f_buffer_size = 2; _tx_msg.f_buffer[0] = _compliance_test.downlink_counter >> 8; _tx_msg.f_buffer[1] = _compliance_test.downlink_counter; break; } } } } /** Hands over the compliance test frame to MAC layer * * \return returns the state of the LoRa MAC */ lora_mac_status_t LoRaWANStack::send_compliance_test_frame_to_mac() { lora_mac_mcps_req_t mcps_req; GetPhyParams_t phy_params; PhyParam_t default_datarate; phy_params.Attribute = PHY_DEF_TX_DR; default_datarate = _lora_phy.get_phy_params(&phy_params); prepare_special_tx_frame(_compliance_test.app_port); if (!_compliance_test.is_tx_confirmed) { mcps_req.type = LORA_MCPS_UNCONFIRMED; mcps_req.req.unconfirmed.f_port = _compliance_test.app_port; mcps_req.f_buffer = _tx_msg.f_buffer; mcps_req.f_buffer_size = _tx_msg.f_buffer_size; mcps_req.req.unconfirmed.datarate = default_datarate.Value; tr_info("Transmit unconfirmed compliance test frame %d bytes.", mcps_req.f_buffer_size); for (uint8_t i = 0; i < mcps_req.f_buffer_size; ++i) { tr_info("Byte %d, data is 0x%x", i+1, ((uint8_t*)mcps_req.f_buffer)[i]); } } else if (_compliance_test.is_tx_confirmed) { mcps_req.type = LORA_MCPS_CONFIRMED; mcps_req.req.confirmed.f_port = _compliance_test.app_port; mcps_req.f_buffer = _tx_msg.f_buffer; mcps_req.f_buffer_size = _tx_msg.f_buffer_size; mcps_req.req.confirmed.nb_trials = _num_retry; mcps_req.req.confirmed.datarate = default_datarate.Value; tr_info("Transmit confirmed compliance test frame %d bytes.", mcps_req.f_buffer_size); for (uint8_t i = 0; i < mcps_req.f_buffer_size; ++i) { tr_info("Byte %d, data is 0x%x", i+1, ((uint8_t*)mcps_req.f_buffer)[i]); } } else { return LORA_MAC_STATUS_SERVICE_UNKNOWN; } return mcps_request_handler(&mcps_req); } #endif uint16_t LoRaWANStack::check_possible_tx_size(uint16_t size) { LoRaMacTxInfo_t txInfo; if (_loramac.LoRaMacQueryTxPossible(size, &txInfo) == LORAMAC_STATUS_LENGTH_ERROR) { // Cannot transmit this much. Return how much data can be sent // at the moment return txInfo.MaxPossiblePayload; } return txInfo.CurrentPayloadSize; } /** Hands over the frame to MAC layer * * \return returns the state of the LoRa MAC */ lora_mac_status_t LoRaWANStack::send_frame_to_mac() { lora_mac_mcps_req_t mcps_req; lora_mac_status_t status; lora_mac_mib_request_confirm_t mib_get_params; GetPhyParams_t phy_params; PhyParam_t default_datarate; phy_params.Attribute = PHY_DEF_TX_DR; default_datarate = _lora_phy.get_phy_params(&phy_params); mcps_req.type = _tx_msg.type; if (LORA_MCPS_UNCONFIRMED == mcps_req.type) { mcps_req.req.unconfirmed.f_port = _tx_msg.message_u.unconfirmed.f_port; mcps_req.f_buffer = _tx_msg.f_buffer; mcps_req.f_buffer_size = _tx_msg.f_buffer_size; mcps_req.req.unconfirmed.datarate = default_datarate.Value; } else if (LORA_MCPS_CONFIRMED == mcps_req.type) { mcps_req.req.confirmed.f_port = _tx_msg.message_u.confirmed.f_port; mcps_req.f_buffer = _tx_msg.f_buffer; mcps_req.f_buffer_size = _tx_msg.f_buffer_size; mcps_req.req.confirmed.nb_trials = _tx_msg.message_u.confirmed.nb_trials; mib_get_params.type = LORA_MIB_CHANNELS_DATARATE; if(mib_get_request(&mib_get_params) != LORA_MAC_STATUS_OK) { tr_debug("Couldn't get MIB parameters: Using default data rate"); mcps_req.req.confirmed.datarate = default_datarate.Value; } else { mcps_req.req.confirmed.datarate = mib_get_params.param.channels_datarate; } } else if (LORA_MCPS_PROPRIETARY == mcps_req.type) { mcps_req.f_buffer = _tx_msg.f_buffer; mcps_req.f_buffer_size = _tx_msg.f_buffer_size; mcps_req.req.proprietary.datarate = default_datarate.Value; } else { return LORA_MAC_STATUS_SERVICE_UNKNOWN; } status = mcps_request_handler(&mcps_req); return status; } lora_mac_status_t LoRaWANStack::set_confirmed_msg_retry(uint8_t count) { if (count >= MAX_CONFIRMED_MSG_RETRIES) { return LORA_MAC_STATUS_PARAMETER_INVALID; } _num_retry = count; return LORA_MAC_STATUS_OK; } void LoRaWANStack::set_device_state(device_states_t new_state) { _device_current_state = new_state; } /** Wrapper function to MCPS-Confirm event function * * \param mcps_confirm Pointer to the confirm structure, * containing confirm attributes. */ void LoRaWANStack::mcps_confirm(McpsConfirm_t *mcps_confirm) { lora_mac_mcps_confirm_t lora_mcps_confirm; lora_mcps_confirm.ack_received = mcps_confirm->AckReceived; lora_mcps_confirm.nb_retries = mcps_confirm->NbRetries; lora_mcps_confirm.datarate = mcps_confirm->Datarate; lora_mcps_confirm.tx_power = mcps_confirm->TxPower; lora_mcps_confirm.uplink_counter = mcps_confirm->UpLinkCounter; lora_mcps_confirm.uplink_frequency = mcps_confirm->UpLinkFrequency; // Interprets from Mcps_t to lora_mac_mcps_t mcps_confirm->McpsRequest = interpret_mcps_confirm_type(lora_mcps_confirm.mcps_request); // Interprets from LoRaMacEventInfoStatus_t to lora_mac_event_info_status_t lora_mcps_confirm.status = interpret_event_info_type(mcps_confirm->Status); lora_mcps_confirm.tx_time_on_air = mcps_confirm->TxTimeOnAir; mcps_confirm_handler(&lora_mcps_confirm); } /** Wrapper function to MCPS-Indication event function * * \param mcps_indication Pointer to the indication structure, * containing indication attributes. */ void LoRaWANStack::mcps_indication(McpsIndication_t *mcps_indication) { lora_mac_mcps_indication_t lora_mcps_indication; lora_mcps_indication.ack_received = mcps_indication->AckReceived; memcpy(lora_mcps_indication.buffer, mcps_indication->Buffer, mcps_indication->BufferSize); lora_mcps_indication.buffer_size = mcps_indication->BufferSize; lora_mcps_indication.downlink_counter = mcps_indication->DownLinkCounter; lora_mcps_indication.frame_pending = mcps_indication->FramePending; lora_mcps_indication.mcps_indication = (lora_mac_mcps_t)mcps_indication->McpsIndication; lora_mcps_indication.multicast = mcps_indication->Multicast; lora_mcps_indication.port = mcps_indication->Port; lora_mcps_indication.rssi = mcps_indication->Rssi; lora_mcps_indication.rx_data = mcps_indication->RxData; lora_mcps_indication.rx_datarate = mcps_indication->RxDatarate; lora_mcps_indication.rx_slot = mcps_indication->RxSlot; lora_mcps_indication.snr = mcps_indication->Snr; lora_mcps_indication.status = (lora_mac_event_info_status_t)mcps_indication->Status; mcps_indication_handler(&lora_mcps_indication); } /** Wrapper function to MLME-Confirm event function * * \param mlme_confirm Pointer to the confirm structure, * containing confirm attributes. */ void LoRaWANStack::mlme_confirm(MlmeConfirm_t *mlme_confirm) { lora_mac_mlme_confirm_t lora_mlme_confirm; lora_mlme_confirm.demod_margin = mlme_confirm->DemodMargin; lora_mlme_confirm.mlme_request = (lora_mac_mlme_t)mlme_confirm->MlmeRequest; lora_mlme_confirm.nb_gateways = mlme_confirm->NbGateways; lora_mlme_confirm.nb_retries = mlme_confirm->NbRetries; lora_mlme_confirm.status = (lora_mac_event_info_status_t)mlme_confirm->Status; lora_mlme_confirm.tx_time_on_air = mlme_confirm->TxTimeOnAir; mlme_confirm_handler(&lora_mlme_confirm); } /*! * \brief MLME-Indication event function * * \param [IN] mlmeIndication - Pointer to the indication structure. */ void LoRaWANStack::mlme_indication( MlmeIndication_t *mlmeIndication ) { switch( mlmeIndication->MlmeIndication ) { case MLME_SCHEDULE_UPLINK: {// The MAC signals that we shall provide an uplink as soon as possible // TODO: Sending implementation missing and will be implemented using // another task. //OnTxNextPacketTimerEvent( ); break; } default: break; } } void LoRaWANStack::set_lora_callbacks(lorawan_app_callbacks_t *cbs) { if (cbs) { if (cbs->events) { _callbacks.events = cbs->events; } if (cbs->link_check_resp) { _callbacks.link_check_resp = cbs->link_check_resp; } } } lora_mac_status_t LoRaWANStack::add_channels(const lora_channelplan_t &channel_plan) { // If device is not initialized, stop right away if (_device_current_state == DEVICE_STATE_NOT_INITIALIZED) { tr_error("Stack not initialized!"); return LORA_MAC_STATUS_NOT_INITIALIZED; } ChannelParams_t mac_layer_ch_params; LoRaMacStatus_t status; GetPhyParams_t get_phy; PhyParam_t phy_param; uint8_t max_num_channels; // Check first how many channels the selected PHY layer supports get_phy.Attribute = PHY_MAX_NB_CHANNELS; phy_param = _lora_phy.get_phy_params(&get_phy); max_num_channels = (uint8_t) phy_param.Value; // check if user is setting more channels than supported if (channel_plan.nb_channels > max_num_channels) { return LORA_MAC_STATUS_PARAMETER_INVALID; } for (uint8_t i = 0; i < channel_plan.nb_channels; i++) { mac_layer_ch_params.Band = channel_plan.channels[i].ch_param.band; mac_layer_ch_params.DrRange.Fields.Max = channel_plan.channels[i].ch_param.dr_range.lora_mac_fields_s.max; mac_layer_ch_params.DrRange.Fields.Min = channel_plan.channels[i].ch_param.dr_range.lora_mac_fields_s.min; mac_layer_ch_params.DrRange.Value = channel_plan.channels[i].ch_param.dr_range.value; mac_layer_ch_params.Frequency = channel_plan.channels[i].ch_param.frequency; mac_layer_ch_params.Rx1Frequency =channel_plan.channels[i].ch_param.rx1_frequency; status = _loramac.LoRaMacChannelAdd(channel_plan.channels[i].id, mac_layer_ch_params); if (status != LORAMAC_STATUS_OK) { return error_type_converter(status); } } return LORA_MAC_STATUS_OK; } lora_mac_status_t LoRaWANStack::drop_channel_list() { if (_device_current_state == DEVICE_STATE_NOT_INITIALIZED ) { tr_error("Stack not initialized!"); return LORA_MAC_STATUS_NOT_INITIALIZED; } lora_mac_status_t status = LORA_MAC_STATUS_OK; GetPhyParams_t get_phy; PhyParam_t phy_param; uint8_t max_num_channels; uint16_t *channel_masks; uint16_t *default_channel_masks; // Check first how many channels the selected PHY layer supports get_phy.Attribute = PHY_MAX_NB_CHANNELS; phy_param = _lora_phy.get_phy_params(&get_phy); max_num_channels = (uint8_t) phy_param.Value; // Now check the channel mask for enabled channels get_phy.Attribute = PHY_CHANNELS_MASK; phy_param = _lora_phy.get_phy_params(&get_phy); channel_masks = phy_param.ChannelsMask; // Now check the channel mask for default channels get_phy.Attribute = PHY_CHANNELS_DEFAULT_MASK; phy_param = _lora_phy.get_phy_params(&get_phy); default_channel_masks = phy_param.ChannelsMask; for (uint8_t i = 0; i < max_num_channels; i++) { // skip any default channels if ((default_channel_masks[0] & (1U<= max_num_channels) { return LORA_MAC_STATUS_PARAMETER_INVALID; } // Now check the Default channel mask get_phy.Attribute = PHY_CHANNELS_DEFAULT_MASK; phy_param = _lora_phy.get_phy_params(&get_phy); channel_masks = phy_param.ChannelsMask; // check if the channel ID give belongs to a default channel // Mostly the default channels are in the first mask if the region // have multiple channel masks for various sub-bands. So we check the first // mask only and return an error code if user sent a default channel id if ((channel_masks[0] & (1U << channel_id)) != 0) { tr_error("Not allowed to remove a Default Channel."); return LORA_MAC_STATUS_PARAMETER_INVALID; } return error_type_converter(_loramac.LoRaMacChannelRemove(channel_id)); } lora_mac_status_t LoRaWANStack::get_enabled_channels(lora_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 LORA_MAC_STATUS_BUSY; } lora_mac_mib_request_confirm_t mib_params; GetPhyParams_t get_phy; PhyParam_t phy_param; uint8_t max_num_channels; uint16_t *channel_masks; uint8_t count = 0; // Check first how many channels the selected PHY layer supports get_phy.Attribute = PHY_MAX_NB_CHANNELS; phy_param = _lora_phy.get_phy_params(&get_phy); max_num_channels = (uint8_t) phy_param.Value; // Now check the Default channel mask get_phy.Attribute = PHY_CHANNELS_MASK; phy_param = _lora_phy.get_phy_params(&get_phy); channel_masks = phy_param.ChannelsMask; // Request Mib to get channels memset(&mib_params, 0, sizeof(mib_params)); mib_params.type = LORA_MIB_CHANNELS; mib_get_request(&mib_params); for (uint8_t i = 0; i < max_num_channels; i++) { // skip the channels which are not enabled if ((channel_masks[0] & (1U << i)) == 0) { continue; } // otherwise add them to the channel_plan struct channel_plan.channels[count].id = i; channel_plan.channels[count].ch_param.frequency = mib_params.param.channel_list[i].frequency; channel_plan.channels[count].ch_param.dr_range.value = mib_params.param.channel_list[i].dr_range.value; channel_plan.channels[count].ch_param.dr_range.lora_mac_fields_s.min = mib_params.param.channel_list[i].dr_range.lora_mac_fields_s.min; channel_plan.channels[count].ch_param.dr_range.lora_mac_fields_s.max = mib_params.param.channel_list[i].dr_range.lora_mac_fields_s.max; channel_plan.channels[count].ch_param.band = mib_params.param.channel_list[i].band; channel_plan.channels[count].ch_param.rx1_frequency = mib_params.param.channel_list[i].rx1_frequency; count++; } channel_plan.nb_channels = count; return LORA_MAC_STATUS_OK; } lora_mac_status_t LoRaWANStack::enable_adaptive_datarate(bool adr_enabled) { if (DEVICE_STATE_NOT_INITIALIZED == _device_current_state) { tr_error("Stack not initialized!"); return LORA_MAC_STATUS_NOT_INITIALIZED; } lora_mac_mib_request_confirm_t adr_mib_params; adr_mib_params.type = LORA_MIB_ADR; adr_mib_params.param.adr_enable = adr_enabled; return mib_set_request(&adr_mib_params); } lora_mac_status_t LoRaWANStack::set_channel_data_rate(uint8_t data_rate) { if (DEVICE_STATE_NOT_INITIALIZED == _device_current_state) { tr_error("Stack not initialized!"); return LORA_MAC_STATUS_NOT_INITIALIZED; } lora_mac_mib_request_confirm_t mib_params; mib_params.type = LORA_MIB_ADR; if (mib_get_request(&mib_params) == true) { tr_error("Cannot set data rate. Please turn off ADR first."); return LORA_MAC_STATUS_PARAMETER_INVALID; } mib_params.type = LORA_MIB_CHANNELS_DATARATE; mib_params.param.channels_datarate = data_rate; return mib_set_request(&mib_params); } void LoRaWANStack::commission_device(const lora_dev_commission_t &commission_data) { _lw_session.connection.connect_type = commission_data.connection.connect_type; _lw_session.downlink_counter = commission_data.downlink_counter; _lw_session.uplink_counter = commission_data.uplink_counter; if (commission_data.connection.connect_type == LORAWAN_CONNECTION_OTAA) { _lw_session.connection.connection_u.otaa.app_eui = commission_data.connection.connection_u.otaa.app_eui; _lw_session.connection.connection_u.otaa.app_key = commission_data.connection.connection_u.otaa.app_key; _lw_session.connection.connection_u.otaa.dev_eui = commission_data.connection.connection_u.otaa.dev_eui; _lw_session.connection.connection_u.otaa.nb_trials = commission_data.connection.connection_u.otaa.nb_trials; } else { _lw_session.connection.connection_u.abp.dev_addr = commission_data.connection.connection_u.abp.dev_addr; _lw_session.connection.connection_u.abp.nwk_skey = commission_data.connection.connection_u.abp.nwk_skey; _lw_session.connection.connection_u.abp.app_skey = commission_data.connection.connection_u.abp.app_skey; } } /** * * Join OTAA */ lora_mac_status_t LoRaWANStack::join_request_by_otaa(const lorawan_connect_t ¶ms) { lora_dev_commission_t commission; if (DEVICE_STATE_NOT_INITIALIZED == _device_current_state) { tr_error("Stack not initialized!"); return LORA_MAC_STATUS_NOT_INITIALIZED; } tr_debug("Initiating OTAA"); commission.connection.connect_type = LORAWAN_CONNECTION_OTAA; commission.connection.connection_u.otaa.dev_eui = params.connection_u.otaa.dev_eui; commission.connection.connection_u.otaa.app_eui = params.connection_u.otaa.app_eui; commission.connection.connection_u.otaa.app_key = params.connection_u.otaa.app_key; commission.connection.connection_u.otaa.nb_trials = params.connection_u.otaa.nb_trials; // 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. commission.downlink_counter = 0; commission.uplink_counter = 0; commission_device(commission); set_device_state(DEVICE_STATE_JOINING); return lora_state_machine(); } /** * * Connect ABP */ lora_mac_status_t LoRaWANStack::activation_by_personalization(const lorawan_connect_t ¶ms) { lora_mac_status_t status; lora_dev_commission_t commission; if (DEVICE_STATE_NOT_INITIALIZED == _device_current_state) { tr_error("Stack not initialized!"); return LORA_MAC_STATUS_NOT_INITIALIZED; } tr_debug("Initiating ABP"); commission.connection.connect_type = LORAWAN_CONNECTION_ABP; commission.connection.connection_u.abp.dev_addr = params.connection_u.abp.dev_addr; commission.connection.connection_u.abp.nwk_skey = params.connection_u.abp.nwk_skey; commission.connection.connection_u.abp.app_skey = params.connection_u.abp.app_skey; // 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, so we try a last ditch effort here to restore correct // frame counters. If that doesn't work, user must manually reset frame // counters on their network server. commission.downlink_counter = _lw_session.downlink_counter; commission.uplink_counter = _lw_session.uplink_counter; tr_debug("Frame Counters. UpCnt=%lu, DownCnt=%lu", commission.uplink_counter, commission.downlink_counter); commission_device(commission); set_device_state(DEVICE_STATE_ABP_CONNECTING); status = lora_state_machine(); return status; } int16_t LoRaWANStack::handle_tx(uint8_t port, const uint8_t* data, uint16_t length, uint8_t flags) { if (!_lw_session.active) { return LORA_MAC_STATUS_NO_ACTIVE_SESSIONS; } if (_tx_msg.tx_ongoing) { return LORA_MAC_STATUS_WOULD_BLOCK; } #if defined(LORAWAN_COMPLIANCE_TEST) if (_compliance_test.running) { return LORA_MAC_STATUS_COMPLIANCE_TEST_ON; } #endif lora_mac_mib_request_confirm_t mib_req; lora_mac_status_t status; mib_req.type = LORA_MIB_NETWORK_JOINED; status = mib_get_request(&mib_req); if (status == LORA_MAC_STATUS_OK) { if (mib_req.param.is_network_joined == false) { return LORA_MAC_STATUS_NO_NETWORK_JOINED; } } status = set_application_port(port); if (status != LORA_MAC_STATUS_OK) { tr_error("Illegal application port definition."); return status; } if (flags == 0 || (flags & MSG_FLAG_MASK) == (MSG_CONFIRMED_FLAG|MSG_UNCONFIRMED_FLAG)) { tr_error("CONFIRMED and UNCONFIRMED are mutually exclusive for send()"); return LORA_MAC_STATUS_PARAMETER_INVALID; } _tx_msg.port = port; uint16_t max_possible_size = check_possible_tx_size(length); if (max_possible_size > LORAWAN_TX_MAX_SIZE) { // LORAWAN_APP_DATA_MAX_SIZE should at least be // either equal to or bigger than maximum possible // tx size because our tx message buffer takes its // length from that macro. Force maximum possible tx unit // to be equal to the buffer size user chose. max_possible_size = LORAWAN_TX_MAX_SIZE; } if (max_possible_size < length) { tr_info("Cannot transmit %d bytes. Possible TX Size is %d bytes", length, max_possible_size); _tx_msg.pending_size = length - max_possible_size; _tx_msg.f_buffer_size = max_possible_size; // copy user buffer upto the max_possible_size memcpy(_tx_msg.f_buffer, data, _tx_msg.f_buffer_size); } else { // Whole message can be sent at one time _tx_msg.f_buffer_size = length; _tx_msg.pending_size = 0; // copy user buffer upto the max_possible_size if (data && length > 0) { memcpy(_tx_msg.f_buffer, data, length); } } // Handles all unconfirmed messages, including proprietary and multicast if ((flags & MSG_FLAG_MASK) == MSG_UNCONFIRMED_FLAG || (flags & MSG_FLAG_MASK) == MSG_UNCONFIRMED_MULTICAST || (flags & MSG_FLAG_MASK) == MSG_UNCONFIRMED_PROPRIETARY) { _tx_msg.type = LORA_MCPS_UNCONFIRMED; _tx_msg.message_u.unconfirmed.f_port = _app_port; } // Handles all confirmed messages, including proprietary and multicast if ((flags & MSG_FLAG_MASK) == MSG_CONFIRMED_FLAG || (flags & MSG_FLAG_MASK) == MSG_CONFIRMED_MULTICAST || (flags & MSG_FLAG_MASK) == MSG_CONFIRMED_PROPRIETARY) { _tx_msg.type = LORA_MCPS_CONFIRMED; _tx_msg.message_u.confirmed.f_port = _app_port; _tx_msg.message_u.confirmed.nb_trials = _num_retry; } tr_info("RTS = %u bytes, PEND = %u", _tx_msg.f_buffer_size, _tx_msg.pending_size); set_device_state(DEVICE_STATE_SEND); lora_state_machine(); // send user the length of data which is scheduled now. // user should take care of the pending data. return _tx_msg.f_buffer_size; } int16_t LoRaWANStack::handle_rx(const uint8_t port, uint8_t* data, uint16_t length, uint8_t flags) { if (!_lw_session.active) { return LORA_MAC_STATUS_NO_ACTIVE_SESSIONS; } // No messages to read. if (!_rx_msg.receive_ready) { return LORA_MAC_STATUS_WOULD_BLOCK; } #if defined(LORAWAN_COMPLIANCE_TEST) if (_compliance_test.running) { return LORA_MAC_STATUS_COMPLIANCE_TEST_ON; } #endif if (data == NULL) { return LORA_MAC_STATUS_PARAMETER_INVALID; } uint8_t *base_ptr = _rx_msg.rx_message.mcps_indication.buffer; uint16_t base_size = _rx_msg.rx_message.mcps_indication.buffer_size; bool read_complete = false; if (_rx_msg.rx_message.mcps_indication.port != port) { // Nothing yet received for this particular port return LORA_MAC_STATUS_WOULD_BLOCK; } // check if message received is a Confirmed message and user subscribed to it or not if (_rx_msg.rx_message.mcps_indication.mcps_indication == LORA_MCPS_CONFIRMED && ((flags & MSG_FLAG_MASK) == MSG_CONFIRMED_FLAG || (flags & MSG_FLAG_MASK) == MSG_CONFIRMED_MULTICAST || (flags & MSG_FLAG_MASK) == MSG_CONFIRMED_PROPRIETARY)) { tr_debug("RX - Confirmed Message, flags=%d", flags); } // check if message received is a Unconfirmed message and user subscribed to it or not if (_rx_msg.rx_message.mcps_indication.mcps_indication == LORA_MCPS_UNCONFIRMED && ((flags & MSG_FLAG_MASK) == MSG_UNCONFIRMED_FLAG || (flags & MSG_FLAG_MASK) == MSG_UNCONFIRMED_MULTICAST || (flags & MSG_FLAG_MASK) == MSG_UNCONFIRMED_PROPRIETARY)) { tr_debug("RX - Unconfirmed Message - flags=%d", flags); } // check the length of received message whether we can fit into user // buffer completely or not if (_rx_msg.rx_message.mcps_indication.buffer_size > length && _rx_msg.prev_read_size == 0) { // we can't fit into user buffer. Invoke counter measures _rx_msg.pending_size = _rx_msg.rx_message.mcps_indication.buffer_size - length; base_size = length; _rx_msg.prev_read_size = base_size; memcpy(data, base_ptr, base_size); } else if (_rx_msg.prev_read_size == 0) { _rx_msg.pending_size = 0; _rx_msg.prev_read_size = 0; memcpy(data, base_ptr, base_size); read_complete = true; } // If its the pending read then we should copy only the remaining part of // the buffer. Due to checks above, in case of a pending read, this block // will be the only one to get invoked if (_rx_msg.pending_size > 0 && _rx_msg.prev_read_size > 0) { memcpy(data, base_ptr+_rx_msg.prev_read_size, base_size); } // we are done handing over received buffer to user. check if there is // anything pending. If not, memset the buffer to zero and indicate // that no read is in progress if (read_complete) { memset(_rx_msg.rx_message.mcps_indication.buffer, 0, LORAMAC_PHY_MAXPAYLOAD); _rx_msg.receive_ready = false; } return base_size; } lora_mac_status_t LoRaWANStack::mlme_request_handler(lora_mac_mlme_req_t *mlme_request) { MlmeReq_t request; if (NULL == mlme_request) { return LORA_MAC_STATUS_PARAMETER_INVALID; } request.Type = (Mlme_t)mlme_request->type; switch (mlme_request->type) { case LORA_MLME_JOIN: request.Req.Join.AppEui = mlme_request->req.join.app_eui; request.Req.Join.AppKey = mlme_request->req.join.app_key; request.Req.Join.DevEui = mlme_request->req.join.dev_eui; request.Req.Join.NbTrials = mlme_request->req.join.nb_trials; break; // This is handled in semtech stack. Only type value is needed. case LORA_MLME_LINK_CHECK: break; case LORA_MLME_TXCW: /* no break */ /* Fall through */ case LORA_MLME_TXCW_1: request.Req.TxCw.Frequency = mlme_request->req.tx_cw.frequency; request.Req.TxCw.Power = mlme_request->req.tx_cw.power; request.Req.TxCw.Timeout = mlme_request->req.tx_cw.timeout; break; default: return LORA_MAC_STATUS_SERVICE_UNKNOWN; break; } return error_type_converter(_loramac.LoRaMacMlmeRequest(&request)); } /** MLME-Confirm event function * * \param mlme_confirm Pointer to the confirm structure, * containing confirm attributes. */ void LoRaWANStack::mlme_confirm_handler(lora_mac_mlme_confirm_t *mlme_confirm) { if (NULL == mlme_confirm) { return; } switch (mlme_confirm->mlme_request) { case LORA_MLME_JOIN: if (mlme_confirm->status == LORA_EVENT_INFO_STATUS_OK) { // Status is OK, node has joined the network set_device_state(DEVICE_STATE_JOINED); lora_state_machine(); } else { // Join attempt failed. set_device_state(DEVICE_STATE_IDLE); lora_state_machine(); if (_callbacks.events) { _queue->call(_callbacks.events, JOIN_FAILURE); } } break; case LORA_MLME_LINK_CHECK: if (mlme_confirm->status == LORA_EVENT_INFO_STATUS_OK) { // Check DemodMargin // Check NbGateways #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 { // normal operation as oppose to compliance testing if (_callbacks.link_check_resp) { _queue->call(_callbacks.link_check_resp, mlme_confirm->demod_margin, mlme_confirm->nb_gateways); } } } break; default: return; break; } } lora_mac_status_t LoRaWANStack::mcps_request_handler(lora_mac_mcps_req_t *mcps_request) { McpsReq_t request; if (NULL == mcps_request) { return LORA_MAC_STATUS_PARAMETER_INVALID; } request.Type = (Mcps_t)mcps_request->type; switch (mcps_request->type) { case LORA_MCPS_UNCONFIRMED: request.Req.Unconfirmed.Datarate = mcps_request->req.unconfirmed.datarate; request.Req.Unconfirmed.fBuffer = mcps_request->f_buffer; request.Req.Unconfirmed.fBufferSize = mcps_request->f_buffer_size; request.Req.Unconfirmed.fPort = mcps_request->req.unconfirmed.f_port; break; case LORA_MCPS_CONFIRMED: request.Req.Confirmed.Datarate = mcps_request->req.confirmed.datarate; request.Req.Confirmed.fBuffer = mcps_request->f_buffer; request.Req.Confirmed.fBufferSize = mcps_request->f_buffer_size; request.Req.Confirmed.fPort = mcps_request->req.confirmed.f_port; request.Req.Confirmed.NbTrials = mcps_request->req.confirmed.nb_trials; break; case LORA_MCPS_PROPRIETARY: request.Req.Proprietary.Datarate = mcps_request->req.proprietary.datarate; request.Req.Proprietary.fBuffer = mcps_request->f_buffer; request.Req.Proprietary.fBufferSize = mcps_request->f_buffer_size; break; default: return LORA_MAC_STATUS_SERVICE_UNKNOWN; break; } return error_type_converter(_loramac.LoRaMacMcpsRequest(&request)); } /** MCPS-Confirm event function * * \param mcps_confirm Pointer to the confirm structure, * containing confirm attributes. */ void LoRaWANStack::mcps_confirm_handler(lora_mac_mcps_confirm_t *mcps_confirm) { if (mcps_confirm == NULL) { tr_error("mcps_confirm: struct [in] is null."); return; } if (mcps_confirm->status != LORA_EVENT_INFO_STATUS_OK) { // Couldn't schedule packet, ack not recieved in CONFIRMED case // or some other error happened. Discard buffer, unset the tx-ongoing // flag and let the application know _tx_msg.tx_ongoing = false; memset(_tx_msg.f_buffer, 0, LORAWAN_TX_MAX_SIZE); _tx_msg.f_buffer_size = LORAWAN_TX_MAX_SIZE; tr_error("mcps_confirm_handler: Error code = %d", mcps_confirm->status); // If sending timed out, we have a special event for that if (mcps_confirm->status == LORA_EVENT_INFO_STATUS_TX_TIMEOUT) { if (_callbacks.events) { _queue->call(_callbacks.events, TX_TIMEOUT); } return; } if (mcps_confirm->status == LORA_EVENT_INFO_STATUS_RX2_TIMEOUT) { tr_debug("Did not receive Ack"); } // Otherwise send a general TX_ERROR event if (_callbacks.events) { _queue->call(_callbacks.events, TX_ERROR); } return; } // If No errors encountered, let's proceed with the status. // CONFIRMED needs special handling because of acks if (mcps_confirm->mcps_request == LORA_MCPS_CONFIRMED) { // In confirmed case, we need to check if we have received the Ack or not. // This is actually just being paranoid about ack because LoRaMac.cpp doesn't // call this callback until an ack is received. if (mcps_confirm->ack_received) { tr_debug("Ack received."); } } // This part is common to both CONFIRMED and UNCONFIRMED. // Tell the application about successful transmission and store // data rate plus frame counter. _lw_session.uplink_counter = mcps_confirm->uplink_counter; _tx_msg.tx_ongoing = false; if (_callbacks.events) { _queue->call(_callbacks.events, TX_DONE); } } /** MCPS-Indication event function * * \param mcps_indication Pointer to the indication structure, * containing indication attributes. */ void LoRaWANStack::mcps_indication_handler(lora_mac_mcps_indication_t *mcps_indication) { if (mcps_indication == NULL) { tr_error("mcps_indication: struct [in] is null."); return; } if (mcps_indication->status != LORA_EVENT_INFO_STATUS_OK) { if (_callbacks.events) { _queue->call(_callbacks.events, RX_ERROR); } return; } switch (mcps_indication->mcps_indication) { case LORA_MCPS_UNCONFIRMED: break; case LORA_MCPS_CONFIRMED: break; case LORA_MCPS_PROPRIETARY: break; case LORA_MCPS_MULTICAST: break; default: break; } // 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->rx_data == true) { switch (mcps_indication->port) { case 224: #if defined(LORAWAN_COMPLIANCE_TEST) tr_debug("Compliance test command received."); compliance_test_handler(mcps_indication); #else tr_debug("Compliance test disabled."); #endif break; default: if (is_port_valid(mcps_indication->port) == true || mcps_indication->mcps_indication == LORA_MCPS_PROPRIETARY) { // Valid message arrived. // Save message to buffer with session information. if (_rx_msg.rx_message.mcps_indication.buffer_size > LORAMAC_PHY_MAXPAYLOAD) { // This may never happen as both radio and MAC are limited // to the size 255 bytes tr_debug("Cannot receive more than buffer capacity!"); if (_callbacks.events) { _queue->call(_callbacks.events, RX_ERROR); } return; } else { _rx_msg.type = LORAMAC_RX_MCPS_INDICATION; _rx_msg.rx_message.mcps_indication.buffer_size = mcps_indication->buffer_size; _rx_msg.rx_message.mcps_indication.port = mcps_indication->port; // Copy message for user memcpy(_rx_msg.rx_message.mcps_indication.buffer, mcps_indication->buffer, mcps_indication->buffer_size); } // Notify application about received frame.. tr_debug("Received %d bytes", _rx_msg.rx_message.mcps_indication.buffer_size); _rx_msg.receive_ready = true; if (_callbacks.events) { _queue->call(_callbacks.events, 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 if (mcps_indication->frame_pending) { handle_tx(mcps_indication->port, NULL, 0, MSG_CONFIRMED_FLAG); } } else { // Invalid port, ports 0, 224 and 225-255 are reserved. } break; } } } #if defined(LORAWAN_COMPLIANCE_TEST) /** Compliance testing function * * \param mcps_indication Pointer to the indication structure, * containing indication attributes. */ void LoRaWANStack::compliance_test_handler(lora_mac_mcps_indication_t *mcps_indication) { if (_compliance_test.running == false) { // Check compliance test enable command (i) if ((mcps_indication->buffer_size == 4) && (mcps_indication->buffer[0] == 0x01) && (mcps_indication->buffer[1] == 0x01) && (mcps_indication->buffer[2] == 0x01) && (mcps_indication->buffer[3] == 0x01)) { _compliance_test.is_tx_confirmed = false; _compliance_test.app_port = 224; _compliance_test.app_data_size = 2; _compliance_test.downlink_counter = 0; _compliance_test.link_check = false; _compliance_test.demod_margin = 0; _compliance_test.nb_gateways = 0; _compliance_test.running = true; _compliance_test.state = 1; lora_mac_mib_request_confirm_t mib_req; mib_req.type = LORA_MIB_ADR; mib_req.param.adr_enable = true; mib_set_request(&mib_req); #if MBED_CONF_LORA_PHY == 0 _loramac.LoRaMacTestSetDutyCycleOn(false); #endif //5000ms _loramac.LoRaMacSetTxTimer(5000); set_device_state(DEVICE_STATE_COMPLIANCE_TEST); tr_debug("Compliance test activated."); } } else { _compliance_test.state = mcps_indication->buffer[0]; switch (_compliance_test.state) { case 0: // Check compliance test disable command (ii) _compliance_test.is_tx_confirmed = true; _compliance_test.app_port = LORAWAN_APP_PORT; _compliance_test.app_data_size = LORAWAN_COMPLIANCE_TEST_DATA_SIZE; _compliance_test.downlink_counter = 0; _compliance_test.running = false; lora_mac_mib_request_confirm_t mib_req; mib_req.type = LORA_MIB_ADR; mib_req.param.adr_enable = LORAWAN_ADR_ON; mib_set_request(&mib_req); #if MBED_CONF_LORA_PHY == 0 _loramac.LoRaMacTestSetDutyCycleOn(LORAWAN_DUTYCYCLE_ON); #endif // Go to idle state after compliance test mode. tr_debug("Compliance test disabled."); _loramac.LoRaMacStopTxTimer(); // Clear any compliance test message stuff before going back to normal operation. memset(&_tx_msg, 0, sizeof(_tx_msg)); set_device_state(DEVICE_STATE_IDLE); lora_state_machine(); break; case 1: // (iii, iv) _compliance_test.app_data_size = 2; break; case 2: // Enable confirmed messages (v) _compliance_test.is_tx_confirmed = true; _compliance_test.state = 1; break; case 3: // Disable confirmed messages (vi) _compliance_test.is_tx_confirmed = false; _compliance_test.state = 1; break; case 4: // (vii) _compliance_test.app_data_size = mcps_indication->buffer_size; _compliance_test.app_data_buffer[0] = 4; for(uint8_t i = 1; i < MIN(_compliance_test.app_data_size, LORAMAC_PHY_MAXPAYLOAD); ++i) { _compliance_test.app_data_buffer[i] = mcps_indication->buffer[i] + 1; } send_compliance_test_frame_to_mac(); break; case 5: // (viii) lora_mac_mlme_req_t mlme_req; mlme_req.type = LORA_MLME_LINK_CHECK; mlme_request_handler(&mlme_req); break; case 6: // (ix) lora_mac_mlme_req_t mlme_request; lora_mac_mib_request_confirm_t mib_request; // Disable TestMode and revert back to normal operation _compliance_test.is_tx_confirmed = true; _compliance_test.app_port = LORAWAN_APP_PORT; _compliance_test.app_data_size = LORAWAN_COMPLIANCE_TEST_DATA_SIZE; _compliance_test.downlink_counter = 0; _compliance_test.running = false; mib_request.type = LORA_MIB_ADR; mib_request.param.adr_enable = LORAWAN_ADR_ON; mib_set_request(&mib_request); #if MBED_CONF_LORA_PHY == 0 _loramac.LoRaMacTestSetDutyCycleOn(LORAWAN_DUTYCYCLE_ON); #endif mlme_request.type = LORA_MLME_JOIN; mlme_request.req.join.dev_eui = _lw_session.connection.connection_u.otaa.dev_eui; mlme_request.req.join.app_eui = _lw_session.connection.connection_u.otaa.app_eui; mlme_request.req.join.app_key = _lw_session.connection.connection_u.otaa.app_key; mlme_request.req.join.nb_trials = _lw_session.connection.connection_u.otaa.nb_trials; mlme_request_handler(&mlme_request); break; case 7: // (x) if (mcps_indication->buffer_size == 3) { lora_mac_mlme_req_t mlme_req; mlme_req.type = LORA_MLME_TXCW; mlme_req.req.tx_cw.timeout = (uint16_t)((mcps_indication->buffer[1] << 8) | mcps_indication->buffer[2]); mlme_request_handler(&mlme_req); } else if (mcps_indication->buffer_size == 7) { lora_mac_mlme_req_t mlme_req; mlme_req.type = LORA_MLME_TXCW_1; mlme_req.req.tx_cw.timeout = (uint16_t)((mcps_indication->buffer[1] << 8) | mcps_indication->buffer[2]); mlme_req.req.tx_cw.frequency = (uint32_t)((mcps_indication->buffer[3] << 16) | (mcps_indication->buffer[4] << 8) | mcps_indication->buffer[5]) * 100; mlme_req.req.tx_cw.power = mcps_indication->buffer[6]; mlme_request_handler(&mlme_req); } _compliance_test.state = 1; break; } } } #endif lora_mac_status_t LoRaWANStack::mib_set_request(lora_mac_mib_request_confirm_t *mib_set_params) { MibRequestConfirm_t stack_mib_set; ChannelParams_t stack_channel_params; MulticastParams_t *stack_multicast_params = NULL; MulticastParams_t *head = NULL; lora_mac_status_t status; if (NULL == mib_set_params) { return LORA_MAC_STATUS_PARAMETER_INVALID; } // Interpreting from lora_mac_mib_t to Mib_t stack_mib_set.Type = interpret_mib_req_confirm_type(mib_set_params->type); switch (mib_set_params->type) { case LORA_MIB_DEVICE_CLASS: stack_mib_set.Param.Class = (DeviceClass_t)mib_set_params->param.lora_class; break; case LORA_MIB_NETWORK_JOINED: stack_mib_set.Param.IsNetworkJoined = mib_set_params->param.is_network_joined; break; case LORA_MIB_ADR: stack_mib_set.Param.AdrEnable = mib_set_params->param.adr_enable; break; case LORA_MIB_NET_ID: stack_mib_set.Param.NetID = mib_set_params->param.net_id; break; case LORA_MIB_DEV_ADDR: stack_mib_set.Param.DevAddr = mib_set_params->param.dev_addr; break; case LORA_MIB_NWK_SKEY: stack_mib_set.Param.NwkSKey = mib_set_params->param.nwk_skey; break; case LORA_MIB_APP_SKEY: stack_mib_set.Param.AppSKey = mib_set_params->param.app_skey; break; case LORA_MIB_PUBLIC_NETWORK: stack_mib_set.Param.EnablePublicNetwork = mib_set_params->param.enable_public_network; break; case LORA_MIB_REPEATER_SUPPORT: stack_mib_set.Param.EnableRepeaterSupport = mib_set_params->param.enable_repeater_support; break; case LORA_MIB_CHANNELS: stack_channel_params.Frequency = mib_set_params->param.channel_list->frequency; stack_channel_params.DrRange.Value = mib_set_params->param.channel_list->dr_range.value; stack_channel_params.DrRange.Fields.Min = mib_set_params->param.channel_list->dr_range.lora_mac_fields_s.min; stack_channel_params.DrRange.Fields.Max = mib_set_params->param.channel_list->dr_range.lora_mac_fields_s.max; stack_channel_params.Band = mib_set_params->param.channel_list->band; stack_channel_params.Rx1Frequency = mib_set_params->param.channel_list->rx1_frequency; stack_mib_set.Param.ChannelList = &stack_channel_params; break; case LORA_MIB_RX2_CHANNEL: stack_mib_set.Param.Rx2Channel.Frequency = mib_set_params->param.rx2_channel.frequency; stack_mib_set.Param.Rx2Channel.Datarate = mib_set_params->param.rx2_channel.datarate; break; case LORA_MIB_RX2_DEFAULT_CHANNEL: stack_mib_set.Param.Rx2DefaultChannel.Frequency = mib_set_params->param.rx2_default_channel.frequency; stack_mib_set.Param.Rx2DefaultChannel.Datarate = mib_set_params->param.rx2_default_channel.datarate; break; case LORA_MIB_CHANNELS_MASK: stack_mib_set.Param.ChannelsMask = mib_set_params->param.channels_mask; break; case LORA_MIB_CHANNELS_DEFAULT_MASK: stack_mib_set.Param.ChannelsDefaultMask = mib_set_params->param.channels_default_mask; break; case LORA_MIB_CHANNELS_NB_REP: stack_mib_set.Param.ChannelNbRep = mib_set_params->param.channel_nb_rep; break; case LORA_MIB_MAX_RX_WINDOW_DURATION: stack_mib_set.Param.MaxRxWindow = mib_set_params->param.max_rx_window; break; case LORA_MIB_RECEIVE_DELAY_1: stack_mib_set.Param.ReceiveDelay1 = mib_set_params->param.receive_delay1; break; case LORA_MIB_RECEIVE_DELAY_2: stack_mib_set.Param.ReceiveDelay2 = mib_set_params->param.receive_delay2; break; case LORA_MIB_JOIN_ACCEPT_DELAY_1: stack_mib_set.Param.JoinAcceptDelay1 = mib_set_params->param.join_accept_delay1; break; case LORA_MIB_JOIN_ACCEPT_DELAY_2: stack_mib_set.Param.JoinAcceptDelay2 = mib_set_params->param.join_accept_delay2; break; case LORA_MIB_CHANNELS_DEFAULT_DATARATE: stack_mib_set.Param.ChannelsDefaultDatarate = mib_set_params->param.channels_default_datarate; break; case LORA_MIB_CHANNELS_DATARATE: stack_mib_set.Param.ChannelsDatarate = mib_set_params->param.channels_datarate; break; case LORA_MIB_CHANNELS_TX_POWER: stack_mib_set.Param.ChannelsTxPower = mib_set_params->param.channels_tx_power; break; case LORA_MIB_CHANNELS_DEFAULT_TX_POWER: stack_mib_set.Param.ChannelsDefaultTxPower = mib_set_params->param.channels_default_tx_power; break; case LORA_MIB_UPLINK_COUNTER: stack_mib_set.Param.UpLinkCounter = mib_set_params->param.uplink_counter; break; case LORA_MIB_DOWNLINK_COUNTER: stack_mib_set.Param.DownLinkCounter = mib_set_params->param.downlink_counter; break; case LORA_MIB_MULTICAST_CHANNEL: /* * Parse multicast list (C++ linked list) */ while (mib_set_params->param.multicast_list != NULL) { if (stack_multicast_params == NULL) { stack_multicast_params = new MulticastParams_t; head = stack_multicast_params; } else { while (stack_multicast_params != NULL) { stack_multicast_params = stack_multicast_params->Next; } stack_multicast_params = new MulticastParams_t; } stack_multicast_params->Address = mib_set_params->param.multicast_list->address; for (int i = 0; i < 16; i++) { stack_multicast_params->NwkSKey[i] = mib_set_params->param.multicast_list->nwk_skey[i]; stack_multicast_params->AppSKey[i] = mib_set_params->param.multicast_list->app_skey[i]; } stack_multicast_params->DownLinkCounter = mib_set_params->param.multicast_list->downlink_counter; } stack_mib_set.Param.MulticastList = head; break; case LORA_MIB_SYSTEM_MAX_RX_ERROR: stack_mib_set.Param.SystemMaxRxError = mib_set_params->param.system_max_rx_error; break; case LORA_MIB_MIN_RX_SYMBOLS: stack_mib_set.Param.MinRxSymbols = mib_set_params->param.min_rx_symbols; break; default: return LORA_MAC_STATUS_SERVICE_UNKNOWN; break; } /* * Set MIB data to LoRa stack */ status = error_type_converter(_loramac.LoRaMacMibSetRequestConfirm(&stack_mib_set)); /* * Release memory if reserved by multicast list */ if (NULL != head) { while (NULL != head) { delete head; head = NULL; head = stack_mib_set.Param.MulticastList->Next; } stack_mib_set.Param.MulticastList = NULL; } return status; } lora_mac_status_t LoRaWANStack::mib_get_request(lora_mac_mib_request_confirm_t *mib_get_params) { MibRequestConfirm_t stack_mib_get; MulticastParams_t *origin_multicast_list = NULL; lora_mac_multicast_params_t *new_multicast_list = NULL; lora_mac_status_t mac_status = LORA_MAC_STATUS_OK; if(NULL == mib_get_params) { return LORA_MAC_STATUS_PARAMETER_INVALID; } // Interprets from lora_mac_mib_t to Mib_t stack_mib_get.Type = interpret_mib_req_confirm_type(mib_get_params->type); mac_status = error_type_converter(_loramac.LoRaMacMibGetRequestConfirm(&stack_mib_get)); if (LORA_MAC_STATUS_OK != mac_status) { return LORA_MAC_STATUS_SERVICE_UNKNOWN; } switch(mib_get_params->type) { case LORA_MIB_DEVICE_CLASS: mib_get_params->param.lora_class = (lora_mac_device_class_t)stack_mib_get.Param.Class; break; case LORA_MIB_NETWORK_JOINED: mib_get_params->param.is_network_joined = stack_mib_get.Param.IsNetworkJoined; break; case LORA_MIB_ADR: mib_get_params->param.adr_enable = stack_mib_get.Param.AdrEnable; break; case LORA_MIB_NET_ID: mib_get_params->param.net_id = stack_mib_get.Param.NetID; break; case LORA_MIB_DEV_ADDR: mib_get_params->param.dev_addr = stack_mib_get.Param.DevAddr; break; case LORA_MIB_NWK_SKEY: mib_get_params->param.nwk_skey = stack_mib_get.Param.NwkSKey; break; case LORA_MIB_APP_SKEY: mib_get_params->param.app_skey = stack_mib_get.Param.AppSKey; break; case LORA_MIB_PUBLIC_NETWORK: mib_get_params->param.enable_public_network = stack_mib_get.Param.EnablePublicNetwork; break; case LORA_MIB_REPEATER_SUPPORT: mib_get_params->param.enable_repeater_support = stack_mib_get.Param.EnableRepeaterSupport; break; case LORA_MIB_CHANNELS: mib_get_params->param.channel_list = (lora_mac_channel_params_t *) stack_mib_get.Param.ChannelList; break; case LORA_MIB_RX2_CHANNEL: mib_get_params->param.rx2_channel.datarate = stack_mib_get.Param.Rx2Channel.Datarate; mib_get_params->param.rx2_channel.frequency = stack_mib_get.Param.Rx2Channel.Frequency; break; case LORA_MIB_RX2_DEFAULT_CHANNEL: mib_get_params->param.rx2_default_channel.datarate = stack_mib_get.Param.Rx2DefaultChannel.Datarate; mib_get_params->param.rx2_default_channel.frequency = stack_mib_get.Param.Rx2DefaultChannel.Frequency; break; case LORA_MIB_CHANNELS_DEFAULT_MASK: mib_get_params->param.channels_default_mask = stack_mib_get.Param.ChannelsDefaultMask; break; case LORA_MIB_CHANNELS_MASK: mib_get_params->param.channels_mask = stack_mib_get.Param.ChannelsMask; break; case LORA_MIB_CHANNELS_NB_REP: mib_get_params->param.channel_nb_rep = stack_mib_get.Param.ChannelNbRep; break; case LORA_MIB_MAX_RX_WINDOW_DURATION: mib_get_params->param.max_rx_window = stack_mib_get.Param.MaxRxWindow; break; case LORA_MIB_RECEIVE_DELAY_1: mib_get_params->param.receive_delay1 = stack_mib_get.Param.ReceiveDelay1; break; case LORA_MIB_RECEIVE_DELAY_2: mib_get_params->param.receive_delay2 = stack_mib_get.Param.ReceiveDelay2; break; case LORA_MIB_JOIN_ACCEPT_DELAY_1: mib_get_params->param.join_accept_delay1 = stack_mib_get.Param.JoinAcceptDelay1; break; case LORA_MIB_JOIN_ACCEPT_DELAY_2: mib_get_params->param.join_accept_delay2 = stack_mib_get.Param.JoinAcceptDelay2; break; case LORA_MIB_CHANNELS_DEFAULT_DATARATE: mib_get_params->param.channels_default_datarate = stack_mib_get.Param.ChannelsDefaultDatarate; break; case LORA_MIB_CHANNELS_DATARATE: mib_get_params->param.channels_datarate = stack_mib_get.Param.ChannelsDatarate; break; case LORA_MIB_CHANNELS_DEFAULT_TX_POWER: mib_get_params->param.channels_default_tx_power = stack_mib_get.Param.ChannelsDefaultTxPower; break; case LORA_MIB_CHANNELS_TX_POWER: mib_get_params->param.channels_tx_power = stack_mib_get.Param.ChannelsTxPower; break; case LORA_MIB_UPLINK_COUNTER: mib_get_params->param.uplink_counter = stack_mib_get.Param.UpLinkCounter; break; case LORA_MIB_DOWNLINK_COUNTER: mib_get_params->param.downlink_counter = stack_mib_get.Param.DownLinkCounter; break; case LORA_MIB_MULTICAST_CHANNEL: /* * Parse multicast list (C++ linked list) */ origin_multicast_list = stack_mib_get.Param.MulticastList; while (NULL != origin_multicast_list) { if (NULL == new_multicast_list) { new_multicast_list = new lora_mac_multicast_params_t; new_multicast_list->next = NULL; mib_get_params->param.multicast_list = new_multicast_list; } else { while (NULL != new_multicast_list) { new_multicast_list = new_multicast_list->next; } new_multicast_list = new lora_mac_multicast_params_t; new_multicast_list->next = NULL; } new_multicast_list->address = origin_multicast_list->Address; for (int i = 0; i < 16; ++i) { new_multicast_list->nwk_skey[i] = origin_multicast_list->NwkSKey[i]; new_multicast_list->app_skey[i] = origin_multicast_list->AppSKey[i]; } new_multicast_list->downlink_counter = origin_multicast_list->DownLinkCounter; origin_multicast_list = origin_multicast_list->Next; } break; case LORA_MIB_SYSTEM_MAX_RX_ERROR: mib_get_params->param.system_max_rx_error = stack_mib_get.Param.SystemMaxRxError; break; case LORA_MIB_MIN_RX_SYMBOLS: mib_get_params->param.min_rx_symbols = stack_mib_get.Param.MinRxSymbols; break; default: return LORA_MAC_STATUS_SERVICE_UNKNOWN; break; } return mac_status; } lora_mac_status_t LoRaWANStack::error_type_converter(LoRaMacStatus_t type) { switch (type) { case LORAMAC_STATUS_OK: return LORA_MAC_STATUS_OK; break; case LORAMAC_STATUS_BUSY: return LORA_MAC_STATUS_BUSY; break; case LORAMAC_STATUS_SERVICE_UNKNOWN: return LORA_MAC_STATUS_SERVICE_UNKNOWN; break; case LORAMAC_STATUS_PARAMETER_INVALID: return LORA_MAC_STATUS_PARAMETER_INVALID; break; case LORAMAC_STATUS_FREQUENCY_INVALID: return LORA_MAC_STATUS_FREQUENCY_INVALID; break; case LORAMAC_STATUS_DATARATE_INVALID: return LORA_MAC_STATUS_DATARATE_INVALID; break; case LORAMAC_STATUS_FREQ_AND_DR_INVALID: return LORA_MAC_STATUS_FREQ_AND_DR_INVALID; break; case LORAMAC_STATUS_NO_NETWORK_JOINED: return LORA_MAC_STATUS_NO_NETWORK_JOINED; break; case LORAMAC_STATUS_LENGTH_ERROR: return LORA_MAC_STATUS_LENGTH_ERROR; break; case LORAMAC_STATUS_DEVICE_OFF: return LORA_MAC_STATUS_DEVICE_OFF; break; case LORAMAC_STATUS_CRYPTO_FAIL: return LORA_MAC_STATUS_CRYPTO_FAIL; break; default: return LORA_MAC_STATUS_SERVICE_UNKNOWN; break; } } lora_mac_status_t LoRaWANStack::set_link_check_request() { if (!_callbacks.link_check_resp) { tr_error("Must assign a callback function for link check request. "); return LORA_MAC_STATUS_PARAMETER_INVALID; } lora_mac_mlme_req_t mlme_req; mlme_req.type = LORA_MLME_LINK_CHECK; return mlme_request_handler(&mlme_req); } void LoRaWANStack::shutdown() { set_device_state(DEVICE_STATE_SHUTDOWN); lora_state_machine(); } lora_mac_status_t LoRaWANStack::lora_state_machine() { lora_mac_mib_request_confirm_t mib_req; lora_mac_status_t status = LORA_MAC_STATUS_DEVICE_OFF; switch (_device_current_state) { case DEVICE_STATE_SHUTDOWN: /* * Remove channels * Radio will be put to sleep by the APIs underneath */ drop_channel_list(); // Stop sending messages and set joined status to false. #if defined(LORAWAN_COMPLIANCE_TEST) _loramac.LoRaMacStopTxTimer(); #endif mib_req.type = LORA_MIB_NETWORK_JOINED; mib_req.param.is_network_joined = false; mib_set_request(&mib_req); // reset buffers to original state memset(_tx_msg.f_buffer, 0, LORAWAN_TX_MAX_SIZE); _tx_msg.pending_size = 0; _tx_msg.f_buffer_size = 0; _tx_msg.tx_ongoing = false; memset(_rx_msg.rx_message.mcps_indication.buffer, 0, LORAMAC_PHY_MAXPAYLOAD); _rx_msg.receive_ready = false; _rx_msg.prev_read_size = 0; _rx_msg.rx_message.mcps_indication.buffer_size = 0; // disable the session _lw_session.active = false; tr_debug("LoRaWAN protocol has been shut down."); if (_callbacks.events) { _queue->call(_callbacks.events, DISCONNECTED); } status = LORA_MAC_STATUS_DEVICE_OFF; break; case DEVICE_STATE_NOT_INITIALIZED: // Device is disconnected. status = LORA_MAC_STATUS_DEVICE_OFF; break; case DEVICE_STATE_INIT: status = LORA_MAC_STATUS_OK; break; case DEVICE_STATE_JOINING: if (_lw_session.connection.connect_type == LORAWAN_CONNECTION_OTAA) { /* * OTAA join */ tr_debug("Send Join-request.."); lora_mac_mlme_req_t mlme_req; mlme_req.type = LORA_MLME_JOIN; mlme_req.req.join.dev_eui = _lw_session.connection.connection_u.otaa.dev_eui; mlme_req.req.join.app_eui = _lw_session.connection.connection_u.otaa.app_eui; mlme_req.req.join.app_key = _lw_session.connection.connection_u.otaa.app_key; mlme_req.req.join.nb_trials = _lw_session.connection.connection_u.otaa.nb_trials; // Send join request to server. status = mlme_request_handler(&mlme_req); if (status != LORA_MAC_STATUS_OK) { return status; } // Otherwise request was successful and OTAA connect is in //progress return LORA_MAC_STATUS_CONNECT_IN_PROGRESS; } else { status = LORA_MAC_STATUS_PARAMETER_INVALID; break; } break; case DEVICE_STATE_JOINED: tr_debug("Join OK!"); // Session is now active _lw_session.active = true; // Tell the application that we are connected if (_callbacks.events) { _queue->call(_callbacks.events, CONNECTED); } break; case DEVICE_STATE_ABP_CONNECTING: /* * ABP connection */ mib_req.type = LORA_MIB_NET_ID; mib_req.param.net_id = _lw_session.connection.connection_u.abp.nwk_id; mib_set_request(&mib_req); mib_req.type = LORA_MIB_DEV_ADDR; mib_req.param.dev_addr = _lw_session.connection.connection_u.abp.dev_addr; mib_set_request(&mib_req); mib_req.type = LORA_MIB_NWK_SKEY; mib_req.param.nwk_skey = _lw_session.connection.connection_u.abp.nwk_skey; mib_set_request(&mib_req); mib_req.type = LORA_MIB_APP_SKEY; mib_req.param.app_skey = _lw_session.connection.connection_u.abp.app_skey; mib_set_request(&mib_req); mib_req.type = LORA_MIB_NETWORK_JOINED; mib_req.param.is_network_joined = true; mib_set_request(&mib_req); tr_debug("ABP Connection OK!"); // tell the application we are okay // if users provide wrong keys, it's their responsibility // there is no way to test ABP authentication success status = LORA_MAC_STATUS_OK; // Session is now active _lw_session.active = true; if (_callbacks.events) { _queue->call(_callbacks.events, CONNECTED); } break; case DEVICE_STATE_SEND: // If a transmission is ongoing, don't interrupt if (_tx_msg.tx_ongoing) { status = LORA_MAC_STATUS_OK; } else { _tx_msg.tx_ongoing = true; status = send_frame_to_mac(); switch (status) { case LORA_MAC_STATUS_OK: tr_debug("Frame scheduled to TX.."); break; case LORA_MAC_STATUS_CRYPTO_FAIL: tr_error("Crypto failed. Clearing TX buffers"); if (_callbacks.events) { _queue->call(_callbacks.events, TX_CRYPTO_ERROR); } break; default: tr_error("Failure to schedule TX!"); if (_callbacks.events) { _queue->call(_callbacks.events, TX_SCHEDULING_ERROR); } break; } } // otherwise all done, put device in idle state set_device_state(DEVICE_STATE_IDLE); break; case DEVICE_STATE_IDLE: //Do nothing status = LORA_MAC_STATUS_IDLE; break; #if defined(LORAWAN_COMPLIANCE_TEST) case DEVICE_STATE_COMPLIANCE_TEST: //Device is in compliance test mode tr_debug("Device is in compliance test mode."); //5000ms _loramac.LoRaMacSetTxTimer(5000); if (_compliance_test.running == true) { send_compliance_test_frame_to_mac(); } status = LORA_MAC_STATUS_COMPLIANCE_TEST_ON; break; #endif default: status = LORA_MAC_STATUS_SERVICE_UNKNOWN; break; } return status; } static Mcps_t interpret_mcps_confirm_type(const lora_mac_mcps_t& local) { switch (local) { case LORA_MCPS_UNCONFIRMED: return MCPS_UNCONFIRMED; case LORA_MCPS_CONFIRMED: return MCPS_CONFIRMED; case LORA_MCPS_MULTICAST: return MCPS_MULTICAST; case LORA_MCPS_PROPRIETARY: return MCPS_PROPRIETARY; default: MBED_ASSERT("Unknown Mcps_t"); } // Never reaches here return MCPS_UNCONFIRMED; } static lora_mac_event_info_status_t interpret_event_info_type(const LoRaMacEventInfoStatus_t& remote) { switch (remote) { case LORAMAC_EVENT_INFO_STATUS_OK: return LORA_EVENT_INFO_STATUS_OK; case LORAMAC_EVENT_INFO_STATUS_ERROR: return LORA_EVENT_INFO_STATUS_ERROR; case LORAMAC_EVENT_INFO_STATUS_TX_TIMEOUT: return LORA_EVENT_INFO_STATUS_TX_TIMEOUT; case LORAMAC_EVENT_INFO_STATUS_RX1_TIMEOUT: return LORA_EVENT_INFO_STATUS_RX1_TIMEOUT; case LORAMAC_EVENT_INFO_STATUS_RX2_TIMEOUT: return LORA_EVENT_INFO_STATUS_RX2_TIMEOUT; case LORAMAC_EVENT_INFO_STATUS_RX1_ERROR: return LORA_EVENT_INFO_STATUS_RX1_ERROR; case LORAMAC_EVENT_INFO_STATUS_RX2_ERROR: return LORA_EVENT_INFO_STATUS_RX2_ERROR; case LORAMAC_EVENT_INFO_STATUS_JOIN_FAIL: return LORA_EVENT_INFO_STATUS_JOIN_FAIL; case LORAMAC_EVENT_INFO_STATUS_DOWNLINK_REPEATED: return LORA_EVENT_INFO_STATUS_DOWNLINK_REPEATED; case LORAMAC_EVENT_INFO_STATUS_TX_DR_PAYLOAD_SIZE_ERROR: return LORA_EVENT_INFO_STATUS_TX_DR_PAYLOAD_SIZE_ERROR; case LORAMAC_EVENT_INFO_STATUS_DOWNLINK_TOO_MANY_FRAMES_LOSS: return LORA_EVENT_INFO_STATUS_DOWNLINK_TOO_MANY_FRAMES_LOSS; case LORAMAC_EVENT_INFO_STATUS_ADDRESS_FAIL: return LORA_EVENT_INFO_STATUS_ADDRESS_FAIL; case LORAMAC_EVENT_INFO_STATUS_MIC_FAIL: return LORA_EVENT_INFO_STATUS_MIC_FAIL; case LORAMAC_EVENT_INFO_STATUS_CRYPTO_FAIL: return LORA_EVENT_INFO_STATUS_CRYPTO_FAIL; default: MBED_ASSERT("Unknown LoRaMacEventInfoStatus_t"); } // Never reaches here actually return LORA_EVENT_INFO_STATUS_OK; } static Mib_t interpret_mib_req_confirm_type(const lora_mac_mib_t& local) { switch (local) { case LORA_MIB_DEVICE_CLASS: return MIB_DEVICE_CLASS; case LORA_MIB_NETWORK_JOINED: return MIB_NETWORK_JOINED; case LORA_MIB_ADR: return MIB_ADR; case LORA_MIB_NET_ID: return MIB_NET_ID; case LORA_MIB_DEV_ADDR: return MIB_DEV_ADDR; case LORA_MIB_NWK_SKEY: return MIB_NWK_SKEY; case LORA_MIB_APP_SKEY: return MIB_APP_SKEY; case LORA_MIB_PUBLIC_NETWORK: return MIB_PUBLIC_NETWORK; case LORA_MIB_REPEATER_SUPPORT: return MIB_REPEATER_SUPPORT; case LORA_MIB_CHANNELS: return MIB_CHANNELS; case LORA_MIB_RX2_CHANNEL: return MIB_RX2_CHANNEL; case LORA_MIB_RX2_DEFAULT_CHANNEL: return MIB_RX2_DEFAULT_CHANNEL; case LORA_MIB_CHANNELS_MASK: return MIB_CHANNELS_MASK; case LORA_MIB_CHANNELS_DEFAULT_MASK: return MIB_CHANNELS_DEFAULT_MASK; case LORA_MIB_CHANNELS_NB_REP: return MIB_CHANNELS_NB_REP; case LORA_MIB_MAX_RX_WINDOW_DURATION: return MIB_MAX_RX_WINDOW_DURATION; case LORA_MIB_RECEIVE_DELAY_1: return MIB_RECEIVE_DELAY_1; case LORA_MIB_RECEIVE_DELAY_2: return MIB_RECEIVE_DELAY_2; case LORA_MIB_JOIN_ACCEPT_DELAY_1: return MIB_JOIN_ACCEPT_DELAY_1; case LORA_MIB_JOIN_ACCEPT_DELAY_2: return MIB_JOIN_ACCEPT_DELAY_2; case LORA_MIB_CHANNELS_DEFAULT_DATARATE: return MIB_CHANNELS_DEFAULT_DATARATE; case LORA_MIB_CHANNELS_DATARATE: return MIB_CHANNELS_DATARATE; case LORA_MIB_CHANNELS_TX_POWER: return MIB_CHANNELS_TX_POWER; case LORA_MIB_CHANNELS_DEFAULT_TX_POWER: return MIB_CHANNELS_DEFAULT_TX_POWER; case LORA_MIB_UPLINK_COUNTER: return MIB_UPLINK_COUNTER; case LORA_MIB_DOWNLINK_COUNTER: return MIB_DOWNLINK_COUNTER; case LORA_MIB_MULTICAST_CHANNEL: return MIB_MULTICAST_CHANNEL; case LORA_MIB_SYSTEM_MAX_RX_ERROR: return MIB_SYSTEM_MAX_RX_ERROR; case LORA_MIB_MIN_RX_SYMBOLS: return MIB_MIN_RX_SYMBOLS; default: MBED_ASSERT("Cannot Interpret Mib_t"); } // Never actually reaches here return MIB_DEVICE_CLASS; }