Skip to content
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
115 changes: 77 additions & 38 deletions app/src/modules/app/app.c
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 */
Expand All @@ -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);

Expand All @@ -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;
}
Expand Down
63 changes: 32 additions & 31 deletions app/src/modules/fota/fota.c
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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 = {
Expand Down Expand Up @@ -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),
Expand All @@ -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);

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand All @@ -367,22 +368,22 @@ 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;

if (&FOTA_CHAN == state_object->chan) {
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");

Expand All @@ -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:
Expand Down
11 changes: 6 additions & 5 deletions app/src/modules/fota/fota.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,
Expand Down
8 changes: 4 additions & 4 deletions tests/module/fota/src/fota_module_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Loading