Manh Pham / Mbed OS Nucleo_rtos_basic_ir_controller
Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers LoRaWANStack.cpp Source File

LoRaWANStack.cpp

00001 /**
00002  / _____)             _              | |
00003 ( (____  _____ ____ _| |_ _____  ____| |__
00004  \____ \| ___ |    (_   _) ___ |/ ___)  _ \
00005  _____) ) ____| | | || |_| ____( (___| | | |
00006 (______/|_____)_|_|_| \__)_____)\____)_| |_|
00007     (C)2013 Semtech
00008  ___ _____ _   ___ _  _____ ___  ___  ___ ___
00009 / __|_   _/_\ / __| |/ / __/ _ \| _ \/ __| __|
00010 \__ \ | |/ _ \ (__| ' <| _| (_) |   / (__| _|
00011 |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
00012 embedded.connectivity.solutions===============
00013 
00014 Description: LoRaWAN stack layer that controls both MAC and PHY underneath
00015 
00016 License: Revised BSD License, see LICENSE.TXT file include in the project
00017 
00018 Maintainer: Miguel Luis ( Semtech ), Gregory Cristian ( Semtech ) and Daniel Jaeckle ( STACKFORCE )
00019 
00020 
00021 Copyright (c) 2017, Arm Limited and affiliates.
00022 
00023 SPDX-License-Identifier: BSD-3-Clause
00024 */
00025 
00026 #include <string.h>
00027 #include <stdlib.h>
00028 #include "platform/Callback.h"
00029 #include "events/EventQueue.h"
00030 #include "lorawan/LoRaWANStack.h"
00031 #if defined(FEATURE_COMMON_PAL)
00032 #include "mbed_trace.h"
00033 #define TRACE_GROUP "LSTK"
00034 #else
00035 #define tr_debug(...) (void(0)) //dummies if feature common pal is not added
00036 #define tr_info(...)  (void(0)) //dummies if feature common pal is not added
00037 #define tr_error(...) (void(0)) //dummies if feature common pal is not added
00038 #define tr_warn(...) (void(0)) //dummies if feature common pal is not added
00039 #endif //defined(FEATURE_COMMON_PAL)
00040 
00041 #define INVALID_PORT                0xFF
00042 #define MAX_CONFIRMED_MSG_RETRIES   255
00043 
00044 using namespace mbed;
00045 using namespace events;
00046 
00047 #if defined(LORAWAN_COMPLIANCE_TEST)
00048     #if (MBED_CONF_LORA_PHY == 0 || MBED_CONF_LORA_PHY == 4 || MBED_CONF_LORA_PHY == 6 || MBED_CONF_LORA_PHY == 7)
00049         #define LORAWAN_COMPLIANCE_TEST_DATA_SIZE                  16
00050     #elif (MBED_CONF_LORA_PHY == 1 || MBED_CONF_LORA_PHY == 2 || MBED_CONF_LORA_PHY == 8 || MBED_CONF_LORA_PHY == 9)
00051         #define LORAWAN_COMPLIANCE_TEST_DATA_SIZE                  11
00052     #else
00053         #error "Must set LoRa PHY layer parameters."
00054     #endif
00055 #endif
00056 
00057 /*****************************************************************************
00058  * Private Member Functions                                                  *
00059  ****************************************************************************/
00060 bool LoRaWANStack::is_port_valid(uint8_t port)
00061 {
00062     //Application should not use reserved and illegal port numbers.
00063     if (port >= 224 || port == 0) {
00064         return false;
00065     } else {
00066         return true;
00067     }
00068 }
00069 
00070 lorawan_status_t LoRaWANStack::set_application_port(uint8_t port)
00071 {
00072     if (is_port_valid(port)) {
00073         _app_port = port;
00074         return LORAWAN_STATUS_OK;
00075     }
00076 
00077     return LORAWAN_STATUS_PORT_INVALID;
00078 }
00079 
00080 /*****************************************************************************
00081  * Constructor and destructor                                                *
00082  ****************************************************************************/
00083 LoRaWANStack::LoRaWANStack()
00084 : _loramac(),
00085   _device_current_state(DEVICE_STATE_NOT_INITIALIZED), _num_retry(1),
00086   _app_port(INVALID_PORT), _link_check_requested(false), _queue(NULL)
00087 {
00088 #ifdef MBED_CONF_LORA_APP_PORT
00089     if (is_port_valid(MBED_CONF_LORA_APP_PORT)) {
00090         _app_port = MBED_CONF_LORA_APP_PORT;
00091     } else {
00092         tr_error("User defined port in .json is illegal.");
00093     }
00094 #endif
00095 
00096      memset(&_lw_session, 0, sizeof(_lw_session));
00097      memset(&_rx_msg, 0, sizeof(_rx_msg));
00098 
00099      LoRaMacPrimitives.mcps_confirm     = callback(this, &LoRaWANStack::mcps_confirm_handler);
00100      LoRaMacPrimitives.mcps_indication  = callback(this, &LoRaWANStack::mcps_indication_handler);
00101      LoRaMacPrimitives.mlme_confirm     = callback(this, &LoRaWANStack::mlme_confirm_handler);
00102      LoRaMacPrimitives.mlme_indication  = callback(this, &LoRaWANStack::mlme_indication_handler);
00103 }
00104 
00105 LoRaWANStack::~LoRaWANStack()
00106 {
00107 }
00108 
00109 /*****************************************************************************
00110  * Public member functions                                                   *
00111  ****************************************************************************/
00112 LoRaWANStack& LoRaWANStack::get_lorawan_stack()
00113 {
00114     static LoRaWANStack _lw_stack;
00115     return _lw_stack;
00116 }
00117 
00118 void LoRaWANStack::bind_radio_driver(LoRaRadio& radio)
00119 {
00120     _loramac.bind_radio_driver(radio);
00121 }
00122 
00123 lorawan_status_t LoRaWANStack::connect()
00124 {
00125     // connection attempt without parameters.
00126     // System tries to look for configuration in mbed_lib.json that can be
00127     // overridden by mbed_app.json. However, if none of the json files are
00128     // available (highly unlikely), we still fallback to some default parameters.
00129     // Check lorawan_data_structure for fallback defaults.
00130 
00131     lorawan_connect_t connection_params;
00132 
00133     //TODO: LoRaWANStack don't need to know these values, move to LoRaMac (or below)
00134 #if MBED_CONF_LORA_OVER_THE_AIR_ACTIVATION
00135     const static uint8_t dev_eui[] = MBED_CONF_LORA_DEVICE_EUI;
00136     const static uint8_t app_eui[] = MBED_CONF_LORA_APPLICATION_EUI;
00137     const static uint8_t app_key[] = MBED_CONF_LORA_APPLICATION_KEY;
00138 
00139     connection_params.connect_type  = LORAWAN_CONNECTION_OTAA;
00140     connection_params.connection_u.otaa .app_eui = const_cast<uint8_t *>(app_eui);
00141     connection_params.connection_u.otaa .dev_eui = const_cast<uint8_t *>(dev_eui);
00142     connection_params.connection_u.otaa .app_key = const_cast<uint8_t *>(app_key);
00143     connection_params.connection_u.otaa .nb_trials = MBED_CONF_LORA_NB_TRIALS;
00144 
00145     return join_request_by_otaa(connection_params);
00146 #else
00147     const static uint8_t nwk_skey[] = MBED_CONF_LORA_NWKSKEY;
00148     const static uint8_t app_skey[] = MBED_CONF_LORA_APPSKEY;
00149     const static uint32_t dev_addr = MBED_CONF_LORA_DEVICE_ADDRESS;
00150     const static uint32_t nwk_id = (MBED_CONF_LORA_DEVICE_ADDRESS & LORAWAN_NETWORK_ID_MASK);
00151 
00152     connection_params.connect_type  = LORAWAN_CONNECTION_ABP;
00153     connection_params.connection_u.abp .nwk_id = const_cast<uint8_t *>(nwk_id);
00154     connection_params.connection_u.abp .dev_addr = const_cast<uint8_t *>(dev_addr);
00155     connection_params.connection_u.abp .nwk_skey = const_cast<uint8_t *>(nwk_skey);
00156     connection_params.connection_u.abp .app_skey = const_cast<uint8_t *>(app_skey);
00157 
00158     return activation_by_personalization(connection_params);
00159 #endif
00160 }
00161 
00162 lorawan_status_t LoRaWANStack::connect(const lorawan_connect_t &connect)
00163 {
00164     lorawan_status_t mac_status;
00165 
00166     if (connect.connect_type  == LORAWAN_CONNECTION_OTAA) {
00167         mac_status = join_request_by_otaa(connect);
00168     } else if (connect.connect_type  == LORAWAN_CONNECTION_ABP) {
00169         mac_status = activation_by_personalization(connect);
00170     } else {
00171         return LORAWAN_STATUS_PARAMETER_INVALID;
00172     }
00173 
00174     return mac_status;
00175 }
00176 
00177 lorawan_status_t LoRaWANStack::initialize_mac_layer(EventQueue *queue)
00178 {
00179     if(!queue) {
00180         return LORAWAN_STATUS_PARAMETER_INVALID;
00181     }
00182 
00183     if (DEVICE_STATE_NOT_INITIALIZED != _device_current_state)
00184     {
00185         tr_debug("Initialized already");
00186         return LORAWAN_STATUS_OK;
00187     }
00188 
00189     tr_debug("Initializing MAC layer");
00190     _queue = queue;
00191 
00192     _loramac.initialize(&LoRaMacPrimitives, queue);
00193 
00194     // Reset counters to zero. Will change in future with 1.1 support.
00195     _lw_session.downlink_counter = 0;
00196     _lw_session.uplink_counter = 0;
00197 
00198     return lora_state_machine(DEVICE_STATE_INIT);
00199 }
00200 
00201 lorawan_status_t LoRaWANStack::set_confirmed_msg_retry(uint8_t count)
00202 {
00203     if (count >= MAX_CONFIRMED_MSG_RETRIES) {
00204         return LORAWAN_STATUS_PARAMETER_INVALID;
00205     }
00206 
00207     _num_retry = count;
00208 
00209     return LORAWAN_STATUS_OK;
00210 }
00211 
00212 /*!
00213  * \brief   MLME-Indication event function
00214  *
00215  * \param   [IN] mlmeIndication - Pointer to the indication structure.
00216  */
00217 void LoRaWANStack::mlme_indication_handler(loramac_mlme_indication_t  *mlmeIndication)
00218 {
00219     switch( mlmeIndication->indication_type  )
00220     {
00221         case MLME_SCHEDULE_UPLINK :
00222         {// The MAC signals that we shall provide an uplink as soon as possible
00223             // TODO: Sending implementation missing and will be implemented using
00224             //       another task.
00225             //OnTxNextPacketTimerEvent( );
00226             break;
00227         }
00228         default:
00229             break;
00230     }
00231 }
00232 
00233 lorawan_status_t LoRaWANStack::set_lora_callbacks(lorawan_app_callbacks_t *cbs)
00234 {
00235     if (!cbs || !cbs->events) {
00236         return LORAWAN_STATUS_PARAMETER_INVALID;
00237     }
00238 
00239     _callbacks.events = cbs->events;
00240 
00241     if (cbs->link_check_resp) {
00242         _callbacks.link_check_resp = cbs->link_check_resp;
00243     }
00244 
00245     if (cbs->battery_level) {
00246         _callbacks.battery_level = cbs->battery_level;
00247     }
00248 
00249     return LORAWAN_STATUS_OK;
00250 }
00251 
00252 lorawan_status_t LoRaWANStack::add_channels(const lorawan_channelplan_t &channel_plan)
00253 {
00254     if (_device_current_state == DEVICE_STATE_NOT_INITIALIZED) {
00255         tr_error("Stack not initialized!");
00256         return LORAWAN_STATUS_NOT_INITIALIZED;
00257     }
00258 
00259     return _loramac.add_channel_plan(channel_plan);
00260 }
00261 
00262 lorawan_status_t LoRaWANStack::drop_channel_list()
00263 {
00264     if (_device_current_state == DEVICE_STATE_NOT_INITIALIZED) {
00265         tr_error("Stack not initialized!");
00266         return LORAWAN_STATUS_NOT_INITIALIZED;
00267     }
00268 
00269     return _loramac.remove_channel_plan();
00270 }
00271 
00272 lorawan_status_t LoRaWANStack::remove_a_channel(uint8_t channel_id)
00273 {
00274     if (_device_current_state == DEVICE_STATE_NOT_INITIALIZED )
00275     {
00276         tr_error("Stack not initialized!");
00277         return LORAWAN_STATUS_NOT_INITIALIZED;
00278     }
00279 
00280     return _loramac.remove_single_channel(channel_id);
00281 }
00282 
00283 lorawan_status_t LoRaWANStack::get_enabled_channels(lorawan_channelplan_t& channel_plan)
00284 {
00285     if (_device_current_state == DEVICE_STATE_JOINING
00286             || _device_current_state == DEVICE_STATE_NOT_INITIALIZED
00287             || _device_current_state == DEVICE_STATE_INIT)
00288     {
00289         tr_error("Cannot get channel plan until Joined!");
00290         return LORAWAN_STATUS_BUSY;
00291     }
00292 
00293   return _loramac.get_channel_plan(channel_plan);
00294 }
00295 
00296 lorawan_status_t LoRaWANStack::enable_adaptive_datarate(bool adr_enabled)
00297 {
00298     if (DEVICE_STATE_NOT_INITIALIZED == _device_current_state)
00299     {
00300         tr_error("Stack not initialized!");
00301         return LORAWAN_STATUS_NOT_INITIALIZED;
00302     }
00303     _loramac.enable_adaptive_datarate(adr_enabled);
00304     return LORAWAN_STATUS_OK;
00305 }
00306 
00307 lorawan_status_t LoRaWANStack::set_channel_data_rate(uint8_t data_rate)
00308 {
00309     if (DEVICE_STATE_NOT_INITIALIZED == _device_current_state)
00310     {
00311         tr_error("Stack not initialized!");
00312         return LORAWAN_STATUS_NOT_INITIALIZED;
00313     }
00314 
00315     return _loramac.set_channel_data_rate(data_rate);
00316 }
00317 
00318 lorawan_status_t LoRaWANStack::join_request_by_otaa(const lorawan_connect_t &params)
00319 {
00320     if (DEVICE_STATE_NOT_INITIALIZED == _device_current_state)
00321     {
00322         tr_error("Stack not initialized!");
00323         return LORAWAN_STATUS_NOT_INITIALIZED;
00324     }
00325 
00326     tr_debug("Initiating OTAA");
00327 
00328     // As mentioned in the comment above, in 1.0.2 spec, counters are always set
00329     // to zero for new connection. This section is common for both normal and
00330     // connection restore at this moment. Will change in future with 1.1 support.
00331     _lw_session.downlink_counter = 0;
00332     _lw_session.uplink_counter = 0;
00333     _lw_session.connection.connect_type = LORAWAN_CONNECTION_OTAA;
00334 
00335     _lw_session.connection.connection_u.otaa.dev_eui = params.connection_u.otaa .dev_eui;
00336     _lw_session.connection.connection_u.otaa.app_eui = params.connection_u.otaa .app_eui;
00337     _lw_session.connection.connection_u.otaa.app_key = params.connection_u.otaa .app_key;
00338     _lw_session.connection.connection_u.otaa.nb_trials = params.connection_u.otaa .nb_trials;
00339 
00340     return lora_state_machine(DEVICE_STATE_JOINING);
00341 }
00342 
00343 lorawan_status_t LoRaWANStack::activation_by_personalization(const lorawan_connect_t &params)
00344 {
00345     if (DEVICE_STATE_NOT_INITIALIZED == _device_current_state) {
00346         tr_error("Stack not initialized!");
00347         return LORAWAN_STATUS_NOT_INITIALIZED;
00348     }
00349 
00350     tr_debug("Initiating ABP");
00351 
00352     _lw_session.connection.connect_type = LORAWAN_CONNECTION_ABP;
00353 
00354     _lw_session.connection.connection_u.abp.dev_addr = params.connection_u.abp .dev_addr;
00355     _lw_session.connection.connection_u.abp.nwk_skey = params.connection_u.abp .nwk_skey;
00356     _lw_session.connection.connection_u.abp.app_skey = params.connection_u.abp .app_skey;
00357 
00358     // If current state is SHUTDOWN, device may be trying to re-establish
00359     // communication. In case of ABP specification is meddled about frame counters.
00360     // It says to reset counters to zero but there is no mechanism to tell the
00361     // network server that the device was disconnected or restarted.
00362     // At the moment, this implementation does not support a non-volatile
00363     // memory storage.
00364     //_lw_session.downlink_counter; //Get from NVM
00365     //_lw_session.uplink_counter; //Get from NVM
00366 
00367     tr_debug("Frame Counters. UpCnt=%lu, DownCnt=%lu",
00368              _lw_session.uplink_counter, _lw_session.downlink_counter);
00369 
00370     return lora_state_machine(DEVICE_STATE_ABP_CONNECTING);
00371 }
00372 
00373 int16_t LoRaWANStack::handle_tx(uint8_t port, const uint8_t* data,
00374                                 uint16_t length, uint8_t flags, bool null_allowed)
00375 {
00376     if (!null_allowed && !data) {
00377         return LORAWAN_STATUS_PARAMETER_INVALID;
00378     }
00379     // add a link check request with normal data, until the application
00380     // explicitly removes it.
00381     if (_link_check_requested) {
00382         set_link_check_request();
00383     }
00384 
00385     if (!_lw_session.active) {
00386         return LORAWAN_STATUS_NO_ACTIVE_SESSIONS;
00387     }
00388 
00389     if(_loramac.tx_ongoing()) {
00390         return LORAWAN_STATUS_WOULD_BLOCK;
00391     }
00392 
00393 #if defined(LORAWAN_COMPLIANCE_TEST)
00394     if (_compliance_test.running) {
00395         return LORAWAN_STATUS_COMPLIANCE_TEST_ON;
00396     }
00397 #endif
00398 
00399     lorawan_status_t status;
00400 
00401     if (_loramac.nwk_joined() == false) {
00402         return LORAWAN_STATUS_NO_NETWORK_JOINED;
00403     }
00404 
00405     status = set_application_port(port);
00406 
00407     if (status != LORAWAN_STATUS_OK) {
00408         tr_error("Illegal application port definition.");
00409         return status;
00410     }
00411 
00412     if (flags == 0 ||
00413            (flags & MSG_FLAG_MASK) == (MSG_CONFIRMED_FLAG|MSG_UNCONFIRMED_FLAG)) {
00414         tr_error("CONFIRMED and UNCONFIRMED are mutually exclusive for send()");
00415         return LORAWAN_STATUS_PARAMETER_INVALID;
00416     }
00417 
00418     int16_t len = _loramac.prepare_ongoing_tx(port, data, length, flags, _num_retry);
00419 
00420 
00421     status = lora_state_machine(DEVICE_STATE_SEND);
00422 
00423     // send user the length of data which is scheduled now.
00424     // user should take care of the pending data.
00425     return (status == LORAWAN_STATUS_OK) ? len : (int16_t) status;
00426 }
00427 
00428 int16_t LoRaWANStack::handle_rx(const uint8_t port, uint8_t* data,
00429                                 uint16_t length, uint8_t flags)
00430 {
00431     if (!_lw_session.active) {
00432         return LORAWAN_STATUS_NO_ACTIVE_SESSIONS;
00433     }
00434 
00435     // No messages to read.
00436     if (!_rx_msg.receive_ready) {
00437         return LORAWAN_STATUS_WOULD_BLOCK;
00438     }
00439 
00440 #if defined(LORAWAN_COMPLIANCE_TEST)
00441     if (_compliance_test.running) {
00442         return LORAWAN_STATUS_COMPLIANCE_TEST_ON;
00443     }
00444 #endif
00445 
00446     if (data == NULL || length == 0) {
00447         return LORAWAN_STATUS_PARAMETER_INVALID;
00448     }
00449 
00450     uint8_t *base_ptr = _rx_msg.msg.mcps_indication.buffer;
00451     uint16_t base_size = _rx_msg.msg.mcps_indication.buffer_size;
00452     bool read_complete = false;
00453 
00454     if (_rx_msg.msg.mcps_indication.port != port) {
00455         // Nothing yet received for this particular port
00456         return LORAWAN_STATUS_WOULD_BLOCK;
00457     }
00458 
00459     // check if message received is a Confirmed message and user subscribed to it or not
00460     if (_rx_msg.msg.mcps_indication.type == MCPS_CONFIRMED 
00461             && ((flags & MSG_FLAG_MASK) == MSG_CONFIRMED_FLAG
00462                     || (flags & MSG_FLAG_MASK) == MSG_CONFIRMED_MULTICAST
00463                     || (flags & MSG_FLAG_MASK) == MSG_CONFIRMED_PROPRIETARY)) {
00464 
00465         tr_debug("RX - Confirmed Message, flags=%d", flags);
00466     }
00467 
00468     // check if message received is a Unconfirmed message and user subscribed to it or not
00469     if (_rx_msg.msg.mcps_indication.type == MCPS_UNCONFIRMED 
00470             && ((flags & MSG_FLAG_MASK) == MSG_UNCONFIRMED_FLAG
00471                     || (flags & MSG_FLAG_MASK) == MSG_UNCONFIRMED_MULTICAST
00472                     || (flags & MSG_FLAG_MASK) == MSG_UNCONFIRMED_PROPRIETARY)) {
00473         tr_debug("RX - Unconfirmed Message - flags=%d", flags);
00474     }
00475 
00476     // check the length of received message whether we can fit into user
00477     // buffer completely or not
00478     if (_rx_msg.msg.mcps_indication.buffer_size > length &&
00479             _rx_msg.prev_read_size == 0) {
00480         // we can't fit into user buffer. Invoke counter measures
00481         _rx_msg.pending_size = _rx_msg.msg.mcps_indication.buffer_size - length;
00482         base_size = length;
00483         _rx_msg.prev_read_size = base_size;
00484         memcpy(data, base_ptr, base_size);
00485     } else if (_rx_msg.prev_read_size == 0) {
00486         _rx_msg.pending_size = 0;
00487         _rx_msg.prev_read_size = 0;
00488         memcpy(data, base_ptr, base_size);
00489         read_complete = true;
00490     }
00491 
00492     // If its the pending read then we should copy only the remaining part of
00493     // the buffer. Due to checks above, in case of a pending read, this block
00494     // will be the only one to get invoked
00495     if (_rx_msg.pending_size > 0 && _rx_msg.prev_read_size > 0) {
00496         memcpy(data, base_ptr+_rx_msg.prev_read_size, base_size);
00497     }
00498 
00499     // we are done handing over received buffer to user. check if there is
00500     // anything pending. If not, memset the buffer to zero and indicate
00501     // that no read is in progress
00502     if (read_complete) {
00503         memset(_rx_msg.msg.mcps_indication.buffer, 0, LORAMAC_PHY_MAXPAYLOAD);
00504         _rx_msg.receive_ready = false;
00505     }
00506 
00507     return base_size;
00508 }
00509 
00510 void LoRaWANStack::mlme_confirm_handler(loramac_mlme_confirm_t  *mlme_confirm)
00511 {
00512     switch (mlme_confirm->req_type ) {
00513         case MLME_JOIN :
00514             if (mlme_confirm->status  == LORAMAC_EVENT_INFO_STATUS_OK ) {
00515                 // Status is OK, node has joined the network
00516                 if (lora_state_machine(DEVICE_STATE_JOINED) != LORAWAN_STATUS_OK) {
00517                     tr_error("Lora state machine did not return LORAWAN_STATUS_OK");
00518                 }
00519             } else {
00520                 // Join attempt failed.
00521                 if (lora_state_machine(DEVICE_STATE_IDLE) != LORAWAN_STATUS_IDLE) {
00522                     tr_error("Lora state machine did not return DEVICE_STATE_IDLE !");
00523                 }
00524 
00525                 if (_callbacks.events) {
00526                     const int ret = _queue->call(_callbacks.events, JOIN_FAILURE);
00527                     MBED_ASSERT(ret != 0);
00528                     (void)ret;
00529                 }
00530             }
00531             break;
00532         case MLME_LINK_CHECK :
00533             if (mlme_confirm->status  == LORAMAC_EVENT_INFO_STATUS_OK ) {
00534                 // Check DemodMargin
00535                 // Check NbGateways
00536 #if defined(LORAWAN_COMPLIANCE_TEST)
00537                 if (_compliance_test.running == true) {
00538                     _compliance_test.link_check = true;
00539                     _compliance_test.demod_margin = mlme_confirm->demod_margin ;
00540                     _compliance_test.nb_gateways = mlme_confirm->nb_gateways ;
00541                 } else
00542 #endif
00543                 {
00544                     // normal operation as oppose to compliance testing
00545                     if (_callbacks.link_check_resp) {
00546                         const int ret = _queue->call(_callbacks.link_check_resp,
00547                                                      mlme_confirm->demod_margin ,
00548                                                      mlme_confirm->nb_gateways );
00549                         MBED_ASSERT(ret != 0);
00550                         (void)ret;
00551                     }
00552                 }
00553             }
00554             break;
00555         default:
00556             break;
00557     }
00558 }
00559 
00560 void LoRaWANStack::mcps_confirm_handler(loramac_mcps_confirm_t  *mcps_confirm)
00561 {
00562     if (mcps_confirm->status  != LORAMAC_EVENT_INFO_STATUS_OK ) {
00563         _loramac.reset_ongoing_tx();
00564 
00565         tr_error("mcps_confirm_handler: Error code = %d", mcps_confirm->status );
00566 
00567         if (mcps_confirm->status  == LORAMAC_EVENT_INFO_STATUS_TX_TIMEOUT ) {
00568             if (_callbacks.events) {
00569                 const int ret = _queue->call(_callbacks.events, TX_TIMEOUT);
00570                 MBED_ASSERT(ret != 0);
00571                 (void)ret;
00572             }
00573             return;
00574         } else if (mcps_confirm->status  == LORAMAC_EVENT_INFO_STATUS_RX2_TIMEOUT ) {
00575             tr_debug("Did not receive Ack");
00576         }
00577 
00578         if (_callbacks.events) {
00579             const int ret = _queue->call(_callbacks.events, TX_ERROR);
00580             MBED_ASSERT(ret != 0);
00581             (void)ret;
00582         }
00583         return;
00584     }
00585 
00586     if (mcps_confirm->req_type  == MCPS_CONFIRMED  &&
00587         mcps_confirm->ack_received ) {
00588             tr_debug("Ack received.");
00589     }
00590 
00591     _lw_session.uplink_counter = mcps_confirm->ul_frame_counter ;
00592     _loramac.set_tx_ongoing(false);
00593      if (_callbacks.events) {
00594          const int ret = _queue->call(_callbacks.events, TX_DONE);
00595          MBED_ASSERT(ret != 0);
00596          (void)ret;
00597      }
00598 }
00599 
00600 void LoRaWANStack::mcps_indication_handler(loramac_mcps_indication_t  *mcps_indication)
00601 {
00602     if (mcps_indication->status  != LORAMAC_EVENT_INFO_STATUS_OK ) {
00603         if (_callbacks.events) {
00604             const int ret = _queue->call(_callbacks.events, RX_ERROR);
00605             MBED_ASSERT(ret != 0);
00606             (void)ret;
00607         }
00608         return;
00609     }
00610 
00611     switch (mcps_indication->type ) {
00612         case MCPS_UNCONFIRMED :
00613             break;
00614         case MCPS_CONFIRMED :
00615             break;
00616         case MCPS_PROPRIETARY :
00617             break;
00618         case MCPS_MULTICAST :
00619             break;
00620         default:
00621             break;
00622     }
00623 
00624     //TODO:
00625     // Check Multicast
00626     // Check Port
00627     // Check Datarate
00628     // Check FramePending
00629     // Check Buffer
00630     // Check BufferSize
00631     // Check Rssi
00632     // Check Snr
00633     // Check RxSlot
00634 
00635     _lw_session.downlink_counter++;
00636 
00637 #if defined(LORAWAN_COMPLIANCE_TEST)
00638     if (_compliance_test.running == true) {
00639         _compliance_test.downlink_counter++;
00640     }
00641 #endif
00642 
00643     if (mcps_indication->is_data_recvd  == true) {
00644         switch (mcps_indication->port ) {
00645             case 224: {
00646 #if defined(LORAWAN_COMPLIANCE_TEST)
00647                 tr_debug("Compliance test command received.");
00648                 compliance_test_handler(mcps_indication);
00649 #else
00650                 tr_info("Compliance test disabled.");
00651 #endif
00652                 break;
00653             }
00654             default: {
00655                 if (is_port_valid(mcps_indication->port ) == true ||
00656                         mcps_indication->type  == MCPS_PROPRIETARY ) {
00657 
00658                     // Valid message arrived.
00659                     _rx_msg.type = LORAMAC_RX_MCPS_INDICATION;
00660                     _rx_msg.msg.mcps_indication.buffer_size = mcps_indication->buffer_size ;
00661                     _rx_msg.msg.mcps_indication.port = mcps_indication->port ;
00662                     _rx_msg.msg.mcps_indication.buffer = mcps_indication->buffer ;
00663 
00664                     // Notify application about received frame..
00665                     tr_debug("Received %d bytes", _rx_msg.msg.mcps_indication.buffer_size);
00666                     _rx_msg.receive_ready = true;
00667 
00668                     if (_callbacks.events) {
00669                         const int ret = _queue->call(_callbacks.events, RX_DONE);
00670                         MBED_ASSERT(ret != 0);
00671                         (void)ret;
00672                     }
00673 
00674                     //TODO: below if clauses can be combined,
00675                     //      because those are calling same function with same parameters
00676 
00677                     // If fPending bit is set we try to generate an empty packet
00678                     // with CONFIRMED flag set. We always set a CONFIRMED flag so
00679                     // that we could retry a certain number of times if the uplink
00680                     // failed for some reason
00681                     if (_loramac.get_device_class() != CLASS_C  && mcps_indication->fpending_status ) {
00682                         handle_tx(mcps_indication->port , NULL, 0, MSG_CONFIRMED_FLAG, true);
00683                     }
00684 
00685                     // Class C and node received a confirmed message so we need to
00686                     // send an empty packet to acknowledge the message.
00687                     // This scenario is unspecified by LoRaWAN 1.0.2 specification,
00688                     // but version 1.1.0 says that network SHALL not send any new
00689                     // confirmed messages until ack has been sent
00690                     if (_loramac.get_device_class() == CLASS_C  && mcps_indication->type  == MCPS_CONFIRMED ) {
00691                         handle_tx(mcps_indication->port , NULL, 0, MSG_CONFIRMED_FLAG, true);
00692                     }
00693                 } else {
00694                     // Invalid port, ports 0, 224 and 225-255 are reserved.
00695                 }
00696                 break;
00697             }
00698         }
00699     }
00700 }
00701 
00702 lorawan_status_t LoRaWANStack::set_link_check_request()
00703 {
00704     _link_check_requested = true;
00705     if (!_callbacks.link_check_resp) {
00706         tr_error("Must assign a callback function for link check request. ");
00707         return LORAWAN_STATUS_PARAMETER_INVALID;
00708     }
00709 
00710     _loramac.setup_link_check_request();
00711     return LORAWAN_STATUS_OK;
00712 }
00713 
00714 void LoRaWANStack::remove_link_check_request()
00715 {
00716     _link_check_requested = false;
00717 }
00718 
00719 lorawan_status_t LoRaWANStack::shutdown()
00720 {
00721     return lora_state_machine(DEVICE_STATE_SHUTDOWN);
00722 }
00723 
00724 lorawan_status_t LoRaWANStack::set_device_class(const device_class_t & device_class)
00725 {
00726     if (device_class == CLASS_B ) {
00727         return LORAWAN_STATUS_UNSUPPORTED;
00728     }
00729     _loramac.set_device_class(device_class);
00730     return LORAWAN_STATUS_OK;
00731 }
00732 
00733 lorawan_status_t LoRaWANStack::lora_state_machine(device_states_t new_state)
00734 {
00735     lorawan_status_t status = LORAWAN_STATUS_DEVICE_OFF;
00736 
00737     _device_current_state = new_state;
00738 
00739     switch (_device_current_state) {
00740         case DEVICE_STATE_SHUTDOWN:
00741             /*
00742              * Remove channels
00743              * Radio will be put to sleep by the APIs underneath
00744              */
00745             drop_channel_list();
00746             _loramac.disconnect();
00747 
00748 #if defined(LORAWAN_COMPLIANCE_TEST)
00749             _loramac.LoRaMacStopTxTimer();
00750 #endif
00751             _loramac.set_nwk_joined(false);
00752 
00753             _loramac.reset_ongoing_tx(true);
00754 
00755             _rx_msg.msg.mcps_indication.buffer = NULL;
00756             _rx_msg.receive_ready = false;
00757             _rx_msg.prev_read_size = 0;
00758             _rx_msg.msg.mcps_indication.buffer_size = 0;
00759 
00760             _lw_session.active = false;
00761 
00762             tr_debug("LoRaWAN protocol has been shut down.");
00763             if (_callbacks.events) {
00764                 const int ret = _queue->call(_callbacks.events, DISCONNECTED);
00765                 MBED_ASSERT(ret != 0);
00766                 (void)ret;
00767             }
00768             status = LORAWAN_STATUS_DEVICE_OFF;
00769             break;
00770         case DEVICE_STATE_NOT_INITIALIZED:
00771             status = LORAWAN_STATUS_DEVICE_OFF;
00772             break;
00773         case DEVICE_STATE_INIT:
00774             status = LORAWAN_STATUS_OK;
00775             break;
00776         case DEVICE_STATE_JOINING:
00777             if (_lw_session.connection.connect_type == LORAWAN_CONNECTION_OTAA) {
00778                 tr_debug("Send Join-request..");
00779 
00780                 status = _loramac.join_by_otaa(_lw_session.connection.connection_u.otaa);
00781                 if (status != LORAWAN_STATUS_OK) {
00782                     return status;
00783                 }
00784 
00785                 return LORAWAN_STATUS_CONNECT_IN_PROGRESS;
00786             } else {
00787                 status = LORAWAN_STATUS_PARAMETER_INVALID;
00788             }
00789             break;
00790         case DEVICE_STATE_JOINED:
00791             tr_debug("Join OK!");
00792 
00793             _lw_session.active = true;
00794 
00795             if (_callbacks.events) {
00796                 const int ret = _queue->call(_callbacks.events, CONNECTED);
00797                 MBED_ASSERT(ret != 0);
00798                 (void)ret;
00799             }
00800             status = LORAWAN_STATUS_OK;
00801             break;
00802         case DEVICE_STATE_ABP_CONNECTING:
00803 
00804             _loramac.join_by_abp(_lw_session.connection.connection_u.abp);
00805 
00806             tr_debug("ABP Connection OK!");
00807 
00808             status = LORAWAN_STATUS_OK;
00809 
00810             _lw_session.active = true;
00811             if (_callbacks.events) {
00812                 const int ret = _queue->call(_callbacks.events, CONNECTED);
00813                 MBED_ASSERT(ret != 0);
00814                 (void)ret;
00815             }
00816             break;
00817         case DEVICE_STATE_SEND:
00818             if (_loramac.tx_ongoing()) {
00819                 status = LORAWAN_STATUS_OK;
00820             } else {
00821                 _loramac.set_tx_ongoing(true);
00822                 status = _loramac.send_ongoing_tx();
00823 
00824                 switch (status) {
00825                     case LORAWAN_STATUS_OK:
00826                         tr_debug("Frame scheduled to TX..");
00827                         break;
00828                     case LORAWAN_STATUS_CRYPTO_FAIL:
00829                         tr_error("Crypto failed. Clearing TX buffers");
00830                         if (_callbacks.events) {
00831                             const int ret = _queue->call(_callbacks.events, TX_CRYPTO_ERROR);
00832                             MBED_ASSERT(ret != 0);
00833                             (void)ret;
00834                         }
00835                         break;
00836                     default:
00837                         tr_error("Failure to schedule TX!");
00838                         if (_callbacks.events) {
00839                             const int ret = _queue->call(_callbacks.events, TX_SCHEDULING_ERROR);
00840                             MBED_ASSERT(ret != 0);
00841                             (void)ret;
00842                         }
00843                         break;
00844                 }
00845             }
00846 
00847             _device_current_state = DEVICE_STATE_IDLE;
00848             break;
00849         case DEVICE_STATE_IDLE:
00850             status = LORAWAN_STATUS_IDLE;
00851             break;
00852 #if defined(LORAWAN_COMPLIANCE_TEST)
00853         case DEVICE_STATE_COMPLIANCE_TEST:
00854             tr_debug("Device is in compliance test mode.");
00855 
00856             _loramac.LoRaMacSetTxTimer(5000); //ms
00857             if (_compliance_test.running == true) {
00858                 send_compliance_test_frame_to_mac();
00859             }
00860             status = LORAWAN_STATUS_COMPLIANCE_TEST_ON;
00861             break;
00862 #endif
00863         default:
00864             status = LORAWAN_STATUS_SERVICE_UNKNOWN;
00865             break;
00866     }
00867 
00868     return status;
00869 }
00870 
00871 #if defined(LORAWAN_COMPLIANCE_TEST)
00872 
00873 lorawan_status_t LoRaWANStack::send_compliance_test_frame_to_mac()
00874 {
00875     loramac_compliance_test_req_t test_req;
00876 
00877     //TODO: What if the port is not 224 ???
00878     if (_compliance_test.app_port == 224) {
00879         // Clear any normal message stuff before compliance test.
00880         memset(&test_req, 0, sizeof(test_req));
00881 
00882         if (_compliance_test.link_check == true) {
00883             _compliance_test.link_check = false;
00884             _compliance_test.state = 1;
00885             test_req.f_buffer_size = 3;
00886             test_req.f_buffer[0] = 5;
00887             test_req.f_buffer[1] = _compliance_test.demod_margin;
00888             test_req.f_buffer[2] = _compliance_test.nb_gateways;
00889         } else {
00890             switch (_compliance_test.state) {
00891             case 4:
00892                 _compliance_test.state = 1;
00893                 test_req.f_buffer_size = _compliance_test.app_data_size;
00894                 test_req.f_buffer[0] = _compliance_test.app_data_buffer[0];
00895                 for(uint8_t i = 1; i < MIN(_compliance_test.app_data_size, MBED_CONF_LORA_TX_MAX_SIZE); ++i) {
00896                     test_req.f_buffer[i] = _compliance_test.app_data_buffer[i];
00897                 }
00898                 break;
00899             case 1:
00900                 test_req.f_buffer_size = 2;
00901                 test_req.f_buffer[0] = _compliance_test.downlink_counter >> 8;
00902                 test_req.f_buffer[1] = _compliance_test.downlink_counter;
00903                 break;
00904             }
00905         }
00906     }
00907 
00908     //TODO: If port is not 224, this might not work!
00909     //Is there a test case where same _tx_msg's buffer would be used, when port is not 224???
00910     if (!_compliance_test.is_tx_confirmed) {
00911         test_req.type = MCPS_UNCONFIRMED ;
00912         test_req.fport = _compliance_test.app_port;
00913         test_req.nb_trials = 1;
00914         test_req.data_rate = _loramac.get_default_tx_datarate();
00915 
00916         tr_info("Transmit unconfirmed compliance test frame %d bytes.", test_req.f_buffer_size);
00917 
00918         for (uint8_t i = 0; i < test_req.f_buffer_size; ++i) {
00919             tr_info("Byte %d, data is 0x%x", i+1, ((uint8_t*)test_req.f_buffer)[i]);
00920         }
00921     } else if (_compliance_test.is_tx_confirmed) {
00922         test_req.type = MCPS_CONFIRMED ;
00923         test_req.fport = _compliance_test.app_port;
00924         test_req.nb_trials = _num_retry;
00925         test_req.data_rate = _loramac.get_default_tx_datarate();
00926 
00927         tr_info("Transmit confirmed compliance test frame %d bytes.", test_req.f_buffer_size);
00928 
00929         for (uint8_t i = 0; i < test_req.f_buffer_size; ++i) {
00930             tr_info("Byte %d, data is 0x%x", i+1, ((uint8_t*)test_req.f_buffer)[i]);
00931         }
00932     } else {
00933         return LORAWAN_STATUS_SERVICE_UNKNOWN;
00934     }
00935 
00936     return _loramac.test_request(&test_req);
00937 }
00938 
00939 void LoRaWANStack::compliance_test_handler(loramac_mcps_indication_t  *mcps_indication)
00940 {
00941     if (_compliance_test.running == false) {
00942         // Check compliance test enable command (i)
00943         if ((mcps_indication->buffer_size  == 4) &&
00944             (mcps_indication->buffer [0] == 0x01) &&
00945             (mcps_indication->buffer [1] == 0x01) &&
00946             (mcps_indication->buffer [2] == 0x01) &&
00947             (mcps_indication->buffer [3] == 0x01)) {
00948             _compliance_test.is_tx_confirmed = false;
00949             _compliance_test.app_port = 224;
00950             _compliance_test.app_data_size = 2;
00951             _compliance_test.downlink_counter = 0;
00952             _compliance_test.link_check = false;
00953             _compliance_test.demod_margin = 0;
00954             _compliance_test.nb_gateways = 0;
00955             _compliance_test.running = true;
00956             _compliance_test.state = 1;
00957 
00958             _loramac.enable_adaptive_datarate(true);
00959 
00960 #if MBED_CONF_LORA_PHY      == 0
00961             _loramac.LoRaMacTestSetDutyCycleOn(false);
00962 #endif
00963             //5000ms
00964             _loramac.LoRaMacSetTxTimer(5000);
00965 
00966             //TODO: Should we call lora_state_machine here instead of just setting the state?
00967             _device_current_state = DEVICE_STATE_COMPLIANCE_TEST;
00968 //            lora_state_machine(DEVICE_STATE_COMPLIANCE_TEST);
00969             tr_debug("Compliance test activated.");
00970         }
00971     } else {
00972         _compliance_test.state = mcps_indication->buffer [0];
00973         switch (_compliance_test.state) {
00974         case 0: // Check compliance test disable command (ii)
00975             _compliance_test.is_tx_confirmed = true;
00976             _compliance_test.app_port = MBED_CONF_LORA_APP_PORT;
00977             _compliance_test.app_data_size = LORAWAN_COMPLIANCE_TEST_DATA_SIZE;
00978             _compliance_test.downlink_counter = 0;
00979             _compliance_test.running = false;
00980 
00981             _loramac.enable_adaptive_datarate(MBED_CONF_LORA_ADR_ON);
00982 
00983 #if MBED_CONF_LORA_PHY      == 0
00984             _loramac.LoRaMacTestSetDutyCycleOn(MBED_CONF_LORA_DUTY_CYCLE_ON);
00985 #endif
00986             // Go to idle state after compliance test mode.
00987             tr_debug("Compliance test disabled.");
00988             _loramac.LoRaMacStopTxTimer();
00989 
00990             // Clear any compliance test message stuff before going back to normal operation.
00991             _loramac.reset_ongoing_tx();
00992             lora_state_machine(DEVICE_STATE_IDLE);
00993             break;
00994         case 1: // (iii, iv)
00995             _compliance_test.app_data_size = 2;
00996             break;
00997         case 2: // Enable confirmed messages (v)
00998             _compliance_test.is_tx_confirmed = true;
00999             _compliance_test.state = 1;
01000             break;
01001         case 3:  // Disable confirmed messages (vi)
01002             _compliance_test.is_tx_confirmed = false;
01003             _compliance_test.state = 1;
01004             break;
01005         case 4: // (vii)
01006             _compliance_test.app_data_size = mcps_indication->buffer_size ;
01007 
01008             _compliance_test.app_data_buffer[0] = 4;
01009             for(uint8_t i = 1; i < MIN(_compliance_test.app_data_size, LORAMAC_PHY_MAXPAYLOAD); ++i) {
01010                 _compliance_test.app_data_buffer[i] = mcps_indication->buffer [i] + 1;
01011             }
01012 
01013             send_compliance_test_frame_to_mac();
01014             break;
01015         case 5: // (viii)
01016             _loramac.setup_link_check_request();
01017             break;
01018         case 6: // (ix)
01019             // Disable TestMode and revert back to normal operation
01020             _compliance_test.is_tx_confirmed = true;
01021             _compliance_test.app_port = MBED_CONF_LORA_APP_PORT;
01022             _compliance_test.app_data_size = LORAWAN_COMPLIANCE_TEST_DATA_SIZE;
01023             _compliance_test.downlink_counter = 0;
01024             _compliance_test.running = false;
01025 
01026             _loramac.enable_adaptive_datarate(MBED_CONF_LORA_ADR_ON);
01027 
01028 #if MBED_CONF_LORA_PHY      == 0
01029             _loramac.LoRaMacTestSetDutyCycleOn(MBED_CONF_LORA_DUTY_CYCLE_ON);
01030 #endif
01031             _loramac.join_by_otaa(_lw_session.connection.connection_u.otaa);
01032             break;
01033         case 7: // (x)
01034             if (mcps_indication->buffer_size  == 3) {
01035                 loramac_mlme_req_t mlme_req;
01036                 mlme_req.type = MLME_TXCW ;
01037                 mlme_req.cw_tx_mode.timeout = (uint16_t)((mcps_indication->buffer [1] << 8) | mcps_indication->buffer [2]);
01038                 _loramac.mlme_request(&mlme_req);
01039             } else if (mcps_indication->buffer_size  == 7) {
01040                 loramac_mlme_req_t mlme_req;
01041                 mlme_req.type = MLME_TXCW_1 ;
01042                 mlme_req.cw_tx_mode.timeout = (uint16_t)((mcps_indication->buffer [1] << 8) | mcps_indication->buffer [2]);
01043                 mlme_req.cw_tx_mode.frequency = (uint32_t)((mcps_indication->buffer [3] << 16) | (mcps_indication->buffer [4] << 8)
01044                         | mcps_indication->buffer [5]) * 100;
01045                 mlme_req.cw_tx_mode.power = mcps_indication->buffer [6];
01046                 _loramac.mlme_request(&mlme_req);
01047             }
01048             _compliance_test.state = 1;
01049             break;
01050         }
01051     }
01052 }
01053 #endif
01054