diff --git a/doc/nrf/releases_and_maturity/releases/release-notes-changelog.rst b/doc/nrf/releases_and_maturity/releases/release-notes-changelog.rst index 3bdda9edbef..3b6d1561041 100644 --- a/doc/nrf/releases_and_maturity/releases/release-notes-changelog.rst +++ b/doc/nrf/releases_and_maturity/releases/release-notes-changelog.rst @@ -394,6 +394,7 @@ Modem libraries * :ref:`nrf_modem_lib_readme`: * Updated the :c:func:`modem_key_mgmt_cmp` function to return ``1`` if the buffer length does not match the certificate length. + * Added CEREG event tracking to ``lte_connectivity``. Libraries for networking ------------------------ diff --git a/lib/nrf_modem_lib/lte_connectivity/lte_connectivity.c b/lib/nrf_modem_lib/lte_connectivity/lte_connectivity.c index 02c8840787e..e39aeb2fb4f 100644 --- a/lib/nrf_modem_lib/lte_connectivity/lte_connectivity.c +++ b/lib/nrf_modem_lib/lte_connectivity/lte_connectivity.c @@ -34,7 +34,7 @@ BUILD_ASSERT((CONFIG_NET_CONNECTION_MANAGER_STACK_SIZE >= 1024), /* Forward declarations */ static void connection_timeout_work_fn(struct k_work *work); -int lte_connectivity_disconnect(struct conn_mgr_conn_binding *const if_conn); +static void connection_timeout_schedule(void); /* Delayable work used to handle LTE connection timeouts. */ static K_WORK_DELAYABLE_DEFINE(connection_timeout_work, connection_timeout_work_fn); @@ -42,8 +42,22 @@ static K_WORK_DELAYABLE_DEFINE(connection_timeout_work, connection_timeout_work_ /* Local reference to the network interface the l2 connection layer is bound to. */ static struct net_if *iface_bound; -/* Variable used to specify behavior when the network interface is brought down. */ -static uint8_t net_if_down_options = LTE_CONNECTIVITY_IF_DOWN_MODEM_SHUTDOWN; +/* Container and protection mutex for internal state of the connectivity binding. */ +struct lte_connectivity_state { + /* Tracks the requested behavior when the network interface is brought down. */ + enum lte_connectivity_if_down_options if_down_setting; + + /* Tracks whether a PDN bearer is currently active. */ + bool has_pdn; + + /* Tracks whether a serving cell is currently or was recently available. */ + bool has_cell; +}; + +static K_MUTEX_DEFINE(internal_state_lock); +static struct lte_connectivity_state internal_state = { + .if_down_setting = LTE_CONNECTIVITY_IF_DOWN_MODEM_SHUTDOWN +}; /* Local functions */ @@ -64,6 +78,108 @@ void nrf_modem_fault_handler(struct nrf_modem_fault_info *fault_info) fatal_error_notify_and_disconnect(); } +/* Called when we detect LTE connectivity has been gained. + * + * Marks the iface as active and cancels any pending timeout. + */ +static void become_active(void) +{ + LOG_DBG("Becoming active"); + net_if_dormant_off(iface_bound); + k_work_cancel_delayable(&connection_timeout_work); +} + +/* Called when we detect LTE connectivity has been lost. + * + * Marks the iface as dormant, and depending on persistence either schedules a timeout, or cancels + * any attempt to regain connectivity. + */ +static void become_dormant(void) +{ + int ret; + + LOG_DBG("Becoming dormant"); + net_if_dormant_on(iface_bound); + + /* Return immediately after removing IP addresses in the case where PDN has been + * deactivated due to the modem being shutdown. + */ + if (!nrf_modem_is_initialized()) { + return; + } + + if (conn_mgr_if_get_flag(iface_bound, CONN_MGR_IF_PERSISTENT)) { + /* If persistence is enabled, don't deactivate LTE. Let the modem try to + * re-establish the connection. Schedule a connection timeout work that + * deactivates LTE if the modem is not able to reconnect before the set timeout. + */ + connection_timeout_schedule(); + } else { + /* If persistence is disabled, LTE is deactivated upon a lost connection. + * Re-establishment is reliant on the application calling conn_mgr_if_connect(). + */ + ret = lte_connectivity_disconnect(NULL); + if (ret) { + LOG_ERR("lte_connectivity_disconnect, error: %d", ret); + net_mgmt_event_notify(NET_EVENT_CONN_IF_FATAL_ERROR, iface_bound); + } + } +} + +/* Updates the internal state as instructed and checks if connectivity was gained or lost. + * Not thread safe. + */ +static void update_connectivity(bool has_pdn, bool has_cell) +{ + bool had_connectivity = internal_state.has_pdn && internal_state.has_cell; + bool has_connectivity = has_pdn && has_cell; + + internal_state.has_pdn = has_pdn; + internal_state.has_cell = has_cell; + + if (had_connectivity != has_connectivity) { + if (has_connectivity) { + become_active(); + } else { + become_dormant(); + } + } +} + +static void update_has_pdn(bool has_pdn) +{ + (void)k_mutex_lock(&internal_state_lock, K_FOREVER); + + if (has_pdn != internal_state.has_pdn) { + if (has_pdn) { + LOG_DBG("Gained PDN bearer"); + } else { + LOG_DBG("Lost PDN bearer"); + } + + update_connectivity(has_pdn, internal_state.has_cell); + } + + (void)k_mutex_unlock(&internal_state_lock); +} + +static void update_has_cell(bool has_cell) +{ + (void)k_mutex_lock(&internal_state_lock, K_FOREVER); + + if (has_cell != internal_state.has_cell) { + if (has_cell) { + LOG_DBG("Gained serving cell"); + } else { + LOG_DBG("Lost serving cell"); + } + + update_connectivity(internal_state.has_pdn, has_cell); + } + + (void)k_mutex_unlock(&internal_state_lock); +} + /* Work handler that deactivates LTE. */ static void connection_timeout_work_fn(struct k_work *work) { @@ -154,9 +270,7 @@ static void on_pdn_activated(void) return; } - net_if_dormant_off(iface_bound); - - k_work_cancel_delayable(&connection_timeout_work); + update_has_pdn(true); } static void on_pdn_deactivated(void) @@ -177,32 +291,7 @@ static void on_pdn_deactivated(void) return; } - net_if_dormant_on(iface_bound); - - /* Return immediately after removing IP addresses in the case where PDN has been - * deactivated due to the modem being shutdown. - */ - if (!nrf_modem_is_initialized()) { - return; - } - - if (conn_mgr_if_get_flag(iface_bound, CONN_MGR_IF_PERSISTENT)) { - /* If persistence is enabled, don't deactivate LTE. Let the modem try to - * re-establish the connection. Schedule a connection timeout work that - * deactivate LTE if the modem is not able to reconnect before the set timeout. - */ - connection_timeout_schedule(); - } else { - /* If persistence is disabled, LTE is deactivated upon a lost connection. - * Re-establishment is reliant on the application calling conn_mgr_if_connect(). - */ - ret = lte_connectivity_disconnect(NULL); - if (ret) { - LOG_ERR("lte_lc_func_mode_set, error: %d", ret); - net_mgmt_event_notify(NET_EVENT_CONN_IF_FATAL_ERROR, iface_bound); - return; - } - } + update_has_pdn(false); } static void on_pdn_ipv6_up(void) @@ -263,16 +352,43 @@ static void pdn_event_handler(uint8_t cid, enum pdn_event event, int reason) } } +static void lte_reg_handler(const struct lte_lc_evt *const evt) +{ + if (evt->type != LTE_LC_EVT_NW_REG_STATUS) { + return; + } + + switch (evt->nw_reg_status) { + case LTE_LC_NW_REG_REGISTERED_HOME: + __fallthrough; + case LTE_LC_NW_REG_REGISTERED_ROAMING: + /* Mark serving cell as available. */ + LOG_DBG("Registered to serving cell"); + update_has_cell(true); + break; + case LTE_LC_NW_REG_SEARCHING: + /* Searching for a new cell, do not consider this cell loss unless it + * fails (which will generate a new LTE_LC_EVT_NW_REG_STATUS event with + * an unregistered status). + */ + break; + default: + LOG_DBG("Not registered to serving cell"); + /* Mark the serving cell as lost. */ + update_has_cell(false); + break; + } +} + /* Public APIs */ void lte_connectivity_init(struct conn_mgr_conn_binding *if_conn) { int ret; int timeout = CONFIG_LTE_CONNECTIVITY_CONNECT_TIMEOUT_SECONDS; - int persistent = IS_ENABLED(CONFIG_LTE_CONNECTIVITY_CONNECTION_PERSISTENCE); net_if_dormant_on(if_conn->iface); - /* Default auto features off. */ + /* Apply requested flag overrides */ if (!IS_ENABLED(CONFIG_LTE_CONNECTIVITY_AUTO_CONNECT)) { ret = conn_mgr_if_set_flag(if_conn->iface, CONN_MGR_IF_NO_AUTO_CONNECT, true); if (ret) { @@ -291,9 +407,17 @@ void lte_connectivity_init(struct conn_mgr_conn_binding *if_conn) } } - /* Set default values for connection persistence and timeout */ + if (IS_ENABLED(CONFIG_LTE_CONNECTIVITY_CONNECTION_PERSISTENCE)) { + ret = conn_mgr_if_set_flag(if_conn->iface, CONN_MGR_IF_PERSISTENT, true); + if (ret) { + LOG_ERR("conn_mgr_if_set_flag, error: %d", ret); + net_mgmt_event_notify(NET_EVENT_CONN_IF_FATAL_ERROR, if_conn->iface); + return; + } + } + + /* Set default values for timeouts */ if_conn->timeout = (timeout > CONN_MGR_IF_NO_TIMEOUT) ? timeout : CONN_MGR_IF_NO_TIMEOUT; - if_conn->flags |= (persistent == 1) ? BIT(CONN_MGR_IF_PERSISTENT) : 0; if (if_conn->timeout > CONN_MGR_IF_NO_TIMEOUT) { LOG_DBG("Connection timeout is enabled and set to %d seconds", if_conn->timeout); @@ -312,6 +436,9 @@ void lte_connectivity_init(struct conn_mgr_conn_binding *if_conn) return; } + /* Register handler for registration status notifications */ + lte_lc_register_handler(lte_reg_handler); + /* Keep local reference to the network interface that the connectivity layer is bound to. */ iface_bound = if_conn->iface; @@ -346,13 +473,21 @@ int lte_connectivity_disable(void) { int ret; - if (net_if_down_options == LTE_CONNECTIVITY_IF_DOWN_MODEM_SHUTDOWN) { + enum lte_connectivity_if_down_options if_down_setting; + + (void)k_mutex_lock(&internal_state_lock, K_FOREVER); + + if_down_setting = internal_state.if_down_setting; + + (void)k_mutex_unlock(&internal_state_lock); + + if (if_down_setting == LTE_CONNECTIVITY_IF_DOWN_MODEM_SHUTDOWN) { ret = nrf_modem_lib_shutdown(); if (ret) { LOG_ERR("nrf_modem_lib_shutdown, retval: %d", ret); return ret; } - } else if (net_if_down_options == LTE_CONNECTIVITY_IF_DOWN_LTE_DISCONNECT) { + } else if (if_down_setting == LTE_CONNECTIVITY_IF_DOWN_LTE_DISCONNECT) { ret = lte_connectivity_disconnect(NULL); if (ret) { LOG_ERR("lte_connectivity_disconnect, retval: %d", ret); @@ -408,29 +543,38 @@ int lte_connectivity_options_set(struct conn_mgr_conn_binding *const if_conn, in { ARG_UNUSED(name); - uint8_t *temp; + switch (name) { + case LTE_CONNECTIVITY_IF_DOWN: + { + uint8_t if_down_setting; - if (name != LTE_CONNECTIVITY_IF_DOWN) { - return -ENOPROTOOPT; - } + if (length > sizeof(if_down_setting)) { + return -ENOBUFS; + } - if (length > sizeof(uint8_t)) { - return -ENOBUFS; - } + if_down_setting = *((uint8_t *)value); - temp = (uint8_t *)value; - net_if_down_options = *temp; + (void)k_mutex_lock(&internal_state_lock, K_FOREVER); - switch (net_if_down_options) { - case LTE_CONNECTIVITY_IF_DOWN_MODEM_SHUTDOWN: - LOG_DBG("Shutdown modem when the network interface is brought down"); - break; - case LTE_CONNECTIVITY_IF_DOWN_LTE_DISCONNECT: - LOG_DBG("Disconnected from LTE when the network interface is brought down"); + internal_state.if_down_setting = if_down_setting; + + (void)k_mutex_unlock(&internal_state_lock); + + switch (if_down_setting) { + case LTE_CONNECTIVITY_IF_DOWN_MODEM_SHUTDOWN: + LOG_DBG("Shutdown modem when the network interface is brought down"); + break; + case LTE_CONNECTIVITY_IF_DOWN_LTE_DISCONNECT: + LOG_DBG("Disconnected from LTE when the network interface is brought down"); + break; + default: + LOG_ERR("Unsupported option"); + return -EBADF; + } break; + } default: - LOG_ERR("Unsupported option"); - return -EBADF; + return -ENOPROTOOPT; } return 0; @@ -439,16 +583,27 @@ int lte_connectivity_options_set(struct conn_mgr_conn_binding *const if_conn, in int lte_connectivity_options_get(struct conn_mgr_conn_binding *const if_conn, int name, void *value, size_t *length) { - ARG_UNUSED(name); + switch (name) { + case LTE_CONNECTIVITY_IF_DOWN: + { + uint8_t *if_down_setting_out = (uint8_t *) value; - uint8_t *temp = (uint8_t *)value; + if (*length < sizeof(*if_down_setting_out)) { + return -ENOBUFS; + } + + *length = sizeof(*if_down_setting_out); + + (void)k_mutex_lock(&internal_state_lock, K_FOREVER); + + *if_down_setting_out = (uint8_t) internal_state.if_down_setting; - if (name != LTE_CONNECTIVITY_IF_DOWN) { + (void)k_mutex_unlock(&internal_state_lock); + break; + } + default: return -ENOPROTOOPT; } - *temp = net_if_down_options; - *length = sizeof(net_if_down_options); - return 0; } diff --git a/tests/lib/nrf_modem_lib/lte_connectivity/prj.conf b/tests/lib/nrf_modem_lib/lte_connectivity/prj.conf index 8fda5468ae2..d46e29b4ad3 100644 --- a/tests/lib/nrf_modem_lib/lte_connectivity/prj.conf +++ b/tests/lib/nrf_modem_lib/lte_connectivity/prj.conf @@ -15,6 +15,9 @@ CONFIG_NET_TEST=y CONFIG_NET_CONNECTION_MANAGER=y CONFIG_NET_CONNECTION_MANAGER_STACK_SIZE=1024 +# Disable auto-down to make test-case expectations simpler +CONFIG_NET_CONNECTION_MANAGER_AUTO_IF_DOWN=n + # Disable DAD CONFIG_NET_IPV6_NBR_CACHE=n CONFIG_NET_IPV6_MLD=n diff --git a/tests/lib/nrf_modem_lib/lte_connectivity/src/lte_connectivity_test.c b/tests/lib/nrf_modem_lib/lte_connectivity/src/lte_connectivity_test.c index 469fe4cd406..2946629c0ef 100644 --- a/tests/lib/nrf_modem_lib/lte_connectivity/src/lte_connectivity_test.c +++ b/tests/lib/nrf_modem_lib/lte_connectivity/src/lte_connectivity_test.c @@ -24,6 +24,14 @@ extern int unity_main(void); static pdn_event_handler_t pdn_event_handler_callback; +static lte_lc_evt_handler_t lte_lc_event_handler_callback; + +/* Initial condition tracking */ +static bool initial_conditions_observed; +static bool initial_persistence; +static bool initial_auto_connect; +static bool initial_auto_down; +static int initial_connect_timeout; /* Stub used to register test local PDN event handler used to invoke PDN event in UUT. */ static int pdn_default_ctx_cb_reg_stub(pdn_event_handler_t cb, int num_of_calls) @@ -32,6 +40,12 @@ static int pdn_default_ctx_cb_reg_stub(pdn_event_handler_t cb, int num_of_calls) return 0; } +/* Stub used to register test local CEREG event handler used to invoke CEREG event in UUT. */ +static void lte_lc_register_handler_stub(lte_lc_evt_handler_t cb, int num_of_calls) +{ + lte_lc_event_handler_callback = cb; +} + static void bring_network_interface_up(void) { struct net_if *net_if = net_if_get_default(); @@ -55,72 +69,74 @@ static void network_interface_down_option_set(enum lte_connectivity_if_down_opti TEST_ASSERT_EQUAL(retval_expected, ret); } -static void ipv4_address_add(void) +/* Ensure consistent starting conditions for each test. */ +void setUp(void) { struct net_if *net_if = net_if_get_default(); - __mock_nrf_modem_at_scanf_ExpectAndReturn( - "AT+CGPADDR=0", "+CGPADDR: %*d,\"%46[.:0-9A-F]\",\"%46[:0-9A-F]\"", 1); - __mock_nrf_modem_at_scanf_ReturnVarg_string("192.9.201.39"); - - pdn_event_handler_callback(0, PDN_EVENT_ACTIVATED, 0); + /* Set up dummy CEREG event struct. */ + struct lte_lc_evt cereg_evt = { + .type = LTE_LC_EVT_NW_REG_STATUS, + .nw_reg_status = LTE_LC_NW_REG_UNKNOWN, + }; + + /* Observe initial conditions */ + if (!initial_conditions_observed) { + initial_conditions_observed = true; + initial_connect_timeout = conn_mgr_if_get_timeout(net_if); + initial_persistence = conn_mgr_if_get_flag(net_if, CONN_MGR_IF_PERSISTENT); + initial_auto_down = !conn_mgr_if_get_flag(net_if, CONN_MGR_IF_NO_AUTO_DOWN); + initial_auto_connect = !conn_mgr_if_get_flag(net_if, CONN_MGR_IF_NO_AUTO_CONNECT); + } - TEST_ASSERT_FALSE(net_if_flag_is_set(net_if, NET_IF_DORMANT)); - TEST_ASSERT_TRUE(net_if_ipv4_get_global_addr(net_if, NET_ADDR_PREFERRED)); -} + /* Temporarily ignore and provide default values to mocked functions */ + __cmock_nrf_modem_is_initialized_IgnoreAndReturn(1); + __cmock_lte_lc_func_mode_set_IgnoreAndReturn(0); + __cmock_nrf_modem_lib_shutdown_IgnoreAndReturn(0); -static void ipv6_address_add(void) -{ - struct net_if *net_if = net_if_get_default(); + /* Lose PDN and CEREG */ + pdn_event_handler_callback(0, PDN_EVENT_DEACTIVATED, 0); + lte_lc_event_handler_callback(&cereg_evt); - __mock_nrf_modem_at_scanf_ExpectAndReturn( - "AT+CGPADDR=0", "+CGPADDR: %*d,\"%46[.:0-9A-F]\",\"%46[:0-9A-F]\"", 2); - __mock_nrf_modem_at_scanf_ReturnVarg_string(""); - __mock_nrf_modem_at_scanf_ReturnVarg_string("2001:8c0:5140:801:1b4d:4ce8:6eed:7b69"); + /* Let events propagate */ + k_sleep(K_MSEC(10)); - pdn_event_handler_callback(0, PDN_EVENT_IPV6_UP, 0); + /* Remove IP addresses if any */ + if (net_if_ipv6_get_global_addr(NET_ADDR_PREFERRED, &net_if)) { + net_if_config_ipv6_put(net_if); + } - TEST_ASSERT_TRUE(net_if_ipv6_get_global_addr(NET_ADDR_PREFERRED, &net_if)); -} + if (net_if_ipv4_get_global_addr(net_if, NET_ADDR_PREFERRED)) { + net_if_config_ipv4_put(net_if); + } -void setUp(void) -{ - mock_nrf_modem_at_Init(); -} + /* Reset flags */ + conn_mgr_if_set_flag(net_if, CONN_MGR_IF_PERSISTENT, false); -void tearDown(void) -{ - uint8_t option; - size_t len; - struct net_if *net_if = net_if_get_default(); - enum lte_lc_func_mode mode_expected = LTE_LC_FUNC_MODE_DEACTIVATE_LTE; + /* Reset options */ + uint8_t option = LTE_CONNECTIVITY_IF_DOWN_LTE_DISCONNECT; + (void)conn_mgr_if_set_opt(net_if, LTE_CONNECTIVITY_IF_DOWN, + (const void *)&option, + sizeof(option)); - int ret = conn_mgr_if_get_opt(net_if, LTE_CONNECTIVITY_IF_DOWN, (void *)&option, &len); + /* Take iface down */ + (void)net_if_down(net_if); - TEST_ASSERT_EQUAL(0, ret); + /* Let events propagate */ + k_sleep(K_MSEC(10)); - /* Do teardown depending on the LTE_CONNECTIVITY_IF_DOWN option. */ - if (net_if_flag_is_set(net_if, NET_IF_UP)) { - if (option == LTE_CONNECTIVITY_IF_DOWN_LTE_DISCONNECT) { - __cmock_lte_lc_func_mode_set_ExpectAndReturn(mode_expected, 0); - TEST_ASSERT_EQUAL(0, net_if_down(net_if)); - } else if (option == LTE_CONNECTIVITY_IF_DOWN_MODEM_SHUTDOWN) { - __cmock_nrf_modem_lib_shutdown_ExpectAndReturn(0); - TEST_ASSERT_EQUAL(0, net_if_down(net_if)); - } else { - TEST_ASSERT_TRUE(false); - } - } + /* Stop ignoring mocked functions*/ + __cmock_nrf_modem_is_initialized_StopIgnore(); + __cmock_lte_lc_func_mode_set_StopIgnore(); + __cmock_nrf_modem_lib_shutdown_StopIgnore(); - /* Remove IP addresses if any */ - if (net_if_ipv6_get_global_addr(NET_ADDR_PREFERRED, &net_if)) { - TEST_ASSERT_EQUAL(0, net_if_config_ipv6_put(net_if)); - } - - if (net_if_ipv4_get_global_addr(net_if, NET_ADDR_PREFERRED)) { - TEST_ASSERT_EQUAL(0, net_if_config_ipv4_put(net_if)); - } + /* Prepare to track/mock modem_at_scanf calls for the upcoming test */ + mock_nrf_modem_at_Init(); +} +void tearDown(void) +{ + /* Validate modem_at_scanf calls */ mock_nrf_modem_at_Verify(); } @@ -143,11 +159,25 @@ void test_init_should_set_timeout(void) TEST_ASSERT_EQUAL(timeout_desired, conn_mgr_if_get_timeout(net_if)); } -void test_init_should_set_connection_persistent_flag(void) +/* Ensure that initial settings reflect what was selected in KConfig */ +void test_initial_settings_should_match_kconfig(void) { - struct net_if *net_if = net_if_get_default(); - - TEST_ASSERT_TRUE(conn_mgr_if_get_flag(net_if, CONN_MGR_IF_PERSISTENT)); + TEST_ASSERT_EQUAL( + IS_ENABLED(CONFIG_LTE_CONNECTIVITY_CONNECTION_PERSISTENCE), + initial_persistence + ); + TEST_ASSERT_EQUAL( + IS_ENABLED(CONFIG_LTE_CONNECTIVITY_AUTO_CONNECT), + initial_auto_connect + ); + TEST_ASSERT_EQUAL( + IS_ENABLED(CONFIG_LTE_CONNECTIVITY_AUTO_DOWN), + initial_auto_down + ); + TEST_ASSERT_EQUAL( + CONFIG_LTE_CONNECTIVITY_CONNECT_TIMEOUT_SECONDS, + initial_connect_timeout + ); } /* Expect pdn_default_ctx_cb_reg() to be called at SYS init. We need to expect this at SYS init @@ -161,6 +191,17 @@ static int test_init_should_set_pdn_event_handler(void) } SYS_INIT(test_init_should_set_pdn_event_handler, POST_KERNEL, 0); +/* Expect lte_lc_register_handler() to be called at SYS init. We need to expect this at SYS init + * due to the mock being called before the test runner. + */ +static int test_init_should_set_cereg_event_handler(void) +{ + __cmock_lte_lc_register_handler_Stub(<e_lc_register_handler_stub); + __cmock_lte_lc_register_handler_ExpectAnyArgs(); + return 0; +} +SYS_INIT(test_init_should_set_cereg_event_handler, POST_KERNEL, 0); + /* Verify lte_connectivity_enable() */ void test_enable_should_init_modem_and_link_controller(void) @@ -198,28 +239,49 @@ void test_enable_should_return_error_upon_dfu_error(void) /* Verify lte_connectivity_disable() */ +/* Verify taking the iface down also shuts down the modem if + * the IF_DOWN mode is configured as such + */ void test_disable_should_shutdown_modem(void) { + /* Set the IF_DOWN mode to SHUTDOWN_MODEM */ + network_interface_down_option_set(LTE_CONNECTIVITY_IF_DOWN_MODEM_SHUTDOWN, 0); + + /* Bring the interface up */ bring_network_interface_up(); + /* Expect that the modem will be shut down when interface is taken down. + * Schedule the shutdown to succeed. + */ __cmock_nrf_modem_lib_shutdown_ExpectAndReturn(0); + /* Take the interface down and verify that it succeeds */ TEST_ASSERT_EQUAL(0, net_if_down(net_if_get_default())); } +/* Verify taking the iface down fails if modem shutdown fails and + * the IF_DOWN mode is set to SHUTDOWN_MODEM + */ void test_disable_should_return_error_if_shutdown_of_modem_fails(void) { + /* Set the IF_DOWN mode to SHUTDOWN_MODEM */ + network_interface_down_option_set(LTE_CONNECTIVITY_IF_DOWN_MODEM_SHUTDOWN, 0); + + /* Bring the interface up */ bring_network_interface_up(); + /* Expect that the modem will be shut down when interface is taken down. + * Schedule the shutdown to fail. + */ __cmock_nrf_modem_lib_shutdown_ExpectAndReturn(-1); + /* Take the interface down and verify that it fails */ TEST_ASSERT_EQUAL(-1, net_if_down(net_if_get_default())); } void test_disable_should_disconnect_lte(void) { bring_network_interface_up(); - network_interface_down_option_set(LTE_CONNECTIVITY_IF_DOWN_LTE_DISCONNECT, 0); __cmock_lte_lc_func_mode_set_ExpectAndReturn(LTE_LC_FUNC_MODE_DEACTIVATE_LTE, 0); @@ -229,7 +291,6 @@ void test_disable_should_disconnect_lte(void) void test_disable_should_return_error_if_lte_disconnect_fails(void) { bring_network_interface_up(); - network_interface_down_option_set(LTE_CONNECTIVITY_IF_DOWN_LTE_DISCONNECT, 0); __cmock_lte_lc_func_mode_set_ExpectAndReturn(LTE_LC_FUNC_MODE_DEACTIVATE_LTE, -1); @@ -322,38 +383,73 @@ void test_options_get_should_return_success(void) { struct net_if *net_if = net_if_get_default(); uint8_t option; - size_t len; + size_t len = sizeof(option); int ret = conn_mgr_if_get_opt(net_if, LTE_CONNECTIVITY_IF_DOWN, (void *)&option, &len); TEST_ASSERT_EQUAL(0, ret); } -void test_options_get_should_return_correct_option_value(void) +/* Verify that conn_mgr_if_set_opt successfully changes the IF_DOWN option from LTE_DISCONNECT + * to MODEM_SHUTDOWN + */ +void test_options_set_should_change_connectivity_if_down_to_modem_shutdown(void) { struct net_if *net_if = net_if_get_default(); - uint8_t option = LTE_CONNECTIVITY_IF_DOWN_MODEM_SHUTDOWN; + uint8_t option = 99; size_t len; int ret; - ret = conn_mgr_if_set_opt(net_if, - LTE_CONNECTIVITY_IF_DOWN, - (const void *)&option, - sizeof(option)); + /* Verify that the IF_DOWN option starts out as LTE_DISCONNECT */ + len = sizeof(option); + ret = conn_mgr_if_get_opt(net_if, LTE_CONNECTIVITY_IF_DOWN, (void *)&option, &len); + TEST_ASSERT_EQUAL(LTE_CONNECTIVITY_IF_DOWN_LTE_DISCONNECT, option); + TEST_ASSERT_EQUAL(0, ret); + /* Attempt to change the IF_DOWN option to MODEM_SHUTDOWN */ + network_interface_down_option_set(LTE_CONNECTIVITY_IF_DOWN_MODEM_SHUTDOWN, 0); + + /* Verify that the IF_DOWN option was successfully changed */ + len = sizeof(option); + ret = conn_mgr_if_get_opt(net_if, LTE_CONNECTIVITY_IF_DOWN, (void *)&option, &len); + TEST_ASSERT_EQUAL(LTE_CONNECTIVITY_IF_DOWN_MODEM_SHUTDOWN, option); TEST_ASSERT_EQUAL(0, ret); +} + +/* Verify that conn_mgr_if_set_opt successfully changes the IF_DOWN option from MODEM_SHUTDOWN + * to LTE_DISCONNECT + */ +void test_options_set_should_change_connectivity_if_down_to_lte_disconnect(void) +{ + struct net_if *net_if = net_if_get_default(); + uint8_t option = 99; + size_t len; + int ret; + + /* Set up initial conditions by setting IF_DOWN option to MODEM_SHUTDOWN */ + network_interface_down_option_set(LTE_CONNECTIVITY_IF_DOWN_MODEM_SHUTDOWN, 0); + /* Verify that the IF_DOWN option is MODEM_SHUTDOWN */ + len = sizeof(option); ret = conn_mgr_if_get_opt(net_if, LTE_CONNECTIVITY_IF_DOWN, (void *)&option, &len); + TEST_ASSERT_EQUAL(LTE_CONNECTIVITY_IF_DOWN_MODEM_SHUTDOWN, option); + TEST_ASSERT_EQUAL(0, ret); + /* Attempt to change the IF_DOWN option to LTE_DISCONNECT */ + network_interface_down_option_set(LTE_CONNECTIVITY_IF_DOWN_LTE_DISCONNECT, 0); + + /* Verify that the IF_DOWN option was successfully changed */ + len = sizeof(option); + ret = conn_mgr_if_get_opt(net_if, LTE_CONNECTIVITY_IF_DOWN, (void *)&option, &len); + TEST_ASSERT_EQUAL(LTE_CONNECTIVITY_IF_DOWN_LTE_DISCONNECT, option); TEST_ASSERT_EQUAL(0, ret); - TEST_ASSERT_EQUAL(LTE_CONNECTIVITY_IF_DOWN_MODEM_SHUTDOWN, option); } void test_options_get_should_return_error_if_wrong_option_name_is_used(void) { struct net_if *net_if = net_if_get_default(); uint8_t option; - size_t len; + size_t len = sizeof(option); int ret; ret = conn_mgr_if_get_opt(net_if, UINT8_MAX, (void *)&option, &len); @@ -365,80 +461,488 @@ void test_options_get_should_return_error_if_wrong_option_name_is_used(void) /* IPv4 */ -void test_pdn_ipv4_address_should_be_added_on_activated(void) +/* Ensure that PDN activation and deativation adds and removes an IPv4 address if provided */ +void test_pdn_events_should_trigger_ipv4_address_changes(void) { - ipv4_address_add(); + struct net_if *net_if = net_if_get_default(); + + /* Expect lte_connectivity to check for an IPv4 address when PDN is activated */ + __mock_nrf_modem_at_scanf_ExpectAndReturn( + "AT+CGPADDR=0", "+CGPADDR: %*d,\"%46[.:0-9A-F]\",\"%46[:0-9A-F]\"", 1); + + /* Provide one */ + __mock_nrf_modem_at_scanf_ReturnVarg_string("192.9.201.39"); + + /* Activate PDN */ + pdn_event_handler_callback(0, PDN_EVENT_ACTIVATED, 0); + + /* Verify IPv4 was added */ + TEST_ASSERT_TRUE(net_if_ipv4_get_global_addr(net_if, NET_ADDR_PREFERRED)); + + /* Let events fire */ + k_sleep(K_MSEC(10)); + + /* Dectivate PDN */ + pdn_event_handler_callback(0, PDN_EVENT_DEACTIVATED, 0); + + /* Verify that IPv4 was removed */ + TEST_ASSERT_FALSE(net_if_ipv4_get_global_addr(net_if, NET_ADDR_PREFERRED)); } -void test_pdn_ipv4_fails_to_be_added_on_activated(void) +/* Ensure that PDN activation does not add an IPv4 address if none is provided */ +void test_pdn_act_should_not_add_nonexistant_ipv4(void) { struct net_if *net_if = net_if_get_default(); + /* Expect lte_connectivity to check for an IPv4 address when PDN is activated */ __mock_nrf_modem_at_scanf_ExpectAndReturn( "AT+CGPADDR=0", "+CGPADDR: %*d,\"%46[.:0-9A-F]\",\"%46[:0-9A-F]\"", 0); + + /* Do not provide one */ __cmock_lte_lc_func_mode_set_ExpectAndReturn(LTE_LC_FUNC_MODE_DEACTIVATE_LTE, 0); + /* Activate PDN */ pdn_event_handler_callback(0, PDN_EVENT_ACTIVATED, 0); + /* Verify IPv4 was not added */ TEST_ASSERT_FALSE(net_if_ipv4_get_global_addr(net_if, NET_ADDR_PREFERRED)); } -void test_pdn_ipv4_address_should_be_removed_on_deactivated(void) -{ - ipv4_address_add(); +/* IPv6 */ +/* Verify that PDN activation and deativation adds and removes an IPv6 address if provided */ +void test_pdn_events_should_trigger_ipv6_address_changes(void) +{ struct net_if *net_if = net_if_get_default(); - __cmock_nrf_modem_is_initialized_ExpectAndReturn(1); + /* Expect lte_connectivity to check for an IPv6 address when PDN is activated */ + __mock_nrf_modem_at_scanf_ExpectAndReturn( + "AT+CGPADDR=0", "+CGPADDR: %*d,\"%46[.:0-9A-F]\",\"%46[:0-9A-F]\"", 2); - pdn_event_handler_callback(0, PDN_EVENT_DEACTIVATED, 0); + /* Provide one */ + __mock_nrf_modem_at_scanf_ReturnVarg_string(""); + __mock_nrf_modem_at_scanf_ReturnVarg_string("2001:8c0:5140:801:1b4d:4ce8:6eed:7b69"); - TEST_ASSERT_TRUE(net_if_flag_is_set(net_if, NET_IF_DORMANT)); - TEST_ASSERT_FALSE(net_if_ipv4_get_global_addr(net_if, NET_ADDR_PREFERRED)); -} + /* Activate PDN */ + pdn_event_handler_callback(0, PDN_EVENT_IPV6_UP, 0); -/* IPv6 */ + /* Verify IPv6 was added */ + TEST_ASSERT_TRUE(net_if_ipv6_get_global_addr(NET_ADDR_PREFERRED, &net_if)); -void test_pdn_ipv6_address_should_be_added_on_ipv6_up(void) -{ - ipv6_address_add(); + /* Let events fire */ + k_sleep(K_MSEC(10)); + + /* Dectivate PDN */ + pdn_event_handler_callback(0, PDN_EVENT_DEACTIVATED, 0); + + /* Verify that IPv6 was removed */ + TEST_ASSERT_FALSE(net_if_ipv6_get_global_addr(NET_ADDR_PREFERRED, &net_if)); } -void test_pdn_ipv6_fails_to_be_added_on_ipv6_up(void) + +/* Verify that PDN activation does not add an IPv6 address if none is provided */ +void test_pdn_act_should_not_add_nonexistant_ipv6(void) { struct net_if *net_if = net_if_get_default(); + /* Expect lte_connectivity to check for an IPv4 address when PDN is activated */ __mock_nrf_modem_at_scanf_ExpectAndReturn( "AT+CGPADDR=0", "+CGPADDR: %*d,\"%46[.:0-9A-F]\",\"%46[:0-9A-F]\"", 0); + + /* Do not provide one */ __cmock_lte_lc_func_mode_set_ExpectAndReturn(LTE_LC_FUNC_MODE_DEACTIVATE_LTE, 0); + /* Activate PDN */ pdn_event_handler_callback(0, PDN_EVENT_ACTIVATED, 0); + /* Verify IPv6 was not added */ TEST_ASSERT_FALSE(net_if_ipv6_get_global_addr(NET_ADDR_PREFERRED, &net_if)); } -void test_pdn_ipv6_address_should_be_removed_on_ipv6_down(void) +/* CEREG and PDN readiness */ + +/* Verify that PDN activation does not activate iface if cereg state is unregistered */ +void test_pdn_act_without_cereg_should_not_activate_iface(void) { struct net_if *net_if = net_if_get_default(); - ipv6_address_add(); + /* We do not care about modem init, initialization checks, or func mode changes for this + * test. Set up some sane values, but ignore call counts. + */ + __cmock_nrf_modem_is_initialized_IgnoreAndReturn(1); + __cmock_lte_lc_func_mode_set_IgnoreAndReturn(0); + __cmock_lte_lc_init_IgnoreAndReturn(0); - pdn_event_handler_callback(0, PDN_EVENT_IPV6_DOWN, 0); + /* Take the iface admin-up */ + net_if_up(net_if); - TEST_ASSERT_FALSE(net_if_ipv6_get_global_addr(NET_ADDR_PREFERRED, &net_if)); + /* Check that the iface is still dormant. */ + TEST_ASSERT_TRUE(net_if_is_dormant(net_if)); + + /* Prepare an IP address to be provided when PDN is set to active */ + __mock_nrf_modem_at_scanf_ExpectAndReturn( + "AT+CGPADDR=0", "+CGPADDR: %*d,\"%46[.:0-9A-F]\",\"%46[:0-9A-F]\"", 1); + __mock_nrf_modem_at_scanf_ReturnVarg_string("192.9.201.39"); + + /* Fire PDN active */ + pdn_event_handler_callback(0, PDN_EVENT_ACTIVATED, 0); + + /* Let events fire */ + k_sleep(K_MSEC(10)); + + /* Check that iface remains inactive */ + TEST_ASSERT_TRUE(net_if_is_dormant(net_if)); } -void test_pdn_should_call_lte_disconnect_on_pdn_deactivated(void) +/* Verify that PDN activation activates iface if cereg state is registered */ +void test_pdn_act_with_cereg_should_activate_iface(void) { struct net_if *net_if = net_if_get_default(); - __cmock_nrf_modem_is_initialized_ExpectAndReturn(0); + /* Set up dummy CEREG event struct. */ + struct lte_lc_evt cereg_evt = { + .type = LTE_LC_EVT_NW_REG_STATUS + }; + + /* We do not care about modem init, initialization checks, or func mode changes for this + * test. Set up some sane values, but ignore call counts. + */ + __cmock_nrf_modem_is_initialized_IgnoreAndReturn(1); + __cmock_lte_lc_func_mode_set_IgnoreAndReturn(0); + __cmock_lte_lc_init_IgnoreAndReturn(0); + + /* Take the iface admin-up */ + net_if_up(net_if); + + /* Fire CEREG registered */ + cereg_evt.nw_reg_status = LTE_LC_NW_REG_REGISTERED_HOME; + lte_lc_event_handler_callback(&cereg_evt); + + /* Let events fire */ + k_sleep(K_MSEC(10)); + + /* Check that the iface is still dormant. */ + TEST_ASSERT_TRUE(net_if_is_dormant(net_if)); + + /* Prepare an IP address to be provided when PDN is set to active */ + __mock_nrf_modem_at_scanf_ExpectAndReturn( + "AT+CGPADDR=0", "+CGPADDR: %*d,\"%46[.:0-9A-F]\",\"%46[:0-9A-F]\"", 1); + __mock_nrf_modem_at_scanf_ReturnVarg_string("192.9.201.39"); + + /* Fire PDN active */ + pdn_event_handler_callback(0, PDN_EVENT_ACTIVATED, 0); + + /* Let events fire */ + k_sleep(K_MSEC(10)); + + /* Check that iface becomes active */ + TEST_ASSERT_FALSE(net_if_is_dormant(net_if)); +} + + +/* Verify that CEREG registration does not activate iface if PDN is inactive */ +void test_cereg_registered_without_pdn_should_not_activate_iface(void) +{ + struct net_if *net_if = net_if_get_default(); + + /* Set up dummy CEREG event struct. */ + struct lte_lc_evt cereg_evt = { + .type = LTE_LC_EVT_NW_REG_STATUS + }; + + /* We do not care about modem init, initialization checks, or func mode changes for this + * test. Set up some sane values, but ignore call counts. + */ + __cmock_nrf_modem_is_initialized_IgnoreAndReturn(1); + __cmock_lte_lc_func_mode_set_IgnoreAndReturn(0); + __cmock_lte_lc_init_IgnoreAndReturn(0); + + /* Take the iface admin-up */ + net_if_up(net_if); + + /* Check that the iface is still dormant. */ + TEST_ASSERT_TRUE(net_if_is_dormant(net_if)); + + /* Fire CEREG registered */ + cereg_evt.nw_reg_status = LTE_LC_NW_REG_REGISTERED_HOME; + lte_lc_event_handler_callback(&cereg_evt); + + /* Let events fire */ + k_sleep(K_MSEC(10)); + + /* Check that iface remains inactive */ + TEST_ASSERT_TRUE(net_if_is_dormant(net_if)); +} + +/* Verify that CEREG registration (home) does activate iface if PDN is active */ +void test_cereg_registered_home_with_pdn_should_activate_iface(void) +{ + struct net_if *net_if = net_if_get_default(); + + /* Set up dummy CEREG event struct. */ + struct lte_lc_evt cereg_evt = { + .type = LTE_LC_EVT_NW_REG_STATUS + }; + + /* We do not care about modem init, initialization checks, or func mode changes for this + * test. Set up some sane values, but ignore call counts. + */ + __cmock_nrf_modem_is_initialized_IgnoreAndReturn(1); + __cmock_lte_lc_func_mode_set_IgnoreAndReturn(0); + __cmock_lte_lc_init_IgnoreAndReturn(0); + + /* Take the iface admin-up */ + net_if_up(net_if); + + /* Prepare an IP address to be provided when PDN is set to active */ + __mock_nrf_modem_at_scanf_ExpectAndReturn( + "AT+CGPADDR=0", "+CGPADDR: %*d,\"%46[.:0-9A-F]\",\"%46[:0-9A-F]\"", 1); + __mock_nrf_modem_at_scanf_ReturnVarg_string("192.9.201.39"); + + /* Fire PDN active */ + pdn_event_handler_callback(0, PDN_EVENT_ACTIVATED, 0); + + /* Let events fire */ + k_sleep(K_MSEC(10)); + + /* Check that the iface is still dormant. */ + TEST_ASSERT_TRUE(net_if_is_dormant(net_if)); + + /* Fire CEREG registered (home) */ + cereg_evt.nw_reg_status = LTE_LC_NW_REG_REGISTERED_HOME; + lte_lc_event_handler_callback(&cereg_evt); + + /* Let events fire */ + k_sleep(K_MSEC(10)); + + /* Check that iface becomes active */ + TEST_ASSERT_FALSE(net_if_is_dormant(net_if)); +} + +/* Verify that CEREG registration (roaming) does activate iface if PDN is active */ +void test_cereg_registered_roaming_with_pdn_should_activate_iface(void) +{ + struct net_if *net_if = net_if_get_default(); + + /* Set up dummy CEREG event struct. */ + struct lte_lc_evt cereg_evt = { + .type = LTE_LC_EVT_NW_REG_STATUS + }; + + /* We do not care about modem init, initialization checks, or func mode changes for this + * test. Set up some sane values, but ignore call counts. + */ + __cmock_nrf_modem_is_initialized_IgnoreAndReturn(1); + __cmock_lte_lc_func_mode_set_IgnoreAndReturn(0); + __cmock_lte_lc_init_IgnoreAndReturn(0); + + /* Take the iface admin-up */ + net_if_up(net_if); + + /* Prepare an IP address to be provided when PDN is set to active */ + __mock_nrf_modem_at_scanf_ExpectAndReturn( + "AT+CGPADDR=0", "+CGPADDR: %*d,\"%46[.:0-9A-F]\",\"%46[:0-9A-F]\"", 1); + __mock_nrf_modem_at_scanf_ReturnVarg_string("192.9.201.39"); + + /* Fire PDN active */ + pdn_event_handler_callback(0, PDN_EVENT_ACTIVATED, 0); + + /* Let events fire */ + k_sleep(K_MSEC(10)); + + /* Check that the iface is still dormant. */ + TEST_ASSERT_TRUE(net_if_is_dormant(net_if)); + + /* Fire CEREG registered (roaming) */ + cereg_evt.nw_reg_status = LTE_LC_NW_REG_REGISTERED_ROAMING; + lte_lc_event_handler_callback(&cereg_evt); + + /* Let events fire */ + k_sleep(K_MSEC(10)); + + /* Check that iface becomes active */ + TEST_ASSERT_FALSE(net_if_is_dormant(net_if)); +} + +/* Verify that CEREG searching does not affect iface activation (from inactive to active) */ +void test_cereg_searching_should_not_activate_iface(void) +{ + struct net_if *net_if = net_if_get_default(); + + /* Set up dummy CEREG event struct. */ + struct lte_lc_evt cereg_evt = { + .type = LTE_LC_EVT_NW_REG_STATUS + }; + + /* We do not care about modem init, initialization checks, or func mode changes for this + * test. Set up some sane values, but ignore call counts. + */ + __cmock_nrf_modem_is_initialized_IgnoreAndReturn(1); + __cmock_lte_lc_func_mode_set_IgnoreAndReturn(0); + __cmock_lte_lc_init_IgnoreAndReturn(0); + + /* Take the iface admin-up */ + net_if_up(net_if); + + /* Check that the iface is inactive. */ + TEST_ASSERT_TRUE(net_if_is_dormant(net_if)); + + /* Fire CEREG searching */ + cereg_evt.nw_reg_status = LTE_LC_NW_REG_SEARCHING; + lte_lc_event_handler_callback(&cereg_evt); + + /* Let events fire */ + k_sleep(K_MSEC(10)); + + /* Check that iface remains active */ + TEST_ASSERT_TRUE(net_if_is_dormant(net_if)); +} + + +/* Verify that CEREG searching does not affect iface activation (from active to inactive) */ +void test_cereg_searching_should_not_deactivate_iface(void) +{ + struct net_if *net_if = net_if_get_default(); + + /* Set up dummy CEREG event struct. */ + struct lte_lc_evt cereg_evt = { + .type = LTE_LC_EVT_NW_REG_STATUS + }; - TEST_ASSERT_EQUAL(0, conn_mgr_if_set_flag(net_if, CONN_MGR_IF_PERSISTENT, false)); + /* We do not care about modem init, initialization checks, or func mode changes for this + * test. Set up some sane values, but ignore call counts. + */ + __cmock_nrf_modem_is_initialized_IgnoreAndReturn(1); + __cmock_lte_lc_func_mode_set_IgnoreAndReturn(0); + __cmock_lte_lc_init_IgnoreAndReturn(0); + + /* Take the iface admin-up */ + net_if_up(net_if); + + /* Prepare an IP address to be provided when PDN is set to active */ + __mock_nrf_modem_at_scanf_ExpectAndReturn( + "AT+CGPADDR=0", "+CGPADDR: %*d,\"%46[.:0-9A-F]\",\"%46[:0-9A-F]\"", 1); + __mock_nrf_modem_at_scanf_ReturnVarg_string("192.9.201.39"); + + /* Fire PDN active */ + pdn_event_handler_callback(0, PDN_EVENT_ACTIVATED, 0); + + /* Fire CEREG registered */ + cereg_evt.nw_reg_status = LTE_LC_NW_REG_REGISTERED_HOME; + lte_lc_event_handler_callback(&cereg_evt); + + /* Let events fire */ + k_sleep(K_MSEC(10)); + + /* Check that the iface is active. */ + TEST_ASSERT_FALSE(net_if_is_dormant(net_if)); + + /* Fire CEREG searching */ + cereg_evt.nw_reg_status = LTE_LC_NW_REG_SEARCHING; + lte_lc_event_handler_callback(&cereg_evt); + + /* Let events fire */ + k_sleep(K_MSEC(10)); + + /* Check that iface remains active */ + TEST_ASSERT_FALSE(net_if_is_dormant(net_if)); +} + +/* Verify that CEREG unregistered deactivates iface */ +void test_cereg_unregistered_should_deactivate_iface(void) +{ + struct net_if *net_if = net_if_get_default(); + /* Set up dummy CEREG event struct. */ + struct lte_lc_evt cereg_evt = { + .type = LTE_LC_EVT_NW_REG_STATUS + }; + + /* We do not care about modem init, initialization checks, or func mode changes for this + * test. Set up some sane values, but ignore call counts. + */ + __cmock_nrf_modem_is_initialized_IgnoreAndReturn(1); + __cmock_lte_lc_func_mode_set_IgnoreAndReturn(0); + __cmock_lte_lc_init_IgnoreAndReturn(0); + + /* Take the iface admin-up */ + net_if_up(net_if); + + /* Prepare an IP address to be provided when PDN is set to active */ + __mock_nrf_modem_at_scanf_ExpectAndReturn( + "AT+CGPADDR=0", "+CGPADDR: %*d,\"%46[.:0-9A-F]\",\"%46[:0-9A-F]\"", 1); + __mock_nrf_modem_at_scanf_ReturnVarg_string("192.9.201.39"); + + /* Fire PDN active */ + pdn_event_handler_callback(0, PDN_EVENT_ACTIVATED, 0); + + /* Fire CEREG registered */ + cereg_evt.nw_reg_status = LTE_LC_NW_REG_REGISTERED_HOME; + lte_lc_event_handler_callback(&cereg_evt); + + /* Let events fire */ + k_sleep(K_MSEC(10)); + + /* Check that the iface is active. */ + TEST_ASSERT_FALSE(net_if_is_dormant(net_if)); + + /* Fire CEREG unregistered */ + cereg_evt.nw_reg_status = LTE_LC_NW_REG_UNKNOWN; + lte_lc_event_handler_callback(&cereg_evt); + + /* Let events fire */ + k_sleep(K_MSEC(10)); + + /* Check that iface becomes inactive */ + TEST_ASSERT_TRUE(net_if_is_dormant(net_if)); +} + +/* Verify that PDN deactivation deactivates iface */ +void test_pdn_deact_should_deactivate_iface(void) +{ + struct net_if *net_if = net_if_get_default(); + + /* Set up dummy CEREG event struct. */ + struct lte_lc_evt cereg_evt = { + .type = LTE_LC_EVT_NW_REG_STATUS + }; + + /* We do not care about modem init, initialization checks, or func mode changes for this + * test. Set up some sane values, but ignore call counts. + */ + __cmock_nrf_modem_is_initialized_IgnoreAndReturn(1); + __cmock_lte_lc_func_mode_set_IgnoreAndReturn(0); + __cmock_lte_lc_init_IgnoreAndReturn(0); + + /* Take the iface admin-up */ + net_if_up(net_if); + + /* Prepare an IP address to be provided when PDN is set to active */ + __mock_nrf_modem_at_scanf_ExpectAndReturn( + "AT+CGPADDR=0", "+CGPADDR: %*d,\"%46[.:0-9A-F]\",\"%46[:0-9A-F]\"", 1); + __mock_nrf_modem_at_scanf_ReturnVarg_string("192.9.201.39"); + + /* Fire PDN active */ + pdn_event_handler_callback(0, PDN_EVENT_ACTIVATED, 0); + + /* Fire CEREG registered */ + cereg_evt.nw_reg_status = LTE_LC_NW_REG_REGISTERED_HOME; + lte_lc_event_handler_callback(&cereg_evt); + + /* Let events fire */ + k_sleep(K_MSEC(10)); + + /* Check that the iface is active. */ + TEST_ASSERT_FALSE(net_if_is_dormant(net_if)); + + /* Fire PDN inactive */ pdn_event_handler_callback(0, PDN_EVENT_DEACTIVATED, 0); - TEST_ASSERT_TRUE(net_if_flag_is_set(net_if, NET_IF_DORMANT)); + /* Let events fire */ + k_sleep(K_MSEC(10)); + + /* Check that iface becomes inactive */ + TEST_ASSERT_TRUE(net_if_is_dormant(net_if)); } int main(void)