Skip to content

Commit d9c783a

Browse files
committed
modules: fota: Apply image on FMFU validation event
This change addresses the NRF_CLOUD_FOTA_FMFU_VALIDATION_NEEDED event that was introduced in the updated nRF SDK version, which is currently causing CI nightly failures. Changes: - Add NETWORK_DISCONNECT event handling in network module - Implement proper state transitions for LTE disconnection - Update FOTA module with new states for FMFU validation scenario The system now correctly disconnects from LTE when the validation event is received, ensuring proper FOTA completion for FMFU. Signed-off-by: Simen S. Røstad <[email protected]>
1 parent 420000e commit d9c783a

File tree

6 files changed

+171
-15
lines changed

6 files changed

+171
-15
lines changed

app/prj.conf

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,8 @@ CONFIG_NRF_CLOUD_FOTA_DOWNLOAD_FRAGMENT_SIZE=1700
120120
CONFIG_NRF_CLOUD_COAP_DOWNLOADS=y
121121
CONFIG_FOTA_DOWNLOAD=y
122122
CONFIG_FOTA_DOWNLOAD_PROGRESS_EVT=y
123-
CONFIG_FOTA_DL_TIMEOUT_MIN=30
123+
CONFIG_FOTA_DL_TIMEOUT_MIN=60
124+
CONFIG_FOTA_SOCKET_RETRIES=5
124125
CONFIG_DFU_TARGET=y
125126

126127
# COAP client

app/src/common/message_channel.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ struct payload {
5555
enum network_status {
5656
NETWORK_DISCONNECTED = 0x1,
5757
NETWORK_CONNECTED,
58+
NETWORK_DISCONNECT_REQUEST,
5859
};
5960

6061
#define MSG_TO_NETWORK_STATUS(_msg) (*(const enum network_status *)_msg)

app/src/modules/fota/fota.c

Lines changed: 76 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,10 @@ ZBUS_MSG_SUBSCRIBER_DEFINE(fota);
3333
/* Observe channels */
3434
ZBUS_CHAN_ADD_OBS(TRIGGER_CHAN, fota, 0);
3535
ZBUS_CHAN_ADD_OBS(CLOUD_CHAN, fota, 0);
36+
ZBUS_CHAN_ADD_OBS(NETWORK_CHAN, fota, 0);
3637

37-
#define MAX_MSG_SIZE MAX(sizeof(enum trigger_type), sizeof(enum cloud_status))
38+
#define MAX_MSG_SIZE MAX(MAX(sizeof(enum trigger_type), sizeof(enum cloud_status)), \
39+
sizeof(enum network_status))
3840

3941
/* FOTA support context */
4042
static void fota_reboot(enum nrf_cloud_fota_reboot_status status);
@@ -48,13 +50,15 @@ static void fota_status(enum nrf_cloud_fota_status status, const char *const sta
4850
* STATE_WAIT_FOR_CLOUD: The FOTA module is initialized and waiting for cloud connection.
4951
* STATE_WAIT_FOR_TRIGGER: The FOTA module is waiting for a trigger to poll for updates.
5052
* STATE_POLL_AND_PROCESS: The FOTA module is polling for updates and processing them.
53+
* STATE_WAIT_FOR_NETWORK_DISCONNECT: Waiting for network to disconnect before applying FMFU.
5154
* STATE_REBOOT_PENDING: The nRF Cloud FOTA library has requested a reboot to complete an update.
5255
*/
5356
enum fota_module_state {
5457
STATE_RUNNING,
5558
STATE_WAIT_FOR_CLOUD,
5659
STATE_WAIT_FOR_TRIGGER,
5760
STATE_POLL_AND_PROCESS,
61+
STATE_WAIT_FOR_NETWORK_DISCONNECT,
5862
STATE_REBOOT_PENDING,
5963
};
6064

@@ -64,7 +68,10 @@ enum priv_fota_evt {
6468
FOTA_PRIV_PROCESSING_DONE,
6569
/* FOTA processing is completed, reboot is needed */
6670
FOTA_PRIV_REBOOT_PENDING,
71+
/* FMFU download complete, need validation of image */
72+
FOTA_PRIV_FMFU_VALIDATION_NEEDED
6773
};
74+
6875
/* User defined state object.
6976
* Used to transfer data between state changes.
7077
*/
@@ -99,6 +106,8 @@ static void state_wait_for_cloud_run(void *o);
99106
static void state_wait_for_trigger_run(void *o);
100107
static void state_poll_and_process_entry(void *o);
101108
static void state_poll_and_process_run(void *o);
109+
static void state_wait_for_network_disconnect_entry(void *o);
110+
static void state_wait_for_network_disconnect_run(void *o);
102111
static void state_reboot_pending_entry(void *o);
103112

104113
static struct s_object s_obj = {
@@ -124,6 +133,12 @@ static const struct smf_state states[] = {
124133
NULL,
125134
&states[STATE_RUNNING],
126135
NULL),
136+
[STATE_WAIT_FOR_NETWORK_DISCONNECT] =
137+
SMF_CREATE_STATE(state_wait_for_network_disconnect_entry,
138+
state_wait_for_network_disconnect_run,
139+
NULL,
140+
&states[STATE_RUNNING],
141+
NULL),
127142
[STATE_REBOOT_PENDING] =
128143
SMF_CREATE_STATE(state_reboot_pending_entry, NULL, NULL,
129144
&states[STATE_RUNNING],
@@ -224,13 +239,63 @@ static void state_poll_and_process_run(void *o)
224239
/* The FOTA module has requested a reboot to complete an update */
225240
STATE_SET(STATE_REBOOT_PENDING);
226241
break;
242+
case FOTA_PRIV_FMFU_VALIDATION_NEEDED:
243+
/* FMFU download is complete, need to validate the image */
244+
STATE_SET(STATE_WAIT_FOR_NETWORK_DISCONNECT);
245+
break;
227246
default:
228247
LOG_ERR("Unknown event: %d", evt);
229248
break;
230249
}
231250
}
232251
}
233252

