Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[gen2, threading] Fixes Cellular taking up to 10 minutes to shutoff modem with Cellular.off() / System.sleep(config) if there is no cell connection [ch73242] #2284

Merged
merged 4 commits into from
Feb 25, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion system/src/system_network_cellular.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ class CellularNetworkInterface : public ManagedIPNetworkInterface<CellularConfig
virtual void on_finalize_listening(bool complete) override { /* n/a */ }

virtual void on_start_listening() override {
connect_cancelled = false;
cellular_cancel(false, true, NULL); // resume
}

Expand Down Expand Up @@ -84,6 +85,7 @@ class CellularNetworkInterface : public ManagedIPNetworkInterface<CellularConfig

int on_now() override {
// Resume unconditionally
connect_cancelled = false;
cellular_cancel(false, HAL_IsISR(), nullptr);
cellular_result_t ret = cellular_on(nullptr);
if (ret != 0) {
Expand Down Expand Up @@ -166,7 +168,7 @@ class CellularNetworkInterface : public ManagedIPNetworkInterface<CellularConfig
ATOMIC_BLOCK() {
if (connecting || !SPARK_WLAN_STARTED)
{
if (cancel!=connect_cancelled) {
if (cancel != connect_cancelled) {
require_cancel = true;
connect_cancelled = cancel;
}
Expand Down
16 changes: 13 additions & 3 deletions system/src/system_sleep.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,11 +82,9 @@ static int system_sleep_network_resume(network_interface_index index) {
return SYSTEM_ERROR_NONE;
}

int system_sleep_ext(const hal_sleep_config_t* config, hal_wakeup_source_base_t** reason, void* reserved) {
int system_sleep_ext_impl(const hal_sleep_config_t* config, hal_wakeup_source_base_t** reason, void* reserved) {
SYSTEM_THREAD_CONTEXT_SYNC(system_sleep_ext(config, reason, reserved));

LOG(TRACE, "Entering system_sleep_ext()");

// Validates the sleep configuration previous to disconnecting network,
// so that the network status remains if the configuration is invalid.
CHECK(hal_sleep_validate_config(config, nullptr));
Expand Down Expand Up @@ -221,3 +219,15 @@ int system_sleep_ext(const hal_sleep_config_t* config, hal_wakeup_source_base_t*

return ret;
}

int system_sleep_ext(const hal_sleep_config_t* config, hal_wakeup_source_base_t** reason, void* reserved) {
LOG(TRACE, "Entering system_sleep_ext()");
#if HAL_PLATFORM_GEN == 2
// Cancel current connection attempt to unblock the system thread
// on Gen 2 platforms
if (network_connecting(NETWORK_INTERFACE_ALL, 0, NULL)) {
network_connect_cancel(NETWORK_INTERFACE_ALL, 1, 0, 0);
}
#endif // HAL_PLATFORM_GEN == 2
return system_sleep_ext_impl(config, reason, reserved);
}
14 changes: 0 additions & 14 deletions user/tests/wiring/cell_connect_after_off/application.cpp

This file was deleted.

This file was deleted.

5 changes: 0 additions & 5 deletions user/tests/wiring/cell_connect_after_off/readme.md

This file was deleted.

39 changes: 39 additions & 0 deletions user/tests/wiring/cellular_no_antenna/application.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Copyright (c) 2021 Particle Industries, Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation, either
* version 3 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/

#ifndef PARTICLE_TEST_RUNNER
#include "application.h"
#include "unit-test/unit-test.h"

SYSTEM_MODE(MANUAL);

// make clean all TEST=wiring/cellular_no_antenna PLATFORM=electron -s COMPILE_LTO=n program-dfu
// make clean all TEST=wiring/cellular_no_antenna PLATFORM=electron -s COMPILE_LTO=n program-dfu USE_THREADING=y
//
// Serial1LogHandler logHandler(115200, LOG_LEVEL_ALL, {
// { "comm", LOG_LEVEL_NONE }, // filter out comm messages
// { "system", LOG_LEVEL_INFO } // only info level for system messages
// });

UNIT_TEST_APP();

// Enable threading if compiled with "USE_THREADING=y"
#if PLATFORM_THREADING == 1 && USE_THREADING == 1
SYSTEM_THREAD(ENABLED);
#endif

#endif // PARTICLE_TEST_RUNNER
193 changes: 193 additions & 0 deletions user/tests/wiring/cellular_no_antenna/cellular_no_antenna.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
/*
* Copyright (c) 2021 Particle Industries, Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation, either
* version 3 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/


/*
*************************************************
* Keep antenna disconnected for all tests below *
*************************************************
*/

#include "application.h"
#include "unit-test/unit-test.h"

// Serial1LogHandler logHandler(115200, LOG_LEVEL_ALL);

namespace {

STARTUP(System.enableFeature(FEATURE_RETAINED_MEMORY));
static retained uint32_t magick = 0;
static retained uint32_t phase = 0;

} // anonymous

#if Wiring_Cellular

void disconnect_from_cloud(system_tick_t timeout) {
Particle.disconnect();
waitFor(Particle.disconnected, timeout);

Cellular.disconnect();
waitForNot(Cellular.ready, timeout);

// Avoids some sort of race condition in AUTOMATIC mode
delay(1000);
}
void connect_to_cloud(system_tick_t timeout) {
Particle.connect();
waitFor(Particle.connected, timeout);
}
// Global variable to indicate a connection attempt
int g_state_conn_attempt = 0;
void nwstatus_callback_handler(system_event_t ev, int param) {
if (param == network_status_connecting){
g_state_conn_attempt = 1;
}
}
int network_is_connecting() {
return g_state_conn_attempt;
}

/*
* The following illustrates the problem statement:
* > Keep antenna disconnected for this test
* 1. With antenna disconnected, run `Particle.connect()`
* 2. Run `Cellular.off()`
* 3. After 60 sec, run `Particle.connect()` again, and verify that you see cellular AT traffic to turn on the modem or connect to the network.
*/
test(CELLULAR_NO_ANTENNA_01_conn_after_off) {
/* This test should only be run with threading enabled */
if (system_thread_get_state(nullptr) != spark::feature::ENABLED) {
skip();
return;
}

if (phase == 0xbeef0002) {
Serial.println(" >> Device is reset from hibernate mode.");
assertEqual(System.resetReason(), (int)RESET_REASON_PIN_RESET);
} else {
// Connect to Particle cloud
Particle.connect();
delay(30000);
// Power off the cell radio
Cellular.off();
delay(60000);
// Callback handler for network_status
System.on(network_status, nwstatus_callback_handler);
// clear g_state_conn_attempt just-in-case
g_state_conn_attempt = 0;
// Check that Particle.connect() attempts to work after the delay
Particle.connect();
// Wait sometime for Particle.connect() to try
waitFor(network_is_connecting, 30000);
// Verify that a connection attempt has been made
assertEqual(g_state_conn_attempt, 1);
}
}

// Test for ch73242
test(CELLULAR_NO_ANTENNA_02_device_will_poweroff_quickly_when_modem_cannot_connect) {
/* This test should only be run with threading enabled */
if (system_thread_get_state(nullptr) != spark::feature::ENABLED) {
skip();
return;
}

if (phase == 0xbeef0002) {
Serial.println(" >> Device is reset from hibernate mode.");
assertEqual(System.resetReason(), (int)RESET_REASON_PIN_RESET);
} else {
const system_tick_t waitMs[9] = {2000, 4000, 5000, 7500, 10000, 12500, 15000, 25000};
// Callback handler for network_status
System.on(network_status, nwstatus_callback_handler);
// clear g_state_conn_attempt just-in-case
g_state_conn_attempt = 0;
for (int x = 0; x < 9; x++) {
Particle.connect();
// Log.info("delaying: %lu", waitMs[x]);
delay(waitMs[x]);
assertTrue(Particle.disconnected());
// cellular_cancel(true, false, NULL); // Workaround: call before Cellular.off() / System.sleep(config);
Cellular.off();
waitFor(Cellular.isOff, 60000);
assertTrue(Cellular.isOff());
}
// Check that Particle.connect() attempts to work after the delay
Particle.connect();
// Wait sometime for Particle.connect() to try
waitFor(network_is_connecting, 30000);
// Verify that a connection attempt has been made
assertEqual(g_state_conn_attempt, 1);
}
}

// Test for ch73242
test(CELLULAR_NO_ANTENNA_03a_device_will_sleep_quickly_when_modem_cannot_connect) {
/* This test should only be run with threading enabled */
if (system_thread_get_state(nullptr) != spark::feature::ENABLED) {
skip();
return;
}

if (magick != 0xdeadbeef) {
magick = 0xdeadbeef;
phase = 0xbeef0001;
}
if (phase == 0xbeef0001) {
Serial.println(" >> Device attempts to connect to the Cloud for 25s.");
Particle.connect();
delay(25000);
assertTrue(Particle.disconnected());
Serial.println(" >> Device is still trying to connect...");
Serial.println(" >> Device enters hibernate mode.");
Serial.println(" >> Please reconnect serial and type 't' immediately after you press the reset button.");
Serial.println(" >> Press any key now - if device does not sleep within 60s, consider it a failure.");
while (Serial.available() <= 0);
while (Serial.available() > 0) {
(void)Serial.read();
}

phase = 0xbeef0002;

// cellular_cancel(true, false, NULL); // Workaround: call before Cellular.off() / System.sleep(config);
SystemSleepConfiguration config;
config.mode(SystemSleepMode::HIBERNATE);
SystemSleepResult result = System.sleep(config);
assertEqual(result.error(), SYSTEM_ERROR_NONE);
} else if (phase == 0xbeef0002) {
Serial.println(" >> Device is reset from hibernate mode.");
assertEqual(System.resetReason(), (int)RESET_REASON_PIN_RESET);
}
}

test(CELLULAR_NO_ANTENNA_03b_device_will_sleep_quickly_when_modem_cannot_connect) {
/* This test should only be run with threading enabled */
if (system_thread_get_state(nullptr) != spark::feature::ENABLED) {
skip();
return;
}

if (phase == 0xbeef0002) {
phase = 0xbeef0001;
assertEqual(System.resetReason(), (int)RESET_REASON_PIN_RESET);
} else {
Serial.println(" >> Please run Test 3a first!");
fail();
}
}

#endif // Wiring_Cellular
18 changes: 18 additions & 0 deletions user/tests/wiring/cellular_no_antenna/test.mk
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
INCLUDE_DIRS += $(SOURCE_PATH)/$(USRSRC) # add user sources to include path
# add C and CPP files - if USRSRC is not empty, then add a slash
CPPSRC += $(call target_files,$(USRSRC_SLASH),*.cpp)
CSRC += $(call target_files,$(USRSRC_SLASH),*.c)

APPSOURCES=$(call target_files,$(USRSRC_SLASH),*.cpp)
APPSOURCES+=$(call target_files,$(USRSRC_SLASH),*.c)
ifeq ($(strip $(APPSOURCES)),)
$(error "No sources found in $(SOURCE_PATH)/$(USRSRC)")
endif

ifeq ("${USE_THREADING}","y")
USE_THREADING_VALUE=1
else
USE_THREADING_VALUE=0
endif

CFLAGS += -DUSE_THREADING=${USE_THREADING_VALUE}
Loading