diff --git a/app/src/modules/app/app.c b/app/src/modules/app/app.c index aea6d84e..12ad7351 100644 --- a/app/src/modules/app/app.c +++ b/app/src/modules/app/app.c @@ -62,31 +62,33 @@ static void idle_run(void *o); static void fota_entry(void *o); static void fota_run(void *o); -static void fota_network_disconnect_pending_entry(void *o); -static void fota_network_disconnect_pending_run(void *o); +static void fota_downloading_run(void *o); -static void fota_image_apply_pending_entry(void *o); -static void fota_image_apply_pending_run(void *o); +static void fota_network_disconnect_entry(void *o); +static void fota_network_disconnect_run(void *o); + +static void fota_applying_image_entry(void *o); +static void fota_applying_image_run(void *o); static void fota_rebooting_entry(void *o); -/* Defining the hierarchical trigger module states: - * STATE_RUNNING: The module is operating normally. - * STATE_PERIODIC_TRIGGERING: The module is sending triggers to the rest of the system. - * STATE_IDLE: The module is disconnected from the cloud, triggers are blocked. - * STATE_FOTA: The module is in the FOTA process, triggers are blocked. - * STATE_FOTA_NETWORK_DISCONNECT_PENDING: The module is waiting for the network to disconnect. - * STATE_FOTA_IMAGE_APPLY_PENDING: The module is waiting for the FOTA image to be applied. - * STATE_FOTA_REBOOTING: The module is rebooting after applying the FOTA image. - */ enum state { + /* Normal operation */ STATE_RUNNING, - STATE_PERIODIC_TRIGGERING, - STATE_IDLE, + /* Triggers are periodically sent at a configured interval */ + STATE_PERIODIC_TRIGGERING, + /* Disconnected from the network, no triggers are sent */ + STATE_IDLE, + /* Ongoing FOTA process, triggers are blocked */ STATE_FOTA, - STATE_FOTA_NETWORK_DISCONNECT_PENDING, - STATE_FOTA_IMAGE_APPLY_PENDING, - STATE_FOTA_REBOOTING, + /* FOTA image is being downloaded */ + STATE_FOTA_DOWNLOADING, + /* Disconnecting from the network */ + STATE_FOTA_NETWORK_DISCONNECT, + /* Applying the image */ + STATE_FOTA_APPLYING_IMAGE, + /* Rebooting */ + STATE_FOTA_REBOOTING, }; /* State object for the app module. @@ -148,18 +150,25 @@ static const struct smf_state states[] = { fota_run, NULL, NULL, + &states[STATE_FOTA_DOWNLOADING] + ), + [STATE_FOTA_DOWNLOADING] = SMF_CREATE_STATE( + NULL, + fota_downloading_run, + NULL, + &states[STATE_FOTA], NULL ), - [STATE_FOTA_NETWORK_DISCONNECT_PENDING] = SMF_CREATE_STATE( - fota_network_disconnect_pending_entry, - fota_network_disconnect_pending_run, + [STATE_FOTA_NETWORK_DISCONNECT] = SMF_CREATE_STATE( + fota_network_disconnect_entry, + fota_network_disconnect_run, NULL, &states[STATE_FOTA], NULL ), - [STATE_FOTA_IMAGE_APPLY_PENDING] = SMF_CREATE_STATE( - fota_image_apply_pending_entry, - fota_image_apply_pending_run, + [STATE_FOTA_APPLYING_IMAGE] = SMF_CREATE_STATE( + fota_applying_image_entry, + fota_applying_image_run, NULL, &states[STATE_FOTA], NULL @@ -400,11 +409,26 @@ static void fota_run(void *o) case FOTA_DOWNLOAD_FAILED: STATE_SET(app_state, STATE_RUNNING); return; - case FOTA_NETWORK_DISCONNECT_NEEDED: - STATE_SET(app_state, STATE_FOTA_NETWORK_DISCONNECT_PENDING); + default: + /* Don't care */ + break; + } + } +} + +/* STATE_FOTA_DOWNLOADING */ + +static void fota_downloading_run(void *o) +{ + const struct app_state_object *state_object = (const struct app_state_object *)o; + + if (state_object->chan == &FOTA_CHAN) { + switch (state_object->fota_status) { + case FOTA_SUCCESS_REBOOT_NEEDED: + STATE_SET(app_state, STATE_FOTA_NETWORK_DISCONNECT); return; - case FOTA_REBOOT_NEEDED: - STATE_SET(app_state, STATE_FOTA_REBOOTING); + case FOTA_IMAGE_APPLY_NEEDED: + STATE_SET(app_state, STATE_FOTA_APPLYING_IMAGE); return; default: /* Don't care */ @@ -413,9 +437,9 @@ static void fota_run(void *o) } } -/* STATE_FOTA_NETWORK_DISCONNECT_PENDING */ +/* STATE_FOTA_NETWORK_DISCONNECT */ -static void fota_network_disconnect_pending_entry(void *o) +static void fota_network_disconnect_entry(void *o) { ARG_UNUSED(o); @@ -433,40 +457,55 @@ static void fota_network_disconnect_pending_entry(void *o) } } -static void fota_network_disconnect_pending_run(void *o) +static void fota_network_disconnect_run(void *o) { const struct app_state_object *state_object = (const struct app_state_object *)o; if (state_object->chan == &NETWORK_CHAN && state_object->network_status == NETWORK_DISCONNECTED) { - STATE_SET(app_state, STATE_FOTA_IMAGE_APPLY_PENDING); + STATE_SET(app_state, STATE_FOTA_REBOOTING); return; } } -/* STATE_FOTA_IMAGE_APPLY_PENDING */ +/* STATE_FOTA_APPLYING_IMAGE, */ -static void fota_image_apply_pending_entry(void *o) +static void fota_applying_image_entry(void *o) { ARG_UNUSED(o); LOG_DBG("%s", __func__); int err; - enum fota_msg_type msg = FOTA_APPLY_IMAGE; + struct network_msg msg = { + .type = NETWORK_DISCONNECT + }; - err = zbus_chan_pub(&FOTA_CHAN, &msg, K_SECONDS(1)); + err = zbus_chan_pub(&NETWORK_CHAN, &msg, K_SECONDS(1)); if (err) { LOG_ERR("zbus_chan_pub, error: %d", err); SEND_FATAL_ERROR(); } } -static void fota_image_apply_pending_run(void *o) +static void fota_applying_image_run(void *o) { const struct app_state_object *state_object = (const struct app_state_object *)o; - if (state_object->chan == &FOTA_CHAN && state_object->fota_status == FOTA_REBOOT_NEEDED) { + if (state_object->chan == &NETWORK_CHAN && + state_object->network_status == NETWORK_DISCONNECTED) { + + int err; + enum fota_msg_type msg = FOTA_IMAGE_APPLY; + + err = zbus_chan_pub(&FOTA_CHAN, &msg, K_SECONDS(1)); + if (err) { + LOG_ERR("zbus_chan_pub, error: %d", err); + SEND_FATAL_ERROR(); + } + + } else if (state_object->chan == &FOTA_CHAN && + state_object->fota_status == FOTA_SUCCESS_REBOOT_NEEDED) { STATE_SET(app_state, STATE_FOTA_REBOOTING); return; } diff --git a/app/src/modules/fota/fota.c b/app/src/modules/fota/fota.c index 87437739..b158fe81 100644 --- a/app/src/modules/fota/fota.c +++ b/app/src/modules/fota/fota.c @@ -47,26 +47,21 @@ ZBUS_CHAN_ADD_OBS(FOTA_CHAN, fota, 0); static void fota_reboot(enum nrf_cloud_fota_reboot_status status); static void fota_status(enum nrf_cloud_fota_status status, const char *const status_details); -/* State machine */ - -/* Defining modules states. - * STATE_RUNNING: The FOTA module is initializing and waiting for a poll request. - * STATE_WAITING_FOR_POLL_REQUEST: The FOTA module is waiting for a poll request. - * STATE_POLLING_FOR_UPDATE: The FOTA module is polling for an update. - * STATE_DOWNLOADING_UPDATE: The FOTA module is downloading an update. - * STATE_WAITING_FOR_NETWORK_DISCONNECT: The FOTA module is waiting for a network disconnect - * in order to apply the update. - * STATE_REBOOT_NEEDED: The FOTA module is waiting for a reboot. - * STATE_CANCELED: The FOTA module has been canceled, cleaning up. - */ enum fota_module_state { + /* The module is initialized and running */ STATE_RUNNING, - STATE_WAITING_FOR_POLL_REQUEST, - STATE_POLLING_FOR_UPDATE, - STATE_DOWNLOADING_UPDATE, - STATE_WAITING_FOR_NETWORK_DISCONNECT, - STATE_REBOOT_NEEDED, - STATE_CANCELED, + /* The module is waiting for a poll request */ + STATE_WAITING_FOR_POLL_REQUEST, + /* The module is polling for an update */ + STATE_POLLING_FOR_UPDATE, + /* The module is downloading an update */ + STATE_DOWNLOADING_UPDATE, + /* The module is waiting for the event FOTA_IMAGE_APPLY to apply the image */ + STATE_WAITING_FOR_IMAGE_APPLY, + /* The FOTA module is waiting for a reboot */ + STATE_REBOOT_NEEDED, + /* The FOTA module has been canceled, cleaning up */ + STATE_CANCELED, }; /* User defined state object. @@ -89,15 +84,21 @@ struct fota_state { /* Forward declarations */ static void state_running_entry(void *o); static void state_running_run(void *o); + static void state_waiting_for_poll_request_entry(void *o); static void state_waiting_for_poll_request_run(void *o); + static void state_polling_for_update_entry(void *o); static void state_polling_for_update_run(void *o); + static void state_downloading_update_entry(void *o); static void state_downloading_update_run(void *o); -static void state_waiting_for_network_disconnect_entry(void *o); -static void state_waiting_for_network_disconnect_run(void *o); + +static void state_waiting_for_image_apply_entry(void *o); +static void state_waiting_for_image_apply_run(void *o); + static void state_reboot_needed_entry(void *o); + static void state_canceled_entry(void *o); static struct fota_state fota_state = { @@ -130,9 +131,9 @@ static const struct smf_state states[] = { NULL, &states[STATE_RUNNING], NULL), - [STATE_WAITING_FOR_NETWORK_DISCONNECT] = - SMF_CREATE_STATE(state_waiting_for_network_disconnect_entry, - state_waiting_for_network_disconnect_run, + [STATE_WAITING_FOR_IMAGE_APPLY] = + SMF_CREATE_STATE(state_waiting_for_image_apply_entry, + state_waiting_for_image_apply_run, NULL, &states[STATE_RUNNING], NULL), @@ -155,7 +156,7 @@ static const struct smf_state states[] = { static void fota_reboot(enum nrf_cloud_fota_reboot_status status) { int err; - enum fota_msg_type evt = FOTA_REBOOT_NEEDED; + enum fota_msg_type evt = FOTA_SUCCESS_REBOOT_NEEDED; LOG_DBG("Reboot requested with FOTA status %d", status); @@ -200,7 +201,7 @@ static void fota_status(enum nrf_cloud_fota_status status, const char *const sta case NRF_CLOUD_FOTA_FMFU_VALIDATION_NEEDED: LOG_DBG("Full Modem FOTA Update validation needed, network disconnect required"); - evt = FOTA_NETWORK_DISCONNECT_NEEDED; + evt = FOTA_IMAGE_APPLY_NEEDED; break; default: LOG_DBG("Unknown FOTA status: %d", status); @@ -354,8 +355,8 @@ static void state_downloading_update_run(void *o) const enum fota_msg_type evt = MSG_TO_FOTA_TYPE(state_object->msg_buf); switch (evt) { - case FOTA_NETWORK_DISCONNECT_NEEDED: - STATE_SET(fota_state, STATE_WAITING_FOR_NETWORK_DISCONNECT); + case FOTA_IMAGE_APPLY_NEEDED: + STATE_SET(fota_state, STATE_WAITING_FOR_IMAGE_APPLY); break; case FOTA_DOWNLOAD_FAILED: STATE_SET(fota_state, STATE_WAITING_FOR_POLL_REQUEST); @@ -367,14 +368,14 @@ static void state_downloading_update_run(void *o) } } -static void state_waiting_for_network_disconnect_entry(void *o) +static void state_waiting_for_image_apply_entry(void *o) { ARG_UNUSED(o); LOG_DBG("%s", __func__); } -static void state_waiting_for_network_disconnect_run(void *o) +static void state_waiting_for_image_apply_run(void *o) { struct fota_state *state_object = o; @@ -382,7 +383,7 @@ static void state_waiting_for_network_disconnect_run(void *o) const enum fota_msg_type evt = MSG_TO_FOTA_TYPE(state_object->msg_buf); switch (evt) { - case FOTA_APPLY_IMAGE: + case FOTA_IMAGE_APPLY: LOG_DBG("Applying downloaded firmware image"); @@ -395,7 +396,7 @@ static void state_waiting_for_network_disconnect_run(void *o) } break; - case FOTA_REBOOT_NEEDED: + case FOTA_SUCCESS_REBOOT_NEEDED: STATE_SET(fota_state, STATE_REBOOT_NEEDED); break; default: diff --git a/app/src/modules/fota/fota.h b/app/src/modules/fota/fota.h index c44ec7f9..c1b8442e 100644 --- a/app/src/modules/fota/fota.h +++ b/app/src/modules/fota/fota.h @@ -32,13 +32,14 @@ enum fota_msg_type { /* Event notified if there is no available update. */ FOTA_NO_AVAILABLE_UPDATE, - /* Event notified when a FOTA update has succeeded, reboot is needed */ - FOTA_REBOOT_NEEDED, + /* Event notified when a FOTA update has succeeded, reboot is needed to apply the image. */ + FOTA_SUCCESS_REBOOT_NEEDED, /* Event notified when the module needs the network to disconnect in order to apply - * an update. When network has been disconnect, send the event FOTA_APPLY_IMAGE. + * an update. When disconnected from the network, send the event FOTA_IMAGE_APPLY. + * This is needed for Full Modem FOTA updates. */ - FOTA_NETWORK_DISCONNECT_NEEDED, + FOTA_IMAGE_APPLY_NEEDED, /* Event notified when the FOTA update has been canceled. */ FOTA_CANCELED, @@ -49,7 +50,7 @@ enum fota_msg_type { FOTA_POLL_REQUEST, /* Request to apply the downloaded firmware image. */ - FOTA_APPLY_IMAGE, + FOTA_IMAGE_APPLY, /* Cancel the FOTA process. */ FOTA_CANCEL, diff --git a/tests/module/fota/src/fota_module_test.c b/tests/module/fota/src/fota_module_test.c index 8cac24e6..1ba54979 100644 --- a/tests/module/fota/src/fota_module_test.c +++ b/tests/module/fota/src/fota_module_test.c @@ -158,15 +158,15 @@ void test_fota_module_should_succeed(void) /* 3. Download succeeded, validation needed */ invoke_nrf_cloud_fota_callback_stub_status(NRF_CLOUD_FOTA_FMFU_VALIDATION_NEEDED); - event_expect(FOTA_NETWORK_DISCONNECT_NEEDED); + event_expect(FOTA_IMAGE_APPLY_NEEDED); /* 4. Apply image */ - event_send(FOTA_APPLY_IMAGE); - event_expect(FOTA_APPLY_IMAGE); + event_send(FOTA_IMAGE_APPLY); + event_expect(FOTA_IMAGE_APPLY); /* 5. Reboot needed */ invoke_nrf_cloud_fota_callback_stub_reboot(FOTA_REBOOT_SUCCESS); - event_expect(FOTA_REBOOT_NEEDED); + event_expect(FOTA_SUCCESS_REBOOT_NEEDED); } void test_fota_module_should_fail_on_timeout(void)