253+
static void state_wait_for_network_disconnect_entry(void *o)
254+
{
255+
int err;
256+
enum network_status network_msg = NETWORK_DISCONNECT_REQUEST;
257+
258+
ARG_UNUSED(o);
259+
260+
LOG_DBG("Requesting network disconnect for FMFU application");
261+
262+
err = zbus_chan_pub(&NETWORK_CHAN, &network_msg, K_SECONDS(1));
263+
if (err) {
264+
LOG_ERR("zbus_chan_pub, error: %d", err);
265+
SEND_FATAL_ERROR();
266+
}
267+
}
268+
269+
static void state_wait_for_network_disconnect_run(void *o)
270+
{
271+
struct s_object *state_object = o;
272+
int err;
273+
274+
if (&NETWORK_CHAN == state_object->chan) {
275+
const enum network_status status = MSG_TO_NETWORK_STATUS(state_object->msg_buf);
276+
277+
if (status == NETWORK_DISCONNECTED) {
278+
LOG_DBG("Network disconnected, applying FMFU");
279+
280+
err = nrf_cloud_fota_poll_update_apply(&state_object->fota_ctx);
281+
if (err) {
282+
LOG_ERR("nrf_cloud_fota_poll_update_apply, error: %d", err);
283+
SEND_FATAL_ERROR();
284+
return;
285+
}
286+
}
287+
}
288+
289+
if (&PRIV_FOTA_CHAN == state_object->chan) {
290+
const enum priv_fota_evt evt = *(const enum priv_fota_evt *)state_object->msg_buf;
291+
292+
if (evt == FOTA_PRIV_REBOOT_PENDING) {
293+
STATE_SET(STATE_REBOOT_PENDING);
294+
return;
295+
}
296+
}
297+
}
298+
234299
static void state_reboot_pending_entry(void *o)
235300
{
236301
ARG_UNUSED(o);
@@ -357,6 +422,11 @@ static void fota_status(enum nrf_cloud_fota_status status, const char *const sta
357422
case NRF_CLOUD_FOTA_FAILED:
358423
LOG_ERR("Firmware download failed");
359424

425+
status_events_notify(FOTA_STATUS_STOP);
426+
break;
427+
case NRF_CLOUD_FOTA_CANCELED:
428+
LOG_WRN("Firmware download canceled");
429+
360430
status_events_notify(FOTA_STATUS_STOP);
361431
break;
362432
case NRF_CLOUD_FOTA_TIMED_OUT:
@@ -369,6 +439,11 @@ static void fota_status(enum nrf_cloud_fota_status status, const char *const sta
369439

370440
status_events_notify(FOTA_STATUS_STOP);
371441
return;
442+
case NRF_CLOUD_FOTA_FMFU_VALIDATION_NEEDED:
443+
LOG_DBG("Full modem FOTA validation needed");
444+
445+
evt = FOTA_PRIV_FMFU_VALIDATION_NEEDED;
446+
break;
372447
default:
373448
LOG_ERR("Unknown FOTA status: %d", status);
374449
break;

app/src/modules/location/location.c

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ ZBUS_CHAN_ADD_OBS(NETWORK_CHAN, location, 0);
4141
sizeof(enum network_status)))))))
4242

