diff --git a/UNITTESTS/stubs/AT_CellularContext_stub.cpp b/UNITTESTS/stubs/AT_CellularContext_stub.cpp index dbecebe18d..87bc66765b 100644 --- a/UNITTESTS/stubs/AT_CellularContext_stub.cpp +++ b/UNITTESTS/stubs/AT_CellularContext_stub.cpp @@ -20,8 +20,8 @@ using namespace mbed; AT_CellularContext::AT_CellularContext(ATHandler &at, CellularDevice *device, const char *apn, bool cp_req, bool nonip_req) : - AT_CellularBase(at), _is_blocking(true), _is_connected(false), - _current_op(OP_INVALID), _nw(0), _fh(0), _cp_req(cp_req), _nonip_req(nonip_req), _cp_in_use(false) + AT_CellularBase(at), _is_connected(false), + _current_op(OP_INVALID), _fh(0), _cp_req(cp_req), _nonip_req(nonip_req), _cp_in_use(false) { _stack = NULL; _pdp_type = DEFAULT_PDP_TYPE; @@ -37,7 +37,12 @@ AT_CellularContext::AT_CellularContext(ATHandler &at, CellularDevice *device, co _new_context_set = false; _next = NULL; _cp_netif = NULL; + memset(_retry_timeout_array, 0, CELLULAR_RETRY_ARRAY_SIZE); + _retry_array_length = 0; + _retry_count = 0; + _is_blocking = true; _device = device; + _nw = NULL; } AT_CellularContext::~AT_CellularContext() @@ -251,10 +256,6 @@ void AT_CellularContext::cellular_callback(nsapi_event_t ev, intptr_t ptr) { } -void AT_CellularContext::call_network_cb(nsapi_connection_status_t status) -{ -} - ControlPlane_netif *AT_CellularContext::get_cp_netif() { return NULL; @@ -281,3 +282,8 @@ void AT_CellularContext::deactivate_non_ip_context() void AT_CellularContext::set_disconnect() { } + +void AT_CellularContext::do_connect_with_retry() +{ + +} diff --git a/UNITTESTS/stubs/CellularContext_stub.cpp b/UNITTESTS/stubs/CellularContext_stub.cpp index 1abd036f40..0ef0cf9b13 100644 --- a/UNITTESTS/stubs/CellularContext_stub.cpp +++ b/UNITTESTS/stubs/CellularContext_stub.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) , Arm Limited and affiliates. + * Copyright (c) 2018, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -17,7 +17,7 @@ #include "CellularContext.h" -using namespace mbed; +namespace mbed { void CellularContext::cp_data_received() { @@ -28,3 +28,25 @@ CellularDevice *CellularContext::get_device() const { return _device; } + +void CellularContext::do_connect_with_retry() +{ + do_connect(); +} + +void CellularContext::do_connect() +{ + _cb_data.error = NSAPI_ERROR_OK; +} + +void CellularContext::call_network_cb(nsapi_connection_status_t status) +{ + if (_connect_status != status) { + _connect_status = status; + if (_status_cb) { + _status_cb(NSAPI_EVENT_CONNECTION_STATUS_CHANGE, _connect_status); + } + } +} + +} diff --git a/UNITTESTS/stubs/CellularStateMachine_stub.cpp b/UNITTESTS/stubs/CellularStateMachine_stub.cpp index 4fa4de49f1..6e9d0ffe34 100644 --- a/UNITTESTS/stubs/CellularStateMachine_stub.cpp +++ b/UNITTESTS/stubs/CellularStateMachine_stub.cpp @@ -74,3 +74,8 @@ void CellularStateMachine::set_cellular_callback(mbed::Callback(CellularContext::get_device()); } +void AT_CellularContext::do_connect_with_retry() +{ + CellularContext::do_connect_with_retry(); +} + nsapi_error_t AT_CellularContext::connect() { tr_info("CellularContext connect"); @@ -129,15 +139,15 @@ nsapi_error_t AT_CellularContext::connect() nsapi_error_t err = _device->attach_to_network(); _cb_data.error = check_operation(err, OP_CONNECT); - + _retry_count = 0; if (_is_blocking) { if (_cb_data.error == NSAPI_ERROR_OK || _cb_data.error == NSAPI_ERROR_ALREADY) { - do_connect(); + do_connect_with_retry(); } } else { if (_cb_data.error == NSAPI_ERROR_ALREADY) { // device is already attached, to be async we must use queue to connect and give proper callbacks - int id = _device->get_queue()->call_in(0, this, &AT_CellularContext::do_connect); + int id = _device->get_queue()->call_in(0, this, &AT_CellularContext::do_connect_with_retry); if (id == 0) { return NSAPI_ERROR_NO_MEMORY; } @@ -580,8 +590,6 @@ void AT_CellularContext::do_connect() _cb_data.error = open_data_channel(); _at.unlock(); if (_cb_data.error != NSAPI_ERROR_OK) { - tr_error("Failed to open data channel!"); - call_network_cb(NSAPI_STATUS_DISCONNECTED); _is_connected = false; } else { _is_context_activated = true; @@ -970,7 +978,8 @@ void AT_CellularContext::cellular_callback(nsapi_event_t ev, intptr_t ptr) if (_status_cb) { _status_cb(ev, ptr); } - do_connect(); + _retry_count = 0; + do_connect_with_retry(); return; } } @@ -1000,19 +1009,6 @@ void AT_CellularContext::cellular_callback(nsapi_event_t ev, intptr_t ptr) } } -void AT_CellularContext::call_network_cb(nsapi_connection_status_t status) -{ - if (_connect_status != status) { - _connect_status = status; - if (_status_cb) { - _status_cb(NSAPI_EVENT_CONNECTION_STATUS_CHANGE, _connect_status); - } - if (_nw && _connect_status == NSAPI_STATUS_DISCONNECTED) { - tr_info("CellularContext disconnected"); - } - } -} - ControlPlane_netif *AT_CellularContext::get_cp_netif() { tr_error("No control plane interface available from base context!"); diff --git a/features/cellular/framework/AT/AT_CellularContext.h b/features/cellular/framework/AT/AT_CellularContext.h index f7653a6e2f..96951e967e 100644 --- a/features/cellular/framework/AT/AT_CellularContext.h +++ b/features/cellular/framework/AT/AT_CellularContext.h @@ -93,12 +93,6 @@ protected: */ virtual uint32_t get_timeout_for_operation(ContextOperation op) const; - /** Helper method to call callback function if it is provided - * - * @param status connection status which is parameter in callback function - */ - void call_network_cb(nsapi_connection_status_t status); - virtual nsapi_error_t activate_non_ip_context(); virtual nsapi_error_t setup_control_plane_opt(); virtual void deactivate_non_ip_context(); @@ -123,13 +117,11 @@ private: nsapi_error_t check_operation(nsapi_error_t err, ContextOperation op); AT_CellularBase::CellularProperty pdp_type_t_to_cellular_property(pdp_type_t pdp_type); void ciot_opt_cb(mbed::CellularNetwork::CIoT_Supported_Opt ciot_opt); - + virtual void do_connect_with_retry(); private: bool _is_connected; - bool _is_blocking; ContextOperation _current_op; char _found_apn[MAX_APN_LENGTH]; - CellularNetwork *_nw; FileHandle *_fh; rtos::Semaphore _semaphore; rtos::Semaphore _cp_opt_semaphore; diff --git a/features/cellular/framework/common/CellularCommon.h b/features/cellular/framework/common/CellularCommon.h index 2b8a6303c5..86c692288f 100644 --- a/features/cellular/framework/common/CellularCommon.h +++ b/features/cellular/framework/common/CellularCommon.h @@ -21,6 +21,8 @@ #include #include "nsapi_types.h" +const int CELLULAR_RETRY_ARRAY_SIZE = 10; + struct cell_callback_data_t { nsapi_error_t error; /* possible error code */ int status_data; /* cellular_event_status related enum or other info in int format. Check cellular_event_status comments.*/ diff --git a/features/cellular/framework/device/CellularContext.cpp b/features/cellular/framework/device/CellularContext.cpp index 03273c1716..8c39912e2f 100644 --- a/features/cellular/framework/device/CellularContext.cpp +++ b/features/cellular/framework/device/CellularContext.cpp @@ -15,6 +15,8 @@ * limitations under the License. */ #include "CellularContext.h" +#include "CellularLog.h" +#include "ThisThread.h" MBED_WEAK CellularInterface *CellularInterface::get_target_default_instance() { @@ -66,4 +68,79 @@ CellularDevice *CellularContext::get_device() const return _device; } +void CellularContext::do_connect_with_retry() +{ + do_connect(); + if (_cb_data.error == NSAPI_ERROR_OK) { + return; + } + + if (_retry_count == 0) { + _device->get_retry_timeout_array(_retry_timeout_array, _retry_array_length); + } + + if (_is_blocking) { + while (_retry_count < _retry_array_length) { + tr_debug("SYNC do_connect failed with %d, retry after %d seconds", _cb_data.error, _retry_timeout_array[_retry_count]); + rtos::ThisThread::sleep_for(_retry_timeout_array[_retry_count] * 1000); + do_connect(); + if (_cb_data.error == NSAPI_ERROR_OK) { + return; + } + _retry_count++; + } + } else { + if (_retry_count < _retry_array_length) { + if (_retry_count == _retry_array_length - 1) { + // set the flag that this is the last try for ppp connect / pdp context activate + _cb_data.final_try = true; + } + tr_debug("ASYNC do_connect failed with %d, retry after %d seconds", _cb_data.error, _retry_timeout_array[_retry_count]); + int id = _device->get_queue()->call_in(_retry_timeout_array[_retry_count] * 1000, this, &CellularContext::do_connect_with_retry); + if (id == 0) { + tr_error("Failed call via eventqueue in do_connect_with_retry()"); +#if !NSAPI_PPP_AVAILABLE + _cb_data.final_try = true; + _cb_data.error = NSAPI_ERROR_NO_MEMORY; + // in PPP mode we did not activate any context, just searched the correct _cid + if (_status_cb) { + _status_cb((nsapi_event_t)CellularActivatePDPContext, (intptr_t)&_cb_data); + } + _cb_data.final_try = false; + _cb_data.error = NSAPI_ERROR_OK; +#else + call_network_cb(NSAPI_STATUS_DISCONNECTED); +#endif // !NSAPI_PPP_AVAILABLE + } + _retry_count++; + return; // don't call NSAPI_STATUS_DISCONNECTED in every failure, only the last one. + } + } + +#if NSAPI_PPP_AVAILABLE + if (_cb_data.error != NSAPI_ERROR_OK) { + tr_error("Failed to open data channel!"); + call_network_cb(NSAPI_STATUS_DISCONNECTED); + } +#endif // #if NSAPI_PPP_AVAILABLE +} + +void CellularContext::do_connect() +{ + _cb_data.error = NSAPI_ERROR_OK; +} + +void CellularContext::call_network_cb(nsapi_connection_status_t status) +{ + if (_connect_status != status) { + _connect_status = status; + if (_status_cb) { + _status_cb(NSAPI_EVENT_CONNECTION_STATUS_CHANGE, _connect_status); + } + if (_nw && _connect_status == NSAPI_STATUS_DISCONNECTED) { + tr_info("CellularContext disconnected"); + } + } +} + } // namespace mbed diff --git a/features/cellular/framework/device/CellularDevice.cpp b/features/cellular/framework/device/CellularDevice.cpp index a13efeafd8..838a671f6e 100644 --- a/features/cellular/framework/device/CellularDevice.cpp +++ b/features/cellular/framework/device/CellularDevice.cpp @@ -34,7 +34,7 @@ MBED_WEAK CellularDevice *CellularDevice::get_target_default_instance() } CellularDevice::CellularDevice(FileHandle *fh) : _network_ref_count(0), _sms_ref_count(0), - _info_ref_count(0), _fh(fh), _queue(5 * EVENTS_EVENT_SIZE), _state_machine(0), _nw(0), _status_cb(0) + _info_ref_count(0), _fh(fh), _queue(8 * EVENTS_EVENT_SIZE), _state_machine(0), _nw(0), _status_cb(0) { MBED_ASSERT(fh); set_sim_pin(NULL); @@ -67,6 +67,13 @@ CellularContext *CellularDevice::get_context_list() const return NULL; } +void CellularDevice::get_retry_timeout_array(uint16_t *timeout, int &array_len) const +{ + if (_state_machine && timeout) { + _state_machine->get_retry_timeout_array(timeout, array_len); + } +} + void CellularDevice::set_sim_pin(const char *sim_pin) { if (sim_pin) { diff --git a/features/cellular/framework/device/CellularStateMachine.cpp b/features/cellular/framework/device/CellularStateMachine.cpp index 0e7ffd5012..384aac1458 100644 --- a/features/cellular/framework/device/CellularStateMachine.cpp +++ b/features/cellular/framework/device/CellularStateMachine.cpp @@ -67,7 +67,7 @@ CellularStateMachine::CellularStateMachine(CellularDevice &device, events::Event _retry_timeout_array[7] = 128; // if around two minutes was not enough then let's wait much longer _retry_timeout_array[8] = 600; _retry_timeout_array[9] = TIMEOUT_NETWORK_MAX; - _retry_array_length = RETRY_ARRAY_SIZE; + _retry_array_length = CELLULAR_RETRY_ARRAY_SIZE; } CellularStateMachine::~CellularStateMachine() @@ -153,7 +153,7 @@ bool CellularStateMachine::open_sim() } else { // No sim pin provided even it's needed, stop state machine tr_error("PIN required but no SIM pin provided."); - _retry_count = RETRY_ARRAY_SIZE; + _retry_count = CELLULAR_RETRY_ARRAY_SIZE; return false; } } @@ -285,8 +285,8 @@ void CellularStateMachine::enter_to_state(CellularState state) void CellularStateMachine::retry_state_or_fail() { - if (++_retry_count < RETRY_ARRAY_SIZE) { - tr_debug("%s: retry %d/%d", get_state_string(_state), _retry_count, RETRY_ARRAY_SIZE); + if (++_retry_count < CELLULAR_RETRY_ARRAY_SIZE) { + tr_debug("%s: retry %d/%d", get_state_string(_state), _retry_count, CELLULAR_RETRY_ARRAY_SIZE); _event_timeout = _retry_timeout_array[_retry_count]; _is_retry = true; _cb_data.error = NSAPI_ERROR_OK; @@ -686,12 +686,20 @@ void CellularStateMachine::set_retry_timeout_array(const uint16_t timeout[], int tr_warn("set_retry_timeout_array, timeout array null or invalid length"); return; } - _retry_array_length = array_len > RETRY_ARRAY_SIZE ? RETRY_ARRAY_SIZE : array_len; + _retry_array_length = array_len > CELLULAR_RETRY_ARRAY_SIZE ? CELLULAR_RETRY_ARRAY_SIZE : array_len; for (int i = 0; i < _retry_array_length; i++) { _retry_timeout_array[i] = timeout[i]; } } +void CellularStateMachine::get_retry_timeout_array(uint16_t *timeout, int &array_len) const +{ + for (int i = 0; i < _retry_array_length; i++) { + timeout[i] = _retry_timeout_array[i]; + } + array_len = _retry_array_length; +} + } // namespace diff --git a/features/cellular/framework/device/CellularStateMachine.h b/features/cellular/framework/device/CellularStateMachine.h index 62cf201e2f..4d311f9941 100644 --- a/features/cellular/framework/device/CellularStateMachine.h +++ b/features/cellular/framework/device/CellularStateMachine.h @@ -30,8 +30,6 @@ namespace mbed { class CellularDevice; -const int RETRY_ARRAY_SIZE = 10; - /** CellularStateMachine class * * Finite State Machine for attaching to cellular network. Used by CellularDevice. @@ -133,6 +131,7 @@ private: */ void reset(); private: + void get_retry_timeout_array(uint16_t *timeout, int &array_len) const; bool power_on(); bool open_sim(); bool get_network_registration(CellularNetwork::RegistrationType type, CellularNetwork::RegistrationStatus &status, bool &is_registered); @@ -171,7 +170,7 @@ private: int _start_time; int _event_timeout; - uint16_t _retry_timeout_array[RETRY_ARRAY_SIZE]; + uint16_t _retry_timeout_array[CELLULAR_RETRY_ARRAY_SIZE]; int _retry_array_length; int _event_id; const char *_plmn;