From 94dcab9d691de5b294ea3752176bf2f4ff3b8003 Mon Sep 17 00:00:00 2001 From: Ari Parkkila Date: Mon, 9 Sep 2019 06:14:20 -0700 Subject: [PATCH] Cellular: Add API to clear CellularDevice A new API `CellularDevice::clear()` to clean-up the modem to a default initial state. Function is virtual so it can be overridden. The default implementation clears all PDP contexts, but the the first one if that has APN defined as `nsapi.default-cellular-apn`. CellularStateMachine calls `clear()` to clean-up the modem on initial `connect()`, if the flag `cellular.clear-on-connect: true` is defined. --- UNITTESTS/stubs/AT_CellularDevice_stub.cpp | 5 ++ UNITTESTS/stubs/AT_CellularNetwork_stub.cpp | 5 ++ UNITTESTS/stubs/CellularDevice_stub.cpp | 5 ++ .../cellular/framework/API/CellularDevice.h | 12 ++++ .../framework/AT/AT_CellularDevice.cpp | 9 +++ .../cellular/framework/AT/AT_CellularDevice.h | 2 + .../framework/AT/AT_CellularNetwork.cpp | 66 +++++++++++++++++++ .../framework/AT/AT_CellularNetwork.h | 7 ++ .../framework/device/CellularDevice.cpp | 7 +- .../framework/device/CellularStateMachine.cpp | 14 ++++ features/cellular/mbed_lib.json | 4 ++ 11 files changed, 135 insertions(+), 1 deletion(-) diff --git a/UNITTESTS/stubs/AT_CellularDevice_stub.cpp b/UNITTESTS/stubs/AT_CellularDevice_stub.cpp index b9ff15aa07..a5da7a1f3f 100644 --- a/UNITTESTS/stubs/AT_CellularDevice_stub.cpp +++ b/UNITTESTS/stubs/AT_CellularDevice_stub.cpp @@ -256,3 +256,8 @@ void AT_CellularDevice::cellular_callback(nsapi_event_t ev, intptr_t ptr, Cellul void AT_CellularDevice::set_at_urcs_impl() { } + +nsapi_error_t AT_CellularDevice::clear() +{ + return NSAPI_ERROR_OK; +} diff --git a/UNITTESTS/stubs/AT_CellularNetwork_stub.cpp b/UNITTESTS/stubs/AT_CellularNetwork_stub.cpp index 15be16c94a..6305b2aba0 100644 --- a/UNITTESTS/stubs/AT_CellularNetwork_stub.cpp +++ b/UNITTESTS/stubs/AT_CellularNetwork_stub.cpp @@ -166,3 +166,8 @@ nsapi_error_t AT_CellularNetwork::set_packet_domain_event_reporting(bool on) { return NSAPI_ERROR_OK; } + +nsapi_error_t AT_CellularNetwork::clear() +{ + return NSAPI_ERROR_OK; +} diff --git a/UNITTESTS/stubs/CellularDevice_stub.cpp b/UNITTESTS/stubs/CellularDevice_stub.cpp index 0d89887cbd..86a3b74935 100644 --- a/UNITTESTS/stubs/CellularDevice_stub.cpp +++ b/UNITTESTS/stubs/CellularDevice_stub.cpp @@ -124,3 +124,8 @@ nsapi_error_t CellularDevice::shutdown() void CellularDevice::cellular_callback(nsapi_event_t ev, intptr_t ptr, CellularContext *ctx) { } + +nsapi_error_t CellularDevice::clear() +{ + return NSAPI_ERROR_OK; +} diff --git a/features/cellular/framework/API/CellularDevice.h b/features/cellular/framework/API/CellularDevice.h index 122750b7cf..5a3e99c046 100644 --- a/features/cellular/framework/API/CellularDevice.h +++ b/features/cellular/framework/API/CellularDevice.h @@ -90,6 +90,18 @@ public: */ virtual ~CellularDevice(); + /** Clear modem to a default initial state + * + * Clear persistent user data from the modem, such as PDP contexts. + * + * @pre All open network services on modem, such as contexts and sockets, must be closed. + * @post Modem power off/on may be needed to clear modem's runtime state. + * @remark CellularStateMachine calls this on connect when `cellular.clear-on-connect: true`. + * + * @return NSAPI_ERROR_OK on success, otherwise modem may be need power cycling + */ + virtual nsapi_error_t clear(); + /** Sets the modem up for powering on * This is equivalent to plugging in the device, i.e., attaching power and serial port. * In general, hard_power_on and soft_power_on provides a simple hardware abstraction layer diff --git a/features/cellular/framework/AT/AT_CellularDevice.cpp b/features/cellular/framework/AT/AT_CellularDevice.cpp index 8835d76a1e..bbfb92e06d 100644 --- a/features/cellular/framework/AT/AT_CellularDevice.cpp +++ b/features/cellular/framework/AT/AT_CellularDevice.cpp @@ -624,3 +624,12 @@ void AT_CellularDevice::cellular_callback(nsapi_event_t ev, intptr_t ptr, Cellul } CellularDevice::cellular_callback(ev, ptr, ctx); } + +nsapi_error_t AT_CellularDevice::clear() +{ + AT_CellularNetwork *net = static_cast(open_network()); + nsapi_error_t err = net->clear(); + close_network(); + + return err; +} diff --git a/features/cellular/framework/AT/AT_CellularDevice.h b/features/cellular/framework/AT/AT_CellularDevice.h index 10ceebefef..0e984a853b 100755 --- a/features/cellular/framework/AT/AT_CellularDevice.h +++ b/features/cellular/framework/AT/AT_CellularDevice.h @@ -39,6 +39,8 @@ public: AT_CellularDevice(FileHandle *fh); virtual ~AT_CellularDevice(); + virtual nsapi_error_t clear(); + virtual nsapi_error_t hard_power_on(); virtual nsapi_error_t hard_power_off(); diff --git a/features/cellular/framework/AT/AT_CellularNetwork.cpp b/features/cellular/framework/AT/AT_CellularNetwork.cpp index 5d3b4f7020..4e35dcb4fa 100644 --- a/features/cellular/framework/AT/AT_CellularNetwork.cpp +++ b/features/cellular/framework/AT/AT_CellularNetwork.cpp @@ -631,3 +631,69 @@ nsapi_error_t AT_CellularNetwork::set_packet_domain_event_reporting(bool on) return _at.at_cmd_discard("+CGEREP", "=", "%d", on ? 1 : 0); } + +nsapi_error_t AT_CellularNetwork::clear() +{ + tr_info("AT_CellularNetwork::clear"); + + _at.lock(); + _at.cmd_start_stop("+CGDCONT", "?"); + _at.resp_start("+CGDCONT:"); + + struct context_s { + int id; + context_s *next; + }; + CellularList contexts; + while (_at.info_resp()) { + int cid = _at.read_int(); + // clear all but the default context + if (cid <= 0) { + continue; + } else if (cid == 1) { +#ifndef MBED_CONF_NSAPI_DEFAULT_CELLULAR_APN + continue; +#else + char pdp_type_from_context[10]; + int pdp_type_len = _at.read_string(pdp_type_from_context, sizeof(pdp_type_from_context)); + if (pdp_type_len > 0) { + char apn[MAX_ACCESSPOINT_NAME_LENGTH]; + int apn_len = _at.read_string(apn, sizeof(apn)); + if (apn_len >= 0) { + if (strcmp(apn, MBED_CONF_NSAPI_DEFAULT_CELLULAR_APN) == 0) { + continue; + } + } + } +#endif + } + contexts.add_new()->id = cid; + } + _at.resp_stop(); + + if (contexts.get_head()) { + // try to detach from network before deleting contexts + (void)detach(); + context_s *context = contexts.get_head(); + while (context) { + if (_at.at_cmd_discard("+CGDCONT", "=", "%d", context->id) != NSAPI_ERROR_OK) { + tr_warn("Clear context %d failed", context->id); + } + context = context->next; + } +#ifdef MBED_CONF_NSAPI_DEFAULT_CELLULAR_APN + char pdp_type_str[sizeof("IPV4V6")]; + if (get_property(PROPERTY_IPV4V6_PDP_TYPE) || + (get_property(PROPERTY_IPV4_PDP_TYPE) && get_property(PROPERTY_IPV6_PDP_TYPE))) { + strcpy(pdp_type_str, "IPV4V6"); + } else if (get_property(PROPERTY_IPV6_PDP_TYPE)) { + strcpy(pdp_type_str, "IPV6"); + } else { + strcpy(pdp_type_str, "IP"); + } + _at.at_cmd_discard("+CGDCONT", "=", "%d%s%s", 1, pdp_type_str, MBED_CONF_NSAPI_DEFAULT_CELLULAR_APN); +#endif + } + + return _at.unlock_return_error(); +} diff --git a/features/cellular/framework/AT/AT_CellularNetwork.h b/features/cellular/framework/AT/AT_CellularNetwork.h index 04cf48b22a..fa265bcd91 100644 --- a/features/cellular/framework/AT/AT_CellularNetwork.h +++ b/features/cellular/framework/AT/AT_CellularNetwork.h @@ -110,6 +110,13 @@ protected: * Can be overridden by the target class. */ virtual void get_context_state_command(); + + /** Clear the network and contexts to a known default state + * + * @return NSAPI_ERROR_OK on success + */ + nsapi_error_t clear(); + private: void urc_creg(); void urc_cereg(); diff --git a/features/cellular/framework/device/CellularDevice.cpp b/features/cellular/framework/device/CellularDevice.cpp index aea7437b09..c3501368f2 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(8 * EVENTS_EVENT_SIZE), _state_machine(0), _nw(0), _status_cb(0) + _info_ref_count(0), _fh(fh), _queue(10 * EVENTS_EVENT_SIZE), _state_machine(0), _nw(0), _status_cb(0) { MBED_ASSERT(fh); set_sim_pin(NULL); @@ -250,4 +250,9 @@ void CellularDevice::set_retry_timeout_array(const uint16_t timeout[], int array } } +nsapi_error_t CellularDevice::clear() +{ + return NSAPI_ERROR_OK; +} + } // namespace mbed diff --git a/features/cellular/framework/device/CellularStateMachine.cpp b/features/cellular/framework/device/CellularStateMachine.cpp index b7e378ea6f..9ed4390079 100644 --- a/features/cellular/framework/device/CellularStateMachine.cpp +++ b/features/cellular/framework/device/CellularStateMachine.cpp @@ -345,6 +345,13 @@ bool CellularStateMachine::device_ready() } #endif // MBED_CONF_CELLULAR_DEBUG_AT +#ifdef MBED_CONF_CELLULAR_CLEAR_ON_CONNECT + if (_cellularDevice.clear() != NSAPI_ERROR_OK) { + tr_warning("CellularDevice clear failed"); + return false; + } +#endif + send_event_cb(CellularDeviceReady); _cellularDevice.set_ready_cb(0); @@ -364,6 +371,13 @@ void CellularStateMachine::state_device_ready() if (device_ready()) { _status = 0; enter_to_state(STATE_SIM_PIN); + } else { + tr_warning("Power cycle CellularDevice and restart connecting"); + (void) _cellularDevice.soft_power_off(); + (void) _cellularDevice.hard_power_off(); + _status = 0; + _is_retry = true; + enter_to_state(STATE_INIT); } } else { _status = 0; diff --git a/features/cellular/mbed_lib.json b/features/cellular/mbed_lib.json index bec9c8eaa8..99ed0af98c 100644 --- a/features/cellular/mbed_lib.json +++ b/features/cellular/mbed_lib.json @@ -24,6 +24,10 @@ "offload-dns-queries" : { "help": "Use modem IP stack for DNS queries, null or numeric simultaneous queries", "value": null + }, + "clear-on-connect" : { + "help": "Clear modem to a known default state on connect()", + "value": true } } }