4343
static bool gnss_enabled;
44-
static bool gnss_initialized;
4544

4645
static void location_event_handler(const struct location_event_data *event_data);
4746

@@ -99,22 +98,22 @@ void trigger_location_update(void)
9998
}
10099
}
101100

102-
void handle_network_chan(enum network_status status) {
103-
int err = 0;
104-
105-
if (gnss_initialized) {
106-
return;
107-
}
101+
void handle_network_chan(enum network_status status)
102+
{
103+
int err;
108104

109105
if (status == NETWORK_CONNECTED) {
110-
/* GNSS has to be enabled after the modem is initialized and enabled */
111106
err = lte_lc_func_mode_set(LTE_LC_FUNC_MODE_ACTIVATE_GNSS);
112107
if (err) {
113108
LOG_ERR("Unable to init GNSS: %d", err);
114109
SEND_FATAL_ERROR();
115-
} else {
116-
gnss_initialized = true;
117-
LOG_DBG("GNSS initialized");
110+
}
111+
112+
} else if (status == NETWORK_DISCONNECTED) {
113+
err = lte_lc_func_mode_set(LTE_LC_FUNC_MODE_DEACTIVATE_GNSS);
114+
if (err) {
115+
LOG_ERR("Unable to init GNSS: %d", err);
116+
SEND_FATAL_ERROR();
118117
}
119118
}
120119
}

app/src/modules/network/network.c

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,10 @@ ZBUS_MSG_SUBSCRIBER_DEFINE(network);
2828
/* Observe trigger channel */
2929
ZBUS_CHAN_ADD_OBS(TRIGGER_CHAN, network, 0);
3030
ZBUS_CHAN_ADD_OBS(TIME_CHAN, network, 0);
31+
ZBUS_CHAN_ADD_OBS(NETWORK_CHAN, network, 0);
3132

32-
#define MAX_MSG_SIZE (MAX(sizeof(enum trigger_type), sizeof(enum time_status)))
33+
#define MAX_MSG_SIZE (MAX(MAX(sizeof(enum trigger_type), sizeof(enum time_status)), \
34+
sizeof(enum network_status)))
3335

3436
/* Macros used to subscribe to specific Zephyr NET management events. */
3537
#define L4_EVENT_MASK (NET_EVENT_L4_CONNECTED | NET_EVENT_L4_DISCONNECTED)
@@ -45,11 +47,14 @@ static struct net_mgmt_event_callback conn_cb;
4547
/* Module states.
4648
*
4749
* STATE_INIT: The module is initializing and waiting for time to be available.
48-
* STATE_SAMPLING: The module is ready to sample upon receiving a trigger.
50+
* STATE_SAMPLING: The module is ready to sample upon receiving a trigger.
51+
* STATE_DISCONNECTED: The module is disconnected from the network, sampling is blocked.
4952
*/
5053
enum network_module_state {
5154
STATE_INIT,
5255
STATE_SAMPLING,
56+
STATE_WAIT_FOR_NETWORK_DISCONNECT,
57+
STATE_DISCONNECTED,
5358
};
5459

5560
/* State object.
@@ -68,6 +73,8 @@ struct s_object {
6873
/* Forward declarations of state handlers */
6974
static void state_init_run(void *o);
7075
static void state_sampling_run(void *o);
76+
static void state_disconnected_entry(void *o);
77+
static void state_wait_for_network_disconnect_run(void *o);
7178

7279
static struct s_object s_obj;
7380
static const struct smf_state states[] = {
@@ -79,6 +86,14 @@ static const struct smf_state states[] = {
7986
SMF_CREATE_STATE(NULL, state_sampling_run, NULL,
8087
NULL,
8188
NULL),
89+
[STATE_WAIT_FOR_NETWORK_DISCONNECT] =
90+
SMF_CREATE_STATE(NULL, state_wait_for_network_disconnect_run, NULL,
91+
NULL,
92+
NULL),
93+
[STATE_DISCONNECTED] =
94+
SMF_CREATE_STATE(state_disconnected_entry, NULL, NULL,
95+
NULL,
96+
NULL),
8297
};
8398

8499
static void network_status_notify(enum network_status status)
@@ -244,6 +259,45 @@ static void state_sampling_run(void *obj)
244259
}
245260
}
246261
}
262+
263+
if (&NETWORK_CHAN == state_object->chan) {
264+
enum network_status status = MSG_TO_NETWORK_STATUS(state_object->msg_buf);
265+
266+
if (status == NETWORK_DISCONNECT_REQUEST) {
267+
LOG_DBG("Network disconnect request request received");
268+
269+
int err = conn_mgr_all_if_disconnect(true);
270+
271+
if (err) {
272+
LOG_ERR("conn_mgr_all_if_disconnect, error: %d", err);
273+
SEND_FATAL_ERROR();
274+
}
275+
276+
STATE_SET(STATE_WAIT_FOR_NETWORK_DISCONNECT);
277+
}
278+
}
279+
}
280+
281+
static void state_wait_for_network_disconnect_run(void *o)
282+
{
283+
struct s_object *state_object = o;
284+
285+
if (&NETWORK_CHAN == state_object->chan) {
286+
const enum network_status status = MSG_TO_NETWORK_STATUS(state_object->msg_buf);
287+
288+
if (status == NETWORK_DISCONNECTED) {
289+
LOG_DBG("Network disconnected, sampling is blocked");
290+
291+
STATE_SET(STATE_DISCONNECTED);
292+
}
293+
}
294+
}
295+
296+
static void state_disconnected_entry(void *obj)
297+
{
298+
ARG_UNUSED(obj);
299+
300+
LOG_DBG("state_disconnected_entry");
247301
}
248302

249303
static void task_wdt_callback(int channel_id, void *user_data)

tests/module/network/src/main.c

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ FAKE_VALUE_FUNC(int, task_wdt_feed, int);
2525
FAKE_VALUE_FUNC(int, task_wdt_add, uint32_t, task_wdt_callback_t, void *);
2626
FAKE_VALUE_FUNC(int, lte_lc_conn_eval_params_get, struct lte_lc_conn_eval_params *);
2727
FAKE_VALUE_FUNC(int, conn_mgr_all_if_connect, bool);
28+
FAKE_VALUE_FUNC(int, conn_mgr_all_if_disconnect, bool);
2829
FAKE_VALUE_FUNC(int, conn_mgr_all_if_up, bool);
2930
FAKE_VOID_FUNC(net_mgmt_add_event_callback, struct net_mgmt_event_callback *);
3031

@@ -96,6 +97,14 @@ static void send_trigger(void)
9697
TEST_ASSERT_EQUAL(0, err);
9798
}
9899

100+
static void send_network_disconnect_request(void)
101+
{
102+
enum network_status status = NETWORK_DISCONNECT_REQUEST;
103+
int err = zbus_chan_pub(&NETWORK_CHAN, &status, K_SECONDS(1));
104+
105+
TEST_ASSERT_EQUAL(0, err);
106+
}
107+
99108
static void wait_for_and_decode_payload(struct conn_info_object *conn_info_obj)
100109
{
101110
const struct zbus_channel *chan;
@@ -138,6 +147,7 @@ void setUp(void)
138147
RESET_FAKE(lte_lc_conn_eval_params_get);
139148
RESET_FAKE(conn_mgr_all_if_connect);
140149
RESET_FAKE(conn_mgr_all_if_up);
150+
RESET_FAKE(conn_mgr_all_if_disconnect);
141151

142152
date_time_now_fake.custom_fake = date_time_now_custom_fake;
143153
send_time_available();
@@ -238,6 +248,22 @@ void test_no_events_on_zbus_until_watchdog_timeout(void)
238248
TEST_ASSERT_GREATER_OR_EQUAL(1, task_wdt_feed_fake.call_count);
239249
}
240250

251+
void test_network_disconnect_request_calls_disconnect_function(void)
252+
{
253+
/* Given */
254+
conn_mgr_all_if_disconnect_fake.return_val = 0;
255+
256+
/* When */
257+
send_network_disconnect_request();
258+
259+
/* Allow the test thread to sleep so that the DUT's thread is allowed to run. */
260+
k_sleep(K_MSEC(100));
261+
262+
/* Then */
263+
TEST_ASSERT_EQUAL(1, conn_mgr_all_if_disconnect_fake.call_count);
264+
TEST_ASSERT_EQUAL(true, conn_mgr_all_if_disconnect_fake.arg0_val);
265+
}
266+
241267
/* This is required to be added to each test. That is because unity's
242268
* main may return nonzero, while zephyr's main currently must
243269
* return 0 in all cases (other values are reserved).

0 commit comments

Comments
 (0)